telegram-tools 3.2.0__tar.gz → 3.3.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.
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/.github/pull_request_template.md +1 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/.github/workflows/tests.yml +2 -0
- telegram_tools-3.3.0/AGENTS.md +15 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/CHANGELOG.md +9 -0
- telegram_tools-3.3.0/CLAUDE.md +1 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/CONTRIBUTING.md +14 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/PKG-INFO +43 -11
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/README.md +42 -10
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/docs/telethon-api-notes.md +7 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/pyproject.toml +1 -1
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/skill/SKILL.md +38 -9
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/__init__.py +1 -1
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/cli.py +122 -1
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/config.py +40 -0
- telegram_tools-3.3.0/src/telegram_tools/create.py +167 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/doctor.py +13 -1
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/menu.py +143 -1
- telegram_tools-3.3.0/src/telegram_tools/send.py +116 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_bot_cli.py +9 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_cli.py +59 -0
- telegram_tools-3.3.0/tests/test_cli_resolution.py +252 -0
- telegram_tools-3.3.0/tests/test_create.py +142 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_doctor.py +37 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_menu.py +219 -29
- telegram_tools-3.3.0/tests/test_send.py +157 -0
- telegram_tools-3.2.0/tests/test_cli_resolution.py +0 -94
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/.env.example +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/.gitignore +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/CODE_OF_CONDUCT.md +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/LICENSE +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/SECURITY.md +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/bot_session.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/bots.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/client.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/delete.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/discovery.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/exporters.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/models.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/prompts.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/records.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/resolver.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/search.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/src/telegram_tools/topics.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_bot_config.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_bot_edits.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_bot_session.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_bots.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_config.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_delete.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_discovery.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_exporters.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_prompts.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_records.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_resolver.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_search.py +0 -0
- {telegram_tools-3.2.0 → telegram_tools-3.3.0}/tests/test_topics.py +0 -0
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
- [ ] I did not include Telegram API hashes, bot tokens, phone numbers, `.env`, session files, or private exports.
|
|
8
8
|
- [ ] I did not make destructive Telegram operations easier to run accidentally.
|
|
9
9
|
- [ ] If I touched `clear-messages`, dry-run, `--execute`, typed `DELETE`, topic preservation, and topic ID preservation are covered by tests.
|
|
10
|
+
- [ ] If I touched `send`, the preview + `y/N` and the `TELEGRAM_SEND_ALLOWLIST` gate on `--yes` are covered by tests.
|
|
10
11
|
|
|
11
12
|
## Validation
|
|
12
13
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# telegram-tools — agent brief
|
|
2
|
+
|
|
3
|
+
Local Telethon CLI operating the user's own Telegram account: `discover` chat/topic IDs, `search`/export messages, `send` a message, `create` group/channel/topic, `clear-messages`, `bots` (BotFather settings), `doctor`. Public repo — contributor-facing; never commit phone numbers, API hashes, tokens, or exported chat data (CONTRIBUTING.md binds).
|
|
4
|
+
|
|
5
|
+
## Working here
|
|
6
|
+
- Test: `.venv/bin/python -m pytest -q` → all pass (300 as of v3.3.0), no network. CI adds `compileall` + per-subcommand `--help` smoke; there is no lint step.
|
|
7
|
+
- Bare invocation is the human menu; agents pass a subcommand. Session + exports live in `~/.telegram-tools/`, outside the repo.
|
|
8
|
+
- `skill/SKILL.md` (independently versioned) is the bundled agent skill's source of truth: a CLI-surface change updates it **in the same commit** — convention, nothing enforces it.
|
|
9
|
+
- A user-visible fix also gets its CHANGELOG entry and version bump in the same change; unreleased fixes silently stacking on the last shipped version is the failure mode to avoid.
|
|
10
|
+
|
|
11
|
+
## Destructive commands
|
|
12
|
+
`clear-messages` deletes real messages: dry-run is the default, execution needs `--execute` plus typed `DELETE`. `bots` mutates live bot settings behind a diff + confirm (`--yes` skips). `send` posts publicly as the user behind a full-message preview + `y/N`; its `--yes` additionally requires the destination in `TELEGRAM_SEND_ALLOWLIST` (unset = every `--yes` send refused). `create` confirms before making a real, visible object. Keep those gates, and keep the menu from ever being the shorter path past one.
|
|
13
|
+
|
|
14
|
+
## Releasing (maintainer only)
|
|
15
|
+
PyPI account is **banozz** (not the GitHub handle). Full recipe with the zsh traps (the `!` negation, the glob no-op, stale-sdist check, twine output redaction) lives in the maintainer's private runbook. Rebuild `dist/` after any source edit; `repo ≠ sdist` — `docs/superpowers/` is tracked here but excluded from the package.
|
|
@@ -4,6 +4,15 @@ All notable changes to this project will be documented here.
|
|
|
4
4
|
|
|
5
5
|
This project follows a practical changelog style: user-visible changes, safety changes, and release notes belong here; active task tracking belongs outside the repo.
|
|
6
6
|
|
|
7
|
+
## 3.3.0 - 2026-08-25
|
|
8
|
+
|
|
9
|
+
- Add `send`: post a message to a chat, or into one forum topic with `--topic`. It prints the destination and the whole message body, then asks `y/N`. `--text -` reads the body from stdin, which is how a multi-line message gets in without fighting your shell.
|
|
10
|
+
- Add `create`: `create group` (a supergroup, with `--forum` to switch topics on in the same call), `create channel` (a broadcast channel), and `create topic` (a topic inside a forum group). Each asks before it acts and prints the new ID, so a new chat can be piped straight into `send`.
|
|
11
|
+
- Both are in the menu, at 3 and 4. The other entries moved down: clearing topic messages is now 5, bots 6, check setup 7. The menu has no `--yes` equivalent — every send it makes shows the message and asks.
|
|
12
|
+
- New optional `TELEGRAM_SEND_ALLOWLIST` (`chat[:topic]`, comma separated). It gates one thing only: `send --yes`, the unattended path where nobody sees the preview. Unset means every `--yes` send is refused; `send` without `--yes` is unaffected. `doctor` reports how many destinations are listed and never which.
|
|
13
|
+
- The menu's staged message shows in the prompt when you go back to it (`Message [hiiiii]`), so keeping it does not mean typing it again. Long or multi-line bodies are flattened and cut for that one line only.
|
|
14
|
+
- No attachments on `send` — text only for now.
|
|
15
|
+
|
|
7
16
|
## 3.2.0 - 2026-08-15
|
|
8
17
|
|
|
9
18
|
- `telegram-tools` with no arguments now opens a menu you walk forward and back through instead of a single screen that exits after one action: numbered lists all the way down, `0` always backs out one screen at a time — inside a picker or a flow's own screen alike — and a return to the menu after every job.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
AGENTS.md
|
|
@@ -35,6 +35,8 @@ telegram-tools --help
|
|
|
35
35
|
telegram-tools discover --help
|
|
36
36
|
telegram-tools clear-messages --help
|
|
37
37
|
telegram-tools search --help
|
|
38
|
+
telegram-tools send --help
|
|
39
|
+
telegram-tools create --help
|
|
38
40
|
telegram-tools bots --help
|
|
39
41
|
telegram-tools doctor --help
|
|
40
42
|
```
|
|
@@ -45,6 +47,18 @@ If you have local Telegram config and session storage, also run:
|
|
|
45
47
|
telegram-tools doctor
|
|
46
48
|
```
|
|
47
49
|
|
|
50
|
+
## Send Changes
|
|
51
|
+
|
|
52
|
+
`send` posts publicly as the person running it, so its gates are load-bearing:
|
|
53
|
+
|
|
54
|
+
- Without `--yes`, the whole message and its resolved destination are printed and a
|
|
55
|
+
`y/N` is required.
|
|
56
|
+
- `--yes` skips that prompt and therefore requires the destination to appear in
|
|
57
|
+
`TELEGRAM_SEND_ALLOWLIST`. An unset allowlist refuses every `--yes` send.
|
|
58
|
+
- The menu never passes `--yes`.
|
|
59
|
+
|
|
60
|
+
Keep all three covered by tests.
|
|
61
|
+
|
|
48
62
|
## Clear-Message Changes
|
|
49
63
|
|
|
50
64
|
Be especially conservative around `clear-messages`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: telegram-tools
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0
|
|
4
4
|
Summary: Local Telethon CLI for Telegram chat/topic ID discovery, message search/export, topic message clearing, and bot settings editing.
|
|
5
5
|
Project-URL: Homepage, https://github.com/banozz0/telegram-tools
|
|
6
6
|
Project-URL: Repository, https://github.com/banozz0/telegram-tools
|
|
@@ -49,7 +49,7 @@ Description-Content-Type: text/markdown
|
|
|
49
49
|
|
|
50
50
|
# telegram-tools
|
|
51
51
|
|
|
52
|
-
A local CLI for operating your own Telegram chats: find the real IDs of your groups, channels, and forum topics, search and export messages, and clear all messages out of forum topics without destroying the topics themselves.
|
|
52
|
+
A local CLI for operating your own Telegram chats: find the real IDs of your groups, channels, and forum topics, search and export messages, send a message into a chat or topic, create groups/channels/topics, and clear all messages out of forum topics without destroying the topics themselves.
|
|
53
53
|
|
|
54
54
|
Built on [Telethon](https://github.com/LonamiWebs/Telethon). Everything runs on your machine with your own Telegram API credentials — no server, no third party, nothing leaves your computer except the Telegram API calls you asked for.
|
|
55
55
|
|
|
@@ -58,14 +58,16 @@ Built on [Telethon](https://github.com/LonamiWebs/Telethon). Everything runs on
|
|
|
58
58
|
- **`discover`** — lists your chats, channels, and forum groups with their exact numeric IDs and every forum topic ID. The fastest way to answer "what is this chat's `-100…` ID and what are its topic IDs?"
|
|
59
59
|
- **`search`** — searches messages by text, sender, date range, or topic, and prints a table or exports JSON/CSV.
|
|
60
60
|
- **`clear-messages`** — deletes all messages inside selected forum topic(s) while preserving the topics and their IDs. Dry-run by default; deleting requires both `--execute` *and* typing `DELETE` at a prompt.
|
|
61
|
+
- **`send`** — posts a message to a chat, or into one forum topic. Shows you the whole message and its destination, then asks `y/N`.
|
|
62
|
+
- **`create`** — makes a supergroup (optionally with topics already on), a broadcast channel, or a topic inside a forum group, and prints the new ID.
|
|
61
63
|
- **`bots`** — lists the bots you own with their numeric IDs, and edits what @BotFather edits: display name, bio, description, commands, profile photo, and default admin rights.
|
|
62
64
|
- **`doctor`** — checks your local setup without printing any secrets.
|
|
63
65
|
|
|
64
66
|
## What it doesn't do (on purpose)
|
|
65
67
|
|
|
66
|
-
- No deleting
|
|
67
|
-
- No media downloads.
|
|
68
|
-
- No
|
|
68
|
+
- No deleting forum topics, and no renaming them — `clear-messages` leaves topic IDs untouched.
|
|
69
|
+
- No media downloads, and no attachments on `send` — text only.
|
|
70
|
+
- No automation loops. The `bots` command edits bot *settings*; it never runs a bot.
|
|
69
71
|
- No changing a bot's `@username`, creating or deleting bots, or reading/revoking bot tokens — those stay with @BotFather.
|
|
70
72
|
- No cloud anything — credentials and session files stay in `~/.telegram-tools/`.
|
|
71
73
|
|
|
@@ -113,6 +115,21 @@ TELEGRAM_BOT_TOKENS=mybot:12345:AAExampleToken,alerts:67890:BBExampleToken
|
|
|
113
115
|
|
|
114
116
|
Nicknames are yours to choose and can be used as `--bot mybot`. The tool only ever reads this variable — it never writes a token anywhere, and never prints one.
|
|
115
117
|
|
|
118
|
+
### Optional: the send allowlist
|
|
119
|
+
|
|
120
|
+
`send` asks you to confirm every message. `--yes` skips that prompt, and because a
|
|
121
|
+
skipped prompt means nobody saw where the message was going, it only works for
|
|
122
|
+
destinations you have named in advance:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# in ~/.telegram-tools/.env
|
|
126
|
+
TELEGRAM_SEND_ALLOWLIST=-1001234567890:141,-1009876543210,@myalerts
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Each entry is a chat ID or `@username`, optionally `:topic-id` to allow just one topic
|
|
130
|
+
in it. Unset means every `--yes` send is refused — `send` without `--yes` still works
|
|
131
|
+
and still asks. `doctor` reports how many destinations are listed, never which.
|
|
132
|
+
|
|
116
133
|
## 30 seconds of usage
|
|
117
134
|
|
|
118
135
|
```bash
|
|
@@ -131,6 +148,16 @@ telegram-tools clear-messages --chat @mygroup --topic 141
|
|
|
131
148
|
# Actually delete: needs --execute AND typing DELETE at the prompt
|
|
132
149
|
telegram-tools clear-messages --chat @mygroup --topic 141 --execute
|
|
133
150
|
|
|
151
|
+
# Send a message into a topic (shows it, then asks y/N)
|
|
152
|
+
telegram-tools send --chat -1001234567890 --topic 141 --text "deploy is green"
|
|
153
|
+
|
|
154
|
+
# Multi-line body, straight from a file or a pipe
|
|
155
|
+
cat notes.txt | telegram-tools send --chat -1001234567890 --text -
|
|
156
|
+
|
|
157
|
+
# Make a group with topics already switched on, then a topic in it
|
|
158
|
+
telegram-tools create group --title "Agency" --forum
|
|
159
|
+
telegram-tools create topic --chat -1001234567890 --title "Deploys"
|
|
160
|
+
|
|
134
161
|
# Which bots do I own, and what are their IDs?
|
|
135
162
|
telegram-tools bots
|
|
136
163
|
|
|
@@ -170,9 +197,11 @@ telegram-tools
|
|
|
170
197
|
--------------------------------------------
|
|
171
198
|
1. Chats & topics (find IDs)
|
|
172
199
|
2. Search / export messages
|
|
173
|
-
3.
|
|
174
|
-
4.
|
|
175
|
-
5.
|
|
200
|
+
3. Send a message
|
|
201
|
+
4. Create a group, channel, or topic
|
|
202
|
+
5. Clear topic messages
|
|
203
|
+
6. My bots
|
|
204
|
+
7. Check setup
|
|
176
205
|
0. Exit
|
|
177
206
|
```
|
|
178
207
|
|
|
@@ -183,14 +212,17 @@ you to type an ID. Bot fields show their current value and offer keep / change /
|
|
|
183
212
|
clear. After every job it returns to the menu.
|
|
184
213
|
|
|
185
214
|
The safety gates are the same as the flags', not looser: clearing topic messages
|
|
186
|
-
dry-runs first and still asks you to type `DELETE`,
|
|
187
|
-
and
|
|
215
|
+
dry-runs first and still asks you to type `DELETE`, sending shows the whole message
|
|
216
|
+
and asks `y/N`, and bot edits still print a diff and ask before writing. The menu has
|
|
217
|
+
no equivalent of `--yes` at all. With no terminal attached it prints this help instead.
|
|
188
218
|
|
|
189
219
|
## Safety model
|
|
190
220
|
|
|
191
221
|
| Command | Destructive? |
|
|
192
222
|
| --- | --- |
|
|
193
223
|
| `discover`, `search`, `doctor` | No — read-only |
|
|
224
|
+
| `create` | No — makes new things, changes nothing existing, after a `y/N` unless you pass `--yes` |
|
|
225
|
+
| `send` | Outward-facing — posts publicly as you, after showing the whole message and asking `y/N`. `--yes` skips the prompt only for destinations in `TELEGRAM_SEND_ALLOWLIST` |
|
|
194
226
|
| `bots` | No — changes settings on bots you own, after a diff and a `y/N` unless you pass `--yes`; reversible if you still have the old values, but `--remove-photo` and `--clear-commands` discard data Telegram will not hand back |
|
|
195
227
|
| `clear-messages` | Yes — but only with `--execute` **and** a typed `DELETE`, only messages, never topics |
|
|
196
228
|
|
|
@@ -200,7 +232,7 @@ and ask before writing. With no terminal attached it prints this help instead.
|
|
|
200
232
|
|
|
201
233
|
## Status
|
|
202
234
|
|
|
203
|
-
Stable for its
|
|
235
|
+
Stable for its six jobs; used regularly by its author. This is a solo project whose code was written by AI agents under review — issues are welcome, fixes are best-effort, and there is no support promise.
|
|
204
236
|
|
|
205
237
|
## License
|
|
206
238
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# telegram-tools
|
|
2
2
|
|
|
3
|
-
A local CLI for operating your own Telegram chats: find the real IDs of your groups, channels, and forum topics, search and export messages, and clear all messages out of forum topics without destroying the topics themselves.
|
|
3
|
+
A local CLI for operating your own Telegram chats: find the real IDs of your groups, channels, and forum topics, search and export messages, send a message into a chat or topic, create groups/channels/topics, and clear all messages out of forum topics without destroying the topics themselves.
|
|
4
4
|
|
|
5
5
|
Built on [Telethon](https://github.com/LonamiWebs/Telethon). Everything runs on your machine with your own Telegram API credentials — no server, no third party, nothing leaves your computer except the Telegram API calls you asked for.
|
|
6
6
|
|
|
@@ -9,14 +9,16 @@ Built on [Telethon](https://github.com/LonamiWebs/Telethon). Everything runs on
|
|
|
9
9
|
- **`discover`** — lists your chats, channels, and forum groups with their exact numeric IDs and every forum topic ID. The fastest way to answer "what is this chat's `-100…` ID and what are its topic IDs?"
|
|
10
10
|
- **`search`** — searches messages by text, sender, date range, or topic, and prints a table or exports JSON/CSV.
|
|
11
11
|
- **`clear-messages`** — deletes all messages inside selected forum topic(s) while preserving the topics and their IDs. Dry-run by default; deleting requires both `--execute` *and* typing `DELETE` at a prompt.
|
|
12
|
+
- **`send`** — posts a message to a chat, or into one forum topic. Shows you the whole message and its destination, then asks `y/N`.
|
|
13
|
+
- **`create`** — makes a supergroup (optionally with topics already on), a broadcast channel, or a topic inside a forum group, and prints the new ID.
|
|
12
14
|
- **`bots`** — lists the bots you own with their numeric IDs, and edits what @BotFather edits: display name, bio, description, commands, profile photo, and default admin rights.
|
|
13
15
|
- **`doctor`** — checks your local setup without printing any secrets.
|
|
14
16
|
|
|
15
17
|
## What it doesn't do (on purpose)
|
|
16
18
|
|
|
17
|
-
- No deleting
|
|
18
|
-
- No media downloads.
|
|
19
|
-
- No
|
|
19
|
+
- No deleting forum topics, and no renaming them — `clear-messages` leaves topic IDs untouched.
|
|
20
|
+
- No media downloads, and no attachments on `send` — text only.
|
|
21
|
+
- No automation loops. The `bots` command edits bot *settings*; it never runs a bot.
|
|
20
22
|
- No changing a bot's `@username`, creating or deleting bots, or reading/revoking bot tokens — those stay with @BotFather.
|
|
21
23
|
- No cloud anything — credentials and session files stay in `~/.telegram-tools/`.
|
|
22
24
|
|
|
@@ -64,6 +66,21 @@ TELEGRAM_BOT_TOKENS=mybot:12345:AAExampleToken,alerts:67890:BBExampleToken
|
|
|
64
66
|
|
|
65
67
|
Nicknames are yours to choose and can be used as `--bot mybot`. The tool only ever reads this variable — it never writes a token anywhere, and never prints one.
|
|
66
68
|
|
|
69
|
+
### Optional: the send allowlist
|
|
70
|
+
|
|
71
|
+
`send` asks you to confirm every message. `--yes` skips that prompt, and because a
|
|
72
|
+
skipped prompt means nobody saw where the message was going, it only works for
|
|
73
|
+
destinations you have named in advance:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# in ~/.telegram-tools/.env
|
|
77
|
+
TELEGRAM_SEND_ALLOWLIST=-1001234567890:141,-1009876543210,@myalerts
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Each entry is a chat ID or `@username`, optionally `:topic-id` to allow just one topic
|
|
81
|
+
in it. Unset means every `--yes` send is refused — `send` without `--yes` still works
|
|
82
|
+
and still asks. `doctor` reports how many destinations are listed, never which.
|
|
83
|
+
|
|
67
84
|
## 30 seconds of usage
|
|
68
85
|
|
|
69
86
|
```bash
|
|
@@ -82,6 +99,16 @@ telegram-tools clear-messages --chat @mygroup --topic 141
|
|
|
82
99
|
# Actually delete: needs --execute AND typing DELETE at the prompt
|
|
83
100
|
telegram-tools clear-messages --chat @mygroup --topic 141 --execute
|
|
84
101
|
|
|
102
|
+
# Send a message into a topic (shows it, then asks y/N)
|
|
103
|
+
telegram-tools send --chat -1001234567890 --topic 141 --text "deploy is green"
|
|
104
|
+
|
|
105
|
+
# Multi-line body, straight from a file or a pipe
|
|
106
|
+
cat notes.txt | telegram-tools send --chat -1001234567890 --text -
|
|
107
|
+
|
|
108
|
+
# Make a group with topics already switched on, then a topic in it
|
|
109
|
+
telegram-tools create group --title "Agency" --forum
|
|
110
|
+
telegram-tools create topic --chat -1001234567890 --title "Deploys"
|
|
111
|
+
|
|
85
112
|
# Which bots do I own, and what are their IDs?
|
|
86
113
|
telegram-tools bots
|
|
87
114
|
|
|
@@ -121,9 +148,11 @@ telegram-tools
|
|
|
121
148
|
--------------------------------------------
|
|
122
149
|
1. Chats & topics (find IDs)
|
|
123
150
|
2. Search / export messages
|
|
124
|
-
3.
|
|
125
|
-
4.
|
|
126
|
-
5.
|
|
151
|
+
3. Send a message
|
|
152
|
+
4. Create a group, channel, or topic
|
|
153
|
+
5. Clear topic messages
|
|
154
|
+
6. My bots
|
|
155
|
+
7. Check setup
|
|
127
156
|
0. Exit
|
|
128
157
|
```
|
|
129
158
|
|
|
@@ -134,14 +163,17 @@ you to type an ID. Bot fields show their current value and offer keep / change /
|
|
|
134
163
|
clear. After every job it returns to the menu.
|
|
135
164
|
|
|
136
165
|
The safety gates are the same as the flags', not looser: clearing topic messages
|
|
137
|
-
dry-runs first and still asks you to type `DELETE`,
|
|
138
|
-
and
|
|
166
|
+
dry-runs first and still asks you to type `DELETE`, sending shows the whole message
|
|
167
|
+
and asks `y/N`, and bot edits still print a diff and ask before writing. The menu has
|
|
168
|
+
no equivalent of `--yes` at all. With no terminal attached it prints this help instead.
|
|
139
169
|
|
|
140
170
|
## Safety model
|
|
141
171
|
|
|
142
172
|
| Command | Destructive? |
|
|
143
173
|
| --- | --- |
|
|
144
174
|
| `discover`, `search`, `doctor` | No — read-only |
|
|
175
|
+
| `create` | No — makes new things, changes nothing existing, after a `y/N` unless you pass `--yes` |
|
|
176
|
+
| `send` | Outward-facing — posts publicly as you, after showing the whole message and asking `y/N`. `--yes` skips the prompt only for destinations in `TELEGRAM_SEND_ALLOWLIST` |
|
|
145
177
|
| `bots` | No — changes settings on bots you own, after a diff and a `y/N` unless you pass `--yes`; reversible if you still have the old values, but `--remove-photo` and `--clear-commands` discard data Telegram will not hand back |
|
|
146
178
|
| `clear-messages` | Yes — but only with `--execute` **and** a typed `DELETE`, only messages, never topics |
|
|
147
179
|
|
|
@@ -151,7 +183,7 @@ and ask before writing. With no terminal attached it prints this help instead.
|
|
|
151
183
|
|
|
152
184
|
## Status
|
|
153
185
|
|
|
154
|
-
Stable for its
|
|
186
|
+
Stable for its six jobs; used regularly by its author. This is a solo project whose code was written by AI agents under review — issues are welcome, fixes are best-effort, and there is no support promise.
|
|
155
187
|
|
|
156
188
|
## License
|
|
157
189
|
|
|
@@ -21,3 +21,10 @@ Checked on 2026-08-13 for the `bots` command (Telethon 1.44.0):
|
|
|
21
21
|
- `photos.deletePhotos` needs an `InputPhoto` with an access hash, which `UserProfilePhoto` does not carry; fetch the photo through `photos.getUserPhotos` and convert it with `telethon.utils.get_input_photo`.
|
|
22
22
|
- `bots.exportBotToken` exists and would let an owner read a bot's token. This project never calls it: it sits next to a `revoke` flag, and reading credentials is outside what the tool does.
|
|
23
23
|
- Changing a bot's `@username`, creating a bot, and deleting a bot have no user-facing API and remain @BotFather-only.
|
|
24
|
+
|
|
25
|
+
Checked on 2026-08-25 for the `send` and `create` commands (Telethon 1.44.0):
|
|
26
|
+
|
|
27
|
+
- `TelegramClient.send_message(entity, message, reply_to=...)` posts into a forum topic by passing the topic id as `reply_to` — a topic *is* its root message thread, so there is no separate topic parameter.
|
|
28
|
+
- `channels.CreateChannelRequest` covers both a supergroup (`megagroup=True`) and a broadcast channel (`broadcast=True`), and takes a `forum` flag. Passing `forum=True` at creation avoids a second `channels.ToggleForumRequest` round trip, and with it the window where a group exists but the toggle failed.
|
|
29
|
+
- `messages.CreateForumTopicRequest` lives under `messages`, not `channels` (unlike `ToggleForumRequest`). Its `random_id` is auto-generated when omitted.
|
|
30
|
+
- Neither create request returns the created object directly. `CreateChannelRequest` returns `Updates` whose `chats[0]` is the new channel — `telethon.utils.get_peer_id` converts it to the marked `-100…` form. `CreateForumTopicRequest` returns `Updates` carrying only the topic's service message; that message's `id` **is** the new topic id.
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "telegram-tools"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.3.0"
|
|
8
8
|
description = "Local Telethon CLI for Telegram chat/topic ID discovery, message search/export, topic message clearing, and bot settings editing."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: telegram-tools
|
|
3
|
-
description: "Use when you need the real numeric ID of a Telegram chat, channel, group or forum topic — 'what's the ID of that topic?', 'which chat is -100…?', 'where do I send this?' —
|
|
4
|
-
version: 1.
|
|
3
|
+
description: "Use when you need the real numeric ID of a Telegram chat, channel, group or forum topic — 'what's the ID of that topic?', 'which chat is -100…?', 'where do I send this?' — when the user wants their own Telegram messages searched or exported to JSON/CSV, or when a message must be posted to a chat or topic the user has allowlisted."
|
|
4
|
+
version: 1.2.0
|
|
5
5
|
author: banozz0
|
|
6
6
|
license: MIT
|
|
7
7
|
platforms: [macos]
|
|
8
8
|
metadata:
|
|
9
9
|
hermes:
|
|
10
|
-
tags: [telegram, chat-ids, topic-ids, forum, search, export, cli]
|
|
10
|
+
tags: [telegram, chat-ids, topic-ids, forum, search, export, send, cli]
|
|
11
11
|
---
|
|
12
12
|
|
|
13
13
|
# telegram-tools
|
|
@@ -40,8 +40,9 @@ their own message history searched, filtered or exported. It is the fastest way
|
|
|
40
40
|
settle "which thread does this go to?", which is the single most common cause of a
|
|
41
41
|
message being delivered into a topic nobody reads.
|
|
42
42
|
|
|
43
|
-
It
|
|
44
|
-
|
|
43
|
+
It can also **send** a message and **create** a group, channel or topic. Those write
|
|
44
|
+
to Telegram as the user, so rules 2 and 3 below govern them — read those before
|
|
45
|
+
running either. It still does not run bots, download media, or delete topics.
|
|
45
46
|
|
|
46
47
|
## Hard rules
|
|
47
48
|
|
|
@@ -49,18 +50,31 @@ Anything that *posts* to Telegram belongs to a bot token and a different tool.
|
|
|
49
50
|
it is their user account, the same one their friends message. Reads are read-only and
|
|
50
51
|
fine. Anything that writes is theirs to run, not yours.
|
|
51
52
|
|
|
52
|
-
**2.
|
|
53
|
+
**2. `send` only goes where the user already said it may.** `send --yes` posts with
|
|
54
|
+
no human in the loop, and the CLI refuses it for any destination not in the user's
|
|
55
|
+
`TELEGRAM_SEND_ALLOWLIST`. That refusal is the whole safety model — do not work
|
|
56
|
+
around it by dropping `--yes` (which would block on a `y/N` prompt no agent can
|
|
57
|
+
answer), by editing the user's `.env`, or by picking a different chat. A destination
|
|
58
|
+
that is not allowlisted is a destination the user has not approved: draft the message,
|
|
59
|
+
show it to them, and let them send it or add the entry.
|
|
60
|
+
|
|
61
|
+
**3. Never run `create` unprompted.** New groups, channels and topics are real,
|
|
62
|
+
visible objects in the user's Telegram — other people see them appear. Create one only
|
|
63
|
+
when the user asked for that specific thing in this conversation, and never invent a
|
|
64
|
+
title. `create` outside an explicit ask is theirs to run, not yours.
|
|
65
|
+
|
|
66
|
+
**4. Never run `clear-messages`.** It deletes real messages out of their forum topics
|
|
53
67
|
and Telegram does not undo that. Dry-run is its default and the destructive path
|
|
54
68
|
needs both `--execute` and a typed confirmation, so you will not trip it by accident
|
|
55
69
|
— but do not run it at all, in any form, even to preview. If the answer is "those
|
|
56
70
|
messages should go", say so and let the user run it.
|
|
57
71
|
|
|
58
|
-
**
|
|
72
|
+
**5. Never print the credentials.** `TELEGRAM_API_ID`, `TELEGRAM_API_HASH` and the
|
|
59
73
|
`.session` file are secrets. Point at where they live; never read them out, copy
|
|
60
74
|
them, or paste them into a reply. `doctor` exists precisely so setup can be checked
|
|
61
75
|
without any of that reaching the screen.
|
|
62
76
|
|
|
63
|
-
**
|
|
77
|
+
**6. If the CLI errors, say so.** A login prompt, a flood-wait, an expired session —
|
|
64
78
|
that *is* the answer. Never guess a chat ID. A made-up `-100…` sends the user's next
|
|
65
79
|
alert into the void, and they will not find out until something they needed never
|
|
66
80
|
arrived.
|
|
@@ -76,6 +90,10 @@ arrived.
|
|
|
76
90
|
| "find where X was discussed" | `telegram-tools search --chat <id> --keyword "X"` |
|
|
77
91
|
| "everything in that topic since Monday" | `telegram-tools search --chat <id> --topic <topic-id> --since 2026-08-10` |
|
|
78
92
|
| "export it" | `telegram-tools search --chat <id> --format csv --output /path/out.csv` |
|
|
93
|
+
| "post this to that topic" (allowlisted) | `telegram-tools send --chat <id> --topic <topic-id> --text "..." --yes` |
|
|
94
|
+
| a long or multi-line message | pipe it: `... \| telegram-tools send --chat <id> --text - --yes` |
|
|
95
|
+
| "make me a group with topics" (they asked) | `telegram-tools create group --title "..." --forum --yes` |
|
|
96
|
+
| "add a topic to that group" (they asked) | `telegram-tools create topic --chat <id> --title "..." --yes` |
|
|
79
97
|
| "is telegram-tools set up?" | `telegram-tools doctor` |
|
|
80
98
|
|
|
81
99
|
- **`discover` defaults to admin/managed chats only** — the ones the user runs. Add
|
|
@@ -87,6 +105,14 @@ arrived.
|
|
|
87
105
|
Narrow with `--topic`, `--keyword`, `--from-user` (a username, an ID, or `me`),
|
|
88
106
|
`--since` / `--until` (ISO dates), and `--limit`. With no `--output` it prints a
|
|
89
107
|
readable table, which is usually what you want to summarise from.
|
|
108
|
+
- **`send` needs `--yes` from an agent session, and `--yes` needs the allowlist.**
|
|
109
|
+
Without `--yes` it prints the message and waits for a `y/N` nobody is there to
|
|
110
|
+
type. With `--yes` it refuses anything outside `TELEGRAM_SEND_ALLOWLIST` and the
|
|
111
|
+
error names the destination to add — relay that to the user verbatim rather than
|
|
112
|
+
retrying. `doctor` says how many destinations are listed, never which.
|
|
113
|
+
- **`send --topic` is the difference between delivered and lost.** Omitting it posts
|
|
114
|
+
to the chat itself, not the thread. Confirm the topic ID with `discover` first;
|
|
115
|
+
never guess one.
|
|
90
116
|
- **Check the tool's own help before using a flag** that is not in this table. The
|
|
91
117
|
CLI's `--help` is current; this file is a snapshot.
|
|
92
118
|
- **The menu is for the human at the keyboard.** `telegram-tools` with no arguments
|
|
@@ -95,7 +121,10 @@ arrived.
|
|
|
95
121
|
|
|
96
122
|
## Never run these
|
|
97
123
|
|
|
98
|
-
- **`clear-messages`** — irreversible deletion of the user's messages. Rule
|
|
124
|
+
- **`clear-messages`** — irreversible deletion of the user's messages. Rule 4 above.
|
|
125
|
+
- **`create` on your own initiative** — rule 3. If a new group or topic looks like
|
|
126
|
+
the right answer, propose it and let the user say yes; do not create it and report
|
|
127
|
+
back.
|
|
99
128
|
- **`bots`** — it edits a live bot's name, bio, description, commands, profile photo
|
|
100
129
|
and default admin rights. Those are the user's public-facing bots; the edits are
|
|
101
130
|
theirs to make. Check `--help` for whether the installed build has it at all.
|
|
@@ -4,6 +4,7 @@ import argparse
|
|
|
4
4
|
import asyncio
|
|
5
5
|
import json
|
|
6
6
|
import sys
|
|
7
|
+
from functools import partial
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
from typing import Sequence
|
|
9
10
|
|
|
@@ -24,12 +25,14 @@ from telegram_tools.bots import (
|
|
|
24
25
|
)
|
|
25
26
|
from telegram_tools.client import create_client
|
|
26
27
|
from telegram_tools.config import ConfigError, bot_id_from_token, load_config, lookup_bot_token, resolve_bot_token
|
|
28
|
+
from telegram_tools.create import confirm_create, create_channel, create_group, create_topic, format_create_preview
|
|
27
29
|
from telegram_tools.delete import confirm_clear_topic_messages, delete_topic_messages
|
|
28
30
|
from telegram_tools.discovery import discover_chats, filter_chats, format_discovery_table
|
|
29
31
|
from telegram_tools.doctor import run_doctor
|
|
30
32
|
from telegram_tools.exporters import write_records
|
|
31
33
|
from telegram_tools.resolver import EntityResolutionError, resolve_chat
|
|
32
34
|
from telegram_tools.search import format_message_records, search_messages
|
|
35
|
+
from telegram_tools.send import SendTarget, confirm_send, format_send_preview, require_send_allowed, send_message
|
|
33
36
|
from telegram_tools.topics import get_forum_topics, get_forum_topics_by_ids
|
|
34
37
|
|
|
35
38
|
|
|
@@ -85,6 +88,35 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
85
88
|
bots_parser.add_argument("--channel-rights", help=f"Default admin rights for channels, comma-separated, or none (needs a bot token). Valid names: {valid_rights}")
|
|
86
89
|
bots_parser.add_argument("--yes", action="store_true", help="Skip the confirmation prompt")
|
|
87
90
|
|
|
91
|
+
send_parser = subparsers.add_parser("send", help="Send a message to a chat or forum topic")
|
|
92
|
+
send_parser.add_argument("--chat", required=True, help="Chat/channel username, link, or ID")
|
|
93
|
+
send_parser.add_argument("--topic", type=int, help="Topic ID to post into; omit for the chat itself")
|
|
94
|
+
send_parser.add_argument("--text", required=True, help="Message text, or - to read it from stdin")
|
|
95
|
+
send_parser.add_argument(
|
|
96
|
+
"--yes",
|
|
97
|
+
action="store_true",
|
|
98
|
+
help="Skip the preview and send; the destination must be in TELEGRAM_SEND_ALLOWLIST",
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
create_parser = subparsers.add_parser("create", help="Create a group, channel, or forum topic")
|
|
102
|
+
create_kinds = create_parser.add_subparsers(dest="create_kind")
|
|
103
|
+
|
|
104
|
+
create_group_parser = create_kinds.add_parser("group", help="Create a supergroup, optionally with topics")
|
|
105
|
+
create_group_parser.add_argument("--title", required=True, help="Group name")
|
|
106
|
+
create_group_parser.add_argument("--about", help="Group description")
|
|
107
|
+
create_group_parser.add_argument("--forum", action="store_true", help="Enable topics on the new group")
|
|
108
|
+
|
|
109
|
+
create_channel_parser = create_kinds.add_parser("channel", help="Create a broadcast channel")
|
|
110
|
+
create_channel_parser.add_argument("--title", required=True, help="Channel name")
|
|
111
|
+
create_channel_parser.add_argument("--about", help="Channel description")
|
|
112
|
+
|
|
113
|
+
create_topic_parser = create_kinds.add_parser("topic", help="Create a topic in a forum group")
|
|
114
|
+
create_topic_parser.add_argument("--chat", required=True, help="Forum group username, link, or ID")
|
|
115
|
+
create_topic_parser.add_argument("--title", required=True, help="Topic name")
|
|
116
|
+
|
|
117
|
+
for kind_parser in (create_group_parser, create_channel_parser, create_topic_parser):
|
|
118
|
+
kind_parser.add_argument("--yes", action="store_true", help="Skip the confirmation prompt")
|
|
119
|
+
|
|
88
120
|
subparsers.add_parser("doctor", help="Check local setup without printing secrets")
|
|
89
121
|
|
|
90
122
|
return parser
|
|
@@ -154,6 +186,91 @@ async def _run_search(client, args) -> int:
|
|
|
154
186
|
return 0
|
|
155
187
|
|
|
156
188
|
|
|
189
|
+
def _entity_title(entity, fallback: str) -> str:
|
|
190
|
+
"""A chat's name for the preview: a title, a person's name, or what was typed."""
|
|
191
|
+
title = getattr(entity, "title", None)
|
|
192
|
+
if title:
|
|
193
|
+
return str(title)
|
|
194
|
+
parts = [getattr(entity, "first_name", None), getattr(entity, "last_name", None)]
|
|
195
|
+
name = " ".join(part for part in parts if part)
|
|
196
|
+
return name or str(getattr(entity, "username", None) or fallback)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _message_text(raw: str) -> str:
|
|
200
|
+
# `-` is how a multi-line body gets in: quoting newlines through a shell flag is
|
|
201
|
+
# the kind of thing that silently sends half a message.
|
|
202
|
+
text = sys.stdin.read() if raw == "-" else raw
|
|
203
|
+
text = text.strip()
|
|
204
|
+
if not text:
|
|
205
|
+
raise ValueError("Nothing to send: the message text is empty.")
|
|
206
|
+
return text
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
async def _run_send(client, args, config) -> int:
|
|
210
|
+
text = _message_text(args.text)
|
|
211
|
+
resolved = await resolve_chat(client, args.chat)
|
|
212
|
+
peer = resolved.input_entity
|
|
213
|
+
|
|
214
|
+
topic = None
|
|
215
|
+
if args.topic is not None:
|
|
216
|
+
topics = await get_forum_topics_by_ids(client, peer, [args.topic])
|
|
217
|
+
topic = topics[0] if topics else None
|
|
218
|
+
|
|
219
|
+
target = SendTarget(chat_id=resolved.id, chat_title=_entity_title(resolved.entity, args.chat), topic=topic)
|
|
220
|
+
|
|
221
|
+
confirm = None
|
|
222
|
+
if args.yes:
|
|
223
|
+
require_send_allowed(
|
|
224
|
+
config.send_allowlist,
|
|
225
|
+
chat_id=resolved.id,
|
|
226
|
+
username=getattr(resolved.entity, "username", None),
|
|
227
|
+
topic_id=args.topic,
|
|
228
|
+
)
|
|
229
|
+
else:
|
|
230
|
+
preview = format_send_preview(target, text, sender=_entity_title(await client.get_me(), "you"))
|
|
231
|
+
confirm = partial(confirm_send, preview)
|
|
232
|
+
|
|
233
|
+
result = await send_message(client, peer, target, text, confirm=confirm)
|
|
234
|
+
print(json.dumps(result.to_dict(), indent=2))
|
|
235
|
+
return 1 if result.cancelled else 0
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
async def _run_create(client, args) -> int:
|
|
239
|
+
if args.create_kind is None:
|
|
240
|
+
raise ValueError("create needs one of: group, channel, topic.")
|
|
241
|
+
|
|
242
|
+
chat_title = None
|
|
243
|
+
peer = None
|
|
244
|
+
chat_id = None
|
|
245
|
+
if args.create_kind == "topic":
|
|
246
|
+
resolved = await resolve_chat(client, args.chat)
|
|
247
|
+
peer = resolved.input_entity
|
|
248
|
+
chat_id = resolved.id
|
|
249
|
+
chat_title = _entity_title(resolved.entity, args.chat)
|
|
250
|
+
|
|
251
|
+
forum = bool(getattr(args, "forum", False))
|
|
252
|
+
confirm = None
|
|
253
|
+
if not args.yes:
|
|
254
|
+
preview = format_create_preview(
|
|
255
|
+
args.create_kind,
|
|
256
|
+
args.title,
|
|
257
|
+
about=getattr(args, "about", None),
|
|
258
|
+
forum=forum,
|
|
259
|
+
chat_title=chat_title,
|
|
260
|
+
)
|
|
261
|
+
confirm = partial(confirm_create, preview)
|
|
262
|
+
|
|
263
|
+
if args.create_kind == "group":
|
|
264
|
+
created = await create_group(client, args.title, about=args.about, forum=forum, confirm=confirm)
|
|
265
|
+
elif args.create_kind == "channel":
|
|
266
|
+
created = await create_channel(client, args.title, about=args.about, confirm=confirm)
|
|
267
|
+
else:
|
|
268
|
+
created = await create_topic(client, peer, chat_id=chat_id, title=args.title, confirm=confirm)
|
|
269
|
+
|
|
270
|
+
print(json.dumps(created.to_dict(), indent=2))
|
|
271
|
+
return 1 if created.cancelled else 0
|
|
272
|
+
|
|
273
|
+
|
|
157
274
|
EDIT_FLAGS = ("name", "bio", "description", "commands", "clear_commands", "photo", "remove_photo", "group_rights", "channel_rights")
|
|
158
275
|
|
|
159
276
|
|
|
@@ -224,7 +341,7 @@ async def _run_bots(client, args, config) -> int:
|
|
|
224
341
|
return 0
|
|
225
342
|
|
|
226
343
|
if not resolved.is_owned:
|
|
227
|
-
raise PermissionError(f"You do not own @{profile.username
|
|
344
|
+
raise PermissionError(f"You do not own {f'@{profile.username}' if profile.username else f'bot {profile.id}'}; only its owner can edit it.")
|
|
228
345
|
|
|
229
346
|
if "commands" in requested:
|
|
230
347
|
requested["commands"] = parse_commands_file(requested["commands"])
|
|
@@ -294,6 +411,10 @@ async def run(args, *, client=None, config=None) -> int:
|
|
|
294
411
|
return await _run_search(client, args)
|
|
295
412
|
if args.command == "bots":
|
|
296
413
|
return await _run_bots(client, args, config)
|
|
414
|
+
if args.command == "send":
|
|
415
|
+
return await _run_send(client, args, config)
|
|
416
|
+
if args.command == "create":
|
|
417
|
+
return await _run_create(client, args)
|
|
297
418
|
raise ValueError(f"Unknown command: {args.command}")
|
|
298
419
|
finally:
|
|
299
420
|
if owns_client:
|