Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
686 changes: 686 additions & 0 deletions feeders/telegrambot.go

Large diffs are not rendered by default.

622 changes: 622 additions & 0 deletions feeders/telegrambot_test.go

Large diffs are not rendered by default.

655 changes: 655 additions & 0 deletions filters/telegrambot.go

Large diffs are not rendered by default.

915 changes: 915 additions & 0 deletions filters/telegrambot_test.go

Large diffs are not rendered by default.

10 changes: 2 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ require (
github.com/ledongthuc/pdf v0.0.0-20250511090121-5959a4027728
github.com/localtunnel/go-localtunnel v0.0.0-20170326223115-8a804488f275
github.com/mmcdole/gofeed v1.3.0
github.com/mozilla-ai/any-llm-go v0.8.0
github.com/robertkrimen/otto v0.5.1
github.com/slack-go/slack v0.19.0
github.com/stretchr/testify v1.11.1
Expand Down Expand Up @@ -58,8 +59,8 @@ require (
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
github.com/go-telegram/bot v1.20.0 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc // indirect
Expand All @@ -74,8 +75,6 @@ require (
github.com/minio/crc64nvme v1.1.1 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/mozilla-ai/any-llm-go v0.8.0 // indirect
github.com/nlnwa/whatwg-url v0.6.2 // indirect
github.com/ogen-go/ogen v1.20.1 // indirect
github.com/ollama/ollama v0.15.4 // indirect
Expand Down Expand Up @@ -109,12 +108,10 @@ require (
golang.org/x/exp v0.0.0-20260218203240-3dfff04db8fa // indirect
golang.org/x/sync v0.19.0 // indirect
google.golang.org/genai v1.45.0 // indirect
nhooyr.io/websocket v1.8.17 // indirect
rsc.io/qr v0.2.0 // indirect
)

require (
cloud.google.com/go/compute v1.33.0 // indirect
cloud.google.com/go/compute/metadata v0.9.0 // indirect
dario.cat/mergo v1.0.2 // indirect
github.com/Masterminds/sprig/v3 v3.3.0
Expand Down Expand Up @@ -147,19 +144,16 @@ require (
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
github.com/minio/minio-go/v7 v7.0.98 // indirect
github.com/minio/sha256-simd v1.0.1 // indirect
github.com/mmcdole/goxpp v1.1.1 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pjbgf/sha1cd v0.5.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/sergi/go-diff v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.3.2 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xuri/excelize/v2 v2.10.1
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.48.0 // indirect
golang.org/x/mod v0.33.0 // indirect
golang.org/x/oauth2 v0.35.0 // indirect
Expand Down
305 changes: 23 additions & 282 deletions go.sum

Large diffs are not rendered by default.

99 changes: 99 additions & 0 deletions src_docs/content/doc/feeders/telegrambot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
title: "TelegramBot"
date: 2026-04-23T00:00:00+00:00
draft: false
---

## TelegramBot

This feeder consumes updates from a bot registered with [@BotFather](https://t.me/BotFather) using the [Telegram Bot API](https://core.telegram.org/bots/api).
Unlike the `telegram` feeder (MTProto user client), `telegrambot` is suited for interactive bots that expose commands or inline keyboards.

Based on [go-telegram/bot](https://github.com/go-telegram/bot).

### Parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| **token** | _STRING_ | empty | Bot token issued by @BotFather. Required. |
| **mode** | _STRING_ | `polling` | `polling` or `webhook`. |
| **addr** | _STRING_ | `:3000` | Listen address (webhook mode only). |
| **webhook_url** | _STRING_ | empty | Public URL registered with Telegram (required if `mode=webhook`). |
| **webhook_secret** | _STRING_ | empty | Telegram `secret_token` for incoming webhook verification. |
| **delete_webhook_on_stop** | _BOOL_ | `false` | Unregister the webhook from Telegram on shutdown. |
| **commands** | _STRING_ | empty | Comma-separated command list (e.g. `/start,/help`). Empty = auto-detect any `/xxx`. |
| **allowed_users** | _STRING_ | empty | Comma-separated user IDs or `@usernames`. Empty = all. Usernames matched case-insensitively. When set, the sender MUST be in the list — non-listed users are rejected regardless of chat origin. |
| **allowed_chats** | _STRING_ | empty | Comma-separated chat IDs. Empty = all. When `allowed_users` is also set, private DMs from listed users bypass this list (so listed users can always DM the bot). |
| **events** | _STRING_ | all | Comma-separated subset of `message,command,callback_query,edited_message,channel_post,edited_channel_post,chat_member,my_chat_member`. |
| **debug** | _BOOL_ | `false` | Enable library debug logging. |

{{< notice info "Polling example" >}}
`tgRule => <telegrambot: token="123:abc", commands="/start,/help", allowed_users="@alice,@bob"> | ...`
{{< /notice >}}

{{< notice info "Webhook example" >}}
`tgRule => <telegrambot: token="123:abc", mode="webhook", webhook_url="https://example.com/webhook", webhook_secret="sekret"> | ...`
{{< /notice >}}

### Output

Every propagated message carries a `type` extra field identifying the update kind.

Common fields (when applicable):

| Name | Description |
| --- | --- |
| type | Event kind. |
| update_id | Telegram update ID. |
| user_id, user_username, user_firstname, user_lastname, user_language, user_isbot, user_ispremium | Sender info. |
| chat_id, chat_type, chat_title, chat_username | Chat info. |

#### Event `message` / `command`

| Name | Description |
| --- | --- |
| text | Message text (also the `main` field). |
| msg_id, msg_timestamp, msg_date, msg_time | Timing. |
| msg_edited | Always `false` for this event. |
| msg_caption | Caption, if present. |
| msg_reply_to_id | Replied-to message ID, if any. |
| msg_forward_from | Forwarded-from user ID (only when forward origin is a user). |
| msg_forward_from_chat | Forwarded-from chat/channel ID. |
| msg_hasmedia | `true` when media attached. |
| msg_mediatype | `photo`, `document`, `video`, `audio`, `voice`, `sticker`, `animation`. |
| msg_medianame, msg_mediaext, msg_mediasize | Media metadata. |
| msg_file_id, msg_file_unique_id | Bot API file identifiers. |
| command | Command (e.g. `/start`). Only on `type=command`. |
| command_args | Text after the command. Only on `type=command`. |

#### Event `edited_message` / `edited_channel_post`

Same fields as above with `msg_edited=true` and an additional `msg_edit_date` field.

#### Event `channel_post` / `edited_channel_post`

Message fields as above. `user_*` may be absent (channel posts can lack a sender).

#### Event `callback_query`

| Name | Description |
| --- | --- |
| callback_id | Callback query ID. |
| callback_data | Button payload (also the `main` field). |
| callback_chatinstance | Telegram chat instance identifier. |
| chat_id, chat_type, chat_title, chat_username | Chat the keyboard message lives in (also populated when the original message is no longer accessible). Absent only for `inline_message_id`-only callbacks. |
| msg_id | ID of the message that carried the inline keyboard (useful with `edit_message`). |

#### Event `chat_member` / `my_chat_member`

| Name | Description |
| --- | --- |
| member_old_status | Previous member status. |
| member_new_status | New member status. |
| member_user_id, member_user_username | Affected user. |

### Examples

{{< notice info "React to /start command from authorized users only" >}}
`tgRule => <telegrambot: token="123:abc", commands="/start", allowed_users="@alice,12345"> | text(target="command", pattern="/start") | log(msg="start received from {{ .user_username }}")`
{{< /notice >}}
93 changes: 93 additions & 0 deletions src_docs/content/doc/filters/telegrambot.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
---
title: "TelegramBot"
date: 2026-04-28T00:00:00+00:00
draft: false
---

## TelegramBot

This filter performs Bot API actions (send/edit/delete a message, answer callback queries, send media, download attachments) on top of the [`telegrambot`](../../feeders/telegrambot/) feeder. It uses the `*bot.Bot` instance the feeder publishes via the `_telegrambot_api` extra, so a single bot connection serves both directions of traffic.

Note: it can be used only in a rule fed by the `telegrambot` feeder.

Based on [go-telegram/bot](https://github.com/go-telegram/bot).

### Parameters

| Parameter | Type | Default | Description |
|---|---|---|---|
| **action** | _STRING_ | `send_message` | One of `send_message`, `edit_message`, `delete_message`, `answer_callback`, `send_media`, `download_file`. |
| **to_chat** | _STRING_ | empty | Target chat ID. Optional: when omitted, falls back to the incoming `chat_id` extra (reply-to-sender). Templated. |
| **text** | _STRING_ | empty | Message text or callback notification text. Templated. Required for `send_message`, `edit_message`, `answer_callback`. |
| **caption** | _STRING_ | empty | Caption for `send_media`. Templated. |
| **parse_mode** | _STRING_ | `HTML` | One of `HTML`, `Markdown`, `MarkdownV2`, or empty (plain). Templated. |
| **message_id** | _STRING_ | empty | Required for `edit_message` and `delete_message`. Templated. |
| **callback_id** | _STRING_ | empty | `answer_callback`: optional explicit callback query ID. Falls back to incoming `callback_id` extra. Templated. |
| **show_alert** | _BOOL_ | `false` | `answer_callback`: when `true`, Telegram shows the answer as a modal alert dialog instead of a top-screen toast. Useful when the toast is too brief to notice. |
| **file_id** | _STRING_ | empty | `download_file`: optional explicit file ID. Falls back to incoming `msg_file_id` extra. Templated. |
| **filename** | _STRING_ | empty | `download_file`: output path on disk. Required. Templated. The basename of the Telegram-side `file_path` is exposed as `{{ .msg_filename }}` so it can be reused in the path. |
| **media** | _STRING_ | empty | `send_media`: source. URL (`http(s)://...`) → Telegram fetches it; existing local path → uploaded; otherwise treated as a `file_id` to re-send. Templated. Required. |
| **media_type** | _STRING_ | `document` | `send_media`: one of `photo`, `document`, `video`, `audio`, `voice`, `animation`, `sticker`. Templated. |
| **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. |

All string parameters support [templates](../../rules/templates/).

### Required parameters per action

| Action | Required |
|---|---|
| `send_message` | `text` |
| `edit_message` | `text`, `message_id` |
| `delete_message` | `message_id` |
| `answer_callback` | `text` |
| `send_media` | `media` |
| `download_file` | `filename` |

### Output

On success the filter mutates the incoming message and propagates it. Original extras are preserved.

| Action | Extras added |
|---|---|
| `send_message` | `main` overwritten with rendered text; `sent_message_id`, `sent_chat_id`, `sent_text`, `sent_date`. |
| `edit_message` | `edit_success="true"`, `edited_message_id`, `edited_chat_id`. |
| `delete_message` | `delete_success="true"`. |
| `answer_callback` | `callback_answered="true"`. |
| `send_media` | `main` overwritten with rendered caption; `sent_message_id`, `sent_chat_id`, `sent_text` (caption), `sent_date`. |
| `download_file` | `downloaded_path` (rendered filename), `msg_filename` (basename of Telegram `file_path`). |

On any Bot API error, missing required extra, or template/JSON failure the filter logs the error and returns `false`, stopping rule propagation. The pipeline keeps running.

### Examples

{{< notice info "Echo `/start` command" >}}
`tgRule => <telegrambot: token="123:abc", commands="/start"> | text(target="command", pattern="/start") | telegrambot(text="welcome {{ .user_username }}!")`
{{< /notice >}}

{{< notice info "Reply with inline keyboard" >}}
`telegrambot(text="Pick one", reply_markup="{\"inline_keyboard\":[[{\"text\":\"yes\",\"callback_data\":\"y\"},{\"text\":\"no\",\"callback_data\":\"n\"}]]}")`
{{< /notice >}}

{{< notice info "Reply keyboard (clickable buttons under input field)" >}}
`telegrambot(text="Pick one", reply_markup="{\"keyboard\":[[{\"text\":\"Yes\"},{\"text\":\"No\"}]],\"resize_keyboard\":true,\"one_time_keyboard\":true}")`
{{< /notice >}}

{{< notice info "Remove a previously-sent reply keyboard" >}}
`telegrambot(text="thanks", reply_markup="{\"remove_keyboard\":true}")`
{{< /notice >}}

{{< notice info "Answer a callback query" >}}
`text(target="type", pattern="callback_query") | telegrambot(action="answer_callback", text="got it")`
{{< /notice >}}

{{< notice info "Send a photo from a URL" >}}
`telegrambot(action="send_media", media_type="photo", media="https://example.com/img.jpg", caption="hi")`
{{< /notice >}}

{{< notice info "Edit a previously-sent message" >}}
`telegrambot(text="Working...") | telegrambot(action="edit_message", text="Done!", message_id="{{ .sent_message_id }}", to_chat="{{ .sent_chat_id }}")`
{{< /notice >}}

{{< notice info "Download an attachment" >}}
`text(target="msg_hasmedia", pattern="true") | telegrambot(action="download_file", filename="/data/{{ .chat_id }}/{{ .msg_filename }}")`
{{< /notice >}}
Loading