A Telegram bot that answers legal questions via the VaquillDocumentation Index
Fetch the complete documentation index at: https://vaquill.ai/docs/llms.txt
Use this file to discover all available pages before exploring further.
/ask API, renders markdown tables as PNG images, and surfaces case-law sources as tappable inline-keyboard buttons. Source code at github.com/Vaquill-AI/integrations/telegram-bot.
Features
- Per-chat conversation history (last N exchanges, configurable)
- Markdown tables rendered as styled PNG images using Pillow
- Source citations shown inline and as inline-keyboard buttons that open PDFs
- Telegram HTML sanitisation with plain-text fallback
- Automatic chunking of long answers (4096-char limit)
- Per-user rate limits (per-minute and per-day)
- Optional user ID allowlist
Prerequisites
| Requirement | Where to get it |
|---|---|
| Python 3.10+ | python.org |
| Telegram account | telegram.org |
| Telegram Bot Token | @BotFather |
| Vaquill API key | app.vaquill.ai - Settings > API Keys. See Authentication. |
Platform setup
Open BotFather
Search for @BotFather in Telegram (or open t.me/botfather) and press Start.
Create the bot
Send
/newbot. Provide a display name (e.g. Vaquill Legal AI), then a username ending in bot (e.g. vaquill_legal_bot). BotFather replies with your API token - copy it as TELEGRAM_BOT_TOKEN.Register slash commands (optional)
Send The bot also sets these on startup, but doing it manually makes them appear before the first run.
/setcommands, choose your bot, and paste:Quickstart
Local development uses polling mode - no public URL or HTTPS certificate required.Polling vs webhook
| Mode | Best for | Public URL? | How to enable |
|---|---|---|---|
| Polling | Local dev, single instance | No | Default - just run python bot.py |
| Webhook | Production, multi-instance | Yes (HTTPS) | Add an HTTP server and call Telegram’s setWebhook |
getWebhookInfo. Switch back to polling any time with deleteWebhook.
Configuration
Top env vars. Full list in the repo.env.example.
| Variable | Required | Default | Description |
|---|---|---|---|
TELEGRAM_BOT_TOKEN | Yes | - | Token from @BotFather |
VAQUILL_API_KEY | Yes | - | Vaquill API key (vq_key_...) |
VAQUILL_API_URL | No | https://api.vaquill.ai/api/v1 | API base URL |
VAQUILL_MODE | No | standard | standard or deep |
VAQUILL_COUNTRY_CODE | No | - | Jurisdiction filter, e.g. US |
RATE_LIMIT_PER_USER_PER_DAY | No | 100 | Per-user daily limit |
RATE_LIMIT_PER_USER_PER_MINUTE | No | 5 | Per-user minute limit |
ALLOWED_USERS | No | - | Comma-separated Telegram user IDs (empty = open) |
MAX_CONVERSATION_HISTORY | No | 10 | Exchange pairs kept in chat history |
MAX_SOURCES_PER_RESPONSE | No | 5 | Max source cards per reply |
LOG_LEVEL | No | INFO | DEBUG, INFO, WARNING, ERROR |
Commands
| Command | Description |
|---|---|
/start | Welcome message with category buttons; clears conversation history |
/help | Show available commands and tips |
/examples | Browse example questions by category as inline buttons |
/stats | Show messages used today, daily remaining, per-minute limit |
/clear | Wipe conversation history for the current chat |
Deployment
Docker
The included Dockerfile usespython:3.11-slim and installs DejaVu fonts for table-image rendering.
Cloud hosts
Render
Deploy as a Background Worker (not Web Service) - polling does not need an HTTP port.
render.yaml is included in the repo.Railway
railway init && railway up. Add TELEGRAM_BOT_TOKEN and VAQUILL_API_KEY in the Variables tab.VPS + systemd
Plain systemd unit with
Restart=always. No reverse proxy needed.Fly.io
fly launch, fly secrets set ..., fly deploy.render.yaml:
Troubleshooting
| Symptom | Fix |
|---|---|
| Bot does not respond | Confirm the process is running, the token is correct, and only one process is polling that token. |
| ”Unauthorized” / 401 from Vaquill | VAQUILL_API_KEY is invalid or expired. Generate a new one. |
| ”Insufficient credits” / 402 | Top up at app.vaquill.ai. |
| Rate limit immediately | You hit the per-minute cap. Wait 60 seconds or raise RATE_LIMIT_PER_USER_PER_MINUTE. |
| Tables render as text, not images | Pillow or fonts missing. In Docker the bot installs fonts-dejavu-core automatically; on macOS it falls back to Helvetica or the Pillow default. |
| HTML parse errors in Telegram | The bot catches BadRequest and falls back to plain text. No action needed unless it happens on every message. |
| Webhook set but no replies | Check getWebhookInfo for pending_update_count. If growing, the endpoint is unreachable. Delete the webhook to go back to polling. |
Related
Twilio-based, comparable consumer-messenger surface.
Signal
Privacy-focused alternative.
Chatbots overview
Compare all six platforms.
Authentication
How Vaquill API keys work.

