rockdove 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +10 -0
- data/Gemfile +4 -0
- data/Guardfile +9 -0
- data/LICENSE +22 -0
- data/README.md +87 -0
- data/Rakefile +6 -0
- data/lib/generators/rockdove/install_generator.rb +12 -0
- data/lib/rockdove/collect_mail.rb +120 -0
- data/lib/rockdove/config.rb +42 -0
- data/lib/rockdove/dovetie.rb +18 -0
- data/lib/rockdove/email_parser.rb +46 -0
- data/lib/rockdove/exchange_mail.rb +52 -0
- data/lib/rockdove/script/rockdove_server.rb +46 -0
- data/lib/rockdove/version.rb +3 -0
- data/lib/rockdove.rb +24 -0
- data/rockdove.gemspec +31 -0
- data/spec/rockdove/collect_mail_spec.rb +92 -0
- data/spec/rockdove/emails/auto_reply_mail.yml +52 -0
- data/spec/rockdove/emails/forwarded_mail.yml +77 -0
- data/spec/rockdove/emails/gmail_fwd_mail.yml +46 -0
- data/spec/rockdove/emails/mail_type_text.yml +47 -0
- data/spec/rockdove/emails/new_mail.yml +52 -0
- data/spec/rockdove/emails/replied_mail.yml +49 -0
- data/spec/rockdove/emails/undeliverable_mail.yml +52 -0
- data/spec/rockdove/fetch_incoming_mail_spec.rb +77 -0
- data/spec/rockdove/rockdove_shared_spec.rb +51 -0
- data/spec/spec_helper.rb +25 -0
- metadata +171 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
## v0.0.1
|
2
|
+
|
3
|
+
### initial release
|
4
|
+
|
5
|
+
* Connects to EWS mailbox
|
6
|
+
* Fetches the mail items
|
7
|
+
* Parses each item (Signatures, Replies, Forward)
|
8
|
+
* Handles bounce types (Undeliverable,AutoReply)
|
9
|
+
* Polls for every interval specified
|
10
|
+
* Provides a template of the Daemon for easy plug & play
|
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2012-2015 Kiran Soumya Bosetti
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
rockdove ![rockdove](http://kiran.gnufied.org/wp-content/uploads/2012/07/1341725724_bird.png) [![Build Status](https://secure.travis-ci.org/kiranh/rockdove.png)](http://travis-ci.org/kiranh/rockdove)
|
2
|
+
========
|
3
|
+
|
4
|
+
Incoming mail processing daemon for Exchange Web Services 1.0 (EWS). This Ruby Gem connects to the EWS mailbox, fetches the mail items, parses each mail item (Signatures, Replies, Forward), handles bounce types (Undeliverable,AutoReply) and polls the mailbox for every interval specified.
|
5
|
+
|
6
|
+
It provides a template of the rockdove server which is a simple plug and play daemon.
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
#Under Gemfile
|
12
|
+
gem 'rockdove', :require => false
|
13
|
+
|
14
|
+
#After bundle install, run this command below
|
15
|
+
bundle exec rails g rockdove:install
|
16
|
+
```
|
17
|
+
It copies a template of rockdove server under you script folder which contains the following snippet:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
|
21
|
+
# ...
|
22
|
+
|
23
|
+
Rockdove::Config.configure do |config|
|
24
|
+
config.ews_url 'https://ewsdomain.com/ews/exchange.asmx'
|
25
|
+
config.ews_username 'ews_username'
|
26
|
+
config.ews_password 'ews_password'
|
27
|
+
config.ews_folder 'Inbox' # ews_folder is Inbox by default
|
28
|
+
config.ews_archive_folder 'Archive' # by default, it deletes the mail after processing,
|
29
|
+
# mention ews_archive_folder if it has to be archived to a different folder
|
30
|
+
config.ews_watch_interval 60 # by default, the polling interval is 60
|
31
|
+
end
|
32
|
+
|
33
|
+
# ...
|
34
|
+
|
35
|
+
Rockdove::CollectMail.watch do |rockdove_parsed_mail|
|
36
|
+
begin
|
37
|
+
#Model.method(rockdove_parsed_mail)
|
38
|
+
rescue Exception => e
|
39
|
+
Rockdove.logger.error "Exception occurred while receiving message:#{rockdove_parsed_mail}"
|
40
|
+
Rockdove.logger.error [e, *e.backtrace].join("\n")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
```
|
45
|
+
You need to change the Model into the one being used in your project that would require this behavior of Mail Processing for Exchange Web Services 1.0 and further process the mail as per the requirements in your project.
|
46
|
+
|
47
|
+
- And then you can run the script in console mode, `^C` will stop it, calling the stop method
|
48
|
+
|
49
|
+
``` sh
|
50
|
+
$ ruby ./script/rockdove_server.rb start #from your project root
|
51
|
+
```
|
52
|
+
- or run it daemonized, by default `./rockdove_server.log` and `./rockdove_server.pid` will be created
|
53
|
+
|
54
|
+
``` sh
|
55
|
+
$ ruby ./script/rockdove_server.rb -d start
|
56
|
+
```
|
57
|
+
|
58
|
+
- you can stop the daemon, removing `./rockdove_server.pid` or
|
59
|
+
|
60
|
+
``` sh
|
61
|
+
$ ruby ./script/rockdove_server.rb stop
|
62
|
+
```
|
63
|
+
---
|
64
|
+
|
65
|
+
## Compatibility
|
66
|
+
|
67
|
+
Tested on all Ruby versions (1.8.7, 1.9.2, 1.9.3) with Travis CI.
|
68
|
+
|
69
|
+
## Contributors
|
70
|
+
|
71
|
+
Welcome for any code optmizations or issue fixes or enhancements
|
72
|
+
|
73
|
+
### Setup:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
git clone git@github.com:kiranh/rockdove.git
|
77
|
+
cd rockdove
|
78
|
+
bundle exec rake # to run tests or bundle exec guard
|
79
|
+
```
|
80
|
+
|
81
|
+
## Acknowledgements
|
82
|
+
|
83
|
+
To [Biju Shoolapani](https://github.com/bijushoolapani) for support and encouragement. And the Code Reviewer, [Hemant Kumar](http://github.com/gnufied).
|
84
|
+
|
85
|
+
## Copyright
|
86
|
+
|
87
|
+
Copyright (c) 2012-2015 Kiran Soumya Bosetti. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
module Rockdove
|
2
|
+
module Generators
|
3
|
+
class InstallGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path("../../../rockdove/script", __FILE__)
|
5
|
+
|
6
|
+
desc "This generator copies the rockdove_server script file to your rails project"
|
7
|
+
def copy_script
|
8
|
+
copy_file "rockdove_server.rb", "script/rockdove_server.rb"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
module Rockdove
|
2
|
+
class CollectMail
|
3
|
+
UNDELIVERABLE = /Undeliverable/i
|
4
|
+
AUTO_REPLY = /Automatic reply/i
|
5
|
+
SPAM = /SPAM/i
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :mail_stack, :inbox_connection
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(mail_stack = nil, inbox = nil)
|
12
|
+
@mail_stack = mail_stack
|
13
|
+
@inbox_connection = inbox
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.watch &block
|
17
|
+
loop do
|
18
|
+
begin
|
19
|
+
new().send_rockdove_to_watch_mail(&block)
|
20
|
+
rescue Exception => e
|
21
|
+
Rockdove.logger.error [e, *e.backtrace].join("\n")
|
22
|
+
ensure
|
23
|
+
sleep(Rockdove::Config.watch_interval)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def send_rockdove_to_watch_mail(&block)
|
29
|
+
Rockdove.logger.info "Rockdove on watch for new mail..."
|
30
|
+
parsed_mails = group_of_mails
|
31
|
+
if parsed_mails
|
32
|
+
Rockdove.logger.info "Rockdove calling App block"
|
33
|
+
block.call(parsed_mails)
|
34
|
+
process()
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def group_of_mails
|
39
|
+
return no_mail_alert unless fetch_from_box
|
40
|
+
Rockdove.logger.info "Rockdove collected #{fetch_from_box.count} mail(s)."
|
41
|
+
letters = RockdoveCollection.new
|
42
|
+
@mail_stack.reverse.each do |item|
|
43
|
+
if bounce_type_mail?(item)
|
44
|
+
@mail_stack.delete(item)
|
45
|
+
else
|
46
|
+
letters << retrieve_mail(@inbox_connection.get_item(item.id))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
letters
|
50
|
+
end
|
51
|
+
|
52
|
+
def bounce_type_mail?(item)
|
53
|
+
case item.subject
|
54
|
+
when UNDELIVERABLE, AUTO_REPLY, SPAM
|
55
|
+
Rockdove.logger.info "Rockdove deleted this mail: #{item.subject}."
|
56
|
+
item.delete!
|
57
|
+
true
|
58
|
+
else
|
59
|
+
false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def retrieve_mail(fetched_mail)
|
64
|
+
Rockdove::ExchangeMail.new(fetched_mail)
|
65
|
+
end
|
66
|
+
|
67
|
+
def no_mail_alert
|
68
|
+
Rockdove.logger.info "Rockdove observed no mail yet."
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
|
72
|
+
def fetch_from_box
|
73
|
+
@inbox_connection = inbox
|
74
|
+
return nil if @inbox_connection.nil? || @inbox_connection == true
|
75
|
+
@mail_stack = @inbox_connection.find_items
|
76
|
+
return nil if @mail_stack.empty?
|
77
|
+
@mail_stack
|
78
|
+
end
|
79
|
+
|
80
|
+
def inbox
|
81
|
+
@incoming_folder = Rockdove::Config.incoming_folder
|
82
|
+
Viewpoint::EWS::Folder.get_folder_by_name(@incoming_folder)
|
83
|
+
rescue
|
84
|
+
reconnect_and_raise_error
|
85
|
+
end
|
86
|
+
|
87
|
+
def reconnect_and_raise_error
|
88
|
+
Rockdove.logger.info "Reconnecting to the Exchange Server & Fetching the mail now."
|
89
|
+
Rockdove::Config.connect
|
90
|
+
Viewpoint::EWS::Folder.get_folder_by_name(@incoming_folder)
|
91
|
+
rescue Viewpoint::EWS::EwsError
|
92
|
+
send_connection_failed_message
|
93
|
+
end
|
94
|
+
|
95
|
+
def send_connection_failed_message
|
96
|
+
Rockdove.logger.info "Rockdove unable to connect to the Exchange Server"
|
97
|
+
return nil
|
98
|
+
end
|
99
|
+
|
100
|
+
def process
|
101
|
+
@to_folder = Rockdove::Config.archive_folder
|
102
|
+
@mail_stack.each {|item| archive(item) }
|
103
|
+
@mail_stack = nil
|
104
|
+
end
|
105
|
+
|
106
|
+
def archive(item)
|
107
|
+
if @to_folder.blank?
|
108
|
+
item.delete!
|
109
|
+
Rockdove.logger.info "Rockdove delivered & deleted the mail."
|
110
|
+
else
|
111
|
+
item.move!(destination(@to_folder))
|
112
|
+
Rockdove.logger.info "Rockdove delivered & archived the mail."
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def destination(to_folder)
|
117
|
+
Viewpoint::EWS::Folder.get_folder_by_name(to_folder)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Rockdove
|
2
|
+
class Config
|
3
|
+
class << self
|
4
|
+
attr_accessor :url, :username, :password, :incoming_folder, :archive_folder, :watch_interval
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.configure(&block)
|
8
|
+
block.call(self)
|
9
|
+
connect
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.ews_url(value)
|
13
|
+
@url = value
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.ews_username(value)
|
17
|
+
@username = value
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.ews_password(value)
|
21
|
+
@password = value
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.ews_folder(value)
|
25
|
+
@incoming_folder = value || 'Inbox'
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.ews_archive_folder(value)
|
29
|
+
@archive_folder = value
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.ews_watch_interval(value)
|
33
|
+
@watch_interval = value || 60
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.connect
|
37
|
+
Viewpoint::EWS::EWS.endpoint = @url
|
38
|
+
Viewpoint::EWS::EWS.set_auth @username, @password
|
39
|
+
Viewpoint::EWS::EWS.instance
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rockdove'
|
2
|
+
require 'rails'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
dir = Pathname.new File.expand_path(File.dirname(__FILE__))
|
6
|
+
|
7
|
+
GENERATOR_PATH = dir + '../generators/rockdove'
|
8
|
+
|
9
|
+
module Rockdove
|
10
|
+
class Dovetie < Rails::Railtie
|
11
|
+
#initializer "dovetie.configure_rails_initialization" do |app|
|
12
|
+
# app.middleware.use Dovetie::Middleware
|
13
|
+
#end
|
14
|
+
generators do
|
15
|
+
require "#{GENERATOR_PATH}/install_generator.rb"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Rockdove
|
2
|
+
class EmailParser
|
3
|
+
HTML_REGEXP = /<\/?[^>]*>/
|
4
|
+
FWD_REGEXP = /^(On(.+)wrote:.+\z)$/nm
|
5
|
+
SIGNATURE_REGEXP = /^(Thanks(.+)Regards.+\z)$/inm
|
6
|
+
REPLY_REGEXP = /^(From:(.+)Sent:.+\z)$/nm
|
7
|
+
DASHES = "________________________________________"
|
8
|
+
|
9
|
+
def self.parse_mail(mail, body_type)
|
10
|
+
email_parser = new()
|
11
|
+
email_parser.parse_email_tags(mail,body_type)
|
12
|
+
end
|
13
|
+
|
14
|
+
def parse_email_tags(mail,body_type)
|
15
|
+
Rockdove.logger.info "Rockdove is parsing the mail content now..."
|
16
|
+
return nil unless mail
|
17
|
+
mail.gsub!(HTML_REGEXP, "").strip! if body_type == "HTML"
|
18
|
+
parsed_content = EmailReplyParser.parse_reply(mail)
|
19
|
+
content = choose_and_parse(parsed_content)
|
20
|
+
handle_outlook_extra_line_breaks(content)
|
21
|
+
end
|
22
|
+
|
23
|
+
def choose_and_parse(content)
|
24
|
+
content.gsub!(DASHES,"")
|
25
|
+
case content
|
26
|
+
when SIGNATURE_REGEXP
|
27
|
+
content.gsub(SIGNATURE_REGEXP,"").strip!
|
28
|
+
when REPLY_REGEXP
|
29
|
+
content.gsub(REPLY_REGEXP,"").strip!
|
30
|
+
when FWD_REGEXP
|
31
|
+
content.gsub(FWD_REGEXP,"").strip!
|
32
|
+
else
|
33
|
+
content
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def handle_outlook_extra_line_breaks(content)
|
38
|
+
if content && !(content.split("\n").first.include?(" "))
|
39
|
+
content.sub!("\n", "")
|
40
|
+
end
|
41
|
+
content
|
42
|
+
rescue
|
43
|
+
content
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Rockdove
|
2
|
+
|
3
|
+
class RockdoveCollection < Array
|
4
|
+
end
|
5
|
+
|
6
|
+
class ExchangeMail
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
def_delegators :@mail_item, :to_recipients, :date_time_created, :date_time_sent, :from
|
10
|
+
def_delegators :@mail_item, :subject, :body, :has_attachments?, :attachments
|
11
|
+
|
12
|
+
def initialize(mail_item)
|
13
|
+
@mail_item = mail_item
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_recipients
|
17
|
+
@mail_item.to_recipients.collect &:email_address if @mail_item.to_recipients
|
18
|
+
end
|
19
|
+
|
20
|
+
def cc_recipients
|
21
|
+
@mail_item.cc_recipients.collect &:email_address if @mail_item.cc_recipients
|
22
|
+
end
|
23
|
+
|
24
|
+
def from
|
25
|
+
@mail_item.from.email_address if @mail_item.from
|
26
|
+
end
|
27
|
+
|
28
|
+
def subject
|
29
|
+
@mail_item.subject
|
30
|
+
end
|
31
|
+
|
32
|
+
def body
|
33
|
+
parse_it(@mail_item.body, @mail_item.body_type) unless @mail_item.body.empty?
|
34
|
+
end
|
35
|
+
|
36
|
+
def attachments
|
37
|
+
@mail_item.attachments
|
38
|
+
end
|
39
|
+
|
40
|
+
def date_time_created
|
41
|
+
@mail_item.date_time_created
|
42
|
+
end
|
43
|
+
|
44
|
+
def date_time_sent
|
45
|
+
@mail_item.date_time_sent
|
46
|
+
end
|
47
|
+
|
48
|
+
def parse_it(mail_body, type)
|
49
|
+
Rockdove::EmailParser.parse_mail(mail_body, type)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# script/rockdove_server
|
2
|
+
#!/usr/bin/env ruby
|
3
|
+
|
4
|
+
# Run this file as ruby ./script/rockdove_server.rb start
|
5
|
+
|
6
|
+
require File.dirname(__FILE__) + '/../config/environment'
|
7
|
+
require "rubygems"
|
8
|
+
require "rockdove"
|
9
|
+
require "raad"
|
10
|
+
|
11
|
+
class RockdoveServer
|
12
|
+
def initialize
|
13
|
+
Rockdove::Config.configure do |config|
|
14
|
+
# (EDIT THIS CONTENT BELOW)
|
15
|
+
config.ews_url 'https://ewsdomain.com/ews/exchange.asmx'
|
16
|
+
config.ews_username 'ews_username'
|
17
|
+
config.ews_password 'ews_password'
|
18
|
+
config.ews_folder 'Inbox' # ews_folder is Inbox by default
|
19
|
+
config.ews_archive_folder 'Archive' # by default, it deletes the mail after processing, mention ews_archive_folder if it
|
20
|
+
# has to be archived to a different folder
|
21
|
+
config.ews_watch_interval 60 # by default, the polling interval is 60
|
22
|
+
end
|
23
|
+
#Raad.env = "production"
|
24
|
+
end
|
25
|
+
|
26
|
+
def start
|
27
|
+
Raad::Logger.debug("Rockdove Daemon started in #{Raad.env} env")
|
28
|
+
process_rockdove_job
|
29
|
+
end
|
30
|
+
|
31
|
+
def stop
|
32
|
+
Raad::Logger.debug("Rockdove Daemon stopped")
|
33
|
+
end
|
34
|
+
|
35
|
+
def process_rockdove_job
|
36
|
+
Rockdove::CollectMail.watch do |rockdove_parsed_mail|
|
37
|
+
begin
|
38
|
+
#Model.method(rockdove_parsed_mail) (EDIT THIS)
|
39
|
+
rescue Exception => e
|
40
|
+
Rockdove.logger.error "Exception occurred while receiving message:\n#{rockdove_parsed_mail}"
|
41
|
+
Rockdove.logger.error [e, *e.backtrace].join("\n")
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
data/lib/rockdove.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'viewpoint'
|
2
|
+
require 'email_reply_parser'
|
3
|
+
require "rockdove/version"
|
4
|
+
require "rockdove/config"
|
5
|
+
require "rockdove/exchange_mail"
|
6
|
+
require "rockdove/email_parser"
|
7
|
+
require "rockdove/collect_mail"
|
8
|
+
require "rockdove/dovetie" if defined?(Rails)
|
9
|
+
|
10
|
+
module Rockdove
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_accessor :logger
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# Enabling the logger for Rockdove to track its where-abouts during the delivery process of the mail.
|
18
|
+
#
|
19
|
+
|
20
|
+
def self.logger
|
21
|
+
@logger ||= Logger.new(STDOUT)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/rockdove.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "rockdove/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "rockdove"
|
7
|
+
s.version = Rockdove::VERSION
|
8
|
+
s.authors = ["Kiran Soumya"]
|
9
|
+
s.email = ["kiran.soumya@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/kiranh/rockdove"
|
11
|
+
s.summary = %q{Incoming mail processing daemon for Exchange Web Services (EWS)}
|
12
|
+
s.description = %q{Incoming mail processing daemon for Exchange Web Services (EWS). This is the Ruby Gem that does fetching, parsing and watching the mailbox for further processing.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "rockdove"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
s.add_development_dependency "guard-rspec"
|
24
|
+
s.add_development_dependency "rake", "~>0.9.2.2"
|
25
|
+
s.add_development_dependency "simplecov"
|
26
|
+
|
27
|
+
s.add_runtime_dependency "raad"
|
28
|
+
s.add_runtime_dependency "viewpoint", "= 0.1.26"
|
29
|
+
s.add_runtime_dependency "email_reply_parser"
|
30
|
+
s.add_runtime_dependency "logger"
|
31
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'spec_helper.rb'
|
2
|
+
|
3
|
+
describe "CollectParseArchive" do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
@connection = Rockdove::Config
|
7
|
+
@mail_retriever = Rockdove::CollectMail.new()
|
8
|
+
@test_server = Rockdove::EWS.new()
|
9
|
+
@log_stream = StringIO.new
|
10
|
+
Rockdove.stub!(:logger).and_return(Logger.new(@log_stream))
|
11
|
+
end
|
12
|
+
|
13
|
+
it "is trying to fetch a group of mails at one polling cycle" do
|
14
|
+
mails = []
|
15
|
+
mails << fetch_mail("new_mail") << fetch_mail("forwarded_mail") << fetch_mail("replied_mail")
|
16
|
+
handle_inbox(mails)
|
17
|
+
result = @mail_retriever.group_of_mails
|
18
|
+
result.should be_an_instance_of(Rockdove::RockdoveCollection)
|
19
|
+
result.count.should == 3
|
20
|
+
@log_stream.string.should include("Rockdove collected 3 mail(s)")
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should archive the mail at the end of collection cycle if user assigns ews move folder" do
|
24
|
+
mail = fetch_mail("new_mail")
|
25
|
+
handle_inbox(Array(mail))
|
26
|
+
@mail_retriever.group_of_mails.should be_an_instance_of(Rockdove::RockdoveCollection)
|
27
|
+
stub_destination_point
|
28
|
+
lambda { @mail_retriever.process }.should_not raise_error
|
29
|
+
@log_stream.string.should =~ /Rockdove delivered & archived the mail/
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should delete the mail at the end of collection cycle if user doesn't assign ews move folder" do
|
33
|
+
@connection.archive_folder = nil
|
34
|
+
mail = fetch_mail("new_mail")
|
35
|
+
handle_inbox(Array(mail))
|
36
|
+
@mail_retriever.group_of_mails.should be_an_instance_of(Rockdove::RockdoveCollection)
|
37
|
+
lambda { @mail_retriever.process }.should_not raise_error
|
38
|
+
@log_stream.string.should =~ /Rockdove delivered & deleted the mail/
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should parse the signature of the mail" do
|
42
|
+
mail = fetch_mail("mail_type_text")
|
43
|
+
parsed_content = Rockdove::EmailParser.parse_mail(mail.body, mail.body_type)
|
44
|
+
parsed_content.should == "This is a new post"
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should parse the forwarded mail of gmail" do
|
48
|
+
mail = fetch_mail("gmail_fwd_mail")
|
49
|
+
parsed_content = Rockdove::EmailParser.parse_mail(mail.body, mail.body_type)
|
50
|
+
parsed_content.should == "I get proper rendering as well."
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should watch for new mail when called" do
|
54
|
+
mail = fetch_mail("new_mail")
|
55
|
+
handle_inbox(Array(mail))
|
56
|
+
output = @mail_retriever.send_rockdove_to_watch_mail do |mail|
|
57
|
+
true
|
58
|
+
end
|
59
|
+
@log_stream.string.should include("Rockdove calling App block")
|
60
|
+
@log_stream.string.should include("Rockdove collected 1 mail(s)")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should handle undeliverable bounce type" do
|
64
|
+
mail = fetch_mail("auto_reply_mail")
|
65
|
+
handle_inbox(Array(mail))
|
66
|
+
@mail_retriever.group_of_mails.should be_an_instance_of(Rockdove::RockdoveCollection)
|
67
|
+
@log_stream.string.should include("Rockdove deleted this mail: Automatic reply: Out of Office Message")
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should handle auto reply bounce type" do
|
71
|
+
mail = fetch_mail("undeliverable_mail")
|
72
|
+
handle_inbox(Array(mail))
|
73
|
+
@mail_retriever.group_of_mails.should be_an_instance_of(Rockdove::RockdoveCollection)
|
74
|
+
@log_stream.string.should include("Rockdove deleted this mail: Undeliverable: New Post")
|
75
|
+
end
|
76
|
+
|
77
|
+
def handle_inbox(mail_array)
|
78
|
+
@mail_retriever.should_receive(:inbox).at_most(:twice).and_return(@test_server)
|
79
|
+
@test_server.should_receive(:find_items).at_most(:twice).and_return(mail_array)
|
80
|
+
@mail_retriever.retrieve_mail(mail_array.first).should be_an_instance_of(Rockdove::ExchangeMail)
|
81
|
+
end
|
82
|
+
|
83
|
+
def stub_destination_point
|
84
|
+
@mail_retriever.should_receive(:destination).and_return(@connection.archive_folder)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe "RockdoveLogs" do
|
89
|
+
it "should log" do
|
90
|
+
Rockdove.logger.should be_an_instance_of(Logger)
|
91
|
+
end
|
92
|
+
end
|