Telegram bot that journals private messages into an Obsidian vault
Project description
Telegram Journal Bot
Capture thoughts on Telegram, persist them in Obsidian or GitHub, and never lose a moment again.
Telejournal is a bot that journals every private message into daily markdown notes, persisting to either a local Obsidian vault or a GitHub repository. Designed for personal journaling and private note-taking with rich media support.
Demo
User messages sent in a private chat are captured by the bot, including text and media.
Captured content is appended to your daily note with timestamped entries and structured formatting.
Features
- Private chat journal capture for text, photos, voice recordings, video messages (including circular video notes), and locations
- UTC daily note partitioning at
YYYY/YYYY-MM-DD.md - Media storage (photos, voice, video) in
YYYY/attachments/ - Pluggable storage providers:
obsidian_vault(filesystem)github_repo(GitHub repository via REST API)
- YAML frontmatter management for
mood,tags, andcreated - In-memory state only (
context.chat_dataandcontext.bot_data) - Date override commands (
/setdate,/resetdate) - Tags and mood management with inline keyboard callbacks
/showasks whether to show note text only or rendered (text + embedded attachments)- Displayed note timestamps are normalized from
%% HH:MM:SS %%markers to>HH:MM:SSfor cleaner chat output - Daily UTC "on this day" brief sends a years summary and asks whether to show history as notes only or rendered
- Replies to historical bot messages can include a quote plus a clickable source-note link back to the original daily note
- Edited text messages update their original journal entry in place instead of creating a duplicate
- Guided
/settingscommand for runtime updates of tag choices, daily brief time, mood prompt toggle, and bot menu visibility /settingsupdates are persisted to YAML so values survive bot restarts (existing config files are backed up before overwrite)
Usage
Installation
Choose one installation method:
From PyPI (recommended for end users):
python -m pip install --upgrade telejournal
From source (recommended for contributors):
git clone https://github.com/hugobatista/telejournal.git
cd telejournal
uv sync --extra dev
If running from source, use uv run before commands in the sections below.
Example: uv run telejournal run --verbose.
Quick Start
-
Configure your environment variables as documented in
Environment. -
Start the bot:
telejournal run --verbose
- Open your bot in Telegram and send:
/help
- Send a normal message (for example,
First journal entry) and confirm it appears in:
<STORAGE_ROOT>/YYYY/YYYY-MM-DD.md
Run Modes
Use whichever configuration style best fits your setup.
Environment variables only:
telejournal run
YAML configuration file (config.yaml auto-detected if present):
telejournal run
telejournal run /path/to/config.yaml
CLI overrides (highest priority):
telejournal run \
--telegram-token your_token \
--storage-provider obsidian_vault \
--obsidian-vault-root /path/to/vault \
--allowed-user-ids 123456,987654 \
--message-timestamp-window-seconds 60 \
--daily-brief-time-utc 09:00 \
--obsidian-vault-secure-file-permissions
GitHub storage provider example:
telejournal run \
--telegram-token your_token \
--storage-provider github_repo \
--github-owner your-org \
--github-repo your-journal-repo \
--github-branch main \
--github-batch-window-seconds 60 \
--github-token ghp_or_github_pat_token \
--allowed-user-ids 123456,987654
When using github_repo, note writes and media uploads are queued in-memory and
flushed in burst commits every batch_window_seconds (default: 60). Bot
feedback remains immediate, while GitHub API traffic is reduced.
During shutdown, telejournal performs a best-effort final flush of queued GitHub writes (for example, when receiving SIGTERM in container stop flows).
Telegram Commands
After the bot is running, these commands are available in your private chat:
/helpShow bot usage summary/setdateStart a guided date selection flow/setdate YYYY-MM-DD [HH:MM:SS]Set target note date/time directly/resetdateReturn to current day/tagsShow tag buttons/tags work kidsAdd/select one or more tags/moodOpen mood picker/showShow current effective day note and choose notes only or rendered output/show YYYY-MM-DDShow a specific day note and choose notes only or rendered output/todayinhistoryShow same-day note years and choose notes only or rendered output/deleteDelete last entry and show deleted content/delete day [YYYY-MM-DD]Delete full day note/settingsGuided runtime configuration fortag_choices,daily_brief_time_utc,prompt_for_mood_if_missing, andbot_menu_enabled- Changes are persisted immediately.
- If the bot started from a YAML config file, that file is backed up and updated.
- If no YAML config was used,
./config.yamlis created/updated.
Helpful CLI Commands
telejournal version
telejournal help
Using secret-tool
If you use Linux secret service (secret-tool), you can skip a local .env
and use secret-tool-run:
secret-tool-run telejournal run
Environment
Create a .env file:
TELEGRAM_TOKEN=your_bot_token
LOG_LEVEL=INFO
TELEGRAM_ALLOWED_USER_IDS=123456,987654
STORAGE_PROVIDER=obsidian_vault
STORAGE_OBSIDIAN_VAULT_ROOT=/path/to/obsidian/vault
Optional Environment Variables
MESSAGE_TIMESTAMP_WINDOW_SECONDS(default:60) - Messages within this window share the same timestampSTORAGE_OBSIDIAN_VAULT_SECURE_FILE_PERMISSIONS(default:true) - Set restrictive permissions (0o700/0o600) on vault directories and files for security. Applies only toobsidian_vaultprovider.DAILY_BRIEF_TIME_UTC(default:09:00) - Daily UTC time for historical same-day brief (HH:MMorHH:MM:SS). Set to0to disable.TAG_CHOICES(default:family,health,love,hobby,other,finance,social) - Comma-separated tag choices used by inline tag buttons.PROMPT_FOR_MOOD_IF_MISSING(default:true) - Enable/disable automatic mood prompts after entry writes and timer checks.BOT_MENU_ENABLED(default:true) - Enable/disable Telegram command menu publishing at startup. When disabled, the bot removes its command menu.- GitHub provider variables:
STORAGE_GITHUB_OWNERSTORAGE_GITHUB_REPOSTORAGE_GITHUB_BRANCH(default:main)STORAGE_GITHUB_TOKEN(required forgithub_repo)STORAGE_GITHUB_PATH_PREFIX(optional)STORAGE_GITHUB_API_BASE_URL(default:https://api.github.com)STORAGE_GITHUB_BATCH_WINDOW_SECONDS(default:60)
For github_repo, use a fine-grained personal access token scoped to exactly one target repository, with Contents read/write permissions.
At startup, telejournal attempts to detect repository visibility and logs a warning if the configured repository is public.
For troubleshooting GitHub batching, set LOG_LEVEL=DEBUG to inspect queue and
flush activity (queue size, flush cycle, and retry logs).
Configuration
The bot supports multiple configuration sources with a clear priority order:
Configuration Priority (highest to lowest)
- CLI Arguments - Command-line options override all other sources
- YAML File - Configuration file specified via
configargument - Environment Variables - Settings from
.envfile - Defaults - Built-in defaults for optional settings
Later sources override earlier ones. For example, if you specify --telegram-token on the command line, it will override the TELEGRAM_TOKEN environment variable.
YAML Configuration
You can provide a config.yaml file for more organized configuration management. The bot automatically looks for ./config.yaml if no config path is specified.
Example config.yaml:
telegram_token: "${TELEGRAM_TOKEN}" # Supports environment variable expansion
allowed_user_ids:
- 123456
- 987654
storage:
provider: obsidian_vault # obsidian_vault or github_repo
obsidian_vault:
root: /path/to/obsidian/vault
secure_file_permissions: true
github_repo:
owner: your-org
repo: your-journal-repo
branch: main
token: "${STORAGE_GITHUB_TOKEN}"
path_prefix: ""
api_base_url: https://api.github.com
batch_window_seconds: 60
log_level: INFO
message_timestamp_window_seconds: 60
daily_brief_time_utc: "0"
tag_choices: ["family", "health", "love", "hobby", "other", "finance", "social"]
prompt_for_mood_if_missing: true
bot_menu_enabled: true
Configuration Keys:
telegram_token(required) - Your Telegram bot tokenallowed_user_ids(required) - List of Telegram user IDs that can use the botstorage(required) - Hierarchical storage provider configurationstorage.provider-obsidian_vaultorgithub_repostorage.obsidian_vault.root- Filesystem root for vault storagestorage.obsidian_vault.secure_file_permissions- Restrictive perms toggle for vault storagestorage.github_repo.owner- GitHub owner/orgstorage.github_repo.repo- GitHub repo namestorage.github_repo.branch- Branch for writes (defaultmain)storage.github_repo.token- Fine-grained token scoped to the target repo with Contents read/writestorage.github_repo.path_prefix- Optional sub-folder inside the repositorystorage.github_repo.api_base_url- API base URL (defaulthttps://api.github.com)storage.github_repo.batch_window_seconds- In-memory queue flush window in seconds (default60)
log_level(optional, default:INFO) - Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)message_timestamp_window_seconds(optional, default:60) - Messages within this window share the same timestampdaily_brief_time_utc(optional, default:09:00) - Daily UTC time for the historical same-day brief (HH:MMorHH:MM:SS), or0to disabletag_choices(optional, default:family,health,love,hobby,other,finance,social) - List of inline tag button choicesprompt_for_mood_if_missing(optional, default:true) - Enable/disable mood prompts when entries exist without moodbot_menu_enabled(optional, default:true) - Enable/disable Telegram command menu publishing at startup
Environment Variable Expansion:
YAML configuration supports ${VAR_NAME} syntax for environment variable expansion:
telegram_token: "${TELEGRAM_TOKEN}"
storage:
provider: obsidian_vault
obsidian_vault:
root: "${STORAGE_OBSIDIAN_VAULT_ROOT}"
This allows you to keep sensitive values in environment variables while using a configuration file for other settings.
Test
uv run pytest
# With full coverage and type checking
bash validate.sh
Docker
You can run the bot in Docker using either docker run or docker compose.
Using Docker Compose
-
Create a
.env.dockerfile with your bot token and settings:TELEGRAM_TOKEN=your_bot_token STORAGE_PROVIDER=obsidian_vault STORAGE_OBSIDIAN_VAULT_ROOT=/data LOG_LEVEL=INFO TELEGRAM_ALLOWED_USER_IDS=123456,987654 STORAGE_OBSIDIAN_VAULT_SECURE_FILE_PERMISSIONS=false # This will avoid permission issues when running as non-root, but use with caution!
-
Create an
obsidian-journaldirectory in the same location as yourdocker-compose.ymlto serve as your vault, and set permissions so the container can write to it:mkdir obsidian-journal chmod 777 obsidian-journal # Use with caution, or set specific user/group permissions as needed
-
Start the container:
docker compose up --build
This will mount your Obsidian vault from ./obsidian-journal to /data inside the container.
On SELinux-enabled Linux distributions (for example Fedora/RHEL), make sure
the bind mount uses :Z in docker-compose.yml:
volumes:
- ./obsidian-journal:/data:Z
Using Docker Run
-
Build the image:
docker build -t telejournal:latest .
-
Run the container:
docker run -d \ --env-file .env.docker \ -v "$PWD"/obsidian-journal:/data:Z \ --name telejournal \ telejournal:latest
This will start the bot in detached mode, using your local .env.docker file and mounting your Obsidian vault.
Note: If you see
pull access denied for telejournal, you must build the image first:docker build -t telejournal:latest .Then run the container as shown above.
For troubleshooting, check logs with:
docker logs telejournal
Signalbackup-Tools HTML Import Utility
A utility is provided to convert HTML exports generated by signalbackup-tools to Obsidian-compatible Markdown, preserving timestamps, attachments, and replies. This is useful for importing Signal chat history into your journal vault.
Usage
python tools/signalbackup-tool-import/html_to_markdown.py <input_html_file> <output_directory>
<input_html_file>: Path to your HTML export from signalbackup-tools (e.g.,html/self.html)<output_directory>: Directory where year folders and markdown files will be created
Example:
python tools/signalbackup-tool-import/html_to_markdown.py html/self.html obsidian-journal
This will create:
- Year folders (e.g.,
2022/,2023/) with daily markdown files - An
attachments/folder in each year for media files
See the script for more details and options.
Running as a Systemd Service
To run the Telegram Journal Bot as a background service on Linux, you can use systemd. This ensures the bot starts on boot and restarts automatically if it fails.
Automated Service File Generation (Recommended)
The install-service command generates the service file automatically with sensible defaults:
telejournal install-service
This will:
- Create the service file at
/etc/systemd/system/telejournal.service - Use your current user account
- Set working directory to
~/obsidian-journal - Use
.envfrom your home directory - Automatically detect the
telejournalexecutable path
You can customize these defaults:
# Use custom paths and user
telejournal install-service \
--user myuser \
--working-directory /obsidian-journal \
--environment-file /telejournal/.env \
--execstart "/home/myuser/.venv/bin/telejournal run"
After running the command, follow the on-screen instructions to enable and start the service.
Manual Service File Creation
Alternatively, you can manually create a service file at /etc/systemd/system/telejournal.service with the following content (adjust paths and user as needed):
[Unit]
Description=Telegram Journal Bot
After=network.target
[Service]
Type=simple
User=youruser
WorkingDirectory=/home/youruser/obsidian-journal
EnvironmentFile=/home/youruser/.env
ExecStart=/home/youruser/.venv/bin/telejournal run
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Configuration details:
User- The user account that will run the bot (should own the vault directory)WorkingDirectory- Your Obsidian vault root directory (where notes are stored)EnvironmentFile- Path to your.envfile with required environment variablesExecStart- Full path to thetelejournalcommand (installed in your virtual environment)RestartSec- Wait 5 seconds before restarting on failure
If you installed telejournal system-wide via pip, you can use just telejournal run without the full path.
Enable and Start the Service
sudo systemctl daemon-reload
sudo systemctl enable telejournal.service
sudo systemctl start telejournal.service
Check logs with:
journalctl -u telejournal.service -f
This will keep the bot running in the background and restart it automatically on failure or reboot.
Project details
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 telejournal-0.0.3.tar.gz.
File metadata
- Download URL: telejournal-0.0.3.tar.gz
- Upload date:
- Size: 435.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.0.1 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
16ecbb1f87ed706e97b9c76217597469d1007c4f56da328421b93e7174eed8e6
|
|
| MD5 |
29fe694ca0d05785569f51f9a719242e
|
|
| BLAKE2b-256 |
68235b07e3148c763311caceea38dd2c2d67206a1742862fa6527ce887839c4c
|
Provenance
The following attestation bundles were made for telejournal-0.0.3.tar.gz:
Publisher:
pypi.yml on hugobatista/telejournal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
telejournal-0.0.3.tar.gz -
Subject digest:
16ecbb1f87ed706e97b9c76217597469d1007c4f56da328421b93e7174eed8e6 - Sigstore transparency entry: 1191075618
- Sigstore integration time:
-
Permalink:
hugobatista/telejournal@1536241f74da60d6191c603c10bbf890dd66c79c -
Branch / Tag:
refs/tags/v0.0.3 - Owner: https://github.com/hugobatista
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@1536241f74da60d6191c603c10bbf890dd66c79c -
Trigger Event:
release
-
Statement type:
File details
Details for the file telejournal-0.0.3-py3-none-any.whl.
File metadata
- Download URL: telejournal-0.0.3-py3-none-any.whl
- Upload date:
- Size: 68.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.0.1 CPython/3.12.8
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
00c778b961941aa549be60e445dfafd56f97a37533624f2d294e546544e4158d
|
|
| MD5 |
bbbc54dec4ee15304f62991e2f6d2049
|
|
| BLAKE2b-256 |
0a2dea17c14e9944d473b6a6ff14b380013da5b0b64f1a3b96e188df07948470
|
Provenance
The following attestation bundles were made for telejournal-0.0.3-py3-none-any.whl:
Publisher:
pypi.yml on hugobatista/telejournal
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
telejournal-0.0.3-py3-none-any.whl -
Subject digest:
00c778b961941aa549be60e445dfafd56f97a37533624f2d294e546544e4158d - Sigstore transparency entry: 1191075621
- Sigstore integration time:
-
Permalink:
hugobatista/telejournal@1536241f74da60d6191c603c10bbf890dd66c79c -
Branch / Tag:
refs/tags/v0.0.3 - Owner: https://github.com/hugobatista
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@1536241f74da60d6191c603c10bbf890dd66c79c -
Trigger Event:
release
-
Statement type: