Skip to content

Telegram channel (Bot API) with allowlist authorization + typing indicator#3

Merged
EdmondDantes merged 1 commit into
mainfrom
feat/telegram-channel
Jun 14, 2026
Merged

Telegram channel (Bot API) with allowlist authorization + typing indicator#3
EdmondDantes merged 1 commit into
mainfrom
feat/telegram-channel

Conversation

@EdmondDantes

Copy link
Copy Markdown
Contributor

What

Adds a Telegram bot channel (Bot API over the existing async HTTP client), with authorization modelled on OpenClaw/NanoClaw.

New (src/Chat/)

  • TelegramClientgetUpdates (long-poll, validates ok), sendMessage, sendChatAction.
  • TelegramChat (gateway) — one poll() loop pulls updates and, per update: authorize the sender by from.id against the allowlist (reuses the existing Config::isChatAllowed), demux by chat_id, queue the text. accept() yields each new authorized chat.
  • TelegramConversationreceive/send; confirm() via text reply (y = once, a = always, else no); updateStatus() maps an animated status to the typing… chat action.

Authorization (the must-have for a public bot)

  • Allowlist by from.id; unauthorized senders are silently dropped (a reply would confirm the bot to a prober). The rejected id is logged so the owner can bootstrap the allowlist.
  • DMs only; group updates ignored.

Wiring

  • New ConversationInterface::id() keys a per-chat SQLite file (consoleconsole.db, telegram → <chat_id>.db).
  • bin/claw: console runs inline; telegram spawns the poll loop + one Session per chat.

Behaviour & limits

  • Confirm is a text reply (inline buttons are a deliberate later step); DMs only; a chat never closes.
  • Routing/auth and the client are unit-tested via the HTTP fakes; the live network path needs a real bot token (couldn't be exercised here).

Run it

.env: CLAW_CHANNEL=telegram, TELEGRAM_BOT_TOKEN=…, CLAW_ALLOWED_CHATS=<your id>. Message the bot; if your id isn't listed yet it's logged so you can add it.

Tests

tests/Chat/Telegram*Test.php — getUpdates parse / API-error / sendMessage; routing + authorization + demux + group/non-text drop; typing on animated status, silence otherwise.

81 tests pass, PHPStan level 8 clean, php-cs-fixer clean.

Telegram bot channel over the existing async HTTP client. TelegramClient: getUpdates long-poll + sendMessage + sendChatAction. TelegramChat: one poll loop authorizes the sender by from.id against the allowlist (reuses Config::isChatAllowed), demuxes by chat_id, and yields each new authorized chat from accept(); unauthorized / non-DM / non-text updates are silently dropped (rejected id logged for allowlist bootstrap), DMs only. TelegramConversation: receive/send, confirm() via text reply (y/a/no), updateStatus() maps an animated status to the 'typing…' chat action.

Adds ConversationInterface::id() so persistence keys a per-chat SQLite file (console -> console.db, telegram -> <chat_id>.db). bin/claw runs the console inline and, for telegram, spawns the poll loop + one Session per chat.

Routing/auth and the client are unit-tested via the HTTP fakes; the live network path needs a real bot token. 81 tests, PHPStan level 8, php-cs-fixer — all green.
@EdmondDantes EdmondDantes merged commit 164c580 into main Jun 14, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant