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.
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.
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:
- Claim due reminders with lease/fencing and a bounded batch.
- Re-check binding, opt-in, stop state, quiet hours, rights/policy, and current member time zone/delivery context.
- Build the Telegram response and inline action keyboard.
- Call the real Bot API and inspect the application response.
- Mark delivered only after acceptance; record retryable/permanent/blocked failure separately.
- Use bounded backoff without losing a transient failure or delivering twice.
- 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:
- validates update/chat/member scope and rate/safety policy;
- resolves Telegram file metadata/download under size/type/time limits;
- sends recording/typing presence as appropriate;
- invokes the configured STT provider with bounded audio and declared locale;
- treats the transcript as untrusted member input and routes it through the same command/question/safety logic;
- 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.