Skip to content

Commit fe663b6

Browse files
DevRohit06claude
andcommitted
feat: add file attachment support to all send commands
Add --file option (repeatable) to message send, message reply, dm send, and thread send. Uses discord.File under the hood. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3b061f8 commit fe663b6

6 files changed

Lines changed: 48 additions & 10 deletions

File tree

README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,10 +116,12 @@ Every command supports `--json` for machine-readable output.
116116
```bash
117117
discli message send #general "Hello world!"
118118
discli message send #general "Check this out" --embed-title "News" --embed-desc "Big update"
119+
discli message send #general "Here's the report" --file report.pdf
120+
discli message send #general "Screenshots" --file bug.png --file logs.txt
119121
discli message list #general --limit 20
120122
discli message list #general --after 2026-03-01 --before 2026-03-14
121123
discli message get #general 123456789
122-
discli message reply #general 123456789 "Thanks for your question!"
124+
discli message reply #general 123456789 "Here you go" --file fix.patch
123125
discli message edit #general 123456789 "Updated text"
124126
discli message delete #general 123456789
125127
```
@@ -140,6 +142,7 @@ discli message history #general --hours 24 --limit 500
140142

141143
```bash
142144
discli dm send alice "Hey, need help?"
145+
discli dm send alice "Check this file" --file notes.pdf
143146
discli dm send 123456789 "Sent by user ID"
144147
discli dm list alice --limit 10
145148
```
@@ -168,6 +171,7 @@ discli channel delete #old-channel
168171
discli thread create #general 123456789 "Support Ticket"
169172
discli thread list #general
170173
discli thread send 987654321 "Following up on your issue"
174+
discli thread send 987654321 "Attached the logs" --file debug.log
171175
```
172176

173177
### Servers

agents/discord-agent.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ You are a Discord agent with access to the `discli` CLI tool. Use the Bash tool
88
```bash
99
discli message send <channel> "text"
1010
discli message send <channel> "text" --embed-title "Title" --embed-desc "Description"
11+
discli message send <channel> "text" --file path/to/file.png
12+
discli message send <channel> "text" --file file1.png --file file2.pdf
1113
discli message reply <channel> <message_id> "text"
14+
discli message reply <channel> <message_id> "text" --file path/to/file.png
1215
discli message get <channel> <message_id>
1316
discli message list <channel> --limit 10 [--before YYYY-MM-DD] [--after YYYY-MM-DD]
1417
discli message edit <channel> <message_id> "new text"
@@ -28,6 +31,7 @@ discli reaction list <channel> <message_id>
2831
### Direct Messages
2932
```bash
3033
discli dm send <user> "text"
34+
discli dm send <user> "text" --file path/to/file.png
3135
discli dm list <user> --limit 10
3236
```
3337

@@ -44,6 +48,7 @@ discli channel info <channel>
4448
discli thread create <channel> <message_id> "thread name"
4549
discli thread list <channel>
4650
discli thread send <thread_id> "text"
51+
discli thread send <thread_id> "text" --file path/to/file.png
4752
```
4853

4954
### Servers

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "discord-cli-agent"
7-
version = "0.3.0"
7+
version = "0.4.0"
88
description = "Discord CLI for AI agents"
99
readme = "README.md"
1010
license = "MIT"

src/discli/commands/dm.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,29 @@ def dm_group():
2828
@dm_group.command("send")
2929
@click.argument("user")
3030
@click.argument("text")
31+
@click.option("--file", "files", multiple=True, type=click.Path(exists=True), help="File to attach (repeatable).")
3132
@click.pass_context
32-
def dm_send(ctx, user, text):
33+
def dm_send(ctx, user, text, files):
3334
"""Send a direct message to a user."""
35+
import discord
3436

3537
def action(client):
3638
async def _action(client):
3739
u = resolve_user(client, user)
3840
dm_channel = await u.create_dm()
39-
msg = await dm_channel.send(content=text)
41+
attachments = [discord.File(f) for f in files]
42+
kwargs = {"content": text}
43+
if attachments:
44+
kwargs["files"] = attachments
45+
msg = await dm_channel.send(**kwargs)
4046
data = {
4147
"id": str(msg.id),
4248
"to": str(u),
4349
"to_id": str(u.id),
4450
"content": msg.content,
4551
}
52+
if msg.attachments:
53+
data["attachments"] = [{"filename": a.filename, "url": a.url, "size": a.size} for a in msg.attachments]
4654
output(ctx, data, plain_text=f"Sent DM to {u} (ID: {u.id}): {text[:50]}")
4755
return _action(client)
4856

src/discli/commands/message.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ def message_group():
1515
@click.argument("text")
1616
@click.option("--embed-title", default=None, help="Embed title.")
1717
@click.option("--embed-desc", default=None, help="Embed description.")
18+
@click.option("--file", "files", multiple=True, type=click.Path(exists=True), help="File to attach (repeatable).")
1819
@click.pass_context
19-
def message_send(ctx, channel, text, embed_title, embed_desc):
20+
def message_send(ctx, channel, text, embed_title, embed_desc, files):
2021
"""Send a message to a channel."""
2122

2223
def action(client):
@@ -25,8 +26,14 @@ async def _action(client):
2526
embed = None
2627
if embed_title or embed_desc:
2728
embed = discord.Embed(title=embed_title, description=embed_desc)
28-
msg = await ch.send(content=text, embed=embed)
29+
attachments = [discord.File(f) for f in files]
30+
kwargs = {"content": text, "embed": embed}
31+
if attachments:
32+
kwargs["files"] = attachments
33+
msg = await ch.send(**kwargs)
2934
data = {"id": str(msg.id), "channel": ch.name, "content": msg.content}
35+
if msg.attachments:
36+
data["attachments"] = [{"filename": a.filename, "url": a.url, "size": a.size} for a in msg.attachments]
3037
output(ctx, data, plain_text=f"Sent message {msg.id} to #{ch.name}")
3138
return _action(client)
3239

@@ -207,16 +214,23 @@ async def _action(client):
207214
@click.argument("channel")
208215
@click.argument("message_id")
209216
@click.argument("text")
217+
@click.option("--file", "files", multiple=True, type=click.Path(exists=True), help="File to attach (repeatable).")
210218
@click.pass_context
211-
def message_reply(ctx, channel, message_id, text):
219+
def message_reply(ctx, channel, message_id, text, files):
212220
"""Reply to a specific message."""
213221

214222
def action(client):
215223
async def _action(client):
216224
ch = resolve_channel(client, channel)
217225
original = await ch.fetch_message(int(message_id))
218-
msg = await original.reply(content=text)
226+
attachments = [discord.File(f) for f in files]
227+
kwargs = {"content": text}
228+
if attachments:
229+
kwargs["files"] = attachments
230+
msg = await original.reply(**kwargs)
219231
data = {"id": str(msg.id), "channel": ch.name, "content": msg.content, "reply_to": message_id}
232+
if msg.attachments:
233+
data["attachments"] = [{"filename": a.filename, "url": a.url, "size": a.size} for a in msg.attachments]
220234
output(ctx, data, plain_text=f"Replied to {message_id} in #{ch.name}")
221235
return _action(client)
222236

src/discli/commands/thread.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,20 +84,27 @@ async def _action(client):
8484
@thread_group.command("send")
8585
@click.argument("thread")
8686
@click.argument("text")
87+
@click.option("--file", "files", multiple=True, type=click.Path(exists=True), help="File to attach (repeatable).")
8788
@click.pass_context
88-
def thread_send(ctx, thread, text):
89+
def thread_send(ctx, thread, text, files):
8990
"""Send a message to a thread."""
9091

9192
def action(client):
9293
async def _action(client):
9394
t = resolve_thread(client, thread)
94-
msg = await t.send(content=text)
95+
attachments = [discord.File(f) for f in files]
96+
kwargs = {"content": text}
97+
if attachments:
98+
kwargs["files"] = attachments
99+
msg = await t.send(**kwargs)
95100
data = {
96101
"id": str(msg.id),
97102
"thread": t.name,
98103
"thread_id": str(t.id),
99104
"content": msg.content,
100105
}
106+
if msg.attachments:
107+
data["attachments"] = [{"filename": a.filename, "url": a.url, "size": a.size} for a in msg.attachments]
101108
output(ctx, data, plain_text=f"Sent message {msg.id} to thread '{t.name}'")
102109
return _action(client)
103110

0 commit comments

Comments
 (0)