discord-tools-cli 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.
- discord_tools_cli-0.1.0/.github/workflows/tests.yml +53 -0
- discord_tools_cli-0.1.0/.gitignore +18 -0
- discord_tools_cli-0.1.0/AGENTS.md +65 -0
- discord_tools_cli-0.1.0/CHANGELOG.md +33 -0
- discord_tools_cli-0.1.0/CLAUDE.md +1 -0
- discord_tools_cli-0.1.0/CONTEXT.md +40 -0
- discord_tools_cli-0.1.0/LICENSE +21 -0
- discord_tools_cli-0.1.0/PKG-INFO +124 -0
- discord_tools_cli-0.1.0/README.md +75 -0
- discord_tools_cli-0.1.0/SPEC.md +117 -0
- discord_tools_cli-0.1.0/docs/agents/domain.md +51 -0
- discord_tools_cli-0.1.0/docs/agents/issue-tracker.md +19 -0
- discord_tools_cli-0.1.0/docs/agents/triage-labels.md +15 -0
- discord_tools_cli-0.1.0/pyproject.toml +52 -0
- discord_tools_cli-0.1.0/skill/SKILL.md +155 -0
- discord_tools_cli-0.1.0/src/discord_tools/__init__.py +1 -0
- discord_tools_cli-0.1.0/src/discord_tools/bot.py +96 -0
- discord_tools_cli-0.1.0/src/discord_tools/cli.py +362 -0
- discord_tools_cli-0.1.0/src/discord_tools/client.py +235 -0
- discord_tools_cli-0.1.0/src/discord_tools/config.py +160 -0
- discord_tools_cli-0.1.0/src/discord_tools/create.py +57 -0
- discord_tools_cli-0.1.0/src/discord_tools/delete.py +108 -0
- discord_tools_cli-0.1.0/src/discord_tools/discovery.py +82 -0
- discord_tools_cli-0.1.0/src/discord_tools/doctor.py +201 -0
- discord_tools_cli-0.1.0/src/discord_tools/exporters.py +46 -0
- discord_tools_cli-0.1.0/src/discord_tools/menu.py +592 -0
- discord_tools_cli-0.1.0/src/discord_tools/models.py +133 -0
- discord_tools_cli-0.1.0/src/discord_tools/portal.py +133 -0
- discord_tools_cli-0.1.0/src/discord_tools/prompts.py +170 -0
- discord_tools_cli-0.1.0/src/discord_tools/records.py +86 -0
- discord_tools_cli-0.1.0/src/discord_tools/search.py +62 -0
- discord_tools_cli-0.1.0/src/discord_tools/send.py +86 -0
- discord_tools_cli-0.1.0/tests/conftest.py +125 -0
- discord_tools_cli-0.1.0/tests/test_bot.py +62 -0
- discord_tools_cli-0.1.0/tests/test_cli.py +216 -0
- discord_tools_cli-0.1.0/tests/test_config.py +114 -0
- discord_tools_cli-0.1.0/tests/test_create.py +50 -0
- discord_tools_cli-0.1.0/tests/test_delete.py +107 -0
- discord_tools_cli-0.1.0/tests/test_discovery.py +59 -0
- discord_tools_cli-0.1.0/tests/test_doctor.py +138 -0
- discord_tools_cli-0.1.0/tests/test_exporters.py +43 -0
- discord_tools_cli-0.1.0/tests/test_menu.py +139 -0
- discord_tools_cli-0.1.0/tests/test_portal.py +165 -0
- discord_tools_cli-0.1.0/tests/test_prompts.py +214 -0
- discord_tools_cli-0.1.0/tests/test_records.py +80 -0
- discord_tools_cli-0.1.0/tests/test_search.py +69 -0
- discord_tools_cli-0.1.0/tests/test_send.py +76 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
name: Tests
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
pull_request:
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
name: Python ${{ matrix.python-version }}
|
|
12
|
+
runs-on: macos-latest
|
|
13
|
+
strategy:
|
|
14
|
+
fail-fast: false
|
|
15
|
+
matrix:
|
|
16
|
+
python-version:
|
|
17
|
+
- "3.11"
|
|
18
|
+
- "3.12"
|
|
19
|
+
- "3.13"
|
|
20
|
+
- "3.14"
|
|
21
|
+
|
|
22
|
+
steps:
|
|
23
|
+
- name: Check out repository
|
|
24
|
+
uses: actions/checkout@v4
|
|
25
|
+
|
|
26
|
+
- name: Set up Python
|
|
27
|
+
uses: actions/setup-python@v5
|
|
28
|
+
with:
|
|
29
|
+
python-version: ${{ matrix.python-version }}
|
|
30
|
+
cache: pip
|
|
31
|
+
|
|
32
|
+
- name: Install package
|
|
33
|
+
run: |
|
|
34
|
+
python -m pip install --upgrade pip
|
|
35
|
+
python -m pip install -e ".[dev]"
|
|
36
|
+
|
|
37
|
+
- name: Run tests
|
|
38
|
+
run: python -m pytest
|
|
39
|
+
|
|
40
|
+
- name: Compile sources
|
|
41
|
+
run: python -m compileall -q src
|
|
42
|
+
|
|
43
|
+
- name: Check CLI help
|
|
44
|
+
run: |
|
|
45
|
+
discord-tools --help
|
|
46
|
+
discord-tools auth --help
|
|
47
|
+
discord-tools doctor --help
|
|
48
|
+
discord-tools discover --help
|
|
49
|
+
discord-tools search --help
|
|
50
|
+
discord-tools send --help
|
|
51
|
+
discord-tools create --help
|
|
52
|
+
discord-tools clear-messages --help
|
|
53
|
+
discord-tools bot --help
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
.venv/
|
|
3
|
+
__pycache__/
|
|
4
|
+
.pytest_cache/
|
|
5
|
+
.ruff_cache/
|
|
6
|
+
.mypy_cache/
|
|
7
|
+
*.egg-info/
|
|
8
|
+
dist/
|
|
9
|
+
build/
|
|
10
|
+
|
|
11
|
+
# .env.example stays untracked too: the global secrets hook blocks *.env* files
|
|
12
|
+
# outright. The same content lives in README setup docs instead.
|
|
13
|
+
.env
|
|
14
|
+
.env.*
|
|
15
|
+
|
|
16
|
+
.discord-tools/
|
|
17
|
+
exports/
|
|
18
|
+
*.log
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# discord-tools — agent brief
|
|
2
|
+
|
|
3
|
+
Public repo — contributor-facing. telegram-tools for Discord: a local CLI on
|
|
4
|
+
Discord's **bot system** — no self-bots, ever (ToS).
|
|
5
|
+
Bot token per profile in `~/.discord-tools/` (0600, env override); a bot per
|
|
6
|
+
agent is the intended use, `--profile <name>` picks one.
|
|
7
|
+
|
|
8
|
+
`SPEC.md` is the build contract — read it before touching code. Shaping record:
|
|
9
|
+
`~/code/incubator/discord-tools/IDEA.md`. Conventions mirror
|
|
10
|
+
`~/code/telegram-tools` (v3.4.1) deliberately: same menu-vs-subcommand split,
|
|
11
|
+
same test culture, same release recipe — when in doubt, look at the sibling.
|
|
12
|
+
|
|
13
|
+
## Commands (v1 = full parity, built)
|
|
14
|
+
|
|
15
|
+
auth (guided portal setup) · discover (server/channel/thread IDs) ·
|
|
16
|
+
search/export (history fetch + local filter; Discord gives bots no search
|
|
17
|
+
API) · send · create (channel/thread/category) · clear-messages · bot
|
|
18
|
+
(settings + invite URL for the active profile) · doctor (token,
|
|
19
|
+
message-content intent, servers, per-channel perms). v1 shipped 2026-08-27
|
|
20
|
+
after the joint testing session.
|
|
21
|
+
|
|
22
|
+
## Releasing (maintainer only)
|
|
23
|
+
|
|
24
|
+
PyPI account is **banozz** (not the GitHub handle); the distribution is
|
|
25
|
+
**discord-tools-cli** (`discord-tools` is squatted by an archived unrelated
|
|
26
|
+
package) while the console script stays `discord-tools`. Recipe + traps: the
|
|
27
|
+
maintainer's private runbook (same one as telegram-tools). Rebuild `dist/`
|
|
28
|
+
after any source edit.
|
|
29
|
+
|
|
30
|
+
## Working here
|
|
31
|
+
|
|
32
|
+
- Python ≥3.11 · discord.py 2.x login-only REST (`client.py` is the one seam;
|
|
33
|
+
tests mock exactly it) · python-dotenv · pytest no-network · hatchling · MIT.
|
|
34
|
+
- Test: `.venv/bin/python -m pytest -q` → all pass, no network, no real token.
|
|
35
|
+
CI adds `compileall` + per-subcommand `--help` smoke.
|
|
36
|
+
- Bare invocation is the human menu; agents pass a subcommand.
|
|
37
|
+
- Domain terms live in `CONTEXT.md`; read it before renaming things.
|
|
38
|
+
- A CLI-surface change updates `skill/SKILL.md` in the same commit.
|
|
39
|
+
- A user-visible fix gets its CHANGELOG entry + version bump in the same change.
|
|
40
|
+
- Never commit tokens, IDs of real servers, or exported chat data. `.env*`
|
|
41
|
+
files (even `.env.example`) stay untracked — the global secrets hook blocks
|
|
42
|
+
them; setup docs live in the README instead.
|
|
43
|
+
|
|
44
|
+
## Agent skills
|
|
45
|
+
|
|
46
|
+
### Issue tracker
|
|
47
|
+
|
|
48
|
+
Shared Beads board at `/Users/Shared/agent-board` (fleet default). See `docs/agents/issue-tracker.md`.
|
|
49
|
+
|
|
50
|
+
### Triage labels
|
|
51
|
+
|
|
52
|
+
Default five-role vocabulary, unchanged. See `docs/agents/triage-labels.md`.
|
|
53
|
+
|
|
54
|
+
### Domain docs
|
|
55
|
+
|
|
56
|
+
Single-context: `CONTEXT.md` + `docs/adr/` at the root. See `docs/agents/domain.md`.
|
|
57
|
+
|
|
58
|
+
## Destructive commands
|
|
59
|
+
|
|
60
|
+
Same gates as the sibling, non-negotiable: `clear-messages` dry-runs by
|
|
61
|
+
default, executes only with `--execute` + typed `DELETE` (bulk API caps at
|
|
62
|
+
14 days; older messages delete one-by-one, slower). `send` previews the full
|
|
63
|
+
message + y/N; `--yes` requires the destination in `DISCORD_SEND_ALLOWLIST`
|
|
64
|
+
(unset = refuse). `create` and `bot` settings confirm before touching anything
|
|
65
|
+
real. The menu is never a shorter path past a gate.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 — 2026-08-27
|
|
4
|
+
|
|
5
|
+
Initial release to telegram-tools parity (v1 contract in `SPEC.md`). Published
|
|
6
|
+
as `discord-tools-cli` (the plain name is squatted on PyPI); the command is
|
|
7
|
+
`discord-tools`:
|
|
8
|
+
|
|
9
|
+
- `auth`: guided Developer Portal setup — walkthrough, hidden token paste,
|
|
10
|
+
live verification, message-content-intent re-check loop, invite URL with the
|
|
11
|
+
right permission bits, token stored per profile in `~/.discord-tools/.env`
|
|
12
|
+
(0600).
|
|
13
|
+
- `doctor`: offline checks (Python, config, token shape, allowlist counts) and
|
|
14
|
+
live checks (login, message-content intent, joined servers); `--channel`
|
|
15
|
+
adds per-permission checks and an empty-content probe.
|
|
16
|
+
- `discover`: server → channel → active-thread tree with IDs, `--server`,
|
|
17
|
+
`--json`.
|
|
18
|
+
- `search`: paginated history fetch + local filtering (keyword, author, date
|
|
19
|
+
bounds, limit) with an early stop at `--since`; exports to JSON/CSV under
|
|
20
|
+
`~/.discord-tools/exports/` by default.
|
|
21
|
+
- `send`: full-message preview + y/N; `--yes` gated by
|
|
22
|
+
`DISCORD_SEND_ALLOWLIST` (unset = refuse); attachment support with
|
|
23
|
+
pre-confirm existence checks.
|
|
24
|
+
- `create channel|category|thread`: confirmation previews naming what will
|
|
25
|
+
exist where.
|
|
26
|
+
- `clear-messages`: dry-run by default reporting the 14-day bulk/single split
|
|
27
|
+
from snowflake math; execution requires `--execute` + typed `DELETE`; bulk
|
|
28
|
+
endpoint for recent messages, paced one-by-one deletes beyond it.
|
|
29
|
+
- `bot`: profile view (identity, description, avatar, intent, invite URL);
|
|
30
|
+
edits behind a diff + confirm; `--invite`.
|
|
31
|
+
- Human menu on bare invocation mirroring telegram-tools; every destructive
|
|
32
|
+
action goes through the same gates as the flags.
|
|
33
|
+
- Bundled agent skill (`skill/SKILL.md`).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
AGENTS.md
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# discord-tools — domain context
|
|
2
|
+
|
|
3
|
+
The terms this codebase uses, and the boundaries they imply.
|
|
4
|
+
|
|
5
|
+
- **Seam** — `DiscordClient` (`client.py`): the one boundary wrapping every
|
|
6
|
+
REST call. Everything above it (commands, menu, gates) works with plain
|
|
7
|
+
models and dicts; everything discord.py stays below it. Tests mock exactly
|
|
8
|
+
this interface (`tests/conftest.py::FakeClient`). Login-only: the gateway is
|
|
9
|
+
never connected.
|
|
10
|
+
- **Profile** — a named bot token in `~/.discord-tools/.env`
|
|
11
|
+
(`DISCORD_BOT_TOKENS=name:token,...`). A bot per agent is the intended use.
|
|
12
|
+
`--profile` / `DISCORD_TOOLS_PROFILE` select one; `DISCORD_TOKEN` overrides.
|
|
13
|
+
- **Gate** — the confirmation pattern on every destructive path, copied from
|
|
14
|
+
telegram-tools verbatim: send = preview + y/N (`--yes` needs the
|
|
15
|
+
allowlist), create = preview + y/N, clear = dry-run default + `--execute` +
|
|
16
|
+
typed `DELETE`, bot edits = diff + confirm. The menu builds the same args
|
|
17
|
+
the flags would and never sets `yes`/`execute` itself — it is never a
|
|
18
|
+
shorter path past a gate.
|
|
19
|
+
- **Allowlist** — `DISCORD_SEND_ALLOWLIST`: channel/thread IDs an unattended
|
|
20
|
+
(`--yes`) send may target. Unset refuses everything; only the unattended
|
|
21
|
+
path consults it.
|
|
22
|
+
- **Bulk window** — Discord's hard 14-day limit on the bulk-delete endpoint.
|
|
23
|
+
`split_bulk_window` (`delete.py`) partitions message IDs by snowflake
|
|
24
|
+
timestamp (pure math, no API calls); older messages delete one-by-one,
|
|
25
|
+
paced.
|
|
26
|
+
- **Snowflake** — a Discord ID; its top bits encode a creation timestamp
|
|
27
|
+
(`records.py::snowflake_time`). Threads are channels: a thread ID is valid
|
|
28
|
+
anywhere a channel ID is.
|
|
29
|
+
- **Intent (message-content)** — the portal toggle without which fetched
|
|
30
|
+
messages have empty `content`. Read from application flags
|
|
31
|
+
(`/applications/@me`); `auth` walks the user through enabling it, `doctor`
|
|
32
|
+
checks the flag and probes real messages for the symptom.
|
|
33
|
+
- **Record** — the plain dict a message becomes (`records.py`): what search
|
|
34
|
+
prints and exports write. `has_media` keeps attachment-only messages from
|
|
35
|
+
reading as empty.
|
|
36
|
+
- **Runner contract** — the menu calls `cli.run(args, client=..., config=...)`
|
|
37
|
+
with namespaces shaped exactly like parsed flags; a passed-in client is
|
|
38
|
+
owned by the caller and never closed by `run`.
|
|
39
|
+
|
|
40
|
+
Architecture decisions with more context than fits here go to `docs/adr/`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 telegram-tools contributors
|
|
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.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: discord-tools-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Local Discord bot CLI for server/channel/thread ID discovery, message search/export, sending, channel creation, message clearing, and bot settings — with a guided Developer Portal setup.
|
|
5
|
+
Project-URL: Homepage, https://github.com/banozz0/discord-tools
|
|
6
|
+
Project-URL: Repository, https://github.com/banozz0/discord-tools
|
|
7
|
+
Project-URL: Issues, https://github.com/banozz0/discord-tools/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/banozz0/discord-tools/blob/main/CHANGELOG.md
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 telegram-tools contributors
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Keywords: bot,channels,cli,discord,export,threads
|
|
32
|
+
Classifier: Development Status :: 3 - Alpha
|
|
33
|
+
Classifier: Environment :: Console
|
|
34
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
35
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
+
Classifier: Programming Language :: Python :: 3
|
|
37
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
41
|
+
Classifier: Topic :: Communications :: Chat
|
|
42
|
+
Classifier: Topic :: Utilities
|
|
43
|
+
Requires-Python: >=3.11
|
|
44
|
+
Requires-Dist: discord-py<3,>=2.4
|
|
45
|
+
Requires-Dist: python-dotenv<2,>=1.0
|
|
46
|
+
Provides-Extra: dev
|
|
47
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
48
|
+
Description-Content-Type: text/markdown
|
|
49
|
+
|
|
50
|
+
# discord-tools
|
|
51
|
+
|
|
52
|
+
A local CLI for operating your Discord **bot**: discover server/channel/thread
|
|
53
|
+
IDs, search and export messages, send messages, create channels and threads,
|
|
54
|
+
clear messages, and manage the bot's settings — with a guided setup that walks
|
|
55
|
+
you through the Discord Developer Portal (the BotFather it never had).
|
|
56
|
+
|
|
57
|
+
Sibling of [telegram-tools](https://github.com/banozz0/telegram-tools): same
|
|
58
|
+
menu for humans, same subcommands for agents, same safety gates on anything
|
|
59
|
+
destructive. Bot-token auth only — no self-bots, ever (Discord ToS).
|
|
60
|
+
|
|
61
|
+
## Install
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
pip install discord-tools-cli
|
|
65
|
+
discord-tools auth # guided bot setup: portal walkthrough, token check, invite URL
|
|
66
|
+
discord-tools doctor # verify token, message-content intent, servers, permissions
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
(The PyPI name is `discord-tools-cli` — plain `discord-tools` is squatted by an
|
|
70
|
+
unrelated, archived package. The installed command is `discord-tools`.)
|
|
71
|
+
|
|
72
|
+
Python 3.11+. Bare `discord-tools` opens a looping menu for humans; agents and
|
|
73
|
+
scripts pass a subcommand.
|
|
74
|
+
|
|
75
|
+
## Commands
|
|
76
|
+
|
|
77
|
+
| Command | What it does |
|
|
78
|
+
|---|---|
|
|
79
|
+
| `auth` | Guided Developer Portal setup; verifies the token and the message-content intent, stores the token as a named profile, prints the invite URL |
|
|
80
|
+
| `doctor` | Checks Python, config, token, intent, joined servers; `--channel <id>` adds per-channel permission checks and a message-visibility probe |
|
|
81
|
+
| `discover` | Prints the server → channel → thread tree with every ID; `--server <id>` narrows, `--json <path>` writes a file |
|
|
82
|
+
| `search` | Searches a channel/thread's history locally (Discord gives bots no search API): `--keyword`, `--from-user`, `--since`, `--until`, `--limit`; `--output <name>` exports JSON/CSV |
|
|
83
|
+
| `send` | Posts as the bot after a full-message preview + y/N; `--yes` skips the prompt only for channels in `DISCORD_SEND_ALLOWLIST` |
|
|
84
|
+
| `create` | `channel` / `category` / `thread`, each behind a confirmation |
|
|
85
|
+
| `clear-messages` | Dry-run by default; deleting for real takes `--execute` **and** typing `DELETE`. The dry-run reports which messages fall inside Discord's 14-day bulk window and which will delete one-by-one (slower) |
|
|
86
|
+
| `bot` | Shows the active profile's bot (username, description, avatar, intent, invite URL); edits go behind a diff + confirm |
|
|
87
|
+
|
|
88
|
+
## Profiles: a bot per agent
|
|
89
|
+
|
|
90
|
+
Tokens live in `~/.discord-tools/.env` (mode 0600) as named profiles:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
DISCORD_BOT_TOKENS=default:token-a,dobby:token-b
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`--profile dobby` (before the subcommand) selects one; `DISCORD_TOOLS_PROFILE`
|
|
97
|
+
sets the default; `DISCORD_TOKEN` overrides everything. `auth` writes this
|
|
98
|
+
file for you — run it once per bot.
|
|
99
|
+
|
|
100
|
+
`DISCORD_SEND_ALLOWLIST` is a comma-separated list of channel/thread IDs that
|
|
101
|
+
`send --yes` may post to. Unset means every unattended send is refused — each
|
|
102
|
+
destination is opted in by hand.
|
|
103
|
+
|
|
104
|
+
## Exports stay out of your repos
|
|
105
|
+
|
|
106
|
+
Relative `--output` names land in `~/.discord-tools/exports/`, never the
|
|
107
|
+
working directory. Message text coming back empty on every message means the
|
|
108
|
+
**message-content intent** is off in the portal — `doctor` names it and `auth`
|
|
109
|
+
walks you through enabling it.
|
|
110
|
+
|
|
111
|
+
## For agents
|
|
112
|
+
|
|
113
|
+
`skill/SKILL.md` is a bundled agent skill describing the CLI surface and the
|
|
114
|
+
rules an agent must follow (never `clear-messages`, allowlist-gated sends,
|
|
115
|
+
never print tokens). It updates in the same commit as any CLI-surface change.
|
|
116
|
+
|
|
117
|
+
## Development
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
|
|
121
|
+
.venv/bin/python -m pytest # no network, no real token
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
MIT. See `SPEC.md` for the v1 contract and `CHANGELOG.md` for history.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# discord-tools
|
|
2
|
+
|
|
3
|
+
A local CLI for operating your Discord **bot**: discover server/channel/thread
|
|
4
|
+
IDs, search and export messages, send messages, create channels and threads,
|
|
5
|
+
clear messages, and manage the bot's settings — with a guided setup that walks
|
|
6
|
+
you through the Discord Developer Portal (the BotFather it never had).
|
|
7
|
+
|
|
8
|
+
Sibling of [telegram-tools](https://github.com/banozz0/telegram-tools): same
|
|
9
|
+
menu for humans, same subcommands for agents, same safety gates on anything
|
|
10
|
+
destructive. Bot-token auth only — no self-bots, ever (Discord ToS).
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install discord-tools-cli
|
|
16
|
+
discord-tools auth # guided bot setup: portal walkthrough, token check, invite URL
|
|
17
|
+
discord-tools doctor # verify token, message-content intent, servers, permissions
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
(The PyPI name is `discord-tools-cli` — plain `discord-tools` is squatted by an
|
|
21
|
+
unrelated, archived package. The installed command is `discord-tools`.)
|
|
22
|
+
|
|
23
|
+
Python 3.11+. Bare `discord-tools` opens a looping menu for humans; agents and
|
|
24
|
+
scripts pass a subcommand.
|
|
25
|
+
|
|
26
|
+
## Commands
|
|
27
|
+
|
|
28
|
+
| Command | What it does |
|
|
29
|
+
|---|---|
|
|
30
|
+
| `auth` | Guided Developer Portal setup; verifies the token and the message-content intent, stores the token as a named profile, prints the invite URL |
|
|
31
|
+
| `doctor` | Checks Python, config, token, intent, joined servers; `--channel <id>` adds per-channel permission checks and a message-visibility probe |
|
|
32
|
+
| `discover` | Prints the server → channel → thread tree with every ID; `--server <id>` narrows, `--json <path>` writes a file |
|
|
33
|
+
| `search` | Searches a channel/thread's history locally (Discord gives bots no search API): `--keyword`, `--from-user`, `--since`, `--until`, `--limit`; `--output <name>` exports JSON/CSV |
|
|
34
|
+
| `send` | Posts as the bot after a full-message preview + y/N; `--yes` skips the prompt only for channels in `DISCORD_SEND_ALLOWLIST` |
|
|
35
|
+
| `create` | `channel` / `category` / `thread`, each behind a confirmation |
|
|
36
|
+
| `clear-messages` | Dry-run by default; deleting for real takes `--execute` **and** typing `DELETE`. The dry-run reports which messages fall inside Discord's 14-day bulk window and which will delete one-by-one (slower) |
|
|
37
|
+
| `bot` | Shows the active profile's bot (username, description, avatar, intent, invite URL); edits go behind a diff + confirm |
|
|
38
|
+
|
|
39
|
+
## Profiles: a bot per agent
|
|
40
|
+
|
|
41
|
+
Tokens live in `~/.discord-tools/.env` (mode 0600) as named profiles:
|
|
42
|
+
|
|
43
|
+
```
|
|
44
|
+
DISCORD_BOT_TOKENS=default:token-a,dobby:token-b
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`--profile dobby` (before the subcommand) selects one; `DISCORD_TOOLS_PROFILE`
|
|
48
|
+
sets the default; `DISCORD_TOKEN` overrides everything. `auth` writes this
|
|
49
|
+
file for you — run it once per bot.
|
|
50
|
+
|
|
51
|
+
`DISCORD_SEND_ALLOWLIST` is a comma-separated list of channel/thread IDs that
|
|
52
|
+
`send --yes` may post to. Unset means every unattended send is refused — each
|
|
53
|
+
destination is opted in by hand.
|
|
54
|
+
|
|
55
|
+
## Exports stay out of your repos
|
|
56
|
+
|
|
57
|
+
Relative `--output` names land in `~/.discord-tools/exports/`, never the
|
|
58
|
+
working directory. Message text coming back empty on every message means the
|
|
59
|
+
**message-content intent** is off in the portal — `doctor` names it and `auth`
|
|
60
|
+
walks you through enabling it.
|
|
61
|
+
|
|
62
|
+
## For agents
|
|
63
|
+
|
|
64
|
+
`skill/SKILL.md` is a bundled agent skill describing the CLI surface and the
|
|
65
|
+
rules an agent must follow (never `clear-messages`, allowlist-gated sends,
|
|
66
|
+
never print tokens). It updates in the same commit as any CLI-surface change.
|
|
67
|
+
|
|
68
|
+
## Development
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
|
|
72
|
+
.venv/bin/python -m pytest # no network, no real token
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
MIT. See `SPEC.md` for the v1 contract and `CHANGELOG.md` for history.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# discord-tools v1 — spec
|
|
2
|
+
|
|
3
|
+
Graduated from incubator shaping 2026-08-27. Decision record:
|
|
4
|
+
`~/code/incubator/discord-tools/IDEA.md`. Sibling and convention source:
|
|
5
|
+
`~/code/telegram-tools` (v3.4.1).
|
|
6
|
+
|
|
7
|
+
## Problem Statement
|
|
8
|
+
|
|
9
|
+
Sven and his agents operate Discord the way they already operate Telegram:
|
|
10
|
+
finding channel/thread IDs, searching and exporting history, sending messages,
|
|
11
|
+
creating channels, clearing messages, managing bots. On Discord there is no
|
|
12
|
+
telegram-tools equivalent — every one of those tasks means clicking through the
|
|
13
|
+
Discord app or hand-rolling API calls, and the bot setup itself (Developer
|
|
14
|
+
Portal, intents, permission bits, invite URLs) is a maze with no BotFather to
|
|
15
|
+
talk to. Discord also bans self-bots, so the Telethon approach cannot be
|
|
16
|
+
ported directly.
|
|
17
|
+
|
|
18
|
+
## Solution
|
|
19
|
+
|
|
20
|
+
A local Python CLI, `discord-tools`, that is a deliberate sibling of
|
|
21
|
+
telegram-tools: bare invocation opens a human menu, agents pass subcommands,
|
|
22
|
+
`--json` where structure helps. It authenticates as a **bot** (ToS-safe, never
|
|
23
|
+
a user token), with a guided setup that walks through the Developer Portal and
|
|
24
|
+
verifies the result — the guided setup is the BotFather replacement. All seven
|
|
25
|
+
telegram-tools functions ship in v1, mapped to what a bot can legally do, with
|
|
26
|
+
the same safety gates on anything destructive. Named token profiles support a
|
|
27
|
+
bot per agent.
|
|
28
|
+
|
|
29
|
+
## User Stories
|
|
30
|
+
|
|
31
|
+
1. As a new user, I want a guided `auth` setup that walks me through creating a Discord application, enabling the message-content intent, and generating the invite URL, so that I get from zero to a working bot without reading Discord docs.
|
|
32
|
+
2. As a user, I want `doctor` to verify my token, intents, joined servers, and per-channel permissions, so that I know exactly what is misconfigured when something fails.
|
|
33
|
+
3. As an agent operator, I want named token profiles in `~/.discord-tools/` with `--profile <name>` and an env-var override, so that each of my agents runs as its own bot.
|
|
34
|
+
4. As an agent, I want `discover` to print the full server → channel → thread tree with IDs, so that I can address any destination without a human copying IDs from the app.
|
|
35
|
+
5. As an agent, I want `--json` output on read commands, so that I can parse results without scraping human-formatted text.
|
|
36
|
+
6. As a user, I want `search` to find messages by keyword/sender/date in a channel, so that I can locate a conversation without scrolling Discord.
|
|
37
|
+
7. As a user, I want `export` to write a channel or thread's history to a local file (JSON/CSV), so that I own a searchable archive outside Discord.
|
|
38
|
+
8. As a user, I want exports to land under `~/.discord-tools/exports/` and never inside a repo, so that chat data cannot leak into version control.
|
|
39
|
+
9. As an agent, I want `send` to post a message to a channel or thread as the bot, so that automations can report into Discord.
|
|
40
|
+
10. As a user, I want `send` to preview the full message and ask y/N before posting, so that nothing goes public by accident.
|
|
41
|
+
11. As an agent operator, I want `send --yes` to work only for destinations on an explicit allowlist (unset = refuse), so that unattended sends are constrained to channels I chose.
|
|
42
|
+
12. As a user, I want `create` to make channels, threads, and categories behind a confirmation, so that I can scaffold a server without the app and without accidental objects.
|
|
43
|
+
13. As a user, I want `clear-messages` to dry-run by default, listing what would be deleted, so that I can see the blast radius before anything happens.
|
|
44
|
+
14. As a user, I want `clear-messages` execution to require `--execute` plus typing `DELETE`, so that real deletion is never one keystroke away.
|
|
45
|
+
15. As a user, I want the dry-run to tell me which messages fall inside the 14-day bulk-delete window and which will delete one-by-one (slower), so that I know what to expect before confirming.
|
|
46
|
+
16. As a user, I want `bot` to show and edit the active profile's name, avatar, and description behind a diff + confirm, so that I manage bot identity without the portal.
|
|
47
|
+
17. As a user, I want `bot` to print the invite URL with the right permission bits, so that adding the bot to another server is copy-paste.
|
|
48
|
+
18. As a human, I want bare `discord-tools` to open the same menu style as telegram-tools, so that I never memorize subcommands.
|
|
49
|
+
19. As a telegram-tools user, I want the menus, flags, and gate behavior to feel identical, so that I carry my habits over with zero relearning.
|
|
50
|
+
20. As an agent, I want a bundled skill (`skill/SKILL.md`) describing the CLI surface, so that any Claude session can drive the tool correctly.
|
|
51
|
+
21. As a contributor, I want the test suite to run with no network and no real token, so that CI and local runs are safe and fast.
|
|
52
|
+
22. As a user, I want clear errors when the message-content intent is off (content comes back empty), so that the classic silent-empty-export trap is named instead of mysterious.
|
|
53
|
+
23. As a user, I want rate limits handled with pacing and retry, so that big exports and slow deletes finish instead of erroring.
|
|
54
|
+
24. As a security-conscious user, I want the token stored 0600 and never printed, committed, or logged, so that a leaked terminal scroll never leaks the bot.
|
|
55
|
+
|
|
56
|
+
## Implementation Decisions
|
|
57
|
+
|
|
58
|
+
- Python ≥3.11; dependencies mirror the sibling's minimalism: discord.py 2.x
|
|
59
|
+
and python-dotenv only. discord.py is used **login-only / REST** (no gateway
|
|
60
|
+
loop) for its rate-limit handling and typed models; if login-only REST
|
|
61
|
+
fights the one-shot CLI shape, the fallback decision is a thin httpx client
|
|
62
|
+
behind the same internal interface.
|
|
63
|
+
- One internal seam: a `DiscordClient` boundary wrapping every REST call the
|
|
64
|
+
CLI makes. The menu, subcommands, exporters, and gates all sit above it;
|
|
65
|
+
tests mock exactly this seam. This mirrors telegram-tools' client boundary.
|
|
66
|
+
- Auth is bot-token only. No user-token code path exists anywhere — not even
|
|
67
|
+
behind a flag (ToS; decided in shaping, non-negotiable).
|
|
68
|
+
- Config: `~/.discord-tools/` with named profiles; file mode 0600; env var
|
|
69
|
+
override for token and profile selection; python-dotenv for local dev.
|
|
70
|
+
- `search` is a local filter over fetched history — Discord exposes no search
|
|
71
|
+
API to bots. Search and export share the paginated history-fetch path.
|
|
72
|
+
- `clear-messages` uses the bulk endpoint for messages <14 days old (API hard
|
|
73
|
+
limit) and one-by-one deletion beyond it; dry-run computes both buckets from
|
|
74
|
+
snowflake timestamps without extra API calls.
|
|
75
|
+
- Destructive gates copy the sibling verbatim: dry-run default + `--execute` +
|
|
76
|
+
typed `DELETE` for clears; full preview + y/N for send; allowlist env
|
|
77
|
+
(`DISCORD_SEND_ALLOWLIST`) required for `--yes` sends; diff + confirm for
|
|
78
|
+
bot settings; confirm before create. The menu never bypasses a gate.
|
|
79
|
+
- CLI structure mirrors telegram-tools: bare invocation = menu; subcommands
|
|
80
|
+
for agents; `--json` on read commands; per-subcommand `--help`.
|
|
81
|
+
- Packaging: hatchling, MIT, console script `discord-tools`, PyPI under the
|
|
82
|
+
banozz account, same release recipe and CHANGELOG discipline as the sibling.
|
|
83
|
+
- `skill/SKILL.md` is independently versioned and updated in the same commit
|
|
84
|
+
as any CLI-surface change.
|
|
85
|
+
- No gateway connection, no event listening, no slash-command registration in
|
|
86
|
+
v1 — the bot is a REST actor driven by the CLI.
|
|
87
|
+
|
|
88
|
+
## Testing Decisions
|
|
89
|
+
|
|
90
|
+
- pytest, no network, no real token — the entire suite runs against a mocked
|
|
91
|
+
`DiscordClient` seam. Test external behavior (CLI output, files written,
|
|
92
|
+
refusals) — never internals.
|
|
93
|
+
- Prior art: telegram-tools' suite (339 no-network tests) is the model; CI
|
|
94
|
+
additionally runs `compileall` and a per-subcommand `--help` smoke.
|
|
95
|
+
- Gate tests are mandatory: clear without `--execute` deletes nothing; wrong
|
|
96
|
+
typed confirmation aborts; `send --yes` without allowlist refuses; every
|
|
97
|
+
destructive path has a refusal test before a success test.
|
|
98
|
+
- The 14-day split logic is pure (snowflake math) and gets direct unit tests.
|
|
99
|
+
- Export tests verify file shape (JSON/CSV) and that exports never land in
|
|
100
|
+
the working directory.
|
|
101
|
+
|
|
102
|
+
## Out of Scope
|
|
103
|
+
|
|
104
|
+
- The localhost web dashboard (parked as a later phase in shaping).
|
|
105
|
+
- Self-bot / user-token operation, in any form, ever.
|
|
106
|
+
- Real-time features: gateway events, message listening, slash commands,
|
|
107
|
+
voice, presence.
|
|
108
|
+
- DM reading or sending as the user (a bot cannot act as Sven).
|
|
109
|
+
- SQLite storage (revisit only if the dashboard phase needs it).
|
|
110
|
+
|
|
111
|
+
## Further Notes
|
|
112
|
+
|
|
113
|
+
- Verify during Phase 1 whether the message-content intent gates REST history
|
|
114
|
+
fetches the same way it gates gateway payloads, and make `doctor` test for
|
|
115
|
+
the empty-content symptom directly.
|
|
116
|
+
- v1 is done only after the joint testing session with Sven covering every
|
|
117
|
+
command (shaping decision), then the PyPI release.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Domain Docs
|
|
2
|
+
|
|
3
|
+
How the engineering skills should consume this repo's domain documentation when exploring the codebase.
|
|
4
|
+
|
|
5
|
+
## Before exploring, read these
|
|
6
|
+
|
|
7
|
+
- **`CONTEXT.md`** at the repo root, or
|
|
8
|
+
- **`CONTEXT-MAP.md`** at the repo root if it exists: it points at one `CONTEXT.md` per context. Read each one relevant to the topic.
|
|
9
|
+
- **`docs/adr/`**: read ADRs that touch the area you're about to work in. In multi-context repos, also check `src/<context>/docs/adr/` for context-scoped decisions.
|
|
10
|
+
|
|
11
|
+
If any of these files don't exist, **proceed silently**. Don't flag their absence; don't suggest creating them upfront. The `/domain-modeling` skill (reached via `/grill-with-docs` and `/improve-codebase-architecture`) creates them lazily when terms or decisions actually get resolved.
|
|
12
|
+
|
|
13
|
+
## File structure
|
|
14
|
+
|
|
15
|
+
Single-context repo (most repos):
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
/
|
|
19
|
+
├── CONTEXT.md
|
|
20
|
+
├── docs/adr/
|
|
21
|
+
│ ├── 0001-event-sourced-orders.md
|
|
22
|
+
│ └── 0002-postgres-for-write-model.md
|
|
23
|
+
└── src/
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Multi-context repo (presence of `CONTEXT-MAP.md` at the root):
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
/
|
|
30
|
+
├── CONTEXT-MAP.md
|
|
31
|
+
├── docs/adr/ ← system-wide decisions
|
|
32
|
+
└── src/
|
|
33
|
+
├── ordering/
|
|
34
|
+
│ ├── CONTEXT.md
|
|
35
|
+
│ └── docs/adr/ ← context-specific decisions
|
|
36
|
+
└── billing/
|
|
37
|
+
├── CONTEXT.md
|
|
38
|
+
└── docs/adr/
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Use the glossary's vocabulary
|
|
42
|
+
|
|
43
|
+
When your output names a domain concept (in an issue title, a refactor proposal, a hypothesis, a test name), use the term as defined in `CONTEXT.md`. Don't drift to synonyms the glossary explicitly avoids.
|
|
44
|
+
|
|
45
|
+
If the concept you need isn't in the glossary yet, that's a signal: either you're inventing language the project doesn't use (reconsider) or there's a real gap (note it for `/domain-modeling`).
|
|
46
|
+
|
|
47
|
+
## Flag ADR conflicts
|
|
48
|
+
|
|
49
|
+
If your output contradicts an existing ADR, surface it explicitly rather than silently overriding:
|
|
50
|
+
|
|
51
|
+
> _Contradicts ADR-0007 (event-sourced orders), but worth reopening because…_
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Issue Tracker
|
|
2
|
+
|
|
3
|
+
Issues for this repo live on the shared **Beads board** at
|
|
4
|
+
`/Users/Shared/agent-board` — the fleet-wide queue Claude Code, Codex, and
|
|
5
|
+
Hermes all read and write (via `bd` or the `beads` MCP tools).
|
|
6
|
+
|
|
7
|
+
- Skills that "create an issue" (`to-tickets`, `to-spec`, `triage`) create a
|
|
8
|
+
bead, written per canon `board-writing.md` (agent-config repo): 2–5 word
|
|
9
|
+
title, two-zone description (≤8 human lines then a `🤖 For the agent` brief),
|
|
10
|
+
no Markdown syntax, every card names an assignee (the worker, never the
|
|
11
|
+
writer).
|
|
12
|
+
- Blocking edges: `bd dep add`.
|
|
13
|
+
- Claim atomically before working a card; link your session with
|
|
14
|
+
`gx board associate <id>`.
|
|
15
|
+
- Repo-scoping: prefix titles or use the board's project field with
|
|
16
|
+
`discord-tools` so the repo's cards are findable.
|
|
17
|
+
|
|
18
|
+
PRs as a request surface: **off**. External PRs are not part of the triage
|
|
19
|
+
queue for this repo.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Triage Labels
|
|
2
|
+
|
|
3
|
+
The skills speak in terms of five canonical triage roles. This file maps those roles to the actual label strings used in this repo's issue tracker.
|
|
4
|
+
|
|
5
|
+
| Label in mattpocock/skills | Label in our tracker | Meaning |
|
|
6
|
+
| -------------------------- | -------------------- | ---------------------------------------- |
|
|
7
|
+
| `needs-triage` | `needs-triage` | Maintainer needs to evaluate this issue |
|
|
8
|
+
| `needs-info` | `needs-info` | Waiting on reporter for more information |
|
|
9
|
+
| `ready-for-agent` | `ready-for-agent` | Fully specified, ready for an AFK agent |
|
|
10
|
+
| `ready-for-human` | `ready-for-human` | Requires human implementation |
|
|
11
|
+
| `wontfix` | `wontfix` | Will not be actioned |
|
|
12
|
+
|
|
13
|
+
When a skill mentions a role (e.g. "apply the AFK-ready triage label"), use the corresponding label string from this table.
|
|
14
|
+
|
|
15
|
+
Edit the right-hand column to match whatever vocabulary you actually use.
|