Mock Telegram Bot API server with LLM-generated replies. It emulates api.telegram.org, so a bot connects to it instead of the real Telegram API and works with virtual users, chats, and messages.
Why use it:
- Develop without Telegram: no internet access, no BotFather token, no real users required
- Automated testing: reproducible scenarios for incoming messages, callback queries, media, joins, and leaves
- Load testing: proactive mode generates a configurable event stream
- CI integration tests: run the server in Docker and test the bot as if it were in production
An LLM (Ollama, OpenAI, Claude, LM Studio, or any OpenAI/Anthropic-compatible endpoint) can generate realistic user replies and create an initial set of chats and users so you do not have to define everything by hand.
git clone https://github.com/skrashevich/telegram-mock-ai.git
cd telegram-mock-ai
cp config.example.yaml config.yaml
docker compose up -dOn first run, pull a model into Ollama:
docker exec ollama ollama pull llama3Done. The Bot API will be available at http://localhost:8081, and the Admin API at http://localhost:8082.
If you only need the mock API with manual control via the Admin API:
docker build -t telegram-mock-ai .
docker run -p 8081:8081 -p 8082:8082 \
-e TELEGRAM_MOCK_LLM_ENABLED=false \
telegram-mock-aiRequires Go 1.22+.
git clone https://github.com/skrashevich/telegram-mock-ai.git
cd telegram-mock-ai
cp config.example.yaml config.yaml
make runReplace the Telegram API base URL with the mock server address. The token can be anything: the server automatically registers the bot on first use and adds it to all existing chats.
from telegram.ext import ApplicationBuilder
app = (
ApplicationBuilder()
.token("YOUR_TOKEN")
.base_url("http://localhost:8081/bot")
.build()
)from aiogram import Bot
from aiogram.client.session.aiohttp import AiohttpSession
session = AiohttpSession()
session.api = "http://localhost:8081"
bot = Bot(token="YOUR_TOKEN", session=session)bot, _ := tele.NewBot(tele.Settings{
Token: "YOUR_TOKEN",
URL: "http://localhost:8081",
})const bot = new Telegraf('YOUR_TOKEN', {
telegram: { apiRoot: 'http://localhost:8081' }
});# Check connectivity
curl http://localhost:8081/botYOUR_TOKEN/getMe
# Send a message to a chat
curl -X POST http://localhost:8081/botYOUR_TOKEN/sendMessage \
-H 'Content-Type: application/json' \
-d '{"chat_id": -1001, "text": "Hello!"}'
# Long polling (wait for 10 seconds)
curl -X POST http://localhost:8081/botYOUR_TOKEN/getUpdates \
-d '{"timeout": 10}'After startup, the server already contains test data from the config: three users (Alice, Bob, Charlie), two chats, and one bot. The bot is automatically added to every chat, and in the first chat it gets admin rights.
When a bot connects for the first time (the first call to any Bot API method), the server immediately sends a message from a random user in one of the chats. During the next 30 seconds, the bot will also receive messages in all other chats, simulating real activity right after launch. If an LLM is enabled, those messages are AI-generated; otherwise, the server falls back to template-based greetings.
To avoid defining users manually, you can enable LLM-powered seed generation so the server creates realistic users, groups, and channels in the language you need.
Via config (on server startup):
seed:
generate:
enabled: true
users_count: 10
groups_count: 3
channels_count: 1
locale: "ru"Via the Admin API (at any time):
curl -X POST http://localhost:8082/api/seed/generate \
-H 'Content-Type: application/json' \
-d '{"users_count": 10, "groups_count": 3, "channels_count": 1, "locale": "ru"}'The LLM generates users with realistic names and usernames, creates groups and channels with meaningful titles, and distributes members across chats. All registered bots are automatically added to every generated chat, and the first chat grants them admin rights. The response includes all created entities with their assigned IDs.
Simulate a message sent "by a user" so all connected bots receive an update:
curl -X POST http://localhost:8082/api/chats/-1001/messages \
-H 'Content-Type: application/json' \
-d '{"user_id": 1001, "text": "Hi, bot!"}'The server can generate its own event stream, including messages, joins, leaves, photos, and stickers, with configurable frequency and content style:
proactive:
enabled: true
interval_min: 10s
interval_max: 60s
style: "normal" # style of generated messagesThe style parameter defines the tone of generated content, which is handy when testing moderation bots:
| Style | Description | Example use case |
|---|---|---|
normal |
Regular conversational messages | General testing |
spam |
Crypto scams, fake giveaways, suspicious links | Anti-spam bot testing |
toxic |
Profanity, insults, hate speech | Anti-toxicity bot testing |
flood |
Repeated characters, emoji spam, ALL CAPS, nonsense sequences | Anti-flood bot testing |
mixed |
Random mix: 40% normal, 20% spam, 20% toxic, 20% flood (default) | End-to-end moderation testing |
If the built-in presets are not enough, custom_prompt lets you supply any instruction for the LLM. It takes priority over style:
proactive:
enabled: true
interval_min: 5s
interval_max: 30s
custom_prompt: "Generate messages advertising online casinos and sports betting. Use typical tricks: easy-money promises, fake testimonials, and links like casino-xyz.com"More custom_prompt examples:
"Generate Ukrainian-language messages discussing current news"for multilingual testing"Write very long messages, 500+ characters, with quotes and links"for limit testing"Alternate normal messages with phishing attempts: ask users to open a link or enter a password"for anti-phishing bot testing
The mock server fully emulates file handling through getFile plus the file download endpoint, generating placeholder content on the fly:
# 1. Get file_path from file_id
curl http://localhost:8081/botYOUR_TOKEN/getFile?file_id=AgACAgIAAxkBAAI...
# Response: {"ok":true,"result":{"file_id":"...","file_path":"photos/file_abc123.jpg"}}
# 2. Download the file by file_path
curl http://localhost:8081/file/botYOUR_TOKEN/photos/file_abc123.jpg -o photo.jpgThe placeholder type is chosen automatically from the file_id prefix:
file_id prefix |
Type | Format | Size |
|---|---|---|---|
AgAC... |
Photo | JPEG (gradient + shape) | 800x600 |
CAAC... |
Sticker | WebP (transparent background, emoji-like) | 512x512 |
BAADAgAD... |
Video | JPEG (preview frame) | 640x480 |
BQAC... |
Document | Stub PDF | - |
CQACAgIAAxkBAAI... |
Audio | Stub MP3 | - |
DQAC... |
Voice | Stub OGG | - |
Photos and stickers are real generated images with a unique pattern derived from the file_path hash, so each file_id produces a visually distinct result.
The server supports two API protocols: OpenAI-compatible (default) and Anthropic.
Ollama (local):
llm:
base_url: "http://localhost:11434/v1"
model: "llama3"OpenAI:
llm:
base_url: "https://api.openai.com/v1"
api_key: "sk-..."
model: "gpt-4o-mini"Anthropic (Claude):
llm:
api_type: "anthropic"
base_url: "https://api.anthropic.com/v1"
api_key: "sk-ant-..."
model: "claude-sonnet-4-5-20250929"
max_tokens: 1024LM Studio / vLLM / any OpenAI-compatible server:
llm:
base_url: "http://localhost:1234/v1"
model: "local-model"Without an LLM:
llm:
enabled: falseWhen the LLM is disabled, the bot only receives updates created manually via the Admin API or pre-defined in the seed data.
server:
host: "0.0.0.0"
port: 8081
read_timeout: 60s
write_timeout: 60s
llm:
enabled: true
api_type: "openai" # "openai" or "anthropic"
base_url: "http://localhost:11434/v1"
api_key: ""
model: "gpt-4o-mini"
temperature: 0.8
max_tokens: 512
timeout: 30s
response_delay_min: 500ms # Simulates "typing..."
response_delay_max: 3s
proactive:
enabled: false
interval_min: 10s
interval_max: 60s
style: "normal" # "normal", "spam", "toxic", "flood", "mixed"
# custom_prompt: "..." # Free-form instruction (takes priority over style)
scenarios:
- type: user_message
weight: 0.6
- type: new_member
weight: 0.1
- type: member_left
weight: 0.05
- type: photo_message
weight: 0.15
- type: sticker_message
weight: 0.1
webhook:
max_retries: 3
retry_delay: 1s
timeout: 10s
seed:
generate:
enabled: false
users_count: 10
groups_count: 3
channels_count: 1
locale: "ru"
max_retries: 2
users:
- id: 1001
first_name: "Alice"
username: "alice"
- id: 1002
first_name: "Bob"
last_name: "Smith"
username: "bob_smith"
- id: 1003
first_name: "Charlie"
username: "charlie"
chats:
- id: -1001
type: "group"
title: "Test Group"
members: [1001, 1002, 1003]
- id: -1002
type: "supergroup"
title: "Development Chat"
members: [1001, 1002]
bots:
- token: "123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11"
username: "test_bot"
first_name: "Test Bot"
log:
level: "info" # debug, info, warn, error
format: "text" # text, json
admin:
enabled: true
host: "127.0.0.1" # localhost only
port: 8082These override values from config.yaml:
| Variable | Description |
|---|---|
TELEGRAM_MOCK_SERVER_HOST |
Bot API host (default: 0.0.0.0) |
TELEGRAM_MOCK_SERVER_PORT |
Bot API port (default: 8081) |
TELEGRAM_MOCK_LLM_ENABLED |
Enable LLM (true/false) |
TELEGRAM_MOCK_LLM_API_TYPE |
API protocol: openai, anthropic |
TELEGRAM_MOCK_LLM_BASE_URL |
LLM endpoint URL |
TELEGRAM_MOCK_LLM_API_KEY |
API key |
TELEGRAM_MOCK_LLM_MODEL |
Model name |
TELEGRAM_MOCK_PROACTIVE_ENABLED |
Enable proactive mode |
TELEGRAM_MOCK_SEED_GENERATE_ENABLED |
Enable automatic seed generation |
TELEGRAM_MOCK_LOG_LEVEL |
Log level: debug, info, warn, error |
TELEGRAM_MOCK_ADMIN_PORT |
Admin API port (default: 8082) |
Manage the mock server state. By default, it is available at 127.0.0.1:8082.
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/health |
Health check |
GET |
/api/state |
Full state dump (users, chats, bots) |
GET |
/api/users |
List users |
POST |
/api/users |
Create a user |
GET |
/api/chats |
List chats |
POST |
/api/chats |
Create a chat |
GET |
/api/chats/{id}/members |
List chat members |
POST |
/api/chats/{id}/members |
Add a member |
GET |
/api/chats/{id}/messages |
Message history |
POST |
/api/chats/{id}/messages |
Inject a user message |
GET |
/api/bots |
List bots |
POST |
/api/bots/{token}/updates |
Inject an arbitrary Update |
POST |
/api/seed/generate |
Generate seed data via LLM |
# Create a user
curl -X POST http://localhost:8082/api/users \
-d '{"first_name": "Diana", "username": "diana"}'
# Create a group
curl -X POST http://localhost:8082/api/chats \
-d '{"type": "group", "title": "New Group", "members": [1001, 1002]}'
# Send a user message (bots will receive an update)
curl -X POST http://localhost:8082/api/chats/-1001/messages \
-d '{"user_id": 1001, "text": "Hi!"}'
# Generate users and chats via LLM
curl -X POST http://localhost:8082/api/seed/generate \
-d '{"users_count": 5, "groups_count": 2, "locale": "ru"}'
# Inject an arbitrary Update into a specific bot
curl -X POST http://localhost:8082/api/bots/YOUR_TOKEN/updates \
-d '{"message":{"message_id":1,"from":{"id":1001,"first_name":"Alice"},"chat":{"id":-1001,"type":"group"},"text":"test"}}'28 methods covering the most common bot workflows:
| Category | Methods |
|---|---|
| Information | getMe, getChat, getChatMember, getChatMemberCount, getChatAdministrators |
| Updates | getUpdates, setWebhook, deleteWebhook, getWebhookInfo |
| Messages | sendMessage, editMessageText, editMessageReplyMarkup, deleteMessage, forwardMessage, copyMessage, answerCallbackQuery |
| Media | sendPhoto, sendDocument, sendVideo, sendAudio, sendVoice, sendSticker, sendAnimation, sendLocation |
| Files | getFile + download endpoint /file/bot{token}/{path} |
| Chat management | banChatMember, unbanChatMember, restrictChatMember, promoteChatMember, leaveChat |
cmd/telegram-mock-ai/main.go - entry point, wiring, graceful shutdown
internal/
|- api/ - Bot API and Admin API HTTP handlers
|- bot/ - Bot registry (auto-register by token)
|- state/ - In-memory store (users, chats, messages, members)
|- updates/ - Update queue and dispatcher (queue/webhook)
|- seed/ - LLM seed generation
|- llm/ - OpenAI/Anthropic client and prompts
|- webhook/ - Webhook delivery with retries
|- proactive/ - Proactive event generation engine
|- config/ - YAML and env configuration
`- models/ - Telegram API data structures
Bot -> POST /bot{token}/sendMessage -> state -> [async LLM reply] -> queue/webhook -> Bot
Bot -> getFile(file_id) -> file_path -> GET /file/bot{token}/{path} -> placeholder JPEG/WebP/stub
Proactive engine -> timer -> scenario -> LLM (style/custom_prompt) -> update -> Bot
Admin API -> POST /api/seed/generate -> LLM -> users + chats in state
make build # binary
make run # build and run
make test # testsApache 2.0