cell_force 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +97 -0
- data/Rakefile +43 -0
- data/cell_force.gemspec +27 -0
- data/lib/cell_force.rb +6 -0
- data/lib/cell_force/api.rb +82 -0
- data/lib/cell_force/configuration.rb +37 -0
- data/lib/cell_force/default_short_code.rb +17 -0
- data/lib/cell_force/mobile_device.rb +30 -0
- data/lib/cell_force/tcpa_opt_in_campaign.rb +41 -0
- data/lib/cell_force/version.rb +3 -0
- data/spec/api_spec.rb +27 -0
- data/spec/configuration_spec.rb +141 -0
- data/spec/mobile_device_spec.rb +81 -0
- data/spec/secrets.yml.example +5 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/support/configuration_from_yaml.rb +21 -0
- metadata +176 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6fd8af7240d36472b094418d8ef3fd322e3178f5
|
4
|
+
data.tar.gz: e790f1469e248b37b5e40d4ff89bf36aaee875d9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 91037b7369f21e6311686dc279be5204e69d53a49ddaa7cbc0e1d27497763552d53acd6b518b3fb6fa302dba9870d1292db8bfb5b3b1f9dfaf85fc34fd5c1e15
|
7
|
+
data.tar.gz: 0746e22d1759e817fb9b1757f61c2a09267156aeeec44108b5b84fc72cfa5cc3bfbc6c86bd7cb494eb72d0d3fabc08b3ec7bade64bb252c216de28687963499c
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Isaac Betesh
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# CellForce
|
2
|
+
|
3
|
+
We use HTTParty to send SMS messages using the CellForce API. See USAGE below for details.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'cell_force'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
``` bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
``` bash
|
22
|
+
$ gem install cell_force
|
23
|
+
```
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
#### Configuration
|
28
|
+
|
29
|
+
cell_force requires you to provide a username, password and API key. Configure them before attempting to post to any AOI resource.
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
CellForce.configure do |config|
|
33
|
+
config.username = "Me"
|
34
|
+
config.password = "MyPassword"
|
35
|
+
config.api_key = "ReallyLongRandomString1234567789"
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
Since cell_force depends on sms_validation (https://github.com/betesh/sms_validation/), it is also recommended that you configure sms_validation.
|
40
|
+
cell_force uses sms_validation's logger. In a Rails environment, you will probably want to rely on the default configuration,
|
41
|
+
but outside of Rails, you will need to configure it if you want any logging:
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
SmsValidation.configure do |config|
|
45
|
+
config.logger = ::Logger.new(STDOUT)
|
46
|
+
end
|
47
|
+
```
|
48
|
+
|
49
|
+
#### Sending an SMS
|
50
|
+
|
51
|
+
Once configured, cell_force will automatically log you in before the first API call and will store your credentials for all subsequent API calls.
|
52
|
+
cell_force transparently logs in again whenever you session expires (i.e. if you log out or if 1 hours has elapsed since your last API call).
|
53
|
+
|
54
|
+
So once configured, just fire away with API calls, which are done using CellForce::Api#post:
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
api = CellForce::Api
|
58
|
+
api.post("shortcode/getusershortcodes")
|
59
|
+
api.post("shortcode/getshortcodecarriers", shortcode_id: ENV['SHORT_CODE'])
|
60
|
+
```
|
61
|
+
|
62
|
+
But don't use CellForce::Api#post to send an SMS. If you do, you'll bypass sms_validation. Instead, use CellForce::MobileDevice#send_mt, so that everything gets validated.
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
mobile_device = CellForce::MobileDevice.new(my_phone_number)
|
66
|
+
mobile_device.send_mt(my_phone_number, "Hi, How are you?")
|
67
|
+
```
|
68
|
+
sms_validation allows you to configure what to do when a message is too long. cell_force is only designed to work with the default behavior: :raise_error
|
69
|
+
|
70
|
+
If you need the :truncate or :split behavior instead, instantiate an SmsValidation::Sms and iterate through its messages:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
mobile_device = CellForce::MobileDevice.new(my_phone_number)
|
74
|
+
|
75
|
+
SmsValidation::Sms.new(my_phone_number, "Hi, How are you?"*387).messages.each do |message|
|
76
|
+
mobile_device.send_mt(message)
|
77
|
+
end
|
78
|
+
```
|
79
|
+
|
80
|
+
CellForce's documentation does recommend that you log out at the end of your activity
|
81
|
+
```ruby
|
82
|
+
CellForce::Api.log_out
|
83
|
+
```
|
84
|
+
|
85
|
+
## Testing
|
86
|
+
|
87
|
+
Copy spec/secrets.yml.example to spec/secrets.yml and modify it to set appropriate credentails. Then run `rspec`.
|
88
|
+
|
89
|
+
Note that none of the tests set any expectations. If you run them and they all pass, it only verifies that nothing through an exception. So make sure to check your phone after running the tests to make sure you received all the expected text messages.
|
90
|
+
|
91
|
+
## Contributing
|
92
|
+
|
93
|
+
1. Fork it ( https://github.com/[my-github-username]/cell_force/fork )
|
94
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
95
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
96
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
97
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
$LOAD_PATH << File.expand_path("#{File.dirname(__FILE__)}/lib") << File.expand_path(File.dirname(__FILE__))
|
4
|
+
require "cell_force"
|
5
|
+
|
6
|
+
namespace :cell_force do
|
7
|
+
desc "configure CellForce API"
|
8
|
+
task :configure do
|
9
|
+
require "sms_validation"
|
10
|
+
SmsValidation.configure do |config|
|
11
|
+
require "logger"
|
12
|
+
config.logger = ::Logger.new(STDOUT)
|
13
|
+
config.logger.level = Logger::INFO
|
14
|
+
config.log_at :info
|
15
|
+
end
|
16
|
+
|
17
|
+
require "yaml"
|
18
|
+
secrets = YAML::load(File.open('spec/secrets.yml'))
|
19
|
+
CellForce.configure do |config|
|
20
|
+
config.username, config.password, config.api_key = secrets["username"], secrets["password"], secrets["api_key"]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "Reserve a keyword (KEYWORD=XXX rake cell_force:reserve_keyword)"
|
25
|
+
task reserve_keyword: :configure do
|
26
|
+
CellForce::TcpaOptInCampaign.new(ENV['KEYWORD']).keyword_id
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Enable a keyword (KEYWORD=XXX rake cell_force:enable_keyword)"
|
30
|
+
task enable_keyword: :configure do
|
31
|
+
CellForce::Api.post("keyword/enable", row_id: CellForce::TcpaOptInCampaign.new(ENV['KEYWORD']).keyword_id)
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "List all keywords associated with the account"
|
35
|
+
task list_keywords: :configure do
|
36
|
+
puts CellForce::Api.post("keyword/list").data.collect { |k| "#{k["keyword"]}#{" (campaign: '#{k["campaign"]}')" unless "N/A" == k["campaign"] }" }.join(", ")
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "Send an MT (PHONE=NNNNNNNNNN rake cell_force:send_mt)"
|
40
|
+
task send_mt: :configure do
|
41
|
+
CellForce::MobileDevice.new(ENV['PHONE']).send_mt("Test message via CellForce. Sent at #{Time.now}")
|
42
|
+
end
|
43
|
+
end
|
data/cell_force.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'cell_force/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cell_force"
|
8
|
+
spec.version = CellForce::VERSION
|
9
|
+
spec.authors = ["Isaac Betesh"]
|
10
|
+
spec.email = ["iybetesh@gmail.com"]
|
11
|
+
spec.description = "Send SMS messages using the CellForce API"
|
12
|
+
spec.summary = `cat README.md`
|
13
|
+
spec.homepage = "https://github.com/betesh/cell_force/"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.7"
|
22
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
|
25
|
+
spec.add_dependency "sms_validation"
|
26
|
+
spec.add_dependency "httparty"
|
27
|
+
end
|
data/lib/cell_force.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require "httparty"
|
2
|
+
require "sms_validation"
|
3
|
+
require "cell_force/configuration"
|
4
|
+
|
5
|
+
module CellForce
|
6
|
+
class Api
|
7
|
+
class Http
|
8
|
+
include HTTParty
|
9
|
+
base_uri "https://www.mycellforce.com"
|
10
|
+
end
|
11
|
+
|
12
|
+
module Util
|
13
|
+
class << self
|
14
|
+
def filtered(hash)
|
15
|
+
hash.is_a?(Hash) ? hash.reject{ |k,v| PARAMETER_FILTER.include?(k.to_sym) } : hash
|
16
|
+
end
|
17
|
+
|
18
|
+
def hash_to_log(hash)
|
19
|
+
hash.sort_by{ |k,v| k }.collect{|k,v| "\t\t\t#{k}:#{" " * (15 - k.length)}\t#{v}" }.join("\n")
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse_response(response)
|
23
|
+
body = JSON.parse(response.body)
|
24
|
+
SmsValidation.log { "CellForce API: #{response.code}:#{response.message}" }
|
25
|
+
SmsValidation.configuration.logger.debug { "\n\t\tHeaders:\n#{hash_to_log(response.headers)}" }
|
26
|
+
SmsValidation.log { "\n\t\tBody:\n#{hash_to_log(body.inject({}) { |hash, (k,v)| hash[k] = filtered(v); hash })}" }
|
27
|
+
raise Failure, body["error"] if "failure" == body["status"]
|
28
|
+
Struct.new(*body.keys.collect(&:to_sym)).new(*body.values)
|
29
|
+
end
|
30
|
+
|
31
|
+
def convert_sms_args(args)
|
32
|
+
validation = args.delete(:sms_validation)
|
33
|
+
unless validation.is_a?(SmsValidation::Sms)
|
34
|
+
raise StandardError, "You cannot send an SMS by calling #{CellForce::Api}#post directly. Use #{CellForce::Api}#send_sms(phone, message) instead."
|
35
|
+
end
|
36
|
+
args.merge(cellnumber: validation.phone[1..-1], message: validation.message)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Failure < StandardError; end
|
42
|
+
|
43
|
+
LOG_IN_RESOURCE = "users/login"
|
44
|
+
SEND_SMS_RESOURCE = "member/sendsms"
|
45
|
+
PARAMETER_FILTER = [:password, :user_key]
|
46
|
+
|
47
|
+
class << self
|
48
|
+
def post(resource, body={})
|
49
|
+
body = Util.convert_sms_args(body.dup) if SEND_SMS_RESOURCE == resource
|
50
|
+
Util.parse_response(post_with_automatic_login(resource, body))
|
51
|
+
end
|
52
|
+
|
53
|
+
def log_out
|
54
|
+
post("users/logout").tap { @login_data = nil }
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def post_with_automatic_login(resource, body)
|
59
|
+
body = body.merge(login_data) unless LOG_IN_RESOURCE == resource
|
60
|
+
SmsValidation.log { "CellForce API: POST #{resource}#{filtered_body = Util.filtered(body); " -- #{filtered_body.inspect}" unless filtered_body.empty?}" }
|
61
|
+
result = Http.post("/exlapiservice/#{resource}", headers: configuration.headers, body: body )
|
62
|
+
if "The user key is invalid" == JSON.parse(result.body)["error"]
|
63
|
+
@login_data = nil
|
64
|
+
post_with_automatic_login(resource, body)
|
65
|
+
else
|
66
|
+
result
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def configuration
|
71
|
+
CellForce.configuration
|
72
|
+
end
|
73
|
+
|
74
|
+
def login_data
|
75
|
+
@login_data ||= begin
|
76
|
+
SmsValidation.log { "CellForce API: Logged out. Logging in again..." }
|
77
|
+
post(LOG_IN_RESOURCE, configuration.credentials).data
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module CellForce
|
2
|
+
class << self
|
3
|
+
def configuration
|
4
|
+
@configuration ||= Configuration.new
|
5
|
+
end
|
6
|
+
|
7
|
+
def configure
|
8
|
+
yield configuration
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Configuration
|
13
|
+
class Error < ::StandardError; end
|
14
|
+
[:username, :password, :api_key].each do |accessor|
|
15
|
+
define_method(:"#{accessor}=") do |arg|
|
16
|
+
raise Error, "#{accessor} must be a String" unless arg.nil? || arg.is_a?(String)
|
17
|
+
raise Error, "#{accessor} cannot be blank" if arg.nil? || arg.empty?
|
18
|
+
@credentials = @headers = nil
|
19
|
+
instance_variable_set("@#{accessor}", arg)
|
20
|
+
end
|
21
|
+
|
22
|
+
define_method(accessor) do
|
23
|
+
instance_variable_get("@#{accessor}").tap do |result|
|
24
|
+
raise Error, "#{accessor} has not been set. Set it with `CellForce.configuration.#{accessor} = ...`" if result.nil?
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def credentials
|
30
|
+
@credentials ||= { username: username, password: password }
|
31
|
+
end
|
32
|
+
|
33
|
+
def headers
|
34
|
+
@headers ||= { "apikey" => api_key }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "cell_force/api"
|
2
|
+
|
3
|
+
module CellForce
|
4
|
+
module DefaultShortCode
|
5
|
+
def short_code_id
|
6
|
+
@short_code_id ||= begin
|
7
|
+
data = Api.post("shortcode/getusershortcodes").data
|
8
|
+
if short_code
|
9
|
+
data.find{ |e| short_code.to_s == e["shortcode"].to_s }
|
10
|
+
else
|
11
|
+
# If the instance was not initialized with a short_code, we use the first one we find
|
12
|
+
data[0].tap { |s| @short_code = s["shortcode"] }
|
13
|
+
end["shortcode_id"]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "cell_force/default_short_code"
|
2
|
+
require "cell_force/tcpa_opt_in_campaign"
|
3
|
+
|
4
|
+
module CellForce
|
5
|
+
class MobileDevice
|
6
|
+
include DefaultShortCode
|
7
|
+
|
8
|
+
attr_reader :phone, :short_code
|
9
|
+
def initialize(phone, short_code=nil)
|
10
|
+
@phone, @short_code = phone, short_code
|
11
|
+
end
|
12
|
+
|
13
|
+
def send_mt(message)
|
14
|
+
Api.post(Api::SEND_SMS_RESOURCE, sms_validation: SmsValidation::Sms.new(phone, message), shortcode_id: short_code_id).data["mt_id"]
|
15
|
+
end
|
16
|
+
|
17
|
+
def simulate_mo(keyword, campaign_options={})
|
18
|
+
tcpa_opt_in_campaign = TcpaOptInCampaign.new(keyword)
|
19
|
+
if tcpa_opt_in_campaign.campaign_ids.empty?
|
20
|
+
tcpa_opt_in_campaign.create_campaign(campaign_options)
|
21
|
+
end
|
22
|
+
Api.post("sms/mo", cellnumber: phone, message: keyword, shortcode_id: short_code_id, carrier_id: carrier_id, trigger: "DOUBLEOPTIN")
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def carrier_id
|
27
|
+
@carrier_id ||= Api.post("member/networklookup", cellphone: phone).data["id"]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "cell_force/default_short_code"
|
2
|
+
|
3
|
+
module CellForce
|
4
|
+
class TcpaOptInCampaign
|
5
|
+
include DefaultShortCode
|
6
|
+
|
7
|
+
REQUIRED_KEYS_TO_CREATE_CAMPAIGN = [:name, :first_message, :second_message, :error_message]
|
8
|
+
|
9
|
+
attr_reader :keyword, :short_code
|
10
|
+
def initialize(keyword, short_code=nil)
|
11
|
+
@keyword, @short_code = keyword, short_code
|
12
|
+
end
|
13
|
+
|
14
|
+
def keyword_id
|
15
|
+
@keyword_id ||= begin
|
16
|
+
begin
|
17
|
+
Api.post("keyword/create", keyword: keyword, shortcodes: short_code_id, action: "check").data["message"]
|
18
|
+
Api.post("keyword/create", keyword: keyword, shortcodes: short_code_id).data["row_id"]
|
19
|
+
rescue CellForce::Api::Failure => e
|
20
|
+
raise e unless e.to_s == "Keyword is already Exist"
|
21
|
+
if (keyword_record = Api.post("keyword/list").data.find{ |r| keyword == r["keyword"] }) #Intentional assignment operator
|
22
|
+
keyword_record["id"]
|
23
|
+
else
|
24
|
+
raise StandardError, "This keyword is already in use by another consumer of the short_code #{short_code}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def campaign_ids
|
31
|
+
@campaign_ids ||= Api.post("campaign/list").data.collect do |campaign|
|
32
|
+
campaign["campaign_id"] if keyword_id == Api.post("campaign/detail", row_id: campaign["campaign_id"]).data["keyword_id"]
|
33
|
+
end.compact
|
34
|
+
end
|
35
|
+
|
36
|
+
def create_campaign(options)
|
37
|
+
raise ArgumentError, "Some options were missing for creating a campaign: #{REQUIRED_KEYS_TO_CREATE_CAMPAIGN - options.keys}" unless (REQUIRED_KEYS_TO_CREATE_CAMPAIGN - options.keys).empty?
|
38
|
+
Api.post("campaign/tcpaoptin", keyword_id: keyword_id, shortcode_id: short_code_id, optinkeyword: "Y", name: options[:name], message: options[:first_message], doubleoptinmessage: options[:second_message], errormessage: options[:error_message]).data["campaign_id"]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/spec/api_spec.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "cell_force/api"
|
3
|
+
require "support/configuration_from_yaml"
|
4
|
+
|
5
|
+
describe CellForce::Api do
|
6
|
+
include ConfigurationFromYaml
|
7
|
+
|
8
|
+
subject { described_class }
|
9
|
+
|
10
|
+
it "should log_out" do
|
11
|
+
subject.log_out
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "with the wrong API key" do
|
15
|
+
before(:each) do
|
16
|
+
CellForce.configuration.api_key = "12345"
|
17
|
+
end
|
18
|
+
|
19
|
+
after(:each) do
|
20
|
+
CellForce.configuration.api_key = api_key
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should raise an Api::Failure" do
|
24
|
+
expect{subject.post("shortcode/getusershortcodes")}.to raise_error(CellForce::Api::Failure, "Invalid API Key")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "cell_force/configuration"
|
3
|
+
|
4
|
+
describe CellForce::Configuration do
|
5
|
+
subject { CellForce.configuration }
|
6
|
+
|
7
|
+
before(:each) { reset_configuration }
|
8
|
+
|
9
|
+
describe "#username" do
|
10
|
+
it "cannot be nil" do
|
11
|
+
expect{subject.username = nil}.to raise_error(CellForce::Configuration::Error, "username cannot be blank")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "cannot be an empty String" do
|
15
|
+
expect{subject.username = ""}.to raise_error(CellForce::Configuration::Error, "username cannot be blank")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "cannot be a Hash" do
|
19
|
+
expect{subject.username = {}}.to raise_error(CellForce::Configuration::Error, "username must be a String")
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "when password and api_key are already set" do
|
23
|
+
before(:each) do
|
24
|
+
subject.password = "VerySecret"
|
25
|
+
subject.api_key = "LongApiKey"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "must be set before calling #credentials" do
|
29
|
+
expect{subject.credentials}.to raise_error(CellForce::Configuration::Error, "username has not been set. Set it with `CellForce.configuration.username = ...`")
|
30
|
+
end
|
31
|
+
|
32
|
+
it "need not be set before calling #headers" do
|
33
|
+
expect{subject.headers}.not_to raise_error
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#password" do
|
39
|
+
it "cannot be nil" do
|
40
|
+
expect{subject.password = nil}.to raise_error(CellForce::Configuration::Error, "password cannot be blank")
|
41
|
+
end
|
42
|
+
|
43
|
+
it "cannot be an empty String" do
|
44
|
+
expect{subject.password = ""}.to raise_error(CellForce::Configuration::Error, "password cannot be blank")
|
45
|
+
end
|
46
|
+
|
47
|
+
it "cannot be a Hash" do
|
48
|
+
expect{subject.password = {}}.to raise_error(CellForce::Configuration::Error, "password must be a String")
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "when username and api_key are already set" do
|
52
|
+
before(:each) do
|
53
|
+
subject.username = "Placeholder"
|
54
|
+
subject.api_key = "LongApiKey"
|
55
|
+
end
|
56
|
+
|
57
|
+
it "must be set before calling #credentials" do
|
58
|
+
expect{subject.credentials}.to raise_error(CellForce::Configuration::Error, "password has not been set. Set it with `CellForce.configuration.password = ...`")
|
59
|
+
end
|
60
|
+
|
61
|
+
it "need not be set before calling #headers" do
|
62
|
+
expect{subject.headers}.not_to raise_error
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#api_key" do
|
68
|
+
it "cannot be nil" do
|
69
|
+
expect{subject.api_key = nil}.to raise_error(CellForce::Configuration::Error, "api_key cannot be blank")
|
70
|
+
end
|
71
|
+
|
72
|
+
it "cannot be an empty String" do
|
73
|
+
expect{subject.api_key = ""}.to raise_error(CellForce::Configuration::Error, "api_key cannot be blank")
|
74
|
+
end
|
75
|
+
|
76
|
+
it "cannot be a Hash" do
|
77
|
+
expect{subject.api_key = {}}.to raise_error(CellForce::Configuration::Error, "api_key must be a String")
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "when username and password are already set" do
|
81
|
+
before(:each) do
|
82
|
+
subject.username = "Placeholder"
|
83
|
+
subject.password = "VerySecret"
|
84
|
+
end
|
85
|
+
|
86
|
+
it "need not be set before calling #credentials" do
|
87
|
+
expect{subject.credentials}.not_to raise_error
|
88
|
+
end
|
89
|
+
|
90
|
+
it "must be set before calling #headers" do
|
91
|
+
expect{subject.headers}.to raise_error(CellForce::Configuration::Error, "api_key has not been set. Set it with `CellForce.configuration.api_key = ...`")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "#credentials" do
|
97
|
+
let(:username) { "PercyWeasley" }
|
98
|
+
let(:password) { "FredWeasley" }
|
99
|
+
|
100
|
+
let(:new_username) { "GeorgeWeasley" }
|
101
|
+
let(:new_password) { "RonaldWeasley" }
|
102
|
+
|
103
|
+
before(:each) do
|
104
|
+
CellForce.configure do |config|
|
105
|
+
config.username = username
|
106
|
+
config.password = password
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should contain username and password" do
|
111
|
+
expect(subject.credentials).to eq(username: username, password: password)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "should detect changes to username" do
|
115
|
+
expect{subject.username = new_username}.to change{subject.credentials}.from(username: username, password: password).to(username: new_username, password: password)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should detect changes to password" do
|
119
|
+
expect{subject.password = new_password}.to change{subject.credentials}.from(username: username, password: password).to(username: username, password: new_password)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "#headers" do
|
124
|
+
let(:api_key) { "DracoMalfoy" }
|
125
|
+
let(:new_api_key) { "HarryPotter" }
|
126
|
+
|
127
|
+
before(:each) do
|
128
|
+
CellForce.configure do |config|
|
129
|
+
config.api_key = api_key
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should contain api_key" do
|
134
|
+
expect(subject.headers).to eq("apikey" => api_key)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should detect changes to username" do
|
138
|
+
expect{subject.api_key = new_api_key}.to change{subject.headers}.from("apikey" => api_key).to("apikey" => new_api_key)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "cell_force/mobile_device"
|
3
|
+
require "support/configuration_from_yaml"
|
4
|
+
|
5
|
+
describe CellForce::MobileDevice do
|
6
|
+
include ConfigurationFromYaml
|
7
|
+
|
8
|
+
subject { described_class.new(phone) }
|
9
|
+
let(:api) { CellForce::Api }
|
10
|
+
|
11
|
+
it "should send an sms" do
|
12
|
+
subject.send_mt("Test message via CellForce. Sent at #{Time.now}")
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "simulating an MO" do
|
16
|
+
def clean_up
|
17
|
+
keyword_record = api.post("keyword/list").data.find{ |r| keyword == r["keyword"] }
|
18
|
+
if keyword_record
|
19
|
+
api.post("campaign/list").data.each do |campaign|
|
20
|
+
if keyword_record["id"] == api.post("campaign/detail", row_id: campaign["campaign_id"]).data["keyword_id"]
|
21
|
+
api.post("campaign/delete", row_id: campaign["campaign_id"])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
api.post("keyword/delete", row_id: keyword_record["id"])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
let(:keyword) { "CDF" }
|
29
|
+
let(:keyword_id) do
|
30
|
+
api.post("keyword/create", keyword: keyword, shortcodes: subject.send(:short_code_id)).data["row_id"]
|
31
|
+
end
|
32
|
+
|
33
|
+
let(:campaign_id) do
|
34
|
+
api.post(
|
35
|
+
"campaign/tcpaoptin",
|
36
|
+
keyword_id: keyword_id,
|
37
|
+
shortcode_id: subject.send(:short_code_id),
|
38
|
+
optinkeyword: "Y",
|
39
|
+
name: "Opt in to Keyword #{keyword}",
|
40
|
+
message: "Welcome to keyword #{keyword} campaigns. To join, reply Y",
|
41
|
+
doubleoptinmessage: "Thanks, you have joined keyword #{keyword} campaigns",
|
42
|
+
errormessage: "We could not understand your response. Please reply Y to join"
|
43
|
+
).data["campaign_id"]
|
44
|
+
end
|
45
|
+
|
46
|
+
before(:each) do
|
47
|
+
clean_up
|
48
|
+
end
|
49
|
+
|
50
|
+
let(:campaign_options) {
|
51
|
+
{
|
52
|
+
name: "Opt in to Keyword #{keyword}",
|
53
|
+
first_message: "Welcome to keyword #{keyword} campaigns. To join, reply Y",
|
54
|
+
second_message: "Thanks, you have joined keyword #{keyword} campaigns",
|
55
|
+
error_message: "We could not understand your response. Please reply Y to join"
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
after(:each) do
|
60
|
+
tcpa_opt_in = CellForce::TcpaOptInCampaign.new(keyword)
|
61
|
+
tcpa_opt_in.campaign_ids.each do |id|
|
62
|
+
api.post("campaign/delete", row_id: id)
|
63
|
+
end
|
64
|
+
api.post("keyword/delete", row_id: tcpa_opt_in.keyword_id)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should simulate an MO when the keyword does not already exist" do
|
68
|
+
subject.simulate_mo(keyword, campaign_options)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should simulate an MO when the keyword exists but the campaign does not" do
|
72
|
+
keyword_id
|
73
|
+
subject.simulate_mo(keyword, campaign_options)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should simulate an MO when the keyword and campaign already exist" do
|
77
|
+
campaign_id
|
78
|
+
subject.simulate_mo(keyword)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
# Use a username and password from the client level of the hierarchy
|
2
|
+
username: MyClient
|
3
|
+
password: MyClientPassword
|
4
|
+
api_key: ReallyLongRandomString1234567789
|
5
|
+
phone: [Put a phone number here. Just digits, no hyphens. That phone number should receive SMS messages during these tests.]
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require "logger"
|
3
|
+
require "rspec/core/shared_context"
|
4
|
+
|
5
|
+
module ConfigurationFromYaml
|
6
|
+
extend RSpec::Core::SharedContext
|
7
|
+
SECRETS = YAML::load(File.open('spec/secrets.yml'))
|
8
|
+
|
9
|
+
[:phone, :username, :password, :api_key].each do |key|
|
10
|
+
let(key) { SECRETS[key.to_s] }
|
11
|
+
end
|
12
|
+
|
13
|
+
before(:each) do
|
14
|
+
SmsValidation.configure do |config|
|
15
|
+
config.logger = ::Logger.new(STDOUT)
|
16
|
+
end
|
17
|
+
CellForce.configure do |config|
|
18
|
+
config.username, config.password, config.api_key = username, password, api_key
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cell_force
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Isaac Betesh
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-04-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.7'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.7'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sms_validation
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: httparty
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Send SMS messages using the CellForce API
|
84
|
+
email:
|
85
|
+
- iybetesh@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- ".rspec"
|
92
|
+
- Gemfile
|
93
|
+
- LICENSE.txt
|
94
|
+
- README.md
|
95
|
+
- Rakefile
|
96
|
+
- cell_force.gemspec
|
97
|
+
- lib/cell_force.rb
|
98
|
+
- lib/cell_force/api.rb
|
99
|
+
- lib/cell_force/configuration.rb
|
100
|
+
- lib/cell_force/default_short_code.rb
|
101
|
+
- lib/cell_force/mobile_device.rb
|
102
|
+
- lib/cell_force/tcpa_opt_in_campaign.rb
|
103
|
+
- lib/cell_force/version.rb
|
104
|
+
- spec/api_spec.rb
|
105
|
+
- spec/configuration_spec.rb
|
106
|
+
- spec/mobile_device_spec.rb
|
107
|
+
- spec/secrets.yml.example
|
108
|
+
- spec/spec_helper.rb
|
109
|
+
- spec/support/configuration_from_yaml.rb
|
110
|
+
homepage: https://github.com/betesh/cell_force/
|
111
|
+
licenses:
|
112
|
+
- MIT
|
113
|
+
metadata: {}
|
114
|
+
post_install_message:
|
115
|
+
rdoc_options: []
|
116
|
+
require_paths:
|
117
|
+
- lib
|
118
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
124
|
+
requirements:
|
125
|
+
- - ">="
|
126
|
+
- !ruby/object:Gem::Version
|
127
|
+
version: '0'
|
128
|
+
requirements: []
|
129
|
+
rubyforge_project:
|
130
|
+
rubygems_version: 2.2.2
|
131
|
+
signing_key:
|
132
|
+
specification_version: 4
|
133
|
+
summary: '# CellForce We use HTTParty to send SMS messages using the CellForce API. See
|
134
|
+
USAGE below for details. ## Installation Add this line to your application''s
|
135
|
+
Gemfile: ```ruby gem ''cell_force'' ``` And then execute: ``` bash $ bundle ``` Or
|
136
|
+
install it yourself as: ``` bash $ gem install cell_force ``` ## Usage #### Configuration cell_force
|
137
|
+
requires you to provide a username, password and API key. Configure them before
|
138
|
+
attempting to post to any AOI resource. ```ruby CellForce.configure do |config|
|
139
|
+
config.username = "Me" config.password = "MyPassword" config.api_key = "ReallyLongRandomString1234567789"
|
140
|
+
end ``` Since cell_force depends on sms_validation (https://github.com/betesh/sms_validation/),
|
141
|
+
it is also recommended that you configure sms_validation. cell_force uses sms_validation''s
|
142
|
+
logger. In a Rails environment, you will probably want to rely on the default configuration,
|
143
|
+
but outside of Rails, you will need to configure it if you want any logging: ```ruby
|
144
|
+
SmsValidation.configure do |config| config.logger = ::Logger.new(STDOUT) end ``` ####
|
145
|
+
Sending an SMS Once configured, cell_force will automatically log you in before
|
146
|
+
the first API call and will store your credentials for all subsequent API calls.
|
147
|
+
cell_force transparently logs in again whenever you session expires (i.e. if you
|
148
|
+
log out or if 1 hours has elapsed since your last API call). So once configured,
|
149
|
+
just fire away with API calls, which are done using CellForce::Api#post: ```ruby
|
150
|
+
api = CellForce::Api api.post("shortcode/getusershortcodes") api.post("shortcode/getshortcodecarriers",
|
151
|
+
shortcode_id: ENV[''SHORT_CODE'']) ``` But don''t use CellForce::Api#post to send
|
152
|
+
an SMS. If you do, you''ll bypass sms_validation. Instead, use CellForce::MobileDevice#send_mt,
|
153
|
+
so that everything gets validated. ```ruby mobile_device = CellForce::MobileDevice.new(my_phone_number)
|
154
|
+
mobile_device.send_mt(my_phone_number, "Hi, How are you?") ``` sms_validation allows
|
155
|
+
you to configure what to do when a message is too long. cell_force is only designed
|
156
|
+
to work with the default behavior: :raise_error If you need the :truncate or :split
|
157
|
+
behavior instead, instantiate an SmsValidation::Sms and iterate through its messages: ```ruby
|
158
|
+
mobile_device = CellForce::MobileDevice.new(my_phone_number) SmsValidation::Sms.new(my_phone_number,
|
159
|
+
"Hi, How are you?"*387).messages.each do |message| mobile_device.send_mt(message)
|
160
|
+
end ``` CellForce''s documentation does recommend that you log out at the end of
|
161
|
+
your activity ```ruby CellForce::Api.log_out ``` ## Testing Copy spec/secrets.yml.example
|
162
|
+
to spec/secrets.yml and modify it to set appropriate credentails. Then run `rspec`. Note
|
163
|
+
that none of the tests set any expectations. If you run them and they all pass,
|
164
|
+
it only verifies that nothing through an exception. So make sure to check your
|
165
|
+
phone after running the tests to make sure you received all the expected text messages. ##
|
166
|
+
Contributing 1. Fork it ( https://github.com/[my-github-username]/cell_force/fork
|
167
|
+
) 2. Create your feature branch (`git checkout -b my-new-feature`) 3. Commit your
|
168
|
+
changes (`git commit -am ''Add some feature''`) 4. Push to the branch (`git push
|
169
|
+
origin my-new-feature`) 5. Create a new Pull Request'
|
170
|
+
test_files:
|
171
|
+
- spec/api_spec.rb
|
172
|
+
- spec/configuration_spec.rb
|
173
|
+
- spec/mobile_device_spec.rb
|
174
|
+
- spec/secrets.yml.example
|
175
|
+
- spec/spec_helper.rb
|
176
|
+
- spec/support/configuration_from_yaml.rb
|