tgpost 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,60 @@
1
+ # Environment
2
+ env/
3
+ venv/
4
+ ENV/
5
+ VENV/
6
+ .venv/
7
+
8
+ # Python cache
9
+ __pycache__/
10
+ *.py[cod]
11
+ *$py.class
12
+ *.so
13
+ .Python
14
+
15
+ # Build and distribution
16
+ build/
17
+ dist/
18
+ *.egg-info/
19
+ .eggs/
20
+ *.egg
21
+ *.whl
22
+ *.tar.gz
23
+ pip-log.txt
24
+ pip-delete-this-directory.txt
25
+
26
+ # Testing / typing / linting
27
+ .pytest_cache/
28
+ .coverage
29
+ htmlcov/
30
+ .mypy_cache/
31
+ .ruff_cache/
32
+ .tox/
33
+
34
+ # Data and downloads
35
+ dl/
36
+ downloads/
37
+ data/
38
+
39
+ # Databases
40
+ *.db
41
+ *.sqlite
42
+ *.sqlite3
43
+
44
+ # Secrets / local config
45
+ .env
46
+ *.local
47
+
48
+ # Temporary files
49
+ # Local working notes (per-chat session summaries; never committed)
50
+ SCRATCHPAD.md
51
+ # Plan-mode plans and other local scratch notes
52
+ scratchpad/
53
+
54
+ # IDE and OS
55
+ .DS_Store
56
+ .vscode/
57
+ .idea/
58
+ *.swp
59
+ *.swo
60
+ *~
@@ -0,0 +1,36 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the project uses
5
+ [semantic versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [0.1.0] - 2026-09-05
8
+
9
+ First release.
10
+
11
+ ### Added
12
+
13
+ - `TelegramClient`, a synchronous Bot API client covering `sendMessage`,
14
+ `sendPhoto`, `sendDocument`, `sendVideo`, `sendAudio`, `sendAnimation`,
15
+ `sendVoice` and `sendMediaGroup`, plus `getMe`, `getChat` and `getChatMember`.
16
+ - Automatic retry that honours the `retry_after` Telegram returns on a 429,
17
+ retries 5xx and transport failures with capped exponential backoff, and never
18
+ retries a 400, 401 or 403.
19
+ - Outbound rate limiting: about one message per second per chat and thirty per
20
+ second overall, matching the documented Bot API limits.
21
+ - Text over 4096 characters is split across messages at paragraph, line or word
22
+ boundaries.
23
+ - HTML and MarkdownV2 escaping, with HTML as the default parse mode.
24
+ - Upload size checks that run before the request and name the local Bot API
25
+ server as the way past the 50 MB limit.
26
+ - Persistent scheduling behind the `[schedule]` extra: one-shot, interval and
27
+ cron jobs stored in SQLite, surviving a restart, with a one-hour misfire grace
28
+ window and coalescing of missed repeats.
29
+ - CLI: `send`, `schedule`, `jobs`, `cancel`, `run-due`, `daemon`, `history`,
30
+ `targets` and `check`.
31
+ - Named channel targets in a TOML config file, so a send names a channel rather
32
+ than repeating a token and a chat id.
33
+ - `tgpost check`, which reports whether the bot can actually post to each
34
+ configured channel before a real send fails.
35
+
36
+ [0.1.0]: https://github.com/pandiyarajk/tgpost/releases/tag/v0.1.0
@@ -0,0 +1,55 @@
1
+ # Disclaimer
2
+
3
+ **tgpost is provided AS IS, without warranty of any kind, express or implied.
4
+ Use it entirely at your own risk.**
5
+
6
+ No warranty is given as to merchantability, fitness for a particular purpose, or
7
+ non-infringement. To the maximum extent permitted by law, the author accepts no
8
+ liability for any claim, damages or other liability arising from the use of this
9
+ software, including but not limited to:
10
+
11
+ - messages or files published to the wrong channel, or to the wrong audience
12
+ - content published at the wrong time, or repeatedly
13
+ - data loss or corruption
14
+ - business interruption, reputational harm, or consequential damages
15
+ - suspension or termination of your Telegram bot or account
16
+
17
+ ## Your responsibilities
18
+
19
+ You are responsible for:
20
+
21
+ - **verifying the target channel** before sending. A chat id is a number, and a
22
+ wrong one sends to a real audience that was never meant to receive it. Use
23
+ `tgpost check` and `--dry-run` first.
24
+ - **what you publish.** This tool transmits whatever you give it.
25
+ - **being authorised** to post in the channel and to distribute the content.
26
+ - **complying with the Telegram Terms of Service** and the Bot API terms,
27
+ including their rate limits and anti-spam rules.
28
+ - **protecting your bot token.** A leaked token lets anyone post as your bot.
29
+ Keep it in an environment variable, not in a committed file.
30
+
31
+ ## Unattended operation
32
+
33
+ `tgpost schedule` and `tgpost daemon` publish **without a human present**. A
34
+ mistaken cron expression can post to a live channel repeatedly, at any hour,
35
+ until someone stops it. Test a new schedule against a private channel before
36
+ pointing it at a real audience.
37
+
38
+ ## Not certified for high-assurance use
39
+
40
+ This software is not certified, validated or approved for use in regulated,
41
+ forensic, safety-critical or high-assurance environments, and must not be relied
42
+ upon where failure to deliver, or delivery of the wrong content, could cause
43
+ injury, legal exposure or significant loss. It offers no delivery guarantee: the
44
+ Telegram Bot API provides no idempotency key, so a send that times out may or may
45
+ not have been delivered.
46
+
47
+ ## The licence governs
48
+
49
+ This document is a plain-language summary offered for convenience. **The
50
+ [MIT LICENSE](LICENSE) is the governing legal text and prevails** wherever the
51
+ two differ. Nothing here grants rights beyond, or narrows, that licence.
52
+
53
+ The author is not a lawyer and this is not legal advice. A disclaimer reduces
54
+ legal risk; it does not remove it. Seek professional advice for anything
55
+ high-stakes.
tgpost-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Pandiyaraj Karuppasamy
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
tgpost-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,247 @@
1
+ Metadata-Version: 2.4
2
+ Name: tgpost
3
+ Version: 0.1.0
4
+ Summary: Send messages and files to Telegram channels, now or on a schedule
5
+ Project-URL: Repository, https://github.com/pandiyarajk/tgpost
6
+ Project-URL: Issues, https://github.com/pandiyarajk/tgpost/issues
7
+ Author-email: Pandiyaraj Karuppasamy <pandiyarajk@live.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: bot,cli,notifications,scheduler,telegram
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Communications :: Chat
19
+ Classifier: Typing :: Typed
20
+ Requires-Python: >=3.11
21
+ Requires-Dist: httpx>=0.27
22
+ Requires-Dist: tzdata; platform_system == 'Windows'
23
+ Provides-Extra: dev
24
+ Requires-Dist: apscheduler<4,>=3.11; extra == 'dev'
25
+ Requires-Dist: build>=1.0; extra == 'dev'
26
+ Requires-Dist: mypy>=1.8; extra == 'dev'
27
+ Requires-Dist: pytest-cov; extra == 'dev'
28
+ Requires-Dist: pytest>=7.0; extra == 'dev'
29
+ Requires-Dist: ruff>=0.6; extra == 'dev'
30
+ Requires-Dist: sqlalchemy>=1.4; extra == 'dev'
31
+ Requires-Dist: twine>=4.0; extra == 'dev'
32
+ Provides-Extra: schedule
33
+ Requires-Dist: apscheduler<4,>=3.11; extra == 'schedule'
34
+ Requires-Dist: sqlalchemy>=1.4; extra == 'schedule'
35
+ Description-Content-Type: text/markdown
36
+
37
+ # tgpost
38
+
39
+ Send messages and files to Telegram channels, now or on a schedule.
40
+
41
+ ---
42
+
43
+ > ### Disclaimer
44
+ >
45
+ > **Provided AS IS, without warranty of any kind, express or implied. Use
46
+ > entirely at your own risk.** The author accepts no liability for data loss,
47
+ > content published to the wrong channel or audience, business interruption or
48
+ > consequential damages.
49
+ >
50
+ > **You** are responsible for verifying the target channel, for what you publish,
51
+ > for being authorised to post there, and for complying with the Telegram Terms
52
+ > of Service. `tgpost schedule` and `tgpost daemon` publish **unattended**: a
53
+ > mistaken cron expression can post to a live channel repeatedly until someone
54
+ > stops it. Test against a private channel first.
55
+ >
56
+ > Not certified for regulated, forensic, safety-critical or high-assurance use.
57
+ > There is no delivery guarantee: the Bot API offers no idempotency key, so a
58
+ > send that times out may or may not have arrived.
59
+ >
60
+ > The [MIT LICENSE](https://github.com/pandiyarajk/tgpost/blob/main/LICENSE) is
61
+ > the governing text and prevails wherever this summary differs from it. See
62
+ > [DISCLAIMER.md](https://github.com/pandiyarajk/tgpost/blob/main/DISCLAIMER.md)
63
+ > for the full text.
64
+
65
+ ---
66
+
67
+ ## Why this exists
68
+
69
+ Sending a file to a Telegram channel from the command line is well covered
70
+ already. What is missing is doing it **later**, reliably.
71
+
72
+ The Telegram HTTP Bot API has no server-side scheduling: there is no
73
+ `schedule_date` parameter on any send method, so "post this at 9am on weekdays"
74
+ has to be solved on your side, and survive a reboot. tgpost is a sender with a
75
+ persistent job store attached, so a scheduled post still goes out after the
76
+ machine restarts.
77
+
78
+ If you only need to send something right now, `telegram-send` and `apprise` both
79
+ do that well.
80
+
81
+ ## Install
82
+
83
+ ```bash
84
+ pip install tgpost # sending only
85
+ pip install tgpost[schedule] # sending and scheduling
86
+ ```
87
+
88
+ Requires Python 3.11 or newer.
89
+
90
+ ## Getting started
91
+
92
+ Create a bot with [@BotFather](https://t.me/BotFather), then add it to your
93
+ channel **as an administrator with permission to post messages**. A bot cannot
94
+ post to a channel as an ordinary member.
95
+
96
+ ```bash
97
+ export TGPOST_BOT_TOKEN="123456789:your-token-here" # Windows: set TGPOST_BOT_TOKEN=...
98
+
99
+ tgpost targets add release-notes -1001234567890 --description "Build announcements"
100
+ tgpost check
101
+ ```
102
+
103
+ `tgpost check` verifies the token and reports, per target, whether the bot can
104
+ actually post. Those two permission failures are the most common cause of a
105
+ send failing later, so it is worth running first.
106
+
107
+ ### Finding a channel id
108
+
109
+ A public channel can be addressed as `@channelusername`. For a private channel,
110
+ post any message in it, then read the numeric id (it starts with `-100`):
111
+
112
+ ```bash
113
+ tgpost targets add private-chan @temporary_username # if public, then:
114
+ tgpost check # prints the numeric id
115
+ ```
116
+
117
+ ## Sending
118
+
119
+ ```bash
120
+ tgpost send --to release-notes --text "Build 1.2.3 shipped"
121
+ tgpost send --to release-notes --file build.zip --caption "Nightly build"
122
+ tgpost send --to release-notes --file a.png --file b.png --album --kind photo
123
+ git log --oneline -10 | tgpost send --to release-notes --stdin
124
+ tgpost send --to release-notes --text "check this first" --dry-run
125
+ ```
126
+
127
+ Text longer than Telegram's 4096-character limit is split across several
128
+ messages at paragraph, line or word boundaries.
129
+
130
+ ## Scheduling
131
+
132
+ ```bash
133
+ tgpost schedule --to release-notes --text "Standup" --cron "0 9 * * 1-5" --tz Europe/London
134
+ tgpost schedule --to alerts --text "Still alive" --every 30m
135
+ tgpost schedule --to release-notes --file report.pdf --at "2026-09-10T14:00"
136
+ tgpost schedule --to alerts --text "in two hours" --at 2h
137
+
138
+ tgpost jobs # what is scheduled, and when each next runs
139
+ tgpost history # what actually happened
140
+ tgpost cancel a41a4c18
141
+ ```
142
+
143
+ Jobs are stored in SQLite and survive a restart. Something has to run them:
144
+
145
+ ```bash
146
+ tgpost daemon # long-running: keeps firing jobs until stopped
147
+ tgpost run-due # fires everything due, then exits
148
+ ```
149
+
150
+ Use `daemon` on a machine that stays up. Use `run-due` from **Windows Task
151
+ Scheduler** or cron if you would rather not keep a process alive:
152
+
153
+ ```
154
+ schtasks /create /tn "tgpost" /tr "tgpost run-due" /sc minute /mo 5
155
+ ```
156
+
157
+ A job whose time passed while nothing was running still fires when the scheduler
158
+ comes back, as long as it is within the one-hour grace window. Missed repeats are
159
+ coalesced, so a weekend of downtime sends once rather than fifty times.
160
+
161
+ ## Formatting
162
+
163
+ The default parse mode is **HTML**, because it only requires `&`, `<` and `>` to
164
+ be escaped. MarkdownV2 requires eighteen characters to be escaped, including
165
+ `.`, `-` and `!`, which appear in ordinary prose, and a single missed escape is
166
+ a hard error rather than a cosmetic one.
167
+
168
+ ```bash
169
+ tgpost send --to alerts --text "<b>Deploy finished</b>" # markup as written
170
+ tgpost send --to alerts --text "$RAW" --escape # escape untrusted text
171
+ tgpost send --to alerts --text "plain" --parse-mode none
172
+ ```
173
+
174
+ Pass `--escape` for text you did not write yourself. Without it, the text is
175
+ sent as markup and a stray `<` will be rejected by Telegram.
176
+
177
+ ## Configuration
178
+
179
+ Config lives at `%APPDATA%\tgpost\config.toml` on Windows, or
180
+ `~/.config/tgpost/config.toml` elsewhere.
181
+
182
+ ```toml
183
+ [defaults]
184
+ parse_mode = "html"
185
+ base_url = "https://api.telegram.org"
186
+
187
+ [targets.release-notes]
188
+ chat_id = "-1001234567890"
189
+ description = "Build announcements"
190
+
191
+ [targets.alerts]
192
+ chat_id = "@my_public_channel"
193
+ parse_mode = "none"
194
+ ```
195
+
196
+ | Variable | Purpose |
197
+ |---|---|
198
+ | `TGPOST_BOT_TOKEN` | Bot token. **Preferred over storing it in the config file.** |
199
+ | `TGPOST_CONFIG` | Path to the config file. |
200
+ | `TGPOST_DB` | Path to the job database. |
201
+
202
+ The token is read from `--token`, then `TGPOST_BOT_TOKEN`, then the config file.
203
+ `tgpost targets add` never writes the token to disk, so a token supplied on the
204
+ command line or in the environment is not persisted by accident.
205
+
206
+ ## Limits
207
+
208
+ Set by the Bot API, not by this package:
209
+
210
+ | Limit | Value |
211
+ |---|---|
212
+ | Message text | 4096 characters (tgpost splits automatically) |
213
+ | Caption | 1024 characters |
214
+ | Photo upload | 10 MB |
215
+ | Any other file | 50 MB |
216
+ | Album | 2 to 10 items |
217
+ | Rate | About 1 message per second per chat, 30 per second overall |
218
+
219
+ tgpost paces sends to stay under the rate limits, and on a `429` waits exactly
220
+ the `retry_after` Telegram asks for rather than guessing.
221
+
222
+ To send files larger than 50 MB, run a
223
+ [local Bot API server](https://github.com/tdlib/telegram-bot-api), which raises
224
+ the limit to 2000 MB, and point tgpost at it:
225
+
226
+ ```bash
227
+ tgpost --base-url http://127.0.0.1:8081 send --to release-notes --file big.iso
228
+ ```
229
+
230
+ ## Python API
231
+
232
+ ```python
233
+ from tgpost import TelegramClient
234
+
235
+ with TelegramClient("123456789:your-token") as client:
236
+ client.send_message("-1001234567890", "Build finished")
237
+ client.send_file("-1001234567890", "report.pdf", caption="Nightly report")
238
+ ```
239
+
240
+ Errors derive from `TgPostError`: `AuthError`, `ForbiddenError`,
241
+ `BadRequestError`, `RateLimitError`, `ServerError`, `NetworkError`,
242
+ `FileTooLargeError`, `ConfigError` and `SchedulerError`. Retryable failures are
243
+ retried internally; what reaches you is a failure worth acting on.
244
+
245
+ ## License
246
+
247
+ MIT. See [LICENSE](https://github.com/pandiyarajk/tgpost/blob/main/LICENSE).
tgpost-0.1.0/README.md ADDED
@@ -0,0 +1,211 @@
1
+ # tgpost
2
+
3
+ Send messages and files to Telegram channels, now or on a schedule.
4
+
5
+ ---
6
+
7
+ > ### Disclaimer
8
+ >
9
+ > **Provided AS IS, without warranty of any kind, express or implied. Use
10
+ > entirely at your own risk.** The author accepts no liability for data loss,
11
+ > content published to the wrong channel or audience, business interruption or
12
+ > consequential damages.
13
+ >
14
+ > **You** are responsible for verifying the target channel, for what you publish,
15
+ > for being authorised to post there, and for complying with the Telegram Terms
16
+ > of Service. `tgpost schedule` and `tgpost daemon` publish **unattended**: a
17
+ > mistaken cron expression can post to a live channel repeatedly until someone
18
+ > stops it. Test against a private channel first.
19
+ >
20
+ > Not certified for regulated, forensic, safety-critical or high-assurance use.
21
+ > There is no delivery guarantee: the Bot API offers no idempotency key, so a
22
+ > send that times out may or may not have arrived.
23
+ >
24
+ > The [MIT LICENSE](https://github.com/pandiyarajk/tgpost/blob/main/LICENSE) is
25
+ > the governing text and prevails wherever this summary differs from it. See
26
+ > [DISCLAIMER.md](https://github.com/pandiyarajk/tgpost/blob/main/DISCLAIMER.md)
27
+ > for the full text.
28
+
29
+ ---
30
+
31
+ ## Why this exists
32
+
33
+ Sending a file to a Telegram channel from the command line is well covered
34
+ already. What is missing is doing it **later**, reliably.
35
+
36
+ The Telegram HTTP Bot API has no server-side scheduling: there is no
37
+ `schedule_date` parameter on any send method, so "post this at 9am on weekdays"
38
+ has to be solved on your side, and survive a reboot. tgpost is a sender with a
39
+ persistent job store attached, so a scheduled post still goes out after the
40
+ machine restarts.
41
+
42
+ If you only need to send something right now, `telegram-send` and `apprise` both
43
+ do that well.
44
+
45
+ ## Install
46
+
47
+ ```bash
48
+ pip install tgpost # sending only
49
+ pip install tgpost[schedule] # sending and scheduling
50
+ ```
51
+
52
+ Requires Python 3.11 or newer.
53
+
54
+ ## Getting started
55
+
56
+ Create a bot with [@BotFather](https://t.me/BotFather), then add it to your
57
+ channel **as an administrator with permission to post messages**. A bot cannot
58
+ post to a channel as an ordinary member.
59
+
60
+ ```bash
61
+ export TGPOST_BOT_TOKEN="123456789:your-token-here" # Windows: set TGPOST_BOT_TOKEN=...
62
+
63
+ tgpost targets add release-notes -1001234567890 --description "Build announcements"
64
+ tgpost check
65
+ ```
66
+
67
+ `tgpost check` verifies the token and reports, per target, whether the bot can
68
+ actually post. Those two permission failures are the most common cause of a
69
+ send failing later, so it is worth running first.
70
+
71
+ ### Finding a channel id
72
+
73
+ A public channel can be addressed as `@channelusername`. For a private channel,
74
+ post any message in it, then read the numeric id (it starts with `-100`):
75
+
76
+ ```bash
77
+ tgpost targets add private-chan @temporary_username # if public, then:
78
+ tgpost check # prints the numeric id
79
+ ```
80
+
81
+ ## Sending
82
+
83
+ ```bash
84
+ tgpost send --to release-notes --text "Build 1.2.3 shipped"
85
+ tgpost send --to release-notes --file build.zip --caption "Nightly build"
86
+ tgpost send --to release-notes --file a.png --file b.png --album --kind photo
87
+ git log --oneline -10 | tgpost send --to release-notes --stdin
88
+ tgpost send --to release-notes --text "check this first" --dry-run
89
+ ```
90
+
91
+ Text longer than Telegram's 4096-character limit is split across several
92
+ messages at paragraph, line or word boundaries.
93
+
94
+ ## Scheduling
95
+
96
+ ```bash
97
+ tgpost schedule --to release-notes --text "Standup" --cron "0 9 * * 1-5" --tz Europe/London
98
+ tgpost schedule --to alerts --text "Still alive" --every 30m
99
+ tgpost schedule --to release-notes --file report.pdf --at "2026-09-10T14:00"
100
+ tgpost schedule --to alerts --text "in two hours" --at 2h
101
+
102
+ tgpost jobs # what is scheduled, and when each next runs
103
+ tgpost history # what actually happened
104
+ tgpost cancel a41a4c18
105
+ ```
106
+
107
+ Jobs are stored in SQLite and survive a restart. Something has to run them:
108
+
109
+ ```bash
110
+ tgpost daemon # long-running: keeps firing jobs until stopped
111
+ tgpost run-due # fires everything due, then exits
112
+ ```
113
+
114
+ Use `daemon` on a machine that stays up. Use `run-due` from **Windows Task
115
+ Scheduler** or cron if you would rather not keep a process alive:
116
+
117
+ ```
118
+ schtasks /create /tn "tgpost" /tr "tgpost run-due" /sc minute /mo 5
119
+ ```
120
+
121
+ A job whose time passed while nothing was running still fires when the scheduler
122
+ comes back, as long as it is within the one-hour grace window. Missed repeats are
123
+ coalesced, so a weekend of downtime sends once rather than fifty times.
124
+
125
+ ## Formatting
126
+
127
+ The default parse mode is **HTML**, because it only requires `&`, `<` and `>` to
128
+ be escaped. MarkdownV2 requires eighteen characters to be escaped, including
129
+ `.`, `-` and `!`, which appear in ordinary prose, and a single missed escape is
130
+ a hard error rather than a cosmetic one.
131
+
132
+ ```bash
133
+ tgpost send --to alerts --text "<b>Deploy finished</b>" # markup as written
134
+ tgpost send --to alerts --text "$RAW" --escape # escape untrusted text
135
+ tgpost send --to alerts --text "plain" --parse-mode none
136
+ ```
137
+
138
+ Pass `--escape` for text you did not write yourself. Without it, the text is
139
+ sent as markup and a stray `<` will be rejected by Telegram.
140
+
141
+ ## Configuration
142
+
143
+ Config lives at `%APPDATA%\tgpost\config.toml` on Windows, or
144
+ `~/.config/tgpost/config.toml` elsewhere.
145
+
146
+ ```toml
147
+ [defaults]
148
+ parse_mode = "html"
149
+ base_url = "https://api.telegram.org"
150
+
151
+ [targets.release-notes]
152
+ chat_id = "-1001234567890"
153
+ description = "Build announcements"
154
+
155
+ [targets.alerts]
156
+ chat_id = "@my_public_channel"
157
+ parse_mode = "none"
158
+ ```
159
+
160
+ | Variable | Purpose |
161
+ |---|---|
162
+ | `TGPOST_BOT_TOKEN` | Bot token. **Preferred over storing it in the config file.** |
163
+ | `TGPOST_CONFIG` | Path to the config file. |
164
+ | `TGPOST_DB` | Path to the job database. |
165
+
166
+ The token is read from `--token`, then `TGPOST_BOT_TOKEN`, then the config file.
167
+ `tgpost targets add` never writes the token to disk, so a token supplied on the
168
+ command line or in the environment is not persisted by accident.
169
+
170
+ ## Limits
171
+
172
+ Set by the Bot API, not by this package:
173
+
174
+ | Limit | Value |
175
+ |---|---|
176
+ | Message text | 4096 characters (tgpost splits automatically) |
177
+ | Caption | 1024 characters |
178
+ | Photo upload | 10 MB |
179
+ | Any other file | 50 MB |
180
+ | Album | 2 to 10 items |
181
+ | Rate | About 1 message per second per chat, 30 per second overall |
182
+
183
+ tgpost paces sends to stay under the rate limits, and on a `429` waits exactly
184
+ the `retry_after` Telegram asks for rather than guessing.
185
+
186
+ To send files larger than 50 MB, run a
187
+ [local Bot API server](https://github.com/tdlib/telegram-bot-api), which raises
188
+ the limit to 2000 MB, and point tgpost at it:
189
+
190
+ ```bash
191
+ tgpost --base-url http://127.0.0.1:8081 send --to release-notes --file big.iso
192
+ ```
193
+
194
+ ## Python API
195
+
196
+ ```python
197
+ from tgpost import TelegramClient
198
+
199
+ with TelegramClient("123456789:your-token") as client:
200
+ client.send_message("-1001234567890", "Build finished")
201
+ client.send_file("-1001234567890", "report.pdf", caption="Nightly report")
202
+ ```
203
+
204
+ Errors derive from `TgPostError`: `AuthError`, `ForbiddenError`,
205
+ `BadRequestError`, `RateLimitError`, `ServerError`, `NetworkError`,
206
+ `FileTooLargeError`, `ConfigError` and `SchedulerError`. Retryable failures are
207
+ retried internally; what reaches you is a failure worth acting on.
208
+
209
+ ## License
210
+
211
+ MIT. See [LICENSE](https://github.com/pandiyarajk/tgpost/blob/main/LICENSE).