A bilingual Telegram bot that serves historical figures on demand.
Figure cards, an optional daily subscription, and inline discovery, in English and French.
Historical Figures Whisper Bot sends you a historical figure whenever you ask for one. Use /random
for a figure at random, /today for the figure of the day, or /subscribe to get one automatically
each day. Every figure arrives as a card with a portrait, a short biography, a few highlights, and a
link to read more on Wikipedia.
The bot runs as a single long-polling process, so it needs no public HTTP endpoint and no webhook. It
is deployed to a VPS inside a dedicated tmux session. All user-facing text is available in English
and French, and the language is picked automatically from each user's Telegram settings.
| Curated figures | Hand-picked historical figures with biographies and highlights, enriched from Wikidata |
| Bilingual EN/FR | Language chosen per user from their Telegram language_code, with no per-user storage |
| Figure cards | HTML cards with a bold name, an italic biography, highlights, a portrait, and a Wikipedia link |
| Daily delivery | Optional daily figure sent to subscribers at 12:00 Europe/Paris through the JobQueue |
| Inline buttons | Random, Today, and Read more buttons under every card |
| Feedback | /feedback forwards suggestions to the owner, with a per-user cooldown against flooding |
| Private only | The bot leaves any group or channel and works one to one |
| Rate limited | AIORateLimiter paces outgoing calls so bursts of traffic stay within Telegram's limits |
| Command | Description |
|---|---|
/start |
Welcome message with an inline keyboard (Random, Today, Help) |
/help |
List the available commands |
/random |
A random historical figure |
/today |
The historical figure of the day |
/subscribe |
Start receiving the daily figure |
/unsubscribe |
Stop the daily figure |
/feedback |
Suggest a figure or send feedback, either as /feedback <text> or interactively |
- Python 3.10+ with
asyncio - python-telegram-bot 22.8, using the
rate-limiterandjob-queueextras for flood control and the daily scheduler - loguru for logging to
logs/app.log - python-dotenv for
.envconfiguration - JSON files for storage: figures in
src/figures.jsonand subscribers insubscribers.json
# Clone
git clone https://github.com/jordantete/history_whisper_bot.git
cd history_whisper_bot
# Install
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
# Configure
cp .env.example .env # then fill TELEGRAM_BOT_TOKEN
# Run (long-polling)
python -m src.mainsrc/main.py loads .env via python-dotenv, so python -m src.main picks up the token without
sourcing your shell.
Only one process can poll a given bot token at a time. If you run the bot locally while the VPS instance is live with the same token, Telegram returns a
getUpdatesconflict. Use a separate token for development.
All configuration lives in .env (gitignored). Copy .env.example and fill it in.
| Variable | Required | Description |
|---|---|---|
TELEGRAM_BOT_TOKEN |
Yes | Bot token from @BotFather |
OWNER_CHAT_ID |
No | Chat that receives forwarded /feedback messages |
SUBSCRIBERS_FILE |
No | Path to the daily-delivery subscribers file (default subscribers.json) |
VPS_USER, VPS_HOST, VPS_BOT_PATH, SSH_KEY |
Deploy | VPS target used by scripts/deploy.sh |
Set the avatar and command menu through @BotFather:
/setuserpicuploadsassets/logo.pngas the bot avatar./setcommandsis optional, because the localized menus are published automatically at startup inBot._post_init.
python -m pytest tests/Run the tests from the project root. Imports use the src. package prefix, so pytest has to run from
the root for them to resolve.
Fill the deployment variables in .env (VPS_USER, VPS_HOST, VPS_BOT_PATH, SSH_KEY), then run:
./scripts/deploy.shscripts/deploy.sh rsyncs the code to the VPS, copies .env separately, recreates the venv, installs
requirements.txt, and restarts the history-whisper-bot tmux session. That session runs
scripts/start.sh, which execs python -m src.main and logs to logs/app.log.
# Follow the logs
ssh $VPS_USER@$VPS_HOST 'tail -f $VPS_BOT_PATH/logs/app.log'
# Attach to the session (detach with Ctrl-b then d)
ssh $VPS_USER@$VPS_HOST 'tmux attach -t history-whisper-bot'src/
├── main.py # entrypoint: load_dotenv, build Database + Bot, bot.run()
├── bot.py # Bot: handlers, figure cards, daily job, feedback
├── database.py # Database: loads and serves figures from figures.json
├── subscribers.py # SubscriberStore: JSON-persisted daily subscribers
├── historical_figure.py # HistoricalFigure model
├── utils.py # env vars and i18n (localize, resolve_locale)
├── logger.py # configured loguru LOGGER singleton
├── figures.json # curated figures (bios, facts, Wikidata ids)
└── localizable.json # EN/FR strings, keyed by locale
scripts/
├── deploy.sh # rsync, venv, restart the tmux session
├── start.sh # exec python -m src.main (what tmux runs)
└── enrich_figures.py # Wikidata enrichment for the figure dataset
tests/ # pytest suite
This project is licensed under the MIT License. See LICENSE for details.
