A local-first Telegram bot for personal memory, files, events, reminders, and lightweight relay workflows.
It runs through a local OpenCode server, keeps canonical state in the repository, and treats Telegram as a platform adapter rather than the center of the architecture.
- remember and retrieve personal facts
- organize uploaded files and materials
- create and manage events and automations
- send messages or scheduled deliveries to authorized users or known group chats
- save Telegram-uploaded files into
tmp/for processing; the latest same-name upload replaces the earlier local copy - let the admin manage durable user roles through the bot
The bot is organized as a small layered system: bot runtime, platform adapters, AI, repository CLI, domain operations, and records, with the repository CLI used as the preferred deterministic execution boundary.
Interaction
receive and deliver user messages
|
v
Scheduling
coordinate loops, sessions, and timing
|
+-- Assistant
| one main agent for interpretation + execution
| |
| +--> Repository CLI + Skills
| | |
| | +--> Operations / Records
| | domain logic and canonical state
|
+-- Maintainer
cleanup and repair
|
+--> Operations
The current conversation path is centered on a single assistant lane:
-
bot-side code and repo-CLI code are kept as separate surfaces in
src/bot/**andsrc/cli/** -
the assistant interprets the request and performs needed work directly
-
the current direction is to let runtime code own current-turn reply publication orchestration while actual Telegram delivery stays on shared deterministic CLI-backed paths the assistant mainly uses via repository-local CLI and skills
-
runtime code keeps waiting-state UI, interruption, short startup coalescing / follow-up merge, and duplicate-publication safeguards
-
fixed UI text stays in i18n; natural conversational wording is generated by the model
-
user-facing assistant replies follow the user's natural conversation language, while fixed UI text follows the configured default UI locale
Short-term conversational context is kept in OpenCode sessions by scope:
- private chat -> one session per user
- group / supergroup -> one session per chat
Long-term facts, access roles, events, automations, and structured state do not rely on model session history. They live in repository state such as system/users.json, system/chats.json, system/state.json, and system/events.json. These stores should be managed through deterministic code paths and the repository CLI instead of prompt-defined persistence protocols. The project-wide engineering rules now live in docs/principles.md.
The repository keeps the skill set intentionally small. Repository-specific work should go through CLI + skills:
cli-shared: shared/base guidance for the repository CLI surfacecli-events: events, reminders, and automation workflowscli-telegram: Telegram outbound delivery workflowscli-access: users and authorization workflowsmemory: repository-local durable notes and preferences
cp config.toml.example config.toml
cp .env.example .env
just install
# uses the project-local OpenCode binary via npm, no global install required
just serveFill in at least:
telegram.bot_tokentelegram.admin_user_id
Typical setup:
[telegram]
bot_token = "YOUR_TELEGRAM_BOT_TOKEN"
admin_user_id = 333333333
waiting_message = "Thinking..."
input_merge_window_seconds = 3
menu_page_size = 8
[bot]
language = "zh-CN"
persona_style = "Speak like the Defect from Slay the Spire."
default_timezone = "Asia/Tokyo"
[maintenance]
enabled = true
idle_after_minutes = 15
[opencode]
base_url = "http://127.0.0.1:4096"Useful optional settings:
telegram.menu_page_size: Telegram inline menu page sizetelegram.input_merge_window_seconds: short window for merging follow-up text/files into the same in-flight turntelegram.waiting_message: initial waiting UI text shown immediately; if empty, no initial waiting message is shownbot.language: default UI locale for fixed UI text; choosezh-CNorenbot.default_timezone: fallback timezone used when the user has not explicitly provided onemaintenance.idle_after_minutes: run maintenance after this many idle minutesopencode.base_url: local OpenCode server address
- Every user who should receive direct bot messages must have started a private chat with the bot at least once.
- If you want to use the bot in group chats, open BotFather and turn Group Privacy off for the bot.
allowed user: may chat with the bot and use only basic low-risk features within their own / linked conversation context; they may upload/process temporary files intmp/, but must not access private information outside that scoped context or write durable memorytrusted user: may read and modify memory, upload/process files, create events/automations, and use other persistent workflowsadmin user: trusted user plus admin-only operations such as durable role management and temporary authorization grants
The code currently enforces the allowed-user privacy boundary in the assistant lane: allowed users are constrained to allowed-user scope and must not be given private information outside the linked conversation context.
The admin may also temporarily allow a @username and choose any expiry window. After that, the user only needs to interact with the bot before the temporary authorization expires so the system can link the account and grant access. This can be a private chat, an @bot mention in a group, or a reply to the bot in a group.
Delayed Telegram delivery is delegated to the external at scheduler through the repository CLI rather than an internal durable queue.
- “Remember my passport number.”
- “What is my home address?”
- “Remind me tomorrow at 9am to submit the application.”
- “Send this to @someone: dinner is ready.”
- “Send this to the family group.”
- “Set @someone to trusted.”
/help/new/model(admin only)
npm test
npm run test:nl
npm run test:live
just testjust serve starts the project-local OpenCode server with npm exec -- opencode serve --port 4096 before launching the bot.
The regression suite covers deterministic storage behavior and live natural-language flows, including schedule CRUD, user access-level changes, outbound delivery, persona-aware reply composition, and requester-timezone-aware time injection.