# Frappe Telegram AI

A Telegram bot that answers questions from your Frappe / ERPNext data.
Each person logs in with **their own Frappe account**, and the bot only shows what **their Frappe role** allows.

```text
Telegram user
     │  Frappe username + password (asked by the bot)
     ▼
Bot backend ── POST /api/method/login ──► Frappe checks the credentials
     │                                        │
     │◄──────────── session id (sid) ─────────┘
     ▼
sid encrypted and saved against the Telegram user id
     │
     ▼
Question ──► AI picks Frappe lookups ──► Frappe REST API (as that user) ──► AI writes the answer
```

- Frappe decides every permission. There are **no users, roles or permission lists in `config.yaml`**.
- The password is used once for the login call and never stored. The password message is deleted from the chat.
- The Frappe session id is stored **encrypted** and is re-checked with Frappe every few minutes.
- The AI never receives passwords or session ids, only the data the user is allowed to see.
- Read-only: the bot cannot create, change, submit or delete records.

How roles and permissions work: [docs/ACCESS_CONTROL.md](docs/ACCESS_CONTROL.md)

## 1. Install

Python 3.10 or newer.

```bash
python -m venv .venv
# Windows:  .venv\Scripts\activate
# Linux/Mac: source .venv/bin/activate
pip install -r requirements.txt
```

## 2. Configure `config/config.yaml`

Every setting is in `config/config.yaml` (a clean copy is in `config/config.example.yaml`). Fill in:

| Setting | What to put |
|---|---|
| `telegram.bot_token` | Token from **@BotFather** in Telegram |
| `frappe.url` | Your site, e.g. `https://erp.example.com` |
| `frappe.api.*` | Frappe REST endpoints. Defaults work for Frappe v14/v15; change only if needed |
| `ai.provider` | `openrouter`, `openai`, `groq`, `gemini`, `ollama` or `custom` |
| `ai.api_key` | Your AI provider key (not needed for `ollama`) |
| `ai.model` | A model that supports tool calling, e.g. `openai/gpt-4o-mini` on OpenRouter |
| `ai.base_url` | Leave empty to use the provider default. Required for `custom` |
| `security.encryption_key` | Run `python main.py genkey` and paste the output |

The other settings (limits, lockout, session check time) have sensible defaults and are explained in the file.

Check everything:

```bash
python main.py check
```

It checks the file, then tests Frappe, the Telegram token and the AI service.

## 3. Frappe side

- The REST API must be reachable from the machine running the bot.
- Each person needs a normal Frappe user with a password.
- Accounts with two-factor authentication cannot log in through the bot.

## 4. Start

```bash
python main.py
```

The bot uses long polling, so no public URL is needed.

## 5. Use

Open the bot in a **private chat**:

1. Send `/start`. The bot asks for your Frappe username, then your password.
2. Ask questions in normal language:
   - `Show my last 5 sales orders`
   - `How many open leads do we have?`
   - `Total unpaid sales invoices this month by customer`
   - `Show the details of SO-00045`

Commands: `/login` (switch account), `/logout`, `/me` (your user and roles), `/new` (new conversation), `/cancel`, `/help`.

## Project structure

```text
frappe_telegram_ai/
├── main.py                  start / check / genkey
├── config/
│   ├── config.yaml          your settings
│   └── config.example.yaml  clean template
├── app/
│   ├── core/                settings loader, logging
│   ├── frappe/              Frappe REST client (runs as the logged-in user)
│   ├── auth/                Frappe login, session check, user roles
│   ├── storage/             encrypted session store (SQLite)
│   ├── security/            encryption, login lockout, rate limit
│   ├── ai/                  OpenAI-compatible AI client
│   ├── agent/               AI tool loop, Frappe tools, query validation
│   ├── bot/                 Telegram handlers and messages
│   └── runtime.py           wires everything together
└── docs/
    ├── ACCESS_CONTROL.md
    └── TROUBLESHOOTING.md
```

Local data: `data/sessions.db` (created on first start) holds the encrypted sessions.

## Production notes

- Keep `config/config.yaml` out of version control (already in `.gitignore`) and readable only by the bot's user.
- Run the bot under a process manager (systemd, Supervisor, Docker).
- Run only **one** copy of the bot per token (Telegram allows one polling client).
