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.
Files changed (47) hide show
  1. discord_tools_cli-0.1.0/.github/workflows/tests.yml +53 -0
  2. discord_tools_cli-0.1.0/.gitignore +18 -0
  3. discord_tools_cli-0.1.0/AGENTS.md +65 -0
  4. discord_tools_cli-0.1.0/CHANGELOG.md +33 -0
  5. discord_tools_cli-0.1.0/CLAUDE.md +1 -0
  6. discord_tools_cli-0.1.0/CONTEXT.md +40 -0
  7. discord_tools_cli-0.1.0/LICENSE +21 -0
  8. discord_tools_cli-0.1.0/PKG-INFO +124 -0
  9. discord_tools_cli-0.1.0/README.md +75 -0
  10. discord_tools_cli-0.1.0/SPEC.md +117 -0
  11. discord_tools_cli-0.1.0/docs/agents/domain.md +51 -0
  12. discord_tools_cli-0.1.0/docs/agents/issue-tracker.md +19 -0
  13. discord_tools_cli-0.1.0/docs/agents/triage-labels.md +15 -0
  14. discord_tools_cli-0.1.0/pyproject.toml +52 -0
  15. discord_tools_cli-0.1.0/skill/SKILL.md +155 -0
  16. discord_tools_cli-0.1.0/src/discord_tools/__init__.py +1 -0
  17. discord_tools_cli-0.1.0/src/discord_tools/bot.py +96 -0
  18. discord_tools_cli-0.1.0/src/discord_tools/cli.py +362 -0
  19. discord_tools_cli-0.1.0/src/discord_tools/client.py +235 -0
  20. discord_tools_cli-0.1.0/src/discord_tools/config.py +160 -0
  21. discord_tools_cli-0.1.0/src/discord_tools/create.py +57 -0
  22. discord_tools_cli-0.1.0/src/discord_tools/delete.py +108 -0
  23. discord_tools_cli-0.1.0/src/discord_tools/discovery.py +82 -0
  24. discord_tools_cli-0.1.0/src/discord_tools/doctor.py +201 -0
  25. discord_tools_cli-0.1.0/src/discord_tools/exporters.py +46 -0
  26. discord_tools_cli-0.1.0/src/discord_tools/menu.py +592 -0
  27. discord_tools_cli-0.1.0/src/discord_tools/models.py +133 -0
  28. discord_tools_cli-0.1.0/src/discord_tools/portal.py +133 -0
  29. discord_tools_cli-0.1.0/src/discord_tools/prompts.py +170 -0
  30. discord_tools_cli-0.1.0/src/discord_tools/records.py +86 -0
  31. discord_tools_cli-0.1.0/src/discord_tools/search.py +62 -0
  32. discord_tools_cli-0.1.0/src/discord_tools/send.py +86 -0
  33. discord_tools_cli-0.1.0/tests/conftest.py +125 -0
  34. discord_tools_cli-0.1.0/tests/test_bot.py +62 -0
  35. discord_tools_cli-0.1.0/tests/test_cli.py +216 -0
  36. discord_tools_cli-0.1.0/tests/test_config.py +114 -0
  37. discord_tools_cli-0.1.0/tests/test_create.py +50 -0
  38. discord_tools_cli-0.1.0/tests/test_delete.py +107 -0
  39. discord_tools_cli-0.1.0/tests/test_discovery.py +59 -0
  40. discord_tools_cli-0.1.0/tests/test_doctor.py +138 -0
  41. discord_tools_cli-0.1.0/tests/test_exporters.py +43 -0
  42. discord_tools_cli-0.1.0/tests/test_menu.py +139 -0
  43. discord_tools_cli-0.1.0/tests/test_portal.py +165 -0
  44. discord_tools_cli-0.1.0/tests/test_prompts.py +214 -0
  45. discord_tools_cli-0.1.0/tests/test_records.py +80 -0
  46. discord_tools_cli-0.1.0/tests/test_search.py +69 -0
  47. 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.