Skip to main content

Telexporter

Get instant Telegram notifications for new emails.

Telexporter watches one or more IMAP mailboxes using IMAP IDLE (push, not polling) and forwards a short summary of each new unread email to a Telegram forum topic. It never marks your mail as read and never downloads message bodies, only the Subject, From and Date headers.

Features

  • Multiple accounts: each IMAP account is routed to its own Telegram chat and topic.
  • Real-time: uses IMAP IDLE, so notifications arrive as soon as the server pushes them.
  • Non-intrusive: fetches headers only, with BODY.PEEK, so messages stay unread.
  • No duplicates: alerts are tracked in a local SQLite database, so restarts never re-send old notifications.
  • Heartbeat: if an account has been silent for longer than a configurable threshold, a "still alive" message is posted, so you can tell "no mail" from "the watcher is broken".
  • Resilient: automatic IMAP reconnection with backoff, and Telegram sends retried with exponential backoff.
  • Docker-ready: ships with a Dockerfile and a Compose file.

How it works

 IMAP account(s) ──IDLE──▶ Watcher ──▶ asyncio.Queue ──▶ Notifier ──▶ Telegram topic
                                                             │
                                                        SQLite (dedup)
                                                             ▲
                                              Heartbeat task (per account)

Each notification looks like this:

UID: 1234
2026-09-30T08:15:00+00:00
Subject
  Your invoice is ready
From: (billing@example.com)

Requirements

  • Python 3.12+ (or Docker)
  • A Telegram bot token from @BotFather
  • A Telegram supergroup with Topics enabled, with your bot added to it and allowed to post
  • IMAP access (over SSL/TLS) to the mailbox(es) you want to watch. Many providers require an app-specific password.

Telegram setup

  1. Talk to @BotFather, run /newbot, and copy the bot token.
  2. Create a Telegram group, convert it to a supergroup, and enable Topics in the group settings.
  3. Add your bot to the group and give it permission to send messages (making it an admin is the simplest way).
  4. Create one topic per mailbox you want to watch.
  5. Find the chat ID (supergroup IDs are negative, e.g. -1001234567890) and the topic ID (the ID of the topic's first message / thread). Bots such as @RawDataBot or @getidsbot can show these values.

Installation

The image is published to the GitHub Container Registry:

docker pull ghcr.io/zeid-m/telexporter:latest

Using the files in the docker/ directory:

git clone https://github.com/Zeid-M/telexporter.git
cd telexporter/docker

# 1. Start once so the default configuration is copied to /etc/telexporter on the host
docker compose up -d
docker compose down

# 2. Edit your configuration (see "Configuration" below)
sudo nano /etc/telexporter/config.toml

# 3. Start for real
docker compose up -d

How the Compose setup is wired:

What Where
Configuration Host /etc/telexporter is mounted at the container's TELEXPORTER_ROOT_PATH_FOR_DYNACONF (/etc/telexporter). On first start, if the folder is empty, the default config is copied into it.
SQLite state Named volume telexporter_sqlite_data at /code/db (database file: /code/db/alerts.db).
Restart policy unless-stopped
Environment Read from docker/.env

To build the image yourself:

./scripts/build_registry_image.sh

The Dockerfile installs Telexporter from PyPI (pip install telexporter), so it builds whatever version is published there.

Option 2: pip

pip install telexporter

Then create a config file and point Telexporter at it (see below):

mkdir -p ~/.config/telexporter
# create ~/.config/telexporter/config.toml (see the "Configuration" section)

export TELEXPORTER_ROOT_PATH_FOR_DYNACONF=$HOME/.config/telexporter
export TELEXPORTER_SQLITE_DB_PATH=$HOME/.config/telexporter/alerts.db

start_telexporter

Option 3: From source

git clone https://github.com/Zeid-M/telexporter.git
cd telexporter
pip install -e .

Configuration

Telexporter is configured with a single TOML file, config.toml, loaded with Dynaconf. A documented template lives in src/telexporter/config/config.toml.

Environment variables

Variable Required Description
TELEXPORTER_ROOT_PATH_FOR_DYNACONF yes Directory containing config.toml.
TELEXPORTER_SQLITE_DB_PATH yes Path of the SQLite file used to remember which emails were already sent. The parent directory must exist.
OMNILOGGER_ROOT_PATH_FOR_DYNACONF for logging config Directory containing the logger configuration (logger_config.json, logger_settings.toml). Used by the Docker image (/etc/omni_logger).

Any setting can also be overridden through environment variables with the TELEXPORTER_ prefix, following Dynaconf conventions (for example TELEXPORTER_TELEGRAM__BOT_TOKEN).

Example config.toml

[[accounts]]
name = "Personal"
host = "imap.mail1.com"
user = "alice@example.com"
password = "app-specific-password"
verify_ssl = true
backoff_interval = 15
idle_timeout = 1800
chat_id = -1001234567890
topic_id = 2
heartbeat_threshold = 10800   # 3 hours

[[accounts]]
name = "Work"
host = "imap.mail2.com"
user = "bob@work.com"
password = "another-password"
verify_ssl = false            # e.g. self-signed certificate
backoff_interval = 15
idle_timeout = 1800
chat_id = -1009876543210
topic_id = 1
heartbeat_threshold = 10800

[telegram]
bot_token = "123456:ABC-your-token"
retry_attempts = 10
wait_multiplier = 3
wait_max_time = 60
heartbeat_check_interval = 60

[system]
queue_maxsize = 300

Account options ([[accounts]])

Add one table per mailbox. All fields are required.

Field Type Description
name string Label used in logs and heartbeat messages.
host string IMAP server hostname (connects over SSL).
user string IMAP username, usually the full email address.
password string IMAP password or app-specific password.
verify_ssl bool true verifies certificates. false disables hostname and certificate checks (use only for self-signed servers).
backoff_interval int (s) Wait time before reconnecting after an IMAP or network error.
idle_timeout int (s) How long each IDLE session lasts before it is renewed. Keep it below your server's IDLE limit (commonly 29 min, hence 1800 or less).
chat_id int Telegram chat / supergroup ID (negative for supergroups).
topic_id int Forum topic ID that receives this account's notifications.
heartbeat_threshold int (s) Silence duration after which a heartbeat message is sent.

[telegram]

Field Description
bot_token Bot token from @BotFather.
retry_attempts Maximum send attempts per notification.
wait_multiplier Base seconds for randomized exponential backoff between retries.
wait_max_time Maximum backoff delay in seconds.
heartbeat_check_interval Seconds between heartbeat checks.

[system]

Field Description
queue_maxsize Maximum size of the internal alert queue.

The configuration is validated at startup, and Telexporter exits with a descriptive error if anything is missing or has the wrong type.

Heartbeat behavior

The heartbeat task checks every heartbeat_check_interval seconds. It posts a message such as:

🟢 [Personal] no new emails — still alive!

when the last recorded event for the account (an email alert or a previous heartbeat) is older than heartbeat_threshold. On the very first run, with nothing recorded yet, one heartbeat is sent immediately. Heartbeats are recorded, so they repeat at most once per threshold period.

Logging

Logging is provided by omni-logger. By default logs are written to /var/log/telexporter and the logger configuration is refreshed every 15 minutes. Both are adjustable in logger_settings.toml:

[production.logger_config]
logs_directory = "/var/log/telexporter"
refresh_enabled = true
refresh_interval = 900

Security notes

  • config.toml contains plaintext credentials (IMAP passwords and the bot token). Restrict its permissions (chmod 600) and never commit it.
  • Use app-specific passwords where your provider supports them.
  • Only set verify_ssl = false for servers you trust; it disables protection against man-in-the-middle attacks.
  • Notifications contain the sender and subject of your emails, so make sure only the right people are in the Telegram group.

Limitations

  • Only the INBOX folder is watched.
  • Only IMAP over SSL/TLS is supported.
  • Only unread messages trigger notifications.
  • Telegram forum topics are required (topic_id is mandatory).

Development

git clone https://github.com/Zeid-M/telexporter.git
cd telexporter
python -m venv .venv && source .venv/bin/activate
pip install -e .

export TELEXPORTER_ROOT_PATH_FOR_DYNACONF=./src/telexporter/config
export TELEXPORTER_SQLITE_DB_PATH=./alerts.db
start_telexporter

Build and publish the package:

python -m pip install build twine
python -m build
twine upload dist/*

Contributing

Issues and pull requests are welcome at github.com/Zeid-M/telexporter/issues.

License

Released under the MIT License. Copyright © 2026 Mhd Zeid Mahfouz.

Metadata

Release files for telexporter 1.0.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for telexporter 1.0.3
File Size Uploaded
telexporter-1.0.3.tar.gz 21.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for telexporter 1.0.3
File Interpreter ABI Platform
telexporter-1.0.3-py3-none-any.whl Python 3 none any Details

Total release size: 40.8 kB

Release files / telexporter-1.0.3.tar.gz

Download URL telexporter-1.0.3.tar.gz
Size 21.2 kB
Tags Source
SHA-256 checksum
How to use checksums
b58dec94985a3725e9fab3588bbd2b4f14051ab368c15733c200c964d06b6d05
BLAKE2b-256 checksum
How to use checksums
8233563471efd73234e8124f42d4009ad4057c6ac4d52c3f82f1769d98be11b3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Release files / telexporter-1.0.3-py3-none-any.whl

Download URL telexporter-1.0.3-py3-none-any.whl
Size 19.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8b01a2858c4c6cb081bc3c131d11efd13a115d1ee0d334784f1cdbe302cd6a54
BLAKE2b-256 checksum
How to use checksums
9b341eb97d7b629146b3528bf754ed94a8c8f61321c22014d9eda9e412352777
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.13.5

Release history Release notifications | RSS feed

This release

1.0.3 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page