mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-17 04:50:51 +00:00
* feat(telegram): add spoiler tag support Render markdown ||spoiler|| syntax as <tg-spoiler> tags in Telegram HTML output. The markdown IR already parses spoiler syntax, but the Telegram renderer was missing the style marker. This adds the spoiler marker to renderTelegramHtml(). Fixes spoiler text appearing as raw ||text|| instead of hidden text. * fix: enable Telegram spoiler rendering (#11543) (thanks @ezhikkk) --------- Co-authored-by: Параша <parasha@openclaw.local> Co-authored-by: Muhammed Mukhthar CM <mukhtharcm@gmail.com>
102 lines
2.7 KiB
TypeScript
102 lines
2.7 KiB
TypeScript
import type { MarkdownTableMode } from "../config/types.base.js";
|
|
import {
|
|
chunkMarkdownIR,
|
|
markdownToIR,
|
|
type MarkdownLinkSpan,
|
|
type MarkdownIR,
|
|
} from "../markdown/ir.js";
|
|
import { renderMarkdownWithMarkers } from "../markdown/render.js";
|
|
|
|
export type TelegramFormattedChunk = {
|
|
html: string;
|
|
text: string;
|
|
};
|
|
|
|
function escapeHtml(text: string): string {
|
|
return text.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
}
|
|
|
|
function escapeHtmlAttr(text: string): string {
|
|
return escapeHtml(text).replace(/"/g, """);
|
|
}
|
|
|
|
function buildTelegramLink(link: MarkdownLinkSpan, _text: string) {
|
|
const href = link.href.trim();
|
|
if (!href) {
|
|
return null;
|
|
}
|
|
if (link.start === link.end) {
|
|
return null;
|
|
}
|
|
const safeHref = escapeHtmlAttr(href);
|
|
return {
|
|
start: link.start,
|
|
end: link.end,
|
|
open: `<a href="${safeHref}">`,
|
|
close: "</a>",
|
|
};
|
|
}
|
|
|
|
function renderTelegramHtml(ir: MarkdownIR): string {
|
|
return renderMarkdownWithMarkers(ir, {
|
|
styleMarkers: {
|
|
bold: { open: "<b>", close: "</b>" },
|
|
italic: { open: "<i>", close: "</i>" },
|
|
strikethrough: { open: "<s>", close: "</s>" },
|
|
code: { open: "<code>", close: "</code>" },
|
|
code_block: { open: "<pre><code>", close: "</code></pre>" },
|
|
spoiler: { open: "<tg-spoiler>", close: "</tg-spoiler>" },
|
|
},
|
|
escapeText: escapeHtml,
|
|
buildLink: buildTelegramLink,
|
|
});
|
|
}
|
|
|
|
export function markdownToTelegramHtml(
|
|
markdown: string,
|
|
options: { tableMode?: MarkdownTableMode } = {},
|
|
): string {
|
|
const ir = markdownToIR(markdown ?? "", {
|
|
linkify: true,
|
|
enableSpoilers: true,
|
|
headingStyle: "none",
|
|
blockquotePrefix: "",
|
|
tableMode: options.tableMode,
|
|
});
|
|
return renderTelegramHtml(ir);
|
|
}
|
|
|
|
export function renderTelegramHtmlText(
|
|
text: string,
|
|
options: { textMode?: "markdown" | "html"; tableMode?: MarkdownTableMode } = {},
|
|
): string {
|
|
const textMode = options.textMode ?? "markdown";
|
|
if (textMode === "html") {
|
|
return text;
|
|
}
|
|
return markdownToTelegramHtml(text, { tableMode: options.tableMode });
|
|
}
|
|
|
|
export function markdownToTelegramChunks(
|
|
markdown: string,
|
|
limit: number,
|
|
options: { tableMode?: MarkdownTableMode } = {},
|
|
): TelegramFormattedChunk[] {
|
|
const ir = markdownToIR(markdown ?? "", {
|
|
linkify: true,
|
|
enableSpoilers: true,
|
|
headingStyle: "none",
|
|
blockquotePrefix: "",
|
|
tableMode: options.tableMode,
|
|
});
|
|
const chunks = chunkMarkdownIR(ir, limit);
|
|
return chunks.map((chunk) => ({
|
|
html: renderTelegramHtml(chunk),
|
|
text: chunk.text,
|
|
}));
|
|
}
|
|
|
|
export function markdownToTelegramHtmlChunks(markdown: string, limit: number): string[] {
|
|
return markdownToTelegramChunks(markdown, limit).map((chunk) => chunk.html);
|
|
}
|