require "net/smtp" # High level Interface to distribute Protocols::Mail objects. # It is robust against network loss. class Protocols::Smtp def initialize reset_smtp_client! end class DistributedCache < Array def initialize(max_size: 4096) @max_size = max_size super() end def <<(message_id) prepend(message_id) if !message_id.nil? slice! @max_size end end def cache (@cache ||= DistributedCache.new) end # @param mail [Protocols::Mail] def distribute(mail) if cache.include?(mail.cache_id) $logger.warn "Already distributed #{mail.message_id}" return end $logger.info "SEND #{mail.from}\t -> #{mail.to}:\t#{mail.subject}" smtp_raw = mail.to_smtp $logger.debug smtp_raw.join("=====") send_message_safe(*smtp_raw) cache << mail.cache_id rescue StandardError => e $logger.warn "FAILED #{mail.from}\t -> #{mail.to}:\t#{mail.subject}" $logger.debug e end # :nodoc: private def reset_smtp_client! @smtp = Net::SMTP.new( ENV["SMTP_HOST"], ENV["SMTP_PORT"], tls: ENV["SMTP_TLS"] == "true", ).start( user: ENV["SMTP_USER"], secret: ENV["SMTP_PASSWORD"], authtype: :login, ) end # :nodoc: private def send_message_safe(*raw, max_tries: 3) return $logger.error("send_message_safe reached max_tries_limit") if max_tries == 0 begin @smtp.send_message(*raw) rescue EOFError, Net::SMTPServerBusy => e $logger.warn e.message reset_smtp_client! send_message_safe(*raw, max_tries: max_tries - 1) rescue => e $logger.error e.full_message reset_smtp_client! end end end