Skip to main content

tgpost

Send messages and files to Telegram channels, now or on a schedule.


Disclaimer

Provided AS IS, without warranty of any kind, express or implied. Use entirely at your own risk. The author accepts no liability for data loss, content published to the wrong channel or audience, business interruption or consequential damages.

You are responsible for verifying the target channel, for what you publish, for being authorised to post there, and for complying with the Telegram Terms of Service. tgpost schedule and tgpost daemon publish unattended: a mistaken cron expression can post to a live channel repeatedly until someone stops it. Test against a private channel first.

Not certified for regulated, forensic, safety-critical or high-assurance use. There is no delivery guarantee: the Bot API offers no idempotency key, so a send that times out may or may not have arrived.

The MIT LICENSE is the governing text and prevails wherever this summary differs from it. See DISCLAIMER.md for the full text.


Why this exists

Sending a file to a Telegram channel from the command line is well covered already. What is missing is doing it later, reliably.

The Telegram HTTP Bot API has no server-side scheduling: there is no schedule_date parameter on any send method, so "post this at 9am on weekdays" has to be solved on your side, and survive a reboot. tgpost is a sender with a persistent job store attached, so a scheduled post still goes out after the machine restarts.

If you only need to send something right now, telegram-send and apprise both do that well.

Install

pip install tgpost              # sending only
pip install tgpost[schedule]    # sending and scheduling

Requires Python 3.11 or newer.

Getting started

Create a bot with @BotFather, then add it to your channel as an administrator with permission to post messages. A bot cannot post to a channel as an ordinary member.

export TGPOST_BOT_TOKEN="123456789:your-token-here"     # Windows: set TGPOST_BOT_TOKEN=...

tgpost targets add release-notes -1001234567890 --description "Build announcements"
tgpost check

tgpost check verifies the token and reports, per target, whether the bot can actually post. Those two permission failures are the most common cause of a send failing later, so it is worth running first.

Finding a channel id

A public channel can be addressed as @channelusername. For a private channel, post any message in it, then read the numeric id (it starts with -100):

tgpost targets add private-chan @temporary_username   # if public, then:
tgpost check                                          # prints the numeric id

Sending

tgpost send --to release-notes --text "Build 1.2.3 shipped"
tgpost send --to release-notes --file build.zip --caption "Nightly build"
tgpost send --to release-notes --file a.png --file b.png --album --kind photo
git log --oneline -10 | tgpost send --to release-notes --stdin
tgpost send --to release-notes --text "check this first" --dry-run

Text longer than Telegram's 4096-character limit is split across several messages at paragraph, line or word boundaries.

Scheduling

tgpost schedule --to release-notes --text "Standup" --cron "0 9 * * 1-5" --tz Europe/London
tgpost schedule --to alerts --text "Still alive" --every 30m
tgpost schedule --to release-notes --file report.pdf --at "2026-09-10T14:00"
tgpost schedule --to alerts --text "in two hours" --at 2h

tgpost jobs          # what is scheduled, and when each next runs
tgpost history       # what actually happened
tgpost cancel a41a4c18

Jobs are stored in SQLite and survive a restart. Something has to run them:

tgpost daemon        # long-running: keeps firing jobs until stopped
tgpost run-due       # fires everything due, then exits

Use daemon on a machine that stays up. Use run-due from Windows Task Scheduler or cron if you would rather not keep a process alive:

schtasks /create /tn "tgpost" /tr "tgpost run-due" /sc minute /mo 5

A job whose time passed while nothing was running still fires when the scheduler comes back, as long as it is within the one-hour grace window. Missed repeats are coalesced, so a weekend of downtime sends once rather than fifty times.

Formatting

The default parse mode is HTML, because it only requires &, < and > to be escaped. MarkdownV2 requires eighteen characters to be escaped, including ., - and !, which appear in ordinary prose, and a single missed escape is a hard error rather than a cosmetic one.

tgpost send --to alerts --text "<b>Deploy finished</b>"        # markup as written
tgpost send --to alerts --text "$RAW" --escape                 # escape untrusted text
tgpost send --to alerts --text "plain" --parse-mode none

Pass --escape for text you did not write yourself. Without it, the text is sent as markup and a stray < will be rejected by Telegram.

Configuration

Config lives at %APPDATA%\tgpost\config.toml on Windows, or ~/.config/tgpost/config.toml elsewhere.

[defaults]
parse_mode = "html"
base_url = "https://api.telegram.org"

[targets.release-notes]
chat_id = "-1001234567890"
description = "Build announcements"

[targets.alerts]
chat_id = "@my_public_channel"
parse_mode = "none"
Variable Purpose
TGPOST_BOT_TOKEN Bot token. Preferred over storing it in the config file.
TGPOST_CONFIG Path to the config file.
TGPOST_DB Path to the job database.

The token is read from --token, then TGPOST_BOT_TOKEN, then the config file. tgpost targets add never writes the token to disk, so a token supplied on the command line or in the environment is not persisted by accident.

Limits

Set by the Bot API, not by this package:

Limit Value
Message text 4096 characters (tgpost splits automatically)
Caption 1024 characters
Photo upload 10 MB
Any other file 50 MB
Album 2 to 10 items
Rate About 1 message per second per chat, 30 per second overall

tgpost paces sends to stay under the rate limits, and on a 429 waits exactly the retry_after Telegram asks for rather than guessing.

To send files larger than 50 MB, run a local Bot API server, which raises the limit to 2000 MB, and point tgpost at it:

tgpost --base-url http://127.0.0.1:8081 send --to release-notes --file big.iso

Python API

from tgpost import TelegramClient

with TelegramClient("123456789:your-token") as client:
    client.send_message("-1001234567890", "Build finished")
    client.send_file("-1001234567890", "report.pdf", caption="Nightly report")

Errors derive from TgPostError: AuthError, ForbiddenError, BadRequestError, RateLimitError, ServerError, NetworkError, FileTooLargeError, ConfigError and SchedulerError. Retryable failures are retried internally; what reaches you is a failure worth acting on.

License

MIT. See LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tgpost-0.1.0.tar.gz (32.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tgpost-0.1.0-py3-none-any.whl (35.9 kB view details)

Uploaded Python 3

File details

Details for the file tgpost-0.1.0.tar.gz.

File metadata

  • Download URL: tgpost-0.1.0.tar.gz
  • Upload date:
  • Size: 32.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.5

File hashes

Hashes for tgpost-0.1.0.tar.gz
Algorithm Hash digest
SHA256 340e93ccab57b32d95882ac621773e4c17a5e1a27054123a90d8ecd57b880fa6
MD5 d99d4252a484068b5fdeb78199767088
BLAKE2b-256 193704cb762062c9ecea3fbc7bca76d44cc4e8d192223dcba2904de1219773be

See more details on using hashes here.

File details

Details for the file tgpost-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: tgpost-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 35.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.13.5

File hashes

Hashes for tgpost-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 00aa7b00464de5495c1abb29ea923c29c2307ff5032484669d4e28f12db82744
MD5 c14d89d18e386de15ab6666a0ed1f03b
BLAKE2b-256 82a253e6812a577e4e2dd0a802128f273486b03720c08979b2b9cd3a0cf86121

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 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