Two parallel atoms in one commit. Both reuse existing KeiSeiKit
primitives (zero new crates) per RULE feedback_inventory_before_decompose.
## src/contacts.rs (200 LOC, +4 tests)
Adapter over kei-social-store. Address book + interaction log + relationship
graph for shared connections.
API:
* Contacts::from_path / from_memory
* add_contact / get_contact / search_contacts
* log_meet(person_id, target_id, channel, note) / interactions_for
* relationship_graph — returns Vec<Pair>, the kei-social-store output
* common_connections(a, b) — post-filters relationship_graph to find
target_ids that appear in pairs with BOTH a and b. This is the
"у нас с Денисом общий друг X" feature.
Pattern: Arc<Mutex<kei_social_store::Store>> + tokio::spawn_blocking,
mirroring chat_log.rs. Errors map to BuddyError::Memory.
Tests: add_and_get_contact_roundtrip / search_contacts_finds_by_name /
log_meet_and_list_interactions / common_connections_finds_shared_target.
## src/topics.rs (200 LOC, +4 tests)
Adapter over kei-sage. Topics + digest notes + FTS5 search. Each topic
is a sage Unit{unit_type="buddy_topic", category="kei-buddy",
source_path="kei-buddy/chat-{chat_id}/topic/{slug}"}. Digests are
Unit{unit_type="buddy_digest"} linked via add_edge(topic→digest,
edge_type="digest_for").
API:
* Topics::from_path / from_memory
* add_topic(chat_id, slug, title, content) — idempotent via path lookup
* add_digest(chat_id, topic_slug, timestamp, content) — creates Unit +
edge
* search(query, limit) — fts_search over all kei-buddy units
* digests_for(chat_id, topic_slug) — follows outgoing edges
* list_topics(chat_id) — raw SELECT scoped by source_path LIKE prefix
Tests: add_topic_then_search_finds_it / add_topic_is_idempotent /
add_digest_creates_edge_and_dest / list_topics_scopes_per_chat.
## Dependencies added
kei-social-store + kei-sage as local path deps. Both already in workspace,
no new external crates.
## Verify-before-commit
* cargo check -p kei-buddy: PASS
* cargo test -p kei-buddy --lib: 31/0 (was 23, +4 contacts +4 topics)
Net change: 4 files touched, ~400 LOC added across the two adapters.
NOT deployed. User still in active bot conversation.
|
||
|---|---|---|
| .. | ||
| src | ||
| Cargo.toml | ||
| README.md | ||
kei-buddy
Maturity: concept / scaffold — no business logic yet.
Purpose
kei-buddy is the runtime crate that composes existing KeiSeiKit
primitives (kei-pet, kei-memory-sqlite, kei-cortex,
kei-notify-telegram) into a personal-assistant Telegram bot called
KeiBuddy.
On first contact the bot walks the user through an 11-state onboarding flow: name, tone, interests, hobbies, per-topic decomposition (specifics → now-or-later → research preference → source selection), and digest schedule. After onboarding the bot enters ongoing conversation mode, drawing on the stored persona and memory.
This crate provides the state-machine enum and skeleton driver. The
onboarding FSM is ported from
keisei-marketplace/src/lib/keibuddy/chat-onboard.ts.
Status
Scaffold only. The OnboardState enum and TransitionInput struct are
defined. All transition logic is stubbed (next() returns self.clone()).
The binary entry point prints a placeholder message and exits 0.
Running
Environment variables
| Variable | Required | Default | Description |
|---|---|---|---|
TELEGRAM_BOT_TOKEN |
yes (serve) | — | Bot token from @BotFather |
TELEGRAM_WEBHOOK_SECRET |
yes (serve) | — | Secret token for webhook verification |
KEI_BUDDY_PORT |
no | 8080 |
HTTP port to bind |
KEI_BUDDY_DB_PATH |
no | ./kei-buddy.db |
SQLite database path |
OPENAI_API_KEY |
no | — | Enables OpenAiExtractor when set (requires extractor-openai feature) |
Subcommands
# Apply schema (idempotent; run once before first serve)
kei-buddy migrate
# Register the webhook URL with Telegram
kei-buddy webhook-set https://your-domain.com/webhook
# Start the HTTP server
kei-buddy serve
# Remove the registered webhook (revert to polling)
kei-buddy webhook-delete
Example systemd unit
[Unit]
Description=KeiBuddy Telegram bot
After=network.target
[Service]
EnvironmentFile=/etc/kei-buddy/env
ExecStart=/usr/local/bin/kei-buddy serve
Restart=on-failure
User=keisei
[Install]
WantedBy=multi-user.target
Roadmap
- OpenAiExtractor wiring — pass real OPENAI_API_KEY to OpenAiExtractor in serve.rs when feature enabled.
- Persona binding — read persona manifest via
kei-pet; apply tone overlay to outgoing replies. - Digest scheduling — wire
kei-cron-schedulerfor morning/evening digest delivery.