mailflow 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,3 @@
1
+ module Mailflow
2
+ VERSION = '0.0.1'
3
+ 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: []