discord-cli-agent 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_cli_agent-0.1.0/LICENSE +21 -0
- discord_cli_agent-0.1.0/PKG-INFO +230 -0
- discord_cli_agent-0.1.0/README.md +213 -0
- discord_cli_agent-0.1.0/pyproject.toml +31 -0
- discord_cli_agent-0.1.0/setup.cfg +4 -0
- discord_cli_agent-0.1.0/src/discli/__init__.py +1 -0
- discord_cli_agent-0.1.0/src/discli/cli.py +41 -0
- discord_cli_agent-0.1.0/src/discli/client.py +51 -0
- discord_cli_agent-0.1.0/src/discli/commands/__init__.py +0 -0
- discord_cli_agent-0.1.0/src/discli/commands/channel.py +104 -0
- discord_cli_agent-0.1.0/src/discli/commands/config_cmd.py +36 -0
- discord_cli_agent-0.1.0/src/discli/commands/dm.py +79 -0
- discord_cli_agent-0.1.0/src/discli/commands/listen.py +191 -0
- discord_cli_agent-0.1.0/src/discli/commands/member.py +148 -0
- discord_cli_agent-0.1.0/src/discli/commands/message.py +154 -0
- discord_cli_agent-0.1.0/src/discli/commands/reaction.py +68 -0
- discord_cli_agent-0.1.0/src/discli/commands/role.py +139 -0
- discord_cli_agent-0.1.0/src/discli/commands/server.py +55 -0
- discord_cli_agent-0.1.0/src/discli/commands/thread.py +104 -0
- discord_cli_agent-0.1.0/src/discli/commands/typing_cmd.py +24 -0
- discord_cli_agent-0.1.0/src/discli/config.py +17 -0
- discord_cli_agent-0.1.0/src/discli/utils.py +53 -0
- discord_cli_agent-0.1.0/src/discord_cli_agent.egg-info/PKG-INFO +230 -0
- discord_cli_agent-0.1.0/src/discord_cli_agent.egg-info/SOURCES.txt +29 -0
- discord_cli_agent-0.1.0/src/discord_cli_agent.egg-info/dependency_links.txt +1 -0
- discord_cli_agent-0.1.0/src/discord_cli_agent.egg-info/entry_points.txt +2 -0
- discord_cli_agent-0.1.0/src/discord_cli_agent.egg-info/requires.txt +6 -0
- discord_cli_agent-0.1.0/src/discord_cli_agent.egg-info/top_level.txt +1 -0
- discord_cli_agent-0.1.0/tests/test_client.py +17 -0
- discord_cli_agent-0.1.0/tests/test_config.py +17 -0
- discord_cli_agent-0.1.0/tests/test_utils.py +20 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 DevRohit06
|
|
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,230 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: discord-cli-agent
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Discord CLI for AI agents
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Classifier: Programming Language :: Python :: 3
|
|
7
|
+
Classifier: Operating System :: OS Independent
|
|
8
|
+
Requires-Python: >=3.10
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: discord.py>=2.3
|
|
12
|
+
Requires-Dist: click>=8.1
|
|
13
|
+
Provides-Extra: dev
|
|
14
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
15
|
+
Requires-Dist: pytest-asyncio>=0.21; extra == "dev"
|
|
16
|
+
Dynamic: license-file
|
|
17
|
+
|
|
18
|
+
# discli
|
|
19
|
+
|
|
20
|
+
A command-line interface for Discord, built for AI agents and humans. Manage servers, send messages, react, handle DMs, threads, and monitor events — all from the terminal.
|
|
21
|
+
|
|
22
|
+
## Install
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pip install discord-cli-agent
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
For development:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install -e .
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Requires Python 3.10+.
|
|
35
|
+
|
|
36
|
+
## Setup
|
|
37
|
+
|
|
38
|
+
1. Create a bot at [Discord Developer Portal](https://discord.com/developers/applications)
|
|
39
|
+
2. Enable **all privileged intents** (Presence, Server Members, Message Content)
|
|
40
|
+
3. Add the bot to your server with appropriate permissions
|
|
41
|
+
4. Configure your token:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Option A: Save to config
|
|
45
|
+
discli config set token YOUR_BOT_TOKEN
|
|
46
|
+
|
|
47
|
+
# Option B: Environment variable
|
|
48
|
+
export DISCORD_BOT_TOKEN=YOUR_BOT_TOKEN
|
|
49
|
+
|
|
50
|
+
# Option C: Pass directly
|
|
51
|
+
discli --token YOUR_BOT_TOKEN server list
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Usage
|
|
55
|
+
|
|
56
|
+
Every command supports `--json` for machine-readable output.
|
|
57
|
+
|
|
58
|
+
### Messages
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
discli message send #general "Hello world!"
|
|
62
|
+
discli message send #general "Check this out" --embed-title "News" --embed-desc "Big update"
|
|
63
|
+
discli message list #general --limit 20
|
|
64
|
+
discli message get #general 123456789
|
|
65
|
+
discli message reply #general 123456789 "Thanks for your question!"
|
|
66
|
+
discli message edit #general 123456789 "Updated text"
|
|
67
|
+
discli message delete #general 123456789
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Direct Messages
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
discli dm send alice "Hey, need help?"
|
|
74
|
+
discli dm send 123456789 "Sent by user ID"
|
|
75
|
+
discli dm list alice --limit 10
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Reactions
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
discli reaction add #general 123456789 👍
|
|
82
|
+
discli reaction remove #general 123456789 👍
|
|
83
|
+
discli reaction list #general 123456789
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Channels
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
discli channel list --server "My Server"
|
|
90
|
+
discli channel create "My Server" new-channel --type text
|
|
91
|
+
discli channel create "My Server" voice-room --type voice
|
|
92
|
+
discli channel info #general
|
|
93
|
+
discli channel delete #old-channel
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Threads
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
discli thread create #general 123456789 "Support Ticket"
|
|
100
|
+
discli thread list #general
|
|
101
|
+
discli thread send 987654321 "Following up on your issue"
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Servers
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
discli server list
|
|
108
|
+
discli server info "My Server"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Roles
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
discli role list "My Server"
|
|
115
|
+
discli role create "My Server" Moderator --color ff0000
|
|
116
|
+
discli role assign "My Server" alice Moderator
|
|
117
|
+
discli role remove "My Server" alice Moderator
|
|
118
|
+
discli role delete "My Server" Moderator
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Members
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
discli member list "My Server" --limit 100
|
|
125
|
+
discli member info "My Server" alice
|
|
126
|
+
discli member kick "My Server" alice --reason "Spam"
|
|
127
|
+
discli member ban "My Server" alice --reason "Repeated violations"
|
|
128
|
+
discli member unban "My Server" alice
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Typing Indicator
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
discli typing #general # 5 seconds (default)
|
|
135
|
+
discli typing #general --duration 10 # 10 seconds
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Live Event Monitoring
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
# Listen to everything
|
|
142
|
+
discli listen
|
|
143
|
+
|
|
144
|
+
# Filter by server/channel
|
|
145
|
+
discli listen --server "My Server" --channel #general
|
|
146
|
+
|
|
147
|
+
# Filter by event type
|
|
148
|
+
discli listen --events messages,reactions
|
|
149
|
+
|
|
150
|
+
# Include bot messages (ignored by default)
|
|
151
|
+
discli listen --include-bots
|
|
152
|
+
|
|
153
|
+
# JSON output for piping to an agent
|
|
154
|
+
discli listen --json --events messages
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Supported event types: `messages`, `reactions`, `members`, `edits`, `deletes`
|
|
158
|
+
|
|
159
|
+
## Resolving Identifiers
|
|
160
|
+
|
|
161
|
+
All commands accept both **IDs** and **names**:
|
|
162
|
+
|
|
163
|
+
| Type | By ID | By Name |
|
|
164
|
+
|------|-------|---------|
|
|
165
|
+
| Channel | `123456789` | `#general` |
|
|
166
|
+
| Server | `123456789` | `My Server` |
|
|
167
|
+
| Member | `123456789` | `alice` |
|
|
168
|
+
| Role | `123456789` | `Moderator` |
|
|
169
|
+
| Thread | `123456789` | `Support Ticket` |
|
|
170
|
+
| User (DM) | `123456789` | `alice` |
|
|
171
|
+
|
|
172
|
+
## JSON Output
|
|
173
|
+
|
|
174
|
+
Add `--json` to any command for machine-readable output:
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
$ discli message list #general --limit 1 --json
|
|
178
|
+
[
|
|
179
|
+
{
|
|
180
|
+
"id": "123456789",
|
|
181
|
+
"author": "alice",
|
|
182
|
+
"content": "Hello!",
|
|
183
|
+
"timestamp": "2026-03-14T10:32:00+00:00"
|
|
184
|
+
}
|
|
185
|
+
]
|
|
186
|
+
|
|
187
|
+
$ discli listen --json
|
|
188
|
+
{"event": "message", "server": "My Server", "channel": "general", "channel_id": "111", "author": "alice", "author_id": "222", "content": "hello", "message_id": "333", "mentions_bot": false, "attachments": [], ...}
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Building an AI Agent
|
|
192
|
+
|
|
193
|
+
A basic agent loop using `discli`:
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
# 1. Listen for messages mentioning the bot
|
|
197
|
+
discli listen --json --events messages | while read -r event; do
|
|
198
|
+
mentions_bot=$(echo "$event" | jq -r '.mentions_bot')
|
|
199
|
+
if [ "$mentions_bot" = "true" ]; then
|
|
200
|
+
channel_id=$(echo "$event" | jq -r '.channel_id')
|
|
201
|
+
message_id=$(echo "$event" | jq -r '.message_id')
|
|
202
|
+
content=$(echo "$event" | jq -r '.content')
|
|
203
|
+
|
|
204
|
+
# 2. Show typing while thinking
|
|
205
|
+
discli typing "$channel_id" --duration 3 &
|
|
206
|
+
|
|
207
|
+
# 3. Generate response (your AI here)
|
|
208
|
+
response="Got your message: $content"
|
|
209
|
+
|
|
210
|
+
# 4. Reply to the message
|
|
211
|
+
discli message reply "$channel_id" "$message_id" "$response"
|
|
212
|
+
fi
|
|
213
|
+
done
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Configuration
|
|
217
|
+
|
|
218
|
+
Config is stored at `~/.discli/config.json`.
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
discli config set token YOUR_TOKEN
|
|
222
|
+
discli config show
|
|
223
|
+
discli config show --json
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Token resolution order: `--token` flag > `DISCORD_BOT_TOKEN` env var > config file.
|
|
227
|
+
|
|
228
|
+
## License
|
|
229
|
+
|
|
230
|
+
MIT
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# discli
|
|
2
|
+
|
|
3
|
+
A command-line interface for Discord, built for AI agents and humans. Manage servers, send messages, react, handle DMs, threads, and monitor events — all from the terminal.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install discord-cli-agent
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
For development:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pip install -e .
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Requires Python 3.10+.
|
|
18
|
+
|
|
19
|
+
## Setup
|
|
20
|
+
|
|
21
|
+
1. Create a bot at [Discord Developer Portal](https://discord.com/developers/applications)
|
|
22
|
+
2. Enable **all privileged intents** (Presence, Server Members, Message Content)
|
|
23
|
+
3. Add the bot to your server with appropriate permissions
|
|
24
|
+
4. Configure your token:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Option A: Save to config
|
|
28
|
+
discli config set token YOUR_BOT_TOKEN
|
|
29
|
+
|
|
30
|
+
# Option B: Environment variable
|
|
31
|
+
export DISCORD_BOT_TOKEN=YOUR_BOT_TOKEN
|
|
32
|
+
|
|
33
|
+
# Option C: Pass directly
|
|
34
|
+
discli --token YOUR_BOT_TOKEN server list
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
Every command supports `--json` for machine-readable output.
|
|
40
|
+
|
|
41
|
+
### Messages
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
discli message send #general "Hello world!"
|
|
45
|
+
discli message send #general "Check this out" --embed-title "News" --embed-desc "Big update"
|
|
46
|
+
discli message list #general --limit 20
|
|
47
|
+
discli message get #general 123456789
|
|
48
|
+
discli message reply #general 123456789 "Thanks for your question!"
|
|
49
|
+
discli message edit #general 123456789 "Updated text"
|
|
50
|
+
discli message delete #general 123456789
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Direct Messages
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
discli dm send alice "Hey, need help?"
|
|
57
|
+
discli dm send 123456789 "Sent by user ID"
|
|
58
|
+
discli dm list alice --limit 10
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Reactions
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
discli reaction add #general 123456789 👍
|
|
65
|
+
discli reaction remove #general 123456789 👍
|
|
66
|
+
discli reaction list #general 123456789
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Channels
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
discli channel list --server "My Server"
|
|
73
|
+
discli channel create "My Server" new-channel --type text
|
|
74
|
+
discli channel create "My Server" voice-room --type voice
|
|
75
|
+
discli channel info #general
|
|
76
|
+
discli channel delete #old-channel
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Threads
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
discli thread create #general 123456789 "Support Ticket"
|
|
83
|
+
discli thread list #general
|
|
84
|
+
discli thread send 987654321 "Following up on your issue"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Servers
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
discli server list
|
|
91
|
+
discli server info "My Server"
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Roles
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
discli role list "My Server"
|
|
98
|
+
discli role create "My Server" Moderator --color ff0000
|
|
99
|
+
discli role assign "My Server" alice Moderator
|
|
100
|
+
discli role remove "My Server" alice Moderator
|
|
101
|
+
discli role delete "My Server" Moderator
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Members
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
discli member list "My Server" --limit 100
|
|
108
|
+
discli member info "My Server" alice
|
|
109
|
+
discli member kick "My Server" alice --reason "Spam"
|
|
110
|
+
discli member ban "My Server" alice --reason "Repeated violations"
|
|
111
|
+
discli member unban "My Server" alice
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Typing Indicator
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
discli typing #general # 5 seconds (default)
|
|
118
|
+
discli typing #general --duration 10 # 10 seconds
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Live Event Monitoring
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# Listen to everything
|
|
125
|
+
discli listen
|
|
126
|
+
|
|
127
|
+
# Filter by server/channel
|
|
128
|
+
discli listen --server "My Server" --channel #general
|
|
129
|
+
|
|
130
|
+
# Filter by event type
|
|
131
|
+
discli listen --events messages,reactions
|
|
132
|
+
|
|
133
|
+
# Include bot messages (ignored by default)
|
|
134
|
+
discli listen --include-bots
|
|
135
|
+
|
|
136
|
+
# JSON output for piping to an agent
|
|
137
|
+
discli listen --json --events messages
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Supported event types: `messages`, `reactions`, `members`, `edits`, `deletes`
|
|
141
|
+
|
|
142
|
+
## Resolving Identifiers
|
|
143
|
+
|
|
144
|
+
All commands accept both **IDs** and **names**:
|
|
145
|
+
|
|
146
|
+
| Type | By ID | By Name |
|
|
147
|
+
|------|-------|---------|
|
|
148
|
+
| Channel | `123456789` | `#general` |
|
|
149
|
+
| Server | `123456789` | `My Server` |
|
|
150
|
+
| Member | `123456789` | `alice` |
|
|
151
|
+
| Role | `123456789` | `Moderator` |
|
|
152
|
+
| Thread | `123456789` | `Support Ticket` |
|
|
153
|
+
| User (DM) | `123456789` | `alice` |
|
|
154
|
+
|
|
155
|
+
## JSON Output
|
|
156
|
+
|
|
157
|
+
Add `--json` to any command for machine-readable output:
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
$ discli message list #general --limit 1 --json
|
|
161
|
+
[
|
|
162
|
+
{
|
|
163
|
+
"id": "123456789",
|
|
164
|
+
"author": "alice",
|
|
165
|
+
"content": "Hello!",
|
|
166
|
+
"timestamp": "2026-03-14T10:32:00+00:00"
|
|
167
|
+
}
|
|
168
|
+
]
|
|
169
|
+
|
|
170
|
+
$ discli listen --json
|
|
171
|
+
{"event": "message", "server": "My Server", "channel": "general", "channel_id": "111", "author": "alice", "author_id": "222", "content": "hello", "message_id": "333", "mentions_bot": false, "attachments": [], ...}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## Building an AI Agent
|
|
175
|
+
|
|
176
|
+
A basic agent loop using `discli`:
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# 1. Listen for messages mentioning the bot
|
|
180
|
+
discli listen --json --events messages | while read -r event; do
|
|
181
|
+
mentions_bot=$(echo "$event" | jq -r '.mentions_bot')
|
|
182
|
+
if [ "$mentions_bot" = "true" ]; then
|
|
183
|
+
channel_id=$(echo "$event" | jq -r '.channel_id')
|
|
184
|
+
message_id=$(echo "$event" | jq -r '.message_id')
|
|
185
|
+
content=$(echo "$event" | jq -r '.content')
|
|
186
|
+
|
|
187
|
+
# 2. Show typing while thinking
|
|
188
|
+
discli typing "$channel_id" --duration 3 &
|
|
189
|
+
|
|
190
|
+
# 3. Generate response (your AI here)
|
|
191
|
+
response="Got your message: $content"
|
|
192
|
+
|
|
193
|
+
# 4. Reply to the message
|
|
194
|
+
discli message reply "$channel_id" "$message_id" "$response"
|
|
195
|
+
fi
|
|
196
|
+
done
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Configuration
|
|
200
|
+
|
|
201
|
+
Config is stored at `~/.discli/config.json`.
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
discli config set token YOUR_TOKEN
|
|
205
|
+
discli config show
|
|
206
|
+
discli config show --json
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Token resolution order: `--token` flag > `DISCORD_BOT_TOKEN` env var > config file.
|
|
210
|
+
|
|
211
|
+
## License
|
|
212
|
+
|
|
213
|
+
MIT
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "discord-cli-agent"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Discord CLI for AI agents"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Programming Language :: Python :: 3",
|
|
14
|
+
"Operating System :: OS Independent",
|
|
15
|
+
]
|
|
16
|
+
dependencies = [
|
|
17
|
+
"discord.py>=2.3",
|
|
18
|
+
"click>=8.1",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[project.scripts]
|
|
22
|
+
discli = "discli.cli:main"
|
|
23
|
+
|
|
24
|
+
[tool.setuptools.packages.find]
|
|
25
|
+
where = ["src"]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
dev = [
|
|
29
|
+
"pytest>=7.0",
|
|
30
|
+
"pytest-asyncio>=0.21",
|
|
31
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""discli — Discord CLI for AI agents."""
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import click
|
|
2
|
+
|
|
3
|
+
from discli.config import load_config
|
|
4
|
+
from discli.commands.channel import channel_group
|
|
5
|
+
from discli.commands.config_cmd import config_group
|
|
6
|
+
from discli.commands.dm import dm_group
|
|
7
|
+
from discli.commands.listen import listen_cmd
|
|
8
|
+
from discli.commands.member import member_group
|
|
9
|
+
from discli.commands.message import message_group
|
|
10
|
+
from discli.commands.reaction import reaction_group
|
|
11
|
+
from discli.commands.role import role_group
|
|
12
|
+
from discli.commands.server import server_group
|
|
13
|
+
from discli.commands.thread import thread_group
|
|
14
|
+
from discli.commands.typing_cmd import typing_cmd
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@click.group()
|
|
18
|
+
@click.option("--token", envvar="DISCORD_BOT_TOKEN", default=None, help="Discord bot token.")
|
|
19
|
+
@click.option("--json", "use_json", is_flag=True, default=False, help="Output as JSON.")
|
|
20
|
+
@click.pass_context
|
|
21
|
+
def main(ctx, token, use_json):
|
|
22
|
+
"""discli — Discord CLI for AI agents."""
|
|
23
|
+
ctx.ensure_object(dict)
|
|
24
|
+
if token is None:
|
|
25
|
+
config = load_config()
|
|
26
|
+
token = config.get("token")
|
|
27
|
+
ctx.obj["token"] = token
|
|
28
|
+
ctx.obj["use_json"] = use_json
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
main.add_command(channel_group)
|
|
32
|
+
main.add_command(config_group)
|
|
33
|
+
main.add_command(dm_group)
|
|
34
|
+
main.add_command(listen_cmd)
|
|
35
|
+
main.add_command(member_group)
|
|
36
|
+
main.add_command(message_group)
|
|
37
|
+
main.add_command(reaction_group)
|
|
38
|
+
main.add_command(role_group)
|
|
39
|
+
main.add_command(server_group)
|
|
40
|
+
main.add_command(thread_group)
|
|
41
|
+
main.add_command(typing_cmd)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from typing import Any, Callable, Coroutine
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
import discord
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def resolve_token(token: str | None, config: dict) -> str:
|
|
9
|
+
if token:
|
|
10
|
+
return token
|
|
11
|
+
config_token = config.get("token")
|
|
12
|
+
if config_token:
|
|
13
|
+
return config_token
|
|
14
|
+
raise click.ClickException(
|
|
15
|
+
"No token provided. Use --token, set DISCORD_BOT_TOKEN, or run: discli config set token YOUR_TOKEN"
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def run_action(token: str, action: Callable[[discord.Client], Coroutine[Any, Any, Any]]) -> Any:
|
|
20
|
+
"""Connect a bot client, run an action on_ready, return the result, disconnect."""
|
|
21
|
+
intents = discord.Intents.all()
|
|
22
|
+
client = discord.Client(intents=intents)
|
|
23
|
+
result = None
|
|
24
|
+
error = None
|
|
25
|
+
|
|
26
|
+
@client.event
|
|
27
|
+
async def on_ready():
|
|
28
|
+
nonlocal result, error
|
|
29
|
+
try:
|
|
30
|
+
result = await action(client)
|
|
31
|
+
except Exception as e:
|
|
32
|
+
error = e
|
|
33
|
+
finally:
|
|
34
|
+
await client.close()
|
|
35
|
+
|
|
36
|
+
await client.start(token)
|
|
37
|
+
|
|
38
|
+
if error:
|
|
39
|
+
raise error
|
|
40
|
+
return result
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def run_discord(ctx, action: Callable[[discord.Client], Coroutine[Any, Any, Any]]) -> Any:
|
|
44
|
+
"""Synchronous entry point: resolve token, run action, return result."""
|
|
45
|
+
token = resolve_token(ctx.obj.get("token"), {})
|
|
46
|
+
try:
|
|
47
|
+
return asyncio.run(run_action(token, action))
|
|
48
|
+
except discord.LoginFailure:
|
|
49
|
+
raise click.ClickException("Invalid bot token.")
|
|
50
|
+
except discord.HTTPException as e:
|
|
51
|
+
raise click.ClickException(f"Discord API error: {e}")
|
|
File without changes
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import discord
|
|
3
|
+
|
|
4
|
+
from discli.client import run_discord
|
|
5
|
+
from discli.utils import output, resolve_channel, resolve_guild
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@click.group("channel")
|
|
9
|
+
def channel_group():
|
|
10
|
+
"""List, create, delete, and inspect channels."""
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@channel_group.command("list")
|
|
14
|
+
@click.option("--server", default=None, help="Server name or ID.")
|
|
15
|
+
@click.pass_context
|
|
16
|
+
def channel_list(ctx, server):
|
|
17
|
+
"""List channels in a server."""
|
|
18
|
+
|
|
19
|
+
def action(client):
|
|
20
|
+
async def _action(client):
|
|
21
|
+
if server:
|
|
22
|
+
guilds = [resolve_guild(client, server)]
|
|
23
|
+
else:
|
|
24
|
+
guilds = client.guilds
|
|
25
|
+
channels = []
|
|
26
|
+
for g in guilds:
|
|
27
|
+
for ch in g.channels:
|
|
28
|
+
if isinstance(ch, (discord.TextChannel, discord.VoiceChannel)):
|
|
29
|
+
channels.append({
|
|
30
|
+
"id": str(ch.id),
|
|
31
|
+
"name": ch.name,
|
|
32
|
+
"type": str(ch.type),
|
|
33
|
+
"server": g.name,
|
|
34
|
+
})
|
|
35
|
+
plain_lines = [f"#{c['name']} ({c['type']}) — {c['server']} (ID: {c['id']})" for c in channels]
|
|
36
|
+
output(ctx, channels, plain_text="\n".join(plain_lines) if plain_lines else "No channels found.")
|
|
37
|
+
return _action(client)
|
|
38
|
+
|
|
39
|
+
run_discord(ctx, action)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@channel_group.command("create")
|
|
43
|
+
@click.argument("server")
|
|
44
|
+
@click.argument("name")
|
|
45
|
+
@click.option("--type", "channel_type", type=click.Choice(["text", "voice", "category"]), default="text")
|
|
46
|
+
@click.pass_context
|
|
47
|
+
def channel_create(ctx, server, name, channel_type):
|
|
48
|
+
"""Create a channel in a server."""
|
|
49
|
+
|
|
50
|
+
def action(client):
|
|
51
|
+
async def _action(client):
|
|
52
|
+
guild = resolve_guild(client, server)
|
|
53
|
+
if channel_type == "text":
|
|
54
|
+
ch = await guild.create_text_channel(name)
|
|
55
|
+
elif channel_type == "voice":
|
|
56
|
+
ch = await guild.create_voice_channel(name)
|
|
57
|
+
else:
|
|
58
|
+
ch = await guild.create_category(name)
|
|
59
|
+
data = {"id": str(ch.id), "name": ch.name, "type": str(ch.type)}
|
|
60
|
+
output(ctx, data, plain_text=f"Created #{ch.name} (ID: {ch.id})")
|
|
61
|
+
return _action(client)
|
|
62
|
+
|
|
63
|
+
run_discord(ctx, action)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@channel_group.command("delete")
|
|
67
|
+
@click.argument("channel")
|
|
68
|
+
@click.pass_context
|
|
69
|
+
def channel_delete(ctx, channel):
|
|
70
|
+
"""Delete a channel."""
|
|
71
|
+
|
|
72
|
+
def action(client):
|
|
73
|
+
async def _action(client):
|
|
74
|
+
ch = resolve_channel(client, channel)
|
|
75
|
+
name = ch.name
|
|
76
|
+
await ch.delete()
|
|
77
|
+
output(ctx, {"id": str(ch.id), "deleted": True}, plain_text=f"Deleted #{name}")
|
|
78
|
+
return _action(client)
|
|
79
|
+
|
|
80
|
+
run_discord(ctx, action)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@channel_group.command("info")
|
|
84
|
+
@click.argument("channel")
|
|
85
|
+
@click.pass_context
|
|
86
|
+
def channel_info(ctx, channel):
|
|
87
|
+
"""Show channel details."""
|
|
88
|
+
|
|
89
|
+
def action(client):
|
|
90
|
+
async def _action(client):
|
|
91
|
+
ch = resolve_channel(client, channel)
|
|
92
|
+
data = {
|
|
93
|
+
"id": str(ch.id),
|
|
94
|
+
"name": ch.name,
|
|
95
|
+
"type": str(ch.type),
|
|
96
|
+
"server": ch.guild.name,
|
|
97
|
+
"topic": getattr(ch, "topic", None),
|
|
98
|
+
"created_at": ch.created_at.isoformat(),
|
|
99
|
+
}
|
|
100
|
+
plain_lines = [f"{k}: {v}" for k, v in data.items() if v is not None]
|
|
101
|
+
output(ctx, data, plain_text="\n".join(plain_lines))
|
|
102
|
+
return _action(client)
|
|
103
|
+
|
|
104
|
+
run_discord(ctx, action)
|