# Telegram Rooms, Reminders, Notifications, and Voice

Telegram offers compact versions of Oshun's member rooms plus continuity
controls that are natural in chat: resume, save, reminders, quiet hours,
delivery stop/resume, time zone, voice notes, and spoken responses. Every read
and write travels through a typed port so the bot cannot claim state it did not
read or persist.

```mermaid
sequenceDiagram
  actor Member
  participant Telegram
  participant Webhook as Oshun BFF webhook
  participant Port as Room reminder or effects port
  participant Scheduler as Fenced delivery cycle
  participant BotAPI as Telegram Bot API
  Member->>Telegram: Room command reminder or voice note
  Telegram->>Webhook: Verified retried update
  Webhook->>Webhook: Durable claim scope safety and local-time checks
  Webhook->>Port: Typed read write or STT-grounded request
  Port-->>Webhook: Ready applied unavailable denied or failed outcome
  Webhook->>BotAPI: Response calls with keyboard or media
  BotAPI-->>Webhook: Application result and message id
  Webhook-->>Telegram: Acknowledge update and record receipt
  Telegram-->>Member: Honest room or action result
  opt reminder becomes due later
    Scheduler->>Port: Claim due reminder and re-check member policy
    Port-->>Scheduler: Deliverable suppressed blocked or retryable
    Scheduler->>BotAPI: Fenced reminder delivery
    BotAPI-->>Scheduler: Receipt retry-after or permanent failure
  end
```

Creation and scheduled delivery are separate promises. The scheduler must
re-check current binding, stop/quiet state, time zone, policy, and provider
result before it marks a reminder delivered.

## Room port

The Telegram rooms contract exposes:

| Command          | Domain content           | Typical actions                                                                  |
| ---------------- | ------------------------ | -------------------------------------------------------------------------------- |
| `/sit`, `/today` | Tara ritual              | Pace breath cues, advance/complete ritual, optionally schedule another reminder. |
| `/checkin`       | Arete check-in           | Present humane habit/mood state and record a permitted check-in.                 |
| `/tonight`       | Nyx sky window           | Resolve member time/location context and show the relevant observation window.   |
| `/passage`       | Nisaba passage           | Present passage/source and offer save/share/deep-link actions.                   |
| `/library`       | Member library           | List open items without exposing another member's state.                         |
| `/continue`      | Continue point           | Resume the latest supported room/activity.                                       |
| `/streak`        | Streak/recovery summary  | Show current state using non-punitive recovery language.                         |
| `/settings`      | Telegram/member settings | Show time zone, quiet hours, voice, and delivery state.                          |

Reads return structured ready/unavailable/forbidden/missing-context outcomes.
The BFF rooms adapter connects these contracts to member-domain services or
returns explicit not-connected copy. Group scope forbids personal room reads;
shared passage and question content follow their separate rules.

## Grounded question room

`/ask <question>` invokes the Sophia grounder with actor/chat context and
bounded question text. It returns answer, citations/evidence, provenance state,
or abstention. A response is rendered with disclosure and source buttons where
available. See
[Safety, privacy, grounding, and rights](./safety-privacy-grounding-and-rights.md).

## Save and preferences effects

The effects port owns actual mutations:

- save a note/link/reference;
- change quiet-hours or delivery preferences;
- stop and resume Telegram delivery;
- change voice preference;
- unlink the Telegram account;
- set member time zone.

Each operation returns an applied/duplicate/invalid/denied/unavailable/failed
outcome as applicable. The bot only uses success copy after an acknowledged
effect. Idempotency uses update/callback/request identity so Telegram retrying
an update cannot duplicate a note or flip a preference twice.

## Time zone and local time

Telegram does not provide a member's time zone. The bot resolves it from the
durable member profile when available or asks for `/timezone`. It validates IANA
zone names and can describe limitations of a fixed offset where supported.

All “today,” “tonight,” quiet-hours, and absolute reminder calculations use the
member zone—not the container clock. Confirmations include local time and zone
so an apparently valid instant is not misleading. DST gaps/overlaps, invalid
zones, changed zones, and stale member profile state have explicit tests.

Location needed for Nyx is a separate permission/context fact; a time zone does
not imply precise location.

## Reminder creation

`/remind` and supported natural phrasing parse a bounded vocabulary such as a
relative duration, time today/tomorrow, or a clock time in the member's zone.
The parser produces a typed `ReminderRequest`; ambiguous or unsupported text
returns “time not understood” rather than guessing.

The reminders port schedules against the durable reminder service with member,
chat/binding, requested local expression, resolved instant/zone, content/room
reference, idempotency, and policy context. The confirmation repeats the actual
resolved time. Maximum relative windows and reminder limits are enforced.

## Scheduled delivery cycle

Creating a reminder is not delivering one. A deployed scheduled cycle must:

1. Claim due reminders with lease/fencing and a bounded batch.
2. Re-check binding, opt-in, stop state, quiet hours, rights/policy, and current
   member time zone/delivery context.
3. Build the Telegram response and inline action keyboard.
4. Call the real Bot API and inspect the application response.
5. Mark delivered only after acceptance; record retryable/permanent/blocked
   failure separately.
6. Use bounded backoff without losing a transient failure or delivering twice.
7. Release/recover stale claims and expose backlog/age/attempt metrics.

Deployment must run exactly one logically fenced cycle per due item even when
multiple tasks/processes are active. The promise-kept and delivery-stop tests in
the BFF pin this boundary.

## Reminder actions

Delivered reminders can carry namespaced callbacks for completion/dismissal and
supported snooze choices (the shared contract currently defines short and
hour-scale options). The action re-authorizes the member/chat/reminder,
deduplicates callbacks, rejects expired/foreign state, persists the outcome, and
answers the callback query.

Snooze creates/updates the durable schedule with an exact new instant. “Done”
and “dismissed” remain distinguishable where domain behavior needs it. A local
button acknowledgement without the reminder-port result is not completion.

## Quiet hours, stop, and resume

Quiet hours suppress ordinary scheduled delivery according to the member zone
and policy, with a declared next eligible time. They do not silently discard the
reminder. Crisis and critical transactional policy is evaluated by the channel
dispatcher, not by bypassing preferences ad hoc.

`/stop` is a durable delivery control and must stop both scheduled and ordinary
eligible outbound sends after acknowledgement. `/resume` is the explicit
recovery. Blocking the bot at Telegram is also observed as stopped/blocked
delivery but cannot be reversed from Oshun; the member must unblock/reopen the
chat.

## Notifications beyond reminders

The shared dispatcher can route allowed member/curated intent classes such as
session notifications, ritual reminders, grounded content, and transactional
receipts to Telegram when consent, residency, tier, capability, quiet-hour,
frequency, crisis, and tenant policy allow it. AAA and operator traffic never
routes to messaging.

Every outbound notification carries content class, disclosure/provenance,
member/binding, correlation/idempotency, and delivery state. A scheduled task or
event producer must be deployed; a renderer and dispatcher alone do not create
notifications.

## Voice notes and speech-to-text

For a Telegram voice message, the runtime:

1. validates update/chat/member scope and rate/safety policy;
2. resolves Telegram file metadata/download under size/type/time limits;
3. sends recording/typing presence as appropriate;
4. invokes the configured STT provider with bounded audio and declared locale;
5. treats the transcript as untrusted member input and routes it through the
   same command/question/safety logic;
6. records provider/version and failure without storing raw audio/transcript as
   durable memory unless policy and consent permit.

Missing STT configuration returns `stt_not_configured` behavior; it never
pretends to understand audio. Provider timeout, invalid media, empty/low-quality
transcript, oversized file, and download failure are explicit.

## Text-to-speech

When voice response is enabled and a TTS provider is configured, response text
is bounded and normalized for speech, synthesized, delivered as Telegram voice
media, and linked to the same response/audit. Long evidence details remain in
text when speech trimming would omit meaning. Missing/failing TTS degrades to
text with honest state rather than dropping the reply.

Voice preference is member-specific and private. Voice synthesis does not
impersonate a person or obscure the Lilith/system disclosure.

## Verification matrix

- Room read success, unavailable, forbidden, missing location/time zone, and
  stale/partial responses.
- Personal-room commands blocked in groups; passage/ask shared behavior.
- Save/preference/timezone/stop/resume/unlink applied, invalid, duplicate,
  denied, unavailable, and failed outcomes.
- Reminder parsing across relative/absolute, locale/zone, DST, ambiguous,
  unsupported, maximum, and past times.
- Scheduler fencing, due selection, quiet hours, opt-in/stop, transient retry,
  permanent/blocked failure, duplicate cycle, callback actions, and backlog.
- STT download/type/size/provider/transcript/safety paths and TTS configured/
  absent/failure/length paths.
- Webhook-to-Bot-API browser/integration evidence that a real effect/delivery is
  reached, not only response copy.

Primary modules are `rooms.ts`, `room-commands.ts`, `reminders.ts`,
`reminder-requests.ts`, `effects.ts`, `stt-provider.ts`, and `tts-provider.ts`
under `libs/oshun/messaging-channels/src/telegram`, with BFF adapters under
`apps/oshun/bff/src/telegram`.
