Telegram
Telegram 은 양쪽 모드 모두 동작합니다.
| Mode | 동작 |
|---|---|
| 알림 | 알림 파이프라인의 단방향 sendMessage. |
| IM 브릿지 | getUpdates long-poll 을 통한 에이전트 양방향 채팅. |
같은 봇 토큰 (8…:AA…, @BotFather 에서) 이 둘을 구동합니다 — Telegram 은 path 의 토큰으로 인증합니다. 별도의 서명 스킴은 없습니다.
Feishu / DingTalk 권역 밖 사용자를 위해 설계됨
Telegram 은 Ongrid 가 비중국 팀과 운영자가 마침 Telegram 에 사는 하이브리드 배포에 권장하는 IM 채널입니다. provider 는 ADR-031 에서 추가되었습니다.
알림 모드
notify.Sender 가 {chat_id, text} 를 https://api.telegram.org/bot<TOKEN>/sendMessage 로 POST. 자격 증명은 (path 의) 봇 토큰; chat_id (숫자) 는 목적지 채팅입니다.
// internal/pkg/notify/webhook.go
NewTelegramSender(name, endpoint, chatID, client)endpoint 는 리터럴 …/bot<TOKEN>/sendMessage URL 이고 chatID 는 대상 채팅 (DM 은 사용자의 숫자 ID, 그룹은 Telegram 이 제공하는 음수) 입니다.
설정:
@BotFather에 DM →/newbot→ 사용자 이름 고르기 → 토큰 복사.- 대상 채팅 (그룹 / 채널) 에 봇 추가 또는 한 번 DM 해서 봇이 당신을 알게 합니다.
- 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 브릿지 모드 (양방향)
인바운드는 매니저의 아웃바운드 HTTPS 연결에서 long-poll 됩니다. 호출이 아웃바운드 이기 때문에 Telegram 은 NAT, 방화벽, HTTPS 프록시 뒤에서 동작합니다. setWebhook (대안) 은 Telegram 이 매니저에 도달해야 하므로 대부분의 사설 클라우드 배포와 호환되지 않고 중국 본토에서 신뢰성이 없습니다.
stream 이 유일한 모드
validator 는 그 외 모든 것을 거부:
telegram only supports stream mode
webhook 모드는 Telegram 에 대해 UI 에 노출되지 않습니다.
자격 증명 매핑
im_apps 행은 기존 컬럼을 재사용합니다 — 스키마 변경 없음:
im_apps column | Telegram 의미 |
|---|---|
provider | "telegram" |
mode | "stream" (validator 가 고정) |
app_id | 봇 사용자 이름 (예: ongridbot). 표시 + dedupe. |
app_secret | BotFather 토큰 (8…:AA…). 저장 시 암호화. |
allow_from | 필수 콤마 구분 숫자 사용자 ID. |
verify_token | 사용 안 함. |
encrypt_key | 사용 안 함. |
allow_from
봇과 대화 가능한 비어 있지 않은 숫자 Telegram 사용자 ID 리스트. validator 는 비숫자 토큰 (오타 alice 는 조용한 빈 화이트리스트 대신 깔끔한 required-error 로 안착) 을 버리고 음수 토큰 (그룹 / 슈퍼그룹 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)
숫자 사용자 ID 찾는 방법:
@userinfobot에 DM 하면 숫자 ID 로 답합니다.- 또는 등록 후 채팅에 메시지를 보내고
curl https://api.telegram.org/bot<TOKEN>/getUpdates를 실행해result[0].message.from.id읽기.
telegram: / tg: 접두사는 조용히 제거 (OpenClaw 호환) 되므로 telegram:123456789 과 123456789 은 같은 엔트리입니다.
Miss 시 조용한 drop
화이트리스트에 없는 발신자는 WARN 로그와 함께 버려짐:
telegram inbound from non-allowlisted sender — ignored
user_id=42 user_name=alice chat_id=42답신 없음. placeholder 없음. 봇은 자신의 존재조차 확인하지 않습니다. 이는 OpenClaw allowFrom 을 미러링 — 답신은 에이전트 백엔드 봇이 이 사용자명에 산다는 사실을 노출하고 사람들이 탐색하도록 유혹할 수 있습니다.
설정
@BotFather에 DM →/newbot→ 이름과bot으로 끝나는 고유 사용자 이름 선택. 토큰 복사.- (선택) 봇이 그룹에서 메시지를 보길 원한다면
@BotFather → /setprivacy → Disable(기본은 mention-only). @userinfobot으로 숫자 사용자 ID 찾기.- Ongrid 에서: Settings → IM bridge → New → Provider =
telegram→ Mode =stream→ App ID = 봇 사용자 이름 → App secret = BotFather 토큰 →allow_from= 자신 (및 팀원) 의 숫자 ID. 저장하고 Enable. - 허용된 계정에서 봇에 DM. 봇이 placeholder 로 답하고 에이전트가 추론할 때마다 그 자리에서 편집합니다.
프록시 이야기
Telegram API 호스트 (api.telegram.org) 는 대부분의 네트워크에서 도달 가능하지만 중국 본토에서는 차단됩니다. provider 는 zero-value http.Client 를 사용하므로 매니저 환경의 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 은 모든 릴리스에 실립니다. override 파일은 환경별이며 make package / install 스크립트 재실행에서 살아남습니다.
알아둘 만한 특이점
message is not modified 는 무해
editMessageText payload 가 현재 메시지 텍스트와 정확히 일치하면 Telegram 은 HTTP 400 (message is not modified) 을 반환합니다. 점진적 스트리밍은 쓰로틀 틱이나 최종 플러시에서 같은 청크를 반복할 수 있습니다. 클라이언트는 정확히 이 에러 문자열만 삼키고 nil 을 반환합니다 — 그 외의 것 (400, 403, 5xx) 은 여전히 전파됩니다. EditMessageText 참고.
봇당 poller 는 하나만
Telegram 은 동시 getUpdates 호출을 거부합니다. StreamSupervisor 는 im_app 행당 하나의 클라이언트를 강제합니다. Telegram 앱 행을 복제하면 하나만 성공적으로 poll 하고 나머지는 409 Conflict 를 보고 백오프합니다.
Rate-limit 재시도
429 Too Many Requests 는 최대 3 회 재시도됩니다. 대기 시간은 Telegram 의 parameters.retry_after 를 60 초 상한으로 따릅니다. 5xx 에러는 1 초 / 2 초 / 4 초 백오프로 재시도. 하드 4xx (400/401/403) 는 재시도 하지 않음 — supervisor 로 버블하며 토큰 / chat-id 이슈를 시사합니다. maxCallRetries 참고.
Long-poll 타임아웃
서버 사이드 long poll 은 25 초 대기 (pollTimeoutSec), 클라이언트 사이드에 10 초 버퍼. 느린 네트워크는 그저 더 긴 poll 사이클을 볼 뿐이며 supervisor 가 busy-wait 재연결을 하지는 않습니다.
텍스트 전용
스티커, 사진, 음성 메모, 파일은 조용히 버려집니다. message.text 이벤트만 에이전트 턴을 구동합니다. 브릿지는 의도적으로 S1 (텍스트 입력 / 텍스트 출력); 리치 미디어는 향후 확장입니다.