# Telegram Bot, Commands, and Conversation

The Lilith bot turns Telegram updates into policy-checked responses and real
effects. The supported live inbound path is the Oshun BFF webhook; the shared
Telegram library contains the pure handler, command registry, delivery
translation, keyboards, limits, and provider seams.

## Entry and discoverability

At startup/deployment, command-surface registration makes capabilities visible:

- `setMyCommands` publishes the full member list to private chats;
- a reduced list is published to group chats;
- `setChatMenuButton` opens **Open Lilith** as a Web App when a Mini App URL is
  configured, otherwise it opens the command list;
- `t.me/<bot>?start=<payload>` links map a closed payload vocabulary to rooms or
  the greeting.

Registration failures are reported but do not stop message handling. Operations
must still alert: a bot that answers only commands users cannot discover is a
functional outage.

## Registered private-chat commands

The canonical menu in `deep-links.ts` registers:

| Command     | Member job                                                |
| ----------- | --------------------------------------------------------- |
| `/sit`      | Start a paced Tara ritual in chat.                        |
| `/checkin`  | Open the member's Arete check-in.                         |
| `/tonight`  | Show the Nyx sky window for the member's location/time.   |
| `/passage`  | Read the current Nisaba passage.                          |
| `/ask`      | Ask Sophia for a grounded answer or an honest abstention. |
| `/remind`   | Create a reminder from a supported time expression.       |
| `/continue` | Resume the member's latest supported activity.            |
| `/library`  | List the member's open items.                             |
| `/streak`   | Show streak/recovery state.                               |
| `/save`     | Capture a note or link through the effects port.          |
| `/timezone` | Set the member's IANA time zone or supported offset.      |
| `/settings` | Show quiet hours, voice, delivery, and clock settings.    |
| `/upgrade`  | Begin the configured upgrade/payment path.                |
| `/help`     | Explain the available command surface.                    |

The handler also recognizes supporting controls and compatibility commands:
`/start`, `/menu`, legacy `/today`, `/sources`, `/quiet`, `/stop`, `/resume`,
`/voice`, and `/unlink`. Some are contextual/help actions rather than menu
entries. A command being recognized does not mean its effect adapter or product
room is configured; unavailable paths say so.

## Group-chat boundary

Personal commands are not allowed to reveal or mutate one member's state in a
group/supergroup. The runtime—not only the menu—enforces the private-only set,
including sitting/check-in/tonight/library/continue/streak/settings/reminders/
save/preferences/timezone/today/upgrade controls.

`/passage` and `/ask` can be shared because their content belongs to the shared
passage or question, not a member profile. Buttons that need a Telegram Web App
are private-chat-only; group messages use a safe deep-link fallback when a known
mapping exists. Channel posts are not bot conversations.

Commands addressed as `/command@botusername` are normalized for Telegram group
behavior. Another bot's addressed command must not be claimed. Edited messages
and non-message update kinds are classified deliberately so a write is not
silently repeated.

## Update kinds

The handler classifies text messages, voice messages, callback queries, inline
queries, pre-checkout queries, successful payments, membership changes, and
other supported Telegram update shapes. Each kind has an explicit claim/dedupe
and response/delivery path. Unsupported or irrelevant updates produce no effect
rather than a misleading reply.

Chat/member updates detect blocked/stopped state without trying to message a
user who just blocked the bot. The delivery layer also recognizes Telegram's
blocked-user descriptions and transitions delivery state accordingly.

## Command execution model

```mermaid
flowchart LR
  U[Verified, claimed update] --> K[Classify chat/update/command]
  K --> G[Scope, rate, safety, delivery-state guards]
  G --> H[Pure handler + room/grounder/effect ports]
  H --> R[BotResponse list]
  R --> P[Bot API call plan]
  P --> D[Deliver and inspect Telegram result]
  D --> A[Receipt/state/audit]
```

The handler separates reads and writes through ports. A room read can return
ready, unavailable, forbidden, or missing-location/context states. A write can
return applied, duplicate/idempotent, denied, invalid, unavailable, or failed.
Copy reflects the outcome; no port means “not connected,” never “saved.”

## Text and rendering

Responses preserve disclosure and provenance for grounded content, escape or
choose parse behavior deliberately, build supported inline keyboards, and
degrade rich content only where the simpler form retains meaning.

Telegram text is bounded. Long output is split on safe boundaries into multiple
calls rather than sent as one rejected payload. Callback-answer text, callback
data, inline-result ids/counts, and start payloads have their own byte/length
limits. Tests pin these limits and multibyte behavior.

## Keyboards and callbacks

Inline buttons can encode namespaced callback data, URLs/deep links, and Web App
buttons. Callback data is parsed as a closed structure and bounded by byte
length. Unknown, expired, malformed, unauthorized, or duplicate callbacks are
answered safely and do not invoke arbitrary actions.

Callbacks support contextual save, reminder delivery/snooze/done, ritual cues,
room actions, and upgrade/payment affordances where configured. The handler
answers the callback query so Telegram removes the loading indicator, then
executes or reports the actual effect.

## Presence and media delivery

The runtime can emit `typing`, `record_voice`, or `upload_voice` chat actions
for update/response work. Presence is best effort; failure never becomes proof
the underlying command failed or succeeded.

Delivery translates a response into one or more Bot API calls for text and
declared media/file/voice forms, resolves upload/download data where required,
and records each outcome. A null/invalid response does not create a provider
call. Partial multi-message delivery is reported as partial, not success.

## Rate and abuse controls

Rate limiting keys actor/chat/update scope and policy, returns retry
information, and runs before expensive grounding/provider work. Inline mode has
additional abuse detection. Limits cannot be bypassed by command casing, bot
addressing, callbacks, duplicate update delivery, or alternating update shapes.

Rate-limit and abuse copy avoids exposing internal thresholds or cross-user
state. Crisis handling remains available under its dedicated policy and does not
turn rate bypass into general command access.

## Failure behavior

- Missing bot token/webhook secret or invalid request fails closed at the edge.
- Duplicate or already-claimed update does not repeat the write.
- Personal command in a group redirects the member to private chat without
  revealing state.
- Unwired room, reminder, grounder, effect, upgrade, STT/TTS, or provider path
  says unavailable or abstains.
- Provider application-level `{ok:false}` is a failure even with HTTP 200.
- Telegram timeouts/5xx/rate limits use bounded retry policy and idempotency;
  malformed/forbidden/blocked failures do not retry indefinitely.
- Long replies split; invalid buttons/media are withheld or safely degraded.
- `/stop` halts delivery and `/resume` provides an explicit recovery path where
  the durable state adapter is available.

## Source and tests

Primary implementation is under
`libs/oshun/messaging-channels/src/telegram/{bot,command-surface,room-commands,deep-links,keyboard,deliver,bot-api-limits,rate-limit}.ts`.
Live composition is under `apps/oshun/bff/src/routes/telegram.ts` and
`apps/oshun/bff/src/telegram`.

Contract, handler, group-scope, Bot API limit/conformance, delivery, webhook,
dedupe/claiming, effect wiring, blocked-user, and deployment-parity tests live
next to those modules and in BFF tests. The user journey is documented in
[Telegram assistant delivery](../../../WALKTHROUGH/journeys/telegram-bot-assistant-delivery.md).
