From a97dbe3bb248f0f2c61f7a22803c839a782c6438 Mon Sep 17 00:00:00 2001 From: Jonathan Lam Date: Fri, 12 Jun 2026 15:17:20 +1000 Subject: [PATCH] feat: expose file id in message output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The files array in `message list`/`message get` only carried name, mimetype, permalink and size — not the id. Without the id there was no way to pull an attachment seen in a message through `file download` / `file info`, since both take a file id. Add it to CompactFile so the download path is actually reachable from a message. Also document the file info/download/upload commands in the usage guide, which weren't covered before. --- SLACK_CLI_USAGE.md | 30 +++++++++++++++++++++++++++--- internal/slack/messages.go | 1 + internal/types/types.go | 1 + 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/SLACK_CLI_USAGE.md b/SLACK_CLI_USAGE.md index 253a82f..602ae5f 100644 --- a/SLACK_CLI_USAGE.md +++ b/SLACK_CLI_USAGE.md @@ -41,7 +41,7 @@ Messages are returned in chronological order (oldest first). Each message includ - `latest_reply` — timestamp of the most recent reply (only on thread root messages) - `author.user_id` — who sent it - `content` — message text -- `files` — attached file metadata (if any) +- `files` — attached file metadata (if any), each with `id`, `name`, `mimetype`, `size`, and `permalink` (see §10 to download) ### 3. Read a thread @@ -125,6 +125,20 @@ slack-cli search messages 'deploy' --channel '#engineering' --user '@jules' --af slack-cli search files 'report' --limit 10 ``` +### 10. Inspect and download file attachments + +Each entry in a message's `files` array has an `id` (e.g. `F0B9ZKGUQ85`). Fetch its metadata: + +``` +slack-cli file info F0B9ZKGUQ85 +``` + +Download the bytes to a local path (omit `--output` to stream to stdout; metadata goes to stderr): + +``` +slack-cli file download F0B9ZKGUQ85 --output /tmp/attachment.png +``` + ## Sending and Modifying Messages ### Send a message to a channel @@ -192,6 +206,15 @@ slack-cli message react add '#channel-name' thumbsup --ts 1772065778.676219 slack-cli message react remove '#channel-name' thumbsup --ts 1772065778.676219 ``` +### Upload a file + +Upload a local file to a channel or thread: + +``` +slack-cli file upload '#channel-name' /tmp/result.png --message 'Here is the output' +slack-cli file upload '#channel-name' /tmp/result.png --thread-ts 1772065778.676219 +``` + ## Channel Management ### Create a channel @@ -214,8 +237,9 @@ slack-cli channel invite --channel '#channel-name' --users 'U01234,U56789' 3. For any message with `reply_count > 0` — `slack-cli message list '#channel' --thread-ts ` to read the thread 4. To poll for new activity since a known timestamp — `slack-cli message list '#channel' --oldest --include-threads` to catch both new messages and new thread replies 5. `slack-cli user get ` — resolve user IDs as needed -6. `slack-cli message send '#channel' 'response'` — reply to the channel -7. `slack-cli message send '#channel' 'response' --thread-ts ` — reply in a specific thread +6. For a message with a `files` attachment — `slack-cli file download --output ` to fetch it +7. `slack-cli message send '#channel' 'response'` — reply to the channel +8. `slack-cli message send '#channel' 'response' --thread-ts ` — reply in a specific thread ## Notes diff --git a/internal/slack/messages.go b/internal/slack/messages.go index efed294..44390ec 100644 --- a/internal/slack/messages.go +++ b/internal/slack/messages.go @@ -397,6 +397,7 @@ func parseRawMessage(channelID string, raw map[string]interface{}, maxBodyChars for _, f := range files { if fm, ok := f.(map[string]interface{}); ok { msg.Files = append(msg.Files, types.CompactFile{ + ID: api.GetStringFromMap(fm, "id"), Name: api.GetStringFromMap(fm, "name"), Title: api.GetStringFromMap(fm, "title"), Mimetype: api.GetStringFromMap(fm, "mimetype"), diff --git a/internal/types/types.go b/internal/types/types.go index 4648ffa..3817753 100644 --- a/internal/types/types.go +++ b/internal/types/types.go @@ -74,6 +74,7 @@ type MessageAuthor struct { // CompactFile is the token-efficient file metadata. type CompactFile struct { + ID string `json:"id,omitempty"` Mimetype string `json:"mimetype,omitempty"` Mode string `json:"mode,omitempty"` Name string `json:"name,omitempty"`