diff --git a/Gemfile b/Gemfile index fdbba4b..c8b2130 100644 --- a/Gemfile +++ b/Gemfile @@ -48,6 +48,8 @@ gem "bootsnap", require: false # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] # gem "image_processing", "~> 1.2" +gem "interactor-rails", "~> 2.0" + group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem gem "debug", platforms: %i[ mri mingw x64_mingw ] diff --git a/Gemfile.lock b/Gemfile.lock index 22ff29f..a75884f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -96,6 +96,10 @@ GEM importmap-rails (1.0.3) actionpack (>= 6.0.0) railties (>= 6.0.0) + interactor (3.1.2) + interactor-rails (2.2.1) + interactor (~> 3.0) + rails (>= 4.2) io-console (0.5.11) io-wait (0.2.1) irb (1.4.1) @@ -217,6 +221,7 @@ DEPENDENCIES capybara debug importmap-rails + interactor-rails (~> 2.0) jbuilder puma (~> 5.0) rails (~> 7.0.2, >= 7.0.2.2) diff --git a/app/interactors/fetch_emails.rb b/app/interactors/fetch_emails.rb new file mode 100644 index 0000000..df0a4dc --- /dev/null +++ b/app/interactors/fetch_emails.rb @@ -0,0 +1,75 @@ +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 diff --git a/spec/interactors/fetch_action_emails_spec.rb b/spec/interactors/fetch_action_emails_spec.rb new file mode 100644 index 0000000..cbe34b8 --- /dev/null +++ b/spec/interactors/fetch_action_emails_spec.rb @@ -0,0 +1,7 @@ +require 'spec_helper' + +RSpec.describe FetchActionEmails, type: :interactor do + describe '.call' do + pending "add some examples to (or delete) #{__FILE__}" + end +end