iachat/app/mailers/conversation_reply_mailer_helper.rb
Sojan Jose 3da26ee025
feat: Move email attachments from links to file attachments (#11304)
Add ability to send files as attachments instead of links
Fixes: https://github.com/chatwoot/chatwoot/issues/1074


## Changes 
- `emaily_reply` : We will attach the small attachments as attachments
and large ones as links
- `reply_with_summary`, `conversation_transcript`,
`reply_with_out_summary` : We will change the attachment format to the
following instead of the previous `View the attachment here`

```
Attachments:
file_name
file_name2
```


---------

ref: https://github.com/chatwoot/chatwoot/pull/10318/files -> for fixing
: https://github.com/chatwoot/chatwoot/pull/9655#issuecomment-2183962550

---------

Co-authored-by: Marco Marinho <marcomarinho12@gmail.com>
Co-authored-by: Pranav <pranavrajs@gmail.com>
2025-04-15 23:43:12 -07:00

139 lines
4.2 KiB
Ruby

module ConversationReplyMailerHelper
def prepare_mail(cc_bcc_enabled)
@options = {
to: to_emails,
from: email_from,
reply_to: email_reply_to,
subject: mail_subject,
message_id: custom_message_id,
in_reply_to: in_reply_to_email
}
if cc_bcc_enabled
@options[:cc] = cc_bcc_emails[0]
@options[:bcc] = cc_bcc_emails[1]
end
ms_smtp_settings
google_smtp_settings
set_delivery_method
# Email type detection logic:
# - email_reply: Sets @message with a single message
# - Other actions: Set @messages with a collection of messages
#
# So this check implicitly determines we're handling an email_reply
# and not one of the other email types (summary, transcript, etc.)
process_attachments_as_files_for_email_reply if @message&.attachments.present?
mail(@options)
end
def process_attachments_as_files_for_email_reply
# Attachment processing for direct email replies (when replying to a single message)
#
# How attachments are handled:
# 1. Total file size (<20MB): Added directly to the email as proper attachments
# 2. Total file size (>20MB): Added to @large_attachments to be displayed as links in the email
@options[:attachments] = []
@large_attachments = []
current_total_size = 0
@message.attachments.each do |attachment|
raw_data = attachment.file.download
attachment_name = attachment.file.filename.to_s
file_size = raw_data.bytesize
# Attach files directly until we hit 20MB total
# After reaching 20MB, send remaining files as links
if current_total_size + file_size <= 20.megabytes
mail.attachments[attachment_name] = raw_data
@options[:attachments] << { name: attachment_name }
current_total_size += file_size
else
@large_attachments << attachment
end
end
end
private
def google_smtp_settings
return unless @inbox.email? && @channel.imap_enabled && @inbox.channel.google?
smtp_settings = base_smtp_settings('smtp.gmail.com')
@options[:delivery_method] = :smtp
@options[:delivery_method_options] = smtp_settings
end
def ms_smtp_settings
return unless @inbox.email? && @channel.imap_enabled && @inbox.channel.microsoft?
smtp_settings = base_smtp_settings('smtp.office365.com')
@options[:delivery_method] = :smtp
@options[:delivery_method_options] = smtp_settings
end
def base_smtp_settings(domain)
{
address: domain,
port: 587,
user_name: @channel.imap_login,
password: @channel.provider_config['access_token'],
domain: domain,
tls: false,
enable_starttls_auto: true,
openssl_verify_mode: 'none',
authentication: 'xoauth2'
}
end
def set_delivery_method
return unless @inbox.inbox_type == 'Email' && @channel.smtp_enabled
smtp_settings = {
address: @channel.smtp_address,
port: @channel.smtp_port,
user_name: @channel.smtp_login,
password: @channel.smtp_password,
domain: @channel.smtp_domain,
tls: @channel.smtp_enable_ssl_tls,
enable_starttls_auto: @channel.smtp_enable_starttls_auto,
openssl_verify_mode: @channel.smtp_openssl_verify_mode,
authentication: @channel.smtp_authentication
}
@options[:delivery_method] = :smtp
@options[:delivery_method_options] = smtp_settings
end
def email_smtp_enabled
@inbox.inbox_type == 'Email' && @channel.smtp_enabled
end
def email_imap_enabled
@inbox.inbox_type == 'Email' && @channel.imap_enabled
end
def email_oauth_enabled
@inbox.inbox_type == 'Email' && (@channel.microsoft? || @channel.google?)
end
def email_from
email_oauth_enabled || email_smtp_enabled ? channel_email_with_name : from_email_with_name
end
def email_reply_to
email_imap_enabled ? @channel.email : reply_email
end
# Use channel email domain in case of account email domain is not set for custom message_id and in_reply_to
def channel_email_domain
return @account.inbound_email_domain if @account.inbound_email_domain.present?
email = @inbox.channel.try(:email)
email.present? ? email.split('@').last : raise(StandardError, 'Channel email domain not present.')
end
end