disturpe-ai-chatbot
Health Warn
- License — License: MIT
- Description — Repository has a description
- Active repo — Last push 0 days ago
- Low visibility — Only 5 GitHub stars
Code Pass
- Code scan — Scanned 11 files during light audit, no dangerous patterns found
Permissions Pass
- Permissions — No dangerous permissions requested
No AI report is available for this listing yet.
Disturpe AI Chatbot is a self-hosted Discord conversation bot with provider-neutral AI configuration, local SQLite memory, configurable daily quotas, optional Discord logging, and privacy-focused defaults.
Disturpe AI Chatbot
Disturpe AI Chatbot is a self-hosted Discord conversation bot with provider-neutral AI configuration, local SQLite memory, configurable daily quotas, optional Discord logging, and privacy-focused defaults.
The project does not depend on a specific AI vendor. You choose the complete API endpoint, model name, authentication method, request limits, and attachment behavior through environment variables. The selected endpoint must accept the OpenAI-compatible chat-completions request format.
[!IMPORTANT]
Self-hosting does not make AI requests private by itself. User messages, selected conversation history, the system prompt, and enabled attachment URLs are sent to the AI endpoint configured by the operator. Review that provider's privacy and retention policies before inviting users.
Table of contents
- Highlights
- How it works
- Requirements
- Discord application setup
- Installation
- Configuration
- AI endpoint examples
- Running the bot
- When the bot responds
- Commands
- Memory and quotas
- Attachments
- Logging
- Privacy and security
- Customizing the personality
- Project structure
- Development and testing
- Troubleshooting
- Known limitations
- Security reports and license
Highlights
- Provider-neutral configuration: choose any OpenAI-compatible chat-completions endpoint and model.
- Flexible authentication: use Bearer tokens, custom API-key headers, no authentication for local services, or additional provider headers.
- Local conversation memory: store messages in a single SQLite database with FTS5/BM25 retrieval when available.
- Automatic retention: delete old conversation messages after 30 days by default.
- Daily quotas: limit successful conversations per Discord user and reset quotas at midnight in a configurable time zone.
- Privacy-focused logging: keep message and response bodies out of Discord log cards by default.
- Secure API behavior: reject remote plaintext HTTP by default, disable redirects, cap response size, and avoid logging provider response bodies.
- Attachment controls: independently decide whether image URLs and non-image file URLs are sent to the AI provider.
- Administrative controls: restrict memory inspection, analysis, export, deletion, and quota reset commands to one Discord user ID.
- Cross-platform scripts: use the included Linux and Windows installation/start scripts.
How it works
flowchart LR
A["Discord message"] --> B{"DM, mention, reply, or trigger name?"}
B -- "No" --> C["Ignore message"]
B -- "Yes" --> D["Load recent and relevant local memory"]
D --> E["Build system prompt and chat-completions request"]
E --> F["Apply client-side rate limit"]
F --> G["Configured AI endpoint"]
G --> H["Validate and limit response"]
H --> I["Store successful exchange and update quota"]
I --> J["Reply in Discord"]
For each accepted message, the bot:
- Checks whether the message should trigger a response.
- Verifies the user's remaining daily quota.
- Loads up to 24 recent messages from the current Discord channel conversation.
- Retrieves up to six older relevant messages from the same user's memory.
- Builds the system prompt using the configured personality and time zone.
- Sends an OpenAI-compatible request to the configured AI endpoint.
- Stores the successful user/assistant exchange locally.
- Sends the response to Discord, splitting long responses safely when necessary.
Failed API requests do not consume quota.
Requirements
- Python 3.12 or newer
- A Discord application with a bot user
- A Discord bot token
- Message Content Intent enabled for the bot
- An AI endpoint that accepts OpenAI-compatible chat-completions requests
- SQLite with FTS5 support for ranked full-text retrieval
- If FTS5 is unavailable, the application automatically uses a simpler SQLite text search.
No Docker configuration is included. The provided scripts use a project-local Python virtual environment at .venv/.
Discord application setup
- Open the Discord Developer Portal.
- Create a new application or select an existing one.
- Open the Bot page and create a bot user if needed.
- Generate or reset the bot token and store it only in your local
.envfile. - Under Privileged Gateway Intents, enable Message Content Intent.
- Open OAuth2 > URL Generator.
- Select the
botscope. - Grant the permissions required by your deployment. The normal minimum is:
- View Channels
- Send Messages
- Read Message History
- Attach Files, if administrator exports will be used
- Use the generated URL to invite the bot to your server.
To copy Discord user and channel IDs:
- Open Discord User Settings > Advanced.
- Enable Developer Mode.
- Right-click a user or channel and select Copy ID.
Installation
1. Create the local configuration
Linux or macOS-style shell:
cp .env.example .env
Windows Command Prompt:
copy .env.example .env
Open .env and configure at least:
DISCORD_TOKEN=your-discord-bot-token
AI_API_URL=https://api.example.com/v1/chat/completions
AI_MODEL=your-model-name
AI_API_KEY=your-provider-api-key
Never commit .env.
2. Install dependencies
Linux:
chmod +x install.sh start.sh stop.sh
./install.sh
Windows:
scripts\windows\install.bat
The installation scripts:
- Create
.venv/if it does not exist. - Upgrade
pip,setuptools, andwheelinside that virtual environment. - Install the pinned runtime dependencies from
requirements.txt. - Verify imports and SQLite FTS5 support.
Do not install the requirements globally with administrator or root privileges.
Configuration
All configuration is read from .env at startup. Restart the bot after changing environment variables. Boolean values accept true, false, 1, 0, yes, no, on, and off.
Discord settings
| Variable | Required | Default | Description |
|---|---|---|---|
DISCORD_TOKEN |
Yes | Empty | Discord bot token. Keep it secret. |
AUTHORIZED_USER_ID |
No | 0 |
Discord user ID allowed to run administrator commands. 0 disables administrator access. |
MESSAGE_LOG_CHANNEL_ID |
No | 0 |
Channel receiving conversation log cards. 0 disables these logs. |
BOT_LOG_CHANNEL_ID |
No | 0 |
Channel receiving operational and guild join/remove logs. 0 disables these logs. |
LOG_MESSAGE_CONTENT |
No | false |
Include user and assistant message bodies in conversation log cards. Keep disabled unless users have been informed. |
BOT_TRIGGER_NAMES |
No | Disturpe,Disturpe AI,Disturpe AI Chatbot |
Comma-separated standalone names that trigger the bot in guild messages. |
BOT_TIMEZONE |
No | UTC |
IANA time-zone name used for logs, quota resets, and time context in the prompt. Example: America/New_York. |
AI endpoint settings
| Variable | Required | Default | Description |
|---|---|---|---|
AI_API_URL |
Yes | Empty | Full chat-completions endpoint URL, including its path. |
AI_MODEL |
Yes | Empty | Model identifier sent in the request body. |
AI_API_KEY |
No | Empty | Provider credential. May remain empty for unauthenticated local endpoints. |
AI_API_KEY_HEADER |
No | Authorization |
Header used for AI_API_KEY. |
AI_API_KEY_PREFIX |
No | Bearer |
Text placed before the API key. Use an empty value for a raw key. |
AI_EXTRA_HEADERS_JSON |
No | {} |
Additional string headers encoded as a JSON object. |
AI_ALLOW_INSECURE_HTTP |
No | false |
Permit remote plaintext HTTP. Localhost HTTP is allowed without enabling this option. |
AI_CONNECT_TIMEOUT |
No | 10 |
Connection timeout in seconds. Minimum effective value: 0.1. |
AI_READ_TIMEOUT |
No | 60 |
Response read timeout in seconds. Minimum effective value: 0.1. |
AI_MAX_RETRIES |
No | 3 |
Maximum request attempts. Minimum effective value: 1. |
AI_MAX_RESPONSE_BYTES |
No | 2000000 |
Maximum accepted JSON response size. Minimum effective value: 1024. |
AI_REQUESTS_PER_MINUTE |
No | 60 |
Client-side request limit. Set to 0 to disable the limiter. |
AI_SEND_IMAGE_URLS |
No | true |
Send Discord image attachment URLs using OpenAI-compatible image content blocks. |
AI_SEND_FILE_URLS |
No | false |
Append non-image attachment URLs to the user prompt. |
The client does not follow HTTP redirects. Retryable responses include HTTP 408, 429, and 5xx. Provider error bodies are not copied into user-visible errors or operational logs.
AI_EXTRA_HEADERS_JSON may contain only string keys and values. It cannot override standard protected headers such as Content-Type, Accept, User-Agent, Host, Content-Length, or Transfer-Encoding. When AI_API_KEY is set, it also cannot override the configured authentication header.
Memory and usage settings
| Variable | Required | Default | Description |
|---|---|---|---|
MESSAGE_QUOTA |
No | 1000 |
Successful conversations allowed per user per day. Minimum effective value: 1. |
MEMORY_RETENTION_DAYS |
No | 30 |
Delete messages older than this many days. 0 disables automatic retention cleanup. |
DATA_DIR |
No | data |
Directory containing runtime data such as memory.db and bot.pid. Relative paths are resolved from the project directory. |
PERSONALITY_FILE |
No | config/personality.md |
Custom system-prompt personality file. Relative paths are resolved from the project directory. |
AI endpoint examples
These examples demonstrate configuration patterns. Replace the example URLs, model names, and credentials with values from your chosen service.
Standard Bearer-token endpoint
AI_API_URL=https://api.example.com/v1/chat/completions
AI_MODEL=example-chat-model
AI_API_KEY=replace-me
AI_API_KEY_HEADER=Authorization
AI_API_KEY_PREFIX=Bearer
AI_EXTRA_HEADERS_JSON={}
The resulting authentication header is:
Authorization: Bearer replace-me
Raw API-key header
AI_API_URL=https://gateway.example.com/v1/chat/completions
AI_MODEL=example-model
AI_API_KEY=replace-me
AI_API_KEY_HEADER=x-api-key
AI_API_KEY_PREFIX=
The resulting authentication header is:
x-api-key: replace-me
Local OpenAI-compatible server
AI_API_URL=http://127.0.0.1:11434/v1/chat/completions
AI_MODEL=local-model-name
AI_API_KEY=
AI_REQUESTS_PER_MINUTE=0
Plain HTTP is accepted automatically only for localhost, 127.0.0.1, and ::1. Use HTTPS for remote endpoints.
Additional provider headers
AI_EXTRA_HEADERS_JSON={"X-Application":"Disturpe AI Chatbot","X-Environment":"production"}
The value must be valid JSON on one line.
Running the bot
Linux
Start the bot in the foreground:
./start.sh
Stop it from another terminal:
./stop.sh
start.sh writes the running process ID to data/bot.pid. stop.sh verifies both the process working directory and command before terminating it, reducing the risk of stopping an unrelated process.
You can also run the module directly:
.venv/bin/python -m app
Windows
Run:
scripts\windows\start.bat
The bot runs in the foreground. Close it with Ctrl+C or close the terminal window.
When the bot responds
The bot ignores messages authored by bots. For human-authored messages:
- Direct messages: always eligible for a response.
- Guild messages: eligible when at least one condition is true:
- The bot is mentioned.
- The message replies directly to one of the bot's messages.
- The message contains a standalone name from
BOT_TRIGGER_NAMES.
Trigger-name matching is case-insensitive and respects word boundaries. For example, Disturpe matches DISTURPE! but does not match disturped.
Up to ten Discord attachments are examined per message. Empty messages without attachments are ignored.
Commands
Commands are recognized as exact first words; a longer word that merely begins with a command name will not trigger it.
| Command | Access | Description |
|---|---|---|
!quota |
Everyone | Display the caller's remaining daily quota. |
!memory |
Administrator | Display stored message count, user count, and active search mode. |
!analyze |
Administrator | Analyze recent stored conversations through the configured AI endpoint. The result is sent to the administrator by DM. |
!export <user_id> |
Administrator | Generate a JSON export in memory and send it to the administrator by DM. The application does not intentionally leave an export file on disk. |
!clear-user <user_id> |
Administrator | Delete the selected user's locally stored conversation messages. This does not reset quota. |
!reset-quota <user_id or @user> |
Administrator | Reset the selected user's daily usage counter. |
Administrator commands are disabled until AUTHORIZED_USER_ID contains a non-zero Discord user ID. Sensitive exports and analyses are never posted to the channel when DM delivery fails.
Memory and quotas
Runtime memory is stored in DATA_DIR/memory.db.
For a normal conversation request, the bot uses:
- Up to 24 recent messages from the same user and Discord channel.
- Up to six older relevant messages belonging to the same user.
- FTS5/BM25 ranking when SQLite FTS5 is available.
- A parameterized SQLite fallback search when FTS5 is unavailable.
Memory is separated by user ID for retrieval and by Discord channel for recent context. The configured AI endpoint receives only the context selected for the current operation.
Quota behavior:
- Only successful AI exchanges consume quota.
- Quota is tracked per Discord user ID.
- Counters reset at midnight in
BOT_TIMEZONE. - The bot checks the date during conversations and also runs a scheduled midnight reset.
- The quota button appears when the remaining count reaches a lower hundred boundary.
Retention cleanup runs when the bot becomes ready and after the scheduled daily reset. SQLite secure deletion is enabled, and the write-ahead log is truncated after explicit user deletion or retention cleanup.
Attachments
The application never downloads Discord attachments to local storage.
- Image attachments are sent as OpenAI-compatible
image_urlcontent blocks whenAI_SEND_IMAGE_URLS=true. - Non-image attachments are not sent by default.
- When
AI_SEND_FILE_URLS=true, non-image URLs are appended to the text prompt. The selected model or provider may not be able to retrieve them. - Stored attachment metadata contains the filename and content type, not the Discord CDN URL.
Attachment URLs still leave Discord and are disclosed to the selected AI provider when enabled. Disable both attachment options if that data flow is not acceptable.
Logging
The bot supports two optional Discord log destinations.
Conversation log channel
Set MESSAGE_LOG_CHANNEL_ID to a channel ID to receive conversation cards containing:
- User display name, username, and Discord ID
- Guild and channel information
- Attachment count
- Response duration
- Recent and related memory counts
- Remaining quota
- A link to the original Discord message
Message and response bodies are replaced with privacy placeholders unless LOG_MESSAGE_CONTENT=true.
Operational log channel
Set BOT_LOG_CHANNEL_ID to receive:
- Startup status
- API status codes for failed requests
- Daily quota resets
- Retention cleanup counts
- Guild join and removal events
The application avoids logging AI-provider response bodies and does not print Discord message content to the terminal.
[!WARNING]
Discord identifiers, guild metadata, and message links are still personal or operational data. Restrict access to log channels even when message-content logging is disabled.
Privacy and security
The default configuration is intentionally conservative, but the deployment operator remains responsible for the host, Discord server, selected AI provider, backups, and credentials.
Built-in protections include:
.env,.venv/, databases, PID files, exports, logs, and runtimedata/content are excluded by.gitignore.- Local data directories use mode
700and the database uses mode600where supported. - Remote AI endpoints must use HTTPS unless
AI_ALLOW_INSECURE_HTTP=trueis explicitly enabled. - Plain HTTP is limited to loopback hosts by default.
- Endpoint URLs cannot contain embedded usernames, passwords, or URL fragments.
- HTTP redirects are not followed.
- API response size is capped before JSON parsing completes.
- Provider error bodies are not exposed in user-facing errors.
- Discord mentions are disabled globally for generated messages.
- Message content logging and non-image file forwarding are disabled by default.
- Exports are generated in memory and sent only to the authorized administrator by DM.
- SQLite queries use bound parameters.
Before publishing or deploying:
- Confirm
.envis ignored withgit status --ignored. - Confirm
data/contains no real runtime database in the commit. - Rotate any credential that may have been copied into logs, screenshots, chat, or Git history.
- Restrict Discord log channels to trusted administrators.
- Review the AI provider's data retention, training, and regional processing policies.
- Use disk encryption and secure backups if conversation memory is enabled.
- Keep dependencies updated and review Dependabot alerts.
The database is protected by filesystem permissions but is not application-level encrypted. Anyone with sufficient access to the host or its backups may be able to read it.
Customizing the personality
The default personality is stored in:
config/personality.md
Edit that file to change the assistant's tone and behavior. You can also set PERSONALITY_FILE to another absolute path or a path relative to the project directory.
The application adds the following context at runtime:
- Current date and time in
BOT_TIMEZONE - Potentially relevant local memory excerpts
- Instructions that prevent assistant statements from being treated as user facts
Do not place credentials or private deployment information in the personality file. Its contents are sent to the configured AI endpoint with every generation request.
Project structure
.
├── app/
│ ├── __main__.py # `python -m app` entry point
│ ├── ai_client.py # OpenAI-compatible HTTP client
│ ├── bot.py # Discord client, commands, quotas, and logging
│ ├── memory_store.py # SQLite storage and retrieval
│ ├── personality.py # Personality loading and system-prompt builder
│ └── text_utils.py # Trigger matching and Discord message splitting
├── config/
│ └── personality.md # Editable default personality
├── data/
│ └── .gitkeep # Runtime directory placeholder
├── scripts/windows/
│ ├── install.bat
│ └── start.bat
├── tests/ # Unit tests
├── .env.example # Safe configuration template
├── .gitignore # Secret and runtime-data exclusions
├── install.sh # Linux installation helper
├── start.sh # Linux start helper
├── stop.sh # Linux stop helper
├── requirements.txt # Pinned runtime dependencies
├── requirements-dev.txt # Pinned development and audit tools
├── SECURITY.md # Vulnerability reporting policy
└── LICENSE # MIT License
Development and testing
Install both runtime and development dependencies:
./install.sh
.venv/bin/python -m pip install -r requirements-dev.txt
Run the unit tests:
.venv/bin/python -m unittest discover -s tests -v
Run formatting and lint checks:
.venv/bin/ruff format --check app tests
.venv/bin/ruff check app tests
Run security checks:
.venv/bin/bandit -r app -q
.venv/bin/pip-audit -r requirements-dev.txt
Compile all Python modules without starting the bot:
.venv/bin/python -m compileall -q app tests
The current tests cover:
- OpenAI-compatible request construction
- Authentication header behavior
- HTTPS enforcement and localhost HTTP support
- Redirect rejection
- Provider-error redaction
- Response-size limits
- Local-memory export, search, retention, and file permissions
- Trigger-name matching and Discord message splitting
- Configurable time-zone context
Troubleshooting
Missing required settings in .env
Ensure .env exists in the project root and includes non-empty values for:
DISCORD_TOKEN=...
AI_API_URL=...
AI_MODEL=...
The bot starts but does not respond in a guild
- Confirm Message Content Intent is enabled in the Discord Developer Portal.
- Confirm the bot can view the channel, read message history, and send messages.
- Mention the bot directly or use a standalone value from
BOT_TRIGGER_NAMES. - Check that the message was sent by a human account; messages from bots are ignored.
The bot responds in DMs but not in server channels
This usually indicates missing guild permissions, disabled Message Content Intent, or a message that did not mention/reply to/call the bot by a configured name.
The API returns HTTP 401 or 403
Check:
AI_API_KEYAI_API_KEY_HEADERAI_API_KEY_PREFIX- Any required
AI_EXTRA_HEADERS_JSONvalues - Whether the configured model is available to that credential
The API returns HTTP 404
AI_API_URL must be the complete chat-completions endpoint, not only the provider's base domain.
A remote HTTP endpoint is rejected
Use HTTPS. If plaintext remote HTTP is unavoidable, explicitly set:
AI_ALLOW_INSECURE_HTTP=true
This may expose credentials and conversation content in transit.
A local server cannot be reached
- Confirm the local server is running.
- Confirm the port and path in
AI_API_URL. - Confirm the endpoint implements the OpenAI-compatible chat-completions schema.
- If the bot runs in a container or virtual machine, remember that
127.0.0.1refers to that isolated environment.
Administrator analysis or export is not delivered
The administrator must allow direct messages from the server. For privacy, the bot does not fall back to posting analysis or export content in the channel.
FTS5 is unavailable
The bot continues operating with SQLite fallback text search. Ranked full-text retrieval may be less accurate, but no manual migration is required.
The bot reports that it is already running
Use:
./stop.sh
If the previous process ended unexpectedly, start.sh validates the stored PID before deciding that a bot process is active.
Known limitations
- The endpoint is provider-neutral but must accept an OpenAI-compatible chat-completions request body. Native provider-specific schemas require a custom adapter.
- Non-image file URLs are appended as text when enabled; this does not guarantee that the model can download or understand the file.
- Local conversation memory is stored as plaintext SQLite data protected by host filesystem permissions.
- The bot uses prefix commands rather than Discord slash commands.
- Quotas are local to one bot deployment and are not synchronized across multiple instances.
- The in-process rate limiter is local to one process and does not coordinate distributed deployments.
- AI-generated content may be inaccurate. Users should verify important information independently.
Security reports and license
Do not disclose exploitable details, credentials, private messages, or personal data in a public issue. Follow the private reporting instructions in SECURITY.md.
Disturpe AI Chatbot is distributed under the MIT License.
Reviews (0)
Sign in to leave a review.
Leave a reviewNo results found