smslist 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE.md +20 -0
- data/README.md +114 -0
- data/Rakefile +19 -0
- data/lib/smslist.rb +26 -0
- data/lib/smslist/authentication.rb +14 -0
- data/lib/smslist/client.rb +29 -0
- data/lib/smslist/client/balance.rb +27 -0
- data/lib/smslist/client/sms.rb +52 -0
- data/lib/smslist/client/state.rb +64 -0
- data/lib/smslist/configuration.rb +55 -0
- data/lib/smslist/error.rb +11 -0
- data/lib/smslist/request.rb +37 -0
- data/lib/smslist/response.rb +10 -0
- data/lib/smslist/version.rb +3 -0
- data/spec/fixtures/balance.xml +6 -0
- data/spec/fixtures/sms.xml +7 -0
- data/spec/fixtures/state.xml +7 -0
- data/spec/fixtures/wrong_credentials.xml +4 -0
- data/spec/smslist/client/balance_spec.rb +21 -0
- data/spec/smslist/client/sms_spec.rb +40 -0
- data/spec/smslist/client/state_spec.rb +32 -0
- data/spec/smslist/client_spec.rb +84 -0
- data/spec/smslist_spec.rb +17 -0
- data/spec/spec_helper.rb +41 -0
- metadata +135 -0
data/LICENSE.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 Yury Lebedev
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
# Smslist
|
2
|
+
|
3
|
+
[][travis]
|
4
|
+
[][gemnasium]
|
5
|
+
[][codeclimate]
|
6
|
+
[][coveralls]
|
7
|
+
|
8
|
+
[travis]: http://travis-ci.org/lebedev-yury/smslist
|
9
|
+
[gemnasium]: https://gemnasium.com/lebedev-yury/smslist
|
10
|
+
[codeclimate]: https://codeclimate.com/github/lebedev-yury/smslist
|
11
|
+
[coveralls]: https://coveralls.io/r/lebedev-yury/smslist
|
12
|
+
|
13
|
+
Simple Ruby wrapper for the SMSlist API. [SMSlist][smslist] is a service for mass sending sms messages.
|
14
|
+
|
15
|
+
[smslist]: http://www.smscell.ru
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
gem install smslist
|
21
|
+
```
|
22
|
+
|
23
|
+
## Configuration
|
24
|
+
|
25
|
+
You can configure this gem in an initializer:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
Smslist.configure do |c|
|
29
|
+
c.sender = '79101234567'
|
30
|
+
|
31
|
+
# you can use the token
|
32
|
+
c.token = 'your_api_token'
|
33
|
+
|
34
|
+
# or authenticate with login and password
|
35
|
+
c.login = 'username'
|
36
|
+
c.password = 'secret'
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
Or simply:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
client = Smslist.new(sender: '79101234567', token: 'your_api_token')
|
44
|
+
```
|
45
|
+
|
46
|
+
## Usage
|
47
|
+
|
48
|
+
### Getting the balance and remaining sms count
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
balance = client.balance
|
52
|
+
remaining = client.remaining_sms
|
53
|
+
```
|
54
|
+
|
55
|
+
### Sending sms messages
|
56
|
+
|
57
|
+
```ruby
|
58
|
+
recipients = %w(79031234567 79032345678 79033456789)
|
59
|
+
response = client.send_sms('Your text message', recipients)
|
60
|
+
```
|
61
|
+
|
62
|
+
If can pass optional param, to send a flash sms message:
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
response = client.send_sms('Your text message', recipients, flash: true)
|
66
|
+
```
|
67
|
+
|
68
|
+
You will get a hash for each number, with status, message id (to retrieve status later) and number of parts:
|
69
|
+
|
70
|
+
```ruby
|
71
|
+
{
|
72
|
+
'79031234567' => { status: :ok, id: 1001, parts: 2 },
|
73
|
+
...
|
74
|
+
'79033456789' => { error: 'Номер телефона присутствует в стоп-листе.' }
|
75
|
+
}
|
76
|
+
```
|
77
|
+
|
78
|
+
Status :ok means, that the message was successfully placed in a queue.
|
79
|
+
|
80
|
+
### Getting state of sent messages
|
81
|
+
|
82
|
+
```ruby
|
83
|
+
message_ids = %w(1001 1002 1003 1004)
|
84
|
+
response = client.state(message_ids)
|
85
|
+
```
|
86
|
+
|
87
|
+
You will get a hash for id, with state and datetime, or error message.
|
88
|
+
|
89
|
+
## Supported Ruby Versions
|
90
|
+
|
91
|
+
This library aims to support and is [tested against][travis] the following Ruby
|
92
|
+
implementations:
|
93
|
+
|
94
|
+
* Ruby 1.8.7
|
95
|
+
* Ruby 1.9.2
|
96
|
+
* Ruby 1.9.3
|
97
|
+
* Ruby 2.0.0
|
98
|
+
|
99
|
+
If something doesn't work on one of these Ruby versions, it's a bug.
|
100
|
+
|
101
|
+
This library may inadvertently work (or seem to work) on other Ruby
|
102
|
+
implementations, however support will only be provided for the versions listed
|
103
|
+
above.
|
104
|
+
|
105
|
+
## Inspiration
|
106
|
+
Smslist was inspired by [Octokit][].
|
107
|
+
|
108
|
+
[octokit]: https://github.com/pengwynn/octokit
|
109
|
+
|
110
|
+
## Copyright
|
111
|
+
Copyright (c) 2013 Yury Lebedev.
|
112
|
+
See [LICENSE][] for details.
|
113
|
+
|
114
|
+
[license]: LICENSE.md
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
task :test => :spec
|
8
|
+
task :default => :spec
|
9
|
+
|
10
|
+
namespace :doc do
|
11
|
+
require 'yard'
|
12
|
+
YARD::Rake::YardocTask.new do |task|
|
13
|
+
task.files = ['README.md', 'LICENSE.md', 'lib/**/*.rb']
|
14
|
+
task.options = [
|
15
|
+
'--output-dir', 'doc/yard',
|
16
|
+
'--markup', 'markdown',
|
17
|
+
]
|
18
|
+
end
|
19
|
+
end
|
data/lib/smslist.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'smslist/configuration'
|
3
|
+
require 'smslist/error'
|
4
|
+
require 'smslist/client'
|
5
|
+
|
6
|
+
module Smslist
|
7
|
+
extend Configuration
|
8
|
+
class << self
|
9
|
+
# Alias for Smslist::Client.new
|
10
|
+
#
|
11
|
+
# @return [Smslist::Client]
|
12
|
+
def new(options = {})
|
13
|
+
Smslist::Client.new(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Delegate to Smslist::Client.new
|
17
|
+
def method_missing(method, *args, &block)
|
18
|
+
return super unless new.respond_to?(method)
|
19
|
+
new.send(method, *args, &block)
|
20
|
+
end
|
21
|
+
|
22
|
+
def respond_to?(method, include_private = false)
|
23
|
+
new.respond_to?(method, include_private) || super(method, include_private)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Smslist
|
2
|
+
module Authentication
|
3
|
+
def authentication
|
4
|
+
if token
|
5
|
+
{:token => token}
|
6
|
+
elsif login && password
|
7
|
+
{:login => login, :password => password}
|
8
|
+
else
|
9
|
+
raise Smslist::UnauthorizedError.new('Access token, or login '\
|
10
|
+
'and password are not initialized')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'smslist/authentication'
|
2
|
+
require 'smslist/request'
|
3
|
+
require 'smslist/response'
|
4
|
+
|
5
|
+
require 'smslist/client/balance'
|
6
|
+
require 'smslist/client/sms'
|
7
|
+
require 'smslist/client/state'
|
8
|
+
|
9
|
+
module Smslist
|
10
|
+
class Client
|
11
|
+
attr_accessor(*Configuration::VALID_OPTIONS_KEYS)
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
options = Smslist.options.merge(options)
|
15
|
+
|
16
|
+
Configuration::VALID_OPTIONS_KEYS.each do |key|
|
17
|
+
self.send("#{key}=", options[key])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
include Smslist::Authentication
|
22
|
+
include Smslist::Request
|
23
|
+
include Smslist::Response
|
24
|
+
|
25
|
+
include Smslist::Client::Balance
|
26
|
+
include Smslist::Client::Sms
|
27
|
+
include Smslist::Client::State
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Smslist
|
2
|
+
class Client
|
3
|
+
module Balance
|
4
|
+
# Get account balance
|
5
|
+
#
|
6
|
+
# @return [Float] account balance
|
7
|
+
# @example Get balance for user qwerty
|
8
|
+
# client = Smslist.new(login: 'qwerty', password: 'secret')
|
9
|
+
# client.balance
|
10
|
+
def balance
|
11
|
+
response = parse_xml(post build_xml_body.to_xml, :balance)
|
12
|
+
response.xpath('response/money').text.to_f
|
13
|
+
end
|
14
|
+
|
15
|
+
# Get remaining sms count
|
16
|
+
#
|
17
|
+
# @return [Integer] remaining sms count
|
18
|
+
# @example Get remaining sms count for user qwerty
|
19
|
+
# client = Smslist.new(login: 'qwerty', password: 'secret')
|
20
|
+
# client.remaining_sms
|
21
|
+
def remaining_sms
|
22
|
+
response = parse_xml(post build_xml_body.to_xml, :balance)
|
23
|
+
response.xpath('response/sms').first.text.to_i
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Smslist
|
2
|
+
class Client
|
3
|
+
module Sms
|
4
|
+
# Send sms message for a list of recipients
|
5
|
+
#
|
6
|
+
# @param text [String] Message text
|
7
|
+
# @param recipients [Array] Array of phone numbers
|
8
|
+
# @option values [Boolean] :flash True for flash sms messages
|
9
|
+
# @return [Hash] Hash with statuses for each sent message
|
10
|
+
# @example Send message to a list of users
|
11
|
+
# client = Smslist.new(token: 'secret', sender: '4040')
|
12
|
+
#
|
13
|
+
# recipients = %w(79031234567 79032345678)
|
14
|
+
# client.send_sms('Message', recipients)
|
15
|
+
def send_sms(text, recipients = [], options = {})
|
16
|
+
unless sms_sender = sender
|
17
|
+
raise Smslist::NoSenderError.new('You must set a sender')
|
18
|
+
end
|
19
|
+
|
20
|
+
body = build_xml_body do |xml|
|
21
|
+
xml.message(type: "#{ 'flash' if options[:flash] }sms") {
|
22
|
+
xml.sender sms_sender
|
23
|
+
xml.text_ text.encode(:xml => :text)
|
24
|
+
recipients.each_with_index do |recipient, index|
|
25
|
+
xml.abonent :phone => recipient, :number_sms => index + 1
|
26
|
+
end
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
response = parse_xml(post body.to_xml)
|
31
|
+
parse_send_sms_response(response, recipients)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def parse_send_sms_response(response, recipients)
|
37
|
+
response_array = response.xpath('response/information').map do |node|
|
38
|
+
if node.text == 'send'
|
39
|
+
[
|
40
|
+
recipients[node[:number_sms].to_i - 1],
|
41
|
+
{:status => :ok, :id => node['id_sms'].to_i, :parts => node['parts'].to_i }
|
42
|
+
]
|
43
|
+
else
|
44
|
+
[recipients[node[:number_sms].to_i - 1], { :error => node.text }]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
Hash[*response_array.flatten]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Smslist
|
2
|
+
class Client
|
3
|
+
module State
|
4
|
+
STATE_ERRORS = {
|
5
|
+
1 => 'The subscriber is absent or out of a coverage',
|
6
|
+
2 => 'Call barred service activated',
|
7
|
+
3 => 'Unknown subscriber',
|
8
|
+
4 => 'Memory capacity exceeded',
|
9
|
+
5 => 'Equipment protocol error',
|
10
|
+
6 => 'Teleservice not provisioned',
|
11
|
+
7 => 'Facility not supported',
|
12
|
+
8 => 'Subscriber is busy',
|
13
|
+
9 => 'Roaming restrictions',
|
14
|
+
10 => 'Timeout',
|
15
|
+
11 => 'SS7 routing error',
|
16
|
+
12 => 'Internal system failure',
|
17
|
+
13 => 'SMSC failure'
|
18
|
+
}.freeze
|
19
|
+
|
20
|
+
# Get state for a list of message ids
|
21
|
+
#
|
22
|
+
# @param message_ids [Array] Array of message ids
|
23
|
+
# @return [Hash] Hash with state, time or error for each message
|
24
|
+
# @example Get state for a list of message ids
|
25
|
+
# client = Smslist.new(token: 'secret')
|
26
|
+
#
|
27
|
+
# message_ids = %w(10001 10002 10003)
|
28
|
+
# client.state(message_ids)
|
29
|
+
def state(message_ids = [])
|
30
|
+
body = build_xml_body do |xml|
|
31
|
+
xml.get_state {
|
32
|
+
message_ids.each do |id|
|
33
|
+
xml.id_sms id
|
34
|
+
end
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
response = parse_xml(post body.to_xml, :state)
|
39
|
+
parse_state_response(response)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def parse_state_response(response)
|
45
|
+
response_array = response.xpath('response/state').map do |node|
|
46
|
+
if node[:err] == '0'
|
47
|
+
[
|
48
|
+
node['id_sms'],
|
49
|
+
{
|
50
|
+
:state => node.text,
|
51
|
+
:datetime => DateTime.strptime(node['time'], '%Y-%m-%d %H:%M:%S')
|
52
|
+
}
|
53
|
+
]
|
54
|
+
else
|
55
|
+
[node['id_sms'], { :state => node.text,
|
56
|
+
:error => STATE_ERRORS[node['err'].to_i] }]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
Hash[*response_array.flatten]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'smslist/version'
|
2
|
+
|
3
|
+
module Smslist
|
4
|
+
module Configuration
|
5
|
+
VALID_OPTIONS_KEYS = [
|
6
|
+
:api_endpoint,
|
7
|
+
:login,
|
8
|
+
:password,
|
9
|
+
:token,
|
10
|
+
:user_agent,
|
11
|
+
:sender
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
METHOD_ENDPOINTS = [
|
15
|
+
:state,
|
16
|
+
:balance,
|
17
|
+
:incoming,
|
18
|
+
:def,
|
19
|
+
:list_bases,
|
20
|
+
:bases,
|
21
|
+
:list_phones,
|
22
|
+
:phones,
|
23
|
+
:delete_phones,
|
24
|
+
:stop,
|
25
|
+
:list_sheduled,
|
26
|
+
:scheduled
|
27
|
+
].freeze
|
28
|
+
|
29
|
+
DEFAULT_API_ENDPOINT = 'https://my.smscell.ru/xml/'
|
30
|
+
DEFAULT_USER_AGENT = "Smslist Ruby Gem #{Smslist::VERSION}".freeze
|
31
|
+
|
32
|
+
attr_accessor(*VALID_OPTIONS_KEYS)
|
33
|
+
|
34
|
+
def self.extended(base)
|
35
|
+
base.reset
|
36
|
+
end
|
37
|
+
|
38
|
+
def configure
|
39
|
+
yield self
|
40
|
+
end
|
41
|
+
|
42
|
+
def options
|
43
|
+
VALID_OPTIONS_KEYS.inject({}) { |o, k| o.merge!(k => send(k)) }
|
44
|
+
end
|
45
|
+
|
46
|
+
def reset
|
47
|
+
self.user_agent = DEFAULT_USER_AGENT
|
48
|
+
self.api_endpoint = DEFAULT_API_ENDPOINT
|
49
|
+
self.login = nil
|
50
|
+
self.password = nil
|
51
|
+
self.token = nil
|
52
|
+
self.sender = nil
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
|
3
|
+
module Smslist
|
4
|
+
module Request
|
5
|
+
def post(xml, method = nil)
|
6
|
+
HTTParty.post request_uri(method), :body => xml, :headers => headers
|
7
|
+
end
|
8
|
+
|
9
|
+
def build_xml_body(&block)
|
10
|
+
Nokogiri::XML::Builder.new(:encoding => 'utf-8') do |xml|
|
11
|
+
xml.request {
|
12
|
+
xml.security {
|
13
|
+
authentication.each { |k, v| xml.send(k, :value => v) }
|
14
|
+
}
|
15
|
+
xml.instance_eval(&block) if block_given?
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def headers
|
23
|
+
{
|
24
|
+
'Content-type' => 'text/xml; charset=utf-8',
|
25
|
+
'User-Agent' => Configuration::DEFAULT_USER_AGENT
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def request_uri(method = nil)
|
30
|
+
if method && Configuration::METHOD_ENDPOINTS.include?(method)
|
31
|
+
[Configuration::DEFAULT_API_ENDPOINT, method.to_s + '.php'].join
|
32
|
+
else
|
33
|
+
Configuration::DEFAULT_API_ENDPOINT
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
2
|
+
<response>
|
3
|
+
<information number_sms="1" id_sms="1001" parts="2">send</information>
|
4
|
+
<information number_sms="2" id_sms="1002" parts="2">send</information>
|
5
|
+
<information number_sms="3" id_sms="1003" parts="2">send</information>
|
6
|
+
<information number_sms="4" id_sms="1004" parts="2">Номер телефона присутствует в стоп-листе.</information>
|
7
|
+
</response>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8" ?>
|
2
|
+
<response>
|
3
|
+
<state id_sms="1001" time="2011-01-01 12:57:46" err="0">deliver</state>
|
4
|
+
<state id_sms="1002" time="2011-01-01 12:57:46" err="0">deliver</state>
|
5
|
+
<state id_sms="1003" time="2011-01-01 12:57:46" err="0">expired</state>
|
6
|
+
<state id_sms="1004" time="" err="1">not_deliver</state>
|
7
|
+
</response>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Smslist::Client::Balance do
|
6
|
+
let(:client) { Smslist::Client.new(:token => 'secret') }
|
7
|
+
|
8
|
+
describe '#balance' do
|
9
|
+
it 'returns remaining balance' do
|
10
|
+
stub_post('balance.php').to_return(xml_response('balance.xml'))
|
11
|
+
expect(client.balance).to eq(10.0)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#remaining_sms' do
|
16
|
+
it 'returns remaining sms count' do
|
17
|
+
stub_post('balance.php').to_return(xml_response('balance.xml'))
|
18
|
+
expect(client.remaining_sms).to eq(20)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Smslist::Client::Sms do
|
6
|
+
|
7
|
+
describe '#send_sms' do
|
8
|
+
context 'sender is set' do
|
9
|
+
let(:client) { Smslist::Client.new(:token => 'secret', :sender => '4000') }
|
10
|
+
let(:recipients) { %w(79031234567 79032345678 79033456789 79034567890) }
|
11
|
+
|
12
|
+
before(:each) { stub_post('').to_return(xml_response('sms.xml')) }
|
13
|
+
|
14
|
+
it 'returns a Hash' do
|
15
|
+
expect(client.send_sms('Hello everyone', recipients)).to be_a(Hash)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'includes a Hash for sent messages, with status, id and parts count' do
|
19
|
+
expect(client.send_sms('Hello everyone', recipients, :flash => true))
|
20
|
+
.to include('79031234567' => { :status => :ok, :id => 1001, :parts => 2 })
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'includes a Hash for not sent messages' do
|
24
|
+
expect(client.send_sms('Hello everyone', recipients))
|
25
|
+
.to include('79034567890' => { :error => 'Номер телефона присутствует в стоп-листе.' })
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'sender is not set' do
|
30
|
+
let(:client) { Smslist::Client.new(:token => 'secret') }
|
31
|
+
let(:recipients) { %w(79031234567) }
|
32
|
+
|
33
|
+
it 'raises NoSenderError' do
|
34
|
+
expect {
|
35
|
+
client.send_sms('Hello everyone', recipients)
|
36
|
+
}.to raise_error(Smslist::NoSenderError)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Smslist::Client::State do
|
6
|
+
|
7
|
+
describe '#state' do
|
8
|
+
let(:client) { Smslist::Client.new(:token => 'secret') }
|
9
|
+
let(:message_ids) { %w(1001 1002 1003 1004) }
|
10
|
+
|
11
|
+
before(:each) { stub_post('state.php').to_return(xml_response('state.xml')) }
|
12
|
+
|
13
|
+
it 'returns a Hash' do
|
14
|
+
expect(client.state(message_ids)).to be_a(Hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'includes a Hash for delivered messages, with state' do
|
18
|
+
expect(client.state(message_ids)['1001'][:state]).to eql('deliver')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'includes a Hash for delivered messages, with delivered datetime' do
|
22
|
+
datetime = DateTime.strptime('2011-01-01 12:57:46', '%Y-%m-%d %H:%M:%S')
|
23
|
+
expect(client.state(message_ids)['1001'][:datetime]).to be_within(1).of(datetime)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'includes a Hash for not delivered messages' do
|
27
|
+
expect(client.state(message_ids))
|
28
|
+
.to include('1004' => { :state => 'not_deliver',
|
29
|
+
:error => 'The subscriber is absent or out of a coverage' })
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Smslist::Client do
|
4
|
+
|
5
|
+
describe 'api endpoint' do
|
6
|
+
it 'defaults to https://my.smscell.ru/xml' do
|
7
|
+
client = Smslist::Client.new
|
8
|
+
expect(client.api_endpoint).to eq('https://my.smscell.ru/xml/')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context 'Authentication module' do
|
13
|
+
describe '#authentication' do
|
14
|
+
it 'hash with a token, if token, login and password are set' do
|
15
|
+
client = Smslist::Client.new(
|
16
|
+
:login => 'username',
|
17
|
+
:password => 'secret',
|
18
|
+
:token => 'api_token'
|
19
|
+
)
|
20
|
+
expect(client.authentication).to eql({ :token => 'api_token' })
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'hash with login and password, if login and password are set' do
|
24
|
+
client = Smslist::Client.new(
|
25
|
+
:login => 'username',
|
26
|
+
:password => 'secret'
|
27
|
+
)
|
28
|
+
expect(client.authentication).to eql({ :login => 'username', :password => 'secret' })
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'raises Smslist::UnauthorizedError, if credentials are not set' do
|
32
|
+
client = Smslist::Client.new
|
33
|
+
expect {
|
34
|
+
client.authentication
|
35
|
+
}.to raise_error(Smslist::UnauthorizedError)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'wrong credentials' do
|
41
|
+
let(:client) { Smslist::Client.new(:token => 'secret') }
|
42
|
+
before(:each) do
|
43
|
+
stub_post('balance.php').to_return(xml_response('wrong_credentials.xml'))
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'raises Error' do
|
47
|
+
expect {
|
48
|
+
client.balance
|
49
|
+
}.to raise_error(Smslist::Error)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'Request module' do
|
54
|
+
let(:client) { Smslist::Client.new }
|
55
|
+
|
56
|
+
describe '#headers' do
|
57
|
+
let(:headers) { client.send(:headers) }
|
58
|
+
it 'Content-type is set to text/xml, charset is utf-8' do
|
59
|
+
expect(headers).to include('Content-type' => 'text/xml; charset=utf-8')
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'User-Agent is set' do
|
63
|
+
expect(headers).to include('User-Agent' => "Smslist Ruby Gem #{Smslist::VERSION}")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#request_uri' do
|
68
|
+
it 'returns API endpoint if method is nil' do
|
69
|
+
expect(client.send(:request_uri)).to eql('https://my.smscell.ru/xml/')
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'returns API endpoint if the method does not exist' do
|
73
|
+
uri = client.send(:request_uri, :wrong_method)
|
74
|
+
expect(uri).to eql('https://my.smscell.ru/xml/')
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'returns full url to php script if the method exists' do
|
78
|
+
uri = client.send(:request_uri, :balance)
|
79
|
+
expect(uri).to eql('https://my.smscell.ru/xml/balance.php')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Smslist do
|
4
|
+
|
5
|
+
describe '.new' do
|
6
|
+
it 'is a Smslist::Client' do
|
7
|
+
expect(Smslist.new).to be_a Smslist::Client
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe '.respond_to?' do
|
12
|
+
it 'is true if method exists' do
|
13
|
+
expect(Smslist.respond_to?(:new, true)).to eq(true)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'coveralls'
|
2
|
+
Coveralls.wear!
|
3
|
+
|
4
|
+
require 'smslist'
|
5
|
+
require 'rspec'
|
6
|
+
require 'webmock/rspec'
|
7
|
+
|
8
|
+
RSpec.configure do |config|
|
9
|
+
config.expect_with :rspec do |c|
|
10
|
+
c.syntax = :expect
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def stub_post(path)
|
15
|
+
WebMock::stub_request(:post, smslist_url(path))
|
16
|
+
end
|
17
|
+
|
18
|
+
def fixture_path
|
19
|
+
File.expand_path("../fixtures", __FILE__)
|
20
|
+
end
|
21
|
+
|
22
|
+
def fixture(file)
|
23
|
+
File.new(fixture_path + '/' + file)
|
24
|
+
end
|
25
|
+
|
26
|
+
def xml_response(file)
|
27
|
+
{
|
28
|
+
:body => fixture(file),
|
29
|
+
:headers => {
|
30
|
+
:content_type => 'text/xml; charset=utf-8'
|
31
|
+
}
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
def smslist_url(url)
|
36
|
+
if url =~ /^http/
|
37
|
+
url
|
38
|
+
else
|
39
|
+
"#{Smslist::Configuration::DEFAULT_API_ENDPOINT}#{url}"
|
40
|
+
end
|
41
|
+
end
|
metadata
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: smslist
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Yury Lebedev
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-04-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: httparty
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.10'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.10'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: nokogiri
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '1.5'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '1.5'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: bundler
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '1.0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '2.0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '2.0'
|
78
|
+
description: Now you can send sms messages to your users.
|
79
|
+
email:
|
80
|
+
- lebedev.yurii@gmail.com
|
81
|
+
executables: []
|
82
|
+
extensions: []
|
83
|
+
extra_rdoc_files: []
|
84
|
+
files:
|
85
|
+
- LICENSE.md
|
86
|
+
- Rakefile
|
87
|
+
- README.md
|
88
|
+
- lib/smslist/authentication.rb
|
89
|
+
- lib/smslist/client/balance.rb
|
90
|
+
- lib/smslist/client/sms.rb
|
91
|
+
- lib/smslist/client/state.rb
|
92
|
+
- lib/smslist/client.rb
|
93
|
+
- lib/smslist/configuration.rb
|
94
|
+
- lib/smslist/error.rb
|
95
|
+
- lib/smslist/request.rb
|
96
|
+
- lib/smslist/response.rb
|
97
|
+
- lib/smslist/version.rb
|
98
|
+
- lib/smslist.rb
|
99
|
+
- spec/fixtures/balance.xml
|
100
|
+
- spec/fixtures/sms.xml
|
101
|
+
- spec/fixtures/state.xml
|
102
|
+
- spec/fixtures/wrong_credentials.xml
|
103
|
+
- spec/smslist/client/balance_spec.rb
|
104
|
+
- spec/smslist/client/sms_spec.rb
|
105
|
+
- spec/smslist/client/state_spec.rb
|
106
|
+
- spec/smslist/client_spec.rb
|
107
|
+
- spec/smslist_spec.rb
|
108
|
+
- spec/spec_helper.rb
|
109
|
+
homepage: https://github.com/lebedev-yury/smslist
|
110
|
+
licenses:
|
111
|
+
- MIT
|
112
|
+
post_install_message:
|
113
|
+
rdoc_options: []
|
114
|
+
require_paths:
|
115
|
+
- lib
|
116
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
117
|
+
none: false
|
118
|
+
requirements:
|
119
|
+
- - ! '>='
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
none: false
|
124
|
+
requirements:
|
125
|
+
- - ! '>='
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: 1.3.6
|
128
|
+
requirements: []
|
129
|
+
rubyforge_project:
|
130
|
+
rubygems_version: 1.8.23
|
131
|
+
signing_key:
|
132
|
+
specification_version: 3
|
133
|
+
summary: Ruby wrapper for smslist API
|
134
|
+
test_files: []
|
135
|
+
has_rdoc:
|