Multi-platform AI chatbot with Bring Your Own Key support — Ollama, OpenAI, Anthropic, Gemini, Groq, OpenRouter
Project description
BotServer
A multi-platform AI chatbot that connects Discord, Telegram, and Signal to locally-running Ollama LLMs.
Zero cloud AI dependencies — all inference runs on your own hardware through a local Ollama server. Your conversations stay private, your data stays local.
Discord ─┐
Telegram ─┤──▶ BotServer ──▶ Ollama (local) ──▶ LLM on your GPU
Signal ──┘
Table of Contents
- Features
- Architecture
- Prerequisites
- Installation
- Configuration
- Running the Bots
- Commands Reference
- AI Modes
- How Streaming Works
- Context Awareness
- Security & Guardrails
- Access Control
- Music Playback (Discord)
- News Feeds
- Project Structure
- Troubleshooting
Features
| Feature | Discord | Telegram | Signal |
|---|---|---|---|
| Streaming responses (live-edit) | Yes (0.6s) | Yes (1.0s) | Buffered |
| Conversation history (10 msgs) | Yes | Yes | Yes |
| Context-aware prompts | Yes | Yes | Yes |
| AI mode switching | Yes | Yes | Yes |
| Custom model per user | Yes | Yes | Yes |
| Web search + AI summary | Yes | Yes | Yes |
| Deep thinking (DeepSeek-R1) | Spoiler tags | Spoiler tags | Separate msg |
| Music playback | Yes (voice) | — | YouTube links |
| News feeds (RSS) | Yes | — | Yes |
| Access control | Open | Location-based | Phone-based |
| Typing indicators | — | — | Yes |
| Group support | Yes | Yes | Yes (commands only) |
| Output guardrails (PII/secrets) | Yes | Yes | Yes |
| Input guardrails (injection) | Yes | Yes | Yes |
Architecture
User Message
│
▼
┌─────────────────────────────────────────────┐
│ Platform Bot │
│ (discord_bot / telegram_bot / signal_bot) │
└─────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────┐
│ Guardrails (Input) │ ── Prompt injection detection
│ guardrails.py │ ── Content safety checks
│ │ ── Rate limiting
└─────────────┬───────────┘
│
▼
┌─────────────────────────┐
│ Context Analyzer │ ── Topic detection (8 categories)
│ context_analyzer.py │ ── Intent classification
│ │ ── Entity extraction
│ │ ── Pronoun referent resolution
└─────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ Ollama Handler │ ── Sync & async streaming
│ ollama_handler.py │ ── /api/generate & /api/chat
│ │ ── Model keep-alive (VRAM)
└─────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ Think Parser │ ── DeepSeek-R1 <think> tag parsing
│ think_parser.py │ ── Streaming state machine
└─────────────┬────────────┘
│
▼
┌─────────────────────────┐
│ Guardrails (Output) │ ── PII redaction
│ guardrails.py │ ── API key/token scrubbing
│ │ ── Infrastructure URL filtering
└─────────────┬────────────┘
│
▼
Platform Response
(formatted, split, streamed)
Module Breakdown
| Module | Lines | Purpose |
|---|---|---|
discord_bot.py |
~1070 | Discord bot with streaming, music, news |
telegram_bot.py |
~1270 | Telegram bot with location-based access control |
signal_bot.py |
~1070 | Signal bot via signal-cli JSON-RPC (fully async) |
ollama_handler.py |
~230 | Ollama API wrapper (sync + async streaming) |
context_analyzer.py |
~390 | Keyword-based conversation analysis (zero ML overhead) |
think_parser.py |
~120 | Streaming <think> tag state machine parser |
web_search.py |
~85 | DuckDuckGo search with retry logic |
guardrails.py |
~590 | 5-layer input/output security system |
access_store.py |
~160 | Thread-safe persistent user approval store |
Prerequisites
- Python 3.10+
- Ollama installed and running locally
- At least one LLM pulled (e.g.,
ollama pull llama3.1:8b) - For Discord: A Discord bot token (Discord Developer Portal)
- For Telegram: A bot token from @BotFather
- For Signal: signal-cli installed and linked to a phone number
- For Discord music: FFmpeg installed and in PATH
Installation
-
Clone the repository:
git clone https://github.com/your-username/BotServer.git cd BotServer
-
Install Python dependencies:
pip install discord.py pyTelegramBotAPI aiohttp requests feedparser yt-dlp qrcode duckduckgo-search python-dotenv
-
Pull Ollama models:
ollama pull llama3.1:8b # General mode ollama pull deepseek-r1:8b # Reasoning mode ollama pull qwen3-coder # Coding mode (adjust tag as needed)
-
Create your
.envfile (see Configuration below). -
Start Ollama (if not already running):
ollama serve
Configuration
Create a .env file in the project root with the following variables:
# ── Discord ──────────────────────────────────
DISCORD_BOT_TOKEN=your_discord_bot_token_here
# ── Telegram ─────────────────────────────────
TELEGRAM_BOT_TOKEN=your_telegram_bot_token_here
TELEGRAM_ADMIN_ID=your_numeric_telegram_user_id
# ── Signal ───────────────────────────────────
SIGNAL_PHONE_NUMBER=+1234567890 # Bot's linked phone number
SIGNAL_CLI_HOST=localhost # signal-cli daemon host
SIGNAL_CLI_PORT=7583 # signal-cli daemon port
SIGNAL_ADMIN_NUMBER=+1234567890 # Admin's phone number
# ── Ollama ───────────────────────────────────
OLLAMA_BASE_URL=http://localhost:11434 # Ollama server URL
OLLAMA_DEFAULT_MODEL=llama3.1:8b # Default model for general mode
Note: Only configure the platforms you plan to use. Each bot loads only its own variables.
Running the Bots
Run individually
python discord_bot.py # Starts Discord bot
python telegram_bot.py # Starts Telegram bot
python signal_bot.py # Starts Signal bot (requires signal-cli daemon)
Run all at once
Windows (batch):
botserver.bat
This launches each bot in its own terminal window, including the signal-cli daemon.
Windows (PowerShell):
.\start_all.ps1
Monitors all processes and reports exits. Ctrl+C stops everything cleanly.
Linux/macOS:
./start_all.sh
Runs all bots in background, traps SIGINT/SIGTERM for clean shutdown.
Signal-specific setup
Signal requires a running signal-cli daemon:
signal-cli -a +YOUR_NUMBER daemon --tcp 7583
To link a new device, use the included helper:
python signal_link.py # Generates QR code for Signal linking
Each bot binds a singleton port (Discord: 47001, Telegram: 47002, Signal: 47003) to prevent duplicate instances.
Commands Reference
Chat & Modes
| Command | Discord | Telegram | Signal | Description |
|---|---|---|---|---|
| Switch to General | !chat |
/chat |
/chat |
llama3.1:8b — general assistant |
| Switch to Coding | !coder |
/coder |
/coder |
qwen3-coder — code-focused |
| Switch to Reasoning | !think |
/think |
/think |
deepseek-r1:8b — step-by-step |
| Toggle thinking display | !deepthink |
/deepthink |
/deepthink |
Show/hide <think> reasoning |
| Reset history | !reset |
/reset |
/reset |
Clear conversation memory |
Questions & Search
| Command | Discord | Telegram | Signal | Description |
|---|---|---|---|---|
| One-shot question | !ask <q> |
/ask <q> |
/ask <q> |
No history, direct answer |
| Problem solver | !solve <q> |
/solve <q> |
/solve <q> |
Uses deepseek-r1 |
| Web search | !search <q> |
/search <q> |
/search <q> |
DuckDuckGo + AI summary |
Model Management
| Command | Discord | Telegram | Signal | Description |
|---|---|---|---|---|
| Show current model | !model |
/model |
/model |
Displays active mode & model |
| Set custom model | !setmodel <name> |
/setmodel <name> |
/setmodel <name> |
Override with any Ollama model |
| List available models | !models |
/models |
/models |
Fetches live list from Ollama |
!setmodelalso accepts a number (e.g.,!setmodel 3) to pick from the models list.
Music (Discord only)
| Command | Description |
|---|---|
!play <song> |
Search YouTube and play in voice channel |
!skip |
Skip current track |
!pause / !resume |
Pause or resume playback |
!stop |
Stop and disconnect from voice |
!queue |
Show upcoming tracks |
!volume <0-100> |
Set playback volume |
Music (Signal)
| Command | Description |
|---|---|
/music <song> |
Returns YouTube search results |
/recommend <mood/genre> |
AI-generated music suggestions |
News Feeds
| Command | Platform | Description |
|---|---|---|
!news [category] [count] |
Discord | Google News RSS headlines |
/news [category] [count] |
Signal | Google News RSS headlines |
Categories: top, tech, science, world, business
Admin Commands (Telegram & Signal)
| Command | Description |
|---|---|
/approve [id] |
Approve a pending user |
/deny [id] |
Deny a pending user |
/revoke <id> |
Revoke an approved user |
/pending |
List pending access requests |
/users |
List all approved users |
Default behavior
- Discord: @mention the bot or type in its channel for a streaming conversation
- Telegram: Any message from an approved user gets a streaming AI response
- Signal DMs: Plain text messages get streaming AI responses
- Signal Groups: Only
/commandsare processed (no free-text chat)
AI Modes
BotServer offers three switchable AI personalities per user:
General Mode (default)
- Model:
llama3.1:8b - Best for: Everyday questions, conversation, general knowledge
- System prompt: Helpful, friendly assistant with conversation context awareness
Coding Mode
- Model:
qwen3-coder:480b-cloud - Best for: Code generation, debugging, technical explanations
- System prompt: Expert programmer focused on clean, efficient code
Reasoning Mode
- Model:
deepseek-r1:8b - Best for: Complex problems, math, logic, step-by-step analysis
- System prompt: Deep analytical thinker showing reasoning process
- Special: Produces
<think>...</think>blocks showing internal reasoning
Users can also override their model with !setmodel / /setmodel to use any model available on their Ollama server.
How Streaming Works
All bots stream responses token-by-token from Ollama, but display them differently:
Discord edits the reply message every 0.6 seconds with accumulated tokens. If a response exceeds 2000 characters, it splits into multiple messages. While generating, the bot shows rotating "thinking" status phrases (81 unique phrases).
Telegram edits the reply message every 1.0 seconds (longer interval to respect Telegram rate limits). Responses are capped at 4000 characters per message.
Signal buffers the entire response since Signal doesn't support message editing. It sends typing indicators while generating. Long responses are split at 4000-character boundaries.
After every response, performance stats are appended:
⏱ 3.2s | ⚙ llama3.1:8b | ↑ 142 tokens | ↓ 256 tokens | 42.1 tok/s
Context Awareness
BotServer enriches every prompt with conversation intelligence — without making extra LLM calls:
-
Topic Detection — Scans the last 6 messages for keywords across 8 categories (programming, math, science, writing, business, health, gaming, music) and tells the model what the conversation is about.
-
Intent Classification — Determines if the user is asking a question, debugging, brainstorming, learning, or just chatting. The model adjusts its response style accordingly.
-
Entity Extraction — Identifies repeated nouns and key terms, helping the model maintain focus on what matters.
-
Referent Resolution — When a user says "fix it" or "explain that," the analyzer determines what "it" or "that" refers to from conversation history.
-
Conversation Type — Labels the chat as debugging, Q&A, learning, brainstorming, or casual — giving the model meta-awareness of the interaction style.
-
Geolocation (Telegram only) — If the user shared their location, it's included in the system prompt for location-aware responses.
All analysis is keyword/pattern-based (regex + word frequency), adding negligible latency.
Security & Guardrails
BotServer implements a 5-layer security system via guardrails.py:
Layer 1: Input Filtering
- Prompt injection detection — Catches "forget previous instructions," "act as DAN," "bypass rules," and other common jailbreak patterns
- Encoded evasion detection — Flags Base64, hex escapes, and HTML entity attempts
- Educational context awareness — Avoids false positives on legitimate questions (e.g., "how to ignore errors in bash")
- Rate limiting — 3 suspicious probes per user in 10 minutes triggers a 30-minute block
Layer 2: Content Safety
- Blocks requests involving violence, self-harm, CSAM, illegal activity, and hate speech
- Severity levels: critical, high, medium, low
Layer 3: Output Filtering
- PII redaction — SSNs, credit card numbers
- Secret scrubbing — API keys, JWT tokens, bot tokens, environment variables
- Infrastructure hiding — Localhost URLs, file paths, internal endpoints
- Smart context — Preserves code blocks and avoids redacting model self-references
Layer 4: Prompt-Level Safety
- A
SAFETY_GUARDRAILstring is appended to every prompt sent to Ollama, instructing the model to refuse harmful requests
Layer 5: Operational Security
- Log sanitization (redacts emails, tokens, phone numbers, URLs)
- Message length enforcement (4000 chars message, 200 chars search query, 500 chars command arg)
Access Control
Discord
Open to all server members — no approval needed. Security is handled at the Discord server level.
Telegram
New users go through a location-based approval flow:
- User sends
/start— bot asks them to share their location - User shares location via keyboard button
- Request goes to pending with name, username, and coordinates
- Admin receives notification and reviews with
/pending - Admin runs
/approveor/deny - Approved users can chat freely; denied users are removed
Signal
Phone-number-based approval:
- New user sends a message — bot notifies admin
- Admin approves with
/approve +phone_number - Approved users can chat in DMs and use commands in groups
All approval data persists in approved_users.json (thread-safe, auto-saving).
Music Playback (Discord)
Discord bot includes a full music player with queue management:
- Searches YouTube via yt-dlp and streams audio into voice channels
- Queue system — add multiple songs, skip, view upcoming tracks
- Volume control — adjustable 0–100%
- Auto-detection — finds FFmpeg from PATH or common Windows install locations
- Requires the bot to be in a voice channel (auto-joins when you use
!play)
News Feeds
Discord and Signal bots can fetch headlines from Google News RSS:
!news tech 5 # 5 latest tech headlines (Discord)
/news science 3 # 3 latest science headlines (Signal)
Available categories: top (default), tech, science, world, business
Project Structure
BotServer/
├── discord_bot.py # Discord bot (streaming, music, news)
├── telegram_bot.py # Telegram bot (location-based access)
├── signal_bot.py # Signal bot (async JSON-RPC)
├── ollama_handler.py # Ollama API wrapper
├── context_analyzer.py # Conversation intelligence
├── think_parser.py # DeepSeek-R1 <think> tag parser
├── web_search.py # DuckDuckGo search integration
├── guardrails.py # 5-layer security system
├── access_store.py # Persistent user approval store
├── gen_qr.py # QR code generator utility
├── signal_link.py # Signal device linking helper
├── approved_users.json # User approval data (auto-generated)
├── .env # Configuration (create manually)
├── botserver.bat # Windows launcher (batch)
├── start_all.ps1 # Windows launcher (PowerShell)
├── start_all.sh # Linux/macOS launcher
└── CLAUDE.md # AI coding assistant instructions
Troubleshooting
Bot won't start — "Address already in use" Each bot uses a singleton port (47001/47002/47003). Another instance is already running. Kill the existing process or restart.
Ollama connection refused
Make sure Ollama is running (ollama serve) and the OLLAMA_BASE_URL in .env matches your setup.
Slow first response
The first request after startup loads the model into VRAM. BotServer pre-loads models with keep_alive() to minimize this. Subsequent responses are fast.
Signal bot can't connect
Ensure signal-cli is running as a daemon: signal-cli -a +YOUR_NUMBER daemon --tcp 7583. The bot auto-reconnects with exponential backoff if the connection drops.
Discord music not working
FFmpeg must be installed and accessible. On Windows, the bot also checks C:\ffmpeg\bin\ and C:\Program Files\ffmpeg\bin\. Verify with ffmpeg -version.
Telegram bot not responding to messages
The user may not be approved. Check /pending as admin, or verify the user completed the location-sharing step.
"Model not found" errors
Run ollama list to see available models. Pull missing ones with ollama pull <model_name>.
State Management
All per-user state is stored in memory (module-level dictionaries) and is not persisted across restarts:
user_modes[user_id]— Current AI mode (general / coding / reasoning)user_custom_models[user_id]— Custom model overridedeep_thinking_enabled[user_id]— Whether to show<think>blocksconversation_history[key]— Last 10 messages per user
The only persisted state is approved_users.json for access control.
License
This project is provided as-is for personal and educational use.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file rotbot-1.0.0.tar.gz.
File metadata
- Download URL: rotbot-1.0.0.tar.gz
- Upload date:
- Size: 254.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3bbcf80f84bec07040b951c00736e4245327f882d0c16105d2bab298a75d6b3d
|
|
| MD5 |
bd58982976ffe4905231bc933d9f477e
|
|
| BLAKE2b-256 |
9077b4f01c3947f19460ae06598d63a529c503ea106e8b879a3208543b8a8415
|
File details
Details for the file rotbot-1.0.0-py3-none-any.whl.
File metadata
- Download URL: rotbot-1.0.0-py3-none-any.whl
- Upload date:
- Size: 118.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f3aa9f0525c73f126bc239d3bc751fc4c09a53117c042e9a3562c3710cf3ca4c
|
|
| MD5 |
82bd0967d39d012601dbbcb4bb0a1e8c
|
|
| BLAKE2b-256 |
f78158c4a03b15f865452fbf1bea009b81d9220aacfee1c410e925847cab2f47
|