A local-first Telegram bot for personal memory, files, events, reminders, and lightweight relay workflows.
It runs through the Pi SDK, 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, Pi SDK lanes, Pi tools, repository CLI, domain operations, and records. Repository Pi tools are the assistant-facing deterministic execution boundary; they are backed by the existing repository CLI. Recent deepening work has pulled schedule lifecycle concerns behind ScheduleEngine, startup/runtime orchestration behind a bot lifecycle module, and Pi SDK session/resource lifecycle behind internal broker/cache seams.
Interaction
receive and deliver user messages
|
v
Scheduling
coordinate loops, sessions, and timing
|
+-- Assistant lane
| main Pi agent for interpretation + execution
| |
| +--> Pi tools -> Repository CLI
| | |
| | +--> Operations / Records
| | domain logic and canonical state
|
+-- Composer / Maintainer lanes
short no-context text generation and maintenance summaries
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 through Pi tools where deterministic state is involved
-
the current direction is to let runtime code own current-turn reply publication orchestration while deterministic repository actions are exposed to the assistant as Pi tools backed by the repository CLI
-
composer/writer tasks such as startup greetings, reminder wording, and reply composition run as narrow no-tools/no-context Pi sessions
-
maintainer tasks stay narrow and should not accidentally gain current-turn delivery or repository mutation powers
-
runtime code keeps waiting-state UI, interruption, short startup coalescing / follow-up merge, and duplicate-publication safeguards
-
i18n stays minimal and only keeps command / UI-bound text; 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 Pi SDK 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, Pi tools, and the repository CLI instead of prompt-defined persistence protocols. The project-wide engineering rules now live in AGENTS.md and docs/principles.md.
Pi agent resources live under agent/:
agent/.pi/AGENTS.md: main assistant instructions loaded only by assistant/tool sessionsagent/.pi/extensions/defect-bot-tools: Pi tools backed by the repository CLI for events, users/auth/rules, and Telegram deliveryagent/.pi/skills/memory: repository-local durable notes and preferencesagent/.pi/skills/custom-toolbox: narrow project-specific utility workflowsagent/.pi/auth.jsonandagent/.pi/models.json: local credentials/model config, ignored by git
Routine deterministic work should use Pi tools instead of re-reading CLI skill wrappers. just agent opens an interactive Pi session against this workspace for local assistant debugging.
See docs/agent-architecture.md for the assistant/composer/maintainer lanes and resource-loading rules.
cp config.toml.example config.toml
cp .env.example .env
just install
# uses Pi SDK directly; configure model credentials in agent/.pi/auth.json or environment variables
just serve
# optional: open the same agent workspace interactively
just agentFill 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
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 minutes
- 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 launches the bot directly; model/provider access is resolved through the Pi SDK.
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.