Skip to content

Add a Telegram adapter - #20

Merged
TroyHernandez merged 2 commits into
mainfrom
telegram
Sep 7, 2026
Merged

TroyHernandez merged 2 commits into
mainfrom
telegram

Conversation

@TroyHernandez

Copy link
Copy Markdown
Contributor

Fourth adapter, the one DESIGN.md calls "nearly free once the poll contract exists". chat_telegram() puts the Bot API behind the contract with HTTP delegated to httr, which was already in Suggests, so the dependency footprint does not change.

  • Receive is getUpdates long polling. One call returns every update Telegram is holding for the bot across every chat it is in, so the cursor is a single update offset rather than one per channel. The first poll returns whatever Telegram is still holding (up to 24h), which is the mail that arrived while the bot was down rather than history, so it comes out as ordinary traffic. allowed_updates asks for messages, channel posts, and reaction events; edits are not requested.
  • Sends render the contract's markdown to Telegram's HTML subset (MarkdownV2 wants a dozen prose characters escaped), carry thread as message_thread_id and reply_to as reply_parameters, send each file as its own sendDocument ahead of the text so ids come back in the order Matrix uses, and honour notify = FALSE. A supplied rich fragment is sent as HTML.
  • Edits, emoji reactions (an empty key clears), reaction events as chat_reaction records (additions only; a removal has no shape in the contract), typing, getChat info, leaveChat, getMe identity, setMyName, and attachment fetch through getFile are wired. chat_addressed() reads @username from the body, treats a private chat and a reply to the bot as addressed, and still honours declared text_mention entities.
  • Capabilities are honest about the Bot API: no history, member list, chat list, read markers, joining, or creating. Reaction events reach a bot in a group only where it is an administrator, and the roxygen says so.
  • Every id goes through sprintf("%.0f") rather than as.character(): R writes the double 100000 as "1e+05", and every id Telegram sends is a number to its JSON parser.

Tests: every verb runs through a .api seam that asserts on the wire shape each Bot API method receives (form-encoded, NULLs dropped, logicals lowercase), and httr's formals are pinned for drift. The real HTTP layer was probed once against api.telegram.org with a fake token: the 401 surfaces as "Telegram refused getMe: Unauthorized" through the parsed body. 1363 tests pass under R CMD check; the one WARNING (chat_edit()'s undocumented rich) is pre-existing on main and gets its own PR.

chat_telegram() puts the Bot API behind the contract, with HTTP
delegated to the suggested httr package. getUpdates long polling is
the poll, with one update offset as the cursor across every chat the
bot is in; the first poll returns whatever Telegram is still holding,
which is the mail that arrived while the bot was down rather than
history. Sends render markdown to Telegram HTML, carry thread and
reply targets, send each file as its own message ahead of the text,
and honour notify = FALSE. Edits, emoji reactions and reaction events,
typing, chat info, leaving, getMe identity, setMyName, @username
addressing, and attachment fetch through getFile are all wired.

Capabilities are honest about the Bot API: no history, member list,
chat list, read markers, joining, or creating. Reaction events reach a
bot in a group only where it is an administrator.

Ids are formatted with %.0f rather than as.character(): R writes the
double 100000 as "1e+05", and every id Telegram sends is a number.

Every verb is exercised through a .api seam asserting on the wire
shape each method receives, and httr formals are pinned for drift.
@TroyHernandez
TroyHernandez merged commit cf5bb9c into main Sep 7, 2026
2 checks passed
@TroyHernandez
TroyHernandez deleted the telegram branch September 7, 2026 23:36
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