require "net/imap" class FetchEmails include Interactor OUTLOOK_IMAP_HOST = 'outlook.office365.com' OUTLOOK_IMAP_PORT = 993 SENT_FOLDER_NAME = 'Sent Items' INBOX_FOLDER_NAME = 'INBOX' DRAFTS_FOLDER_NAME = 'Drafts' def call establish_imap_connection( host: OUTLOOK_IMAP_HOST, port: OUTLOOK_IMAP_PORT, username: context.email, password: context.password ) do |imap| context.messages=get_messages( imap: imap, folder: INBOX_FOLDER_NAME, after_datetime: context.query_from ) end end def establish_imap_connection(host:, port:, username:, password:) connection = Net::IMAP.new(OUTLOOK_IMAP_HOST, OUTLOOK_IMAP_PORT, ssl: true) connection.login(username, password) return connection unless block_given? begin yield connection ensure connection&.disconnect end end # imap_query: is an array following IMAP query format such as ["SINCE", "11-Jan-2021"] def get_messages(imap:, folder: INBOX_FOLDER_NAME, after_datetime: nil, imap_query: nil) raise ::NotImplementedError, 'Still to be implemented fetching of all messages' unless imap_query || after_datetime query = if imap_query imap_query elsif after_datetime # The IMAP SINCE filter does not accept a time component to be able to skip # the fetching will iterate over the emails but will not include the ones that # have a date on the envelope that is in the past respect to after_datetime # therefore ignoring these. ['SINCE', after_datetime.strftime('%d-%b-%Y')] end messages = [] imap.examine(folder) imap.search(query).each do |message_id| res = imap.fetch(message_id, %w(RFC822 ENVELOPE UID))[0] envelope = res.attr['ENVELOPE'] next if after_datetime && envelope.date.to_time.to_i < after_datetime.to_i rfc = res.attr['RFC822'] uid = res.attr['UID'] mail = Mail::Message.new(rfc) message = OpenStruct.new( date: envelope.date.to_time.utc.iso8601, thread: mail.references, from: "#{envelope.from&.dig(0)&.name} <#{envelope.from&.dig(0)&.mailbox}@#{envelope.from&.dig(0)&.host}>", subject: envelope.subject, mail_object: mail, uid: uid, ) messages << message end messages end end