Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions internal/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,19 @@ type Reaction struct {
Users []string `json:"users"`
}

// File represents a file attachment on a Slack message
type File struct {
ID string `json:"id"`
Name string `json:"name"`
Title string `json:"title,omitempty"`
Mimetype string `json:"mimetype,omitempty"`
Filetype string `json:"filetype,omitempty"`
Size int64 `json:"size,omitempty"`
URLPrivate string `json:"url_private,omitempty"`
URLPrivateDownload string `json:"url_private_download,omitempty"`
Permalink string `json:"permalink,omitempty"`
}

// Message represents a Slack message
type Message struct {
Type string `json:"type"`
Expand All @@ -239,6 +252,7 @@ type Message struct {
ThreadTS string `json:"thread_ts,omitempty"`
ReplyCount int `json:"reply_count,omitempty"`
Reactions []Reaction `json:"reactions,omitempty"`
Files []File `json:"files,omitempty"`
}

// Team represents workspace info
Expand Down
73 changes: 73 additions & 0 deletions internal/cmd/messages/messages_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,79 @@ func TestRunThread_JSONIncludesReactions(t *testing.T) {
assert.Empty(t, messages[1].Reactions)
}

func TestRunThread_JSONIncludesFiles(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/conversations.replies":
_ = json.NewEncoder(w).Encode(map[string]interface{}{
"ok": true,
"messages": []map[string]interface{}{
{
"ts": "1234567890.123456",
"user": "U001",
"text": "See the screenshot below",
"files": []map[string]interface{}{
{
"id": "F0123ABC",
"name": "screenshot.png",
"title": "Screenshot",
"mimetype": "image/png",
"filetype": "png",
"size": 54321,
"url_private": "https://files.slack.com/files-pri/T123-F0123ABC/screenshot.png",
"url_private_download": "https://files.slack.com/files-pri/T123-F0123ABC/download/screenshot.png",
"permalink": "https://example.slack.com/files/U001/F0123ABC/screenshot.png",
},
},
},
{
"ts": "1234567890.123457",
"user": "U002",
"text": "Plain reply without files",
},
},
})
case "/users.info":
mockUserInfoHandler(w, r)
}
}))
defer server.Close()

c := client.NewWithConfig(server.URL, "test-token", nil)
opts := &threadOptions{limit: 100}

// Capture JSON output
output.OutputFormat = output.FormatJSON
defer func() { output.OutputFormat = output.FormatText }()

var buf strings.Builder
output.Writer = &buf
defer func() { output.Writer = os.Stdout }()

err := runThread("C123", "1234567890.123456", opts, c)
require.NoError(t, err)

// Parse the JSON output
var messages []client.Message
err = json.Unmarshal([]byte(buf.String()), &messages)
require.NoError(t, err)

require.Len(t, messages, 2)

// First message should have files
require.Len(t, messages[0].Files, 1)
assert.Equal(t, "F0123ABC", messages[0].Files[0].ID)
assert.Equal(t, "screenshot.png", messages[0].Files[0].Name)
assert.Equal(t, "image/png", messages[0].Files[0].Mimetype)
assert.Equal(t, "png", messages[0].Files[0].Filetype)
assert.Equal(t, int64(54321), messages[0].Files[0].Size)
assert.Equal(t, "https://files.slack.com/files-pri/T123-F0123ABC/screenshot.png", messages[0].Files[0].URLPrivate)
assert.Equal(t, "https://example.slack.com/files/U001/F0123ABC/screenshot.png", messages[0].Files[0].Permalink)

// Second message should have no files
assert.Empty(t, messages[1].Files)
}

func TestRunHistory_JSONIncludesReactions(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
Expand Down