openemail 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.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +183 -0
  3. data/lib/openemail/api_paths.rb +249 -0
  4. data/lib/openemail/client.rb +69 -0
  5. data/lib/openemail/constants.rb +851 -0
  6. data/lib/openemail/credentials.rb +53 -0
  7. data/lib/openemail/decode.rb +34 -0
  8. data/lib/openemail/defaults.rb +111 -0
  9. data/lib/openemail/endpoint.rb +144 -0
  10. data/lib/openemail/errors.rb +121 -0
  11. data/lib/openemail/http.rb +172 -0
  12. data/lib/openemail/languages.rb +42 -0
  13. data/lib/openemail/pagination.rb +63 -0
  14. data/lib/openemail/protocol.rb +169 -0
  15. data/lib/openemail/raw_body.rb +83 -0
  16. data/lib/openemail/request_path.rb +50 -0
  17. data/lib/openemail/resource.rb +70 -0
  18. data/lib/openemail/resources/account.rb +64 -0
  19. data/lib/openemail/resources/addresses.rb +38 -0
  20. data/lib/openemail/resources/analytics.rb +17 -0
  21. data/lib/openemail/resources/app_host.rb +23 -0
  22. data/lib/openemail/resources/audiences.rb +90 -0
  23. data/lib/openemail/resources/billing.rb +64 -0
  24. data/lib/openemail/resources/branding.rb +24 -0
  25. data/lib/openemail/resources/broadcasts.rb +65 -0
  26. data/lib/openemail/resources/calendar.rb +59 -0
  27. data/lib/openemail/resources/chats.rb +31 -0
  28. data/lib/openemail/resources/contacts.rb +111 -0
  29. data/lib/openemail/resources/dns_connections.rb +19 -0
  30. data/lib/openemail/resources/domains.rb +153 -0
  31. data/lib/openemail/resources/drafts.rb +35 -0
  32. data/lib/openemail/resources/emails.rb +100 -0
  33. data/lib/openemail/resources/encryption.rb +19 -0
  34. data/lib/openemail/resources/exports.rb +27 -0
  35. data/lib/openemail/resources/files.rb +95 -0
  36. data/lib/openemail/resources/forms.rb +111 -0
  37. data/lib/openemail/resources/imports.rb +86 -0
  38. data/lib/openemail/resources/keys.rb +127 -0
  39. data/lib/openemail/resources/labels.rb +39 -0
  40. data/lib/openemail/resources/languages.rb +11 -0
  41. data/lib/openemail/resources/me.rb +19 -0
  42. data/lib/openemail/resources/members.rb +74 -0
  43. data/lib/openemail/resources/provider_imports.rb +40 -0
  44. data/lib/openemail/resources/roles.rb +39 -0
  45. data/lib/openemail/resources/rules.rb +57 -0
  46. data/lib/openemail/resources/security.rb +19 -0
  47. data/lib/openemail/resources/senders.rb +15 -0
  48. data/lib/openemail/resources/settings.rb +16 -0
  49. data/lib/openemail/resources/subscriptions.rb +30 -0
  50. data/lib/openemail/resources/support.rb +11 -0
  51. data/lib/openemail/resources/suppressions.rb +31 -0
  52. data/lib/openemail/resources/temp_mail.rb +67 -0
  53. data/lib/openemail/resources/templates.rb +151 -0
  54. data/lib/openemail/resources/threads.rb +104 -0
  55. data/lib/openemail/resources/tools.rb +19 -0
  56. data/lib/openemail/resources/tracking.rb +63 -0
  57. data/lib/openemail/resources/webhooks.rb +142 -0
  58. data/lib/openemail/resources/workspaces.rb +28 -0
  59. data/lib/openemail/results.rb +33 -0
  60. data/lib/openemail/transport.rb +272 -0
  61. data/lib/openemail/update_notice.rb +69 -0
  62. data/lib/openemail/version.rb +5 -0
  63. data/lib/openemail/webhook.rb +82 -0
  64. data/lib/openemail/wire.rb +95 -0
  65. data/lib/openemail.rb +125 -0
  66. metadata +109 -0
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenEmail
4
+ module Wire
5
+ RECIPIENT_FIELDS = %w[to cc bcc].freeze
6
+ BASE64_TEXT = %r{\A(?:[A-Za-z0-9+/_-]{4})*(?:[A-Za-z0-9+/_-]{2}==|[A-Za-z0-9+/_-]{3}=)?\z}
7
+ BASE64_WHITESPACE = " \t\n\f\r"
8
+ ISO_INSTANT = "%Y-%m-%dT%H:%M:%S.%LZ"
9
+
10
+ module_function
11
+
12
+ def body(value, fields)
13
+ base = value.nil? ? {} : hash_of(value)
14
+ base.merge(fields).transform_keys(&:to_s)
15
+ end
16
+
17
+ def hash_of(value)
18
+ return value if value.is_a?(Hash)
19
+ return value.to_hash if value.respond_to?(:to_hash)
20
+
21
+ raise ArgumentError, Defaults::MESSAGES[:BODY_SHAPE]
22
+ end
23
+
24
+ def email(body)
25
+ recipients(body).tap do |wire|
26
+ next unless wire.key?("attachments")
27
+
28
+ attachments = wire.delete("attachments")
29
+ wire["attachments"] = list(attachments).map { |attachment| attachment(attachment) } unless attachments.nil?
30
+ end
31
+ end
32
+
33
+ def recipients(body)
34
+ body.dup.tap do |wire|
35
+ RECIPIENT_FIELDS.each { |field| wire[field] = many(wire[field]) unless wire[field].nil? }
36
+ wire["scheduledAt"] = instant(wire["scheduledAt"]) if wire.key?("scheduledAt")
37
+ end
38
+ end
39
+
40
+ def many(value)
41
+ collection?(value) ? list(value) : [value]
42
+ end
43
+
44
+ def attachment(value)
45
+ entry = hash_of(value).transform_keys(&:to_s)
46
+ return {"fileId" => entry["fileId"]} if entry.key?("fileId")
47
+
48
+ wire = {"filename" => entry["filename"], "content" => content(entry["content"])}
49
+ wire["contentType"] = entry["contentType"] if entry.key?("contentType")
50
+ wire
51
+ end
52
+
53
+ def content(value)
54
+ return [RawBody.read(value)].pack("m0") unless value.is_a?(String) && value.encoding != Encoding::BINARY
55
+ return value if BASE64_TEXT.match?(value.delete(BASE64_WHITESPACE))
56
+
57
+ raise ArgumentError, Defaults::MESSAGES[:ATTACHMENT_CONTENT_SHAPE]
58
+ end
59
+
60
+ def instant(value)
61
+ return value.getutc.strftime(ISO_INSTANT) if value.respond_to?(:getutc)
62
+ return value.new_offset(0).strftime(ISO_INSTANT) if value.is_a?(DateTime)
63
+ return value.iso8601 if value.is_a?(Date)
64
+
65
+ value
66
+ end
67
+
68
+ def number(value)
69
+ return value if value.is_a?(Integer)
70
+ return value unless value.is_a?(Numeric) && value.finite?
71
+
72
+ (value == value.truncate) ? value.truncate : value.to_f
73
+ end
74
+
75
+ def scalar(value)
76
+ number(instant(value))
77
+ end
78
+
79
+ def collection?(value)
80
+ value.is_a?(Array) || value.is_a?(Set)
81
+ end
82
+
83
+ def list(value)
84
+ collection?(value) ? value.to_a : value
85
+ end
86
+
87
+ def json_ready(value)
88
+ case value
89
+ when Hash then value.to_h { |key, item| [key, json_ready(item)] }
90
+ when Array, Set then value.map { |item| json_ready(item) }
91
+ else scalar(value)
92
+ end
93
+ end
94
+ end
95
+ end
data/lib/openemail.rb ADDED
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ipaddr"
4
+ require "json"
5
+ require "net/http"
6
+ require "openssl"
7
+ require "pathname"
8
+ require "securerandom"
9
+ require "time"
10
+ require "uri"
11
+
12
+ require_relative "openemail/version"
13
+ require_relative "openemail/constants"
14
+ require_relative "openemail/protocol"
15
+ require_relative "openemail/api_paths"
16
+ require_relative "openemail/defaults"
17
+ require_relative "openemail/errors"
18
+ require_relative "openemail/credentials"
19
+ require_relative "openemail/endpoint"
20
+ require_relative "openemail/decode"
21
+ require_relative "openemail/request_path"
22
+ require_relative "openemail/raw_body"
23
+ require_relative "openemail/wire"
24
+ require_relative "openemail/http"
25
+ require_relative "openemail/update_notice"
26
+ require_relative "openemail/transport"
27
+ require_relative "openemail/results"
28
+ require_relative "openemail/pagination"
29
+ require_relative "openemail/webhook"
30
+ require_relative "openemail/languages"
31
+ require_relative "openemail/resource"
32
+
33
+ Dir[File.join(__dir__, "openemail", "resources", "*.rb")].sort.each { |path| require path }
34
+
35
+ require_relative "openemail/client"
36
+
37
+ module OpenEmail
38
+ @default_client = nil
39
+ @default_lock = Mutex.new
40
+
41
+ class << self
42
+ def new(...)
43
+ Client.new(...)
44
+ end
45
+
46
+ def create_client(key = nil, api_key: nil, access_token: nil, base_url: nil, **options)
47
+ raise ArgumentError, Defaults::MESSAGES[:KEY_TWICE] unless key.nil? || api_key.nil?
48
+
49
+ api_key = key if api_key.nil?
50
+ explicit = !api_key.nil? || !access_token.nil?
51
+ api_key = from_environment(Protocol::ENV_VARS[:API_KEY]) unless explicit
52
+ access_token = from_environment(Protocol::ENV_VARS[:ACCESS_TOKEN]) unless explicit || !api_key.nil?
53
+ base_url = Endpoint.normalise(base_url || from_environment(Protocol::ENV_VARS[:BASE_URL]))
54
+
55
+ Client.new(api_key:, access_token:, base_url:, **options)
56
+ end
57
+
58
+ def init(...)
59
+ created = create_client(...)
60
+ @default_lock.synchronize { @default_client = created }
61
+ end
62
+
63
+ def client
64
+ @default_lock.synchronize { @default_client ||= create_client }
65
+ end
66
+
67
+ def reset_client
68
+ @default_lock.synchronize { @default_client = nil }
69
+ end
70
+
71
+ def create_temp_mail(base_url: nil, inbox_token: nil, adapter: nil, max_retries: nil, timeout: nil, user_agent: nil,
72
+ headers: nil, disable_update_notice: false)
73
+ base_url = Endpoint.normalise(base_url || from_environment(Protocol::ENV_VARS[:BASE_URL]))
74
+ transport = Transport.new(base_url:, inbox_token:, adapter:, max_retries:, timeout:, user_agent:, headers:,
75
+ disable_update_notice:)
76
+ Resources::TempMail.new(transport)
77
+ end
78
+
79
+ def verify_webhook_signature(payload:, headers:, secret:, tolerance_seconds: nil)
80
+ Webhook.verify(payload:, headers:, secret:, tolerance_seconds:)
81
+ end
82
+
83
+ def to_base64(bytes)
84
+ [RawBody.read(bytes)].pack("m0")
85
+ end
86
+
87
+ def api_key?(value)
88
+ Credentials.api_key?(value)
89
+ end
90
+
91
+ def access_token?(value)
92
+ Credentials.access_token?(value)
93
+ end
94
+
95
+ def sealed?(message)
96
+ encryption = message[:encryption] || message["encryption"]
97
+ return false unless encryption.is_a?(Hash)
98
+
99
+ Protocol::SEALED_ENCRYPTION_FORMATS.include?(encryption[:format] || encryption["format"])
100
+ end
101
+
102
+ def resolve_language(input)
103
+ Languages.resolve(input)
104
+ end
105
+
106
+ def language_by_code(code)
107
+ Languages.by_code(code)
108
+ end
109
+
110
+ def rtl_language?(code)
111
+ Languages.rtl?(code)
112
+ end
113
+
114
+ Client::NAMESPACES.each_key do |name|
115
+ define_method(name) { client.public_send(name) }
116
+ end
117
+
118
+ private
119
+
120
+ def from_environment(name)
121
+ value = ENV.fetch(name, nil)
122
+ (value.nil? || value.empty?) ? nil : value
123
+ end
124
+ end
125
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: openemail
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - OpenEmail
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 2026-10-04 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Send email and broadcasts, work with threads, drafts, labels, templates,
13
+ rules, webhooks, tracking, contacts, audiences, sign-up forms, domains, suppressions,
14
+ files, API keys, roles, members, mailbox imports and disposable inboxes from Ruby.
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - README.md
20
+ - lib/openemail.rb
21
+ - lib/openemail/api_paths.rb
22
+ - lib/openemail/client.rb
23
+ - lib/openemail/constants.rb
24
+ - lib/openemail/credentials.rb
25
+ - lib/openemail/decode.rb
26
+ - lib/openemail/defaults.rb
27
+ - lib/openemail/endpoint.rb
28
+ - lib/openemail/errors.rb
29
+ - lib/openemail/http.rb
30
+ - lib/openemail/languages.rb
31
+ - lib/openemail/pagination.rb
32
+ - lib/openemail/protocol.rb
33
+ - lib/openemail/raw_body.rb
34
+ - lib/openemail/request_path.rb
35
+ - lib/openemail/resource.rb
36
+ - lib/openemail/resources/account.rb
37
+ - lib/openemail/resources/addresses.rb
38
+ - lib/openemail/resources/analytics.rb
39
+ - lib/openemail/resources/app_host.rb
40
+ - lib/openemail/resources/audiences.rb
41
+ - lib/openemail/resources/billing.rb
42
+ - lib/openemail/resources/branding.rb
43
+ - lib/openemail/resources/broadcasts.rb
44
+ - lib/openemail/resources/calendar.rb
45
+ - lib/openemail/resources/chats.rb
46
+ - lib/openemail/resources/contacts.rb
47
+ - lib/openemail/resources/dns_connections.rb
48
+ - lib/openemail/resources/domains.rb
49
+ - lib/openemail/resources/drafts.rb
50
+ - lib/openemail/resources/emails.rb
51
+ - lib/openemail/resources/encryption.rb
52
+ - lib/openemail/resources/exports.rb
53
+ - lib/openemail/resources/files.rb
54
+ - lib/openemail/resources/forms.rb
55
+ - lib/openemail/resources/imports.rb
56
+ - lib/openemail/resources/keys.rb
57
+ - lib/openemail/resources/labels.rb
58
+ - lib/openemail/resources/languages.rb
59
+ - lib/openemail/resources/me.rb
60
+ - lib/openemail/resources/members.rb
61
+ - lib/openemail/resources/provider_imports.rb
62
+ - lib/openemail/resources/roles.rb
63
+ - lib/openemail/resources/rules.rb
64
+ - lib/openemail/resources/security.rb
65
+ - lib/openemail/resources/senders.rb
66
+ - lib/openemail/resources/settings.rb
67
+ - lib/openemail/resources/subscriptions.rb
68
+ - lib/openemail/resources/support.rb
69
+ - lib/openemail/resources/suppressions.rb
70
+ - lib/openemail/resources/temp_mail.rb
71
+ - lib/openemail/resources/templates.rb
72
+ - lib/openemail/resources/threads.rb
73
+ - lib/openemail/resources/tools.rb
74
+ - lib/openemail/resources/tracking.rb
75
+ - lib/openemail/resources/webhooks.rb
76
+ - lib/openemail/resources/workspaces.rb
77
+ - lib/openemail/results.rb
78
+ - lib/openemail/transport.rb
79
+ - lib/openemail/update_notice.rb
80
+ - lib/openemail/version.rb
81
+ - lib/openemail/webhook.rb
82
+ - lib/openemail/wire.rb
83
+ homepage: https://openemail.uk
84
+ licenses:
85
+ - Nonstandard
86
+ metadata:
87
+ homepage_uri: https://openemail.uk
88
+ documentation_uri: https://openemail.uk/docs/ruby
89
+ changelog_uri: https://openemail.uk/docs/ruby/changelog
90
+ bug_tracker_uri: https://openemail.uk/contact
91
+ rubygems_mfa_required: 'true'
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: '3.2'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubygems_version: 3.6.3
107
+ specification_version: 4
108
+ summary: The official Ruby SDK for the OpenEmail API.
109
+ test_files: []