mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-27 21:07:01 -06:00
[codex] Fix Telegram rich local Markdown link hrefs (#94118)
Merged via squash.
Prepared head SHA: 42c738978f
Co-authored-by: dankarization <26330178+dankarization@users.noreply.github.com>
Co-authored-by: obviyus <22031114+obviyus@users.noreply.github.com>
Reviewed-by: @obviyus
This commit is contained in:
@@ -206,6 +206,28 @@ describe("markdownToTelegramHtml", () => {
|
||||
).toBe('<a href="https://example.com">docs</a>');
|
||||
});
|
||||
|
||||
it("keeps unsupported markdown link hrefs as visible text in rich HTML", () => {
|
||||
expect(
|
||||
markdownToTelegramRichHtml(
|
||||
"[scripts/yougile.py](/home/dankar/.openclaw/workspace-yougile/scripts/yougile.py#L41)",
|
||||
),
|
||||
).toBe("<code>scripts/yougile.py</code>");
|
||||
expect(markdownToTelegramRichHtml("[config](./openclaw.json)")).toBe("config");
|
||||
expect(markdownToTelegramRichHtml("[docs](https://example.com/docs)")).toBe(
|
||||
'<a href="https://example.com/docs">docs</a>',
|
||||
);
|
||||
expect(markdownToTelegramRichHtml("[user](tg://user?id=123)")).toBe(
|
||||
'<a href="tg://user?id=123">user</a>',
|
||||
);
|
||||
expect(markdownToTelegramRichHtml("[support](mailto:user@example.com)")).toBe(
|
||||
'<a href="mailto:user@example.com">support</a>',
|
||||
);
|
||||
expect(markdownToTelegramRichHtml("[call](tel:+123456789)")).toBe(
|
||||
'<a href="tel:+123456789">call</a>',
|
||||
);
|
||||
expect(markdownToTelegramRichHtml("[back](#top)")).toBe('<a href="#top">back</a>');
|
||||
});
|
||||
|
||||
it("preserves Markdown heading levels in rich HTML", () => {
|
||||
expect(markdownToTelegramRichHtml("# Title\n\n### Detail")).toBe(
|
||||
"<h1>Title</h1>\n\n<h3>Detail</h3>",
|
||||
|
||||
@@ -32,6 +32,10 @@ function escapeHtmlAttr(text: string): string {
|
||||
return escapeHtml(text).replace(/"/g, """);
|
||||
}
|
||||
|
||||
function isTelegramRichLinkHref(href: string): boolean {
|
||||
return /^(?:https?:\/\/|tg:\/\/|mailto:|tel:|#)/i.test(href);
|
||||
}
|
||||
|
||||
/**
|
||||
* File extensions that share TLDs and commonly appear in code/documentation.
|
||||
* These are wrapped in <code> tags to prevent Telegram from generating
|
||||
@@ -51,6 +55,11 @@ function buildTelegramLink(link: MarkdownLinkSpan, text: string) {
|
||||
if (link.start === link.end) {
|
||||
return null;
|
||||
}
|
||||
// Telegram rich links reject local or relative hrefs; keep the label visible
|
||||
// instead of letting one unsupported link drop the whole message.
|
||||
if (!isTelegramRichLinkHref(href)) {
|
||||
return null;
|
||||
}
|
||||
// Suppress auto-linkified file references (e.g. README.md → http://README.md)
|
||||
const label = text.slice(link.start, link.end);
|
||||
if (isAutoLinkedFileRef(href, label)) {
|
||||
|
||||
@@ -1044,6 +1044,35 @@ describe("sendMessageTelegram", () => {
|
||||
expect(botRawApi.sendRichMessage.mock.calls[0]?.[0]?.rich_message.html).toBe(markdown);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
name: "local path",
|
||||
markdown:
|
||||
"See [scripts/yougile.py](/home/user/.openclaw/workspace/scripts/yougile.py#L41) and [docs](https://example.com/docs)",
|
||||
rejectedAnchor: '<a href="/home',
|
||||
visibleLabel: "<code>scripts/yougile.py</code>",
|
||||
},
|
||||
{
|
||||
name: "relative path",
|
||||
markdown: "Edit [config](./openclaw.json) or see [docs](https://example.com/docs)",
|
||||
rejectedAnchor: '<a href="./',
|
||||
visibleLabel: "config",
|
||||
},
|
||||
])("keeps rich delivery when a markdown link targets a $name", async (testCase) => {
|
||||
botApi.sendMessage.mockResolvedValue({ message_id: 48, chat: { id: "123" } });
|
||||
|
||||
await sendMessageTelegram("123", testCase.markdown, {
|
||||
cfg: { channels: { telegram: { richMessages: true } } },
|
||||
token: "tok",
|
||||
});
|
||||
|
||||
expect(botRawApi.sendRichMessage).toHaveBeenCalledTimes(1);
|
||||
const richHtml = String(botRawApi.sendRichMessage.mock.calls[0]?.[0]?.rich_message.html ?? "");
|
||||
expect(richHtml).not.toContain(testCase.rejectedAnchor);
|
||||
expect(richHtml).toContain(testCase.visibleLabel);
|
||||
expect(richHtml).toContain('<a href="https://example.com/docs">docs</a>');
|
||||
});
|
||||
|
||||
it("renders complex markdown into HTML text", async () => {
|
||||
botApi.sendMessage.mockResolvedValue({ message_id: 46, chat: { id: "123" } });
|
||||
const markdown = [
|
||||
|
||||
Reference in New Issue
Block a user