sms_traffic_sdk 0.0.7 → 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 +4 -4
- data/CHANGELOG.md +64 -0
- data/README.md +66 -40
- data/lib/sms_traffic/client/deliver_reply.rb +20 -37
- data/lib/sms_traffic/client/reply.rb +26 -7
- data/lib/sms_traffic/client/status_reply.rb +58 -24
- data/lib/sms_traffic/client.rb +62 -35
- data/lib/sms_traffic/configuration.rb +20 -56
- data/lib/sms_traffic/errors.rb +28 -0
- data/lib/sms_traffic/sms.rb +39 -26
- data/lib/sms_traffic/version.rb +1 -1
- data/lib/sms_traffic.rb +6 -9
- data/lib/sms_traffic_sdk.rb +1 -0
- metadata +14 -38
- data/.env.template +0 -4
- data/.fasterer.yml +0 -2
- data/.github/workflows/ci.yml +0 -72
- data/.gitignore +0 -12
- data/.rspec +0 -2
- data/.rubocop.yml +0 -20
- data/.tool-versions +0 -1
- data/Gemfile +0 -17
- data/Rakefile +0 -10
- data/bin/console +0 -18
- data/bin/setup +0 -7
- data/exe/konsierge-sms.rb +0 -2
- data/lib/sms_traffic/client/response.rb +0 -39
- data/sms_traffic_sdk.gemspec +0 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c877028062a36207636853033e3e623612a7ae44daf41d91686d86840f3b75c8
|
|
4
|
+
data.tar.gz: bd57b25436c7ea9e76d0e68b213730e7f77cf271f28c170d70234a2080e38a97
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 47f4123facc0dcb03f1fbf072cc266199df3fcf7483d6189fdfcee257988d188bec2607aae674575c9efb3153b36bdf69c9a6ac8c9af972bfdc878c0ca4d8577
|
|
7
|
+
data.tar.gz: 96bf4d2e9ea69cc8d6aad0aa87165f57e88b5163b00fffd034af98ee269d87f257cffdce2731e5793bdb6c539f213f06420db805978591da118b3bcf5cf6cb6c
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.0 - 2026-11-03
|
|
4
|
+
|
|
5
|
+
### Breaking changes
|
|
6
|
+
|
|
7
|
+
- Ruby 3.1 or newer is required.
|
|
8
|
+
- Zeitwerk is no longer a dependency; files are loaded with `require_relative`.
|
|
9
|
+
- `SmsTraffic::Configuration` is a plain class, not a `Singleton`. Defaults changed: `logger` is `nil` (was `$stdout`),
|
|
10
|
+
new `open_timeout` (10) and `read_timeout` (30). `originator` is no longer required by `configure`.
|
|
11
|
+
`Configuration#values_at` is removed.
|
|
12
|
+
- `SmsTraffic::Client::Response` is removed. `Client.deliver` returns a `DeliverReply` and `Client.status` a
|
|
13
|
+
`StatusReply` directly, and raise `SmsTraffic::ConnectionError`, `SmsTraffic::HttpError` or `SmsTraffic::ApiError`
|
|
14
|
+
(all `SmsTraffic::Error`) instead of returning a failed response.
|
|
15
|
+
- `Reply#hash` no longer returns the parsed reply (it is `Object#hash` again); use `Reply#to_h`.
|
|
16
|
+
- `DeliverReply#ok?` is strictly boolean and requires a queued message. `#sms_ids` is an Array of every sms id (it was
|
|
17
|
+
a Hash keyed by phone). `#message_infos` is replaced by `#messages`. `#error_description` reads
|
|
18
|
+
`"Sms has not been enqueued: <description>"`.
|
|
19
|
+
- `StatusReply#sms_array` is replaced by `#entries` (`StatusReply::Entry`); `#entry`, `#error?`, `#not_found?`
|
|
20
|
+
and `#send_date` are new.
|
|
21
|
+
- `Sms#response` is replaced by `Sms#reply`. `Sms#update_status` always returns the status String, and `Sms#deliver`
|
|
22
|
+
records `SmsTraffic::Error`s in `errors` instead of the HTTP code.
|
|
23
|
+
- The `konsierge-sms.rb` executable is removed.
|
|
24
|
+
- The default XML parser no longer typecasts (`Nori.new(advanced_typecasting: false)`): dates and `true` stay
|
|
25
|
+
provider strings.
|
|
26
|
+
- `nori` 2.8 or newer is required.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- `Sms` phone validation no longer accepts a newline-separated value (`\A`/`\z` anchors).
|
|
31
|
+
- `Client.status` with several ids sends a flat comma-separated `sms_id` list.
|
|
32
|
+
- The client builds its URI from the current `host` on every request instead of memoizing the first one.
|
|
33
|
+
- `open_timeout` and `read_timeout` are applied to every request.
|
|
34
|
+
- `Reply` no longer mutates (or fails on) frozen input.
|
|
35
|
+
- A multi-part SMS keeps every `sms_id`; a single `<message_info>` with a phone is parsed.
|
|
36
|
+
- `require 'sms_traffic_sdk'` works with `Bundler.require`.
|
|
37
|
+
- Loading the gem without a preloaded `StringIO` no longer fails with `NameError` (nori 2.7.0 missed
|
|
38
|
+
`require 'stringio'`; fixed in nori 2.8).
|
|
39
|
+
|
|
40
|
+
### Changed
|
|
41
|
+
|
|
42
|
+
- CI runs lint and RSpec on Ruby 3.1-3.4, smoke-tests the built gem in a clean Ruby process, uploads Cobertura
|
|
43
|
+
coverage to Codecov, and publishes to RubyGems through trusted publishing (OIDC) from a published release whose tag
|
|
44
|
+
matches the gem version. The gem package contains only `lib`, `README.md`, `LICENSE` and `CHANGELOG.md`.
|
|
45
|
+
|
|
46
|
+
### Security
|
|
47
|
+
|
|
48
|
+
- `Net::HTTP#set_debug_output` is no longer used, so the login, password and SMS text are never written to the logger.
|
|
49
|
+
Debug logging emits one request line with `login`, `password` and `message` filtered, and one response line with
|
|
50
|
+
the HTTP status and body size only.
|
|
51
|
+
|
|
52
|
+
### Migration
|
|
53
|
+
|
|
54
|
+
- Replace `Client.deliver(...).reply` / `.success?` with the returned reply and `rescue SmsTraffic::Error`.
|
|
55
|
+
- Replace `reply.hash` with `reply.to_h`, and `sms.response` with `sms.reply`.
|
|
56
|
+
- Treat `reply.sms_ids` as an Array; use `reply.messages` for per-phone data.
|
|
57
|
+
- Set `config.logger` explicitly if you relied on the `$stdout` default with `debug = true`.
|
|
58
|
+
- `Client.deliver` and `Client.status` no longer raise `SocketError`, `SystemCallError` (`Errno::*`), `IOError`,
|
|
59
|
+
`Net::OpenTimeout`, `Net::ReadTimeout` or `OpenSSL::SSL::SSLError`; rescue `SmsTraffic::ConnectionError` (the
|
|
60
|
+
original exception is in `cause`). `Sms#deliver` and `Sms#update_status` already rescue `SmsTraffic::Error` into
|
|
61
|
+
`errors`.
|
|
62
|
+
- Replace `response.as_json` / `sms.response.as_json` with `reply.to_h` / `sms.reply&.to_h`.
|
|
63
|
+
- Replace `response.success?` checks with `rescue SmsTraffic::Error`.
|
|
64
|
+
- Replace `reply.hash["send_date"]` / `reply.hash["status"]` with `reply.send_date` / `reply.status`.
|
data/README.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SmsTraffic
|
|
2
2
|
|
|
3
3
|
[](https://badge.fury.io/rb/sms_traffic_sdk)
|
|
4
|
-
[](https://codeclimate.com/github/golifox/
|
|
5
|
-
[](https://codeclimate.com/github/golifox/sms_traffic_sdk/maintainability)
|
|
5
|
+
[](https://codecov.io/github/golifox/sms_traffic_sdk)
|
|
6
|
+
[](https://github.com/golifox/sms_traffic_sdk/actions/workflows/ci.yml)
|
|
7
7
|
|
|
8
8
|
Ruby Gem as a software development kit (SDK) that facilitates interaction with the SMS Traffic HTTP API (smstraffic.ru/api).
|
|
9
9
|
This gem provides a convenient wrapper to integrate SMS Traffic services within Ruby applications, allowing for easy
|
|
@@ -37,63 +37,89 @@ Or install it yourself as:
|
|
|
37
37
|
Define settings:
|
|
38
38
|
|
|
39
39
|
```ruby
|
|
40
|
-
# initializers/sms_traffic.rb
|
|
41
|
-
require '
|
|
40
|
+
# config/initializers/sms_traffic.rb
|
|
41
|
+
require 'sms_traffic'
|
|
42
42
|
|
|
43
43
|
SmsTraffic.configure do |config|
|
|
44
|
-
config.login
|
|
45
|
-
config.password
|
|
46
|
-
config.
|
|
47
|
-
config.
|
|
48
|
-
config.
|
|
49
|
-
config.
|
|
44
|
+
config.login = 'login'
|
|
45
|
+
config.password = 'password'
|
|
46
|
+
config.host = 'https://api.smstraffic.ru'
|
|
47
|
+
config.originator = 'default_originator' # optional, can be given per SMS
|
|
48
|
+
config.open_timeout = 10 # seconds, default 10
|
|
49
|
+
config.read_timeout = 30 # seconds, default 30
|
|
50
|
+
config.validate_phone = true # default true: 11+ digits
|
|
51
|
+
config.debug = true # default false
|
|
52
|
+
config.logger = Rails.logger # required when debug is true
|
|
50
53
|
end
|
|
51
54
|
```
|
|
52
55
|
|
|
53
|
-
|
|
56
|
+
`login`, `password` and `host` are required; `logger` is required when `debug` is enabled.
|
|
57
|
+
With `debug` on, one request line and one response line are written through `logger.debug`;
|
|
58
|
+
`login`, `password` and `message` are replaced with `[FILTERED]`.
|
|
59
|
+
|
|
60
|
+
Send an SMS:
|
|
61
|
+
|
|
54
62
|
```ruby
|
|
55
|
-
sms = SmsTraffic::Sms.new('
|
|
56
|
-
# => #<SmsTraffic::Sms:0x0000 @errors=[], @message="text", @originator="default_originator", @phone="phone", @status="not-sent">
|
|
63
|
+
sms = SmsTraffic::Sms.new('79991112233', 'text', originator: 'my_brand') # originator defaults to the configured one
|
|
57
64
|
|
|
65
|
+
sms.deliver # => true when the provider enqueued it, false otherwise
|
|
66
|
+
sms.status # => 'sent' ('not-sent' until delivered)
|
|
67
|
+
sms.id # => '123456789' (the first sms id of the reply)
|
|
68
|
+
sms.reply # => #<SmsTraffic::Client::DeliverReply>
|
|
69
|
+
sms.errors # => [] (messages of failed attempts)
|
|
58
70
|
```
|
|
59
71
|
|
|
60
|
-
|
|
72
|
+
`deliver` and `update_status` never raise on provider or network failures; they append the message to `errors`
|
|
73
|
+
and keep the current `status`.
|
|
74
|
+
|
|
75
|
+
Refresh the status:
|
|
76
|
+
|
|
61
77
|
```ruby
|
|
62
|
-
sms.
|
|
63
|
-
# => true
|
|
64
|
-
sms.id # get sms id
|
|
65
|
-
# => '123456789'
|
|
78
|
+
sms.update_status # => 'delivered' (downcased provider status, or the unchanged status on failure)
|
|
66
79
|
```
|
|
67
80
|
|
|
68
|
-
|
|
81
|
+
Use the client directly. It raises typed errors:
|
|
82
|
+
|
|
69
83
|
```ruby
|
|
70
|
-
|
|
71
|
-
# =>
|
|
72
|
-
|
|
73
|
-
# => '
|
|
84
|
+
reply = SmsTraffic::Client.deliver('79991112233,79261112233', 'text', 'my_brand')
|
|
85
|
+
reply.ok? # => true
|
|
86
|
+
reply.sms_ids # => ['8287366071', '8287366073']
|
|
87
|
+
reply.messages # => [{ phone: '79991112233', sms_id: '8287366071', push_id: 'a' }, ...]
|
|
88
|
+
reply.sms_id('79261112233') # first sms id for the phone
|
|
89
|
+
|
|
90
|
+
reply = SmsTraffic::Client.status('8287713301', '8287713303')
|
|
91
|
+
reply.entries # => [#<struct SmsTraffic::Client::StatusReply::Entry sms_id="8287713301", status="Delivered", ...>]
|
|
92
|
+
reply.statuses # => { '8287713301' => 'Delivered' }
|
|
93
|
+
reply.errors # => { '8287713303' => 'no such message or this message does not belong to you' }
|
|
94
|
+
reply.entry('8287713303').not_found? # => true
|
|
95
|
+
reply.status('8287713301') # => 'Delivered'; the id may be omitted when there is one entry
|
|
74
96
|
```
|
|
75
97
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
response = SmsTraffic.status(sms_id) # status - sms delivery status unless error or return error
|
|
79
|
-
reply = response.reply
|
|
80
|
-
# => #<SmsTraffic::Client::StatusReply:0x0000>
|
|
81
|
-
status = reply.status
|
|
82
|
-
# => "Delivered"
|
|
83
|
-
reply.hash
|
|
84
|
-
# =>
|
|
85
|
-
# {"error"=>nil,
|
|
86
|
-
# "submition_date"=>"2024-07-15 11:36:25",
|
|
87
|
-
# "send_date"=>"2024-07-15 11:36:25",
|
|
88
|
-
# "last_status_change_date"=>"2024-07-15 11:36:25",
|
|
89
|
-
# "sms_id"=>"865149797164801235",
|
|
90
|
-
# "status"=>"Delivered"}
|
|
98
|
+
Each entry has `sms_id`, `status`, `error`, `submission_date`, `send_date`, `last_status_change_date`
|
|
99
|
+
(strings as sent by the provider), `error?` and `not_found?`.
|
|
91
100
|
|
|
101
|
+
### Errors
|
|
102
|
+
|
|
103
|
+
All errors inherit from `SmsTraffic::Error`:
|
|
104
|
+
|
|
105
|
+
- `SmsTraffic::ConnectionError` - network, timeout or TLS failure (the original exception is `cause`).
|
|
106
|
+
- `SmsTraffic::HttpError` - non-2xx response; has `status` and `body`.
|
|
107
|
+
- `SmsTraffic::ApiError` - the provider replied `ERROR`; has `code` and `description`.
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
begin
|
|
111
|
+
SmsTraffic::Client.deliver('79991112233', 'text')
|
|
112
|
+
rescue SmsTraffic::ApiError => e
|
|
113
|
+
e.code # => '401'
|
|
114
|
+
e.description # => 'login param is missing'
|
|
115
|
+
rescue SmsTraffic::Error => e
|
|
116
|
+
e.message
|
|
117
|
+
end
|
|
92
118
|
```
|
|
93
119
|
|
|
94
120
|
# Contributing
|
|
95
121
|
|
|
96
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/golifox/sms_traffic_sdk.
|
|
122
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/golifox/sms_traffic_sdk.
|
|
97
123
|
|
|
98
124
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
99
125
|
|
|
@@ -1,62 +1,45 @@
|
|
|
1
1
|
module SmsTraffic
|
|
2
2
|
class Client
|
|
3
|
-
class DeliverReply <
|
|
4
|
-
|
|
3
|
+
class DeliverReply < Reply
|
|
4
|
+
OK_RESULT = 'OK'.freeze
|
|
5
5
|
|
|
6
|
+
# @return [Boolean] result is OK and at least one message was queued
|
|
6
7
|
def ok?
|
|
7
|
-
result ==
|
|
8
|
+
result == OK_RESULT && !messages.empty?
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
def error?
|
|
11
12
|
!ok?
|
|
12
13
|
end
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def code
|
|
19
|
-
fetch_value(hash, :code)
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
def description
|
|
23
|
-
fetch_value(hash, :description)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def message_infos
|
|
27
|
-
fetch_value(hash, :message_infos)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def sms_id(phone = nil)
|
|
31
|
-
message_info = fetch_value(message_infos, :message_info)
|
|
32
|
-
return fetch_value(message_info, :sms_id) if message_info.is_a?(Hash) || message_info.nil?
|
|
33
|
-
|
|
34
|
-
if phone
|
|
35
|
-
fetch_value(find_message_info(phone), :sms_id)
|
|
36
|
-
elsif message_info.is_a?(Array)
|
|
37
|
-
fetch_value(message_info.first, :sms_id)
|
|
15
|
+
# @return [Array<Hash>] queued messages as { phone:, sms_id:, push_id: }
|
|
16
|
+
def messages
|
|
17
|
+
@messages ||= message_infos.map do |info|
|
|
18
|
+
{ phone: fetch_value(info, :phone), sms_id: fetch_value(info, :sms_id), push_id: fetch_value(info, :push_id) }
|
|
38
19
|
end
|
|
39
20
|
end
|
|
40
21
|
|
|
22
|
+
# @return [Array<String>] every sms id in reply order
|
|
41
23
|
def sms_ids
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return [fetch_value(message_info, :sms_id)] if message_info&.size == 1
|
|
45
|
-
return [] if message_info.nil?
|
|
24
|
+
messages.filter_map { |message| message[:sms_id] }
|
|
25
|
+
end
|
|
46
26
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
27
|
+
# @param phone [String, nil]
|
|
28
|
+
# @return [String, nil] the first sms id, or the first one for the phone
|
|
29
|
+
def sms_id(phone = nil)
|
|
30
|
+
message = phone ? messages.find { |item| item[:phone].to_s == phone.to_s } : messages.first
|
|
31
|
+
message&.fetch(:sms_id)
|
|
50
32
|
end
|
|
51
33
|
|
|
52
34
|
def error_description
|
|
53
|
-
"
|
|
35
|
+
"Sms has not been enqueued: #{description}" if error?
|
|
54
36
|
end
|
|
55
37
|
|
|
56
38
|
private
|
|
57
39
|
|
|
58
|
-
def
|
|
59
|
-
fetch_value(
|
|
40
|
+
def message_infos
|
|
41
|
+
info = fetch_value(fetch_value(to_h, :message_infos), :message_info)
|
|
42
|
+
info.is_a?(Array) ? info : [info].compact
|
|
60
43
|
end
|
|
61
44
|
end
|
|
62
45
|
end
|
|
@@ -1,19 +1,38 @@
|
|
|
1
1
|
module SmsTraffic
|
|
2
2
|
class Client
|
|
3
3
|
class Reply
|
|
4
|
-
|
|
4
|
+
ERROR_RESULT = 'ERROR'.freeze
|
|
5
|
+
|
|
6
|
+
attr_reader :xml, :to_h
|
|
5
7
|
|
|
6
8
|
def initialize(xml, xml_parser: SmsTraffic.configuration.xml_parser)
|
|
7
|
-
@xml
|
|
8
|
-
|
|
9
|
+
@xml = xml.gsub(/\s+/, ' ')
|
|
10
|
+
parsed = fetch_value(xml_parser.parse(@xml), :reply)
|
|
11
|
+
@to_h = parsed.is_a?(Hash) ? parsed : {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def result
|
|
15
|
+
fetch_value(to_h, :result)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def code
|
|
19
|
+
fetch_value(to_h, :code)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def description
|
|
23
|
+
fetch_value(to_h, :description)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def api_error?
|
|
27
|
+
result == ERROR_RESULT
|
|
9
28
|
end
|
|
10
29
|
|
|
11
30
|
private
|
|
12
31
|
|
|
13
|
-
def fetch_value(
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
32
|
+
def fetch_value(source, key)
|
|
33
|
+
return unless source.is_a?(Hash)
|
|
34
|
+
|
|
35
|
+
source.fetch(key.to_s) { source[key.to_sym] }
|
|
17
36
|
end
|
|
18
37
|
end
|
|
19
38
|
end
|
|
@@ -1,43 +1,77 @@
|
|
|
1
1
|
module SmsTraffic
|
|
2
2
|
class Client
|
|
3
3
|
class StatusReply < Reply
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
NOT_FOUND = /no such message/i
|
|
5
|
+
|
|
6
|
+
Entry = Struct.new(
|
|
7
|
+
:sms_id, :status, :error, :submission_date, :send_date, :last_status_change_date, keyword_init: true
|
|
8
|
+
) do
|
|
9
|
+
def error?
|
|
10
|
+
!error.nil?
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def not_found?
|
|
14
|
+
error? && error.match?(NOT_FOUND)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @return [Array<Entry>]
|
|
19
|
+
def entries
|
|
20
|
+
@entries ||= entry_sources.map { |source| build_entry(source) }
|
|
6
21
|
end
|
|
7
22
|
|
|
8
|
-
|
|
9
|
-
|
|
23
|
+
# @param sms_id [String, nil] required unless the reply has exactly one entry
|
|
24
|
+
# @return [Entry, nil]
|
|
25
|
+
def entry(sms_id = nil)
|
|
26
|
+
return entries.find { |item| item.sms_id.to_s == sms_id.to_s } if sms_id
|
|
27
|
+
return entries.first if entries.size == 1
|
|
10
28
|
|
|
11
|
-
raise ArgumentError, 'sms_id is required'
|
|
29
|
+
raise ArgumentError, 'sms_id is required'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
%i[status error send_date].each do |name|
|
|
33
|
+
define_method(name) { |sms_id = nil| entry(sms_id)&.public_send(name) }
|
|
34
|
+
end
|
|
12
35
|
|
|
13
|
-
|
|
36
|
+
%i[error? not_found?].each do |name|
|
|
37
|
+
define_method(name) { |sms_id = nil| entry(sms_id)&.public_send(name) || false }
|
|
14
38
|
end
|
|
15
39
|
|
|
16
|
-
# @return [Hash]
|
|
17
|
-
# @example
|
|
18
|
-
#
|
|
19
|
-
# {
|
|
20
|
-
# '1234567890': 'DELIVERED',
|
|
21
|
-
# '0987654321': 'FAILED'
|
|
22
|
-
# }
|
|
40
|
+
# @return [Hash{String => String}] sms_id => status, without nil values
|
|
23
41
|
def statuses
|
|
24
|
-
|
|
25
|
-
hash[fetch_value(sms, :sms_id)] = fetch_value(sms, :status)
|
|
26
|
-
end.compact
|
|
42
|
+
entries.to_h { |item| [item.sms_id, item.status] }.compact
|
|
27
43
|
end
|
|
28
44
|
|
|
29
|
-
|
|
30
|
-
|
|
45
|
+
# @return [Hash{String => String}] sms_id => error, without nil values
|
|
46
|
+
def errors
|
|
47
|
+
entries.to_h { |item| [item.sms_id, item.error] }.compact
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
31
51
|
|
|
32
|
-
|
|
52
|
+
def entry_sources
|
|
53
|
+
sms = fetch_value(to_h, :sms)
|
|
54
|
+
return sms if sms.is_a?(Array)
|
|
55
|
+
return [sms] if sms.is_a?(Hash)
|
|
33
56
|
|
|
34
|
-
|
|
57
|
+
to_h.empty? ? [] : [to_h]
|
|
35
58
|
end
|
|
36
59
|
|
|
37
|
-
def
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
60
|
+
def blank_to_nil(value)
|
|
61
|
+
stripped = value&.strip
|
|
62
|
+
stripped unless stripped.nil? || stripped.empty?
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def build_entry(source)
|
|
66
|
+
Entry.new(
|
|
67
|
+
sms_id: fetch_value(source, :sms_id),
|
|
68
|
+
status: fetch_value(source, :status),
|
|
69
|
+
error: blank_to_nil(fetch_value(source, :error)),
|
|
70
|
+
# The provider spells this field "submition_date".
|
|
71
|
+
submission_date: fetch_value(source, :submition_date),
|
|
72
|
+
send_date: fetch_value(source, :send_date),
|
|
73
|
+
last_status_change_date: fetch_value(source, :last_status_change_date)
|
|
74
|
+
)
|
|
41
75
|
end
|
|
42
76
|
end
|
|
43
77
|
end
|
data/lib/sms_traffic/client.rb
CHANGED
|
@@ -1,77 +1,104 @@
|
|
|
1
1
|
require 'net/http'
|
|
2
|
+
require 'openssl'
|
|
3
|
+
require 'uri'
|
|
4
|
+
require_relative 'client/reply'
|
|
5
|
+
require_relative 'client/deliver_reply'
|
|
6
|
+
require_relative 'client/status_reply'
|
|
2
7
|
|
|
3
8
|
module SmsTraffic
|
|
4
9
|
class Client
|
|
5
|
-
|
|
6
|
-
|
|
10
|
+
ENDPOINT = 'multi.php'.freeze
|
|
11
|
+
FILTERED = '[FILTERED]'.freeze
|
|
12
|
+
FILTERED_PARAMS = %i[login password message].freeze
|
|
13
|
+
CONNECTION_ERRORS = [
|
|
14
|
+
SocketError, SystemCallError, IOError, Net::OpenTimeout, Net::ReadTimeout, OpenSSL::SSL::SSLError
|
|
15
|
+
].freeze
|
|
7
16
|
|
|
8
|
-
|
|
17
|
+
class << self
|
|
18
|
+
# @param phone [String] phone number or phone numbers separated by comma
|
|
9
19
|
# @param message [String] message
|
|
10
|
-
# @param originator [String] originator (subject)
|
|
20
|
+
# @param originator [String] originator (subject), defaults to the configured one
|
|
11
21
|
# @param options [Hash] additional options
|
|
22
|
+
# @return [DeliverReply]
|
|
23
|
+
# @raise [ConnectionError, HttpError, ApiError]
|
|
12
24
|
def deliver(phone, message, originator = nil, **options)
|
|
13
25
|
originator ||= SmsTraffic.configuration.originator
|
|
14
|
-
|
|
15
|
-
response = perform_request(form_data)
|
|
16
|
-
Response.new(response, type: :deliver)
|
|
26
|
+
build_reply(DeliverReply, post(deliver_params(phone, message, originator, **options)))
|
|
17
27
|
end
|
|
18
28
|
|
|
19
|
-
# @param
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
29
|
+
# @param sms_ids [Array<String>] sms ids
|
|
30
|
+
# @return [StatusReply]
|
|
31
|
+
# @raise [ConnectionError, HttpError, ApiError]
|
|
32
|
+
def status(*sms_ids)
|
|
33
|
+
build_reply(StatusReply, post(status_params(sms_ids)))
|
|
24
34
|
end
|
|
25
35
|
|
|
26
36
|
private
|
|
27
37
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
def build_reply(reply_class, body)
|
|
39
|
+
reply = reply_class.new(body)
|
|
40
|
+
raise ApiError.new(reply.code, reply.description) if reply.api_error?
|
|
31
41
|
|
|
32
|
-
|
|
33
|
-
http = Net::HTTP.new(uri.host, uri.port)
|
|
34
|
-
http.read_timeout = 30
|
|
35
|
-
http.use_ssl = (uri.scheme == 'https')
|
|
36
|
-
http.set_debug_output(logger)
|
|
37
|
-
http
|
|
42
|
+
reply
|
|
38
43
|
end
|
|
39
44
|
|
|
40
45
|
# @param form_data [Hash<Symbol, String|Integer>]
|
|
41
|
-
# @return [
|
|
42
|
-
def
|
|
43
|
-
|
|
46
|
+
# @return [String] response body
|
|
47
|
+
def post(form_data)
|
|
48
|
+
config = SmsTraffic.configuration
|
|
49
|
+
uri = URI.join(config.host, ENDPOINT)
|
|
50
|
+
log("request POST #{uri} #{filter(form_data)}")
|
|
51
|
+
|
|
52
|
+
response = perform(uri, form_data, config)
|
|
53
|
+
log("response #{response.code} (#{response.body.to_s.bytesize} bytes)")
|
|
54
|
+
successful_body(response)
|
|
55
|
+
rescue *CONNECTION_ERRORS => e
|
|
56
|
+
raise ConnectionError, "#{e.class}: #{e.message}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def successful_body(response)
|
|
60
|
+
raise HttpError.new(response.code.to_i, response.body) unless response.is_a?(Net::HTTPSuccess)
|
|
61
|
+
|
|
62
|
+
response.body
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def perform(uri, form_data, config)
|
|
66
|
+
options = { use_ssl: uri.scheme == 'https', open_timeout: config.open_timeout,
|
|
67
|
+
read_timeout: config.read_timeout }
|
|
68
|
+
Net::HTTP.start(uri.host, uri.port, **options) do |http|
|
|
44
69
|
request = Net::HTTP::Post.new(uri.request_uri)
|
|
45
70
|
request.set_form_data(form_data)
|
|
46
71
|
http.request(request)
|
|
47
72
|
end
|
|
48
73
|
end
|
|
49
74
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
75
|
+
def log(line)
|
|
76
|
+
config = SmsTraffic.configuration
|
|
77
|
+
config.logger.debug("[SmsTraffic] #{line}") if config.debug
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def filter(form_data)
|
|
81
|
+
form_data.to_h { |key, value| [key, FILTERED_PARAMS.include?(key) ? FILTERED : value] }
|
|
82
|
+
end
|
|
83
|
+
|
|
55
84
|
def deliver_params(phone, message, originator, **options)
|
|
56
85
|
{
|
|
57
86
|
login: SmsTraffic.configuration.login,
|
|
58
87
|
password: SmsTraffic.configuration.password,
|
|
59
88
|
phones: phone,
|
|
60
|
-
message
|
|
89
|
+
message:,
|
|
61
90
|
want_sms_ids: 1,
|
|
62
91
|
rus: 5,
|
|
63
|
-
originator:
|
|
92
|
+
originator:
|
|
64
93
|
}.merge(options)
|
|
65
94
|
end
|
|
66
95
|
|
|
67
|
-
|
|
68
|
-
# @return [Hash<Symbol, String|Integer>]
|
|
69
|
-
def status_params(*message_ids)
|
|
96
|
+
def status_params(sms_ids)
|
|
70
97
|
{
|
|
71
98
|
login: SmsTraffic.configuration.login,
|
|
72
99
|
password: SmsTraffic.configuration.password,
|
|
73
100
|
operation: 'status',
|
|
74
|
-
sms_id:
|
|
101
|
+
sms_id: sms_ids.join(',')
|
|
75
102
|
}
|
|
76
103
|
end
|
|
77
104
|
end
|
|
@@ -1,75 +1,39 @@
|
|
|
1
|
-
require 'singleton'
|
|
2
1
|
require 'nori'
|
|
3
2
|
|
|
4
3
|
module SmsTraffic
|
|
5
4
|
class Configuration
|
|
6
|
-
|
|
5
|
+
ATTRIBUTES = %i[
|
|
6
|
+
login password host originator debug logger open_timeout read_timeout validate_phone xml_parser
|
|
7
|
+
].freeze
|
|
7
8
|
|
|
8
|
-
attr_accessor
|
|
9
|
+
attr_accessor(*ATTRIBUTES)
|
|
9
10
|
|
|
10
11
|
def initialize
|
|
11
|
-
|
|
12
|
-
self.logger = default_configuration[:logger]
|
|
13
|
-
self.xml_parser = default_configuration[:xml_parser]
|
|
14
|
-
self.validate_phone = default_configuration[:validate_phone]
|
|
15
|
-
super
|
|
12
|
+
reset!
|
|
16
13
|
end
|
|
17
14
|
|
|
18
15
|
def reset!
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
self.login = nil
|
|
17
|
+
self.password = nil
|
|
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)
|
|
22
26
|
end
|
|
23
27
|
|
|
24
28
|
def validate!
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
29
|
+
raise ArgumentError, 'login should be defined' if login.nil?
|
|
30
|
+
raise ArgumentError, 'password should be defined' if password.nil?
|
|
31
|
+
raise ArgumentError, 'host should be defined' if host.nil?
|
|
32
|
+
raise ArgumentError, 'logger should be defined when debug is enabled' if debug && logger.nil?
|
|
28
33
|
end
|
|
29
34
|
|
|
30
35
|
def to_h
|
|
31
|
-
{
|
|
32
|
-
login: login,
|
|
33
|
-
password: password,
|
|
34
|
-
host: host,
|
|
35
|
-
originator: originator,
|
|
36
|
-
debug: debug,
|
|
37
|
-
logger: logger,
|
|
38
|
-
xml_parser: xml_parser
|
|
39
|
-
}
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def values_at(*keys)
|
|
43
|
-
to_h.values_at(*keys)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
private
|
|
47
|
-
|
|
48
|
-
def default_configuration
|
|
49
|
-
{
|
|
50
|
-
login: nil,
|
|
51
|
-
password: nil,
|
|
52
|
-
host: nil,
|
|
53
|
-
originator: nil,
|
|
54
|
-
debug: false,
|
|
55
|
-
logger: $stdout,
|
|
56
|
-
xml_parser: default_xml_parser,
|
|
57
|
-
validate_phone: true
|
|
58
|
-
}
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def validation_rules
|
|
62
|
-
{
|
|
63
|
-
'Login' => login,
|
|
64
|
-
'Password' => password,
|
|
65
|
-
'Server' => host,
|
|
66
|
-
'Originator' => originator,
|
|
67
|
-
'Debug logger' => debug && !logger ? nil : true
|
|
68
|
-
}
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
def default_xml_parser
|
|
72
|
-
@default_xml_parser ||= Nori.new(advanced_typecasting: true).freeze
|
|
36
|
+
ATTRIBUTES.to_h { |attribute| [attribute, public_send(attribute)] }
|
|
73
37
|
end
|
|
74
38
|
end
|
|
75
39
|
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module SmsTraffic
|
|
2
|
+
class Error < StandardError; end
|
|
3
|
+
|
|
4
|
+
# Network or TLS failure; the original exception is available as +cause+.
|
|
5
|
+
class ConnectionError < Error; end
|
|
6
|
+
|
|
7
|
+
# Non-2xx HTTP status.
|
|
8
|
+
class HttpError < Error
|
|
9
|
+
attr_reader :status, :body
|
|
10
|
+
|
|
11
|
+
def initialize(status, body)
|
|
12
|
+
@status = status
|
|
13
|
+
@body = body
|
|
14
|
+
super("HTTP #{status}")
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# HTTP 200 reply whose result is ERROR.
|
|
19
|
+
class ApiError < Error
|
|
20
|
+
attr_reader :code, :description
|
|
21
|
+
|
|
22
|
+
def initialize(code, description)
|
|
23
|
+
@code = code
|
|
24
|
+
@description = description
|
|
25
|
+
super("#{code}: #{description}")
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/sms_traffic/sms.rb
CHANGED
|
@@ -1,54 +1,67 @@
|
|
|
1
1
|
module SmsTraffic
|
|
2
2
|
class Sms
|
|
3
|
+
PHONE_FORMAT = /\A\d{11,}\z/
|
|
4
|
+
|
|
3
5
|
attr_accessor :phone, :message, :originator
|
|
4
|
-
attr_reader :id, :status, :errors
|
|
6
|
+
attr_reader :id, :status, :errors, :reply
|
|
5
7
|
|
|
6
8
|
def initialize(phone, message, originator: nil)
|
|
7
9
|
@phone = phone
|
|
8
10
|
@message = message
|
|
9
11
|
@originator = originator || SmsTraffic.configuration.originator
|
|
12
|
+
@reply = nil
|
|
10
13
|
@status = 'not-sent'
|
|
11
14
|
@errors = []
|
|
12
15
|
validate!
|
|
13
16
|
end
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
unless response.success?
|
|
20
|
-
@errors << response.error_description
|
|
21
|
-
return false
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# @type [Client::Response::Reply]
|
|
25
|
-
reply = response.reply
|
|
18
|
+
# @return [Boolean] true when the provider enqueued the sms
|
|
19
|
+
def deliver
|
|
20
|
+
@reply = Client.deliver(phone, message, originator)
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
@errors << (reply.error_description || 'Sms has been not enqueued')
|
|
33
|
-
false
|
|
34
|
-
end
|
|
22
|
+
reply.ok? ? mark_sent : add_error(reply.error_description)
|
|
23
|
+
reply.ok?
|
|
24
|
+
rescue SmsTraffic::Error => e
|
|
25
|
+
add_error(e.message)
|
|
26
|
+
false
|
|
35
27
|
end
|
|
36
28
|
|
|
29
|
+
# @return [String] the current status; unchanged when the check fails
|
|
37
30
|
def update_status
|
|
38
31
|
return status if id.nil?
|
|
39
32
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
33
|
+
apply_entry(Client.status(id).entry(id))
|
|
34
|
+
status
|
|
35
|
+
rescue SmsTraffic::Error => e
|
|
36
|
+
add_error(e.message)
|
|
37
|
+
status
|
|
44
38
|
end
|
|
45
39
|
|
|
46
40
|
private
|
|
47
41
|
|
|
48
|
-
def
|
|
42
|
+
def mark_sent
|
|
43
|
+
@status = 'sent'
|
|
44
|
+
@id = reply.sms_id
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def add_error(error)
|
|
48
|
+
@errors << error
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def apply_entry(entry)
|
|
52
|
+
if entry&.error?
|
|
53
|
+
add_error(entry.error)
|
|
54
|
+
elsif entry&.status
|
|
55
|
+
@status = entry.status.downcase
|
|
56
|
+
else
|
|
57
|
+
add_error("No status returned for sms #{id}")
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def validate! # rubocop:disable Metrics/AbcSize
|
|
49
62
|
raise ArgumentError, "Phone should be assigned to #{self.class}." if phone.nil?
|
|
50
63
|
|
|
51
|
-
if SmsTraffic.configuration.validate_phone && phone.to_s
|
|
64
|
+
if SmsTraffic.configuration.validate_phone && !PHONE_FORMAT.match?(phone.to_s)
|
|
52
65
|
raise ArgumentError, 'Phone number should contain only numbers. Minimum length is 11.'
|
|
53
66
|
end
|
|
54
67
|
|
data/lib/sms_traffic/version.rb
CHANGED
data/lib/sms_traffic.rb
CHANGED
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
require 'zeitwerk'
|
|
2
|
-
|
|
3
|
-
loader = Zeitwerk::Loader.for_gem
|
|
4
|
-
loader.ignore("#{__dir__}/sms_traffic/version.rb")
|
|
5
|
-
loader.enable_reloading
|
|
6
|
-
loader.setup
|
|
7
|
-
|
|
8
1
|
require_relative 'sms_traffic/version'
|
|
2
|
+
require_relative 'sms_traffic/errors'
|
|
3
|
+
require_relative 'sms_traffic/configuration'
|
|
4
|
+
require_relative 'sms_traffic/client'
|
|
5
|
+
require_relative 'sms_traffic/sms'
|
|
9
6
|
|
|
10
7
|
module SmsTraffic
|
|
11
8
|
class << self
|
|
12
9
|
def configuration
|
|
13
|
-
@configuration ||= Configuration.
|
|
10
|
+
@configuration ||= Configuration.new
|
|
14
11
|
end
|
|
15
12
|
|
|
16
|
-
def configure
|
|
13
|
+
def configure
|
|
17
14
|
yield(configuration) if block_given?
|
|
18
15
|
configuration.validate!
|
|
19
16
|
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require_relative 'sms_traffic'
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sms_traffic_sdk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0
|
|
4
|
+
version: 0.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Rybolovlev
|
|
8
8
|
autorequire:
|
|
9
|
-
bindir:
|
|
9
|
+
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-11-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: nokogiri
|
|
@@ -30,68 +30,44 @@ dependencies:
|
|
|
30
30
|
requirements:
|
|
31
31
|
- - "~>"
|
|
32
32
|
- !ruby/object:Gem::Version
|
|
33
|
-
version: '2.
|
|
33
|
+
version: '2.8'
|
|
34
34
|
type: :runtime
|
|
35
35
|
prerelease: false
|
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
37
|
requirements:
|
|
38
38
|
- - "~>"
|
|
39
39
|
- !ruby/object:Gem::Version
|
|
40
|
-
version: '2.
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: zeitwerk
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - "~>"
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '2.5'
|
|
48
|
-
type: :runtime
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - "~>"
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '2.5'
|
|
40
|
+
version: '2.8'
|
|
55
41
|
description: |-
|
|
56
42
|
Ruby Gem as a SDK for interacting with the SMS Traffic HTTP API (smstraffic.ru/api).
|
|
57
43
|
This gem provides a convenient way to integrate SMS Traffic services into Ruby applications,
|
|
58
44
|
enabling tasks such as sending SMS messages, checking delivery status, and others.
|
|
59
45
|
email:
|
|
60
46
|
- i@golifox.ru
|
|
61
|
-
executables:
|
|
62
|
-
- konsierge-sms.rb
|
|
47
|
+
executables: []
|
|
63
48
|
extensions: []
|
|
64
49
|
extra_rdoc_files: []
|
|
65
50
|
files:
|
|
66
|
-
-
|
|
67
|
-
- ".fasterer.yml"
|
|
68
|
-
- ".github/workflows/ci.yml"
|
|
69
|
-
- ".gitignore"
|
|
70
|
-
- ".rspec"
|
|
71
|
-
- ".rubocop.yml"
|
|
72
|
-
- ".tool-versions"
|
|
73
|
-
- Gemfile
|
|
51
|
+
- CHANGELOG.md
|
|
74
52
|
- LICENSE
|
|
75
53
|
- README.md
|
|
76
|
-
- Rakefile
|
|
77
|
-
- bin/console
|
|
78
|
-
- bin/setup
|
|
79
|
-
- exe/konsierge-sms.rb
|
|
80
54
|
- lib/sms_traffic.rb
|
|
81
55
|
- lib/sms_traffic/client.rb
|
|
82
56
|
- lib/sms_traffic/client/deliver_reply.rb
|
|
83
57
|
- lib/sms_traffic/client/reply.rb
|
|
84
|
-
- lib/sms_traffic/client/response.rb
|
|
85
58
|
- lib/sms_traffic/client/status_reply.rb
|
|
86
59
|
- lib/sms_traffic/configuration.rb
|
|
60
|
+
- lib/sms_traffic/errors.rb
|
|
87
61
|
- lib/sms_traffic/sms.rb
|
|
88
62
|
- lib/sms_traffic/version.rb
|
|
89
|
-
- sms_traffic_sdk.
|
|
90
|
-
homepage: https://github.com/golifox/
|
|
63
|
+
- lib/sms_traffic_sdk.rb
|
|
64
|
+
homepage: https://github.com/golifox/sms_traffic_sdk
|
|
91
65
|
licenses:
|
|
92
66
|
- MIT
|
|
93
67
|
metadata:
|
|
94
68
|
rubygems_mfa_required: 'true'
|
|
69
|
+
source_code_uri: https://github.com/golifox/sms_traffic_sdk
|
|
70
|
+
changelog_uri: https://github.com/golifox/sms_traffic_sdk/blob/main/CHANGELOG.md
|
|
95
71
|
post_install_message:
|
|
96
72
|
rdoc_options: []
|
|
97
73
|
require_paths:
|
|
@@ -100,14 +76,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
100
76
|
requirements:
|
|
101
77
|
- - ">="
|
|
102
78
|
- !ruby/object:Gem::Version
|
|
103
|
-
version:
|
|
79
|
+
version: '3.1'
|
|
104
80
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
81
|
requirements:
|
|
106
82
|
- - ">="
|
|
107
83
|
- !ruby/object:Gem::Version
|
|
108
84
|
version: '0'
|
|
109
85
|
requirements: []
|
|
110
|
-
rubygems_version: 3.5.
|
|
86
|
+
rubygems_version: 3.5.22
|
|
111
87
|
signing_key:
|
|
112
88
|
specification_version: 4
|
|
113
89
|
summary: Send sms via SmsTraffic service.
|
data/.env.template
DELETED
data/.fasterer.yml
DELETED
data/.github/workflows/ci.yml
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
name: Ruby Gem CI/CD
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches: [ main ]
|
|
6
|
-
pull_request:
|
|
7
|
-
branches: [ main ]
|
|
8
|
-
release:
|
|
9
|
-
types: [created]
|
|
10
|
-
|
|
11
|
-
jobs:
|
|
12
|
-
build:
|
|
13
|
-
runs-on: ubuntu-latest
|
|
14
|
-
strategy:
|
|
15
|
-
matrix:
|
|
16
|
-
ruby-version: ['2.7', '3.0', '3.1', '3.2', '3.3']
|
|
17
|
-
|
|
18
|
-
steps:
|
|
19
|
-
- uses: actions/checkout@v2
|
|
20
|
-
|
|
21
|
-
- name: Set up Ruby
|
|
22
|
-
uses: ruby/setup-ruby@v1
|
|
23
|
-
with:
|
|
24
|
-
ruby-version: ${{ matrix.ruby-version }}
|
|
25
|
-
|
|
26
|
-
- name: Install Bundler for Ruby < 3.0
|
|
27
|
-
if: ${{ matrix.ruby-version < '3.0' }}
|
|
28
|
-
run: gem install bundler -v 2.4.22 --no-document
|
|
29
|
-
|
|
30
|
-
- name: Install Bundler for Ruby >= 3.0
|
|
31
|
-
if: ${{ matrix.ruby-version >= '3.0' }}
|
|
32
|
-
run: gem install bundler -v '~> 2.5' --no-document
|
|
33
|
-
|
|
34
|
-
- name: Install dependencies
|
|
35
|
-
run: bundle install --jobs 4 --retry 3
|
|
36
|
-
|
|
37
|
-
- name: Run tests and linting
|
|
38
|
-
run: bundle exec rake
|
|
39
|
-
|
|
40
|
-
- name: Upload coverage reports to Codecov
|
|
41
|
-
uses: codecov/codecov-action@v4.0.1
|
|
42
|
-
with:
|
|
43
|
-
token: ${{ secrets.CODECOV_TOKEN }}
|
|
44
|
-
file: ./coverage/.resultset.json
|
|
45
|
-
fail_ci_if_error: false
|
|
46
|
-
|
|
47
|
-
publish:
|
|
48
|
-
needs: build
|
|
49
|
-
runs-on: ubuntu-latest
|
|
50
|
-
if: startsWith(github.ref, 'refs/tags/v')
|
|
51
|
-
steps:
|
|
52
|
-
- uses: actions/checkout@v2
|
|
53
|
-
|
|
54
|
-
- name: Set up Ruby for publish
|
|
55
|
-
uses: ruby/setup-ruby@v1
|
|
56
|
-
with:
|
|
57
|
-
ruby-version: '3.3'
|
|
58
|
-
|
|
59
|
-
- name: Install dependencies
|
|
60
|
-
run: |
|
|
61
|
-
gem install bundler --no-document
|
|
62
|
-
bundle install --jobs 4 --retry 3
|
|
63
|
-
|
|
64
|
-
- name: Build gem
|
|
65
|
-
run: |
|
|
66
|
-
gem build *.gemspec
|
|
67
|
-
|
|
68
|
-
- name: Push gem to RubyGems
|
|
69
|
-
run: |
|
|
70
|
-
gem push *.gem
|
|
71
|
-
env:
|
|
72
|
-
GEM_HOST_API_KEY: ${{secrets.GEM_HOST_API_KEY}}
|
data/.gitignore
DELETED
data/.rspec
DELETED
data/.rubocop.yml
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
AllCops:
|
|
2
|
-
TargetRubyVersion: 2.7.4
|
|
3
|
-
NewCops: enable
|
|
4
|
-
SuggestExtensions: false
|
|
5
|
-
Exclude:
|
|
6
|
-
- 'bin/**/*'
|
|
7
|
-
|
|
8
|
-
Style/Documentation:
|
|
9
|
-
Enabled: false
|
|
10
|
-
|
|
11
|
-
Style/FrozenStringLiteralComment:
|
|
12
|
-
EnforcedStyle: 'never'
|
|
13
|
-
|
|
14
|
-
Layout/HashAlignment:
|
|
15
|
-
EnforcedHashRocketStyle: table
|
|
16
|
-
EnforcedColonStyle: table
|
|
17
|
-
|
|
18
|
-
Metrics/BlockLength:
|
|
19
|
-
Exclude:
|
|
20
|
-
- 'spec/**/*'
|
data/.tool-versions
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
ruby 3.3.3
|
data/Gemfile
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
source 'https://rubygems.org'
|
|
2
|
-
|
|
3
|
-
gemspec
|
|
4
|
-
|
|
5
|
-
group :development, :test do
|
|
6
|
-
gem 'dotenv', '~> 2.7'
|
|
7
|
-
gem 'fasterer', '~> 0.9'
|
|
8
|
-
gem 'rack', '~> 3.1.7'
|
|
9
|
-
gem 'rake', '~> 13.2'
|
|
10
|
-
gem 'rspec', '~> 3.10'
|
|
11
|
-
gem 'rspec-mocks', '~> 3.10'
|
|
12
|
-
gem 'rubocop', '~> 1.18'
|
|
13
|
-
gem 'rubocop-performance', '~> 1.11'
|
|
14
|
-
gem 'rubocop-rspec', '~> 2.11'
|
|
15
|
-
gem 'simplecov', '~> 0.22'
|
|
16
|
-
gem 'webmock', '~> 3.14'
|
|
17
|
-
end
|
data/Rakefile
DELETED
data/bin/console
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
require 'bundler/setup'
|
|
4
|
-
require 'dotenv'
|
|
5
|
-
require 'sms_traffic'
|
|
6
|
-
|
|
7
|
-
Dotenv.load('.env')
|
|
8
|
-
|
|
9
|
-
SmsTraffic.configure do |config|
|
|
10
|
-
config.host = ENV['SMS_TRAFFIC_HOST']
|
|
11
|
-
config.login = ENV['SMS_TRAFFIC_LOGIN']
|
|
12
|
-
config.password = ENV['SMS_TRAFFIC_PASSWORD']
|
|
13
|
-
config.originator = ENV['SMS_TRAFFIC_ORIGINATOR']
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
require 'irb'
|
|
18
|
-
IRB.start
|
data/bin/setup
DELETED
data/exe/konsierge-sms.rb
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
require_relative 'deliver_reply'
|
|
2
|
-
require_relative 'status_reply'
|
|
3
|
-
|
|
4
|
-
module SmsTraffic
|
|
5
|
-
class Client
|
|
6
|
-
class Response
|
|
7
|
-
attr_reader :origin, :reply, :code, :body, :message
|
|
8
|
-
|
|
9
|
-
REPLY_HASH_MAP = {
|
|
10
|
-
:status => StatusReply,
|
|
11
|
-
:deliver => DeliverReply,
|
|
12
|
-
nil => Reply
|
|
13
|
-
}.freeze
|
|
14
|
-
|
|
15
|
-
def initialize(response, type: nil)
|
|
16
|
-
@origin = response
|
|
17
|
-
@code = response.code.to_i
|
|
18
|
-
@body = response.body
|
|
19
|
-
@message = response.message
|
|
20
|
-
|
|
21
|
-
return unless success?
|
|
22
|
-
|
|
23
|
-
@reply = REPLY_HASH_MAP[type].new(body)
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def success?
|
|
27
|
-
code == 200
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def failure?
|
|
31
|
-
!success?
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def error_description
|
|
35
|
-
"#{code}: #{message}" if failure?
|
|
36
|
-
end
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
data/sms_traffic_sdk.gemspec
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
lib = File.expand_path('lib', __dir__)
|
|
2
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
-
|
|
4
|
-
require_relative 'lib/sms_traffic/version'
|
|
5
|
-
|
|
6
|
-
Gem::Specification.new do |spec|
|
|
7
|
-
spec.name = 'sms_traffic_sdk'
|
|
8
|
-
spec.version = SmsTraffic::VERSION
|
|
9
|
-
spec.authors = ['David Rybolovlev']
|
|
10
|
-
spec.email = ['i@golifox.ru']
|
|
11
|
-
|
|
12
|
-
spec.summary = 'Send sms via SmsTraffic service.'
|
|
13
|
-
spec.description = 'Ruby Gem as a SDK for interacting with the SMS Traffic HTTP API (smstraffic.ru/api).
|
|
14
|
-
This gem provides a convenient way to integrate SMS Traffic services into Ruby applications,
|
|
15
|
-
enabling tasks such as sending SMS messages, checking delivery status, and others.'
|
|
16
|
-
spec.homepage = 'https://github.com/golifox/sms_traffic'
|
|
17
|
-
spec.license = 'MIT'
|
|
18
|
-
|
|
19
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
20
|
-
spec.bindir = 'exe'
|
|
21
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
22
|
-
spec.require_paths = ['lib']
|
|
23
|
-
|
|
24
|
-
spec.required_ruby_version = '>= 2.7.0'
|
|
25
|
-
|
|
26
|
-
spec.add_dependency 'nokogiri', '~> 1.12'
|
|
27
|
-
spec.add_dependency 'nori', '~> 2.6'
|
|
28
|
-
spec.add_dependency 'zeitwerk', '~> 2.5'
|
|
29
|
-
|
|
30
|
-
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
31
|
-
end
|