multi_mail 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +4 -0
- data/.yardopts +4 -0
- data/Gemfile +1 -1
- data/README.md +107 -12
- data/Rakefile +75 -52
- data/lib/multi_mail/cloudmailin/receiver.rb +100 -0
- data/lib/multi_mail/mailgun/receiver.rb +74 -36
- data/lib/multi_mail/mailgun/sender.rb +14 -0
- data/lib/multi_mail/mandrill/receiver.rb +77 -35
- data/lib/multi_mail/mandrill/sender.rb +14 -0
- data/lib/multi_mail/postmark/receiver.rb +68 -0
- data/lib/multi_mail/postmark/sender.rb +14 -0
- data/lib/multi_mail/receiver/base.rb +125 -8
- data/lib/multi_mail/receiver.rb +10 -4
- data/lib/multi_mail/sender/base.rb +7 -0
- data/lib/multi_mail/sender.rb +10 -4
- data/lib/multi_mail/sendgrid/receiver.rb +42 -0
- data/lib/multi_mail/sendgrid/sender.rb +11 -0
- data/lib/multi_mail/service.rb +15 -4
- data/lib/multi_mail/simple/receiver.rb +15 -0
- data/lib/multi_mail/simple/sender.rb +14 -0
- data/lib/multi_mail/version.rb +1 -1
- data/lib/multi_mail.rb +71 -3
- data/multi_mail.gemspec +12 -5
- data/spec/cloudmailin/receiver_spec.rb +112 -0
- data/spec/fixtures/cloudmailin/json/spam.txt +59 -0
- data/spec/fixtures/cloudmailin/json/valid.txt +59 -0
- data/spec/fixtures/cloudmailin/multipart/spam.txt +135 -0
- data/spec/fixtures/cloudmailin/multipart/valid.txt +135 -0
- data/spec/fixtures/cloudmailin/raw/spam.txt +137 -0
- data/spec/fixtures/cloudmailin/raw/valid.txt +137 -0
- data/spec/fixtures/mailgun/parsed/invalid.txt +8 -0
- data/spec/fixtures/mailgun/parsed/missing.txt +8 -0
- data/spec/fixtures/mailgun/parsed/spam.txt +8 -0
- data/spec/fixtures/mailgun/parsed/valid.txt +187 -0
- data/spec/fixtures/mandrill/spam.txt +9 -0
- data/spec/fixtures/mandrill/valid.txt +10 -0
- data/spec/fixtures/multipart.txt +99 -0
- data/spec/fixtures/postmark/spam.txt +83 -0
- data/spec/fixtures/postmark/valid.txt +92 -0
- data/spec/fixtures/simple/valid.txt +4 -0
- data/spec/mailgun/receiver_spec.rb +105 -50
- data/spec/mailgun/sender_spec.rb +0 -0
- data/spec/mandrill/receiver_spec.rb +35 -35
- data/spec/mandrill/sender_spec.rb +0 -0
- data/spec/multi_mail_spec.rb +63 -0
- data/spec/postmark/receiver_spec.rb +60 -0
- data/spec/postmark/sender_spec.rb +0 -0
- data/spec/receiver/base_spec.rb +73 -8
- data/spec/sender/base_spec.rb +21 -0
- data/spec/service_spec.rb +2 -2
- data/spec/simple/receiver_spec.rb +36 -0
- data/spec/simple/sender_spec.rb +0 -0
- data/spec/spec_helper.rb +123 -10
- metadata +141 -21
- data/spec/fixtures/mailgun/invalid.txt +0 -8
- data/spec/fixtures/mailgun/missing.txt +0 -8
- data/spec/fixtures/mailgun/spam.txt +0 -8
- data/spec/fixtures/mailgun/valid.txt +0 -8
@@ -1,71 +1,113 @@
|
|
1
1
|
module MultiMail
|
2
2
|
module Receiver
|
3
|
+
# Mandrill's incoming email receiver.
|
4
|
+
#
|
5
|
+
# Mandrill uses an HTTP header to ensure a request originates from Mandrill.
|
6
|
+
#
|
7
|
+
# @see http://help.mandrill.com/entries/23704122-Authenticating-webhook-requests
|
3
8
|
class Mandrill < MultiMail::Service
|
4
9
|
include MultiMail::Receiver::Base
|
5
10
|
|
6
|
-
|
11
|
+
recognizes :spamassassin_threshold
|
7
12
|
|
13
|
+
# Initializes a Mandrill incoming email receiver.
|
14
|
+
#
|
8
15
|
# @param [Hash] options required and optional arguments
|
9
|
-
# @option
|
16
|
+
# @option option [Float] :spamassassin_threshold the SpamAssassin score
|
17
|
+
# needed to flag a message as spam
|
10
18
|
def initialize(options = {})
|
11
19
|
super
|
12
|
-
@
|
20
|
+
@spamassassin_threshold = options[:spamassassin_threshold] || 5
|
13
21
|
end
|
14
22
|
|
23
|
+
# Transforms the content of Mandrill's webhook into a list of messages.
|
24
|
+
#
|
15
25
|
# @param [Hash] params the content of Mandrill's webhook
|
16
|
-
# @return [
|
17
|
-
|
18
|
-
|
26
|
+
# @return [Array<Mail::Message>] messages
|
27
|
+
# @see http://help.mandrill.com/entries/22092308-What-is-the-format-of-inbound-email-webhooks-
|
28
|
+
def transform(params)
|
29
|
+
# JSON is necessarily UTF-8.
|
30
|
+
JSON.parse(params['mandrill_events']).select do |event|
|
19
31
|
event.fetch('event') == 'inbound'
|
20
|
-
end
|
21
|
-
|
32
|
+
end.map do |event|
|
33
|
+
msg = event['msg']
|
22
34
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
35
|
+
headers = Multimap.new
|
36
|
+
msg['headers'].each do |key,value|
|
37
|
+
if Array === value
|
38
|
+
value.each do |v|
|
39
|
+
headers[key] = v
|
40
|
+
end
|
41
|
+
else
|
42
|
+
headers[key] = value
|
43
|
+
end
|
44
|
+
end
|
28
45
|
|
29
|
-
|
30
|
-
# @return [Array<Mail::Message>] messages
|
31
|
-
# @todo parse attachments properly
|
32
|
-
def transform(params)
|
33
|
-
JSON.parse(params['mandrill_events']).map do |event|
|
46
|
+
this = self
|
34
47
|
message = Mail.new do
|
35
|
-
headers
|
48
|
+
headers headers
|
36
49
|
|
37
50
|
# The following are redundant with `message-headers`:
|
38
51
|
#
|
39
|
-
# address = Mail::Address.new
|
40
|
-
# address.display_name =
|
52
|
+
# address = Mail::Address.new(msg['from_email'])
|
53
|
+
# address.display_name = msg['from_name']
|
41
54
|
#
|
42
55
|
# from address.format
|
43
|
-
# to
|
44
|
-
# subject
|
56
|
+
# to msg['to'].flatten.compact
|
57
|
+
# subject msg['subject']
|
45
58
|
|
46
59
|
text_part do
|
47
|
-
body
|
60
|
+
body msg['text']
|
48
61
|
end
|
49
62
|
|
50
|
-
|
51
|
-
|
52
|
-
|
63
|
+
# If an email contains multiple HTML parts, Mandrill will only
|
64
|
+
# include the first HTML part in its `html` parameter. We therefore
|
65
|
+
# parse its `raw_msg` parameter to set the HTML part correctly.
|
66
|
+
html = this.class.condense(Mail.new(msg['raw_msg'])).parts.find do |part|
|
67
|
+
part.content_type == 'text/html; charset=UTF-8'
|
53
68
|
end
|
54
|
-
end
|
55
69
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
70
|
+
if html
|
71
|
+
html_part do
|
72
|
+
content_type 'text/html; charset=UTF-8'
|
73
|
+
body html.body.decoded
|
74
|
+
end
|
75
|
+
elsif msg.key?('html')
|
76
|
+
html_part do
|
77
|
+
content_type 'text/html; charset=UTF-8'
|
78
|
+
body msg['html']
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
if msg.key?('attachments')
|
83
|
+
msg['attachments'].each do |_,attachment|
|
84
|
+
add_file(:filename => attachment['name'], :content => attachment['content'])
|
85
|
+
end
|
63
86
|
end
|
64
87
|
end
|
65
88
|
|
89
|
+
# Extra Mandrill parameters. Discard `sender` and `tags`, which are
|
90
|
+
# null according to the docs, `matched_rules` within `spam_report`,
|
91
|
+
# `detail` within `spf`, which is just a human-readable version of
|
92
|
+
# `result`, and `raw_msg`.
|
93
|
+
message['ts'] = event['ts']
|
94
|
+
message['email'] = msg['email']
|
95
|
+
message['dkim-signed'] = msg['dkim']['signed'].to_s
|
96
|
+
message['dkim-valid'] = msg['dkim']['valid'].to_s
|
97
|
+
message['spam_report-score'] = msg['spam_report']['score']
|
98
|
+
message['spf-result'] = msg['spf']['result']
|
99
|
+
|
66
100
|
message
|
67
101
|
end
|
68
102
|
end
|
103
|
+
|
104
|
+
# Returns whether a message is spam.
|
105
|
+
#
|
106
|
+
# @param [Mail::Message] message a message
|
107
|
+
# @return [Boolean] whether the message is spam
|
108
|
+
def spam?(message)
|
109
|
+
message['spam_report-score'] && message['spam_report-score'].value.to_f > @spamassassin_threshold
|
110
|
+
end
|
69
111
|
end
|
70
112
|
end
|
71
113
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module MultiMail
|
2
|
+
module Receiver
|
3
|
+
# Postmark's incoming email receiver.
|
4
|
+
class Postmark < MultiMail::Service
|
5
|
+
include MultiMail::Receiver::Base
|
6
|
+
|
7
|
+
def transform(params)
|
8
|
+
headers = Multimap.new
|
9
|
+
params['Headers'].each do |header|
|
10
|
+
headers[header['Name']] = header['Value']
|
11
|
+
end
|
12
|
+
|
13
|
+
# Due to scoping issues, we can't call `transform_address` within `Mail.new`.
|
14
|
+
from = transform_address(params['FromFull'])
|
15
|
+
to = params['ToFull'].map{|hash| transform_address(hash)}
|
16
|
+
cc = params['CcFull'].map{|hash| transform_address(hash)}
|
17
|
+
|
18
|
+
message = Mail.new do
|
19
|
+
headers headers
|
20
|
+
|
21
|
+
from from
|
22
|
+
to to
|
23
|
+
cc cc
|
24
|
+
reply_to params['ReplyTo']
|
25
|
+
subject params['Subject']
|
26
|
+
date params['Date']
|
27
|
+
|
28
|
+
text_part do
|
29
|
+
body params['TextBody']
|
30
|
+
end
|
31
|
+
|
32
|
+
html_part do
|
33
|
+
content_type 'text/html; charset=UTF-8'
|
34
|
+
body CGI.unescapeHTML(params['HtmlBody'])
|
35
|
+
end
|
36
|
+
|
37
|
+
params['Attachments'].each do |attachment|
|
38
|
+
add_file(:filename => attachment['Name'], :content => Base64.decode64(attachment['Content']))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Extra Postmark parameters.
|
43
|
+
%w(MailboxHash MessageID Tag).each do |key|
|
44
|
+
if params.key?(key) && !params[key].empty?
|
45
|
+
message[key] = params[key]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
[message]
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param [Mail::Message] message a message
|
53
|
+
# @return [Boolean] whether the message is spam
|
54
|
+
# @see http://developer.postmarkapp.com/developer-inbound-parse.html#spam
|
55
|
+
def spam?(message)
|
56
|
+
message['X-Spam-Status'].value == 'Yes'
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def transform_address(hash)
|
62
|
+
address = Mail::Address.new(hash['Email'])
|
63
|
+
address.display_name = hash['Name']
|
64
|
+
address.to_s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module MultiMail
|
2
2
|
module Receiver
|
3
|
-
# Abstract class for incoming email
|
3
|
+
# Abstract class for incoming email receivers.
|
4
4
|
#
|
5
5
|
# The `transform` instance method must be implemented in sub-classes. The
|
6
6
|
# `valid?` and `spam?` instance methods may be implemented in sub-classes.
|
@@ -11,30 +11,39 @@ module MultiMail
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
#
|
14
|
+
# Ensures a request is authentic, parses it into a params hash, and
|
15
|
+
# transforms it into a list of messages.
|
16
|
+
#
|
17
|
+
# @param [String,Array,Hash,Rack::Request] raw raw POST data or a params hash
|
15
18
|
# @return [Array<Mail::Message>] messages
|
16
|
-
# @
|
19
|
+
# @raise [ForgedRequest] if the request is not authentic
|
17
20
|
def process(raw)
|
18
|
-
params = self.class.parse
|
19
|
-
if valid?
|
20
|
-
transform
|
21
|
+
params = self.class.parse(raw)
|
22
|
+
if valid?(params)
|
23
|
+
transform(params)
|
21
24
|
else
|
22
25
|
raise ForgedRequest
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
29
|
+
# Returns whether a request is authentic.
|
30
|
+
#
|
26
31
|
# @param [Hash] params the content of the provider's webhook
|
27
32
|
# @return [Boolean] whether the request is authentic
|
28
33
|
def valid?(params)
|
29
34
|
true
|
30
35
|
end
|
31
36
|
|
37
|
+
# Transforms the content of a provider's webhook into a list of messages.
|
38
|
+
#
|
32
39
|
# @param [Hash] params the content of the provider's webhook
|
33
40
|
# @return [Array<Mail::Message>] messages
|
34
41
|
def transform(params)
|
35
42
|
raise NotImplementedError
|
36
43
|
end
|
37
44
|
|
45
|
+
# Returns whether a message is spam.
|
46
|
+
#
|
38
47
|
# @param [Mail::Message] message a message
|
39
48
|
# @return [Boolean] whether the message is spam
|
40
49
|
def spam?(message)
|
@@ -42,23 +51,131 @@ module MultiMail
|
|
42
51
|
end
|
43
52
|
|
44
53
|
module ClassMethods
|
54
|
+
# ActionDispatch::Http::Request subclasses Rack::Request and turns
|
55
|
+
# attachment hashes into instances of ActionDispatch::Http::UploadedFile
|
56
|
+
# in Rails 3 and 4 and instances of ActionController::UploadedFile in
|
57
|
+
# Rails 2.3, both of which have the same interface.
|
58
|
+
#
|
59
|
+
# @param [ActionDispatch::Http::UploadedFile,ActionController::UploadedFile,Hash] attachment an attachment
|
60
|
+
# @return [Hash] arguments for `Mail::Message#add_file`
|
61
|
+
def add_file_arguments(attachment)
|
62
|
+
if Hash === attachment
|
63
|
+
{:filename => attachment[:filename], :content => attachment[:tempfile].read}
|
64
|
+
else
|
65
|
+
{:filename => attachment.original_filename, :content => attachment.read}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Parses raw POST data into a params hash.
|
70
|
+
#
|
45
71
|
# @param [String,Hash] raw raw POST data or a params hash
|
72
|
+
# @raise [ArgumentError] if the argument is not a string or a hash
|
46
73
|
def parse(raw)
|
47
74
|
case raw
|
48
75
|
when String
|
49
|
-
|
50
|
-
|
76
|
+
begin
|
77
|
+
JSON.parse(raw)
|
78
|
+
rescue JSON::ParserError
|
79
|
+
params = CGI.parse(raw)
|
80
|
+
|
81
|
+
# Flatten the parameters.
|
82
|
+
params.each do |key,value|
|
83
|
+
if Array === value && value.size == 1
|
84
|
+
params[key] = value.first
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
params
|
89
|
+
end
|
90
|
+
when Array
|
91
|
+
params = {}
|
92
|
+
|
93
|
+
# Collect the values for each key.
|
94
|
+
map = Multimap.new
|
95
|
+
raw.each do |key,value|
|
96
|
+
map[key] = value
|
97
|
+
end
|
98
|
+
|
99
|
+
# Flatten the parameters.
|
100
|
+
map.each do |key,value|
|
51
101
|
if Array === value && value.size == 1
|
52
102
|
params[key] = value.first
|
103
|
+
else
|
104
|
+
params[key] = value
|
53
105
|
end
|
54
106
|
end
|
107
|
+
|
55
108
|
params
|
109
|
+
when Rack::Request
|
110
|
+
raw.params
|
56
111
|
when Hash
|
57
112
|
raw
|
58
113
|
else
|
59
114
|
raise ArgumentError, "Can't handle #{raw.class.name} input"
|
60
115
|
end
|
61
116
|
end
|
117
|
+
|
118
|
+
# Condenses a message's HTML parts to a single HTML part.
|
119
|
+
#
|
120
|
+
# @example
|
121
|
+
# flat = self.class.condense(message.dup)
|
122
|
+
#
|
123
|
+
# @param [Mail::Message] message a message with zero or more HTML parts
|
124
|
+
# @return [Mail::Message] the message with a single HTML part
|
125
|
+
def condense(message)
|
126
|
+
if message.multipart? && message.parts.any?(&:multipart?)
|
127
|
+
# Get the message parts as a flat array.
|
128
|
+
result = flatten(Mail.new, message.parts.dup)
|
129
|
+
|
130
|
+
# Rebuild the message's parts.
|
131
|
+
message.parts.clear
|
132
|
+
|
133
|
+
# Merge non-attachments with the same content type.
|
134
|
+
(result.parts - result.attachments).group_by(&:content_type).each do |content_type,group|
|
135
|
+
body = group.map{|part| part.body.decoded}.join
|
136
|
+
|
137
|
+
# Make content types match across all APIs.
|
138
|
+
if content_type == 'text/plain; charset=us-ascii'
|
139
|
+
# `text/plain; charset=us-ascii` is the default content type.
|
140
|
+
content_type = 'text/plain'
|
141
|
+
elsif content_type == 'text/html; charset=us-ascii'
|
142
|
+
content_type = 'text/html; charset=UTF-8'
|
143
|
+
body = body.encode('UTF-8') if body.respond_to?(:encode)
|
144
|
+
end
|
145
|
+
|
146
|
+
message.parts << Mail::Part.new({
|
147
|
+
:content_type => content_type,
|
148
|
+
:body => body,
|
149
|
+
})
|
150
|
+
end
|
151
|
+
|
152
|
+
# Add attachments last.
|
153
|
+
result.attachments.each do |part|
|
154
|
+
message.parts << part
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
message
|
159
|
+
end
|
160
|
+
|
161
|
+
# Flattens a hierarchy of message parts.
|
162
|
+
#
|
163
|
+
# @example
|
164
|
+
# flat = self.class.flatten(Mail.new, parts.dup)
|
165
|
+
#
|
166
|
+
# @param [Mail::Message] message a message
|
167
|
+
# @param [Mail::PartsList] parts parts to add to the message
|
168
|
+
# @return [Mail::Message] the message with all the parts
|
169
|
+
def flatten(message, parts)
|
170
|
+
parts.each do |part|
|
171
|
+
if part.multipart?
|
172
|
+
flatten(message, part.parts)
|
173
|
+
else
|
174
|
+
message.parts << part
|
175
|
+
end
|
176
|
+
end
|
177
|
+
message
|
178
|
+
end
|
62
179
|
end
|
63
180
|
end
|
64
181
|
end
|
data/lib/multi_mail/receiver.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
module MultiMail
|
2
|
+
# Endpoint for initializing different incoming email receivers.
|
3
|
+
#
|
2
4
|
# @see http://rdoc.info/gems/fog/Fog/Storage
|
3
5
|
module Receiver
|
4
|
-
|
5
|
-
|
6
|
+
# Initializes an incoming email receiver.
|
7
|
+
#
|
6
8
|
# @example
|
7
9
|
# require 'multi_mail'
|
8
10
|
# service = MultiMail::Receiver.new({
|
@@ -11,8 +13,9 @@ module MultiMail
|
|
11
13
|
# })
|
12
14
|
#
|
13
15
|
# @param [Hash] attributes required arguments
|
14
|
-
# @option
|
15
|
-
# @
|
16
|
+
# @option attributes [String,Symbol] :provider a provider
|
17
|
+
# @return [MultiMail::Service] an incoming email receiver
|
18
|
+
# @raise [ArgumentError] if the provider does not exist
|
16
19
|
# @see Fog::Storage::new
|
17
20
|
def self.new(attributes)
|
18
21
|
attributes = attributes.dup # prevent delete from having side effects
|
@@ -32,6 +35,9 @@ module MultiMail
|
|
32
35
|
when :sendgrid
|
33
36
|
require 'multi_mail/sendgrid/receiver'
|
34
37
|
MultiMail::Receiver::SendGrid.new(attributes)
|
38
|
+
when :simple
|
39
|
+
require 'multi_mail/simple/receiver'
|
40
|
+
MultiMail::Receiver::Simple.new(attributes)
|
35
41
|
when :mock
|
36
42
|
# for testing
|
37
43
|
MultiMail::Receiver::Mock.new(attributes)
|
data/lib/multi_mail/sender.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
module MultiMail
|
2
|
+
# Endpoint for initializing different outgoing email senders.
|
3
|
+
#
|
2
4
|
# @see http://rdoc.info/gems/fog/Fog/Storage
|
3
5
|
module Sender
|
4
|
-
|
5
|
-
|
6
|
+
# Initializers an outgoing email sender.
|
7
|
+
#
|
6
8
|
# @example
|
7
9
|
# require 'multi_mail'
|
8
10
|
# service = MultiMail::Sender.new({
|
@@ -11,8 +13,9 @@ module MultiMail
|
|
11
13
|
# })
|
12
14
|
#
|
13
15
|
# @param [Hash] attributes required arguments
|
14
|
-
# @option
|
15
|
-
# @
|
16
|
+
# @option attributes [String,Symbol] :provider a provider
|
17
|
+
# @return [MultiMail::Service] an outgoing email sender
|
18
|
+
# @raise [ArgumentError] if the provider does not exist
|
16
19
|
# @see Fog::Storage::new
|
17
20
|
def self.new(attributes)
|
18
21
|
attributes = attributes.dup # prevent delete from having side effects
|
@@ -29,6 +32,9 @@ module MultiMail
|
|
29
32
|
when :sendgrid
|
30
33
|
require 'multi_mail/sendgrid/sender'
|
31
34
|
MultiMail::Sender::SendGrid.new(attributes)
|
35
|
+
when :simple
|
36
|
+
require 'multi_mail/simple/sender'
|
37
|
+
MultiMail::Sender::Simple.new(attributes)
|
32
38
|
when :mock
|
33
39
|
# for testing
|
34
40
|
MultiMail::Sender::Mock.new(attributes)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module MultiMail
|
2
|
+
module Receiver
|
3
|
+
class SendGrid < MultiMail::Service
|
4
|
+
include MultiMail::Receiver::Base
|
5
|
+
|
6
|
+
requires :sendgrid_username
|
7
|
+
requires :sendgrid_password
|
8
|
+
recognizes :http_post_format
|
9
|
+
attr_reader :http_post_format
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
super
|
13
|
+
@sendgrid_username = options[:sendgrid_username]
|
14
|
+
@sendgrid_password = options[:sendgrid_password]
|
15
|
+
@http_post_format = options[:http_post_format]
|
16
|
+
end
|
17
|
+
|
18
|
+
def transform(params)
|
19
|
+
attachments = 1.upto(params['attachments'].to_i).map do |num|
|
20
|
+
attachment_from_params(params["attachment#{num}"])
|
21
|
+
end
|
22
|
+
|
23
|
+
@message = Mail.new do
|
24
|
+
header params['headers']
|
25
|
+
|
26
|
+
body params['text']
|
27
|
+
|
28
|
+
html_part do
|
29
|
+
content_type 'text/html; charset=UTF-8'
|
30
|
+
body params['html']
|
31
|
+
end if params['html']
|
32
|
+
|
33
|
+
attachments.each do |attachment|
|
34
|
+
add_file(attachment)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
data/lib/multi_mail/service.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
module MultiMail
|
2
|
+
# Interacts with email APIs to send or receive email.
|
3
|
+
#
|
2
4
|
# @see http://rdoc.info/gems/fog/Fog/Service
|
3
5
|
class Service
|
6
|
+
# Initializers an email API service.
|
7
|
+
#
|
4
8
|
# @param [Hash] options optional arguments
|
5
9
|
def initialize(options = {})
|
6
10
|
self.class.validate_options(options)
|
@@ -15,6 +19,8 @@ module MultiMail
|
|
15
19
|
requirements.concat(args)
|
16
20
|
end
|
17
21
|
|
22
|
+
# Returns the list of required arguments.
|
23
|
+
#
|
18
24
|
# @return [Array] a list of required arguments
|
19
25
|
# @see Fog::Service::requirements
|
20
26
|
def requirements
|
@@ -29,15 +35,20 @@ module MultiMail
|
|
29
35
|
recognized.concat(args)
|
30
36
|
end
|
31
37
|
|
38
|
+
# Returns the list of optional arguments.
|
39
|
+
#
|
32
40
|
# @return [Array] a list of optional arguments
|
33
41
|
# @see Fog::Service::recognized
|
34
42
|
def recognized
|
35
43
|
@recognized ||= []
|
36
44
|
end
|
37
45
|
|
46
|
+
# Ensures that required arguments are present and that optional arguments
|
47
|
+
# are recognized.
|
48
|
+
#
|
38
49
|
# @param [Hash] options arguments
|
39
|
-
# @
|
40
|
-
# recognize optional
|
50
|
+
# @raise [ArgumentError] if it can't find a required argument or can't
|
51
|
+
# recognize an optional argument
|
41
52
|
# @see Fog::Service::validate_options
|
42
53
|
def validate_options(options)
|
43
54
|
keys = []
|
@@ -48,13 +59,13 @@ module MultiMail
|
|
48
59
|
end
|
49
60
|
missing = requirements - keys
|
50
61
|
unless missing.empty?
|
51
|
-
raise ArgumentError, "Missing required arguments: #{missing.join(', ')}"
|
62
|
+
raise ArgumentError, "Missing required arguments: #{missing.map(&:to_s).sort.join(', ')}"
|
52
63
|
end
|
53
64
|
|
54
65
|
unless recognizes.empty?
|
55
66
|
unrecognized = options.keys - requirements - recognized
|
56
67
|
unless unrecognized.empty?
|
57
|
-
raise ArgumentError, "Unrecognized arguments: #{unrecognized.join(', ')}"
|
68
|
+
raise ArgumentError, "Unrecognized arguments: #{unrecognized.map(&:to_s).sort.join(', ')}"
|
58
69
|
end
|
59
70
|
end
|
60
71
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module MultiMail
|
2
|
+
module Receiver
|
3
|
+
class Simple < MultiMail::Service
|
4
|
+
include MultiMail::Receiver::Base
|
5
|
+
# Expects the value of the "message" query string parameter to be a raw
|
6
|
+
# email message parsable by the Mail gem.
|
7
|
+
#
|
8
|
+
# @param [Hash] params the content of the webhook
|
9
|
+
# @return [Array<Mail::Message>] messages
|
10
|
+
def transform(params)
|
11
|
+
[Mail.new(params['message'])]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/multi_mail/version.rb
CHANGED