feat: Add support for grouped file uploads in Slack (#12454)

Fixes
https://linear.app/chatwoot/issue/CW-5646/add-support-for-grouped-file-uploads-in-slack

Previously, when sending multiple attachments to Slack, we uploaded them
one by one. For example, sending 5 images would result in 5 separate
Slack messages. This created clutter and a poor user experience, since
Slack displayed each file as an individual message.
This PR updates the implementation to group all attachments from a
message and send them as a single Slack message. As a result,
attachments now appear together in one grouped block, providing a much
cleaner and more intuitive experience for users.

**Before:** 
Each file uploaded as a separate Slack message.
<img width="400" height="800" alt="before"
src="https://github.com/user-attachments/assets/c8c7f666-549b-428f-bd19-c94e39ed2513"
/>

**After:** 
All files from a single message grouped and displayed together in one
Slack message (similar to how Slack natively handles grouped uploads).
<img width="400" height="800" alt="after"
src="https://github.com/user-attachments/assets/0b1f22d5-4d37-4b84-905a-15e742317e72"
/>

**Changes**

- Upgraded Slack file upload implementation to use the new multiple
attachments API available in slack-ruby-client `v2.7.0`.
- Updated attachment handling to upload all files from a message in a
single API call.
- Enabled proper attachment grouping in Slack, ensuring related files
are presented together.
This commit is contained in:
Muhsin Keloth
2025-09-24 11:31:06 +05:30
committed by GitHub
parent 68c070bcd9
commit 44fab70048
4 changed files with 52 additions and 47 deletions

View File

@@ -101,7 +101,7 @@ class Integrations::Slack::SendOnSlackService < Base::SendOnChannelService
def send_message
post_message if message_content.present?
upload_file if message.attachments.any?
upload_files if message.attachments.any?
rescue Slack::Web::Api::Errors::AccountInactive, Slack::Web::Api::Errors::MissingScope, Slack::Web::Api::Errors::InvalidAuth,
Slack::Web::Api::Errors::ChannelNotFound, Slack::Web::Api::Errors::NotInChannel => e
Rails.logger.error e
@@ -120,36 +120,35 @@ class Integrations::Slack::SendOnSlackService < Base::SendOnChannelService
)
end
def upload_file
message.attachments.each do |attachment|
next unless attachment.with_attached_file?
def upload_files
return unless message.attachments.any?
begin
result = slack_client.files_upload_v2(
filename: attachment.file.filename.to_s,
content: attachment.file.download,
initial_comment: 'Attached File!',
thread_ts: conversation.identifier,
channel_id: hook.reference_id
)
Rails.logger.info "slack_upload_result: #{result}"
rescue Slack::Web::Api::Errors::SlackError => e
Rails.logger.error "Failed to upload file #{attachment.file.filename}: #{e.message}"
end
files = build_files_array
return if files.empty?
begin
result = slack_client.files_upload_v2(
files: files,
initial_comment: 'Attached File!',
thread_ts: conversation.identifier,
channel_id: hook.reference_id
)
Rails.logger.info "slack_upload_result: #{result}"
rescue Slack::Web::Api::Errors::SlackError => e
Rails.logger.error "Failed to upload files: #{e.message}"
end
end
def file_type
File.extname(message.attachments.first.download_url).strip.downcase[1..]
end
def build_files_array
message.attachments.filter_map do |attachment|
next unless attachment.with_attached_file?
def file_information
{
filename: message.attachments.first.file.filename,
filetype: file_type,
content: message.attachments.first.file.download,
title: message.attachments.first.file.filename
}
{
filename: attachment.file.filename.to_s,
content: attachment.file.download,
title: attachment.file.filename.to_s
}
end
end
def sender_name(sender)