mailhandler 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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