{"id":50214067,"url":"https://github.com/hugobatista/telejournal","last_synced_at":"2026-05-26T07:04:16.613Z","repository":{"id":344415296,"uuid":"1175495360","full_name":"hugobatista/telejournal","owner":"hugobatista","description":"Telegram bot that journals every private message into Obsidian daily notes.","archived":false,"fork":false,"pushed_at":"2026-05-22T21:40:08.000Z","size":717,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-22T23:38:34.004Z","etag":null,"topics":["bot","daily-notes","journal","notes","obsidian","telegram"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hugobatista.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null},"funding":{"github":"hugobatista"}},"created_at":"2026-03-07T19:39:40.000Z","updated_at":"2026-05-22T21:40:12.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/hugobatista/telejournal","commit_stats":null,"previous_names":["hugobatista/telejournal"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/hugobatista/telejournal","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hugobatista%2Ftelejournal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hugobatista%2Ftelejournal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hugobatista%2Ftelejournal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hugobatista%2Ftelejournal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hugobatista","download_url":"https://codeload.github.com/hugobatista/telejournal/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hugobatista%2Ftelejournal/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33508319,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T03:12:49.672Z","status":"ssl_error","status_checked_at":"2026-05-26T03:12:47.976Z","response_time":63,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bot","daily-notes","journal","notes","obsidian","telegram"],"created_at":"2026-05-26T07:04:15.218Z","updated_at":"2026-05-26T07:04:16.593Z","avatar_url":"https://github.com/hugobatista.png","language":"Python","funding_links":["https://github.com/sponsors/hugobatista"],"categories":[],"sub_categories":[],"readme":"[![PyPI - Version](https://img.shields.io/pypi/v/telejournal.svg)](https://pypi.org/project/telejournal)\n[![GitHub Tag](https://img.shields.io/github/v/tag/hugobatista/telejournal?logo=github\u0026label=latest)](https://go.hugobatista.com/gh/telejournal/releases)\n[![GHCR Tag](https://img.shields.io/github/v/tag/hugobatista/telejournal?logo=docker\u0026logoColor=white\u0026label=GHCR)](https://go.hugobatista.com/gh/telejournal/packages)\n[![Test](https://go.hugobatista.com/gh/telejournal/actions/workflows/test.yml/badge.svg)](https://go.hugobatista.com/gh/telejournal/actions/workflows/test.yml)\n[![Lint](https://go.hugobatista.com/gh/telejournal/actions/workflows/lint.yml/badge.svg)](https://go.hugobatista.com/gh/telejournal/actions/workflows/lint.yml)\n# Telegram Journal Bot\n\n**Capture thoughts on Telegram, persist them in Obsidian, GitHub, OneDrive, or Google Drive, and never lose a moment again.**\n\nTelejournal is a bot that journals every private message into daily markdown notes, persisting to a local Obsidian vault, a GitHub repository, OneDrive, or Google Drive. Designed for personal journaling and private note-taking with rich media support.\n\n## Demo\nUser messages sent in a private chat are captured by the bot, including text and media.\n\n![Telegram demo](docs/telejournal-telegram-demo.jpeg)\n\nCaptured content is appended to your daily note with timestamped entries and structured formatting.\n\n![Obsidian demo](docs/telejournal-obsidian-demo.jpeg)\n\n## Features\n\n- Private chat journal capture for text, photos, voice recordings, video messages (including circular video notes), and locations\n- UTC daily note partitioning at `YYYY/YYYY-MM-DD.md`\n- Media storage (photos, voice, video) in `YYYY/attachments/`\n- Pluggable storage providers:\n  - `obsidian_vault` (filesystem)\n  - `github_repo` (GitHub repository via REST API)\n  - `onedrive` (Microsoft OneDrive via Microsoft Graph API)\n  - `google_drive` (Google Drive via Drive API)\n- YAML frontmatter management for `mood`, `tags`, and `created`\n- In-memory state only (`context.chat_data` and `context.bot_data`)\n- Date override commands (`/setdate`, `/resetdate`)\n- Tags and mood management with inline keyboard callbacks\n- `/show` asks whether to show note text only or rendered (text + embedded attachments)\n- Displayed note timestamps are normalized from `%% HH:MM:SS %%` markers to `\u003eHH:MM:SS` for cleaner chat output\n- Daily UTC \"on this day\" brief sends a years summary and asks whether to show history as notes only or rendered\n- Replies to historical bot messages can include a quote plus a clickable source-note link back to the original daily note\n- Edited text messages update their original journal entry in place instead of creating a duplicate\n- Guided `/settings` command for runtime updates of tag choices, daily brief time, mood prompt toggle, and bot menu visibility\n- `/settings` updates are persisted to YAML so values survive bot restarts (existing config files are backed up before overwrite)\n\n## Usage\n\n### Installation\n\nChoose one installation method:\n\nFrom PyPI (recommended for end users):\n\n```bash\npython -m pip install --upgrade telejournal\n```\n\nFrom source (recommended for contributors):\n\n```bash\ngit clone https://github.com/hugobatista/telejournal.git\ncd telejournal\nuv sync --extra dev\n```\n\nIf running from source, use `uv run` before commands in the sections below.\nExample: `uv run telejournal run --verbose`.\n\n### Quick Start\n\n1. Configure your environment variables as documented in `Environment`.\n\n2. Start the bot:\n\n```bash\ntelejournal run --verbose\n```\n\n3. Open your bot in Telegram and send:\n\n```text\n/help\n```\n\n4. Send a normal message (for example, `First journal entry`) and confirm it appears in:\n\n```text\n\u003cSTORAGE_ROOT\u003e/YYYY/YYYY-MM-DD.md\n```\n\n### Run Modes\n\nUse whichever configuration style best fits your setup.\n\nEnvironment variables only:\n\n```bash\ntelejournal run\n```\n\nYAML configuration file (`config.yaml` auto-detected if present):\n\n```bash\ntelejournal run\ntelejournal run /path/to/config.yaml\n```\n\nCLI overrides (highest priority):\n\n```bash\ntelejournal run \\\n  --telegram-token your_token \\\n  --storage-provider obsidian_vault \\\n  --obsidian-vault-root /path/to/vault \\\n  --allowed-user-ids 123456,987654 \\\n  --message-timestamp-window-seconds 60 \\\n  --daily-brief-time-utc 09:00 \\\n  --obsidian-vault-secure-file-permissions\n```\n\nGitHub storage provider example:\n\n```bash\ntelejournal run \\\n  --telegram-token your_token \\\n  --storage-provider github_repo \\\n  --github-owner your-org \\\n  --github-repo your-journal-repo \\\n  --github-branch main \\\n  --github-batch-window-seconds 60 \\\n  --github-token ghp_or_github_pat_token \\\n  --allowed-user-ids 123456,987654\n```\n\nDetailed setup guide (repository + token):\n\n- [GitHub storage configuration](docs/github-storage.md)\n\nWhen using `github_repo`, note writes and media uploads are queued in-memory and\nflushed in burst commits every `batch_window_seconds` (default: `60`). Bot\nfeedback remains immediate, while GitHub API traffic is reduced.\n\nFor queued providers (`github_repo`, `onedrive`, and `google_drive`), the bot\nacknowledges new entries with `Queued to journal ✅` and sends\n`Flushed to journal ✅` after a successful provider flush cycle. For immediate\nstorage (`obsidian_vault`), the acknowledgment remains `Added to journal ✅`.\n\nDuring shutdown, telejournal performs a best-effort final flush of queued\nGitHub writes (for example, when receiving SIGTERM in container stop flows).\n\nOneDrive storage provider example:\n\n```bash\ntelejournal run \\\n  --telegram-token your_token \\\n  --storage-provider onedrive \\\n  --onedrive-client-id your_app_client_id \\\n  --onedrive-client-secret your_app_client_secret \\\n  --onedrive-tenant-id common \\\n  --onedrive-root-path Apps/telejournal \\\n  --onedrive-batch-window-seconds 60 \\\n  --allowed-user-ids 123456,987654\n```\n\nDetailed setup guide (app registration + credentials):\n\n- [OneDrive storage configuration](docs/onedrive-storage.md)\n\nWhen using `onedrive`, writes and media uploads are queued in-memory and\nflushed in bursts every `batch_window_seconds` (default: `60`).\n\nFor first-time setup, only `--onedrive-client-id` and\n`--onedrive-client-secret` are required. On startup, telejournal sends OneDrive\ndevice authorization instructions to allowed chats. Complete the browser flow,\nthen run `/storageauth complete` to capture and persist tokens.\n\nUseful OneDrive auth commands:\n\n- `/storageauth` or `/storageauth status` Show current authorization details\n- `/storageauth start` Start/restart the device code flow\n- `/storageauth complete` Poll once to finish authorization and persist tokens\n\nGoogle Drive storage provider example:\n\n```bash\ntelejournal run \\\n  --telegram-token your_token \\\n  --storage-provider google_drive \\\n  --google-drive-client-id your_google_client_id \\\n  --google-drive-client-secret your_google_client_secret \\\n  --google-drive-folder-id your_drive_folder_id \\\n  --google-drive-batch-window-seconds 60 \\\n  --allowed-user-ids 123456,987654\n```\n\nDetailed setup guide (app registration + credentials):\n\n- [Google Drive storage configuration](docs/google-drive-storage.md)\n\nWhen using `google_drive`, writes and media uploads are queued in-memory and\nflushed in bursts every `batch_window_seconds` (default: `60`).\n\nFor first-time setup, only `--google-drive-client-id` and\n`--google-drive-client-secret` are required. On startup, telejournal sends\nGoogle Drive device authorization instructions to allowed chats. Complete the\nbrowser flow, then run `/storageauth complete` to capture and persist tokens.\n\n### Telegram Commands\n\nAfter the bot is running, these commands are available in your private chat:\n\n- `/help` Show bot usage summary\n- `/setdate` Start a guided date selection flow\n- `/setdate YYYY-MM-DD [HH:MM:SS]` Set target note date/time directly\n- `/resetdate` Return to current day\n- `/tags` Show tag buttons\n- `/tags work kids` Add/select one or more tags\n- `/mood` Open mood picker\n- `/show` Show current effective day note and choose notes only or rendered output\n- `/show YYYY-MM-DD` Show a specific day note and choose notes only or rendered output\n- `/todayinhistory` Show same-day note years and choose notes only or rendered output\n- `/delete` Delete last entry and show deleted content\n- `/delete day [YYYY-MM-DD]` Delete full day note\n- `/settings` Guided runtime configuration for `tag_choices`, `daily_brief_time_utc`, `prompt_for_mood_if_missing`, and `bot_menu_enabled`\n- `/storageauth [start|complete|status]` Storage device authorization workflow (when `storage.provider` is `onedrive` or `google_drive`)\n  - Changes are persisted immediately.\n  - If the bot started from a YAML config file, that file is backed up and updated.\n  - If no YAML config was used, `./config.yaml` is created/updated.\n\n### Helpful CLI Commands\n\n```bash\ntelejournal version\ntelejournal help\n```\n\n### Using kleys\n\nIf you use a system keyring, you can skip a local `.env`\nand use [kleys](https://go.hugobatista.com/gh/kleys):\n\n```bash\nkleys telejournal run\n```\n\n\nInstead of using env file variables, you can also keep those secrets in a yaml format and use kleys with it:\n\n```bash\nkleys --secrets-file config.yaml uv run telejournal run config.yaml --verbose\n```\n\nYou even avoid the creation of config.yaml at all and use a file descriptor:\n\n```bash\nkleys uv run telejournal run @SECRETS@  --verbose\n```\nWhere `SECRETS` is a file descriptor containing the yaml configuration.\n\n## Environment\n\nCreate a `.env` file:\n\n```env\nTELEGRAM_TOKEN=your_bot_token\nLOG_LEVEL=INFO\nTELEGRAM_ALLOWED_USER_IDS=123456,987654\nSTORAGE_PROVIDER=obsidian_vault\nSTORAGE_OBSIDIAN_VAULT_ROOT=/path/to/obsidian/vault\n```\n\n### Optional Environment Variables\n\n- `MESSAGE_TIMESTAMP_WINDOW_SECONDS` (default: `60`) - Messages within this window share the same timestamp\n- `STORAGE_OBSIDIAN_VAULT_SECURE_FILE_PERMISSIONS` (default: `true`) - Set restrictive permissions (0o700/0o600) on vault directories and files for security. Applies only to `obsidian_vault` provider.\n- `DAILY_BRIEF_TIME_UTC` (default: `09:00`) - Daily UTC time for historical same-day brief (`HH:MM` or `HH:MM:SS`). Set to `0` to disable.\n- `TAG_CHOICES` (default: `family,health,love,hobby,other,finance,social`) - Comma-separated tag choices used by inline tag buttons.\n- `PROMPT_FOR_MOOD_IF_MISSING` (default: `true`) - Enable/disable automatic mood prompts after entry writes and timer checks.\n- `BOT_MENU_ENABLED` (default: `true`) - Enable/disable Telegram command menu publishing at startup. When disabled, the bot removes its command menu.\n- GitHub provider variables:\n  - `STORAGE_GITHUB_OWNER`\n  - `STORAGE_GITHUB_REPO`\n  - `STORAGE_GITHUB_BRANCH` (default: `main`)\n  - `STORAGE_GITHUB_TOKEN` (required for `github_repo`)\n  - `STORAGE_GITHUB_PATH_PREFIX` (optional)\n  - `STORAGE_GITHUB_API_BASE_URL` (default: `https://api.github.com`)\n  - `STORAGE_GITHUB_BATCH_WINDOW_SECONDS` (default: `60`)\n- OneDrive provider variables:\n  - `STORAGE_ONEDRIVE_TENANT_ID` (default: `common`)\n  - `STORAGE_ONEDRIVE_CLIENT_ID` (required for `onedrive`)\n  - `STORAGE_ONEDRIVE_CLIENT_SECRET` (required for `onedrive`)\n  - `STORAGE_ONEDRIVE_ROOT_PATH` (default: `Apps/telejournal`)\n  - `STORAGE_ONEDRIVE_API_BASE_URL` (default: `https://graph.microsoft.com/v1.0`)\n  - `STORAGE_ONEDRIVE_BATCH_WINDOW_SECONDS` (default: `60`)\n  - `STORAGE_ONEDRIVE_ACCESS_TOKEN` (optional cached access token)\n  - `STORAGE_ONEDRIVE_REFRESH_TOKEN` (optional cached refresh token)\n  - `STORAGE_ONEDRIVE_TOKEN_EXPIRES_AT_UTC` (optional UTC expiry `YYYY-MM-DDTHH:MM:SSZ`)\n- Google Drive provider variables:\n  - `STORAGE_GOOGLE_DRIVE_CLIENT_ID` (required for `google_drive`)\n  - `STORAGE_GOOGLE_DRIVE_CLIENT_SECRET` (required for `google_drive`)\n  - `STORAGE_GOOGLE_DRIVE_FOLDER_ID` (optional; defaults to My Drive root)\n  - `STORAGE_GOOGLE_DRIVE_BATCH_WINDOW_SECONDS` (default: `60`)\n  - `STORAGE_GOOGLE_DRIVE_ACCESS_TOKEN` (optional cached access token)\n  - `STORAGE_GOOGLE_DRIVE_REFRESH_TOKEN` (optional cached refresh token)\n  - `STORAGE_GOOGLE_DRIVE_TOKEN_EXPIRES_AT_UTC` (optional UTC expiry `YYYY-MM-DDTHH:MM:SSZ`)\n\nFor `github_repo`, use a fine-grained personal access token scoped to exactly one target repository, with `Contents` read/write permissions.\n\nAt startup, telejournal attempts to detect repository visibility and logs a warning if the configured repository is public.\n\nFor troubleshooting GitHub batching, set `LOG_LEVEL=DEBUG` to inspect queue and\nflush activity (queue size, flush cycle, and retry logs).\n\n## Configuration\n\nThe bot supports multiple configuration sources with a clear priority order:\n\n### Configuration Priority (highest to lowest)\n\n1. **CLI Arguments** - Command-line options override all other sources\n2. **YAML File** - Configuration file specified via `config` argument\n3. **Environment Variables** - Settings from `.env` file\n4. **Defaults** - Built-in defaults for optional settings\n\nLater sources override earlier ones. For example, if you specify `--telegram-token` on the command line, it will override the `TELEGRAM_TOKEN` environment variable.\n\n### YAML Configuration\n\nYou can provide a `config.yaml` file for more organized configuration management. The bot automatically looks for `./config.yaml` if no config path is specified.\n\n**Example `config.yaml`:**\n\n```yaml\ntelegram_token: \"${TELEGRAM_TOKEN}\"  # Supports environment variable expansion\nallowed_user_ids:\n  - 123456\n  - 987654\nstorage:\n  provider: obsidian_vault # obsidian_vault, github_repo, onedrive, or google_drive\n  obsidian_vault:\n    root: /path/to/obsidian/vault\n    secure_file_permissions: true\n  github_repo:\n    owner: your-org\n    repo: your-journal-repo\n    branch: main\n    token: \"${STORAGE_GITHUB_TOKEN}\"\n    path_prefix: \"\"\n    api_base_url: https://api.github.com\n    batch_window_seconds: 60\n  onedrive:\n    tenant_id: common\n    client_id: \"${STORAGE_ONEDRIVE_CLIENT_ID}\"\n    client_secret: \"${STORAGE_ONEDRIVE_CLIENT_SECRET}\"\n    root_path: Apps/telejournal\n    api_base_url: https://graph.microsoft.com/v1.0\n    batch_window_seconds: 60\n    access_token: \"${STORAGE_ONEDRIVE_ACCESS_TOKEN}\"\n    refresh_token: \"${STORAGE_ONEDRIVE_REFRESH_TOKEN}\"\n    token_expires_at_utc: \"${STORAGE_ONEDRIVE_TOKEN_EXPIRES_AT_UTC}\"\n  google_drive:\n    client_id: \"${STORAGE_GOOGLE_DRIVE_CLIENT_ID}\"\n    client_secret: \"${STORAGE_GOOGLE_DRIVE_CLIENT_SECRET}\"\n    folder_id: \"${STORAGE_GOOGLE_DRIVE_FOLDER_ID}\"\n    batch_window_seconds: 60\n    access_token: \"${STORAGE_GOOGLE_DRIVE_ACCESS_TOKEN}\"\n    refresh_token: \"${STORAGE_GOOGLE_DRIVE_REFRESH_TOKEN}\"\n    token_expires_at_utc: \"${STORAGE_GOOGLE_DRIVE_TOKEN_EXPIRES_AT_UTC}\"\nlog_level: INFO\nmessage_timestamp_window_seconds: 60\ndaily_brief_time_utc: \"0\"\ntag_choices: [\"family\", \"health\", \"love\", \"hobby\", \"other\", \"finance\", \"social\"]\nprompt_for_mood_if_missing: true\nbot_menu_enabled: true\n```\n\n**Configuration Keys:**\n\n- `telegram_token` (required) - Your Telegram bot token\n- `allowed_user_ids` (required) - List of Telegram user IDs that can use the bot\n- `storage` (required) - Hierarchical storage provider configuration\n  - `storage.provider` - `obsidian_vault`, `github_repo`, `onedrive`, or `google_drive`\n  - `storage.obsidian_vault.root` - Filesystem root for vault storage\n  - `storage.obsidian_vault.secure_file_permissions` - Restrictive perms toggle for vault storage\n  - `storage.github_repo.owner` - GitHub owner/org\n  - `storage.github_repo.repo` - GitHub repo name\n  - `storage.github_repo.branch` - Branch for writes (default `main`)\n  - `storage.github_repo.token` - Fine-grained token scoped to the target repo with Contents read/write\n  - `storage.github_repo.path_prefix` - Optional sub-folder inside the repository\n  - `storage.github_repo.api_base_url` - API base URL (default `https://api.github.com`)\n  - `storage.github_repo.batch_window_seconds` - In-memory queue flush window in seconds (default `60`)\n  - `storage.onedrive.tenant_id` - Microsoft tenant ID (default `common`)\n  - `storage.onedrive.client_id` - Microsoft app client ID\n  - `storage.onedrive.client_secret` - Microsoft app client secret\n  - `storage.onedrive.root_path` - Root folder path inside OneDrive (default `Apps/telejournal`)\n  - `storage.onedrive.api_base_url` - Graph API base URL (default `https://graph.microsoft.com/v1.0`)\n  - `storage.onedrive.batch_window_seconds` - In-memory queue flush window in seconds (default `60`)\n  - `storage.onedrive.access_token` - Cached access token (optional)\n  - `storage.onedrive.refresh_token` - Cached refresh token (optional)\n  - `storage.onedrive.token_expires_at_utc` - Access token UTC expiry in `YYYY-MM-DDTHH:MM:SSZ` format (optional)\n  - `storage.google_drive.client_id` - Google OAuth client ID\n  - `storage.google_drive.client_secret` - Google OAuth client secret\n  - `storage.google_drive.folder_id` - Drive folder ID target (optional)\n  - `storage.google_drive.batch_window_seconds` - In-memory queue flush window in seconds (default `60`)\n  - `storage.google_drive.access_token` - Cached access token (optional)\n  - `storage.google_drive.refresh_token` - Cached refresh token (optional)\n  - `storage.google_drive.token_expires_at_utc` - Access token UTC expiry in `YYYY-MM-DDTHH:MM:SSZ` format (optional)\n- `log_level` (optional, default: `INFO`) - Logging level (DEBUG, INFO, WARNING, ERROR, CRITICAL)\n- `message_timestamp_window_seconds` (optional, default: `60`) - Messages within this window share the same timestamp\n- `daily_brief_time_utc` (optional, default: `09:00`) - Daily UTC time for the historical same-day brief (`HH:MM` or `HH:MM:SS`), or `0` to disable\n- `tag_choices` (optional, default: `family,health,love,hobby,other,finance,social`) - List of inline tag button choices\n- `prompt_for_mood_if_missing` (optional, default: `true`) - Enable/disable mood prompts when entries exist without mood\n- `bot_menu_enabled` (optional, default: `true`) - Enable/disable Telegram command menu publishing at startup\n\n**Environment Variable Expansion:**\n\nYAML configuration supports `${VAR_NAME}` syntax for environment variable expansion:\n\n```yaml\ntelegram_token: \"${TELEGRAM_TOKEN}\"\nstorage:\n  provider: obsidian_vault\n  obsidian_vault:\n    root: \"${STORAGE_OBSIDIAN_VAULT_ROOT}\"\n```\n\nThis allows you to keep sensitive values in environment variables while using a configuration file for other settings.\n\n## Test\n\n```bash\nhatch run test\n\n# Run lint only\nhatch run lint\n\n# With full coverage and type checking\nbash validate.sh\n```\n\n## Docker\n\nYou can run the bot in Docker using either `docker run` or `docker compose`.\n\n### Using Docker Compose\n\n1. Create a `.env.docker` file with your bot token and settings:\n\n    ```env\n    TELEGRAM_TOKEN=your_bot_token\n    STORAGE_PROVIDER=obsidian_vault\n    STORAGE_OBSIDIAN_VAULT_ROOT=/data\n    LOG_LEVEL=INFO\n    TELEGRAM_ALLOWED_USER_IDS=123456,987654\n    STORAGE_OBSIDIAN_VAULT_SECURE_FILE_PERMISSIONS=false # This will avoid permission issues when running as non-root, but use with caution!\n    ```\n\n2. Create an `obsidian-journal` directory in the same location as your `docker-compose.yml` to serve as your vault, and set permissions so the container can write to it:\n\n    ```bash\n    mkdir obsidian-journal\n    chmod 777 obsidian-journal # Use with caution, or set specific user/group permissions as needed\n    ```\n\n2. Start the container:\n\n    ```bash\n    docker compose up --build\n    ```\n\nThis will mount your Obsidian vault from `./obsidian-journal` to `/data` inside the container.\n\nOn SELinux-enabled Linux distributions (for example Fedora/RHEL), make sure\nthe bind mount uses `:Z` in `docker-compose.yml`:\n\n```yaml\nvolumes:\n    - ./obsidian-journal:/data:Z\n```\n\n### Using Docker Run\n\n1. Build the image:\n\n    ```bash\n    docker build -t telejournal:latest .\n    ```\n\n2. Run the container:\n\n    ```bash\n    docker run -d \\\n      --env-file .env.docker \\\n            -v \"$PWD\"/obsidian-journal:/data:Z \\\n      --name telejournal \\\n      telejournal:latest\n    ```\n\nThis will start the bot in detached mode, using your local `.env.docker` file and mounting your Obsidian vault.\n\n\u003e **Note:** If you see `pull access denied for telejournal`, you must build the image first:\n\u003e\n\u003e ```bash\n\u003e docker build -t telejournal:latest .\n\u003e ```\n\u003e Then run the container as shown above.\n\nFor troubleshooting, check logs with:\n\n```bash\ndocker logs telejournal\n```\n\n## Signalbackup-Tools HTML Import Utility\n\nA utility is provided to convert HTML exports generated by [signalbackup-tools](https://github.com/bepaald/signalbackup-tools) to Obsidian-compatible Markdown, preserving timestamps, attachments, and replies. This is useful for importing Signal chat history into your journal vault.\n\n### Usage\n\n```bash\npython tools/signalbackup-tool-import/html_to_markdown.py \u003cinput_html_file\u003e \u003coutput_directory\u003e\n```\n\n- `\u003cinput_html_file\u003e`: Path to your HTML export from signalbackup-tools (e.g., `html/self.html`)\n- `\u003coutput_directory\u003e`: Directory where year folders and markdown files will be created\n\nExample:\n\n```bash\npython tools/signalbackup-tool-import/html_to_markdown.py html/self.html obsidian-journal\n```\n\nThis will create:\n- Year folders (e.g., `2022/`, `2023/`) with daily markdown files\n- An `attachments/` folder in each year for media files\n\nSee the script for more details and options.\n\n## Running as a Systemd Service\n\nTo 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.\n\n### Automated Service File Generation (Recommended)\n\nThe `install-service` command generates the service file automatically with sensible defaults:\n\n```bash\ntelejournal install-service\n```\n\nThis will:\n- Create the service file at `/etc/systemd/system/telejournal.service`\n- Use your current user account\n- Set working directory to `~/obsidian-journal`\n- Use `.env` from your home directory\n- Automatically detect the `telejournal` executable path\n\nYou can customize these defaults:\n\n```bash\n# Use custom paths and user\ntelejournal install-service \\\n  --user myuser \\\n  --working-directory /obsidian-journal \\\n  --environment-file /telejournal/.env \\\n  --execstart \"/home/myuser/.venv/bin/telejournal run\"\n\n```\n\nAfter running the command, follow the on-screen instructions to enable and start the service.\n\n### Manual Service File Creation\n\nAlternatively, you can manually create a service file at `/etc/systemd/system/telejournal.service` with the following content (adjust paths and user as needed):\n\n```ini\n[Unit]\nDescription=Telegram Journal Bot\nAfter=network.target\n\n[Service]\nType=simple\nUser=youruser\nWorkingDirectory=/home/youruser/obsidian-journal\nEnvironmentFile=/home/youruser/.env\nExecStart=/home/youruser/.venv/bin/telejournal run\nRestart=on-failure\nRestartSec=5\n\n[Install]\nWantedBy=multi-user.target\n```\n\n**Configuration details:**\n\n- `User` - The user account that will run the bot (should own the vault directory)\n- `WorkingDirectory` - Your Obsidian vault root directory (where notes are stored)\n- `EnvironmentFile` - Path to your `.env` file with required environment variables\n- `ExecStart` - Full path to the `telejournal` command (installed in your virtual environment)\n- `RestartSec` - Wait 5 seconds before restarting on failure\n\nIf you installed `telejournal` system-wide via pip, you can use just `telejournal run` without the full path.\n\n### Enable and Start the Service\n\n```bash\nsudo systemctl daemon-reload\nsudo systemctl enable telejournal.service\nsudo systemctl start telejournal.service\n```\n\nCheck logs with:\n\n```bash\njournalctl -u telejournal.service -f\n```\n\nThis will keep the bot running in the background and restart it automatically on failure or reboot.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhugobatista%2Ftelejournal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhugobatista%2Ftelejournal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhugobatista%2Ftelejournal/lists"}