mailflow 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/mailflow/attachment.rb +15 -0
- data/lib/mailflow/client.rb +39 -0
- data/lib/mailflow/config.rb +18 -0
- data/lib/mailflow/error.rb +30 -0
- data/lib/mailflow/header_set.rb +25 -0
- data/lib/mailflow/message.rb +104 -0
- data/lib/mailflow/message_scope.rb +26 -0
- data/lib/mailflow/send_message.rb +69 -0
- data/lib/mailflow/send_raw_message.rb +43 -0
- data/lib/mailflow/send_result.rb +27 -0
- data/lib/mailflow/version.rb +3 -0
- data/lib/mailflow.rb +22 -0
- metadata +75 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f617724f63d55f15c40dd14b1b7156f29d7d0dfccf267c11cf9f7f8a5191424a
|
4
|
+
data.tar.gz: efe48e465aeb2b3c6f41989410277a814b0c34691e2f4725c2f4470ea746a1af
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f376c6309c998b0d778b63aacc595b51cf6985d4f21841ab11b15cdc6f1bd793ed3ba0b2f5a60c9e61f1e90397b40eefabe05cd8dd3b8fde5c6574cb797e5f83
|
7
|
+
data.tar.gz: f653456b8f272d9255723cbf8b7e8d09e0894c80e56db4eb4df18190da2a1fe5716de889b70573eddd807a9682774a510135f50faa204b3e455d5b94c842a851
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
3
|
+
module Mailflow
|
4
|
+
class Attachment
|
5
|
+
attr_reader :filename, :content_type, :size, :hash, :data
|
6
|
+
|
7
|
+
def initialize(attributes)
|
8
|
+
@filename = attributes['filename']
|
9
|
+
@content_type = attributes['content_type']
|
10
|
+
@size = attributes['size']
|
11
|
+
@hash = attributes['hash']
|
12
|
+
@data = Base64.decode64(attributes['data'])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'moonrope_client'
|
2
|
+
require 'mailflow/message_scope'
|
3
|
+
require 'mailflow/send_message'
|
4
|
+
require 'mailflow/send_raw_message'
|
5
|
+
|
6
|
+
module Mailflow
|
7
|
+
class Client
|
8
|
+
attr_reader :host, :server_key
|
9
|
+
|
10
|
+
def self.instance
|
11
|
+
@instance ||= new(Mailflow.config.host, Mailflow.config.server_key)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(host, server_key)
|
15
|
+
@host = host
|
16
|
+
@server_key = server_key
|
17
|
+
end
|
18
|
+
|
19
|
+
def messages
|
20
|
+
@messages ||= MessageScope.new(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
def send_message(&block)
|
24
|
+
send_message = SendMessage.new(self)
|
25
|
+
block.call(send_message)
|
26
|
+
send_message.send!
|
27
|
+
end
|
28
|
+
|
29
|
+
def send_raw_message(&block)
|
30
|
+
send_raw_message = SendRawMessage.new(self)
|
31
|
+
block.call(send_raw_message)
|
32
|
+
send_raw_message.send!
|
33
|
+
end
|
34
|
+
|
35
|
+
def moonrope
|
36
|
+
@moonrope ||= MoonropeClient::Connection.new(@host, headers: { 'X-Server-API-Key' => @server_key }, ssl: true)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Mailflow
|
2
|
+
class Config
|
3
|
+
attr_accessor :host, :server_key
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@host = ENV['MAILFLOW_HOST']
|
7
|
+
@server_key = ENV['MAILFLOW_KEY']
|
8
|
+
end
|
9
|
+
|
10
|
+
def host
|
11
|
+
@host ||= raise(Error, "The host has not been configured. Please set it using the `Mailflow.configure` block or utilize the `MAILFLOW_HOST` environment variable.")
|
12
|
+
end
|
13
|
+
|
14
|
+
def server_key
|
15
|
+
@server_key ||= raise(Error, "The server key has not been configured. Please set it using the `Mailflow.configure` block or make use of the `MAILFLOW_KEY` environment variable.")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Mailflow
|
2
|
+
class Error < StandardError; end
|
3
|
+
|
4
|
+
class MessageNotFound < Error
|
5
|
+
def initialize(id)
|
6
|
+
@id = id
|
7
|
+
end
|
8
|
+
|
9
|
+
def message
|
10
|
+
"No message was found that matches the provided ID. '#{@id}'"
|
11
|
+
end
|
12
|
+
|
13
|
+
alias to_s message
|
14
|
+
end
|
15
|
+
|
16
|
+
class SendError < Error
|
17
|
+
def initialize(code, error_message)
|
18
|
+
@code = code
|
19
|
+
@error_message = error_message
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :code, :error_message
|
23
|
+
|
24
|
+
def message
|
25
|
+
"[#{@code}] #{@error_message}"
|
26
|
+
end
|
27
|
+
|
28
|
+
alias to_s message
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Mailflow
|
2
|
+
class HeaderSet
|
3
|
+
attr_reader :headers
|
4
|
+
|
5
|
+
def initialize(headers)
|
6
|
+
@headers = headers.transform_keys(&:to_s).transform_keys(&:downcase)
|
7
|
+
end
|
8
|
+
|
9
|
+
def [](name)
|
10
|
+
headers[name.to_s.downcase]
|
11
|
+
end
|
12
|
+
|
13
|
+
def has_key?(key)
|
14
|
+
headers.has_key?(key.to_s.downcase)
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(*args, &block)
|
18
|
+
headers.send(*args, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def respond_to_missing?(method_name, include_private = false)
|
22
|
+
headers.respond_to?(method_name) || super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'mailflow/error'
|
2
|
+
require 'mailflow/header_set'
|
3
|
+
require 'mailflow/attachment'
|
4
|
+
|
5
|
+
module Mailflow
|
6
|
+
class Message
|
7
|
+
attr_reader :id, :token, :headers, :attachments, :raw_message
|
8
|
+
|
9
|
+
def self.find_with_scope(scope, id)
|
10
|
+
api = scope.client.moonrope.messages.message(id: id.to_i, _expansions: scope.expansions)
|
11
|
+
if api.success?
|
12
|
+
Message.new(scope.client, api.data)
|
13
|
+
elsif api.status == 'error' && api.data['code'] == 'MessageNotFound'
|
14
|
+
raise MessageNotFound.new(id)
|
15
|
+
else
|
16
|
+
raise Error, "Couldn't load message from API (#{api.data})"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.method_missing(name, *args, &block)
|
21
|
+
if MessageScope.instance_methods(false).include?(name)
|
22
|
+
Mailflow::Client.instance.messages.send(name, *args, &block)
|
23
|
+
else
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def initialize(client, attributes)
|
29
|
+
@client = client
|
30
|
+
@attributes = attributes
|
31
|
+
@id = @attributes['id']
|
32
|
+
@token = @attributes['token']
|
33
|
+
@headers = HeaderSet.new(from_expansion(:headers))
|
34
|
+
@attachments = from_expansion(:attachments).map { |a| Attachment.new(a) }
|
35
|
+
@raw_message = Base64.decode64(from_expansion(:raw_message))
|
36
|
+
end
|
37
|
+
|
38
|
+
ATTRIBUTES = {
|
39
|
+
status: [:status, :status],
|
40
|
+
last_delivery_attempt: [:status, :last_delivery_attempt, :timestamp],
|
41
|
+
held?: [:status, :held, :boolean],
|
42
|
+
hold_expiry: [:status, :hold_expiry, :timestamp],
|
43
|
+
rcpt_to: [:details, :rcpt_to],
|
44
|
+
mail_from: [:details, :mail_from],
|
45
|
+
subject: [:details, :subject],
|
46
|
+
message_id: [:details, :message_id],
|
47
|
+
timestamp: [:details, :timestamp, :timestamp],
|
48
|
+
direction: [:details, :direction],
|
49
|
+
size: [:details, :size],
|
50
|
+
bounce?: [:details, :bounce, :boolean],
|
51
|
+
bounce_for_id: [:details, :bounce],
|
52
|
+
tag: [:details, :tag],
|
53
|
+
received_with_ssl?: [:details, :received_with_ssl, :boolean],
|
54
|
+
inspected?: [:inspection, :inspected, :boolean],
|
55
|
+
spam?: [:inspection, :spam, :boolean],
|
56
|
+
spam_score: [:inspection, :spam_score],
|
57
|
+
threat?: [:inspection, :threat, :boolean],
|
58
|
+
threat_details: [:inspection, :threat_details],
|
59
|
+
plain_body: [:plain_body],
|
60
|
+
html_body: [:html_body]
|
61
|
+
}
|
62
|
+
|
63
|
+
def method_missing(name, *args, &block)
|
64
|
+
if mapping = ATTRIBUTES[name.to_sym]
|
65
|
+
expansion, attribute, type = mapping
|
66
|
+
value = from_expansion(expansion, attribute)
|
67
|
+
case type
|
68
|
+
when :timestamp
|
69
|
+
value ? Time.at(value) : nil
|
70
|
+
when :boolean
|
71
|
+
value == 1
|
72
|
+
else
|
73
|
+
value
|
74
|
+
end
|
75
|
+
else
|
76
|
+
super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def from_expansion(expansion, attribute = nil, loaded = false)
|
83
|
+
if @attributes.key?(expansion.to_s) || loaded
|
84
|
+
attribute ? @attributes[expansion.to_s][attribute.to_s] : @attributes[expansion.to_s]
|
85
|
+
else
|
86
|
+
load_expansions(expansion)
|
87
|
+
from_expansion(expansion, attribute, true)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def load_expansions(*names)
|
92
|
+
api = @client.moonrope.messages.message(id: self.id, _expansions: names)
|
93
|
+
if api.success?
|
94
|
+
names.each do |expansion_name|
|
95
|
+
if api.data.key?(expansion_name.to_s)
|
96
|
+
@attributes[expansion_name.to_s] = api.data[expansion_name.to_s]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
else
|
100
|
+
raise Mailflow::Error, "Couldn't load expansion data (#{names.join(', ')}) for message ID '#{self.id}'"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'mailflow/message'
|
2
|
+
|
3
|
+
module Mailflow
|
4
|
+
class MessageScope
|
5
|
+
attr_reader :client
|
6
|
+
|
7
|
+
def initialize(client)
|
8
|
+
@client = client
|
9
|
+
@includes = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def includes(*includes)
|
13
|
+
@includes.concat(includes)
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def expansions
|
18
|
+
return true if @includes.include?(:all)
|
19
|
+
@includes.map(&:to_s)
|
20
|
+
end
|
21
|
+
|
22
|
+
def find_by_id(id)
|
23
|
+
Message.find_with_scope(self, id)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'mailflow/send_result'
|
3
|
+
|
4
|
+
module Mailflow
|
5
|
+
class SendMessage
|
6
|
+
attr_reader :client
|
7
|
+
attr_accessor :attributes
|
8
|
+
|
9
|
+
def initialize(client)
|
10
|
+
@client = client
|
11
|
+
initialize_attributes
|
12
|
+
end
|
13
|
+
|
14
|
+
def send!
|
15
|
+
api = client.moonrope.request(:send, :message, attributes)
|
16
|
+
return SendResult.new(client, api.data) if api.success?
|
17
|
+
|
18
|
+
raise SendError.new(api.data['code'], api.data['message']) if api.status == 'error'
|
19
|
+
|
20
|
+
raise Error, "API response unsuccessful: #{api.data}"
|
21
|
+
end
|
22
|
+
|
23
|
+
%i[from sender subject tag reply_to plain_body html_body].each do |method_name|
|
24
|
+
define_method(method_name) do |value|
|
25
|
+
attributes[method_name] = value
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
%i[to cc bcc].each do |method_name|
|
30
|
+
define_method(method_name) do |*addresses|
|
31
|
+
add_addresses(method_name, addresses)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def header(key, value)
|
36
|
+
attributes[:headers][key.to_s] = value
|
37
|
+
end
|
38
|
+
|
39
|
+
def attach(filename, content_type, data)
|
40
|
+
attributes[:attachments] << {
|
41
|
+
name: filename,
|
42
|
+
content_type: content_type,
|
43
|
+
data: Base64.encode64(data)
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def initialize_attributes
|
50
|
+
@attributes = {
|
51
|
+
to: [],
|
52
|
+
cc: [],
|
53
|
+
bcc: [],
|
54
|
+
headers: {},
|
55
|
+
attachments: []
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_addresses(type, addresses)
|
60
|
+
sanitized_addresses = sanitize_addresses(addresses)
|
61
|
+
attributes[type] += sanitized_addresses
|
62
|
+
end
|
63
|
+
|
64
|
+
def sanitize_addresses(addresses)
|
65
|
+
# implement address sanitization logic here
|
66
|
+
addresses
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'mailflow/send_result'
|
3
|
+
|
4
|
+
module Mailflow
|
5
|
+
class SendRawMessage
|
6
|
+
attr_accessor :attributes
|
7
|
+
|
8
|
+
def initialize(client)
|
9
|
+
@client = client
|
10
|
+
@attributes = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def send!
|
14
|
+
api = @client.moonrope.request(:send, :raw, @attributes)
|
15
|
+
handle_api_response(api)
|
16
|
+
end
|
17
|
+
|
18
|
+
def mail_from(address)
|
19
|
+
@attributes[:mail_from] = address
|
20
|
+
end
|
21
|
+
|
22
|
+
def rcpt_to(*addresses)
|
23
|
+
@attributes[:rcpt_to] ||= []
|
24
|
+
@attributes[:rcpt_to] += addresses
|
25
|
+
end
|
26
|
+
|
27
|
+
def data(data)
|
28
|
+
@attributes[:data] = Base64.encode64(data)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def handle_api_response(api)
|
34
|
+
if api.success?
|
35
|
+
SendResult.new(@client, api.data)
|
36
|
+
elsif api.status == 'error'
|
37
|
+
raise SendError.new(api.data['code'], api.data['message'])
|
38
|
+
else
|
39
|
+
raise Error, "Couldn't send message"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Mailflow
|
2
|
+
class SendResult
|
3
|
+
attr_reader :message_id, :recipients
|
4
|
+
|
5
|
+
def initialize(client, result)
|
6
|
+
@client = client
|
7
|
+
@message_id = result['message_id']
|
8
|
+
@recipients = build_recipients(result['messages'])
|
9
|
+
end
|
10
|
+
|
11
|
+
def [](recipient)
|
12
|
+
recipients[recipient.to_s.downcase]
|
13
|
+
end
|
14
|
+
|
15
|
+
def size
|
16
|
+
recipients.size
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def build_recipients(messages)
|
22
|
+
messages.each_with_object({}) do |(recipient, message_details), hash|
|
23
|
+
hash[recipient.to_s.downcase] = Message.new(@client, message_details)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/mailflow.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'mailflow'
|
2
|
+
require 'mailflow/client'
|
3
|
+
require 'mailflow/config'
|
4
|
+
|
5
|
+
module Mailflow
|
6
|
+
class << self
|
7
|
+
attr_reader :config
|
8
|
+
|
9
|
+
def configure
|
10
|
+
@config ||= Config.new
|
11
|
+
yield config if block_given?
|
12
|
+
end
|
13
|
+
|
14
|
+
def send_message(&block)
|
15
|
+
Mailflow::Client.instance.send_message(&block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def send_raw_message(&block)
|
19
|
+
Mailflow::Client.instance.send_raw_message(&block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mailflow
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mailflow
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-09-28 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.5
|
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.5
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '1.1'
|
33
|
+
description: Mailflow Ruby library
|
34
|
+
email:
|
35
|
+
- support@mailflow.cloud
|
36
|
+
executables: []
|
37
|
+
extensions: []
|
38
|
+
extra_rdoc_files: []
|
39
|
+
files:
|
40
|
+
- lib/mailflow.rb
|
41
|
+
- lib/mailflow/attachment.rb
|
42
|
+
- lib/mailflow/client.rb
|
43
|
+
- lib/mailflow/config.rb
|
44
|
+
- lib/mailflow/error.rb
|
45
|
+
- lib/mailflow/header_set.rb
|
46
|
+
- lib/mailflow/message.rb
|
47
|
+
- lib/mailflow/message_scope.rb
|
48
|
+
- lib/mailflow/send_message.rb
|
49
|
+
- lib/mailflow/send_raw_message.rb
|
50
|
+
- lib/mailflow/send_result.rb
|
51
|
+
- lib/mailflow/version.rb
|
52
|
+
homepage: https://mailflow.cloud
|
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
|
+
rubygems_version: 3.2.15
|
72
|
+
signing_key:
|
73
|
+
specification_version: 4
|
74
|
+
summary: Mailflow Ruby library
|
75
|
+
test_files: []
|