Class: UnsubscribesMailbox
- Inherits:
-
ApplicationMailbox
- Object
- ActionMailbox::Base
- ApplicationMailbox
- UnsubscribesMailbox
- Defined in:
- app/mailboxes/unsubscribes_mailbox.rb
Overview
ActionMailbox handler for inbound list-unsubscribe replies.
Routed from config/initializers/450_action_mailbox_routing.rb for
recipients matching RECIPIENT_FORMAT (e.g.
list-unsubscribe+<uid>@warmlyyours.com or list-remove+<uid>@...).
The <uid> is the Communication#unique_id minted in
Communication#email and surfaced as the List-Unsubscribe header on
outbound marketing email — see Communication#email.
When the UID resolves to known recipients, their EmailPreference rows
are flipped to fully-unsubscribed via EmailPreference.unsubscribe.
Defined Under Namespace
Classes: EmailNotFoundError
Constant Summary collapse
- RECIPIENT_FORMAT =
E.g list-unsubscribe+79b3b15c-4f68-4d53-bf32-b44966744c45@warmlyyours.com
list-remove+8b3ee4d1-6c53-4c7b-b654-66f4328030ce@warmlyyours.com>
See communication mailer where this is set into the header in #email method.
If this format changes then it needs to change there as well. The named
uidcapture is consumed by#process—String#scanwith multiple
capture groups previously returned[type, uid]and a.first.first
silently extracted "remove"/"unsubscribe" instead of the UUID, so every
inbound list-unsubscribe became a no-op. /list-(?:remove|unsubscribe)\+(?<uid>[\w-]+)@/i
Constants inherited from ApplicationMailbox
ApplicationMailbox::DIRECT_FILE_EXTENSIONS, ApplicationMailbox::FETCHABLE_MIME_TYPES, ApplicationMailbox::FETCH_MAX_SIZE, ApplicationMailbox::GDRIVE_FILE_RE, ApplicationMailbox::GDRIVE_OPEN_RE, ApplicationMailbox::MAX_FILENAME_LENGTH
Instance Method Summary collapse
-
#process ⇒ void
Routes a single inbound list-unsubscribe email to the relevant EmailPreference rows.
Methods inherited from ApplicationMailbox
#attempt_url_download, #collect_fetchable_urls, #create_activity, #create_communication, #fetch_linked_files, #find_sender_party, #get_resource_id, #inline_or_attachment_part?, #process_attachments, #process_content, #replace_cid_references, #sanitize_attachment_filename
Instance Method Details
#process ⇒ void
This method returns an undefined value.
Routes a single inbound list-unsubscribe email to the relevant
EmailPreference rows. When the sender's address matches one of the
original communication's recipients, only that address is unsubscribed;
otherwise every recipient on the originating communication is
unsubscribed (the typical case for "Clean Email"-style proxies that
send from their own domain).
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'app/mailboxes/unsubscribes_mailbox.rb', line 44 def process to = mail.to&.first sender = mail.from&.first Rails.logger.info "Processing Unbuscribe Mail for #{sender}, in response to #{to}" uid = to&.match(RECIPIENT_FORMAT)&.[](:uid) if uid.blank? msg = "UID could not be extracted for sender #{sender} with unsubscribe #{to} process manually" Rails.logger.error msg ErrorReporting.error(msg, source: :background, sender: sender, to: to, inbound_email_id: inbound_email.id) return end Rails.logger.info "Parsed uid as #{uid}, seeking emails matching" emails = CommunicationRecipient.emails .joins(:communication) .where(communications: { unique_id: uid }) .where.not(detail: nil) .distinct .pluck(:detail) .map(&:downcase) if emails.empty? msg = "UnsubscribesMailbox could not find any communication recipients for uid=#{uid}" ErrorReporting.error(msg, source: :background, sender: sender, to: to, uid: uid, inbound_email_id: inbound_email.id) raise EmailNotFoundError, msg end if (parsed_emails = emails.select { |e| e == sender&.downcase }).present? Rails.logger.info "Detected #{parsed_emails.join(', ')} and unsubscribing" emails = parsed_emails else Rails.logger.info "Unsubscribing all emails: #{emails.join(', ')}" end PaperTrail.request(whodunnit: 'UnsubscribesMailbox') do EmailPreference.unsubscribe(emails) end end |