Telegram
Telegram 两种模式都支持:
| 模式 | 行为 |
|---|---|
| Notification | 告警流水线单向 sendMessage。 |
| IM bridge | 通过 getUpdates 长轮询做双向对话。 |
两种模式用同一个 bot token(8…:AA…,从 @BotFather 拿)—— Telegram 用 path 里的 token 鉴权,没有额外签名机制。
面向非飞书/钉钉地区的用户
对海外团队,或运维恰好都用 Telegram 的混合部署,Ongrid 推荐 Telegram 作为 IM 通道。Provider 加入见 ADR-031。
Notification 模式
notify.Sender 把 {chat_id, text} POST 到 https://api.telegram.org/bot<TOKEN>/sendMessage。凭据是 bot token(path 里),chat_id(数字)是目标会话。
// internal/pkg/notify/webhook.go
NewTelegramSender(name, endpoint, chatID, client)endpoint 就是字面量 …/bot<TOKEN>/sendMessage,chatID 是目标会话(私聊就是对方的数字 user id,群就是 Telegram 给的那个负数)。
配置:
- 私聊
@BotFather→/newbot→ 起一个 username → 复制 token。 - 把 bot 加进目标会话(群/频道)或先私聊一下,让它认识你。
- 拿 chat ID。最快办法:在会话里随便发一句,然后
curl https://api.telegram.org/bot<TOKEN>/getUpdates,读result[0].message.chat.id。 - 在 Ongrid:Settings → Channels → New → Provider =
telegram→ Endpoint =https://api.telegram.org/bot<TOKEN>/sendMessage,chat_id = 第 3 步那个数。
IM bridge 模式(双向)
入站走 manager 主动外联的 HTTPS 长轮询。因为是外联调用,Telegram 在 NAT、防火墙、HTTPS 代理后面都能跑。setWebhook(替代方案)要求 Telegram 反向到达 manager —— 这对大多数私有云部署不适用,对国内访问也不靠谱。
只支持 stream
校验器拒绝其他值:
telegram only supports stream mode
UI 里 Telegram 不会暴露 webhook 模式选项。
字段映射
im_apps 行复用现有列,没有 schema 变更:
im_apps 列 | Telegram 含义 |
|---|---|
provider | "telegram" |
mode | "stream"(校验器写死) |
app_id | Bot username(比如 ongridbot)。用于展示和去重。 |
app_secret | BotFather token(8…:AA…)。落盘加密。 |
allow_from | 必填,逗号分隔的数字 user ID。 |
verify_token | 不用。 |
encrypt_key | 不用。 |
allow_from
非空的 Telegram 数字 user ID 列表,允许对话。校验器丢掉非数字 token(这样写错 alice 会以清晰的"必填"错误冒出来,而不是变成静默空白名单),并拒掉负数 token(那些是群 / 超级群的 chat ID,不该放在发送方白名单里)。
telegram requires allow_from — at least one numeric Telegram user ID (the bot is publicly reachable; an empty allowlist would let anyone command the agent)
怎么查数字 user ID:
- 私聊
@userinfobot,它会把你的数字 ID 回过来。 - 或者注册完之后在会话里发一条消息,跑
curl https://api.telegram.org/bot<TOKEN>/getUpdates,读result[0].message.from.id。
telegram: / tg: 前缀会被静默剥掉(兼容 OpenClaw),所以 telegram:123456789 和 123456789 是同一条。
不在白名单则静默丢
非白名单发送者的消息在 WARN 等级丢日志:
telegram inbound from non-allowlisted sender — ignored
user_id=42 user_name=alice chat_id=42不回复、不发占位、bot 甚至不确认自己存在。这与 OpenClaw 的 allowFrom 行为一致 —— 回复会泄露这个 username 背后有一个带工具的 Agent,会勾人来探。
配置步骤
- 私聊
@BotFather→/newbot→ 起一个名字 + 以bot结尾的唯一 username。复制 token。 - (可选)
@BotFather → /setprivacy → Disable,如果想让 bot 看到群里所有消息(默认只看 @mention)。 - 通过
@userinfobot查到你自己的数字 user ID。 - 在 Ongrid:Settings → IM bridge → New → Provider =
telegram→ Mode =stream→ App ID = bot username → App secret = BotFather token →allow_from= 你的数字 ID(和队友的)。保存并 Enable。 - 从白名单账号私聊 bot。bot 先回一条占位,然后随着 Agent 推理在原地编辑。
代理这件事
Telegram API 主机(api.telegram.org)在大多数网络可达,但在国内被墙。provider 用零值 http.Client,所以会遵守 manager 进程环境里的 HTTPS_PROXY / HTTP_PROXY / NO_PROXY —— getUpdates 走哪个代理,sendMessage 就走哪个。
docker-compose 部署下,代理写进 docker-compose.override.yml 持久化:
services:
manager:
environment:
HTTPS_PROXY: http://your-proxy:8080
NO_PROXY: localhost,127.0.0.1,manager,mysql,prometheus,loki,tempo,grafana,qdrant不要把代理写进主 docker-compose.yml
主 docker-compose.yml 是随每个 release 发的。override 文件是按环境的,能扛过 make package 和安装脚本重跑。
需要知道的小坑
message is not modified 是无害的
当 editMessageText 的 payload 和当前消息文本完全一致时,Telegram 会返 HTTP 400(message is not modified)。流式输出在节流 tick 或最终 flush 时可能重复同一段 chunk。客户端只吞这一种错误字符串并返回 nil —— 其他(400/403/5xx)照常上报。见 EditMessageText。
每个 bot 只允许一个 poller
Telegram 拒绝并发 getUpdates。StreamSupervisor 每个 im_app 行强制只起一个 client。如果你复制了一个 Telegram app 行,只有一个能成功轮询 —— 另一个会拿到 409 Conflict 然后退避。
限流重试
429 Too Many Requests 最多重试 3 次。等待时间遵守 Telegram 的 parameters.retry_after,上限 60s。5xx 错误以 1s / 2s / 4s 退避重试。硬 4xx(400/401/403)不重试 —— 直接冒到 supervisor,通常意味着 token / chat-id 出问题了。见 maxCallRetries。
长轮询超时
服务端长轮询等 25s(pollTimeoutSec),客户端再加 10s buffer。慢网络只是看到更长的轮询周期;supervisor 不会做忙等重连。
只接文本
贴纸、图片、语音、文件都被静默丢。只有 message.text 事件触发 Agent。bridge 有意保持 S1(文本进文本出);富媒体留待未来扩展。