2026-07-24 22:36:04 +03:00
2026-07-24 22:36:04 +03:00
2026-07-24 22:36:04 +03:00
2026-07-24 22:36:04 +03:00
2026-07-24 22:36:04 +03:00
2026-07-24 22:36:04 +03:00
2026-07-24 22:36:04 +03:00
2026-07-24 22:36:04 +03:00
2026-07-24 22:36:04 +03:00
2026-07-24 22:36:04 +03:00
2026-07-24 22:36:04 +03:00

Relay Bot

Relay Bot is a self-hosted Telegram assistant for a small software team. It keeps only messages received after it is connected, reads one Git repository in read-only mode, and answers short, source-grounded questions. It is intentionally not an architect, code-review system, task manager, or replacement for a senior engineer.

MVP boundaries

The bot uses Telegram long polling and PostgreSQL. It does not retrieve old group history (Telegram Bot API does not expose it), create commits/PRs, index embeddings, download random group files, or turn chat/repository content into instructions. Webhooks, automatic archive extraction, and rich document workflows are deliberately outside this MVP.

Setup

  1. Create a bot with @BotFather, copy its token, and disable Privacy Mode (or make it a group administrator) so it receives ordinary group messages.
  2. Add the bot to the project group. Obtain your numeric Telegram user ID and the group chat ID (they are not usernames). Set the owner as OWNER_TELEGRAM_ID and the group as PROJECT_GROUP_ID.
  3. Create a DeepSeek API key and set DEEPSEEK_API_KEY. The API is used through its OpenAI-compatible endpoint.
  4. Copy configuration and fill the required values:
cp .env.example .env

REPOSITORY_URL is optional: it provides an initial fallback repository. The recommended flow is to send the owner message посмотри этот репозиторий: https://github.com/acme/project. Relay Bot attaches that repository to the current chat/topic. For a private HTTPS repository, set the token separately; it is injected only for the Git operation, is never persisted in the database or logged, and is not sent to the model.

  1. Start the service:
docker compose up --build

The container runs alembic upgrade head before polling. PostgreSQL uses a persistent Docker volume and the bot waits for its health check.

For local checks, use Python 3.12+:

python -m pip install -e '.[dev]'
pytest
ruff check .
ruff format --check .

Configuration

Required secrets and endpoints are kept only in environment variables:

Variable Purpose
TELEGRAM_BOT_TOKEN BotFather token
DEEPSEEK_API_KEY, DEEPSEEK_BASE_URL, DEEPSEEK_MODEL DeepSeek client configuration
DATABASE_URL async SQLAlchemy PostgreSQL URL
REPOSITORY_URL, REPOSITORY_BRANCH optional initial read-only project repository
REPOSITORY_ACCESS_TOKEN optional private HTTPS repository credential
OWNER_TELEGRAM_ID, PROJECT_GROUP_ID numeric authorization allowlist
BOT_TIMEZONE, LOG_LEVEL operation settings

Additional limits in .env.example cap repository file size, attachment size, context, summary trigger, confidence, and proactive cooldown.

Commands

Everyone in the configured project group or the owner’s private chat can use /start, /help, /ask <question>, /context, /status, /forget_me, and /cancel. The owner can attach a repository in natural language: посмотри этот репозиторий: https://github.com/acme/project, or explicitly with /repo <URL> (the /syncrepo <URL> alias is also supported). /sync_repo only updates an already attached repository.

Only the numeric owner ID can use /set_prompt, /show_prompt, /prompt_history, /rollback_prompt, /summarize, /sync_repo, /repo_status, /proactive off|mentions|safe|active, /memory_status, and /settings. /set_prompt opens a one-message private-chat flow. The owner’s prompt augments, never replaces, the immutable safety prompt.

Memory and answers

Each incoming message is saved separately with chat, topic/thread, user, reply, edit, type, and attachment metadata. Group, owner-DM, participant-DM, and forum topics remain separate by chat/topic keys. Old originals are retained after a versioned summary is made. A summary is generated per chat/topic on /summarize; the service also exposes an automatic token threshold for scheduling during normal operation.

Answers receive: immutable safety rules, active owner metaprompt, the latest summary, recent messages, and limited repository evidence. A dynamically attached repository is stored per chat/topic and cloned with Git partial-clone filtering (blob:none): the bot receives the Git tree and history first, then fetches the content of only a file it explicitly reads. Repository research uses a bounded tool-selection pass: directory map, filename search, safe ranged read, commit log, diff/stat, and file information. It never sends whole repositories. Answers should name an exact path or commit; absence of evidence is reported as needing a human decision.

Proactivity

Default safe mode uses a cheap classification step and may reply without a mention only when a project question is factual, non-rhetorical, high-confidence, not a new decision, and not suppressed by duplicate/cooldown protection. mentions requires an explicit mention, off suppresses proactive replies, and active is reserved for extra blocker/contradiction detection. The bot does not routinely interrupt unknown questions; questions requiring a new design or management choice are left to the owner.

Files, privacy, and safety

Group documents are ignored unless the bot is explicitly mentioned in the caption, the user replies with an explicit read request, or a future explicit command invokes processing. Files sent in a private bot chat are eligible. Supported MVP formats are .txt, .md, .json, .yaml/.yml, .csv, text-extractable .pdf, and .docx; executables and archives are rejected. Size and extracted-text limits apply. File contents are untrusted data, never policy instructions.

Only the configured group and the owner private chat are accepted; unknown groups are ignored. Repository paths are contained under the clone, sensitive names/extensions and common generated/vendor directories are excluded, and subprocesses use argument lists rather than shell interpolation. Logs contain operational metadata, not tokens, keys, full private messages, or attachment content. /forget_me deletes a user’s individual message records where permitted; project summaries may retain an aggregate historical fact.

Common problems

  • Bot sees only commands: disable Privacy Mode or grant appropriate group administrator access, then re-add/restart the bot.
  • No group response: check the numeric negative PROJECT_GROUP_ID, the selected proactive mode, confidence threshold, and cooldown.
  • Repository sync fails: verify URL, branch, and private HTTPS token; do not put the token in the URL.
  • Model unavailable: verify DeepSeek URL/key/model and retry; temporary API failures are retried with exponential backoff.
  • Database connection fails: wait for the Compose health check and keep DATABASE_URL pointed at postgres from inside Docker.
S
Description
No description provided
Readme
58 KiB
Languages
Python 99.2%
Dockerfile 0.4%
Mako 0.4%