opensms 0.1.0
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/LICENSE +21 -0
- data/README.md +301 -0
- data/lib/opensms/client.rb +64 -0
- data/lib/opensms/error.rb +84 -0
- data/lib/opensms/models.rb +117 -0
- data/lib/opensms/pagination.rb +34 -0
- data/lib/opensms/resources/analytics.rb +46 -0
- data/lib/opensms/resources/base.rb +64 -0
- data/lib/opensms/resources/batches.rb +96 -0
- data/lib/opensms/resources/compliance.rb +26 -0
- data/lib/opensms/resources/contact_groups.rb +55 -0
- data/lib/opensms/resources/contacts.rb +44 -0
- data/lib/opensms/resources/countries.rb +31 -0
- data/lib/opensms/resources/inbound.rb +26 -0
- data/lib/opensms/resources/lookups.rb +25 -0
- data/lib/opensms/resources/messages.rb +53 -0
- data/lib/opensms/resources/numbers.rb +63 -0
- data/lib/opensms/resources/otp.rb +32 -0
- data/lib/opensms/resources/pricing.rb +16 -0
- data/lib/opensms/resources/sandbox.rb +16 -0
- data/lib/opensms/resources/sender_ids.rb +96 -0
- data/lib/opensms/resources/suppressions.rb +40 -0
- data/lib/opensms/resources/templates.rb +42 -0
- data/lib/opensms/resources/wallet.rb +44 -0
- data/lib/opensms/resources/webhooks.rb +77 -0
- data/lib/opensms/transport.rb +200 -0
- data/lib/opensms/version.rb +6 -0
- data/lib/opensms/webhook.rb +96 -0
- data/lib/opensms.rb +17 -0
- metadata +106 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
require "uri"
|
|
5
|
+
|
|
6
|
+
require_relative "../models"
|
|
7
|
+
|
|
8
|
+
module Opensms
|
|
9
|
+
# Resource groups hanging off {Opensms::Client}. Each resource is thin: build
|
|
10
|
+
# the path, query and body, call the transport, decode the model.
|
|
11
|
+
module Resources
|
|
12
|
+
# Shared plumbing for resources. Not part of the public API.
|
|
13
|
+
class Base
|
|
14
|
+
# @param transport [Opensms::Transport]
|
|
15
|
+
def initialize(transport)
|
|
16
|
+
@transport = transport
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
# Build a path from literal segments and escaped values:
|
|
22
|
+
# path("/v1/messages", id, "attempts").
|
|
23
|
+
def path(prefix, *segments)
|
|
24
|
+
([prefix] + segments.map { |s| s.is_a?(Symbol) ? s.to_s : escape(s) }).join("/")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Escape a path segment ("a/b" -> "a%2Fb", space -> "%20").
|
|
28
|
+
def escape(value)
|
|
29
|
+
URI.encode_www_form_component(value.to_s).gsub("+", "%20")
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Validate that an id argument is present.
|
|
33
|
+
def id!(value, name = "id")
|
|
34
|
+
raise ArgumentError, "Opensms: `#{name}` is required." if value.nil? || value.to_s.strip.empty?
|
|
35
|
+
|
|
36
|
+
value
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Idempotency-Key for methods marked req/opt: the caller's key or a new
|
|
40
|
+
# UUIDv4, generated once per call and reused on every retry.
|
|
41
|
+
def idem(key)
|
|
42
|
+
key.nil? ? SecureRandom.uuid : key.to_s
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Merge a positional Hash with keyword arguments.
|
|
46
|
+
def merge(params, kwargs)
|
|
47
|
+
Models.symbolize(params || {}).merge(kwargs)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def http_get(p, query = nil)
|
|
51
|
+
@transport.request(:get, p, query: query)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def http_page(p, query = nil)
|
|
55
|
+
Page.from(http_get(p, query))
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def http_delete(p, idempotency_key: nil)
|
|
59
|
+
@transport.request(:delete, p, idempotency_key: idempotency_key)
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
|
|
5
|
+
require_relative "base"
|
|
6
|
+
|
|
7
|
+
module Opensms
|
|
8
|
+
module Resources
|
|
9
|
+
# The +batches+ resource: bulk sends that are validated first and started
|
|
10
|
+
# explicitly. Accessed as +client.batches+.
|
|
11
|
+
class Batches < Base
|
|
12
|
+
ITEM_FIELDS = %i[to text sender_id traffic_type callback_url metadata].freeze
|
|
13
|
+
ITEMS_QUERY = %i[status limit cursor].freeze
|
|
14
|
+
|
|
15
|
+
# Create a batch from items. POST /v1/messages/batch -> 202 Batch
|
|
16
|
+
# (status "ready"; nothing is sent until {#start}).
|
|
17
|
+
#
|
|
18
|
+
# @param params [Hash] :items (Array of {to:, text:, sender_id:, ...}), :dedupe
|
|
19
|
+
# @param idempotency_key [String, nil]
|
|
20
|
+
# @return [Hash] Batch
|
|
21
|
+
def create(params = nil, idempotency_key: nil, **kwargs)
|
|
22
|
+
body = Models.build(merge(params, kwargs), %i[items dedupe], required: %i[items])
|
|
23
|
+
body[:items] = Array(body[:items]).map { |item| Models.build(item, ITEM_FIELDS) }
|
|
24
|
+
@transport.request(:post, "/v1/messages/batch", body: body, idempotency_key: idem(idempotency_key))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Create a batch from CSV text (header row "to,text[,sender_id,...]").
|
|
28
|
+
# Sent as text/csv. With +dedupe: false+ the CSV is sent as a
|
|
29
|
+
# multipart/form-data upload instead, because that is the only form in
|
|
30
|
+
# which the API accepts a dedupe flag next to CSV.
|
|
31
|
+
#
|
|
32
|
+
# @param csv [String]
|
|
33
|
+
# @param dedupe [Boolean, nil] default true on the server
|
|
34
|
+
# @param idempotency_key [String, nil]
|
|
35
|
+
# @return [Hash] Batch
|
|
36
|
+
def create_from_csv(csv, dedupe: nil, idempotency_key: nil)
|
|
37
|
+
raise ArgumentError, "Opensms: `csv` is required." if csv.nil? || csv.to_s.empty?
|
|
38
|
+
|
|
39
|
+
key = idem(idempotency_key)
|
|
40
|
+
if dedupe == false
|
|
41
|
+
boundary = "OpensmsBoundary#{SecureRandom.hex(12)}"
|
|
42
|
+
return @transport.request(:post, "/v1/messages/batch",
|
|
43
|
+
raw_body: multipart(boundary, csv.to_s),
|
|
44
|
+
content_type: "multipart/form-data; boundary=#{boundary}",
|
|
45
|
+
idempotency_key: key)
|
|
46
|
+
end
|
|
47
|
+
@transport.request(:post, "/v1/messages/batch", raw_body: csv.to_s, content_type: "text/csv",
|
|
48
|
+
idempotency_key: key)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# GET /v1/batches/{id} -> Batch.
|
|
52
|
+
def get(id)
|
|
53
|
+
http_get(path("/v1/batches", id!(id)))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Per-row validation report. GET /v1/batches/{id}/validation.
|
|
57
|
+
# @return [Hash] { rows:, total:, valid:, invalid:, duplicates:, suppressed: }
|
|
58
|
+
def validation(id)
|
|
59
|
+
http_get(path("/v1/batches", id!(id), :validation))
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Start a ready batch. POST /v1/batches/{id}/start -> Batch.
|
|
63
|
+
def start(id, idempotency_key: nil)
|
|
64
|
+
@transport.request(:post, path("/v1/batches", id!(id), :start), idempotency_key: idem(idempotency_key))
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Stop a batch. POST /v1/batches/{id}/stop -> { id:, status: "stopped", cancelled: }.
|
|
68
|
+
def stop(id, idempotency_key: nil)
|
|
69
|
+
@transport.request(:post, path("/v1/batches", id!(id), :stop), idempotency_key: idem(idempotency_key))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Messages created by a batch. GET /v1/batches/{id}/items -> Page<BatchItem>.
|
|
73
|
+
#
|
|
74
|
+
# @param params [Hash] :status, :limit, :cursor
|
|
75
|
+
# @return [Opensms::Page]
|
|
76
|
+
def list_items(id, params = nil, **kwargs)
|
|
77
|
+
http_page(path("/v1/batches", id!(id), :items), Models.build(merge(params, kwargs), ITEMS_QUERY))
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def multipart(boundary, csv)
|
|
83
|
+
[
|
|
84
|
+
"--#{boundary}\r\n",
|
|
85
|
+
%(Content-Disposition: form-data; name="dedupe"\r\n\r\n),
|
|
86
|
+
"false\r\n",
|
|
87
|
+
"--#{boundary}\r\n",
|
|
88
|
+
%(Content-Disposition: form-data; name="file"; filename="batch.csv"\r\n),
|
|
89
|
+
"Content-Type: text/csv\r\n\r\n",
|
|
90
|
+
csv.b,
|
|
91
|
+
"\r\n--#{boundary}--\r\n"
|
|
92
|
+
].map(&:b).join
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module Opensms
|
|
6
|
+
module Resources
|
|
7
|
+
# The +compliance+ resource: per-country rules and content rules.
|
|
8
|
+
# Accessed as +client.compliance+.
|
|
9
|
+
class Compliance < Base
|
|
10
|
+
# GET /v1/compliance/countries -> Array<CountryRules>.
|
|
11
|
+
def list_countries
|
|
12
|
+
http_get("/v1/compliance/countries")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# GET /v1/compliance/countries/{iso2} -> CountryRules.
|
|
16
|
+
def get_country(iso2)
|
|
17
|
+
http_get(path("/v1/compliance/countries", id!(iso2, "iso2")))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# GET /v1/content-rules -> Array<ContentRule>.
|
|
21
|
+
def list_content_rules
|
|
22
|
+
http_get("/v1/content-rules")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module Opensms
|
|
6
|
+
module Resources
|
|
7
|
+
# The +contact_groups+ resource. Accessed as +client.contact_groups+.
|
|
8
|
+
class ContactGroups < Base
|
|
9
|
+
FIELDS = %i[name contact_ids].freeze
|
|
10
|
+
SEND_FIELDS = %i[text template_id variables sender_id traffic_type callback_url].freeze
|
|
11
|
+
|
|
12
|
+
# GET /v1/contact-groups -> Page<Group>.
|
|
13
|
+
# @param params [Hash] :limit, :cursor
|
|
14
|
+
def list(params = nil, **kwargs)
|
|
15
|
+
http_page("/v1/contact-groups", Models.build(merge(params, kwargs), %i[limit cursor]))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# POST /v1/contact-groups -> 201 Group.
|
|
19
|
+
# @param params [Hash] :name (required), :contact_ids
|
|
20
|
+
def create(params = nil, idempotency_key: nil, **kwargs)
|
|
21
|
+
body = Models.build(merge(params, kwargs), FIELDS, required: %i[name])
|
|
22
|
+
@transport.request(:post, "/v1/contact-groups", body: body, idempotency_key: idem(idempotency_key))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# GET /v1/contact-groups/{id} -> Group.
|
|
26
|
+
def get(id)
|
|
27
|
+
http_get(path("/v1/contact-groups", id!(id)))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# PATCH /v1/contact-groups/{id} -> Group.
|
|
31
|
+
# @param params [Hash] :name, :contact_ids
|
|
32
|
+
def update(id, params = nil, **kwargs)
|
|
33
|
+
body = Models.build(merge(params, kwargs), FIELDS)
|
|
34
|
+
@transport.request(:patch, path("/v1/contact-groups", id!(id)), body: body)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# DELETE /v1/contact-groups/{id} -> 204.
|
|
38
|
+
# @return [nil]
|
|
39
|
+
def delete(id)
|
|
40
|
+
http_delete(path("/v1/contact-groups", id!(id)))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Send to every contact in the group. POST /v1/contact-groups/{id}/send
|
|
44
|
+
# -> Batch (already "running").
|
|
45
|
+
#
|
|
46
|
+
# @param params [Hash] :text or :template_id, :variables, :sender_id,
|
|
47
|
+
# :traffic_type, :callback_url
|
|
48
|
+
def send(id, params = nil, idempotency_key: nil, **kwargs)
|
|
49
|
+
body = Models.build(merge(params, kwargs), SEND_FIELDS)
|
|
50
|
+
@transport.request(:post, path("/v1/contact-groups", id!(id), :send), body: body,
|
|
51
|
+
idempotency_key: idem(idempotency_key))
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module Opensms
|
|
6
|
+
module Resources
|
|
7
|
+
# The +contacts+ resource. Accessed as +client.contacts+.
|
|
8
|
+
class Contacts < Base
|
|
9
|
+
FIELDS = %i[e164 name attributes].freeze
|
|
10
|
+
|
|
11
|
+
# GET /v1/contacts -> Page<Contact>.
|
|
12
|
+
# @param params [Hash] :limit, :cursor
|
|
13
|
+
# @return [Opensms::Page]
|
|
14
|
+
def list(params = nil, **kwargs)
|
|
15
|
+
http_page("/v1/contacts", Models.build(merge(params, kwargs), %i[limit cursor]))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# POST /v1/contacts -> 201 Contact.
|
|
19
|
+
# @param params [Hash] :e164 (required), :name, :attributes
|
|
20
|
+
def create(params = nil, idempotency_key: nil, **kwargs)
|
|
21
|
+
body = Models.build(merge(params, kwargs), FIELDS, required: %i[e164])
|
|
22
|
+
@transport.request(:post, "/v1/contacts", body: body, idempotency_key: idem(idempotency_key))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# GET /v1/contacts/{id} -> Contact.
|
|
26
|
+
def get(id)
|
|
27
|
+
http_get(path("/v1/contacts", id!(id)))
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Partial update. PATCH /v1/contacts/{id} -> Contact.
|
|
31
|
+
# @param params [Hash] :e164, :name, :attributes
|
|
32
|
+
def update(id, params = nil, **kwargs)
|
|
33
|
+
body = Models.build(merge(params, kwargs), FIELDS)
|
|
34
|
+
@transport.request(:patch, path("/v1/contacts", id!(id)), body: body)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# DELETE /v1/contacts/{id} -> 204.
|
|
38
|
+
# @return [nil]
|
|
39
|
+
def delete(id)
|
|
40
|
+
http_delete(path("/v1/contacts", id!(id)))
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module Opensms
|
|
6
|
+
module Resources
|
|
7
|
+
# The +countries+ resource: the public country catalog. Accessed as
|
|
8
|
+
# +client.countries+.
|
|
9
|
+
class Countries < Base
|
|
10
|
+
# GET /v1/countries -> Array<Country>.
|
|
11
|
+
def list
|
|
12
|
+
http_get("/v1/countries")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# GET /v1/countries/{iso2}/carriers -> Array<{ id:, name:, mcc_mnc:, prefixes: }>.
|
|
16
|
+
def carriers(iso2)
|
|
17
|
+
http_get(path("/v1/countries", id!(iso2, "iso2"), :carriers))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# GET /v1/countries/{iso2}/routes -> Array<Route>.
|
|
21
|
+
def routes(iso2)
|
|
22
|
+
http_get(path("/v1/countries", id!(iso2, "iso2"), :routes))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# GET /v1/countries/{iso2}/compliance -> CountryRules.
|
|
26
|
+
def compliance(iso2)
|
|
27
|
+
http_get(path("/v1/countries", id!(iso2, "iso2"), :compliance))
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module Opensms
|
|
6
|
+
module Resources
|
|
7
|
+
# The +inbound+ resource: messages received on your numbers. Accessed as
|
|
8
|
+
# +client.inbound+.
|
|
9
|
+
class Inbound < Base
|
|
10
|
+
# GET /v1/inbound -> Page<InboundMessage>.
|
|
11
|
+
def list(params = nil, **kwargs)
|
|
12
|
+
http_page("/v1/inbound", Models.build(merge(params, kwargs), %i[limit cursor]))
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Reply to an inbound message (live keys only).
|
|
16
|
+
# POST /v1/inbound/{id}/reply -> 201 Message.
|
|
17
|
+
#
|
|
18
|
+
# @param params [Hash] :text (required)
|
|
19
|
+
def reply(id, params = nil, idempotency_key: nil, **kwargs)
|
|
20
|
+
body = Models.build(merge(params, kwargs), %i[text], required: %i[text])
|
|
21
|
+
@transport.request(:post, path("/v1/inbound", id!(id), :reply), body: body,
|
|
22
|
+
idempotency_key: idem(idempotency_key))
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module Opensms
|
|
6
|
+
module Resources
|
|
7
|
+
# The +lookups+ resource: number lookups (country, carrier, porting).
|
|
8
|
+
# Accessed as +client.lookups+.
|
|
9
|
+
class Lookups < Base
|
|
10
|
+
# POST /v1/lookup -> 200 (completed) or 202 (pending) Lookup.
|
|
11
|
+
#
|
|
12
|
+
# @param params [Hash] :to (E.164, required)
|
|
13
|
+
# @return [Hash] Lookup
|
|
14
|
+
def create(params = nil, idempotency_key: nil, **kwargs)
|
|
15
|
+
body = Models.build(merge(params, kwargs), %i[to], required: %i[to])
|
|
16
|
+
@transport.request(:post, "/v1/lookup", body: body, idempotency_key: idem(idempotency_key))
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# GET /v1/lookup/{id} -> Lookup.
|
|
20
|
+
def get(id)
|
|
21
|
+
http_get(path("/v1/lookup", id!(id)))
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module Opensms
|
|
6
|
+
module Resources
|
|
7
|
+
# The +messages+ resource: send, list, inspect and cancel SMS. Accessed as
|
|
8
|
+
# +client.messages+.
|
|
9
|
+
class Messages < Base
|
|
10
|
+
SEND_FIELDS = %i[to text sender_id traffic_type scheduled_at callback_url metadata].freeze
|
|
11
|
+
LIST_FIELDS = %i[limit cursor status to country date_from date_to].freeze
|
|
12
|
+
|
|
13
|
+
# Send one SMS. POST /v1/messages -> 201 Message.
|
|
14
|
+
#
|
|
15
|
+
# @param params [Hash] :to (E.164, required), :text (required), :sender_id,
|
|
16
|
+
# :traffic_type, :scheduled_at (Time or RFC 3339), :callback_url, :metadata
|
|
17
|
+
# @param idempotency_key [String, nil] generated when omitted
|
|
18
|
+
# @return [Hash] Message
|
|
19
|
+
def send(params = nil, idempotency_key: nil, **kwargs)
|
|
20
|
+
body = Models.build(merge(params, kwargs), SEND_FIELDS, required: %i[to text], times: %i[scheduled_at])
|
|
21
|
+
@transport.request(:post, "/v1/messages", body: body, idempotency_key: idem(idempotency_key))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# List messages, newest first. GET /v1/messages -> Page<Message>.
|
|
25
|
+
#
|
|
26
|
+
# @param params [Hash] :limit (1..100), :cursor, :status, :to, :country,
|
|
27
|
+
# :date_from, :date_to
|
|
28
|
+
# @return [Opensms::Page]
|
|
29
|
+
def list(params = nil, **kwargs)
|
|
30
|
+
http_page("/v1/messages", Models.build(merge(params, kwargs), LIST_FIELDS, times: %i[date_from date_to]))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# GET /v1/messages/{id} -> Message.
|
|
34
|
+
# @return [Hash]
|
|
35
|
+
def get(id)
|
|
36
|
+
http_get(path("/v1/messages", id!(id)))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Delivery attempts for a message. GET /v1/messages/{id}/attempts.
|
|
40
|
+
# @return [Array<Hash>]
|
|
41
|
+
def attempts(id)
|
|
42
|
+
http_get(path("/v1/messages", id!(id), :attempts))
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Cancel a queued or scheduled message. Never auto-retried.
|
|
46
|
+
# POST /v1/messages/{id}/cancel -> Message.
|
|
47
|
+
# @return [Hash]
|
|
48
|
+
def cancel(id)
|
|
49
|
+
@transport.request(:post, path("/v1/messages", id!(id), :cancel))
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module Opensms
|
|
6
|
+
module Resources
|
|
7
|
+
# The +numbers+ resource: virtual numbers and their inbound rules.
|
|
8
|
+
# Everything except +list+ and +available+ needs a live key. Accessed as
|
|
9
|
+
# +client.numbers+.
|
|
10
|
+
class Numbers < Base
|
|
11
|
+
RULE_FIELDS = %i[match pattern action target position].freeze
|
|
12
|
+
|
|
13
|
+
# GET /v1/numbers -> Page<Number>.
|
|
14
|
+
def list(params = nil, **kwargs)
|
|
15
|
+
http_page("/v1/numbers", Models.build(merge(params, kwargs), %i[limit cursor]))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# GET /v1/numbers/available -> Array<Number>.
|
|
19
|
+
# @param params [Hash] :country, :kind
|
|
20
|
+
def available(params = nil, **kwargs)
|
|
21
|
+
http_get("/v1/numbers/available", Models.build(merge(params, kwargs), %i[country kind]))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Assign (buy) a number; charges the wallet. POST /v1/numbers -> 201 Number.
|
|
25
|
+
# @param params [Hash] :country (required), :kind (required)
|
|
26
|
+
def assign(params = nil, idempotency_key: nil, **kwargs)
|
|
27
|
+
body = Models.build(merge(params, kwargs), %i[country kind], required: %i[country kind])
|
|
28
|
+
@transport.request(:post, "/v1/numbers", body: body, idempotency_key: idem(idempotency_key))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# DELETE /v1/numbers/{id} -> 204.
|
|
32
|
+
# @return [nil]
|
|
33
|
+
def release(id)
|
|
34
|
+
http_delete(path("/v1/numbers", id!(id)))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# GET /v1/numbers/{id}/rules -> Page<Rule>.
|
|
38
|
+
def list_rules(id, params = nil, **kwargs)
|
|
39
|
+
http_page(path("/v1/numbers", id!(id), :rules), Models.build(merge(params, kwargs), %i[limit cursor]))
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# POST /v1/numbers/{id}/rules -> 201 Rule.
|
|
43
|
+
# @param rule [Hash] :match, :pattern, :action, :target, :position
|
|
44
|
+
def create_rule(id, rule = nil, idempotency_key: nil, **kwargs)
|
|
45
|
+
body = Models.build(merge(rule, kwargs), RULE_FIELDS, required: %i[match action target])
|
|
46
|
+
@transport.request(:post, path("/v1/numbers", id!(id), :rules), body: body,
|
|
47
|
+
idempotency_key: idem(idempotency_key))
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# PUT /v1/numbers/{id}/rules/{rule_id} -> Rule.
|
|
51
|
+
def update_rule(id, rule_id, rule = nil, **kwargs)
|
|
52
|
+
body = Models.build(merge(rule, kwargs), RULE_FIELDS, required: %i[match action target])
|
|
53
|
+
@transport.request(:put, path("/v1/numbers", id!(id), :rules, id!(rule_id, "rule_id")), body: body)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# DELETE /v1/numbers/{id}/rules/{rule_id} -> 204.
|
|
57
|
+
# @return [nil]
|
|
58
|
+
def delete_rule(id, rule_id)
|
|
59
|
+
http_delete(path("/v1/numbers", id!(id), :rules, id!(rule_id, "rule_id")))
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module Opensms
|
|
6
|
+
module Resources
|
|
7
|
+
# The +otp+ resource: send and verify one-time passcodes. Accessed as
|
|
8
|
+
# +client.otp+.
|
|
9
|
+
class Otp < Base
|
|
10
|
+
# Send a code. POST /v1/otp/send -> 201 { otp_id: }.
|
|
11
|
+
#
|
|
12
|
+
# @param params [Hash] :to (required), :sender_id, :template (must contain
|
|
13
|
+
# "{{code}}"), :length (4..10), :ttl_seconds (30..86400)
|
|
14
|
+
# @return [Hash] { otp_id: }
|
|
15
|
+
def send(params = nil, idempotency_key: nil, **kwargs)
|
|
16
|
+
body = Models.build(merge(params, kwargs), %i[to sender_id template length ttl_seconds], required: %i[to])
|
|
17
|
+
@transport.request(:post, "/v1/otp/send", body: body, idempotency_key: idem(idempotency_key))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Check a code. A wrong code returns { valid: false } and burns an
|
|
21
|
+
# attempt, so this call is never auto-retried.
|
|
22
|
+
# POST /v1/otp/verify -> { valid:, attempts_left: }.
|
|
23
|
+
#
|
|
24
|
+
# @param params [Hash] :otp_id, :code
|
|
25
|
+
# @return [Hash]
|
|
26
|
+
def verify(params = nil, **kwargs)
|
|
27
|
+
body = Models.build(merge(params, kwargs), %i[otp_id code], required: %i[otp_id code])
|
|
28
|
+
@transport.request(:post, "/v1/otp/verify", body: body)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module Opensms
|
|
6
|
+
module Resources
|
|
7
|
+
# The +pricing+ resource. Accessed as +client.pricing+.
|
|
8
|
+
class Pricing < Base
|
|
9
|
+
# GET /v1/pricing -> PriceList.
|
|
10
|
+
# @param params [Hash] :product (sms|lookup|number_monthly), :country (ISO2)
|
|
11
|
+
def get(params = nil, **kwargs)
|
|
12
|
+
http_get("/v1/pricing", Models.build(merge(params, kwargs), %i[product country]))
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module Opensms
|
|
6
|
+
module Resources
|
|
7
|
+
# The +sandbox+ resource: the rendered text of sandbox sends, including
|
|
8
|
+
# OTP codes. Accessed as +client.sandbox+.
|
|
9
|
+
class Sandbox < Base
|
|
10
|
+
# GET /v1/sandbox/messages -> Page<SandboxMessage>.
|
|
11
|
+
def list_messages(params = nil, **kwargs)
|
|
12
|
+
http_page("/v1/sandbox/messages", Models.build(merge(params, kwargs), %i[limit cursor]))
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "base"
|
|
4
|
+
|
|
5
|
+
module Opensms
|
|
6
|
+
module Resources
|
|
7
|
+
# The +sender_ids+ resource: registered sender IDs, availability checks,
|
|
8
|
+
# fee quotes, documents and drafts. Accessed as +client.sender_ids+.
|
|
9
|
+
class SenderIds < Base
|
|
10
|
+
CREATE_FIELDS = %i[value kind countries use_case sample_message documents draft_id draft_version quote_id].freeze
|
|
11
|
+
UPDATE_FIELDS = %i[use_case countries documents sample_message].freeze
|
|
12
|
+
DRAFT_FIELDS = %i[source value kind countries use_case sample_message documents].freeze
|
|
13
|
+
DRAFT_UPDATE_FIELDS = %i[version value kind countries use_case sample_message documents].freeze
|
|
14
|
+
|
|
15
|
+
# GET /v1/sender-ids -> Page<SenderId>.
|
|
16
|
+
def list(params = nil, **kwargs)
|
|
17
|
+
http_page("/v1/sender-ids", Models.build(merge(params, kwargs), %i[limit cursor]))
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# GET /v1/sender-ids/{id} -> SenderId.
|
|
21
|
+
def get(id)
|
|
22
|
+
http_get(path("/v1/sender-ids", id!(id)))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Register a sender ID. May charge fees, so it is never auto-retried.
|
|
26
|
+
# POST /v1/sender-ids -> 201 SenderId.
|
|
27
|
+
#
|
|
28
|
+
# @param params [Hash] :value, :kind, :countries, :documents (required),
|
|
29
|
+
# :use_case, :sample_message, :draft_id, :draft_version, :quote_id
|
|
30
|
+
def create(params = nil, **kwargs)
|
|
31
|
+
body = Models.build(merge(params, kwargs), CREATE_FIELDS, required: %i[value kind countries documents])
|
|
32
|
+
@transport.request(:post, "/v1/sender-ids", body: body)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Amend a sender ID. PATCH /v1/sender-ids/{id} -> SenderId.
|
|
36
|
+
# @param params [Hash] :use_case, :countries, :documents (required), :sample_message
|
|
37
|
+
def update(id, params = nil, **kwargs)
|
|
38
|
+
body = Models.build(merge(params, kwargs), UPDATE_FIELDS, required: %i[use_case countries documents])
|
|
39
|
+
@transport.request(:patch, path("/v1/sender-ids", id!(id)), body: body)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# DELETE /v1/sender-ids/{id} -> 204.
|
|
43
|
+
# @return [nil]
|
|
44
|
+
def delete(id)
|
|
45
|
+
http_delete(path("/v1/sender-ids", id!(id)))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# GET /v1/sender-ids/check -> { valid:, available:, reserved:, reason: }.
|
|
49
|
+
# @param params [Hash] :value (required), :country
|
|
50
|
+
def check(params = nil, **kwargs)
|
|
51
|
+
http_get("/v1/sender-ids/check", Models.build(merge(params, kwargs), %i[value country], required: %i[value]))
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Registration fee quote. GET /v1/sender-ids/quote?countries=KE,NG.
|
|
55
|
+
# @param params [Hash] :countries (Array or comma-separated String)
|
|
56
|
+
def quote(params = nil, **kwargs)
|
|
57
|
+
http_get("/v1/sender-ids/quote", Models.build(merge(params, kwargs), %i[countries], required: %i[countries]))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Uploaded registration documents. GET /v1/sender-documents.
|
|
61
|
+
# @return [Array<Hash>] the +items+ of the response (no cursor)
|
|
62
|
+
def list_documents
|
|
63
|
+
body = http_get("/v1/sender-documents")
|
|
64
|
+
body.is_a?(Hash) ? (body[:items] || []) : body
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# GET /v1/sender-id-drafts -> Page<Draft>.
|
|
68
|
+
def list_drafts(params = nil, **kwargs)
|
|
69
|
+
http_page("/v1/sender-id-drafts", Models.build(merge(params, kwargs), %i[limit cursor]))
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# POST /v1/sender-id-drafts -> 201 Draft. Not auto-retried.
|
|
73
|
+
def create_draft(params = nil, **kwargs)
|
|
74
|
+
@transport.request(:post, "/v1/sender-id-drafts", body: Models.build(merge(params, kwargs), DRAFT_FIELDS))
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# GET /v1/sender-id-drafts/{id} -> Draft.
|
|
78
|
+
def get_draft(id)
|
|
79
|
+
http_get(path("/v1/sender-id-drafts", id!(id)))
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# PATCH /v1/sender-id-drafts/{id} -> Draft. Requires the current
|
|
83
|
+
# +version+ (optimistic lock; 409 on mismatch).
|
|
84
|
+
def update_draft(id, params = nil, **kwargs)
|
|
85
|
+
body = Models.build(merge(params, kwargs), DRAFT_UPDATE_FIELDS, required: %i[version])
|
|
86
|
+
@transport.request(:patch, path("/v1/sender-id-drafts", id!(id)), body: body)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# DELETE /v1/sender-id-drafts/{id} -> 204.
|
|
90
|
+
# @return [nil]
|
|
91
|
+
def delete_draft(id)
|
|
92
|
+
http_delete(path("/v1/sender-id-drafts", id!(id)))
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|