appmail 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/app_mail.rb +18 -0
- data/lib/app_mail/attachment.rb +31 -0
- data/lib/app_mail/client.rb +51 -0
- data/lib/app_mail/config.rb +15 -0
- data/lib/app_mail/error.rb +47 -0
- data/lib/app_mail/header_set.rb +21 -0
- data/lib/app_mail/message.rb +156 -0
- data/lib/app_mail/message_scope.rb +40 -0
- data/lib/app_mail/send_message.rb +77 -0
- data/lib/app_mail/send_result.rb +30 -0
- data/lib/app_mail/version.rb +3 -0
- data/lib/appmail.rb +1 -0
- metadata +76 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: cf42054c6e347a5c7cad150857aa334eab7e5ea6
|
4
|
+
data.tar.gz: 6a1e0e40ed2c04a67232875132d9d2dddb4ad7ee
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b7e91f5b55dff971e14a56a146684d23666737209459d40c01da057f985e2b1bc5903667357ae4eaea8b8cd489ebe8d87ff526f745748c673af9492f8ba40386
|
7
|
+
data.tar.gz: 9d90c3d37b84bfe5e8585d9a72607ab214b137765d2afb952d16dc144102d1562f5b9da7e2cd18fcacc171aaccd476d1162a2d84e99ead6e27c5afb8dad37970
|
data/lib/app_mail.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'app_mail/client'
|
2
|
+
require 'app_mail/config'
|
3
|
+
|
4
|
+
module AppMail
|
5
|
+
|
6
|
+
def self.config
|
7
|
+
@config ||= Config.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.configure(&block)
|
11
|
+
block.call(config)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.send_message(&block)
|
15
|
+
AppMail::Client.instance.send_message(&block)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module AppMail
|
4
|
+
class Attachment
|
5
|
+
|
6
|
+
def initialize(attributes)
|
7
|
+
@attributes = attributes
|
8
|
+
end
|
9
|
+
|
10
|
+
def filename
|
11
|
+
@attributes['filename']
|
12
|
+
end
|
13
|
+
|
14
|
+
def content_type
|
15
|
+
@attributes['content_type']
|
16
|
+
end
|
17
|
+
|
18
|
+
def size
|
19
|
+
@attributes['size']
|
20
|
+
end
|
21
|
+
|
22
|
+
def hash
|
23
|
+
@attributes['hash']
|
24
|
+
end
|
25
|
+
|
26
|
+
def data
|
27
|
+
@data ||= Base64.decode64(@attributes['data'])
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'moonrope_client'
|
2
|
+
require 'app_mail/message_scope'
|
3
|
+
require 'app_mail/send_message'
|
4
|
+
|
5
|
+
module AppMail
|
6
|
+
class Client
|
7
|
+
|
8
|
+
#
|
9
|
+
# Create and cache a global instance of client based on the environment variables
|
10
|
+
# which can be provided. In 90% of cases, AppMail will be accessed through this.
|
11
|
+
#
|
12
|
+
def self.instance
|
13
|
+
@instance ||= Client.new(AppMail.config.host, AppMail.config.server_key)
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# Initialize a new client with the host and API key
|
18
|
+
#
|
19
|
+
def initialize(host, server_key)
|
20
|
+
@host = host
|
21
|
+
@server_key = server_key
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Provide a scope to access messages
|
26
|
+
#
|
27
|
+
def messages
|
28
|
+
MessageScope.new(self)
|
29
|
+
end
|
30
|
+
|
31
|
+
#
|
32
|
+
# Send a message
|
33
|
+
#
|
34
|
+
def send_message(&block)
|
35
|
+
message = SendMessage.new(self)
|
36
|
+
block.call(message)
|
37
|
+
message.send!
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# Return the backend moonrope instance for this client
|
42
|
+
#
|
43
|
+
def moonrope
|
44
|
+
@moonrope ||= begin
|
45
|
+
headers= {'X-Server-API-Key' => @server_key}
|
46
|
+
MoonropeClient::Connection.new(@host, :headers => headers, :ssl => true)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module AppMail
|
2
|
+
class Config
|
3
|
+
|
4
|
+
def host
|
5
|
+
@host || ENV['APPMAIL_HOST'] || 'api.appmail.io'
|
6
|
+
end
|
7
|
+
attr_writer :host
|
8
|
+
|
9
|
+
def server_key
|
10
|
+
@server_key || ENV['APPMAIL_KEY'] || raise(Error, "Server key has not been configured. Set it using the `AppMail.configure` block or use `APPMAIL_KEY` environment variable.")
|
11
|
+
end
|
12
|
+
attr_writer :server_key
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module AppMail
|
2
|
+
|
3
|
+
#
|
4
|
+
# A generic error that all errors will inherit from
|
5
|
+
#
|
6
|
+
class Error < StandardError
|
7
|
+
end
|
8
|
+
|
9
|
+
#
|
10
|
+
# Raised when a message cannot be found by its ID
|
11
|
+
#
|
12
|
+
class MessageNotFound < Error
|
13
|
+
def initialize(id)
|
14
|
+
@id = id
|
15
|
+
end
|
16
|
+
|
17
|
+
def message
|
18
|
+
"No message found matching ID '#{@id}'"
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
message
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Raised when a message cannot be found by its ID
|
28
|
+
#
|
29
|
+
class SendError < Error
|
30
|
+
def initialize(code, error_message)
|
31
|
+
@code = code
|
32
|
+
@error_message = error_message
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :code
|
36
|
+
attr_reader :error_message
|
37
|
+
|
38
|
+
def message
|
39
|
+
"[#{@code}] #{@error_message}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s
|
43
|
+
message
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module AppMail
|
2
|
+
class HeaderSet
|
3
|
+
|
4
|
+
def initialize(headers)
|
5
|
+
@headers = headers
|
6
|
+
end
|
7
|
+
|
8
|
+
def [](name)
|
9
|
+
@headers[name.to_s.downcase]
|
10
|
+
end
|
11
|
+
|
12
|
+
def has_key?(key)
|
13
|
+
@headers.has_key?(name.to_s.downcase)
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(*args, &block)
|
17
|
+
@headers.send(*args, &block)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'app_mail/error'
|
2
|
+
require 'app_mail/header_set'
|
3
|
+
require 'app_mail/attachment'
|
4
|
+
|
5
|
+
module AppMail
|
6
|
+
class Message
|
7
|
+
|
8
|
+
#
|
9
|
+
# Find a specific messsage with the given scope
|
10
|
+
#
|
11
|
+
def self.find_with_scope(scope, id)
|
12
|
+
api = scope.client.moonrope.messages.message(:id => id.to_i, :_expansions => scope.expansions)
|
13
|
+
if api.success?
|
14
|
+
Message.new(scope.client, api.data)
|
15
|
+
elsif api.status == 'error' && api.data['code'] == 'MessageNotFound'
|
16
|
+
raise MessageNotFound.new(id)
|
17
|
+
else
|
18
|
+
raise Error, "Couldn't load message from API (#{api.data})"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# If methods are called directly on the Message class, we likely want to see if we can
|
24
|
+
# run them through the global client message scope.
|
25
|
+
#
|
26
|
+
def self.method_missing(name, *args, &block)
|
27
|
+
if MessageScope.instance_methods(false).include?(name)
|
28
|
+
AppMail::Client.instance.messages.send(name, *args, &block)
|
29
|
+
else
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
#
|
35
|
+
# Initialize a new message object with the client and a set of initial attributes.
|
36
|
+
#
|
37
|
+
def initialize(client, attributes)
|
38
|
+
@client = client
|
39
|
+
@attributes = attributes
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
# Return the message ID
|
44
|
+
#
|
45
|
+
def id
|
46
|
+
@attributes['id']
|
47
|
+
end
|
48
|
+
|
49
|
+
#
|
50
|
+
# Return the message token
|
51
|
+
#
|
52
|
+
def token
|
53
|
+
@attributes['token']
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Set a has of all the attributes from the API that should be exposed through
|
58
|
+
# the Message class.
|
59
|
+
#
|
60
|
+
ATTRIBUTES = {
|
61
|
+
:status => [:status, :status],
|
62
|
+
:last_delivery_attempt => [:status, :last_delivery_attempt, :timestamp],
|
63
|
+
:held? => [:status, :held, :boolean],
|
64
|
+
:hold_expiry => [:status, :hold_expiry, :timestamp],
|
65
|
+
:rcpt_to => [:details, :rcpt_to],
|
66
|
+
:mail_from => [:details, :mail_from],
|
67
|
+
:subject => [:details, :subject],
|
68
|
+
:message_id => [:details, :message_id],
|
69
|
+
:timestamp => [:details, :timestamp, :timestamp],
|
70
|
+
:direction => [:details, :direction],
|
71
|
+
:size => [:details, :size],
|
72
|
+
:bounce? => [:details, :bounce, :boolean],
|
73
|
+
:bounce_for_id => [:details, :bounce],
|
74
|
+
:tag => [:details, :tag],
|
75
|
+
:received_with_ssl? => [:details, :received_with_ssl, :boolean],
|
76
|
+
:inspected? => [:inspection, :inspected, :boolean],
|
77
|
+
:spam? => [:inspection, :spam, :boolean],
|
78
|
+
:spam_score => [:inspection, :spam_score],
|
79
|
+
:threat? => [:inspection, :thret, :boolean],
|
80
|
+
:threat_details => [:inspection, :threat_details],
|
81
|
+
:plain_body => [:plain_body],
|
82
|
+
:html_body => [:html_body],
|
83
|
+
}
|
84
|
+
|
85
|
+
#
|
86
|
+
# Catch calls to any of the default attributes for a message and return the
|
87
|
+
# data however we'd like it
|
88
|
+
#
|
89
|
+
def method_missing(name, *args, &block)
|
90
|
+
if mapping = ATTRIBUTES[name.to_sym]
|
91
|
+
expansion, attribute, type = mapping
|
92
|
+
value = from_expansion(expansion, attribute)
|
93
|
+
case type
|
94
|
+
when :timestamp
|
95
|
+
value ? Time.at(value) : nil
|
96
|
+
when :boolean
|
97
|
+
value == 1
|
98
|
+
else
|
99
|
+
value
|
100
|
+
end
|
101
|
+
else
|
102
|
+
super
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# Return a set of headers which can be queried like a hash however looking up
|
108
|
+
# values using [] will be case-insensitive.
|
109
|
+
#
|
110
|
+
def headers
|
111
|
+
@headers ||= HeaderSet.new(from_expansion(:headers))
|
112
|
+
end
|
113
|
+
|
114
|
+
#
|
115
|
+
# Return an array of attachment objects
|
116
|
+
#
|
117
|
+
def attachments
|
118
|
+
@attachments ||= from_expansion(:attachments).map do |a|
|
119
|
+
Attachment.new(a)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
# Return the full raw message
|
125
|
+
#
|
126
|
+
def raw_message
|
127
|
+
@raw_message ||= Base64.decode64(from_expansion(:raw_message))
|
128
|
+
end
|
129
|
+
|
130
|
+
private
|
131
|
+
|
132
|
+
def from_expansion(expansion, attribute = nil, loaded = false)
|
133
|
+
if @attributes.has_key?(expansion.to_s) || loaded
|
134
|
+
attribute ? @attributes[expansion.to_s][attribute.to_s] : @attributes[expansion.to_s]
|
135
|
+
else
|
136
|
+
load_expansions(expansion)
|
137
|
+
from_expansion(expansion, attribute, true)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def load_expansions(*names)
|
142
|
+
puts "\e[31mLoading expansion #{names}\e[0m"
|
143
|
+
api = @client.moonrope.messages.message(:id => self.id, :_expansions => names)
|
144
|
+
if api.success?
|
145
|
+
names.each do |expansion_name|
|
146
|
+
if api.data.has_key?(expansion_name.to_s)
|
147
|
+
@attributes[expansion_name.to_s] = api.data[expansion_name.to_s]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
else
|
151
|
+
raise AppMail::Error, "Couldn't load expansion data (#{names.join(', ')}) for message ID '#{self.id}'"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'app_mail/message'
|
2
|
+
|
3
|
+
module AppMail
|
4
|
+
class MessageScope
|
5
|
+
|
6
|
+
attr_reader :client
|
7
|
+
|
8
|
+
def initialize(client)
|
9
|
+
@client = client
|
10
|
+
@includes = []
|
11
|
+
end
|
12
|
+
|
13
|
+
#
|
14
|
+
# Add includes to the scope
|
15
|
+
#
|
16
|
+
def includes(*includes)
|
17
|
+
@includes.push(*includes)
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# Return the current includes
|
23
|
+
#
|
24
|
+
def expansions
|
25
|
+
if @includes.include?(:all)
|
26
|
+
true
|
27
|
+
else
|
28
|
+
@includes.map(&:to_s)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Find a given message by its ID
|
34
|
+
#
|
35
|
+
def find_by_id(id)
|
36
|
+
Message.find_with_scope(self, id)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'app_mail/send_result'
|
3
|
+
|
4
|
+
module AppMail
|
5
|
+
class SendMessage
|
6
|
+
|
7
|
+
def initialize(client)
|
8
|
+
@client = client
|
9
|
+
@attributes = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def send!
|
13
|
+
api = @client.moonrope.request(:send, :message, @attributes)
|
14
|
+
if api.success?
|
15
|
+
SendResult.new(@client, api.data)
|
16
|
+
elsif api.status == 'error'
|
17
|
+
raise SendError.new(api.data['code'], api.data['message'])
|
18
|
+
else
|
19
|
+
raise Error, "Couldn't send message"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def from(address)
|
24
|
+
@attributes[:from] = address
|
25
|
+
end
|
26
|
+
|
27
|
+
def to(*addresses)
|
28
|
+
@attributes[:to] ||= []
|
29
|
+
@attributes[:to] += addresses
|
30
|
+
end
|
31
|
+
|
32
|
+
def cc(*addresses)
|
33
|
+
@attributes[:cc] ||= []
|
34
|
+
@attributes[:cc] += addresses
|
35
|
+
end
|
36
|
+
|
37
|
+
def bcc(*addresses)
|
38
|
+
@attributes[:bcc] ||= []
|
39
|
+
@attributes[:bcc] += addresses
|
40
|
+
end
|
41
|
+
|
42
|
+
def subject(subject)
|
43
|
+
@attributes[:subject] = subject
|
44
|
+
end
|
45
|
+
|
46
|
+
def tag(tag)
|
47
|
+
@attributes[:tag] = subject
|
48
|
+
end
|
49
|
+
|
50
|
+
def reply_to(reply_to)
|
51
|
+
@attributes[:reply_to] = subject
|
52
|
+
end
|
53
|
+
|
54
|
+
def plain_body(content)
|
55
|
+
@attributes[:plain_body] = content
|
56
|
+
end
|
57
|
+
|
58
|
+
def html_body(content)
|
59
|
+
@attributes[:html_body] = content
|
60
|
+
end
|
61
|
+
|
62
|
+
def header(key, value)
|
63
|
+
@attributes[:headers] ||= {}
|
64
|
+
@attributes[:headers][key.to_s] = value
|
65
|
+
end
|
66
|
+
|
67
|
+
def attach(filename, content_type, data)
|
68
|
+
@attributes[:attachments] ||= []
|
69
|
+
@attributes[:attachments] << {
|
70
|
+
:name => filename,
|
71
|
+
:content_type => content_type,
|
72
|
+
:data => Base64.encode64(data)
|
73
|
+
}
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module AppMail
|
2
|
+
class SendResult
|
3
|
+
|
4
|
+
def initialize(client, result)
|
5
|
+
@client = client
|
6
|
+
@result = result
|
7
|
+
end
|
8
|
+
|
9
|
+
def message_id
|
10
|
+
@result['message_id']
|
11
|
+
end
|
12
|
+
|
13
|
+
def recipients
|
14
|
+
@recipients ||= begin
|
15
|
+
@result['messages'].each_with_object({}) do |(recipient, message_details), hash|
|
16
|
+
hash[recipient.to_s.downcase] = Message.new(@client, message_details)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](recipient)
|
22
|
+
recipients[recipient.to_s.downcase]
|
23
|
+
end
|
24
|
+
|
25
|
+
def size
|
26
|
+
recipients.size
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
data/lib/appmail.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'app_mail'
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: appmail
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adam Cooke
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-10-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: moonrope-client
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.2
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '1.1'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.0.2
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.1'
|
33
|
+
description: Ruby library for the AppMail E-Mail Platform
|
34
|
+
email:
|
35
|
+
- me@adamcooke.io
|
36
|
+
executables: []
|
37
|
+
extensions: []
|
38
|
+
extra_rdoc_files: []
|
39
|
+
files:
|
40
|
+
- lib/app_mail.rb
|
41
|
+
- lib/app_mail/attachment.rb
|
42
|
+
- lib/app_mail/client.rb
|
43
|
+
- lib/app_mail/config.rb
|
44
|
+
- lib/app_mail/error.rb
|
45
|
+
- lib/app_mail/header_set.rb
|
46
|
+
- lib/app_mail/message.rb
|
47
|
+
- lib/app_mail/message_scope.rb
|
48
|
+
- lib/app_mail/send_message.rb
|
49
|
+
- lib/app_mail/send_result.rb
|
50
|
+
- lib/app_mail/version.rb
|
51
|
+
- lib/appmail.rb
|
52
|
+
homepage: https://appmail.io
|
53
|
+
licenses:
|
54
|
+
- MIT
|
55
|
+
metadata: {}
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
require_paths:
|
59
|
+
- lib
|
60
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">="
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
requirements: []
|
71
|
+
rubyforge_project:
|
72
|
+
rubygems_version: 2.5.1
|
73
|
+
signing_key:
|
74
|
+
specification_version: 4
|
75
|
+
summary: Ruby library for the AppMail E-Mail Platform
|
76
|
+
test_files: []
|