A Discord chatbot that emulates the tone and typing style of provided example messages.
Project description
Faithful
Faithful is a Discord bot that reads example messages and mimics the author's tone and typing style. It responds when mentioned or replied to, and can optionally post on its own.
Features
- Swappable backends:
Backend Description Requirements openai-compatibleAny OpenAI-compatible API via Chat Completions. (default) base_url(API key optional)openaiOpenAI API key geminiGoogle Gemini API key anthropicAnthropic Claude API key - Web search: optionally searches the web for current information.
- Memory: optionally remembers facts about users and channels across conversations
- Reactions: optionally reacts to messages with emojis including custom ones
- Random posts: optionally sends a few messages per day into a configured channel
- Random replies: optionally replies to any message in configured channels
Prerequisites
- A Discord bot with the Message Content privileged intent enabled
- A LLM provider of your choice
Quick Start
pip install "faithful[all]" # core plus all three backend SDKs
faithful # run the interactive setup wizard
faithful run # start the bot
The wizard writes ~/.faithful/config.toml. Run faithful doctor any time to check connectivity, or faithful info to see where things live.
If you want a slimmer install, the per-backend extras are [openai], [gemini], and [anthropic]. The OpenAI-compatible backend uses the openai package as well. Override paths with --config <path>, --data-dir <path>, or set FAITHFUL_HOME=/some/dir.
Commands
All commands are slash commands and only usable by users listed in admin_ids.
| Command | Description |
|---|---|
/upload |
Upload a .txt file of example messages |
/add_message <text> |
Add a single example message |
/list_messages [page] |
View stored messages (paginated) |
/remove_message <index> |
Remove a message by its index |
/clear_messages |
Remove all example messages |
/download_messages |
Download all messages as a .txt file |
/generate_test <prompt> |
Manually trigger a response test |
/status |
Show detailed configuration status |
/memory list <target> [user] |
List memories for a user or channel |
/memory add <target> <text> [user] |
Add a memory for a user or channel |
/memory remove <target> <index> [user] |
Remove a memory by index |
/memory clear <target> [user] |
Clear all memories for a user or channel |
You can also right-click any message and use the Add to Persona context menu to add it directly as an example.
How It Works
The bot responds when pinged or in chat, ignoring replies to messages older than 5 minutes (configurable).
The bot can react to messages with emoji, including server custom emoji. Reactions happen in two ways:
If channels is configured under [scheduler], the bot sends messages at random intervals into one of those channels.
Example Messages Format
Create a .txt file with one message per line:
lol yeah thats what i was thinking
bruh no way
ok but have u considered... maybe not doing that
Upload via /upload or add individually with /add_message.
Configuration
The bot is configured via ~/.faithful/config.toml. The repo's config.example.toml documents every setting. Environment variables override their TOML equivalents:
| Variable | Overrides |
|---|---|
DISCORD_TOKEN |
discord.token |
ADMIN_USER_IDS (comma-separated) |
discord.admin_ids |
API_KEY |
backend.api_key |
[discord]
| Key | Description | Default |
|---|---|---|
token |
Your Discord bot token | (Required) |
admin_ids |
List of Discord user IDs who can manage the bot | (Required) |
[backend]
| Key | Description | Default |
|---|---|---|
active |
Backend to use: openai, openai-compatible, gemini, anthropic |
openai-compatible |
api_key |
API key for the active LLM backend | |
model |
Model name for the active LLM backend | (per-backend default) |
base_url |
Endpoint URL, required for openai-compatible (e.g. http://localhost:11434/v1 for Ollama) |
[llm]
| Key | Description | Default |
|---|---|---|
temperature |
Controls randomness (0.0-2.0) | 1.0 |
max_tokens |
Maximum tokens per response | 16000 |
sample_size |
Example messages to include in the system prompt | 300 |
[behavior]
| Key | Description | Default |
|---|---|---|
persona_name |
The persona name used in system prompts | faithful |
reply_probability |
Chance of random unsolicited reply (0.0-1.0) | 0.02 |
reaction_probability |
Chance of reacting to a message without replying (0.0-1.0) | 0.05 |
debounce_delay |
Seconds to wait for multi-message bursts | 3.0 |
conversation_expiry |
Seconds before a thread is considered stale | 300.0 |
max_context_messages |
Number of previous messages to include | 20 |
max_session_messages |
Per-channel session history window | 50 |
enable_web_search |
Allow the LLM to search the web | false |
enable_memory |
Enable per-user and per-channel memory | false |
system_prompt |
Custom system prompt template ({name}, {examples}, {custom_emojis} placeholders) |
(built-in) |
[scheduler]
| Key | Description | Default |
|---|---|---|
channels |
Channel IDs for unprompted messages | [] |
min_hours |
Minimum hours between spontaneous messages | 12 |
max_hours |
Maximum hours between spontaneous messages | 24 |
Project Structure
faithful/
├── pyproject.toml # dependencies and project metadata
├── config.example.toml # reference config for hand-editors
├── README.md
├── CONTRIBUTING.md
├── SECURITY.md
├── LICENSE
└── faithful/ # main package
├── cli.py # argparse entry point and verb dispatch
├── verbs.py # `info` and `run` verbs
├── wizard.py # interactive setup wizard
├── doctor.py # connectivity self-check
├── bot.py # Discord bot class
├── config.py # TOML config loader (read-only at runtime)
├── paths.py # config and data directory resolution
├── errors.py # friendly user-facing exceptions
├── store.py # example message storage
├── prompt.py # prompt assembly and custom emoji
├── chunker.py # message chunking, typing delays, reaction parsing
├── tools/ # tool definitions and executors
│ ├── definitions.py # provider-agnostic tool schemas
│ ├── executor.py # dispatch (web search, web fetch, memory)
│ └── memory.py # MemoryExecutor for the file-based memory tool
├── backends/ # text-generation backends
│ ├── base.py # Backend ABC, GenerationRequest, session history, tool loop
│ ├── openai.py # OpenAI Responses API
│ ├── openai_compat.py # OpenAI-compatible Chat Completions API
│ ├── gemini.py # Google Gemini
│ └── anthropic.py # Anthropic Claude
└── cogs/ # Discord command and event modules
├── admin.py # admin slash commands and memory management
├── chat.py # message handling, responses, reactions
├── onboarding.py # welcome DM and `/help`
└── scheduler.py # spontaneous message scheduler
License
AGPL-3.0-or-later. See LICENSE.
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 faithful-1.0.1.tar.gz.
File metadata
- Download URL: faithful-1.0.1.tar.gz
- Upload date:
- Size: 66.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ead64147e77126ea9a6fa13a986e13d7e0796b263b56d47d1db94fb551bc9164
|
|
| MD5 |
6262c0a183647e2f25e95e8f1db8ba58
|
|
| BLAKE2b-256 |
24ffadab11dffe74c5b9885b8fb715668848199ee0ed4605c48d5a63636c681b
|
Provenance
The following attestation bundles were made for faithful-1.0.1.tar.gz:
Publisher:
release.yml on a9lim/faithful
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
faithful-1.0.1.tar.gz -
Subject digest:
ead64147e77126ea9a6fa13a986e13d7e0796b263b56d47d1db94fb551bc9164 - Sigstore transparency entry: 1386713298
- Sigstore integration time:
-
Permalink:
a9lim/faithful@9047bb9aee6443b10cf195b212b2944c16da92d0 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/a9lim
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9047bb9aee6443b10cf195b212b2944c16da92d0 -
Trigger Event:
push
-
Statement type:
File details
Details for the file faithful-1.0.1-py3-none-any.whl.
File metadata
- Download URL: faithful-1.0.1-py3-none-any.whl
- Upload date:
- Size: 63.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ffb5e39a333a72fd52210f3fa35482c5109ea796eb64035d81341d73226383f1
|
|
| MD5 |
90a27375ae6101c722b39fd8271ea872
|
|
| BLAKE2b-256 |
85ac11b701e00947fb77271abb2e17fffbdec88fcc9bfbb7baabae70c56782dd
|
Provenance
The following attestation bundles were made for faithful-1.0.1-py3-none-any.whl:
Publisher:
release.yml on a9lim/faithful
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
faithful-1.0.1-py3-none-any.whl -
Subject digest:
ffb5e39a333a72fd52210f3fa35482c5109ea796eb64035d81341d73226383f1 - Sigstore transparency entry: 1386713401
- Sigstore integration time:
-
Permalink:
a9lim/faithful@9047bb9aee6443b10cf195b212b2944c16da92d0 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/a9lim
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@9047bb9aee6443b10cf195b212b2944c16da92d0 -
Trigger Event:
push
-
Statement type: