Co-authored-by: Pranav <pranav@chatwoot.com> Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
84 lines
2.6 KiB
Ruby
84 lines
2.6 KiB
Ruby
class Mailbox::ConversationFinderStrategies::NewConversationStrategy < Mailbox::ConversationFinderStrategies::BaseStrategy
|
|
include MailboxHelper
|
|
include IncomingEmailValidityHelper
|
|
|
|
attr_accessor :processed_mail, :account, :inbox, :contact, :contact_inbox, :conversation, :channel
|
|
|
|
def initialize(mail)
|
|
super(mail)
|
|
@channel = EmailChannelFinder.new(mail).perform
|
|
return unless @channel
|
|
|
|
@account = @channel.account
|
|
@inbox = @channel.inbox
|
|
@processed_mail = MailPresenter.new(mail, @account)
|
|
end
|
|
|
|
# This strategy prepares a new conversation but doesn't persist it yet.
|
|
# Why we don't use create! here:
|
|
# - Avoids orphan conversations if message/attachment creation fails later
|
|
# - Prevents duplicate conversations on job retry (no idempotency issue)
|
|
# - Follows the pattern from old SupportMailbox where everything was in one transaction
|
|
# The actual persistence happens in ReplyMailbox within a transaction that includes message creation.
|
|
def find
|
|
return nil unless @channel # No valid channel found
|
|
return nil unless incoming_email_from_valid_email? # Skip edge cases
|
|
|
|
# Check if conversation already exists by in_reply_to
|
|
existing_conversation = find_conversation_by_in_reply_to
|
|
return existing_conversation if existing_conversation
|
|
|
|
# Prepare contact (persisted) and build conversation (not persisted)
|
|
find_or_create_contact
|
|
build_conversation
|
|
end
|
|
|
|
private
|
|
|
|
def find_or_create_contact
|
|
@contact = @inbox.contacts.from_email(original_sender_email)
|
|
if @contact.present?
|
|
@contact_inbox = ContactInbox.find_by(inbox: @inbox, contact: @contact)
|
|
else
|
|
create_contact
|
|
end
|
|
end
|
|
|
|
def original_sender_email
|
|
@processed_mail.original_sender&.downcase
|
|
end
|
|
|
|
def identify_contact_name
|
|
@processed_mail.sender_name || @processed_mail.from.first.split('@').first
|
|
end
|
|
|
|
def build_conversation
|
|
# Build but don't persist - ReplyMailbox will save in transaction with message
|
|
@conversation = ::Conversation.new(
|
|
account_id: @account.id,
|
|
inbox_id: @inbox.id,
|
|
contact_id: @contact.id,
|
|
contact_inbox_id: @contact_inbox.id,
|
|
additional_attributes: {
|
|
in_reply_to: in_reply_to,
|
|
source: 'email',
|
|
auto_reply: @processed_mail.auto_reply?,
|
|
mail_subject: @processed_mail.subject,
|
|
initiated_at: {
|
|
timestamp: Time.now.utc
|
|
}
|
|
}
|
|
)
|
|
end
|
|
|
|
def in_reply_to
|
|
mail['In-Reply-To'].try(:value)
|
|
end
|
|
|
|
def find_conversation_by_in_reply_to
|
|
return if in_reply_to.blank?
|
|
|
|
@account.conversations.where("additional_attributes->>'in_reply_to' = ?", in_reply_to).first
|
|
end
|
|
end
|