mailhandler 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ad4de21ce6ad278dd2916e5cb898691c10961b87
4
+ data.tar.gz: caedb45d153cbfa44d41b13328a9afb20c81b2fa
5
+ SHA512:
6
+ metadata.gz: a0da923ce9b696ca05ce4cc1b322678bd272b44227ca25a330239375fac65c3bdddbe3fbc08b6b425a27b89c24907b4a48fd259a5a99cec88f5e4ac6f165f8c4
7
+ data.tar.gz: 7667ee7a964c55262104b954abd8289f7855b4107056041587702d76dbecab016425b139d49a3989ddcc53d7a905d781c029f39ca339cb122278d75542c46eae
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ **/*.log
2
+ **/*.rvm
3
+ **/*.DS_Store
4
+ **/*.idea
5
+
@@ -0,0 +1,91 @@
1
+ require_relative 'mailhandler/sender'
2
+ require_relative 'mailhandler/receiver'
3
+
4
+ require_relative 'mailhandler/receiving/notification/email'
5
+ require_relative 'mailhandler/receiving/notification/console'
6
+
7
+ module MailHandler
8
+
9
+ class Handler
10
+
11
+ attr_accessor :sender,
12
+ :receiver
13
+
14
+ def self.sender(type = :postmark_api)
15
+
16
+ verify_type(type, SENDER_TYPES)
17
+ sender = MailHandler::Sender.new(SENDER_TYPES[type].new)
18
+ yield(sender.dispatcher) if block_given?
19
+
20
+ sender
21
+
22
+ end
23
+
24
+
25
+ def self.receiver(type = :folder, notifications = [])
26
+
27
+ verify_type(type, CHECKER_TYPES)
28
+ receiver = MailHandler::Receiver.new(CHECKER_TYPES[type].new)
29
+ add_receiving_notifications(receiver, notifications)
30
+
31
+ yield(receiver.checker) if block_given?
32
+
33
+ receiver
34
+
35
+ end
36
+
37
+ def self.handler(sender, receiver)
38
+
39
+ handler = new
40
+ handler.sender = sender
41
+ handler.receiver = receiver
42
+ handler
43
+
44
+ end
45
+
46
+ private
47
+
48
+ def self.add_receiving_notifications(receiver, notifications)
49
+
50
+ if (notifications - NOTIFICATION_TYPES.keys).empty?
51
+
52
+ notifications.each { |n| receiver.add_observer(NOTIFICATION_TYPES[n].new) }
53
+
54
+ end
55
+
56
+ end
57
+
58
+ def self.verify_type(type, types)
59
+
60
+ raise StandardError, "Unknown type - #{type}, possible options: #{types.keys}" unless types.keys.include? type
61
+
62
+ end
63
+
64
+ CHECKER_TYPES = {
65
+
66
+ :folder => Receiving::FolderChecker,
67
+ :imap => Receiving::IMAPChecker
68
+
69
+ }
70
+
71
+ SENDER_TYPES = {
72
+
73
+ :postmark_api => Sending::PostmarkAPISender,
74
+ :postmark_batch_api => Sending::PostmarkBatchAPISender,
75
+ :smtp => Sending::SMTPSender
76
+
77
+ }
78
+
79
+ NOTIFICATION_TYPES = {
80
+
81
+ :console => Receiving::Notification::Console,
82
+ :email => Receiving::Notification::Email
83
+
84
+ }
85
+
86
+ end
87
+
88
+ end
89
+
90
+
91
+
@@ -0,0 +1,86 @@
1
+ require_relative 'receiving/checker_folder'
2
+ require_relative 'receiving/checker_imap'
3
+ require_relative 'receiving/observer'
4
+
5
+ module MailHandler
6
+
7
+ class Receiver
8
+
9
+ include Receiving::Observer
10
+
11
+ attr_accessor :checker,
12
+ :search,
13
+ :search_max_duration
14
+
15
+ module DEFAULTS
16
+
17
+ MAX_SEARCH_DURATION = 240 # maximum time for search to last in [seconds]
18
+
19
+ end
20
+
21
+ # @param [Hash] - search options
22
+ # @param [Time] - search started at Time
23
+ # @param [Time] - search finished at Time
24
+ # @param [int] - how long search lasted
25
+ # @param [int] - how long search can last
26
+ # @param [boolean] - result of search
27
+ # @param [Mail] - first email found
28
+ # @param [Array] - all emails found
29
+ Search = Struct.new( :options, :started_at, :finished_at, :duration, :max_duration, :result, :email, :emails)
30
+
31
+ def initialize(checker)
32
+
33
+ @checker = checker
34
+ @search_max_duration = DEFAULTS::MAX_SEARCH_DURATION
35
+
36
+ end
37
+
38
+ def find_email(options)
39
+
40
+ init_search_details(options)
41
+
42
+ while 1
43
+
44
+ received = checker.find(options) || search_time_expired?
45
+ update_search_details
46
+ notify_observers(search)
47
+
48
+ break if received
49
+ sleep 1
50
+
51
+ end
52
+
53
+ checker.search_result
54
+
55
+ end
56
+
57
+ private
58
+
59
+ def init_search_details(options)
60
+
61
+ @search = Search.new
62
+ @search.options = options
63
+ @search.started_at = Time.now
64
+ @search.max_duration = @search_max_duration
65
+
66
+ end
67
+
68
+ def update_search_details
69
+
70
+ search.finished_at = Time.now
71
+ search.duration = search.finished_at - search.started_at
72
+ search.result = checker.search_result
73
+ search.emails = checker.found_emails
74
+ search.email = search.emails.first
75
+
76
+ end
77
+
78
+ def search_time_expired?
79
+
80
+ (Time.now - search.started_at) > @search_max_duration
81
+
82
+ end
83
+
84
+ end
85
+
86
+ end
@@ -0,0 +1,60 @@
1
+ module MailHandler
2
+
3
+ module Receiving
4
+
5
+ #
6
+ # Email receiving checker interface. All email checking types need to implement it.
7
+ # @see MailHandler::Receiving::FolderChecker for example for one of implemented checkers.
8
+ #
9
+ # Checker interface is used for doing a single check whether email is in your inbox.
10
+ #
11
+ class Checker
12
+
13
+ attr_accessor :search_options,
14
+ :found_emails
15
+
16
+ AVAILABLE_SEARCH_OPTIONS = [
17
+
18
+ :by_subject,
19
+ :by_content,
20
+ :by_date,
21
+ :by_recipient,
22
+ :count,
23
+ :archive
24
+
25
+ ]
26
+
27
+ def initialize
28
+
29
+ # Default number of email results to return, and whether to archive emails.
30
+ @search_options = {:count => 10, :archive => false}
31
+
32
+ end
33
+
34
+ def find(options)
35
+
36
+ raise StandardError, 'Method not implemented'
37
+
38
+ end
39
+
40
+ def verify_and_set_search_options(options)
41
+
42
+ unless (options.keys - AVAILABLE_SEARCH_OPTIONS).empty?
43
+ raise StandardError, "#{(options.keys - AVAILABLE_SEARCH_OPTIONS)} - Incorrect search option values, options are #{AVAILABLE_SEARCH_OPTIONS}"
44
+ end
45
+
46
+ @search_options = search_options.merge options
47
+
48
+ end
49
+
50
+ def search_result
51
+
52
+ found_emails != nil and !found_emails.empty?
53
+
54
+ end
55
+
56
+ end
57
+
58
+ end
59
+
60
+ end
@@ -0,0 +1,126 @@
1
+ require_relative 'checker.rb'
2
+ require_relative 'filter'
3
+ require 'fileutils'
4
+ require 'mail'
5
+
6
+ module MailHandler
7
+
8
+ module Receiving
9
+
10
+ class FolderChecker < Checker
11
+
12
+ # folders in which emails will be searched for and managed
13
+ attr_accessor :inbox_folder,
14
+ :archive_folder
15
+
16
+ def initialize(inbox_folder = nil, archive_folder = nil)
17
+
18
+ super()
19
+
20
+ @inbox_folder = inbox_folder
21
+ @archive_folder = archive_folder
22
+
23
+ end
24
+
25
+ # check whether email is received by checking for an email in folder
26
+ def find(options)
27
+
28
+ verify_and_set_search_options(options)
29
+ email_files = find_files(search_options)
30
+
31
+ if email_files.empty?
32
+
33
+ @found_emails = []
34
+
35
+ else
36
+
37
+ @found_emails = read_found_emails(email_files, search_options[:count])
38
+ move_files(email_files) if search_options[:archive]
39
+
40
+ end
41
+
42
+ !@found_emails.empty?
43
+
44
+ end
45
+
46
+ private
47
+
48
+ # filter options which need to be done by searching files
49
+ FILE_SEARCH_OPTIONS = {
50
+
51
+ :by_subject => Filter::ByContent,
52
+ :by_content => Filter::ByContent,
53
+ :by_date => Filter::ByDate,
54
+ :by_recipient => Filter::Email::ByRecipient
55
+ }
56
+
57
+ def search_pattern
58
+
59
+ @inbox_folder + '/*.*'
60
+
61
+ end
62
+
63
+ def read_found_emails(files, count)
64
+
65
+ files.first(count).map do |file|
66
+
67
+ email_content = File.read(file)
68
+ Mail.read_from_string(email_content)
69
+
70
+ end
71
+
72
+ end
73
+
74
+ # find files by FILE_SEARCH_OPTIONS options
75
+ # this will ignore filter criteria options which can't be done on files directly
76
+ def find_files(options)
77
+
78
+ files = Filter::Base.new.get(search_pattern)
79
+
80
+ options.each do |key, value|
81
+
82
+ files = (files & FILE_SEARCH_OPTIONS[key].new(value).get(search_pattern)) if FILE_SEARCH_OPTIONS[key] != nil
83
+
84
+ end
85
+
86
+ Filter::Base.sort(files)
87
+
88
+ end
89
+
90
+ def move_files(files)
91
+
92
+ setup_inbox_folders
93
+
94
+ files.each do |file|
95
+
96
+ file = File.basename(file)
97
+
98
+ if inbox_folder != archive_folder
99
+
100
+ FileUtils.mv("#{inbox_folder}/#{file}", "#{archive_folder}/#{file}")
101
+
102
+ else
103
+
104
+ FileUtils.rm_r "#{inbox_folder}/#{file}", :force => true
105
+
106
+ end
107
+
108
+ end
109
+
110
+ end
111
+
112
+ # create folders if they don't exist
113
+ def setup_inbox_folders
114
+
115
+ raise StandardError, 'Folder variables are not set' if inbox_folder.nil? or archive_folder.nil?
116
+
117
+ Dir::mkdir inbox_folder unless File.directory? inbox_folder
118
+ Dir::mkdir archive_folder unless File.directory? archive_folder
119
+
120
+ end
121
+
122
+ end
123
+
124
+ end
125
+
126
+ end
@@ -0,0 +1,90 @@
1
+ require 'mail'
2
+ require_relative 'checker.rb'
3
+ # encoding: utf-8
4
+
5
+ module MailHandler
6
+
7
+ module Receiving
8
+
9
+ class IMAPChecker < Checker
10
+
11
+ AVAILABLE_SEARCH_OPTIONS = [
12
+
13
+ :by_subject,
14
+ :count,
15
+ :archive
16
+
17
+ ]
18
+
19
+ def initialize
20
+
21
+ super
22
+
23
+ end
24
+
25
+ # set email account from which we will read email
26
+ def imap_details(address, port, username, password, use_ssl)
27
+
28
+ Mail.defaults do
29
+ retriever_method :imap,
30
+ :address => address,
31
+ :port => port,
32
+ :user_name => username,
33
+ :password => password,
34
+ :enable_ssl => use_ssl
35
+ end
36
+
37
+ end
38
+
39
+ def find(options)
40
+
41
+ verify_and_set_search_options(options)
42
+ validate_options(options)
43
+ emails = find_emails(search_options)
44
+
45
+ @found_emails = []
46
+
47
+ unless emails.empty?
48
+
49
+ emails.each do |email|
50
+
51
+ found = email.subject.include? search_options[:by_subject]
52
+ @found_emails << email if found
53
+
54
+ end
55
+
56
+ end
57
+
58
+ !@found_emails.empty?
59
+
60
+ end
61
+
62
+ private
63
+
64
+ def find_emails(options)
65
+
66
+ if options[:archive]
67
+
68
+ Mail.find_and_delete(:what => :last, :count => search_options[:count], :order => :desc, :keys => ['SUBJECT', search_options[:by_subject]])
69
+
70
+ else
71
+
72
+ Mail.find(:what => :last, :count => search_options[:count], :order => :desc, :keys => ['SUBJECT', search_options[:by_subject]])
73
+
74
+ end
75
+
76
+ end
77
+
78
+ def validate_options(options)
79
+
80
+ unless (options.keys - AVAILABLE_SEARCH_OPTIONS).empty?
81
+ raise StandardError, "#{(options.keys - AVAILABLE_SEARCH_OPTIONS)} - Not supported search option values for imap, options are #{AVAILABLE_SEARCH_OPTIONS}"
82
+ end
83
+
84
+ end
85
+
86
+ end
87
+
88
+ end
89
+
90
+ end