Skip to content

Commit 1c99bcc

Browse files
committed
fix
1 parent e86663c commit 1c99bcc

3 files changed

Lines changed: 77 additions & 2 deletions

File tree

filters/telegrambot.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"encoding/json"
66
"fmt"
7+
"html"
78
"io"
89
"net/http"
910
"os"
@@ -21,6 +22,33 @@ import (
2122
"github.com/Matrix86/driplane/data"
2223
)
2324

25+
// telegramTemplateFuncs returns sprig's funcs plus telegram-specific helpers.
26+
//
27+
// Extra funcs:
28+
// - htmlEscape: escapes <, >, &, " for Telegram HTML parse_mode.
29+
// - mdv2Escape: escapes the MarkdownV2 reserved set (_*[]()~`>#+-=|{}.!).
30+
func telegramTemplateFuncs() template.FuncMap {
31+
fm := sprig.FuncMap()
32+
fm["htmlEscape"] = html.EscapeString
33+
fm["mdv2Escape"] = escapeMarkdownV2
34+
return fm
35+
}
36+
37+
// escapeMarkdownV2 escapes every reserved character defined by Telegram's
38+
// MarkdownV2 parse_mode. See https://core.telegram.org/bots/api#markdownv2-style
39+
func escapeMarkdownV2(s string) string {
40+
const reserved = "_*[]()~`>#+-=|{}.!"
41+
var b strings.Builder
42+
b.Grow(len(s))
43+
for _, r := range s {
44+
if strings.ContainsRune(reserved, r) {
45+
b.WriteByte('\\')
46+
}
47+
b.WriteRune(r)
48+
}
49+
return b.String()
50+
}
51+
2452
// TelegramBot is a Filter to perform Bot API actions on top of the telegrambot feeder.
2553
type TelegramBot struct {
2654
Base
@@ -109,7 +137,7 @@ func (f *TelegramBot) compileTemplates() error {
109137
if !ok || v == "" {
110138
continue
111139
}
112-
tpl, err := template.New("telegrambot:" + e.key).Funcs(sprig.FuncMap()).Parse(v)
140+
tpl, err := template.New("telegrambot:" + e.key).Funcs(telegramTemplateFuncs()).Parse(v)
113141
if err != nil {
114142
return fmt.Errorf("telegrambot: invalid template for %q: %w", e.key, err)
115143
}

filters/telegrambot_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,44 @@ func TestTelegramBotResolveParseMode(t *testing.T) {
332332
}
333333
}
334334

335+
func TestTelegramBotTemplateHTMLEscape(t *testing.T) {
336+
f := newTestFilter(t, map[string]string{
337+
"action": "send_message",
338+
"to_chat": "1",
339+
"text": `{{ htmlEscape .raw }}`,
340+
})
341+
mock := newMockBotAPI(t)
342+
b, _ := newMockedBot(t, mock)
343+
msg := newTestMessage(b, "x", map[string]interface{}{"raw": `<test> & "ok"`})
344+
got, err := msg.ApplyPlaceholder(f.text)
345+
if err != nil {
346+
t.Fatalf("ApplyPlaceholder: %v", err)
347+
}
348+
want := `&lt;test&gt; &amp; &#34;ok&#34;`
349+
if got != want {
350+
t.Fatalf("htmlEscape: got %q want %q", got, want)
351+
}
352+
}
353+
354+
func TestTelegramBotTemplateMarkdownV2Escape(t *testing.T) {
355+
f := newTestFilter(t, map[string]string{
356+
"action": "send_message",
357+
"to_chat": "1",
358+
"text": `{{ mdv2Escape .raw }}`,
359+
})
360+
mock := newMockBotAPI(t)
361+
b, _ := newMockedBot(t, mock)
362+
msg := newTestMessage(b, "x", map[string]interface{}{"raw": `a_b*c [d](e) ! .`})
363+
got, err := msg.ApplyPlaceholder(f.text)
364+
if err != nil {
365+
t.Fatalf("ApplyPlaceholder: %v", err)
366+
}
367+
want := `a\_b\*c \[d\]\(e\) \! \.`
368+
if got != want {
369+
t.Fatalf("mdv2Escape: got %q want %q", got, want)
370+
}
371+
}
372+
335373
func TestTelegramBotParseReplyMarkup_Empty(t *testing.T) {
336374
f := newTestFilter(t, map[string]string{"action": "send_message", "text": "hi", "to_chat": "1"})
337375
mock := newMockBotAPI(t)

src_docs/content/doc/filters/telegrambot.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,16 @@ Based on [go-telegram/bot](https://github.com/go-telegram/bot).
3030
| **media_type** | _STRING_ | `document` | `send_media`: one of `photo`, `document`, `video`, `audio`, `voice`, `animation`, `sticker`. Templated. |
3131
| **reply_markup** | _STRING_ | empty | Raw Bot API JSON. Variant auto-detected by top-level key: `inline_keyboard` → InlineKeyboardMarkup, `keyboard` → ReplyKeyboardMarkup, `remove_keyboard` → ReplyKeyboardRemove, `force_reply` → ForceReply. Templated, then parsed. |
3232

33-
All string parameters support [templates](../../rules/templates/).
33+
All string parameters support [templates](en/doc/rules/templates/).
34+
35+
In addition to the standard sprig functions, two extra template helpers are exposed for safe text rendering:
36+
37+
| Function | Purpose |
38+
|---|---|
39+
| `htmlEscape` | Escapes `<`, `>`, `&`, `"` so user-supplied text can be safely embedded with `parse_mode="HTML"` (the default). Required when the rendered text contains `<`, otherwise Telegram parses it as an HTML tag and rejects the message. |
40+
| `mdv2Escape` | Escapes the MarkdownV2 reserved set (`` _*[]()~`>#+-=|{}.! ``) for use with `parse_mode="MarkdownV2"`. |
41+
42+
Example: `telegrambot(text="found {{ htmlEscape .main }}")`.
3443

3544
### Required parameters per action
3645

0 commit comments

Comments
 (0)