Skip to content

Latest commit

 

History

History
242 lines (192 loc) · 10.9 KB

File metadata and controls

242 lines (192 loc) · 10.9 KB

Telegram setup

Blacki connects to Telegram with long polling. It does not implement webhook mode, so you do not need a public domain, TLS certificate, or inbound Telegram port.

1. Create a bot

  1. Open the verified BotFather account in Telegram.
  2. Send /newbot.
  3. Choose a display name and a username ending in bot.
  4. Copy the token.

Treat the token like a password. Anyone with it can control the bot.

2. Configure Blacki

In .env:

TELEGRAM_ENABLED=true
TELEGRAM_BOT_TOKEN=replace-me
# Optional: restrict new private chats with one shared access code.
# TELEGRAM_ACCESS_CODE=replace-with-a-dedicated-high-entropy-code

Replace replace-me with the token from BotFather. Blacki validates that the token is present and follows Telegram's number:string format at startup.

When TELEGRAM_ACCESS_CODE is set, new users must send /start <access-code> in a private chat before Blacki processes their messages. The bot consumes this command locally and attempts to delete it, but Telegram may retain it in server-side history or backups, so use a dedicated code rather than a password you use elsewhere. Historical private chats with persisted Blacki sessions are grandfathered; group chats and forum topics are rejected. Changing the access code requires passphrase-authorized users to authenticate again but does not delete any chat history, preferences, reminders, files, or health data.

At least one model provider must also be configured. See Configuration.

Optional voice-note transcription

To let Blacki turn Telegram's native voice-note button into a normal text conversation, add a Cloudflare Workers AI account ID and API token:

CLOUDFLARE_ACCOUNT_ID=replace-me
CLOUDFLARE_API_TOKEN=replace-me

The token should have Workers AI Read and Write permissions. Blacki sends voice notes to Cloudflare's hosted @cf/openai/whisper-large-v3-turbo model, then passes the transcript through the existing Telegram text-turn path so conversation history and tool confirmations continue to work. Voice notes are kept transiently in memory; regular audio files continue to use the existing file-upload behavior. If the credentials are absent, Blacki reports that voice transcription is not configured instead of failing startup. The raw voice bytes are transient; the resulting transcript is handled like ordinary Telegram text and follows the existing conversation-history and logging/privacy settings.

Optional Kokoro speech replies

To let the Telegram-only root agent turn text into playable MP3 audio, add:

KOKORO_TTS_BASE_URL=http://100.x.y.z:8880
KOKORO_TTS_VOICE=af_heart

Use the Tailscale IP or MagicDNS name that is reachable from the Blacki container. localhost refers to the Blacki container itself, not a Kokoro server on another machine. Blacki calls Kokoro's OpenAI-compatible /v1/audio/speech endpoint, keeps the bounded MP3 in memory, and uploads it to the current Telegram chat or topic. Synthesis and upload are serialized so only one audio payload is retained at a time. The tool is not registered on the public ADK HTTP runner or delegated task worker.

When this private tool is configured, Blacki disables content-rich ADK and OpenInference logging. Tool notifications may show that speech synthesis is running, but never include the text being spoken. ADK session history still retains the model's tool call and arguments.

Optional Connect Google Health

Blacki can read normalized health summaries after a user completes Google OAuth from a private Telegram chat. If the user grants both nutrition permissions, Blacki also queues existing meals from that private chat once for the connected Google account, then exports future meal logs, edits, and deletions. This is intentionally named Connect Google Health: Blacki does not request Apple ID credentials, access HealthKit, scrape Fitbit, or receive arbitrary Apple Health records. The user must first configure an Apple Health-to-Google Health/Fitbit-compatible import path if their account and app version support it.

Configure the Google Cloud OAuth web client and the GOOGLE_HEALTH_* values in Configuration, then set the callback URL to the exact public HTTPS URL. In Telegram:

  1. Send /connect_health in a private chat.
  2. Open the one-time Google authorization link. Grant the read-only categories for summaries. Grant both googlehealth.nutrition.readonly and googlehealth.nutrition.writeonly if you want future meal export. Existing connections must reconnect to add these nutrition permissions. The nutrition read permission lets Blacki verify records it created by exact data point ID; it does not import unrelated food logs.
  3. Return to Telegram and use /health_refresh for an on-demand sync or /health_summary for the latest stored records.
  4. Log meals normally. Existing eligible meals are queued once after the connection is saved. New meals continue through the background export worker. A local Blacki save remains successful when remote sync is pending or fails, and the meal must not be logged again. Ask Blacki for meal sync status when you want to check the queue, or ask it to retry failed exports.
  5. Use /disconnect_health, then confirm the button, to revoke the token best-effort, cancel pending meal sync, and remove Blacki's stored token and normalized health summaries. Local calorie logs remain. Blacki does not delete records already sent to Google Health, and requests already submitted may still finish.

The background sync runs every 12 hours by default and fetches a bounded recent window so late device imports can replace earlier daily records. Missing values are omitted rather than guessed. Stored data is limited to normalized daily activity, workout, sleep, heart-rate, weight, and body-fat summaries; raw Google payloads and provider IDs are not persisted in the summary table. Meal exports are persisted separately with retry state and opaque data point IDs. A one-time per-account backfill queues existing local meals with a durable cursor; the meal export worker runs every minute independently of health imports. Run only one active scheduler process per tools.db so a deployment does not dispatch duplicate work.

Google's v4 discovery document currently lists nutrition-log as a supported data type. Blacki writes only the local meal description, kcal, available macros, meal type, and selected local date; it omits unknown nutrients and does not substitute food-database estimates. See Google's nutrition data type and data point REST reference for the provider contract.

Google Health availability does not prove that a particular Apple Health metric was imported. Test the desired categories on a non-production account before promising steps, workouts, sleep, or heart-rate coverage to users.

3. Start or recreate the service

Docker Compose:

docker compose -f compose.yaml -f compose.prod.yaml up --build -d
docker compose -f compose.yaml -f compose.prod.yaml logs --tail=100 agent

Local Python:

uv run python -m blacki.server

Look for startup logs confirming that the Telegram configuration was detected and polling started.

4. Verify

Open the bot in Telegram and send:

Command Behavior
/start Show the welcome message
/help Show the supported command summary
/model Open the model and thinking settings panel
/thinking Open the supported reasoning-effort choices for the active model
/reset Start a fresh conversation session
/connect_health Send a Google Health authorization link and consent for optional meal sync
/health_refresh Fetch recent Google Health data (rate limited)
/health_summary Show normalized daily records and trends
/disconnect_health Confirm disconnection and cancellation of meal sync

Then send a normal message and confirm the model responds. Blacki does not currently implement a /clear command.

If Kokoro speech is configured, ask the bot to “send that as audio” and confirm Telegram receives an MP3 in its native audio player. Kokoro failures return a text error and do not create a local audio file.

The /model panel stores one profile for the Telegram chat. Choose a model, then select a thinking effort supported by that model. Default uses ROOT_AGENT_REASONING_EFFORT when configured and otherwise leaves the provider's reasoning setting unchanged. Off is shown only when the model advertises that it can disable reasoning. Changes apply to the next turn and do not change existing conversation history. Changing models resets the chat's explicit thinking choice to Default, avoiding an unsupported setting being carried to a different model. /thinking is a shortcut to the same capability-aware menu.

To verify image input, select a vision-capable model, send a Telegram photo, and optionally add a caption as the instruction. Without a caption, Blacki asks the model to describe the image. Native photo input is limited to 10 MB; documents, audio, video, and voice messages continue to use the sandbox upload path. When an image is sent as a Telegram file/document, Blacki exposes its /workspace/uploads/... path to the agent and the agent can call sandbox_view_image to attach it as a visual input. Call the tool once per image when several files are present; each image remains a separate model input rather than being combined into a collage. The tool accepts a path below /workspace, validates common PNG, JPEG, GIF, WebP, and BMP files, and is read-only. A model that does not support images will return the normal photo processing error without changing the selected model.

Tool notifications

Telegram turns automatically show one live-updating tool status message while tools run. The status is updated in place and ends with the elapsed working time, so tool names and arguments are not posted as separate chat messages.

Security

  • Never paste the token into an issue, log, command history, or committed file.
  • Keep .env mode 600.
  • If the token is exposed, revoke and regenerate it through BotFather.
  • Group privacy settings are controlled through BotFather; review them before adding the bot to a group.

Troubleshooting

If the bot does not respond:

docker compose -f compose.yaml -f compose.prod.yaml ps
docker compose -f compose.yaml -f compose.prod.yaml logs --tail=200 agent

Check that:

  • TELEGRAM_ENABLED=true;
  • the token contains no quotes or trailing whitespace;
  • one real model API key is active;
  • the model identifier matches that provider; and
  • the VPS can make outbound HTTPS requests.

See Troubleshooting for startup and model failures.