mail_runner 0.1.0 → 0.2.0
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 +4 -4
- data/README.md +137 -11
- data/lib/mail_runner/archive_manager.rb +107 -0
- data/lib/mail_runner/bot_helpers/helpers.rb +8 -2
- data/lib/mail_runner/bot_helpers/tests.rb +28 -8
- data/lib/mail_runner/cli.rb +1 -18
- data/lib/mail_runner/head_manager_bot.rb +27 -4
- data/lib/mail_runner/inbound_manager_bot.rb +9 -6
- data/lib/mail_runner/version.rb +1 -1
- data/mail_runner.gemspec +1 -0
- data/test/helper.rb +1 -0
- data/test/test_archivist.rb +90 -0
- data/test/test_assets/_1447427485391-46840973-67af09f8-7a989c0d_mixmax.com_.json +1 -0
- data/test/test_assets/archive_opts.json +7 -0
- data/test/test_assets/test_config.yml +13 -10
- data/test/test_assets/test_config_archive_cloud.yml +22 -0
- data/test/test_bot_helpers.rb +67 -0
- data/test/test_cli.rb +35 -1
- data/test/{test_mail_runner.rb → test_head_manager_bot.rb} +8 -8
- metadata +22 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ea440f11b72d87c1092c0d42120549bffbe0c997
|
4
|
+
data.tar.gz: 9ec7d59c90a08b354ccf904e3f21dec2372f9865
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a262d9ee7a41d399ac11ef6849ec6fe87dcb606b2ac53a423e916e87ede61120915f6d19c3918ee569af4d6342ab7caabcbf22ee968c18e089aa1cd6ba4e376c
|
7
|
+
data.tar.gz: fdb1fc94033e3cf60b00264700184eba923973aee4c93eacfdcc42d96e6b28fc69ec030fd69cf59849ac7d80e8bde43a9e9c85a6b3a88c4c8bb5f2d5d3e2ff6a
|
data/README.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
#mail_runner
|
2
|
+
|
3
|
+
[](https://badge.fury.io/rb/mail_runner) [](https://codeclimate.com/github/kert-io/mail_runner)
|
4
|
+
|
2
5
|
**WORK IN PROGRESS**. See Roadmap below for features not yet completed.
|
3
6
|
|
4
7
|
MailRunner acts as your mailman picking up your email from an [MTA](https://en.wikipedia.org/wiki/Message_transfer_agent), such as Postfix, and then delivering it directly to your app sending each email object as json to webhook. You can tell it to deliver locally or send it to any active webhook making it a functional mailserver for several apps.
|
@@ -105,28 +108,151 @@ keys | Value
|
|
105
108
|
If for any reason, mailrunner is not able to deliver the mail to the specified webhook, it will add it to the mailrunner mail queue to process later. The usual reason this occurs is the webhook is unresponsive, it returns an error code or the server is down. Mailrunner will intermittently test the server if this occurs and once it is working properly, it will process the queue. If mail is not being delivered, you can check the mailrunner log for details on what is happening on mailrunners end.
|
106
109
|
|
107
110
|
## Additional Options
|
108
|
-
|
111
|
+
###Daemonize
|
109
112
|
Use the `-d ` flag to turn mailrunner into a [daemon](https://en.wikipedia.org/wiki/Daemon_(computing)) & keep it running in the the background. When running as a daemon, be sure to set the logfile path using the ` -L ` flag.
|
110
113
|
|
111
|
-
|
112
|
-
Mailrunner wil output all logging info to STDOUT if no logfile path is set.
|
114
|
+
###Logging
|
115
|
+
Mailrunner wil output all logging info to STDOUT if no logfile path is set.
|
116
|
+
|
117
|
+
To set a logfile path, use the `-L path/to/logfile.log ` flag followed by the absolute path to the logfile location. If the file doesn't exist, it will be created, but the directory path must still be valid.
|
118
|
+
|
119
|
+
Mailrunner logging is designed for leaving it running continuously in the background. So the log doesn't become unusable, logs will automatically archive themselves in the same directory and start a new log each week.
|
113
120
|
|
114
|
-
|
115
|
-
|
121
|
+
###Archive
|
122
|
+
Alongside delivery to any webhook hook, Mailrunner also supports archiving your emails. Beyond archival purposes, this is super convenient for chasing down bugs, feature testing, and system error recovery. Mailrunner can archive locally or to a cloud storage device. Mailrunner currently supports AWS & Rackspace.
|
123
|
+
|
124
|
+
Use the Config file option when using the archive feature to simplify passing archive parameters to mailrunner. Archive option notes are included directly in the [sample config file](https://gist.github.com/kert-io/3d8d24d048dd25801b7f)
|
125
|
+
|
126
|
+
**File Naming**
|
127
|
+
All archived messages are saved as json and use the unique message-id as the file name. (NOTE: All illegal file system characters have been replaced with underscores; check the message-id in the message header section when seeking the actual id.)
|
128
|
+
|
129
|
+
When looking for an archived copy of a message, you must locate it by the message-id. To do so, run the id through the same name-scrubbing process used during the archive process: `/[#<$+%>!&*?=\/:@]/` are replaced with '_'.
|
130
|
+
|
131
|
+
###Config File
|
116
132
|
|
117
133
|
Mailrunner can also be launched with a config file storing all defaults in one place. When using a config file, you can launch a mailrunner instance with `mailrunner -c /path/to/config.yml` leaving off all typically required flags. [sample config file](https://gist.github.com/kert-io/3d8d24d048dd25801b7f)
|
118
134
|
|
119
135
|
When using a config file you can set your defaults in the config file but still override them for one-off instances using flags. The instance will launch according to the config file, but override only the options passed manually with each flag.
|
120
136
|
|
121
|
-
|
122
|
-
|
123
|
-
|
137
|
+
## Other usage Scenarios
|
138
|
+
###Monit
|
139
|
+
NOTE: before setting up Monit, test manually with desired config file settings.
|
140
|
+
|
141
|
+
I prefer to use [Monit](https://mmonit.com/monit/) to manage my worker bots. Why Monit? Because I like working with native linux config files when working at the system level. (Many others try to blend conventions and I find it leads to holes in seeing your system and its bugs completely.)
|
142
|
+
|
143
|
+
Monit will monitor your daemonized processes, restart them in case of failure and dutifully send you a notification or email with each action. It makes it possible to pretty much start and walk away. (psst, it does a ton more too!!)
|
144
|
+
|
145
|
+
You can tweak mailrunner to run with your preferred monitor, but I offer the monit setup here:
|
124
146
|
|
147
|
+
**step 1 - Configure Upstart to manage mailrunner at the system service level**
|
148
|
+
|
149
|
+
While monit monitors, it uses Upstart control processes directly. [Upstart](http://upstart.ubuntu.com) is the native linux process for starting and stopping processes and keeping track of all system processes.
|
150
|
+
|
151
|
+
* navigate to init folder where all Upstart config files are stored. Create a new .conf file for mailrunner & open it with text editor:
|
152
|
+
|
153
|
+
```sh
|
154
|
+
cd /etc/init
|
155
|
+
sudo touch mailrunner.conf
|
156
|
+
sudo vim mailrunner.conf
|
157
|
+
```
|
158
|
+
|
159
|
+
* Paste the following inside the .conf file. Substitute your username into the setuid & HOME variables in the User Variables section to suit your deployment and save.
|
160
|
+
* **setuid** - login name of user account you used when installing mailrunner.
|
161
|
+
* **HOME** - Note this is to locate your locally installed gems using rbenv. You can also modify uncomment th global install setup included as well.
|
162
|
+
|
163
|
+
```sh
|
164
|
+
# sudo start mailrunner
|
165
|
+
# sudo stop mailrunner
|
166
|
+
# sudo status mailrunner
|
167
|
+
#
|
168
|
+
# or use the service command:
|
169
|
+
# sudo service mailrunner {start,stop,restart,status}
|
170
|
+
|
171
|
+
description "Upstart control over MailRunner bots. From ruby gem mail_runner"
|
172
|
+
|
173
|
+
# no "start on", we don't want to automatically start
|
174
|
+
stop on (runlevel [06])
|
175
|
+
|
176
|
+
##########################################
|
177
|
+
# change to match your deployment user
|
178
|
+
setuid username
|
179
|
+
env HOME="/home/username"
|
180
|
+
############################################
|
181
|
+
script
|
182
|
+
# this script runs in /bin/sh by default
|
183
|
+
# respawn as bash so we can source in rbenv
|
184
|
+
exec /bin/bash <<'EOT'
|
185
|
+
|
186
|
+
# pull in system rbenv
|
187
|
+
# source /etc/profile.d/rbenv.sh
|
188
|
+
# or
|
189
|
+
# pull in user installed rbenv
|
190
|
+
export PATH="$HOME/.rbenv/bin:$PATH"
|
191
|
+
eval "$(rbenv init -)"
|
192
|
+
export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"
|
193
|
+
|
194
|
+
#launch mailrunner
|
195
|
+
exec mail_runner -c $HOME/mailrunner_config.yml
|
196
|
+
|
197
|
+
EOT
|
198
|
+
end script
|
199
|
+
```
|
200
|
+
|
201
|
+
* You can now start and stop mailrunner with the the following system level commands:
|
202
|
+
|
203
|
+
```sh
|
204
|
+
sudo start mailrunner
|
205
|
+
sudo stop mailrunner
|
206
|
+
sudo restart mailrunner
|
207
|
+
```
|
208
|
+
|
209
|
+
* NOTE: You must use the Mailrunner Config file to use Upstart and Monit. See Mailrunner config section above. This Upstart config assumes you store the mailrunner_config.yml file in your home directory.
|
210
|
+
|
211
|
+
**step 2 - Install and set up Monit to monitor & manage Mailrunner**
|
212
|
+
|
213
|
+
* Install Monit. see this [gist]() for guidance
|
214
|
+
|
215
|
+
With Monit installed, it is easy to set it up to monitor Mailrunner. Monit stores the config files for each process it monitors in the conf.d folder.
|
216
|
+
|
217
|
+
* navigate to conf.d folder. Create a new conf file for mailrunner & open it with text editor:
|
218
|
+
|
219
|
+
```sh
|
220
|
+
cd /etc/monit/conf.d
|
221
|
+
sudo touch mailrunner
|
222
|
+
sudo vim mailrunner
|
223
|
+
```
|
224
|
+
* Paste the following inside the .conf file & save. You can modify the triggers to suit your workload and resource allocation, but best to just let it run and watch it for a while and fine tune it for you setup.
|
225
|
+
|
226
|
+
```sh
|
227
|
+
# mailrunner
|
228
|
+
check process mailrunner
|
229
|
+
with matching mail_runner
|
230
|
+
|
231
|
+
start program = "/bin/bash -c 'start mailrunner'"
|
232
|
+
stop program = "/bin/bash -c 'stop mailrunner'"
|
233
|
+
|
234
|
+
#process monitor triggers
|
235
|
+
if cpu is greater than 10% for 2 cycles then alert
|
236
|
+
if mem is greater than 3% for 1 cycles then restart
|
237
|
+
if 3 restarts within 5 cycles then timeout
|
238
|
+
```
|
239
|
+
|
240
|
+
* reload monit settings to pull in your new config
|
241
|
+
|
242
|
+
```sh
|
243
|
+
sudo monit reload
|
244
|
+
```
|
245
|
+
* Start mailrunner using monit. Check status to see it initializing
|
246
|
+
|
247
|
+
```sh
|
248
|
+
sudo monit start mailrunner
|
249
|
+
sudo monit status
|
250
|
+
```
|
251
|
+
* Monit has a ton more features like alerts & notifications. I highly recommend setting these up and monit will let you know whenever it needs your attention. Now, relax;-)
|
252
|
+
|
253
|
+
|
125
254
|
#Roadmap
|
126
|
-
* Archiving
|
127
|
-
* Run from Config file
|
128
255
|
* Single bot managing several mailboxes and webhooks
|
129
|
-
* Run bots from config file
|
130
256
|
* test server
|
131
257
|
|
132
258
|
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module MailRunner
|
2
|
+
module ArchivistBot
|
3
|
+
|
4
|
+
def self.add_to_archive_stack(msg, archive)
|
5
|
+
que_packet = [msg, archive]
|
6
|
+
$redis.lpush("archive_stack", que_packet.to_json)
|
7
|
+
$logger.info("Archivist") { "#add_to_archive_stack:: email added to queue for processing later." }
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.stack_height
|
11
|
+
return $redis.llen("archive_stack")
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.archive_stack
|
15
|
+
$logger.info("Archivist") { "Stack Height:: #{stack_height}"}
|
16
|
+
while stack_height > 0
|
17
|
+
$logger.info("Archivist") { "#archive_stack:: Processing stack item:: #{stack_height}"}
|
18
|
+
msg, archive = pop_from_stack
|
19
|
+
|
20
|
+
if archive["destination"] == 'local'
|
21
|
+
deliver_to_local_archive(msg, archive)
|
22
|
+
else
|
23
|
+
deliver_to_cloud_archive(msg, archive)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.pop_from_stack
|
29
|
+
key, que_packet = $redis.blpop("archive_stack", :timeout => 5) #timeout needed for MockRedis in Testing Env.
|
30
|
+
$logger.info("Archivist") { "#item popped from stack for processing"}
|
31
|
+
data = JSON::parse(que_packet)
|
32
|
+
msg = data[0]
|
33
|
+
archive = data[1]
|
34
|
+
return msg, archive
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.deliver_to_local_archive(msg, archive_opts)
|
38
|
+
begin
|
39
|
+
filename = name_msg(msg)
|
40
|
+
target = File.open("#{archive_opts["local_archive"]}/#{filename}", 'w')
|
41
|
+
target.write(msg)
|
42
|
+
target.close()
|
43
|
+
$logger.info("Archivist") { "#Message archived to local"}
|
44
|
+
rescue => e
|
45
|
+
$logger.error("Archivist") { "#deliver_to_local_archive:: failed: #{e.inspect}"}
|
46
|
+
add_to_archive_stack(msg,archive_opts)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.deliver_to_cloud_archive(msg, archive_opts)
|
51
|
+
attempts = 0
|
52
|
+
begin
|
53
|
+
archive = establish_archive_link(archive_opts)
|
54
|
+
filename = name_msg(msg)
|
55
|
+
mimetype = "application/json"
|
56
|
+
|
57
|
+
saved = archive.files.create :key => filename, :body => msg, :Content_type => mimetype
|
58
|
+
$logger.info("Archivist") { "#Message archived to cloud"}
|
59
|
+
return saved #explicit for testing
|
60
|
+
rescue => e
|
61
|
+
$logger.error("Archivist") { "#deliver_to_cloud_archive:: attempt #{attempts} failed: #{e.inspect}"}
|
62
|
+
attempts += 1
|
63
|
+
retry unless attempts > 1
|
64
|
+
unless saved
|
65
|
+
$logger.info("Archivist") { "Too many failed attempts, restacking msg."}
|
66
|
+
add_to_archive_stack(msg,archive_opts)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.name_msg(msg)
|
72
|
+
msg_id = JSON.parse(msg)[0]["msg"]["headers"]["Message-ID"]
|
73
|
+
return "#{msg_id.gsub(/[#<$+%>!&*?=\/:@]/,'_')}.json"#clean illegal charaters
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.establish_archive_link(archive_opts)
|
77
|
+
service = new_storage_object(archive_opts)
|
78
|
+
dir_name = archive_opts["directory"]
|
79
|
+
archive_directory = service.directories.new :key => dir_name
|
80
|
+
return archive_directory
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.new_storage_object(archive_opts)
|
84
|
+
options = format_options(archive_opts)
|
85
|
+
Fog::Storage.new(options)
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.format_options(archive_opts)
|
89
|
+
provider = archive_opts["provider"].downcase
|
90
|
+
|
91
|
+
options = {
|
92
|
+
:provider => archive_opts["provider"]
|
93
|
+
}
|
94
|
+
if provider == "rackspace"
|
95
|
+
options[:"#{provider}_username"] = archive_opts["username"]
|
96
|
+
options[:"#{provider}_api_key"] = archive_opts["api_key"]
|
97
|
+
options[:"#{provider}_region"] = archive_opts["region"]
|
98
|
+
elsif provider == "aws"
|
99
|
+
options[:"#{provider}_access_key_id"] = archive_opts["api_key"]
|
100
|
+
options[:"#{provider}_secret_access_key"] = archive_opts["secret_key"]
|
101
|
+
end
|
102
|
+
|
103
|
+
return options
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
@@ -7,8 +7,14 @@ module BotHelpers
|
|
7
7
|
def self.print_monitoring_started_msg(bot)
|
8
8
|
$logger.info("Helpers") { "mailbox: #{bot.mailbox}" }
|
9
9
|
$logger.info("Helpers") { "path: #{bot.webhook}"}
|
10
|
-
|
11
|
-
|
10
|
+
unless bot.archive.nil?
|
11
|
+
if bot.archive[:destination] == 'cloud'
|
12
|
+
$logger.info("Helpers") {"archive: #{bot.archive[:provider]} :: #{bot.archive[:directory]}"}
|
13
|
+
else
|
14
|
+
$logger.info("Helpers") {"archive: #{bot.archive[:destination]} :: #{bot.archive[:local_archive]}"}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
puts "Getter Bot is on the Job!"
|
12
18
|
end
|
13
19
|
|
14
20
|
def self.convert_raw_mail_to_json(mail)
|
@@ -1,14 +1,10 @@
|
|
1
1
|
module BotHelpers
|
2
2
|
module Tests #series of initial command validation tests on launch.
|
3
|
-
|
3
|
+
|
4
|
+
def self.all_args_included?(args)
|
4
5
|
if args[:mailbox].nil? or args[:webhook].nil?
|
5
6
|
raise ArgumentError, 'You must include mailbox & webhook minimum. Archive argument is optional. Add -h to see help.'
|
6
7
|
end
|
7
|
-
#if args.size > 3
|
8
|
-
# raise ArgumentError, 'You can only include mailbox, webhook & Archive argument. 3 max! Add -h to see help.'
|
9
|
-
#end
|
10
|
-
#?test format of mailbox?
|
11
|
-
#? test valid webhook format?
|
12
8
|
end
|
13
9
|
|
14
10
|
def self.test_mailbox (path)
|
@@ -21,7 +17,7 @@ module BotHelpers
|
|
21
17
|
begin
|
22
18
|
response = RestClient.head url
|
23
19
|
MailRunner.manager_bot.update_webhook_status("live")
|
24
|
-
rescue
|
20
|
+
rescue
|
25
21
|
raise ArgumentError, "ERROR: \nMake sure the server is running and the webhook exists.\nNOTE: Server must respond to http HEAD method.\nSee README.md for proper setup.\n"
|
26
22
|
end
|
27
23
|
unless response.code == 200
|
@@ -38,6 +34,30 @@ module BotHelpers
|
|
38
34
|
end
|
39
35
|
end
|
40
36
|
|
41
|
-
|
37
|
+
def self.test_archive(a_set)
|
38
|
+
if a_set[:destination] == 'local'
|
39
|
+
test_local_archive(a_set)
|
40
|
+
elsif a_set[:destination] == 'cloud'
|
41
|
+
test_cloud_archive_connection(a_set)
|
42
|
+
else
|
43
|
+
raise ArgumentError, "ERROR: Archive destination setting invalid."
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.test_local_archive(a_set)
|
48
|
+
unless File.directory?(a_set[:local_archive])
|
49
|
+
raise ArgumentError, "ERROR: Invalid local archive path."
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.test_cloud_archive_connection(a_set)
|
54
|
+
a_set = JSON.parse(a_set.to_json) #Must parse as json, similar to redis queues. Stringifies symbol keys.
|
55
|
+
begin
|
56
|
+
response = MailRunner::ArchivistBot.establish_archive_link(a_set)
|
57
|
+
rescue => e
|
58
|
+
raise ArgumentError, "ERROR: Archive connection failed. Check your archive config options or disable."
|
59
|
+
end
|
60
|
+
end
|
42
61
|
|
62
|
+
end
|
43
63
|
end
|
data/lib/mail_runner/cli.rb
CHANGED
@@ -26,7 +26,7 @@ module MailRunner
|
|
26
26
|
opts[:webhook] = arg
|
27
27
|
end
|
28
28
|
|
29
|
-
o.on '-a', '--archive', "
|
29
|
+
o.on '-a', '--archive', "Use config file to setup archive option." do |arg|
|
30
30
|
opts[:archive] = arg
|
31
31
|
end
|
32
32
|
|
@@ -93,23 +93,6 @@ module MailRunner
|
|
93
93
|
def self.set_globals
|
94
94
|
MailRunner.set_globals
|
95
95
|
end
|
96
|
-
|
97
|
-
def self.banner
|
98
|
-
%q{
|
99
|
-
m,
|
100
|
-
`$b
|
101
|
-
.ss, $$: .,d$
|
102
|
-
`$$P,d$P' .,md$P"'
|
103
|
-
,$$$$$bmmd$$$P^'
|
104
|
-
.d$$$$$$$$$$P'
|
105
|
-
$$^' `"^$$$' ____ _ _ _ _
|
106
|
-
$: ,$$: / ___|(_) __| | ___| | _(_) __ _
|
107
|
-
`b :$$ \___ \| |/ _` |/ _ \ |/ / |/ _` |
|
108
|
-
$$: ___) | | (_| | __/ <| | (_| |
|
109
|
-
$$ |____/|_|\__,_|\___|_|\_\_|\__, |
|
110
|
-
.d$$ |_|
|
111
|
-
}
|
112
|
-
end
|
113
96
|
end
|
114
97
|
end
|
115
98
|
|
@@ -3,6 +3,7 @@ require 'json'
|
|
3
3
|
require 'base64'
|
4
4
|
require 'rest-client'
|
5
5
|
require 'redis'
|
6
|
+
require 'fog'
|
6
7
|
|
7
8
|
module MailRunner
|
8
9
|
|
@@ -24,7 +25,7 @@ module MailRunner
|
|
24
25
|
def initialize
|
25
26
|
@mailbox = nil
|
26
27
|
@webhook = nil
|
27
|
-
@archive =
|
28
|
+
@archive = nil
|
28
29
|
@webhook_status = nil
|
29
30
|
end
|
30
31
|
|
@@ -39,7 +40,7 @@ module MailRunner
|
|
39
40
|
|
40
41
|
@mailbox = "/var/mail/#{opts[:mailbox]}"
|
41
42
|
@webhook = opts[:webhook]
|
42
|
-
@archive = opts[:archive]
|
43
|
+
@archive = opts[:archive].nil? ? nil : opts[:archive]
|
43
44
|
end
|
44
45
|
|
45
46
|
def update_webhook_status(status)
|
@@ -47,8 +48,9 @@ module MailRunner
|
|
47
48
|
end
|
48
49
|
|
49
50
|
def test_options
|
50
|
-
|
51
|
-
|
51
|
+
BotHelpers::Tests.test_mailbox(self.mailbox)
|
52
|
+
BotHelpers::Tests.test_webhook(self.webhook)
|
53
|
+
BotHelpers::Tests.test_archive(self.archive) unless self.archive.nil?
|
52
54
|
end
|
53
55
|
|
54
56
|
def run
|
@@ -64,10 +66,15 @@ module MailRunner
|
|
64
66
|
delegate_delayed_queue_processing
|
65
67
|
end
|
66
68
|
|
69
|
+
if archive_stack?
|
70
|
+
delegate_archive_stack_processing
|
71
|
+
end
|
72
|
+
|
67
73
|
sleep 5
|
68
74
|
end
|
69
75
|
end
|
70
76
|
|
77
|
+
#### Delegation Methods ####
|
71
78
|
|
72
79
|
def inbound_manager
|
73
80
|
MailRunner::InboundManagerBot
|
@@ -97,6 +104,22 @@ module MailRunner
|
|
97
104
|
puts msg.inspect
|
98
105
|
end
|
99
106
|
end
|
107
|
+
|
108
|
+
def archivist
|
109
|
+
MailRunner::ArchivistBot
|
110
|
+
end
|
111
|
+
|
112
|
+
def archive_stack?
|
113
|
+
archivist.stack_height > 0
|
114
|
+
end
|
115
|
+
|
116
|
+
def delegate_archive_stack_processing
|
117
|
+
begin
|
118
|
+
archivist.archive_stack
|
119
|
+
rescue Exception => msg
|
120
|
+
puts msg.inspect
|
121
|
+
end
|
122
|
+
end
|
100
123
|
|
101
124
|
end
|
102
125
|
end
|
@@ -2,7 +2,7 @@ module MailRunner
|
|
2
2
|
|
3
3
|
module InboundManagerBot
|
4
4
|
|
5
|
-
def self.process_inbound(mailbox, webhook, archive)
|
5
|
+
def self.process_inbound(mailbox, webhook, archive=nil)
|
6
6
|
raw_mail = get_mail(mailbox)
|
7
7
|
|
8
8
|
unless raw_mail.nil?
|
@@ -13,15 +13,18 @@ module MailRunner
|
|
13
13
|
json_packet = BotHelpers::Helpers.convert_raw_mail_to_json(mail)
|
14
14
|
|
15
15
|
deliver_mail(webhook, json_packet)
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
|
17
|
+
#Mail archived regardless of delivery errors.
|
18
|
+
#Delayed mail processed by queue manager, so only archived once on initial pickup.
|
19
|
+
if archive
|
20
|
+
MailRunner::ArchivistBot.add_to_archive_stack(json_packet, archive)
|
19
21
|
end
|
20
|
-
|
21
22
|
end
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
26
|
+
###Delegation Methods###
|
27
|
+
|
25
28
|
def self.get_mail(mailbox)
|
26
29
|
BotHelpers::Runner.get_contents_from_mailbox(mailbox)
|
27
30
|
end
|
@@ -40,6 +43,6 @@ module MailRunner
|
|
40
43
|
end
|
41
44
|
end
|
42
45
|
|
43
|
-
end
|
44
46
|
|
47
|
+
end
|
45
48
|
end
|
data/lib/mail_runner/version.rb
CHANGED
data/mail_runner.gemspec
CHANGED
data/test/helper.rb
CHANGED
@@ -0,0 +1,90 @@
|
|
1
|
+
#####################
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
######################
|
6
|
+
require_relative 'helper'
|
7
|
+
|
8
|
+
class TestArchivist < Minitest::Test
|
9
|
+
|
10
|
+
####### Top Level Call Tests ##########
|
11
|
+
describe"Archivist::stack_height" do
|
12
|
+
before do
|
13
|
+
@archivist = MailRunner::ArchivistBot
|
14
|
+
end
|
15
|
+
it "returns an integer" do
|
16
|
+
assert_instance_of Fixnum, @archivist.stack_height
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "Archivist::deliver_to_local_archive" do
|
21
|
+
before do
|
22
|
+
`mkdir /tmp/local_test_archive`
|
23
|
+
$redis.del("archive_stack") #zero list
|
24
|
+
@archivist = MailRunner::ArchivistBot
|
25
|
+
@filename = "_1447427485391-46840973-67af09f8-7a989c0d_mixmax.com_.json"
|
26
|
+
@msg = File.read("#{Dir[File.dirname(__FILE__)][0] + '/test_assets/' + @filename}")
|
27
|
+
@archive_opts = {"local_archive" => '/tmp/local_test_archive'}
|
28
|
+
end
|
29
|
+
after do
|
30
|
+
`rm -rf /tmp/local_test_archive`
|
31
|
+
end
|
32
|
+
context 'when local path is valid' do
|
33
|
+
it "successfully adds msg to archive" do
|
34
|
+
assert_silent { @archivist.deliver_to_local_archive(@msg, @archive_opts) }
|
35
|
+
assert_equal true, File.file?("#{@archive_opts["local_archive"]}" + "/" + "#{@filename}")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when local path is invalid' do
|
40
|
+
it "adds msg to archive_stack" do
|
41
|
+
@archivist.deliver_to_local_archive(@msg, "/home/invalid_path")
|
42
|
+
assert_equal 1, $redis.llen('archive_stack')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "Archivist::deliver_to_cloud_archive" do
|
48
|
+
before do
|
49
|
+
$redis.del("archive_stack") #zero list
|
50
|
+
@archive_opts = JSON.parse(File.read("#{Dir[File.dirname(__FILE__)][0] + '/test_assets/archive_opts.json'}"))[0]
|
51
|
+
@msg = File.read("#{Dir[File.dirname(__FILE__)][0] + '/test_assets/_1447427485391-46840973-67af09f8-7a989c0d_mixmax.com_.json'}")
|
52
|
+
@archivist = MailRunner::ArchivistBot
|
53
|
+
#set for Mocking only.
|
54
|
+
Fog.mock!
|
55
|
+
#Must create fake directory in mock stub
|
56
|
+
service = @archivist.new_storage_object(@archive_opts)
|
57
|
+
service.directories.create :key => 'raw_msg_archive'
|
58
|
+
end
|
59
|
+
|
60
|
+
context "when cloud archive credentials valid" do
|
61
|
+
it "successfully adds msg to archive" do
|
62
|
+
assert_instance_of Fog::Storage::Rackspace::File, @archivist.deliver_to_cloud_archive(@msg, @archive_opts)
|
63
|
+
assert_equal 0, $redis.llen('archive_stack') #doesn't hit stack
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "When cloud archive credentials invalid" do
|
68
|
+
before do
|
69
|
+
@archive_opts["api_key"] = nil
|
70
|
+
@archivist.deliver_to_cloud_archive(@msg, @archive_opts)
|
71
|
+
end
|
72
|
+
it "adds msg to archive_stack" do
|
73
|
+
assert_equal 1, $redis.llen('archive_stack')
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
describe 'Archivist::establish_archive_link' do
|
79
|
+
it 'Test included under: test BotHelpers:: Tests :: archive_tests' do
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe 'Archivist::format_options' do
|
84
|
+
it 'Test included under: test BotHelpers:: Tests :: archive_tests' do
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
########################################
|
89
|
+
|
90
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
[{"msg":{"raw_msg":"Return-Path: <Coming Soon_account@gmail.com>\r\nReceived: from Coming Soon.domain.co (localhost [127.0.0.1]) by mail.Coming Soon.domain.co (Postfix) with ESMTP id A11E62202F2 for <test@Coming Soon.domain.co>; Fri, 13 Nov 2015 10:11:33 -0500\r\nReceived: by mail.Coming Soon.domain.co (Postfix, from userid 1001) id 886C222199E; Fri, 13 Nov 2015 10:11:33 -0500\r\nReceived: from mail-qk0-f175.google.com (mail-qk0-f175.google.com [209.85.220.175]) by mail.Coming Soon.domain.co (Postfix) with ESMTPS id EBFDB22F2 for <test@Coming Soon.domain.co>; Fri, 13 Nov 2015 10:11:26 -0500\r\nReceived: by qkas77 with SMTP id s77so494528qka.0 for <test@Coming Soon.domain.co>; Fri, 13 Nov 2015 07:11:26 -0800\r\nReceived: from [127.0.0.1] (ec2-54-85-60-240.compute-1.amazonaws.com. [54.85.60.240]) by smtp.gmail.com with ESMTPSA id 31sm5383325qgy.13.2015.11.13.07.11.25 for <test@Coming Soon.domain.co> (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 13 Nov 2015 07:11:25 -0800\r\nDate: Fri, 13 Nov 2015 15:11:25 +0000\r\nFrom: Coming Soon_account@gmail.com>\r\nTo: test <test@Coming Soon.domain.co>\r\nMessage-ID: <1447427485391-46840973-67af09f8-7a989c0d@mixmax.com>\r\nSubject: Local Archive\r\nMime-Version: 1.0\r\nContent-Type: multipart/alternative;\r\n boundary=\"----sinikael-?=_1-14474274854200.5982260059099644\";\r\n charset=UTF-8\r\nContent-Transfer-Encoding: 7bit\r\nX-Original-To: test@Coming Soon.domain.co\r\nDelivered-To: talkt@Coming Soon.domain.co\r\nAuthentication-Results: mail.Coming Soon.domain.co; dkim=pass reason=\"2048-bit key;\r\n unprotected key\" header.d=gmail.com header.i=@gmail.com header.b=bkC0SRlC;\r\n dkim-adsp=pass; dkim-atps=neutral\r\nX-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on dev.fake.co\r\nX-Spam-Level: \r\nX-Spam-Status: No, score=0.8 required=5.0 tests=FREEMAIL_FROM,\r\n HTML_IMAGE_RATIO_02,HTML_MESSAGE,MIME_HTML_MOSTLY,RCVD_IN_MSPIKE_H2,\r\n T_DKIM_INVALID,URIBL_BLOCKED autolearn=no autolearn_force=no version=3.4.0\r\nDKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113;\r\n h=content-type:from:to:subject:message-id:date:mime-version;\r\n bh=OvFWnVTuQzHtF6tDRv5J3zWMNR04gofxsqdKPQBrJOA=;\r\n b=bkC0SRlCOMx0TAfP1Xo6YxpFkUtwEO/cSPiZUE9MNXyKEx6EgMOkHpNJoNuwUrQDZx\r\n NkfERwLbmeP8qTmikvrbBnsDDg6QWQX05T99B5QSf7LOJ511eorgvrnP5rPrFdhDq4EK\r\n JwjTKDvFuCSHtx3WUlvo0Gw9rqrQRGNRjUTNSU1Rc5oRIZZdspPuFVkL7lsSnVGx0j5B\r\n Jh2Qq93bnh1FOTJ+xKCIRGSGuYcohkumwV8J5dmaebTPkUv56wj+E5T3mkLFN8zPiKz1\r\n kFlKq1CsB4XcFnSs48LEbswsAhXstUYbYFtZqwXWrDohvBIoLMlRN2gdGanMXP2iRA64 XCrg==\r\nX-Received: by 10.140.16.21 with SMTP id 21mr22446073qga.87.1447427486385;\r\n Fri, 13 Nov 2015 07:11:26 -0800 (PST)\r\nX-Mailer: Mixmax (mixmax.com)\r\nX-Virus-Scanned: ClamAV using ClamSMTP\r\n\r\n\r\n------sinikael-?=_1-14474274854200.5982260059099644\r\nContent-Type: text/plain;\r\n charset=UTF-8\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\nLocal Arhive\r\n\r\n\r\nSent with Mixmax=\r\n\r\n------sinikael-?=_1-14474274854200.5982260059099644\r\nContent-Type: text/html;\r\n charset=UTF-8\r\nContent-Transfer-Encoding: quoted-printable\r\n\r\n\r\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://ww=\r\nw.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\r\n\r\n<html xmlns=3D\"http://www.w3.org/1999/xhtml\" xmlns:v=3D\"urn:schemas-micro=\r\nsoft-com:vml\" xmlns:o=3D\"urn:schemas-microsoft-com:office:office\">\r\n\r\n <head>\r\n <meta name=3D\"viewport\" content=3D\"width=3Ddevice-width, initial-scal=\r\ne=3D1.0\">\r\n =\r\n\r\n =\r\n\r\n <!--[if gte mso 9]>\r\n <xml>\r\n <o:OfficeDocumentSettings>\r\n <o:AllowPNG/>\r\n <o:PixelsPerInch>96</o:PixelsPerInch>\r\n </o:OfficeDocumentSettings>\r\n </xml>\r\n <![endif]-->\r\n =\r\n\r\n =\r\n\r\n <style type=3D\"text/css\">* a:hover{cursor:pointer;}</style>\r\n =\r\n\r\n\r\n <style>body {-webkit-animation:bugfix infinite 1s;}@-webkit-keyframes=\r\n bugfix {from {position:relative;}to {position:relative;}}</style>\r\n\r\n </head>\r\n\r\n <body yahoo=3D\"fix\" style=3D\"word-wrap:normal; word-break:break-word;\"=\r\n>\r\n\r\n =\r\n\r\n <!--[if (gte mso 9)|(IE)]>\r\n <style>a,body {font-family:'Calibri',Arial,sans-serif;}img {border:=\r\nnone !important;-ms-interpolation-mode:bicubic;}td {mso-line-height-rule:=\r\nexactly !important;}table.border-outer {margin-bottom:15px !important;}.b=\r\norder-middle,.border-inner {width:100%;}td.mso-card-inner table {border-c=\r\nollapse:collapse !important;mso-table-lspace:0pt;mso-table-rspace:0pt;ver=\r\ntical-align:top;}.border-outer,.border-middle,.border-inner {border:none =\r\n!important;}.mso-border-outer,.mso-border-middle,.mso-border-inner {paddi=\r\nng:1px;}.mso-border-outer {background-color:rgb(245,255,255);}.mso-border=\r\n-middle {background-color:rgb(223,246,255);}.mso-border-inner {background=\r\n-color:rgb(153,176,225);}.preview-card {margin-bottom:0 !important;paddin=\r\ng:0 !important;}.outlook-only {display:block !important;max-height:none !=\r\nimportant;overflow:visible !important;}.outlook-com-only {display:none !i=\r\nmportant;font-size:0 !important;}#mso-one-whole {width:100% !important;}<=\r\n/style>\r\n <![endif]-->\r\n =\r\n\r\n =\r\n\r\n <style>a {word-wrap:normal;word-break:break-word;}@media only screen =\r\nand (max-width:600px) {.container[not-yahoo] {-webkit-text-size-adjust:no=\r\nne !important;}.container[not-yahoo],.container[not-yahoo] .palm-one-whol=\r\ne {width:100% !important;min-width:100% !important;}.container[not-yahoo]=\r\n .palm-one-half {width:50% !important;min-width:50% !important;box-sizing=\r\n:border-box;}blockquote table[lang=3D\"container\"],blockquote table[lang=3D=\r\n\"container\"] div,blockquote table[lang=3D\"container\"] table {width:auto !=\r\nimportant;min-width:0 !important;position:relative !important;}img {max-w=\r\nidth:100%;}[class=3D\"border-outer\"],[class=3D\"border-middle\"],[class=3D\"b=\r\norder-inner\"],[title=3D\"separator\"],[class=3D\"inner\"] {width:100% !import=\r\nant;}[class=3D\"innercell\"] {padding:8px !important;}.palm-block {display:=\r\nblock !important;}.container[not-yahoo] td.palm-one-whole {display:inline=\r\n-block !important;padding:0;}.container[not-yahoo] td.palm-one-whole:firs=\r\nt-child:not(:only-child) {margin-bottom:16px;}td.hostname {padding-top:3p=\r\nx !important;}}@media only screen and (min-width:601px) {.preview-card {m=\r\nax-width:600px !important;}}@media only screen and (min-device-width :320=\r\npx) and (max-device-width :568px),only screen and (min-device-width :768p=\r\nx) and (max-device-width :1024px),only screen and (max-device-width:640px=\r\n),only screen and (max-device-width:667px),only screen and (max-width:480=\r\npx){table[class=3D\"container\"] {width:100% !important;min-width:100% !imp=\r\nortant;}.p,.small,li,font[size=3D\"2\"],font[size=3D\"3\"] {font-size:17px !i=\r\nmportant;line-height:1.5 !important;}audio {margin-bottom:10px;}.backgrou=\r\nnd-contain {background-size:contain;}}@media only screen and (min-device-=\r\nwidth :320px) and (max-device-width :568px),only screen and (min-device-w=\r\nidth :768px) and (max-device-width :1024px),only screen and (min-device-w=\r\nidth :1224px) {.container[not-yahoo] .message-wrapper {padding-top:6px;}=\r\n.container[not-yahoo] .apple-only[style] {display:block !important;max-he=\r\night:none !important;line-height:normal !important;overflow:visible !impo=\r\nrtant;height:auto !important;width:100% !important;position:relative !imp=\r\nortant;}.ExternalClass .ecxapple-only {display:none !important;}.containe=\r\nr[not-yahoo] .no-apple {display:none !important;}.container[not-yahoo] .n=\r\no-apple {display:block;}.container[not-yahoo] form {font-size:inherit;}.c=\r\nontainer[not-yahoo] form input[type=3D\"text\"] {height:43px;padding-left:4=\r\npx !important;}.container[not-yahoo] form button:hover {cursor:pointer;}@=\r\nmedia only screen and (min-device-width :1224px) {.apple-mail-form {disp=\r\nlay:block !important;background-color:white !important;}}}</style>\r\n =\r\n\r\n =\r\n\r\n <style>.ExternalClass {width:100%;}.ExternalClass .outlook-com-button=\r\n {display:block;}.ExternalClass button {height:auto;}.ExternalClass .outl=\r\nook-com-hidden {display:none !important;}.ExternalClass .outlook-com-only=\r\n {display:block !important;max-height:none !important;line-height:normal =\r\n!important;overflow:visible !important;height:auto !important;width:100% =\r\n!important;position:relative !important;}</style>\r\n =\r\n\r\n\r\n <table class=3D\"container\" lang=3D\"container\" not-yahoo=3D\"fix\" borde=\r\nr=3D\"0\" cellpadding=3D\"0\" cellspacing=3D\"0\" valign=3D\"top\" style=3D\"width=\r\n:100%; margin-top:6px;\">\r\n <tr>\r\n <td valign=3D\"top\" class=3D\"message-wrapper\" style=3D\"line-height=\r\n: 1.31; color: #222; font-family: arial, sans-serif;\">\r\n\r\n <!--[if mso]><table border=3D\"0\" cellpadding=3D\"0\" cellspacing=\r\n=3D\"0\" valign=3D\"top\" style=3D\"border-collapse:separate;\"><tr><td valign=3D=\r\n\"top\"><![endif]-->\r\n <div>Local Arhive</div><div><br></div><div><br></div><div><br=\r\n></div><div>\r\n <div class=3D\"signature\" style=3D\"color:#999999;\">\r\n Sent with <a target=3D\"_blank\" href=3D\"https://mixmax.com/s/LDQhPf8Ft=\r\n5nm5ccBu?utm_source=3Dmixmax&utm_medium=3Demail&utm_campaign=3Dsi=\r\ngnature_link&utm_content=3Dsent_with_mixmax\">Mixmax</a>\r\n =\r\n\r\n </div>\r\n =\r\n\r\n</div><img width=3D\"0\" height=3D\"0\" style=3D\"border:0; width:0px; height:=\r\n0px;\" src=3D\"https://app.mixmax.com/api/track/v2/eoHCRmlPtUqTlCTRt/i02bj5=\r\nCbpFWbnBEdy9GcrN2byBnI/gIvNmL1JXdrF2cuQ3cvBHQ0NXZ09VdyV3ahNnI/ISe0lmb11Wb=\r\nvNEI0NXZ09VdyV3ahNnI\" alt=3D\"\">\r\n <!--[if mso]></td></tr></table><![endif]-->\r\n </td>\r\n </tr>\r\n </table>\r\n\r\n </body>\r\n</html>\r\n\r\n------sinikael-?=_1-14474274854200.5982260059099644--\r\n","headers":{"Received":"from [127.0.0.1] (ec2-54-85-60-240.compute-1.amazonaws.com. [54.85.60.240]) by smtp.gmail.com with ESMTPSA id 31sm5383325qgy.13.2015.11.13.07.11.25 for <fake_test@post.fake.co> (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 13 Nov 2015 07:11:25 -0800","Date":"Fri, 13 Nov 2015 15:11:25 +0000","From":"Fake <fake_account@gmail.com>","To":"fake_test Community <fake_test@post.fake.co>","Message-ID":"<1447427485391-46840973-67af09f8-7a989c0d@mixmax.com>","Subject":"Local Archive","Mime-Version":"1.0","Content-Type":"multipart/alternative;","X-Original-To":"fake_test@post.fake.co","Delivered-To":"talkpost@dev.fake.co","Authentication-Results":"mail.dev.fake.co; dkim=pass reason=\"2048-bit key;","X-Spam-Checker-Version":"SpamAssassin 3.4.0 (2014-02-07) on dev.fake.co","X-Spam-Level":"","X-Spam-Status":"No, score=0.8 required=5.0 tests=FREEMAIL_FROM,","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113;","h=content-type":"from:to:subject:message-id:date:mime-version;","X-Received":"by 10.140.16.21 with SMTP id 21mr22446073qga.87.1447427486385;","Fri, 13 Nov 2015 07":"11:26 -0800 (PST)","X-Mailer":"Mixmax (mixmax.com)","X-Virus-Scanned":"ClamAV using ClamSMTP"},"from_email":"fake_account@gmail.com","from_name":"K H","to":"test Community <test@Coming Soon.domain.co>","email":"test@post.fake.co","subject":"Local Archive","tags":"","sender":null,"spam_report":"spam report","text":"Local Arhive\n\n\nSent with Mixmax","html":"\n<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n\n<html xmlns=\"http://www.w3.org/1999/xhtml\" xmlns:v=\"urn:schemas-microsoft-com:vml\" xmlns:o=\"urn:schemas-microsoft-com:office:office\">\n\n <head>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n \n \n <!--[if gte mso 9]>\n <xml>\n <o:OfficeDocumentSettings>\n <o:AllowPNG/>\n <o:PixelsPerInch>96</o:PixelsPerInch>\n </o:OfficeDocumentSettings>\n </xml>\n <![endif]-->\n \n \n <style type=\"text/css\">* a:hover{cursor:pointer;}</style>\n \n\n <style>body {-webkit-animation:bugfix infinite 1s;}@-webkit-keyframes bugfix {from {position:relative;}to {position:relative;}}</style>\n\n </head>\n\n <body yahoo=\"fix\" style=\"word-wrap:normal; word-break:break-word;\">\n\n \n <!--[if (gte mso 9)|(IE)]>\n <style>a,body {font-family:'Calibri',Arial,sans-serif;}img {border:none !important;-ms-interpolation-mode:bicubic;}td {mso-line-height-rule:exactly !important;}table.border-outer {margin-bottom:15px !important;}.border-middle,.border-inner {width:100%;}td.mso-card-inner table {border-collapse:collapse !important;mso-table-lspace:0pt;mso-table-rspace:0pt;vertical-align:top;}.border-outer,.border-middle,.border-inner {border:none !important;}.mso-border-outer,.mso-border-middle,.mso-border-inner {padding:1px;}.mso-border-outer {background-color:rgb(245,255,255);}.mso-border-middle {background-color:rgb(223,246,255);}.mso-border-inner {background-color:rgb(153,176,225);}.preview-card {margin-bottom:0 !important;padding:0 !important;}.outlook-only {display:block !important;max-height:none !important;overflow:visible !important;}.outlook-com-only {display:none !important;font-size:0 !important;}#mso-one-whole {width:100% !important;}</style>\n <![endif]-->\n \n \n <style>a {word-wrap:normal;word-break:break-word;}@media only screen and (max-width:600px) {.container[not-yahoo] {-webkit-text-size-adjust:none !important;}.container[not-yahoo],.container[not-yahoo] .palm-one-whole {width:100% !important;min-width:100% !important;}.container[not-yahoo] .palm-one-half {width:50% !important;min-width:50% !important;box-sizing:border-box;}blockquote table[lang=\"container\"],blockquote table[lang=\"container\"] div,blockquote table[lang=\"container\"] table {width:auto !important;min-width:0 !important;position:relative !important;}img {max-width:100%;}[class=\"border-outer\"],[class=\"border-middle\"],[class=\"border-inner\"],[title=\"separator\"],[class=\"inner\"] {width:100% !important;}[class=\"innercell\"] {padding:8px !important;}.palm-block {display:block !important;}.container[not-yahoo] td.palm-one-whole {display:inline-block !important;padding:0;}.container[not-yahoo] td.palm-one-whole:first-child:not(:only-child) {margin-bottom:16px;}td.hostname {padding-top:3px !important;}}@media only screen and (min-width:601px) {.preview-card {max-width:600px !important;}}@media only screen and (min-device-width :320px) and (max-device-width :568px),only screen and (min-device-width :768px) and (max-device-width :1024px),only screen and (max-device-width:640px),only screen and (max-device-width:667px),only screen and (max-width:480px){table[class=\"container\"] {width:100% !important;min-width:100% !important;}.p,.small,li,font[size=\"2\"],font[size=\"3\"] {font-size:17px !important;line-height:1.5 !important;}audio {margin-bottom:10px;}.background-contain {background-size:contain;}}@media only screen and (min-device-width :320px) and (max-device-width :568px),only screen and (min-device-width :768px) and (max-device-width :1024px),only screen and (min-device-width :1224px) {.container[not-yahoo] .message-wrapper {padding-top:6px;}.container[not-yahoo] .apple-only[style] {display:block !important;max-height:none !important;line-height:normal !important;overflow:visible !important;height:auto !important;width:100% !important;position:relative !important;}.ExternalClass .ecxapple-only {display:none !important;}.container[not-yahoo] .no-apple {display:none !important;}.container[not-yahoo] .no-apple {display:block;}.container[not-yahoo] form {font-size:inherit;}.container[not-yahoo] form input[type=\"text\"] {height:43px;padding-left:4px !important;}.container[not-yahoo] form button:hover {cursor:pointer;}@media only screen and (min-device-width :1224px) {.apple-mail-form {display:block !important;background-color:white !important;}}}</style>\n \n \n <style>.ExternalClass {width:100%;}.ExternalClass .outlook-com-button {display:block;}.ExternalClass button {height:auto;}.ExternalClass .outlook-com-hidden {display:none !important;}.ExternalClass .outlook-com-only {display:block !important;max-height:none !important;line-height:normal !important;overflow:visible !important;height:auto !important;width:100% !important;position:relative !important;}</style>\n \n\n <table class=\"container\" lang=\"container\" not-yahoo=\"fix\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\" valign=\"top\" style=\"width:100%; margin-top:6px;\">\n <tr>\n <td valign=\"top\" class=\"message-wrapper\" style=\"line-height: 1.31; color: #222; font-family: arial, sans-serif;\">\n\n <!--[if mso]><table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" valign=\"top\" style=\"border-collapse:separate;\"><tr><td valign=\"top\"><![endif]-->\n <div>Local Arhive</div><div><br></div><div><br></div><div><br></div><div>\n <div class=\"signature\" style=\"color:#999999;\">\n Sent with <a target=\"_blank\" href=\"https://mixmax.com/s/LDQhPf8Ft5nm5ccBu?utm_source=mixmax&utm_medium=email&utm_campaign=signature_link&utm_content=sent_with_mixmax\">Mixmax</a>\n \n </div>\n \n</div><img width=\"0\" height=\"0\" style=\"border:0; width:0px; height:0px;\" src=\"https://app.mixmax.com/api/track/v2/eoHCRmlPtUqTlCTRt/i02bj5CbpFWbnBEdy9GcrN2byBnI/gIvNmL1JXdrF2cuQ3cvBHQ0NXZ09VdyV3ahNnI/ISe0lmb11WbvNEI0NXZ09VdyV3ahNnI\" alt=\"\">\n <!--[if mso]></td></tr></table><![endif]-->\n </td>\n </tr>\n </table>\n\n </body>\n</html>\n"}}]
|
@@ -4,16 +4,19 @@
|
|
4
4
|
#absolute means '~/' must be written as '/home/username/...'
|
5
5
|
################################################
|
6
6
|
|
7
|
-
:mailbox: test_mailbox
|
8
|
-
:webhook: localhost:4000/test_hook
|
9
|
-
:archive:
|
10
|
-
:provider: rackspace
|
11
|
-
:username: name
|
12
|
-
:api_key: key
|
13
|
-
:region: :iad
|
7
|
+
:mailbox: test_mailbox #mbox file name
|
8
|
+
:webhook: localhost:4000/test_hook #or any remote or local url endpoint
|
14
9
|
:logfile: /home/user/mailrunner.log
|
15
10
|
:daemon: false
|
16
|
-
:verbose: false
|
17
|
-
|
18
|
-
|
11
|
+
:verbose: false #run in debug mode - adds a few more notes to help track down bugs.
|
19
12
|
|
13
|
+
#comment out all below to skip archive feature.
|
14
|
+
:archive:
|
15
|
+
:destination: local #local or cloud
|
16
|
+
:local_archive: /path/to/local_archive #path/to/archive_directory #only if destination set to local. folder must exist prior to launching!!
|
17
|
+
# :provider: rackspace #Rackspace, AWS
|
18
|
+
# :username: my_name #Rackspace only
|
19
|
+
# :api_key: my_api_key #rackspace 'api_key' or aws 'access_key_id'
|
20
|
+
# :secret_key: some_key #aws only - 'secret_access_key'
|
21
|
+
# :region: :iad #must be set for aws & Rackspace
|
22
|
+
# :directory: raw_msg_archive #name of cloud directory
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Mailrunner config file.
|
2
|
+
#To use the config file, just add teh -c flag when launching mailrunner.
|
3
|
+
# mailrunner -c /absolute/path_to/config.yml
|
4
|
+
#absolute means '~/' must be written as '/home/username/...'
|
5
|
+
################################################
|
6
|
+
|
7
|
+
:mailbox: test_mailbox #mbox file name
|
8
|
+
:webhook: localhost:4000/test_hook #or any remote or local url endpoint
|
9
|
+
:logfile: /home/user/mailrunner.log
|
10
|
+
:daemon: false
|
11
|
+
:verbose: false #run in debug mode - adds a few more notes to help track down bugs.
|
12
|
+
|
13
|
+
#comment out all below to skip archive feature.
|
14
|
+
:archive:
|
15
|
+
:destination: cloud #local or cloud
|
16
|
+
# :local_archive: /path/to/local_archive #path/to/archive_directory #only if destination set to local. folder must exist prior to launching!!
|
17
|
+
:provider: rackspace #Rackspace, AWS
|
18
|
+
:username: my_name #Rackspace only
|
19
|
+
:api_key: my_api_key #rackspace 'api_key' or aws 'access_key_id'
|
20
|
+
# :secret_key: some_key #aws only - 'secret_access_key'
|
21
|
+
:region: :iad #must be set for aws & Rackspace
|
22
|
+
:directory: raw_msg_archive #name of cloud directory
|
data/test/test_bot_helpers.rb
CHANGED
@@ -44,6 +44,73 @@ class TestBotHelpers < Minitest::Test
|
|
44
44
|
assert_raises(ArgumentError) {@test.test_webhook("http://127.0.0.1:4000/faulty_hook")}
|
45
45
|
end
|
46
46
|
end
|
47
|
+
|
48
|
+
describe "test BotHelpers:: Tests :: archive_tests" do
|
49
|
+
context "method : test_archive" do
|
50
|
+
before do
|
51
|
+
@arch_opts ={:destination => 'random'}
|
52
|
+
@test = BotHelpers::Tests
|
53
|
+
end
|
54
|
+
it "raises argument error if destination not cloud or local" do
|
55
|
+
assert_raises(ArgumentError) {@test.test_archive(@arch_opts)}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "method : test_local_archive" do
|
60
|
+
before do
|
61
|
+
`mkdir /tmp/temp_archive`
|
62
|
+
@arch_opts = {
|
63
|
+
:destination => 'local',
|
64
|
+
:local_archive => '/tmp/temp_archive'
|
65
|
+
}
|
66
|
+
@test = BotHelpers::Tests
|
67
|
+
end
|
68
|
+
after do
|
69
|
+
`rm -rf /tmp/temp_archive`
|
70
|
+
end
|
71
|
+
|
72
|
+
it "is silent if directory exists" do
|
73
|
+
assert_silent {@test.test_local_archive(@arch_opts)}
|
74
|
+
end
|
75
|
+
|
76
|
+
it "raises error if directory doesn't exist" do
|
77
|
+
@arch_opts[:local_archive] = '/tmp/other_archive'
|
78
|
+
assert_raises(ArgumentError) {@test.test_local_archive(@arch_opts)}
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
context "method : test_cloud_archive_connection" do
|
84
|
+
before do
|
85
|
+
require '~/.apikeys'
|
86
|
+
#Set to test live Rackspace account. pulls in keys from require location above.
|
87
|
+
#to test live, make .apikeys file in home directory and add keys there to keep out of git.
|
88
|
+
#to test only stub, uncomment #Fog.mock! & change username & api_key in arch_opts
|
89
|
+
@test = BotHelpers::Tests
|
90
|
+
#Fog.mock!
|
91
|
+
@arch_opts = {
|
92
|
+
:destination => 'cloud',
|
93
|
+
:provider => 'Rackspace',
|
94
|
+
:username => "#{RACKSPACE_USERNAME}",
|
95
|
+
:api_key => "#{RACKSPACE__API_KEY}",
|
96
|
+
:secret_key => "sdsd34d334",
|
97
|
+
:directory => 'raw_msg_archive'
|
98
|
+
}
|
99
|
+
end
|
100
|
+
it "successfully connects to cloud archive" do
|
101
|
+
assert_silent {@test.test_cloud_archive_connection(@arch_opts)}
|
102
|
+
end
|
103
|
+
|
104
|
+
it "raises error if fails connect to cloud archive" do
|
105
|
+
@arch_opts[:api_key] = nil
|
106
|
+
assert_raises(ArgumentError) {@test.test_cloud_archive_connection(@arch_opts)}
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
|
47
114
|
########################################
|
48
115
|
|
49
116
|
|
data/test/test_cli.rb
CHANGED
@@ -135,5 +135,39 @@ class TestCLI< Minitest::Test
|
|
135
135
|
assert_equal '/home/user/flag.log', @options[:logfile]
|
136
136
|
end
|
137
137
|
end
|
138
|
+
|
139
|
+
describe 'local archive setup via config' do
|
140
|
+
before do
|
141
|
+
@cli = MailRunner::CLI
|
142
|
+
@options = @cli.parse_options(['mailrunner', '-c', './test/test_assets/test_config.yml'])
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'sets destination to local' do
|
146
|
+
assert_equal 'local', @options[:archive][:destination]
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'sets local archive path' do
|
150
|
+
assert_equal '/path/to/local_archive', @options[:archive][:local_archive]
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe 'remote archive setup via config' do
|
155
|
+
before do
|
156
|
+
@cli = MailRunner::CLI
|
157
|
+
@options = @cli.parse_options(['mailrunner', '-c', './test/test_assets/test_config_archive_cloud.yml'])
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'sets destination to cloud' do
|
161
|
+
assert_equal 'cloud', @options[:archive][:destination]
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'sets all cloud destination params' do
|
165
|
+
assert_equal 'rackspace', @options[:archive][:provider]
|
166
|
+
assert_equal 'my_name', @options[:archive][:username]
|
167
|
+
assert_equal 'my_api_key', @options[:archive][:api_key]
|
168
|
+
assert_equal :iad, @options[:archive][:region]
|
169
|
+
assert_equal 'raw_msg_archive', @options[:archive][:directory]
|
170
|
+
end
|
171
|
+
end
|
138
172
|
|
139
|
-
end
|
173
|
+
end
|
@@ -1,16 +1,16 @@
|
|
1
1
|
#####################
|
2
2
|
# Several tests require a live server behind the test webhook.
|
3
|
-
# will cause
|
3
|
+
# will cause several fails if not. Must be Post method.
|
4
4
|
|
5
5
|
|
6
6
|
|
7
7
|
######################
|
8
8
|
require_relative 'helper'
|
9
9
|
|
10
|
-
class
|
10
|
+
class TestHeadManager < Minitest::Test
|
11
11
|
|
12
12
|
####### Top Level Call Tests ##########
|
13
|
-
describe 'MailRunner Manager initialize' do
|
13
|
+
describe 'MR Manager::MailRunner Manager initialize' do
|
14
14
|
before do
|
15
15
|
@bot = MailRunner.initialize_manager_bot
|
16
16
|
end
|
@@ -19,14 +19,14 @@ class TestMailRunnerManager < Minitest::Test
|
|
19
19
|
assert_instance_of MailRunner::ManagerBot, @bot
|
20
20
|
assert_equal nil, @bot.mailbox
|
21
21
|
assert_equal nil, @bot.webhook
|
22
|
-
assert_equal
|
22
|
+
assert_equal nil, @bot.archive
|
23
23
|
end
|
24
24
|
|
25
25
|
it "includes BotHelpers" do
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
describe "Parse Options method" do
|
29
|
+
describe "MR Manager::Parse Options method" do
|
30
30
|
before do
|
31
31
|
@bot = MailRunner.initialize_manager_bot
|
32
32
|
@opts = {:mailbox => "box_name", :webhook => "webhook/path"}
|
@@ -36,17 +36,17 @@ class TestMailRunnerManager < Minitest::Test
|
|
36
36
|
@bot.parse_options(@opts)
|
37
37
|
assert_equal "/var/mail/box_name", @bot.mailbox
|
38
38
|
assert_equal "webhook/path", @bot.webhook
|
39
|
-
assert_equal
|
39
|
+
assert_equal nil, @bot.archive
|
40
40
|
end
|
41
41
|
|
42
42
|
it "archive = true if passed archive argument" do
|
43
|
-
@opts[:archive] =
|
43
|
+
@opts[:archive] = true
|
44
44
|
@bot.parse_options(@opts)
|
45
45
|
assert_equal true, @bot.archive
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
|
-
describe "Run Method" do
|
49
|
+
describe "MR Manager::Run Method" do
|
50
50
|
before do
|
51
51
|
@bot = MailRunner.initialize_manager_bot
|
52
52
|
@bot.mailbox = "/var/mail/root" #app
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mail_runner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kert Heinecke
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-11-
|
11
|
+
date: 2015-11-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mail
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: 1.8.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: fog
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.23.0
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 1.23.0
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: rake
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,6 +178,7 @@ files:
|
|
164
178
|
- Rakefile
|
165
179
|
- bin/mail_runner
|
166
180
|
- lib/mail_runner.rb
|
181
|
+
- lib/mail_runner/archive_manager.rb
|
167
182
|
- lib/mail_runner/bot_helpers/helpers.rb
|
168
183
|
- lib/mail_runner/bot_helpers/runner.rb
|
169
184
|
- lib/mail_runner/bot_helpers/tests.rb
|
@@ -176,16 +191,20 @@ files:
|
|
176
191
|
- lib/mail_runner/version.rb
|
177
192
|
- mail_runner.gemspec
|
178
193
|
- test/helper.rb
|
194
|
+
- test/test_archivist.rb
|
195
|
+
- test/test_assets/_1447427485391-46840973-67af09f8-7a989c0d_mixmax.com_.json
|
196
|
+
- test/test_assets/archive_opts.json
|
179
197
|
- test/test_assets/empty.txt
|
180
198
|
- test/test_assets/inline_attachments.email
|
181
199
|
- test/test_assets/test.email
|
182
200
|
- test/test_assets/test.json
|
183
201
|
- test/test_assets/test_config.yml
|
202
|
+
- test/test_assets/test_config_archive_cloud.yml
|
184
203
|
- test/test_assets/with_attachments.email
|
185
204
|
- test/test_bot_helpers.rb
|
186
205
|
- test/test_cli.rb
|
206
|
+
- test/test_head_manager_bot.rb
|
187
207
|
- test/test_inbound_manager_bot.rb
|
188
|
-
- test/test_mail_runner.rb
|
189
208
|
- test/test_queue_manager_bot.rb
|
190
209
|
homepage: https://github.com/kert-io/mailRunner
|
191
210
|
licenses:
|