84 lines
6.8 KiB
Markdown
84 lines
6.8 KiB
Markdown
# 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](https://t.me/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:
|
||
|
||
```bash
|
||
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.
|
||
|
||
5. Start the service:
|
||
|
||
```bash
|
||
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+:
|
||
|
||
```bash
|
||
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.
|