/start is the only command. It opens the main menu in DM. In groups and
supergroups it is registered as a Bot API 10.2 ephemeral command: Branchy's DM
prompt is visible only to the invoking user. The reply uses Bot API 10.3
ephemeral_message_parameters plus reply_parameters.ephemeral_message_id.
An ordinary non-ephemeral group /start gets no public response. Scope
registration retries after transient Telegram failures. The private reply is
sent before the best-effort presence write so database latency cannot consume
the 15-second ephemeral reply window.
All setup happens through inline keyboards. Unrecognized private-chat text
nudges the user to send /start. Groups stay quiet.
Subscription settings are managed in DM. Group delivery works after:
- The bot is added to a group or supergroup.
- Branchy receives a
my_chat_memberupdate and records the group. - The user selects that group in DM.
- Branchy verifies the user is a group
creatororadministratorwithgetChatMember.
If Telegram cannot verify admin status, Branchy does not save the group destination.
GitHub notifications use Telegram Bot API Rich Messages
(sendRichMessage) with Rich HTML. Branchy parses GitHub Flavored Markdown,
keeps supported headings, lists, tables, quotes, links, code, task lists, and
media, then serializes a strict allowlist. Raw GitHub HTML and non-HTTP(S) URLs
do not pass through directly: safe formatting tags such as underline,
superscript, and subscript are retained, while scripts, Telegram-specific tags
(including tg-* tags; their inner text is kept), unsafe attributes, mentions, and
non-HTTP(S) URLs are removed.
Each notification is a title-first card: an h2 for the event (#7 Title,
N new commits, or the release name), one quiet line for repository, action,
branches, and author, then the body and a single trusted in-message button
row. There is no divider. Push offers Open compare and Copy SHA only
when the payload has a single commit (the full SHA). Pull requests offer
Open pull request plus More (type="callback_data" style="link").
Releases offer Open release and show the tag in the quiet line. Classic
HTML fallback keeps ordinary links and does not include More. Those buttons
are generated by Branchy, not taken from the GitHub body. GFM tables are
emitted as bordered, striped, and compact. Short bodies use Rich HTML
blockquotes; long flat notes use <blockquote expandable> with inline text
and <br> (not nested paragraphs); truncated or structured notes (tables,
headings, lists, media) use a collapsible <details> block. Bodies that hit
Telegram Rich Message limits stay folded there; cards do not add a separate
Read more / Full release notes link. Open pull request / Open release
already go to GitHub.
Tapping More on a pull request sends a separate ephemeral Rich Message to
the tapping user (ephemeral_message_parameters.receiver_user_id plus
callback_query_id). The public card is not replaced. The overlay is a thin
header (#N Title, head → base, draft/merged) plus non-zero diff stats and
a compact file table (File / + / −) loaded live from GitHub
GET /repos/{owner}/{repo}/pulls/{n}/files using the subscription owner's
OAuth token, not the tapping user. Description, labels, and reviewers are not
repeated. URL buttons are Open and Files. If GitHub has not computed the
diff yet, the overlay says so and still offers Files. A failed files fetch
still sends the header and snapshot stats, and toasts Could not load the file list. (or GitHub access expired. on 401). Callback data is m: plus
the job UUID without hyphens (34 bytes). Lookup is by job id and
destination_chat_id; missing, foreign-chat, or retained-and-purged jobs
toast This snapshot expired. Anyone who can see the card can open More.
Test notifications stay a short card without More.
The first 50 valid HTTP(S) media items remain visible Rich Message blocks, including linked Markdown images and supported raw image, video, and audio tags; later items remain available as links. Rendering also enforces Telegram's body, block, nesting, and 20-column table limits while keeping tags balanced and removing implicit HTML5 table wrappers unsupported by Telegram.
Each outbox job stores a bounded classic HTML fallback. If Telegram rejects the
preferred payload with a content, permission, size, or method error, Branchy
tries Rich HTML with media replaced by links, then classic HTML, then plain
text. Each attempt gets a fresh timeout. Rate limits, server failures, transport
errors, and unreachable chats keep their normal retry or auto-pause behavior
instead of falling through and risking duplicates. Pending alpha Rich Markdown
jobs are sanitized into Rich HTML before delivery. Bot UI (/start, settings,
OAuth replies) still uses classic HTML sendMessage / editMessageText.
Message content stays concise and includes:
- repository
- event type
- actor
- branch
- title or summary
- GitHub link
- optional PR/release body
Subscription creation collects event-specific settings before saving:
pushandpull_requestcan use all branches, the default branch, or a selected set of branches.pull_requestcan deliver opened, merged, closed, or any combination of those actions.releasecan deliver stable releases, pre-releases, or both.
Release-only subscriptions do not ask for branch settings. Existing
subscriptions expose branch, pull request, and release controls under
Advanced settings; test notifications stay on the individual subscription
screen and use the same Rich HTML card chrome as live events. Incomplete
Continue, Create, Save, and Done actions, plus pagination edges and the current
all/default branch or release radio, stay on screen as Bot API 10.3 disabled
buttons. The selected-branch radio stays tappable so the user can open the
branch list.