sms_traffic_sdk 0.1.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +23 -0
- data/README.md +41 -2
- data/lib/sms_traffic/client/account_reply.rb +14 -0
- data/lib/sms_traffic/client/deliver_reply.rb +0 -2
- data/lib/sms_traffic/client/reply.rb +3 -1
- data/lib/sms_traffic/client/status_reply.rb +21 -2
- data/lib/sms_traffic/client.rb +55 -12
- data/lib/sms_traffic/configuration.rb +6 -11
- data/lib/sms_traffic/errors.rb +18 -1
- data/lib/sms_traffic/sms.rb +13 -0
- data/lib/sms_traffic/status.rb +26 -0
- data/lib/sms_traffic/version.rb +1 -1
- data/lib/sms_traffic.rb +1 -0
- metadata +17 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7073f3fdf9537cf98008d5c6fac2bbc28457fbe4ef05852ccfd4e3b3a4606fbc
|
|
4
|
+
data.tar.gz: 7ad131b8ca90beebf7d4e227fa4031655bdc49d7e5db4f12c7ee11763cde8e0a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f22009b2e84816ea0ad7294fd80ec8810dfc99c4ad46a7c66e9f9604f837c31fe5d6ae996639e776cb79501523b50257d20390069412993c704b2456ed3b5fb7
|
|
7
|
+
data.tar.gz: 4dc45cb30d25dd521d716f16bcb7a4ae530ee9fa3b8d14f0b8ec0cfde6a479c81d41268b1015a7518bdd16d79a11fc4f8c5663d2e8266dc7f34c1d0415202843
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 - 2026-11-03
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `Configuration#fallback_host` (default `nil`). When set, the client repeats a request once on that host after a
|
|
8
|
+
connection failure that proves the server was not reached (`SocketError`, `Errno::ECONNREFUSED`,
|
|
9
|
+
`Errno::EHOSTUNREACH`, `Errno::ENETUNREACH`, `Net::OpenTimeout`) or after API error 1000. `Client.status` and
|
|
10
|
+
`Client.balance` also retry after any `ConnectionError` and HTTP 5xx; `Client.deliver` never does, because the
|
|
11
|
+
provider may already have queued the SMS. With `debug`, the retry is logged in one line without credentials.
|
|
12
|
+
- `Client.balance` returns an `AccountReply`; `AccountReply#balance` is a `BigDecimal` (units as sent by the
|
|
13
|
+
provider). `bigdecimal` is a runtime dependency.
|
|
14
|
+
- `SmsTraffic::Status` (`FINAL`, `INTERMEDIATE`), `StatusReply::Entry#final?` / `#delivered?`, the same methods on
|
|
15
|
+
`StatusReply` (optional `sms_id`) and `Sms#final?` / `#delivered?`.
|
|
16
|
+
- `ApiError#retryable?`, `#access_denied?`, `#insufficient_funds?` and `#blocked_phone?`.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- A provider reply with `<result>ERRO</result>` (the spelling in the provider's own example) is now an
|
|
21
|
+
`ApiError`. `Reply#api_error?` is true for any present `result` other than `OK`.
|
|
22
|
+
- `Sms#update_status` no longer records "No status returned" for an empty `<status>` element (an intermediate
|
|
23
|
+
provider status); the status stays `sent`. `StatusReply::Entry#status_reported?` tells an empty status from an
|
|
24
|
+
absent one.
|
|
25
|
+
|
|
3
26
|
## 0.1.0 - 2026-11-03
|
|
4
27
|
|
|
5
28
|
### Breaking changes
|
data/README.md
CHANGED
|
@@ -13,6 +13,8 @@ sending of SMS messages and checking delivery statuses.
|
|
|
13
13
|
|
|
14
14
|
- Send SMS messages to single or multiple recipients.
|
|
15
15
|
- Check the delivery status of sent messages.
|
|
16
|
+
- Read the account balance.
|
|
17
|
+
- Fail over to the provider's backup host.
|
|
16
18
|
- **TODO:** Support for various message formats and encodings.
|
|
17
19
|
|
|
18
20
|
|
|
@@ -44,6 +46,7 @@ SmsTraffic.configure do |config|
|
|
|
44
46
|
config.login = 'login'
|
|
45
47
|
config.password = 'password'
|
|
46
48
|
config.host = 'https://api.smstraffic.ru'
|
|
49
|
+
config.fallback_host = 'https://api2.smstraffic.ru' # optional backup host, default nil
|
|
47
50
|
config.originator = 'default_originator' # optional, can be given per SMS
|
|
48
51
|
config.open_timeout = 10 # seconds, default 10
|
|
49
52
|
config.read_timeout = 30 # seconds, default 30
|
|
@@ -96,7 +99,42 @@ reply.status('8287713301') # => 'Delivered'; the id may be omitted whe
|
|
|
96
99
|
```
|
|
97
100
|
|
|
98
101
|
Each entry has `sms_id`, `status`, `error`, `submission_date`, `send_date`, `last_status_change_date`
|
|
99
|
-
(strings as sent by the provider), `error?` and `
|
|
102
|
+
(strings as sent by the provider), `error?`, `not_found?`, `final?`, `delivered?` and `status_reported?`
|
|
103
|
+
(true when the provider sent a status element, even an empty one).
|
|
104
|
+
|
|
105
|
+
### Final and delivered statuses
|
|
106
|
+
|
|
107
|
+
Provider statuses `Delivered`, `Non Delivered`, `Rejected`, `Expired`, `Deleted` and `Unknown status` are final;
|
|
108
|
+
an empty status and `Buffered SMSC` are intermediate (`SmsTraffic::Status::FINAL` / `INTERMEDIATE`).
|
|
109
|
+
Comparison is case-insensitive.
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
reply = SmsTraffic::Client.status('8287713301')
|
|
113
|
+
reply.final? # => true
|
|
114
|
+
reply.delivered? # => true; pass an sms id when the reply has several entries
|
|
115
|
+
reply.entry.final?
|
|
116
|
+
|
|
117
|
+
sms.update_status
|
|
118
|
+
sms.final? # => true for a final stored status; false for 'not-sent' and 'sent'
|
|
119
|
+
sms.delivered? # => true only for 'delivered'
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Balance
|
|
123
|
+
|
|
124
|
+
```ruby
|
|
125
|
+
SmsTraffic::Client.balance.balance # => 0.10025e5 (BigDecimal), nil when absent or not a number
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The value is returned as the provider sends it; its units are not documented and are not converted.
|
|
129
|
+
|
|
130
|
+
### Fallback host
|
|
131
|
+
|
|
132
|
+
Set `config.fallback_host` (the provider documents `https://api2.smstraffic.ru`) to repeat a failed request once
|
|
133
|
+
on the backup host. The request is repeated after `SocketError`, `Errno::ECONNREFUSED`, `Errno::EHOSTUNREACH`,
|
|
134
|
+
`Errno::ENETUNREACH`, `Net::OpenTimeout`, or API error 1000 (temporary server problem). `Client.status` and
|
|
135
|
+
`Client.balance` also retry after any `ConnectionError` (read timeout, reset connection, TLS failure) and HTTP 5xx.
|
|
136
|
+
`Client.deliver` does not, since the provider may already have queued the SMS. The error of the last attempt is raised. With `debug`, the retry is
|
|
137
|
+
logged in one line without credentials.
|
|
100
138
|
|
|
101
139
|
### Errors
|
|
102
140
|
|
|
@@ -104,7 +142,8 @@ All errors inherit from `SmsTraffic::Error`:
|
|
|
104
142
|
|
|
105
143
|
- `SmsTraffic::ConnectionError` - network, timeout or TLS failure (the original exception is `cause`).
|
|
106
144
|
- `SmsTraffic::HttpError` - non-2xx response; has `status` and `body`.
|
|
107
|
-
- `SmsTraffic::ApiError` - the provider replied `ERROR
|
|
145
|
+
- `SmsTraffic::ApiError` - the provider replied `ERROR` (or `ERRO`); has `code` and `description`, and the
|
|
146
|
+
predicates `retryable?` (1000), `access_denied?` (411, 412), `insufficient_funds?` (415) and `blocked_phone?` (432).
|
|
108
147
|
|
|
109
148
|
```ruby
|
|
110
149
|
begin
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'bigdecimal'
|
|
2
|
+
|
|
3
|
+
module SmsTraffic
|
|
4
|
+
class Client
|
|
5
|
+
class AccountReply < Reply
|
|
6
|
+
# @return [BigDecimal, nil] account balance as sent by the provider (units are not converted); nil when absent
|
|
7
|
+
# or not a number
|
|
8
|
+
def balance
|
|
9
|
+
value = fetch_value(to_h, :account).to_s.strip
|
|
10
|
+
BigDecimal(value, exception: false) unless value.empty?
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module SmsTraffic
|
|
2
2
|
class Client
|
|
3
3
|
class Reply
|
|
4
|
+
OK_RESULT = 'OK'.freeze
|
|
4
5
|
ERROR_RESULT = 'ERROR'.freeze
|
|
5
6
|
|
|
6
7
|
attr_reader :xml, :to_h
|
|
@@ -23,8 +24,9 @@ module SmsTraffic
|
|
|
23
24
|
fetch_value(to_h, :description)
|
|
24
25
|
end
|
|
25
26
|
|
|
27
|
+
# The provider documents `ERROR` but its example replies `ERRO`, so any result other than OK is an error.
|
|
26
28
|
def api_error?
|
|
27
|
-
result
|
|
29
|
+
!result.nil? && result != OK_RESULT
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
private
|
|
@@ -4,8 +4,14 @@ module SmsTraffic
|
|
|
4
4
|
NOT_FOUND = /no such message/i
|
|
5
5
|
|
|
6
6
|
Entry = Struct.new(
|
|
7
|
-
:sms_id, :status, :error, :submission_date, :send_date, :last_status_change_date,
|
|
7
|
+
:sms_id, :status, :status_reported, :error, :submission_date, :send_date, :last_status_change_date,
|
|
8
|
+
keyword_init: true
|
|
8
9
|
) do
|
|
10
|
+
# @return [Boolean] the provider sent a status element, even an empty one (an intermediate status)
|
|
11
|
+
def status_reported?
|
|
12
|
+
status_reported == true
|
|
13
|
+
end
|
|
14
|
+
|
|
9
15
|
def error?
|
|
10
16
|
!error.nil?
|
|
11
17
|
end
|
|
@@ -13,6 +19,14 @@ module SmsTraffic
|
|
|
13
19
|
def not_found?
|
|
14
20
|
error? && error.match?(NOT_FOUND)
|
|
15
21
|
end
|
|
22
|
+
|
|
23
|
+
def final?
|
|
24
|
+
Status.final?(status)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def delivered?
|
|
28
|
+
Status.delivered?(status)
|
|
29
|
+
end
|
|
16
30
|
end
|
|
17
31
|
|
|
18
32
|
# @return [Array<Entry>]
|
|
@@ -33,7 +47,7 @@ module SmsTraffic
|
|
|
33
47
|
define_method(name) { |sms_id = nil| entry(sms_id)&.public_send(name) }
|
|
34
48
|
end
|
|
35
49
|
|
|
36
|
-
%i[error? not_found?].each do |name|
|
|
50
|
+
%i[error? not_found? final? delivered?].each do |name|
|
|
37
51
|
define_method(name) { |sms_id = nil| entry(sms_id)&.public_send(name) || false }
|
|
38
52
|
end
|
|
39
53
|
|
|
@@ -57,6 +71,10 @@ module SmsTraffic
|
|
|
57
71
|
to_h.empty? ? [] : [to_h]
|
|
58
72
|
end
|
|
59
73
|
|
|
74
|
+
def key?(source, key)
|
|
75
|
+
source.is_a?(Hash) && (source.key?(key.to_s) || source.key?(key.to_sym))
|
|
76
|
+
end
|
|
77
|
+
|
|
60
78
|
def blank_to_nil(value)
|
|
61
79
|
stripped = value&.strip
|
|
62
80
|
stripped unless stripped.nil? || stripped.empty?
|
|
@@ -66,6 +84,7 @@ module SmsTraffic
|
|
|
66
84
|
Entry.new(
|
|
67
85
|
sms_id: fetch_value(source, :sms_id),
|
|
68
86
|
status: fetch_value(source, :status),
|
|
87
|
+
status_reported: key?(source, :status),
|
|
69
88
|
error: blank_to_nil(fetch_value(source, :error)),
|
|
70
89
|
# The provider spells this field "submition_date".
|
|
71
90
|
submission_date: fetch_value(source, :submition_date),
|
data/lib/sms_traffic/client.rb
CHANGED
|
@@ -4,6 +4,7 @@ require 'uri'
|
|
|
4
4
|
require_relative 'client/reply'
|
|
5
5
|
require_relative 'client/deliver_reply'
|
|
6
6
|
require_relative 'client/status_reply'
|
|
7
|
+
require_relative 'client/account_reply'
|
|
7
8
|
|
|
8
9
|
module SmsTraffic
|
|
9
10
|
class Client
|
|
@@ -13,6 +14,10 @@ module SmsTraffic
|
|
|
13
14
|
CONNECTION_ERRORS = [
|
|
14
15
|
SocketError, SystemCallError, IOError, Net::OpenTimeout, Net::ReadTimeout, OpenSSL::SSL::SSLError
|
|
15
16
|
].freeze
|
|
17
|
+
# Failures that prove the request never reached the server.
|
|
18
|
+
UNREACHABLE_ERRORS = [
|
|
19
|
+
SocketError, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH, Net::OpenTimeout
|
|
20
|
+
].freeze
|
|
16
21
|
|
|
17
22
|
class << self
|
|
18
23
|
# @param phone [String] phone number or phone numbers separated by comma
|
|
@@ -23,30 +28,65 @@ module SmsTraffic
|
|
|
23
28
|
# @raise [ConnectionError, HttpError, ApiError]
|
|
24
29
|
def deliver(phone, message, originator = nil, **options)
|
|
25
30
|
originator ||= SmsTraffic.configuration.originator
|
|
26
|
-
|
|
31
|
+
request(DeliverReply, deliver_params(phone, message, originator, **options), read_only: false)
|
|
27
32
|
end
|
|
28
33
|
|
|
29
34
|
# @param sms_ids [Array<String>] sms ids
|
|
30
35
|
# @return [StatusReply]
|
|
31
36
|
# @raise [ConnectionError, HttpError, ApiError]
|
|
32
37
|
def status(*sms_ids)
|
|
33
|
-
|
|
38
|
+
request(StatusReply, status_params(sms_ids), read_only: true)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [AccountReply]
|
|
42
|
+
# @raise [ConnectionError, HttpError, ApiError]
|
|
43
|
+
def balance
|
|
44
|
+
request(AccountReply, account_params, read_only: true)
|
|
34
45
|
end
|
|
35
46
|
|
|
36
47
|
private
|
|
37
48
|
|
|
38
|
-
|
|
39
|
-
|
|
49
|
+
# Sends the request to the configured host and, when a fallback host is set, repeats it there once if the
|
|
50
|
+
# failure allows it.
|
|
51
|
+
def request(reply_class, form_data, read_only:)
|
|
52
|
+
config = SmsTraffic.configuration
|
|
53
|
+
attempt(reply_class, form_data, config.host)
|
|
54
|
+
rescue Error => e
|
|
55
|
+
raise unless config.fallback_host && retry_on_fallback?(e, read_only:)
|
|
56
|
+
|
|
57
|
+
log("retry on #{config.fallback_host} after #{retry_reason(e)}")
|
|
58
|
+
attempt(reply_class, form_data, config.fallback_host)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Names the root cause and, for an API error, its code; never the message text.
|
|
62
|
+
def retry_reason(error)
|
|
63
|
+
reason = (error.cause&.class || error.class).to_s
|
|
64
|
+
error.is_a?(ApiError) ? "#{reason} #{error.code}" : reason
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def attempt(reply_class, form_data, host)
|
|
68
|
+
reply = reply_class.new(post(form_data, host))
|
|
40
69
|
raise ApiError.new(reply.code, reply.description) if reply.api_error?
|
|
41
70
|
|
|
42
71
|
reply
|
|
43
72
|
end
|
|
44
73
|
|
|
74
|
+
# A read-only request is safe to repeat after any transport failure. A deliver is repeated only when the
|
|
75
|
+
# request never reached the server, as the provider may already have queued the SMS otherwise.
|
|
76
|
+
def retry_on_fallback?(error, read_only:)
|
|
77
|
+
case error
|
|
78
|
+
when ApiError then error.retryable?
|
|
79
|
+
when ConnectionError then read_only || UNREACHABLE_ERRORS.any? { |klass| error.cause.is_a?(klass) }
|
|
80
|
+
when HttpError then read_only && error.status >= 500
|
|
81
|
+
else false
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
45
85
|
# @param form_data [Hash<Symbol, String|Integer>]
|
|
46
86
|
# @return [String] response body
|
|
47
|
-
def post(form_data)
|
|
87
|
+
def post(form_data, host)
|
|
48
88
|
config = SmsTraffic.configuration
|
|
49
|
-
uri = URI.join(
|
|
89
|
+
uri = URI.join(host, ENDPOINT)
|
|
50
90
|
log("request POST #{uri} #{filter(form_data)}")
|
|
51
91
|
|
|
52
92
|
response = perform(uri, form_data, config)
|
|
@@ -94,12 +134,15 @@ module SmsTraffic
|
|
|
94
134
|
end
|
|
95
135
|
|
|
96
136
|
def status_params(sms_ids)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
137
|
+
operation_params('status').merge(sms_id: sms_ids.join(','))
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def account_params
|
|
141
|
+
operation_params('account')
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def operation_params(operation)
|
|
145
|
+
{ login: SmsTraffic.configuration.login, password: SmsTraffic.configuration.password, operation: }
|
|
103
146
|
end
|
|
104
147
|
end
|
|
105
148
|
end
|
|
@@ -3,9 +3,12 @@ require 'nori'
|
|
|
3
3
|
module SmsTraffic
|
|
4
4
|
class Configuration
|
|
5
5
|
ATTRIBUTES = %i[
|
|
6
|
-
login password host originator debug logger open_timeout read_timeout validate_phone xml_parser
|
|
6
|
+
login password host fallback_host originator debug logger open_timeout read_timeout validate_phone xml_parser
|
|
7
7
|
].freeze
|
|
8
8
|
|
|
9
|
+
# Attributes not listed here default to nil.
|
|
10
|
+
DEFAULTS = { debug: false, open_timeout: 10, read_timeout: 30, validate_phone: true }.freeze
|
|
11
|
+
|
|
9
12
|
attr_accessor(*ATTRIBUTES)
|
|
10
13
|
|
|
11
14
|
def initialize
|
|
@@ -13,16 +16,8 @@ module SmsTraffic
|
|
|
13
16
|
end
|
|
14
17
|
|
|
15
18
|
def reset!
|
|
16
|
-
|
|
17
|
-
self.
|
|
18
|
-
self.host = nil
|
|
19
|
-
self.originator = nil
|
|
20
|
-
self.debug = false
|
|
21
|
-
self.logger = nil
|
|
22
|
-
self.open_timeout = 10
|
|
23
|
-
self.read_timeout = 30
|
|
24
|
-
self.validate_phone = true
|
|
25
|
-
self.xml_parser = Nori.new(advanced_typecasting: false)
|
|
19
|
+
ATTRIBUTES.each { |attribute| public_send("#{attribute}=", DEFAULTS[attribute]) }
|
|
20
|
+
self.xml_parser = Nori.new(advanced_typecasting: false)
|
|
26
21
|
end
|
|
27
22
|
|
|
28
23
|
def validate!
|
data/lib/sms_traffic/errors.rb
CHANGED
|
@@ -15,7 +15,7 @@ module SmsTraffic
|
|
|
15
15
|
end
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
# HTTP 200 reply whose result is ERROR.
|
|
18
|
+
# HTTP 200 reply whose `result` is not `OK` (`ERROR`, `ERRO`).
|
|
19
19
|
class ApiError < Error
|
|
20
20
|
attr_reader :code, :description
|
|
21
21
|
|
|
@@ -24,5 +24,22 @@ module SmsTraffic
|
|
|
24
24
|
@description = description
|
|
25
25
|
super("#{code}: #{description}")
|
|
26
26
|
end
|
|
27
|
+
|
|
28
|
+
# Temporary server problem: retry later or use the backup host.
|
|
29
|
+
def retryable?
|
|
30
|
+
code.to_i == 1000
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def access_denied?
|
|
34
|
+
[411, 412].include?(code.to_i)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def insufficient_funds?
|
|
38
|
+
code.to_i == 415
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def blocked_phone?
|
|
42
|
+
code.to_i == 432
|
|
43
|
+
end
|
|
27
44
|
end
|
|
28
45
|
end
|
data/lib/sms_traffic/sms.rb
CHANGED
|
@@ -37,6 +37,16 @@ module SmsTraffic
|
|
|
37
37
|
status
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
+
# @return [Boolean] the stored status is a final provider status
|
|
41
|
+
def final?
|
|
42
|
+
Status.final?(status)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# @return [Boolean] the stored status is Delivered
|
|
46
|
+
def delivered?
|
|
47
|
+
Status.delivered?(status)
|
|
48
|
+
end
|
|
49
|
+
|
|
40
50
|
private
|
|
41
51
|
|
|
42
52
|
def mark_sent
|
|
@@ -53,6 +63,9 @@ module SmsTraffic
|
|
|
53
63
|
add_error(entry.error)
|
|
54
64
|
elsif entry&.status
|
|
55
65
|
@status = entry.status.downcase
|
|
66
|
+
elsif entry&.status_reported?
|
|
67
|
+
# An empty status element is an intermediate provider status: keep the current one.
|
|
68
|
+
nil
|
|
56
69
|
else
|
|
57
70
|
add_error("No status returned for sms #{id}")
|
|
58
71
|
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module SmsTraffic
|
|
2
|
+
# Provider delivery statuses (HTTP API v1, operation=status).
|
|
3
|
+
module Status
|
|
4
|
+
INTERMEDIATE = ['', 'Buffered SMSC'].freeze
|
|
5
|
+
DELIVERED = 'Delivered'.freeze
|
|
6
|
+
FINAL = [DELIVERED, 'Non Delivered', 'Rejected', 'Expired', 'Deleted', 'Unknown status'].freeze
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
# @param status [String, nil] provider status, compared case-insensitively
|
|
10
|
+
def final?(status)
|
|
11
|
+
matches?(FINAL, status)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def delivered?(status)
|
|
15
|
+
matches?([DELIVERED], status)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def matches?(list, status)
|
|
21
|
+
value = status.to_s.strip
|
|
22
|
+
list.any? { |item| item.casecmp?(value) }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
data/lib/sms_traffic/version.rb
CHANGED
data/lib/sms_traffic.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sms_traffic_sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Rybolovlev
|
|
@@ -10,6 +10,20 @@ bindir: bin
|
|
|
10
10
|
cert_chain: []
|
|
11
11
|
date: 2026-11-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bigdecimal
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3.1'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '3.1'
|
|
13
27
|
- !ruby/object:Gem::Dependency
|
|
14
28
|
name: nokogiri
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -53,12 +67,14 @@ files:
|
|
|
53
67
|
- README.md
|
|
54
68
|
- lib/sms_traffic.rb
|
|
55
69
|
- lib/sms_traffic/client.rb
|
|
70
|
+
- lib/sms_traffic/client/account_reply.rb
|
|
56
71
|
- lib/sms_traffic/client/deliver_reply.rb
|
|
57
72
|
- lib/sms_traffic/client/reply.rb
|
|
58
73
|
- lib/sms_traffic/client/status_reply.rb
|
|
59
74
|
- lib/sms_traffic/configuration.rb
|
|
60
75
|
- lib/sms_traffic/errors.rb
|
|
61
76
|
- lib/sms_traffic/sms.rb
|
|
77
|
+
- lib/sms_traffic/status.rb
|
|
62
78
|
- lib/sms_traffic/version.rb
|
|
63
79
|
- lib/sms_traffic_sdk.rb
|
|
64
80
|
homepage: https://github.com/golifox/sms_traffic_sdk
|