sms-pilot-api-v1 0.0.3

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1c7152ee4bb497bc839d159e8cdc7bb9b9b5735179a8c5ced0176924acd53727
4
+ data.tar.gz: 9dfa498a10e6b68c6115ed42ff4a6b678a18b4b31ad2532f1092095d9efe9a60
5
+ SHA512:
6
+ metadata.gz: fb942ccb1bfab86c399c1c0567885d8ca0b43ca54f3e832d6316d6ad2838beb7d1ade363f537247aff90aa8090886a78f2ac0187c9e82c077ade0944bb4e5497
7
+ data.tar.gz: 6ca91958e3d50371b7a48c7679b6a6285f6ff9d8389e2e2a0605e02f6eea85359e2b873366351d762fe48bad1bf288c3f9d94244fa8c0ac5a09a1c85230ae5de
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2021-05-06
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ # Specify your gem's dependencies in gemspec
6
+ gemspec
7
+
8
+ gem "rake", "~> 13.0"
9
+ gem "rspec", "~> 3.0"
data/Gemfile.lock ADDED
@@ -0,0 +1,57 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sms-pilot-api-v1 (0.1.0)
5
+ http (>= 4.4)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ addressable (2.7.0)
11
+ public_suffix (>= 2.0.2, < 5.0)
12
+ diff-lcs (1.4.4)
13
+ domain_name (0.5.20190701)
14
+ unf (>= 0.0.5, < 1.0.0)
15
+ ffi (1.15.0)
16
+ ffi-compiler (1.0.1)
17
+ ffi (>= 1.0.0)
18
+ rake
19
+ http (4.4.1)
20
+ addressable (~> 2.3)
21
+ http-cookie (~> 1.0)
22
+ http-form_data (~> 2.2)
23
+ http-parser (~> 1.2.0)
24
+ http-cookie (1.0.3)
25
+ domain_name (~> 0.5)
26
+ http-form_data (2.3.0)
27
+ http-parser (1.2.3)
28
+ ffi-compiler (>= 1.0, < 2.0)
29
+ public_suffix (4.0.6)
30
+ rake (13.0.3)
31
+ rspec (3.10.0)
32
+ rspec-core (~> 3.10.0)
33
+ rspec-expectations (~> 3.10.0)
34
+ rspec-mocks (~> 3.10.0)
35
+ rspec-core (3.10.1)
36
+ rspec-support (~> 3.10.0)
37
+ rspec-expectations (3.10.1)
38
+ diff-lcs (>= 1.2.0, < 2.0)
39
+ rspec-support (~> 3.10.0)
40
+ rspec-mocks (3.10.2)
41
+ diff-lcs (>= 1.2.0, < 2.0)
42
+ rspec-support (~> 3.10.0)
43
+ rspec-support (3.10.2)
44
+ unf (0.1.4)
45
+ unf_ext
46
+ unf_ext (0.0.7.7)
47
+
48
+ PLATFORMS
49
+ x86_64-darwin-17
50
+
51
+ DEPENDENCIES
52
+ rake (~> 13.0)
53
+ rspec (~> 3.0)
54
+ sms-pilot-api-v1!
55
+
56
+ BUNDLED WITH
57
+ 2.2.11
data/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # SmsPilot API v1 client
2
+
3
+ Обёртка отправки GET-запроса на API endpoint сервиса SMS Pilot (API v1) для удобства доступа к ошибкам, статусам, цене SMS и т. п.
4
+
5
+ ## Installation
6
+
7
+ from RubyGems:
8
+
9
+ ```ruby
10
+ gem "sms-pilot-api-v1"
11
+ ```
12
+
13
+ from GitHub:
14
+
15
+ ```ruby
16
+ gem "sms-pilot-api-v1", git: "https://github.com/sergeypedan/sms-pilot-api-v1.git"
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ Test sending SMS from console with a test API key (find it at the end of this page):
22
+
23
+ ```sh
24
+ cd $(bundle info sms-pilot-api-v1 --path)
25
+ bin/console
26
+ ```
27
+
28
+ ### Initialize
29
+
30
+ ```ruby
31
+ client = SmsPilot::Client.new(api_key: "XXXXXXXXXXXXYYYYYYYYYYYYZZZZZZZZXXXXXXXXXXXXYYYYYYYYYYYYZZZZZZZZ")
32
+ #<SmsPilot::Client:0x00007fb1c602d490 @api_key="XXXXX...", @error=nil, @response_status=nil, @response_headers=nil, @response_body=nil, @response_data={}, @url=nil>
33
+ ```
34
+
35
+ ### Before sending
36
+
37
+ ```ruby
38
+ client.api_key # => "YOUR API KEY"
39
+ client.balance # => nil
40
+ client.error # => nil
41
+ client.phone # => nil
42
+ client.rejected? # => false
43
+ client.response_body # => nil
44
+ client.response_data # => {}
45
+ client.response_headers # => nil
46
+ client.response_status # => nil
47
+ client.sender_blocked? # => false
48
+ client.sms_cost # => nil
49
+ client.sms_sent? # => false
50
+ client.sms_status # => nil
51
+ client.url # => nil
52
+ ```
53
+
54
+ ### Sending SMS
55
+
56
+ ```ruby
57
+ client.send_sms("+7 (902) 123-45-67", "Привет, мир!") # => true
58
+ ```
59
+
60
+ Returns result of `sms_sent?`, so it’s either `true` or `false`.
61
+
62
+
63
+ ### Sending SMS succeeded
64
+
65
+ ```ruby
66
+ client.api_key # => "YOUR API KEY"
67
+ client.balance # => 20006.97
68
+ client.error # => nil
69
+ client.phone # => "79021234567"
70
+ client.rejected? # => false
71
+ client.response_body # => "{\"send\":[{\"server_id\":\"10000\",\"phone\":\"79021234567\",\"price\":\"1.68\",\"status\":\"0\"}],\"balance\":\"20006.97\",\"cost\":\"1.68\"}"
72
+ client.response_data # => {"send"=>[{"server_id"=>"10000", "phone"=>"79021234567", "price"=>"1.68", "status"=>"0"}], "balance"=>"20006.97", "cost"=>"1.68"}
73
+ client.response_headers # => {"Server"=>"nginx", "Date"=>"Thu, 06 May 2021 04:52:58 GMT", "Content-Type"=>"application/json; charset=utf-8", "Content-Length"=>"179", "Connection"=>"close", "Access-Control-Allow-Origin"=>"*"}
74
+ client.response_status # => 200
75
+ client.sender_blocked? # => false
76
+ client.sms_cost # => 1.68
77
+ client.sms_sent? # => true
78
+ client.sms_status # => 1
79
+ client.url # => "https://smspilot.ru/api.php?apikey=1234567890&format=json&send=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82%2C+%D0%BC%D0%B8%D1%80%21&to=79021234567"
80
+ ```
81
+
82
+ ### Sending SMS failed (but HTTP request succeeded)
83
+
84
+ ```ruby
85
+ client.api_key # => "YOUR API KEY"
86
+ client.balance # => nil
87
+ client.error # => "Неправильный API-ключ (см. настройки API в личном кабинете) (код ошибки: 101)"
88
+ client.phone # => "79021234567"
89
+ client.rejected? # => true
90
+ client.response_body # => "{\"error\":{\"code\":\"101\",\"description\":\"APIKEY is invalid\",\"description_ru\":\"Неправильный API-ключ (см. настройки API в личном кабинете)\"}}"
91
+ client.response_data # => {"error"=>{"code"=>"101", "description"=>"APIKEY is invalid", "description_ru"=>"Неправильный API-ключ (см. настройки API в личном кабинете)"}}
92
+ client.response_headers # => {"Server"=>"nginx", "Date"=>"Thu, 06 May 2021 04:52:58 GMT", "Content-Type"=>"application/json; charset=utf-8", "Content-Length"=>"179", "Connection"=>"close", "Access-Control-Allow-Origin"=>"*"}
93
+ client.response_status # => 200
94
+ client.sender_blocked? # => false
95
+ client.sms_cost # => nil
96
+ client.sms_sent? # => false
97
+ client.sms_status # => nil
98
+ client.url # => "https://smspilot.ru/api.php?apikey=1234567890&format=json&send=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82%2C+%D0%BC%D0%B8%D1%80%21&to=79021234567"
99
+ ```
100
+
101
+ ### HTTP request failed
102
+
103
+ ```ruby
104
+ client.api_key # => "YOUR API KEY"
105
+ client.balance # => nil
106
+ client.error # => "HTTP request failed with code 404"
107
+ client.phone # => "79021234567"
108
+ client.rejected? # => false
109
+ client.response_body # => "<html>\r\n<head><title>404 Not Found</title></head>\r\n<body>\r\n<center><h1>404 Not Found</h1></center>\r\n<hr><center>nginx</center>\r\n</body>\r\n</html>\r\n"
110
+ client.response_data # => {}
111
+ client.response_headers # => {"Server"=>"nginx", "Date"=>"Thu, 06 May 2021 05:30:23 GMT", "Content-Type"=>"text/html", "Content-Length"=>"146", "Connection"=>"close"}
112
+ client.response_status # => 404
113
+ client.sender_blocked? # => false
114
+ client.sms_cost # => nil
115
+ client.sms_sent? # => false
116
+ client.sms_status # => nil
117
+ client.url # => "https://smspilot.ru/api.php?apikey=1234567890&format=json&send=%D0%9F%D1%80%D0%B8%D0%B2%D0%B5%D1%82%2C+%D0%BC%D0%B8%D1%80%21&to=79021234567"
118
+ ```
119
+
120
+
121
+ ## SMS pilot API docs
122
+
123
+ - [web version](https://smspilot.ru/apikey.php) — см. вкладку PHP, в остальных ничего нет
124
+ - [PDF version](https://smspilot.ru/download/SMSPilotRu-HTTP-v1.9.19.pdf) — тут намного подробнее
125
+ - [Коды ошибок](https://smspilot.ru/apikey.php#err)
126
+
127
+
128
+ ## Test API key
129
+
130
+ https://smspilot.ru/apikey.php
131
+
132
+ ```
133
+ "XXXXXXXXXXXXYYYYYYYYYYYYZZZZZZZZXXXXXXXXXXXXYYYYYYYYYYYYZZZZZZZZ"
134
+ ```
135
+
136
+
137
+ ## API response examples
138
+
139
+ SMS sent:
140
+
141
+ ```json
142
+ {
143
+ "send": [
144
+ { "server_id": "10000", "phone": "79021234567", "price": "1.68", "status": "0" }
145
+ ],
146
+ "balance": "11908.50", "cost": "1.68"
147
+ }
148
+ ```
149
+
150
+ SMS rejected:
151
+
152
+ ```json
153
+ {
154
+ "error": {
155
+ "code": "400",
156
+ "description": "User not found",
157
+ "description_ru": "Пользователь не найден"
158
+ }
159
+ }
160
+ ```
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "sms_pilot"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/sms_pilot.rb ADDED
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "sms_pilot/version"
4
+ require_relative "sms_pilot/errors"
5
+ require_relative "sms_pilot/client"
6
+
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "http"
4
+ require "uri"
5
+
6
+ module SmsPilot
7
+
8
+ API_ENDPOINT = "https://smspilot.ru/api.php".freeze
9
+
10
+ class Client
11
+
12
+ attr_reader :api_key
13
+ attr_reader :error
14
+ attr_reader :phone
15
+ attr_reader :response_body
16
+ attr_reader :response_data
17
+ attr_reader :response_headers
18
+ attr_reader :response_status
19
+ attr_reader :url
20
+
21
+
22
+ def initialize(api_key:)
23
+ fail TypeError, "API key must be a String, you pass a #{api_key.class} (#{api_key})" unless api_key.is_a? String
24
+ fail TypeError, "API key cannot be empty" if api_key == ""
25
+
26
+ @api_key = api_key
27
+ @error = nil
28
+ @response_status = nil
29
+ @response_headers = nil
30
+ @response_body = nil
31
+ @response_data = {}
32
+ @url = nil
33
+ end
34
+
35
+ def send_sms(phone, text)
36
+ fail TypeError, "`phone` must be a String, you pass a #{phone.class} (#{phone})" unless phone.is_a? String
37
+ fail TypeError, "`text` must be a String, you pass a #{ text.class} (#{ text})" unless text.is_a? String
38
+ fail ArgumentError, "`phone` cannot be empty" if phone == ""
39
+ fail ArgumentError, "`text` cannot be empty" if text == ""
40
+ fail ArgumentError, "`phone` must contain digits" if phone.scan(/\d/).none?
41
+
42
+ @phone = normalize_phone(phone)
43
+ @url = build_url(@phone, text)
44
+
45
+ response = HTTP.timeout(connect: 15, read: 30).accept(:json).get(@url)
46
+ @response_status = response.status.code
47
+ @response_headers = response.headers.to_h
48
+ @response_body = response.body.to_s
49
+
50
+ unless response.status.success?
51
+ @error = "HTTP request failed with code #{response.status.code}"
52
+ return false
53
+ end
54
+
55
+ @response_data = JSON.parse @response_body
56
+
57
+ return @error = "#{error_description} (код ошибки: #{error_code})" if rejected?
58
+ return true
59
+
60
+ rescue JSON::ParserError => error
61
+ @error = "API returned invalid JSON. #{error.message}"
62
+
63
+ rescue HTTP::Error => error
64
+ @error = error.message
65
+
66
+ rescue => error
67
+ @error = error.message
68
+ end
69
+
70
+
71
+ def balance
72
+ @response_data["balance"]&.to_f if sms_sent?
73
+ end
74
+
75
+
76
+ # Коды ошибок: https://smspilot.ru/apikey.php#err
77
+ # Расшифровка ошибки пишется в @error
78
+ #
79
+ def error_code
80
+ @response_data.dig("error", "code")&.to_i if rejected?
81
+ end
82
+
83
+
84
+ # Коды ошибок: https://smspilot.ru/apikey.php#err
85
+ # Расшифровка ошибки пишется в @error
86
+ #
87
+ def error_description
88
+ @response_data.dig("error", "description_ru") if rejected?
89
+ end
90
+
91
+
92
+ # HTTP запрос удался, но API отказался отправлять SMS
93
+ def rejected?
94
+ return false if sms_sent?
95
+ @response_data["error"].is_a? Hash
96
+ end
97
+
98
+
99
+ # API сообщает, что мы заблокированы
100
+ #
101
+ # 105 из-за низкого баланса
102
+ # 106 за спам/ошибки
103
+ # 107 за недостоверные учетные данные / недоступна эл. почта / проблемы с телефоном
104
+ # 122 спорная ситуация
105
+ #
106
+ # Расшифровка ошибки пишется в @error
107
+ #
108
+ def sender_blocked?
109
+ [105, 106, 107, 122].include? error_code
110
+ end
111
+
112
+
113
+ # Цена отправленной только что SMS
114
+ def sms_cost
115
+ @response_data["cost"] if sms_sent?
116
+ end
117
+
118
+
119
+ # API успешно отправил SMS
120
+ def sms_sent?
121
+ @response_data["send"] != nil
122
+ end
123
+
124
+
125
+ # Статус доставки SMS
126
+ # https://smspilot.ru/apikey.php#status
127
+ #
128
+ def sms_status
129
+ @response_data.dig("send", 0, "status")&.to_i if sms_sent?
130
+ end
131
+
132
+
133
+ private
134
+
135
+ def build_url(phone, text)
136
+ URI.parse(API_ENDPOINT).tap do |url|
137
+ url.query = URI.encode_www_form({ apikey: @api_key, format: :json, send: text, to: phone })
138
+ end.to_s
139
+ end
140
+
141
+ def normalize_phone(phone)
142
+ phone.gsub(/[^0-9]/, '').sub(/^8/, '7').gsub('+7', '8')
143
+ end
144
+
145
+ end
146
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmsPilot
4
+ class Error < StandardError; end
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SmsPilot
4
+ VERSION = "0.0.3"
5
+ end
Binary file
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/sms_pilot/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.authors = ["Sergey Pedan"]
7
+ spec.summary = "Simple wrapper around SMS pilot API v1"
8
+ spec.description = "#{spec.summary}. Version 1 because it returns more data within its standard response"
9
+ spec.email = ["sergey.pedan@gmail.com"]
10
+ spec.homepage = "https://github.com/sergeypedan/sms-pilot-api-v1"
11
+ spec.license = "MIT"
12
+ spec.name = "sms-pilot-api-v1"
13
+ spec.version = SmsPilot::VERSION
14
+
15
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
16
+
17
+ spec.metadata = {
18
+ "changelog_uri" => "#{spec.homepage}/blob/master/Changelog.md",
19
+ "documentation_uri" => "#{spec.homepage}#usage",
20
+ "homepage_uri" => spec.homepage,
21
+ "source_code_uri" => spec.homepage
22
+ }
23
+
24
+ # Specify which files should be added to the gem when it is released.
25
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
26
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
27
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
28
+ end
29
+
30
+ spec.bindir = "bin"
31
+ spec.require_paths = ["lib"]
32
+
33
+ spec.add_runtime_dependency "http", "~> 4"
34
+
35
+ end
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sms-pilot-api-v1
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Sergey Pedan
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-05-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: http
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4'
27
+ description: Simple wrapper around SMS pilot API v1. Version 1 because it returns
28
+ more data within its standard response
29
+ email:
30
+ - sergey.pedan@gmail.com
31
+ executables: []
32
+ extensions: []
33
+ extra_rdoc_files: []
34
+ files:
35
+ - ".gitignore"
36
+ - ".rspec"
37
+ - CHANGELOG.md
38
+ - Gemfile
39
+ - Gemfile.lock
40
+ - README.md
41
+ - Rakefile
42
+ - bin/console
43
+ - bin/setup
44
+ - lib/sms_pilot.rb
45
+ - lib/sms_pilot/client.rb
46
+ - lib/sms_pilot/errors.rb
47
+ - lib/sms_pilot/version.rb
48
+ - sms-pilot-api-v1-0.0.2.gem
49
+ - sms-pilot-api-v1.gemspec
50
+ homepage: https://github.com/sergeypedan/sms-pilot-api-v1
51
+ licenses:
52
+ - MIT
53
+ metadata:
54
+ changelog_uri: https://github.com/sergeypedan/sms-pilot-api-v1/blob/master/Changelog.md
55
+ documentation_uri: https://github.com/sergeypedan/sms-pilot-api-v1#usage
56
+ homepage_uri: https://github.com/sergeypedan/sms-pilot-api-v1
57
+ source_code_uri: https://github.com/sergeypedan/sms-pilot-api-v1
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 2.5.0
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubygems_version: 3.2.5
74
+ signing_key:
75
+ specification_version: 4
76
+ summary: Simple wrapper around SMS pilot API v1
77
+ test_files: []