paycall-sms 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 +15 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +84 -0
- data/LICENSE.txt +20 -0
- data/README.md +2 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/lib/pay_call_sms/delivery_notification_parser.rb +66 -0
- data/lib/pay_call_sms/gateway_error.rb +9 -0
- data/lib/pay_call_sms/incoming_message_parser.rb +65 -0
- data/lib/pay_call_sms/phone_number_utils.rb +58 -0
- data/lib/pay_call_sms/sms_sender.rb +85 -0
- data/lib/paycall-sms.rb +9 -0
- data/paycall-sms.gemspec +94 -0
- data/spec/pay_call_sms/delivery_notifications_parser_spec.rb +41 -0
- data/spec/pay_call_sms/incoming_message_parser_spec.rb +29 -0
- data/spec/pay_call_sms/phone_number_utils_spec.rb +37 -0
- data/spec/pay_call_sms/sms_sender_spec.rb +90 -0
- data/spec/spec_helper.rb +15 -0
- metadata +245 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YWEzOWVhNDhhNDRlMmFiYWRlMzU4MDg3NzY5NjUyYTJlZDNkZDRmMA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NzMwYmQyN2QxNGExMTI5ZmJhZTdhZDVmMjcyYjEwYzc0ZTdjYWVkMA==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MTA3NTgxMzI1YzM0MDhhZGZlYzE3NTI2YjM5OGEwOGY3MTViNTUwZDI0MmRh
|
10
|
+
NWVlZDQ0YzU1ZGIzYzRiYjY4NTA1MDA1MWFhNTM4NmMwNjc5ZGJmYmUzYTA2
|
11
|
+
ZmRiM2IyNjQ1ZTdmNmU4OWMwMGU2MTI2Y2YxY2JmNjBlZTgzYmQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
Y2JlM2MxM2ZlODE3M2E5YTI1NDE5NTQ0MTM4Y2IyYzY5MmUwOGM3YmY1MjRl
|
14
|
+
ZDlkY2RlNTc3YTM5MjIwMTVmMDRhYzhlYWY2MmVjMTNhZDJjMDYzNDczMjc4
|
15
|
+
ODEyNDRlNzU1MGZkNGE1ODI1NTZjM2UxMjEzNzdmZTZkYjhlMzU=
|
data/Gemfile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem 'httparty', '~> 0.10.2'
|
4
|
+
gem 'builder', '~> 2.1'
|
5
|
+
gem 'nokogiri', '~> 1.5.10'
|
6
|
+
gem 'uuidtools', '~> 2.1'
|
7
|
+
gem 'logging', '~> 1.7'
|
8
|
+
gem 'activesupport', '~> 3.0.9'
|
9
|
+
gem 'tzinfo', '~> 0.3' # for timezones support
|
10
|
+
gem 'i18n', '~> 0.5'
|
11
|
+
|
12
|
+
# Add dependencies to develop your gem here.
|
13
|
+
# Include everything needed to run rake, tests, features, etc.
|
14
|
+
group :development do
|
15
|
+
gem "rspec", "2.7.0"
|
16
|
+
gem "webmock"
|
17
|
+
gem "rdoc", "~> 3.12"
|
18
|
+
gem "bundler"
|
19
|
+
gem "jeweler", "~> 1.8.8"
|
20
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (3.0.9)
|
5
|
+
addressable (2.3.2)
|
6
|
+
builder (2.1.2)
|
7
|
+
crack (0.3.1)
|
8
|
+
diff-lcs (1.1.3)
|
9
|
+
faraday (0.8.9)
|
10
|
+
multipart-post (~> 1.2.0)
|
11
|
+
git (1.2.9.1)
|
12
|
+
github_api (0.10.1)
|
13
|
+
addressable
|
14
|
+
faraday (~> 0.8.1)
|
15
|
+
hashie (>= 1.2)
|
16
|
+
multi_json (~> 1.4)
|
17
|
+
nokogiri (~> 1.5.2)
|
18
|
+
oauth2
|
19
|
+
hashie (3.4.2)
|
20
|
+
highline (1.6.20)
|
21
|
+
httparty (0.10.2)
|
22
|
+
multi_json (~> 1.0)
|
23
|
+
multi_xml (>= 0.5.2)
|
24
|
+
i18n (0.6.1)
|
25
|
+
jeweler (1.8.8)
|
26
|
+
builder
|
27
|
+
bundler (~> 1.0)
|
28
|
+
git (>= 1.2.5)
|
29
|
+
github_api (= 0.10.1)
|
30
|
+
highline (>= 1.6.15)
|
31
|
+
nokogiri (= 1.5.10)
|
32
|
+
rake
|
33
|
+
rdoc
|
34
|
+
json (1.7.5)
|
35
|
+
jwt (1.5.0)
|
36
|
+
little-plugger (1.1.3)
|
37
|
+
logging (1.8.1)
|
38
|
+
little-plugger (>= 1.1.3)
|
39
|
+
multi_json (>= 1.3.6)
|
40
|
+
multi_json (1.12.1)
|
41
|
+
multi_xml (0.6.0)
|
42
|
+
multipart-post (1.2.0)
|
43
|
+
nokogiri (1.5.10)
|
44
|
+
oauth2 (1.0.0)
|
45
|
+
faraday (>= 0.8, < 0.10)
|
46
|
+
jwt (~> 1.0)
|
47
|
+
multi_json (~> 1.3)
|
48
|
+
multi_xml (~> 0.5)
|
49
|
+
rack (~> 1.2)
|
50
|
+
rack (1.4.1)
|
51
|
+
rake (10.4.2)
|
52
|
+
rdoc (3.12)
|
53
|
+
json (~> 1.4)
|
54
|
+
rspec (2.7.0)
|
55
|
+
rspec-core (~> 2.7.0)
|
56
|
+
rspec-expectations (~> 2.7.0)
|
57
|
+
rspec-mocks (~> 2.7.0)
|
58
|
+
rspec-core (2.7.1)
|
59
|
+
rspec-expectations (2.7.0)
|
60
|
+
diff-lcs (~> 1.1.2)
|
61
|
+
rspec-mocks (2.7.0)
|
62
|
+
tzinfo (0.3.39)
|
63
|
+
uuidtools (2.1.3)
|
64
|
+
webmock (1.8.10)
|
65
|
+
addressable (>= 2.2.7)
|
66
|
+
crack (>= 0.1.7)
|
67
|
+
|
68
|
+
PLATFORMS
|
69
|
+
ruby
|
70
|
+
|
71
|
+
DEPENDENCIES
|
72
|
+
activesupport (~> 3.0.9)
|
73
|
+
builder (~> 2.1)
|
74
|
+
bundler
|
75
|
+
httparty (~> 0.10.2)
|
76
|
+
i18n (~> 0.5)
|
77
|
+
jeweler (~> 1.8.8)
|
78
|
+
logging (~> 1.7)
|
79
|
+
nokogiri (~> 1.5.10)
|
80
|
+
rdoc (~> 3.12)
|
81
|
+
rspec (= 2.7.0)
|
82
|
+
tzinfo (~> 0.3)
|
83
|
+
uuidtools (~> 2.1)
|
84
|
+
webmock
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Alex
|
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
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "paycall-sms"
|
18
|
+
gem.homepage = "http://github.com/iplan/paycall-sms"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Ruby api for sms service provider: PayCall}
|
21
|
+
gem.description = %Q{Ruby api for sms service provider: PayCall}
|
22
|
+
gem.email = "tkachev.alex@gmail.com"
|
23
|
+
gem.authors = ["Alex Tkachev"]
|
24
|
+
|
25
|
+
gem.files.exclude 'api_docs/**/*'
|
26
|
+
# dependencies defined in Gemfile
|
27
|
+
end
|
28
|
+
Jeweler::RubygemsDotOrgTasks.new
|
29
|
+
|
30
|
+
require 'rspec/core'
|
31
|
+
require 'rspec/core/rake_task'
|
32
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
33
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
34
|
+
end
|
35
|
+
|
36
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
37
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
38
|
+
spec.rcov = true
|
39
|
+
end
|
40
|
+
|
41
|
+
task :default => :spec
|
42
|
+
|
43
|
+
require 'rdoc/task'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
46
|
+
|
47
|
+
rdoc.rdoc_dir = 'rdoc'
|
48
|
+
rdoc.title = "paycall-sms #{version}"
|
49
|
+
rdoc.rdoc_files.include('README*')
|
50
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
51
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module PayCallSms
|
2
|
+
class DeliveryNotificationParser
|
3
|
+
attr_reader :logger
|
4
|
+
|
5
|
+
# Create new sms delivery notification parser
|
6
|
+
# options can contain the following keys:
|
7
|
+
# time_zone: the timezone through which the notification date would be parsed
|
8
|
+
def initialize(options = {})
|
9
|
+
@options = options
|
10
|
+
@logger = Logging.logger[self.class]
|
11
|
+
end
|
12
|
+
|
13
|
+
# params will look something like the following:
|
14
|
+
# {"PhoneNumber"=>"972545290862", "CustomerMessageId"=>"34", "Status"=>"inprogress", "dateTime"=>"20-11-2017 17:22:55"}
|
15
|
+
def from_http_push_params(params)
|
16
|
+
%w(PhoneNumber Status CustomerMessageId dateTime).each do |p|
|
17
|
+
raise ArgumentError.new("Missing http delivery notification push parameter #{p}. Parameters were: #{params.inspect}") if params[p].blank?
|
18
|
+
end
|
19
|
+
logger.debug "Parsing http push delivery notification params: #{params.inspect}"
|
20
|
+
|
21
|
+
values = {
|
22
|
+
:gateway_status => params['Status'],
|
23
|
+
:phone => params['PhoneNumber'],
|
24
|
+
:message_id => params['CustomerMessageId'],
|
25
|
+
:occurred_at => params['dateTime'],
|
26
|
+
}
|
27
|
+
|
28
|
+
parse_notification_values_hash(values)
|
29
|
+
end
|
30
|
+
|
31
|
+
# This method receives notification +values+ Hash and tries to type cast it's values and determine delivery status (add delivered?)
|
32
|
+
# @raises Smsim::GatewayError when values hash is missing attributes or when one of the attributes fails to be parsed
|
33
|
+
#
|
34
|
+
# Method returns object with the following attributes:
|
35
|
+
# * +gateway_status+ - gateway status: [inprogress,delivered]
|
36
|
+
# * +delivery_status+ - :delivered, :in_progress, :failed, :unknown
|
37
|
+
# * +occurred_at+ - when the sms became in gateway_status (as reported by gateway)
|
38
|
+
# * +phone+ - the phone to which sms was sent
|
39
|
+
# * +message_id+ - gateway message id of the sms that was sent
|
40
|
+
def parse_notification_values_hash(values)
|
41
|
+
logger.debug "Parsing delivery notification values hash: #{values.inspect}"
|
42
|
+
[:gateway_status, :phone, :message_id, :occurred_at].each do |key|
|
43
|
+
raise ArgumentError.new("Missing notification values key #{key}. Values were: #{values.inspect}") if values[key].blank?
|
44
|
+
end
|
45
|
+
|
46
|
+
values[:phone] = PhoneNumberUtils.ensure_country_code(values[:phone])
|
47
|
+
values[:delivery_status] = self.class.gateway_delivery_status_to_delivery_status(values[:gateway_status])
|
48
|
+
|
49
|
+
begin
|
50
|
+
Time.use_zone(@options[:time_zone] || Time.zone || 'Jerusalem') do
|
51
|
+
values[:occurred_at] = DateTime.strptime(values[:occurred_at], '%d-%m-%Y %H:%M:%S')
|
52
|
+
values[:occurred_at] = Time.zone.parse(values[:occurred_at].strftime('%d-%m-%Y %H:%M:%S')) #convert to ActiveSupport::TimeWithZone
|
53
|
+
end
|
54
|
+
rescue Exception => e
|
55
|
+
logger.error "occurred_at could not be converted to integer. occurred_at was: #{values[:occurred_at]}. \n\t #{e.message}: \n\t #{e.backtrace.join("\n\t")}"
|
56
|
+
raise ArgumentError.new("occurred_at could not be converted to date. occurred_at was: #{values[:occurred_at]}")
|
57
|
+
end
|
58
|
+
OpenStruct.new(values)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.gateway_delivery_status_to_delivery_status(gateway_status)
|
62
|
+
{inprogress: :in_progress, delivered: :delivered, failed: :failed}.with_indifferent_access[gateway_status] || :unknown
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module PayCallSms
|
2
|
+
class IncomingMessageParser
|
3
|
+
attr_reader :logger
|
4
|
+
|
5
|
+
# Create new sms sender with given +gateway+
|
6
|
+
def initialize(options={})
|
7
|
+
@options = options
|
8
|
+
@logger = Logging.logger[self.class]
|
9
|
+
end
|
10
|
+
|
11
|
+
# params will look something like the following:
|
12
|
+
# msgId - uniq id of the message
|
13
|
+
# sender - the phone that have sent the message
|
14
|
+
# recipient - the virtual phone number that received the message (at gateway operator)
|
15
|
+
# segments - number of segments
|
16
|
+
# content - text of the message
|
17
|
+
def from_http_push_params(params)
|
18
|
+
%w(msgId sender recipient content).each do |p|
|
19
|
+
raise ArgumentError.new("Missing http parameter #{p}. Parameters were: #{params.inspect}") if params[p].blank?
|
20
|
+
end
|
21
|
+
|
22
|
+
logger.debug "Parsing http push reply xml: \n#{params['IncomingXML']}"
|
23
|
+
parse_reply_values_hash(
|
24
|
+
phone: params['sender'],
|
25
|
+
reply_to_phone: params['recipient'],
|
26
|
+
text: params['content'],
|
27
|
+
message_id: params['msgId']
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
# This method receives sms reply +values+ Hash and tries to type cast it's values
|
32
|
+
# @raises Smsim::GatewayError when values hash is missing attributes or when one of attributes fails to be type casted
|
33
|
+
#
|
34
|
+
# Method returns object with the following attributes:
|
35
|
+
# * +phone+ - the phone that sent the sms (from which sms reply was received)
|
36
|
+
# * +text+ - contents of the message that were received
|
37
|
+
# * +reply_to_phone+ - the phone to sms which reply was sent (gateway phone number)
|
38
|
+
# * +received_at+ - when the sms was received (as reported by gateway server)
|
39
|
+
# * +message_id+ - uniq message id generated from phone,reply_to_phone and received_at timestamp
|
40
|
+
def parse_reply_values_hash(values)
|
41
|
+
logger.debug "Parsing reply_values_hash: #{values.inspect}"
|
42
|
+
[:message_id, :phone, :text, :reply_to_phone].each do |key|
|
43
|
+
raise ArgumentError.new("Missing sms reply values key #{key}. Values were: #{values.inspect}") if values[key].blank?
|
44
|
+
end
|
45
|
+
|
46
|
+
values[:phone] = PhoneNumberUtils.ensure_country_code(values[:phone])
|
47
|
+
values[:reply_to_phone] = PhoneNumberUtils.ensure_country_code(values[:reply_to_phone])
|
48
|
+
|
49
|
+
if values[:received_at].is_a?(String)
|
50
|
+
begin
|
51
|
+
Time.use_zone(@options[:time_zone] || Time.zone || 'Jerusalem') do
|
52
|
+
values[:received_at] = DateTime.strptime(values[:received_at], '%Y-%m-%d %H:%M:%S')
|
53
|
+
values[:received_at] = Time.zone.parse(values[:received_at].strftime('%Y-%m-%d %H:%M:%S')) #convert to ActiveSupport::TimeWithZone
|
54
|
+
end
|
55
|
+
rescue Exception => e
|
56
|
+
raise ArgumentError.new("received_at could not be converted to date. received_at was: #{values[:received_at]}")
|
57
|
+
end
|
58
|
+
else
|
59
|
+
values[:received_at] = Time.now
|
60
|
+
end
|
61
|
+
OpenStruct.new(values)
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module PayCallSms
|
2
|
+
|
3
|
+
class PhoneNumberUtils
|
4
|
+
@@valid_lengths = {
|
5
|
+
:cellular => '545123456'.length,
|
6
|
+
:land_line => ['31235678'.length, '777078406'.length]
|
7
|
+
}
|
8
|
+
@@country_code = '972'
|
9
|
+
|
10
|
+
# this method adds 972 country code to given phone if needed
|
11
|
+
# if phone is blank --> doesn't change it
|
12
|
+
def self.ensure_country_code(phone)
|
13
|
+
if !phone.blank? && !phone.start_with?(@@country_code)
|
14
|
+
phone = phone[1..phone.size] if phone.start_with?('0')
|
15
|
+
phone = "972#{phone}"
|
16
|
+
end
|
17
|
+
phone
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.without_country_code(phone)
|
21
|
+
phone.start_with?('972') ? phone.gsub('972', '0') : phone
|
22
|
+
end
|
23
|
+
|
24
|
+
# validates that given phone is Israeli cellular format with country code: 972545123456
|
25
|
+
def self.valid_cellular_phone?(phone)
|
26
|
+
valid_phone_length?(phone, @@valid_lengths[:cellular])
|
27
|
+
end
|
28
|
+
|
29
|
+
# validates that given phone is Israeli landline format with country code: 972545123456
|
30
|
+
def self.valid_land_line_phone?(phone)
|
31
|
+
valid_phone_length?(phone, @@valid_lengths[:land_line].first) || valid_phone_length?(phone, @@valid_lengths[:land_line].last)
|
32
|
+
end
|
33
|
+
|
34
|
+
# valid sender number is between 4 and 14 digits
|
35
|
+
def self.valid_sender_number?(sender_number)
|
36
|
+
sender_number =~ /^[0-9]{4,14}$/i
|
37
|
+
end
|
38
|
+
|
39
|
+
# valid sender name is between 2 and 11 latin chars or digits
|
40
|
+
def self.valid_sender_name?(sender_name)
|
41
|
+
sender_name =~ /^[a-z0-9]{2,11}$/i
|
42
|
+
end
|
43
|
+
|
44
|
+
# make sure phone is in given length and starts with country code
|
45
|
+
def self.valid_phone_length?(phone, length)
|
46
|
+
phone = phone.to_s
|
47
|
+
phone.start_with?(@@country_code) && phone =~ /^#{@@country_code}[0-9]{#{length}}$/
|
48
|
+
end
|
49
|
+
|
50
|
+
# this method will convert given phone number to base 36 string if phone contains digits only
|
51
|
+
# if phone contains digits and letters it will leave it untouched
|
52
|
+
def self.phone_number_to_id_string(phone)
|
53
|
+
phone = phone.to_i.to_s(36) if phone =~ /^[0-9]+$/
|
54
|
+
phone
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'httparty'
|
2
|
+
require 'logging'
|
3
|
+
|
4
|
+
module PayCallSms
|
5
|
+
|
6
|
+
# this class sends smses and parses responses
|
7
|
+
class SmsSender
|
8
|
+
attr_reader :logger
|
9
|
+
|
10
|
+
# Create new sms sender with given +options+
|
11
|
+
# - api_url: endpoint api url to make request to
|
12
|
+
# - username: endpoint auth user
|
13
|
+
# - password: endpoint auth pass
|
14
|
+
def initialize(options = {})
|
15
|
+
@options = {api_url: 'https://api.multisend.co.il/MultiSendAPI/sendsms'}.merge(options)
|
16
|
+
@logger = Logging.logger[self.class]
|
17
|
+
|
18
|
+
%w(api_url username password).each{|key| raise ArgumentError.new("options :#{key} must be present") if @options[key.to_sym].blank? }
|
19
|
+
end
|
20
|
+
|
21
|
+
def api_url
|
22
|
+
@options[:api_url]
|
23
|
+
end
|
24
|
+
|
25
|
+
# send +text+ string to the +phones+ array of phone numbers
|
26
|
+
# +options+ - is a hash of optional configuration that can be passed to sms sender:
|
27
|
+
# * +sender_name+ - sender name that will override gateway sender name
|
28
|
+
# * +sender_number+ - sender number that will override gateway sender number
|
29
|
+
# * +delivery_notification_url+ - url which will be invoked upon notification delivery
|
30
|
+
# Returns response OpenStruct that contains:
|
31
|
+
# * +message_id+ - message id string. You must save this id if you want to receive delivery notifications via push/pull
|
32
|
+
def send_sms(message_text, phones, options = {})
|
33
|
+
raise ArgumentError.new("Text must be at least 1 character long") if message_text.blank?
|
34
|
+
raise ArgumentError.new("No phones were given") if phones.blank?
|
35
|
+
raise ArgumentError.new("Either :sender_name or :sender_number attribute required") if options[:sender_name].blank? && options[:sender_number].blank?
|
36
|
+
raise ArgumentError.new("Sender number must be between 4 to 14 digits: #{options[:sender_number]}") if options[:sender_number].present? && !PhoneNumberUtils.valid_sender_number?(options[:sender_number])
|
37
|
+
raise ArgumentError.new("Sender name must be between 2 and 11 latin chars") if options[:sender_name].present? && !PhoneNumberUtils.valid_sender_name?(options[:sender_name])
|
38
|
+
|
39
|
+
phones = [phones] unless phones.is_a?(Array)
|
40
|
+
phones.each do |p| # check that phones are in valid cellular format
|
41
|
+
raise ArgumentError.new("Phone number '#{p}' must be cellular phone with 972 country code") unless PhoneNumberUtils.valid_cellular_phone?(p)
|
42
|
+
end
|
43
|
+
|
44
|
+
message_id = UUIDTools::UUID.timestamp_create.to_str
|
45
|
+
body_params = build_send_sms_params(message_text, phones, message_id, options)
|
46
|
+
logger.debug "#send_sms - making post to #{@options[:api_url]} with params: \n #{body_params}"
|
47
|
+
http_response = HTTParty.post(@options[:api_url], :body => body_params, :headers => {'Accept' => 'application/json'})
|
48
|
+
logger.debug "#send_sms - got http response: code=#{http_response.code}; body=\n#{http_response.parsed_response}"
|
49
|
+
raise StandardError.new("Non 200 http response code: #{http_response.code} \n #{http_response.parsed_response}") if http_response.code != 200
|
50
|
+
if http_response.parsed_response.is_a?(Hash)
|
51
|
+
json = http_response.parsed_response
|
52
|
+
elsif http_response.parsed_response.is_a?(String)
|
53
|
+
begin
|
54
|
+
json = JSON.parse(http_response.parsed_response)
|
55
|
+
rescue JSON::ParserError => e
|
56
|
+
raise PayCallSms::GatewayError.new("Failed to parse response to json: #{http_response.parsed_response}")
|
57
|
+
end
|
58
|
+
logger.debug "#send_sms - parsed response: #{json.inspect}"
|
59
|
+
end
|
60
|
+
if json['success'] == true
|
61
|
+
OpenStruct.new(
|
62
|
+
message_id: message_id,
|
63
|
+
)
|
64
|
+
else
|
65
|
+
raise PayCallSms::GatewayError.new("Failed to send sms: #{json.inspect}")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def build_send_sms_params(message_text, phones, message_id, options = {})
|
70
|
+
result = {
|
71
|
+
user: @options[:username],
|
72
|
+
password: @options[:password],
|
73
|
+
recipient: phones.join(','),
|
74
|
+
message: message_text,
|
75
|
+
customermessageid: message_id
|
76
|
+
}
|
77
|
+
result[:deliverynotificationURL] = options[:delivery_notification_url] if options[:delivery_notification_url].present?
|
78
|
+
result[:from] = options[:sender_number]
|
79
|
+
result[:from] = options[:sender_name] if options[:sender_name].present?
|
80
|
+
result
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
data/lib/paycall-sms.rb
ADDED
data/paycall-sms.gemspec
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: paycall-sms 0.1.0 ruby lib
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "paycall-sms"
|
9
|
+
s.version = "0.1.0"
|
10
|
+
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib"]
|
13
|
+
s.authors = ["Alex Tkachev"]
|
14
|
+
s.date = "2017-11-24"
|
15
|
+
s.description = "Ruby api for sms service provider: PayCall"
|
16
|
+
s.email = "tkachev.alex@gmail.com"
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"LICENSE.txt",
|
19
|
+
"README.md"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
"Gemfile",
|
23
|
+
"Gemfile.lock",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"README.md",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"lib/pay_call_sms/delivery_notification_parser.rb",
|
29
|
+
"lib/pay_call_sms/gateway_error.rb",
|
30
|
+
"lib/pay_call_sms/incoming_message_parser.rb",
|
31
|
+
"lib/pay_call_sms/phone_number_utils.rb",
|
32
|
+
"lib/pay_call_sms/sms_sender.rb",
|
33
|
+
"lib/paycall-sms.rb",
|
34
|
+
"paycall-sms.gemspec",
|
35
|
+
"spec/pay_call_sms/delivery_notifications_parser_spec.rb",
|
36
|
+
"spec/pay_call_sms/incoming_message_parser_spec.rb",
|
37
|
+
"spec/pay_call_sms/phone_number_utils_spec.rb",
|
38
|
+
"spec/pay_call_sms/sms_sender_spec.rb",
|
39
|
+
"spec/spec_helper.rb"
|
40
|
+
]
|
41
|
+
s.homepage = "http://github.com/iplan/paycall-sms"
|
42
|
+
s.licenses = ["MIT"]
|
43
|
+
s.rubygems_version = "2.4.6"
|
44
|
+
s.summary = "Ruby api for sms service provider: PayCall"
|
45
|
+
|
46
|
+
if s.respond_to? :specification_version then
|
47
|
+
s.specification_version = 4
|
48
|
+
|
49
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
50
|
+
s.add_runtime_dependency(%q<httparty>, ["~> 0.10.2"])
|
51
|
+
s.add_runtime_dependency(%q<builder>, ["~> 2.1"])
|
52
|
+
s.add_runtime_dependency(%q<nokogiri>, ["~> 1.5.10"])
|
53
|
+
s.add_runtime_dependency(%q<uuidtools>, ["~> 2.1"])
|
54
|
+
s.add_runtime_dependency(%q<logging>, ["~> 1.7"])
|
55
|
+
s.add_runtime_dependency(%q<activesupport>, ["~> 3.0.9"])
|
56
|
+
s.add_runtime_dependency(%q<tzinfo>, ["~> 0.3"])
|
57
|
+
s.add_runtime_dependency(%q<i18n>, ["~> 0.5"])
|
58
|
+
s.add_development_dependency(%q<rspec>, ["= 2.7.0"])
|
59
|
+
s.add_development_dependency(%q<webmock>, [">= 0"])
|
60
|
+
s.add_development_dependency(%q<rdoc>, ["~> 3.12"])
|
61
|
+
s.add_development_dependency(%q<bundler>, [">= 0"])
|
62
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.8.8"])
|
63
|
+
else
|
64
|
+
s.add_dependency(%q<httparty>, ["~> 0.10.2"])
|
65
|
+
s.add_dependency(%q<builder>, ["~> 2.1"])
|
66
|
+
s.add_dependency(%q<nokogiri>, ["~> 1.5.10"])
|
67
|
+
s.add_dependency(%q<uuidtools>, ["~> 2.1"])
|
68
|
+
s.add_dependency(%q<logging>, ["~> 1.7"])
|
69
|
+
s.add_dependency(%q<activesupport>, ["~> 3.0.9"])
|
70
|
+
s.add_dependency(%q<tzinfo>, ["~> 0.3"])
|
71
|
+
s.add_dependency(%q<i18n>, ["~> 0.5"])
|
72
|
+
s.add_dependency(%q<rspec>, ["= 2.7.0"])
|
73
|
+
s.add_dependency(%q<webmock>, [">= 0"])
|
74
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
75
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
76
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.8"])
|
77
|
+
end
|
78
|
+
else
|
79
|
+
s.add_dependency(%q<httparty>, ["~> 0.10.2"])
|
80
|
+
s.add_dependency(%q<builder>, ["~> 2.1"])
|
81
|
+
s.add_dependency(%q<nokogiri>, ["~> 1.5.10"])
|
82
|
+
s.add_dependency(%q<uuidtools>, ["~> 2.1"])
|
83
|
+
s.add_dependency(%q<logging>, ["~> 1.7"])
|
84
|
+
s.add_dependency(%q<activesupport>, ["~> 3.0.9"])
|
85
|
+
s.add_dependency(%q<tzinfo>, ["~> 0.3"])
|
86
|
+
s.add_dependency(%q<i18n>, ["~> 0.5"])
|
87
|
+
s.add_dependency(%q<rspec>, ["= 2.7.0"])
|
88
|
+
s.add_dependency(%q<webmock>, [">= 0"])
|
89
|
+
s.add_dependency(%q<rdoc>, ["~> 3.12"])
|
90
|
+
s.add_dependency(%q<bundler>, [">= 0"])
|
91
|
+
s.add_dependency(%q<jeweler>, ["~> 1.8.8"])
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PayCallSms::DeliveryNotificationParser do
|
4
|
+
let(:parser){ PayCallSms::DeliveryNotificationParser.new }
|
5
|
+
|
6
|
+
describe '#from_http_push_params' do
|
7
|
+
let(:http_params) { {'Status' => 'inprogress', 'CustomerMessageId' => 'a1', 'PhoneNumber' => '0545123456', 'dateTime' => "19-03-2012 23:29:12"} }
|
8
|
+
let(:notification) { parser.from_http_push_params(http_params) }
|
9
|
+
|
10
|
+
it 'should raise GatewayError if parameters are missing or not of expected type' do
|
11
|
+
['PhoneNumber', 'Status', 'CustomerMessageId', 'dateTime'].each do |p|
|
12
|
+
params = http_params.clone
|
13
|
+
params.delete(p)
|
14
|
+
lambda { parser.from_http_push_params(params) }.should raise_error(ArgumentError)
|
15
|
+
end
|
16
|
+
|
17
|
+
lambda { parser.from_http_push_params(http_params.update('dateTime' => 'asdf')) }.should raise_error(ArgumentError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should return DeliveryNotification with all fields initialized' do
|
21
|
+
notification.should be_present
|
22
|
+
notification.message_id.should == 'a1'
|
23
|
+
notification.phone.should == '972545123456'
|
24
|
+
notification.gateway_status.should == 'inprogress'
|
25
|
+
notification.occurred_at.should be_present
|
26
|
+
notification.occurred_at.strftime('%d/%m/%Y %H:%M:%S').should == "19/03/2012 23:29:12"
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should be delivered when status is delivered' do
|
30
|
+
http_params.update('Status' => 'delivered')
|
31
|
+
notification.delivery_status.should == :delivered
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should be not delivered when status is failed' do
|
35
|
+
http_params.update('Status' => 'failed')
|
36
|
+
notification.delivery_status.should == :failed
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PayCallSms::IncomingMessageParser do
|
4
|
+
let(:parser){ PayCallSms::IncomingMessageParser.new }
|
5
|
+
|
6
|
+
describe '#from_http_push_params' do
|
7
|
+
let(:http_params) { {'msgId' => 'a1234', 'sender' => '0541234567', 'recipient' => '972529992090', 'content' => 'kak dila'} }
|
8
|
+
let(:reply) { parser.from_http_push_params(http_params) }
|
9
|
+
|
10
|
+
it 'should raise ArgumentError if parameters are missing or not of expected type' do
|
11
|
+
%w(msgId sender recipient content).each do |p|
|
12
|
+
params = http_params.clone.tap{|h| h.delete(p) }
|
13
|
+
lambda{ parser.from_http_push_params(params) }.should raise_error(ArgumentError)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should return incoming message object with all fields initialized' do
|
18
|
+
Time.stub(:now).and_return(Time.utc(2011, 8, 1, 11, 15, 00))
|
19
|
+
|
20
|
+
reply.should be_present
|
21
|
+
reply.message_id.should == 'a1234'
|
22
|
+
reply.phone.should == '972541234567'
|
23
|
+
reply.text.should == 'kak dila'
|
24
|
+
reply.reply_to_phone.should == '972529992090'
|
25
|
+
reply.received_at.strftime('%d/%m/%Y %H:%M:%S').should == '01/08/2011 11:15:00'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PayCallSms::PhoneNumberUtils do
|
4
|
+
let(:utils) { PayCallSms::PhoneNumberUtils }
|
5
|
+
|
6
|
+
describe '#valid_cellular_phone?' do
|
7
|
+
it 'should not be valid without country code' do
|
8
|
+
utils.valid_cellular_phone?('0545290862').should be_false
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should not be valid for landline phones' do
|
12
|
+
utils.valid_cellular_phone?('035447037').should be_false
|
13
|
+
utils.valid_cellular_phone?('97235447037').should be_false
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should not be valid with country code but of different lenth' do
|
17
|
+
utils.valid_cellular_phone?('9725452908622').should be_false
|
18
|
+
utils.valid_cellular_phone?('97254529086').should be_false
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'should be valid with country code' do
|
22
|
+
utils.valid_cellular_phone?('972545290862').should be_true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#valid_land_line_phone?' do
|
27
|
+
it 'should not be valid without country code' do
|
28
|
+
utils.valid_land_line_phone?('031234567').should be_false
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should be valid with country code' do
|
32
|
+
utils.valid_land_line_phone?('97231234567').should be_true
|
33
|
+
utils.valid_land_line_phone?('972771234567').should be_true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PayCallSms::SmsSender do
|
4
|
+
let(:sender){ ::PayCallSms::SmsSender.new(:username => 'user', :password => 'pass') }
|
5
|
+
|
6
|
+
describe '#send_sms' do
|
7
|
+
let(:message){ 'my message text' }
|
8
|
+
let(:phone){ '972541234567' }
|
9
|
+
let(:api_url){ sender.api_url }
|
10
|
+
|
11
|
+
it 'should raise error if text is blank' do
|
12
|
+
lambda{ sender.send_sms('', phone) }.should raise_error(ArgumentError)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should raise error if phone is blank' do
|
16
|
+
lambda{ sender.send_sms(message, '') }.should raise_error(ArgumentError)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should raise error if phone is not valid cellular phone' do
|
20
|
+
lambda{ sender.send_sms(message, '0541234567') }.should raise_error(ArgumentError)
|
21
|
+
lambda{ sender.send_sms(message, '541234567') }.should raise_error(ArgumentError)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should raise error if url not found' do
|
25
|
+
stub_request(:any, api_url).to_return(:status => 404)
|
26
|
+
lambda{ sender.send_sms('asdf', phone, sender_name: '1234') }.should raise_error(StandardError)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'should raise error url if response code is 200 but json Success is false' do
|
30
|
+
stub_request(:any, api_url).to_return(:status => 200, :body => {success: false}.to_json)
|
31
|
+
lambda{ sender.send_sms('asdf', phone, sender_name: '1234') }.should raise_error(PayCallSms::GatewayError)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should raise error url if response code is 200 and json Success is true' do
|
35
|
+
stub_request(:any, api_url).to_return(:status => 200, :body => {success: true}.to_json)
|
36
|
+
result = sender.send_sms('asdf', phone, sender_name: '1234')
|
37
|
+
result.class.should == OpenStruct
|
38
|
+
result.message_id.should be_present
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'should not raise error url if response code is 200 and but response is not a json' do
|
42
|
+
stub_request(:any, api_url).to_return(:status => 200, :body => 'kljidf,dfdef')
|
43
|
+
lambda{ sender.send_sms('asdf', phone, sender_name: '1234') }.should raise_error(PayCallSms::GatewayError)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#build_send_sms_params' do
|
49
|
+
let(:message){ 'my message text' }
|
50
|
+
let(:phones){ ['0541234567'] }
|
51
|
+
let(:http_params){ sender.build_send_sms_params(message, phones, '123', :sender_number => '972501234567') }
|
52
|
+
|
53
|
+
it 'should have username and password' do
|
54
|
+
http_params[:user].should == 'user'
|
55
|
+
http_params[:password].should == 'pass'
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should have message text' do
|
59
|
+
http_params[:message].should == message
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should have recepients phone number' do
|
63
|
+
http_params[:recipient].should == phones.first
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'should have recepients phone numbers separated by ; without spaces' do
|
67
|
+
phones << '0541234568' << '0541234569'
|
68
|
+
http_params[:recipient].should == phones.join(',')
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should have sender number' do
|
72
|
+
http_params[:from].should == '972501234567'
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'should have message_id' do
|
76
|
+
http_params[:customermessageid].should == '123'
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'should have delivery notification url if specified' do
|
80
|
+
http_params = sender.build_send_sms_params(message, phones, '123', :sender_number => '972501234567', :delivery_notification_url => 'http://google.com?auth=1234&alex=king')
|
81
|
+
http_params[:deliverynotificationURL].should == "http://google.com?auth=1234&alex=king"
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should not have delivery notification url if not specified' do
|
85
|
+
http_params[:deliverynotificationURL].should be_nil
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'paycall-sms'
|
5
|
+
require 'nokogiri'
|
6
|
+
require 'ostruct'
|
7
|
+
require 'webmock/rspec'
|
8
|
+
|
9
|
+
# Requires supporting files with custom matchers and macros, etc,
|
10
|
+
# in ./support/ and its subdirectories.
|
11
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,245 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: paycall-sms
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alex Tkachev
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-11-24 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: httparty
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.10.2
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.10.2
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: builder
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.1'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.1'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: nokogiri
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 1.5.10
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 1.5.10
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: uuidtools
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.1'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: logging
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.7'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.7'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: activesupport
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 3.0.9
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 3.0.9
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: tzinfo
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ~>
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.3'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ~>
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.3'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: i18n
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.5'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ~>
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.5'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: rspec
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 2.7.0
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 2.7.0
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: webmock
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ! '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ! '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: rdoc
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ~>
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '3.12'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ~>
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '3.12'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: bundler
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ! '>='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: jeweler
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ~>
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: 1.8.8
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - ~>
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: 1.8.8
|
195
|
+
description: ! 'Ruby api for sms service provider: PayCall'
|
196
|
+
email: tkachev.alex@gmail.com
|
197
|
+
executables: []
|
198
|
+
extensions: []
|
199
|
+
extra_rdoc_files:
|
200
|
+
- LICENSE.txt
|
201
|
+
- README.md
|
202
|
+
files:
|
203
|
+
- Gemfile
|
204
|
+
- Gemfile.lock
|
205
|
+
- LICENSE.txt
|
206
|
+
- README.md
|
207
|
+
- Rakefile
|
208
|
+
- VERSION
|
209
|
+
- lib/pay_call_sms/delivery_notification_parser.rb
|
210
|
+
- lib/pay_call_sms/gateway_error.rb
|
211
|
+
- lib/pay_call_sms/incoming_message_parser.rb
|
212
|
+
- lib/pay_call_sms/phone_number_utils.rb
|
213
|
+
- lib/pay_call_sms/sms_sender.rb
|
214
|
+
- lib/paycall-sms.rb
|
215
|
+
- paycall-sms.gemspec
|
216
|
+
- spec/pay_call_sms/delivery_notifications_parser_spec.rb
|
217
|
+
- spec/pay_call_sms/incoming_message_parser_spec.rb
|
218
|
+
- spec/pay_call_sms/phone_number_utils_spec.rb
|
219
|
+
- spec/pay_call_sms/sms_sender_spec.rb
|
220
|
+
- spec/spec_helper.rb
|
221
|
+
homepage: http://github.com/iplan/paycall-sms
|
222
|
+
licenses:
|
223
|
+
- MIT
|
224
|
+
metadata: {}
|
225
|
+
post_install_message:
|
226
|
+
rdoc_options: []
|
227
|
+
require_paths:
|
228
|
+
- lib
|
229
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
230
|
+
requirements:
|
231
|
+
- - ! '>='
|
232
|
+
- !ruby/object:Gem::Version
|
233
|
+
version: '0'
|
234
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
235
|
+
requirements:
|
236
|
+
- - ! '>='
|
237
|
+
- !ruby/object:Gem::Version
|
238
|
+
version: '0'
|
239
|
+
requirements: []
|
240
|
+
rubyforge_project:
|
241
|
+
rubygems_version: 2.4.6
|
242
|
+
signing_key:
|
243
|
+
specification_version: 4
|
244
|
+
summary: ! 'Ruby api for sms service provider: PayCall'
|
245
|
+
test_files: []
|