mailflow 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/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: []
|