smslist 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://secure.travis-ci.org/lebedev-yury/smslist.png?branch=master)][travis]
|
4
|
+
[![Dependency Status](https://gemnasium.com/lebedev-yury/smslist.png?travis)][gemnasium]
|
5
|
+
[![Code Climate](https://codeclimate.com/github/lebedev-yury/smslist.png)][codeclimate]
|
6
|
+
[![Coverage Status](https://coveralls.io/repos/lebedev-yury/smslist/badge.png?branch=master)][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:
|