feat: Clean up email configuration for from and reply to emails (#12453)

We first added conversation continuity for the live chat widget, and
then carried the same logic over to email channels.

The problem was that this added a reply+conversationUUID@domain.com as
the reply-to for emails, which was unnecessary. For email channels, the
reply-to can just be the channel’s own email address.

That extra layer made things more complex than it needed to be. In this
PR, I’ve cleaned up the config so it’s simpler. The table below shows
how it’ll work going forward.

---

| Type | From Email | Reply To Email |
| -- | -- | -- |
| Standard IMAP, SMTP email channel | channel.email | channel.email |
| Google OAuth Email channel | channel.email | channel.email |
| Microsoft OAuth Email channel | channel.email | channel.email |
| Email forwarded to Chatwoot, brought their own SMTP | channel.email |
channel.email |
| Imap to fetch email, Use Chatwoot's SMTP | channel.email if verified
with Chatwoot's SMTP provider. Otherwise account support email |
channel.email |
| Email forwarded to Chatwoot, Use Chatwoot's SMTP | channel.email if
verified with Chatwoot's SMTP provider. Otherwise account support email
| channel.email |
| -- | --  | -- |
| Website Live Chat - Conversation Continuity Inbound Emails enabled|
Account Support Email | reply+{conversation-uuid}@{account_domain} |
| Website Live Chat - Conversation Continuity Inbound Emails disabled|
Account Support Email | Account Support Email |

Fixes https://github.com/chatwoot/chatwoot/issues/10614
Fixes https://github.com/chatwoot/chatwoot/issues/10521
Fixes https://github.com/chatwoot/chatwoot/issues/10300
Fixes https://github.com/chatwoot/chatwoot/issues/10091
Fixes https://github.com/chatwoot/chatwoot/issues/4890
Fixes https://github.com/chatwoot/chatwoot/issues/10676
Fixes https://github.com/chatwoot/chatwoot/issues/10756
Fixes https://github.com/chatwoot/chatwoot/issues/11515
Fixes https://github.com/chatwoot/chatwoot/issues/9471

---------

Co-authored-by: Sojan Jose <sojan@pepalo.com>
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
This commit is contained in:
Pranav
2025-09-24 11:36:53 -07:00
committed by GitHub
parent c3680d50bc
commit 47bdb6d2bb
10 changed files with 422 additions and 2 deletions

View File

@@ -0,0 +1,54 @@
class Email::BaseBuilder
pattr_initialize [:inbox!]
private
def channel
@channel ||= inbox.channel
end
def account
@account ||= inbox.account
end
def conversation
@conversation ||= message.conversation
end
def custom_sender_name
message&.sender&.available_name || I18n.t('conversations.reply.email.header.notifications')
end
def sender_name(sender_email)
# Friendly: <agent_name> from <business_name>
# Professional: <business_name>
if inbox.friendly?
I18n.t(
'conversations.reply.email.header.friendly_name',
sender_name: custom_sender_name,
business_name: business_name,
from_email: sender_email
)
else
I18n.t(
'conversations.reply.email.header.professional_name',
business_name: business_name,
from_email: sender_email
)
end
end
def business_name
inbox.business_name || inbox.sanitized_name
end
def account_support_email
# Parse the email to ensure it's in the correct format, the user
# can save it in the format "Name <email@domain.com>"
parse_email(account.support_email)
end
def parse_email(email_string)
Mail::Address.new(email_string).address
end
end

View File

@@ -0,0 +1,51 @@
class Email::FromBuilder < Email::BaseBuilder
pattr_initialize [:inbox!, :message!]
def build
return sender_name(account_support_email) unless inbox.email?
from_email = case email_channel_type
when :standard_imap_smtp,
:google_oauth,
:microsoft_oauth,
:forwarding_own_smtp
channel.email
when :imap_chatwoot_smtp,
:forwarding_chatwoot_smtp
channel.verified_for_sending ? channel.email : account_support_email
else
account_support_email
end
sender_name(from_email)
end
private
def email_channel_type
return :google_oauth if channel.google?
return :microsoft_oauth if channel.microsoft?
return :standard_imap_smtp if imap_and_smtp_enabled?
return :imap_chatwoot_smtp if imap_enabled_without_smtp?
return :forwarding_own_smtp if forwarding_with_own_smtp?
return :forwarding_chatwoot_smtp if forwarding_without_smtp?
:unknown
end
def imap_and_smtp_enabled?
channel.imap_enabled && channel.smtp_enabled
end
def imap_enabled_without_smtp?
channel.imap_enabled && !channel.smtp_enabled
end
def forwarding_with_own_smtp?
!channel.imap_enabled && channel.smtp_enabled
end
def forwarding_without_smtp?
!channel.imap_enabled && !channel.smtp_enabled
end
end

View File

@@ -0,0 +1,21 @@
class Email::ReplyToBuilder < Email::BaseBuilder
pattr_initialize [:inbox!, :message!]
def build
reply_to = if inbox.email?
channel.email
elsif inbound_email_enabled?
"reply+#{conversation.uuid}@#{account.inbound_email_domain}"
else
account_support_email
end
sender_name(reply_to)
end
private
def inbound_email_enabled?
account.feature_enabled?('inbound_emails') && account.inbound_email_domain.present?
end
end

View File

@@ -118,10 +118,14 @@ module ConversationReplyMailerHelper
end
def email_from
return Email::FromBuilder.new(inbox: @inbox, message: current_message).build if @account.feature_enabled?(:reply_mailer_migration)
email_oauth_enabled || email_smtp_enabled ? channel_email_with_name : from_email_with_name
end
def email_reply_to
return Email::ReplyToBuilder.new(inbox: @inbox, message: current_message).build if @account.feature_enabled?(:reply_mailer_migration)
email_imap_enabled ? @channel.email : reply_email
end

View File

@@ -23,6 +23,7 @@
# smtp_openssl_verify_mode :string default("none")
# smtp_password :string default("")
# smtp_port :integer default(0)
# verified_for_sending :boolean default(FALSE), not null
# created_at :datetime not null
# updated_at :datetime not null
# account_id :integer not null
@@ -42,7 +43,7 @@ class Channel::Email < ApplicationRecord
self.table_name = 'channel_email'
EDITABLE_ATTRS = [:email, :imap_enabled, :imap_login, :imap_password, :imap_address, :imap_port, :imap_enable_ssl,
:smtp_enabled, :smtp_login, :smtp_password, :smtp_address, :smtp_port, :smtp_domain, :smtp_enable_starttls_auto,
:smtp_enable_ssl_tls, :smtp_openssl_verify_mode, :smtp_authentication, :provider].freeze
:smtp_enable_ssl_tls, :smtp_openssl_verify_mode, :smtp_authentication, :provider, :verified_for_sending].freeze
validates :email, uniqueness: true
validates :forward_to_email, uniqueness: true