rocket_sms 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/.gitignore +19 -0
  2. data/.rspec +2 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +1 -0
  7. data/bin/scheduler_runner.rb +11 -0
  8. data/bin/transceiver_runner.rb +10 -0
  9. data/examples/gateway.rb +7 -0
  10. data/examples/gateway.yml +40 -0
  11. data/examples/test.rb +60 -0
  12. data/lib/rocket_sms.rb +111 -0
  13. data/lib/rocket_sms/did.rb +25 -0
  14. data/lib/rocket_sms/gateway.rb +123 -0
  15. data/lib/rocket_sms/lock.rb +0 -0
  16. data/lib/rocket_sms/message.rb +30 -0
  17. data/lib/rocket_sms/scheduler.rb +212 -0
  18. data/lib/rocket_sms/transceiver.rb +260 -0
  19. data/lib/rocket_sms/version.rb +3 -0
  20. data/rocket_sms.gemspec +34 -0
  21. data/spec/spec_helper.rb +17 -0
  22. data/vendor/ruby-smpp/CHANGELOG +52 -0
  23. data/vendor/ruby-smpp/CONTRIBUTORS.txt +11 -0
  24. data/vendor/ruby-smpp/Gemfile +8 -0
  25. data/vendor/ruby-smpp/LICENSE +20 -0
  26. data/vendor/ruby-smpp/README.rdoc +89 -0
  27. data/vendor/ruby-smpp/Rakefile +53 -0
  28. data/vendor/ruby-smpp/VERSION +1 -0
  29. data/vendor/ruby-smpp/config/environment.rb +2 -0
  30. data/vendor/ruby-smpp/examples/PDU1.example +26 -0
  31. data/vendor/ruby-smpp/examples/PDU2.example +26 -0
  32. data/vendor/ruby-smpp/examples/sample_gateway.rb +137 -0
  33. data/vendor/ruby-smpp/examples/sample_smsc.rb +102 -0
  34. data/vendor/ruby-smpp/lib/smpp.rb +25 -0
  35. data/vendor/ruby-smpp/lib/smpp/base.rb +308 -0
  36. data/vendor/ruby-smpp/lib/smpp/encoding/utf8_encoder.rb +37 -0
  37. data/vendor/ruby-smpp/lib/smpp/optional_parameter.rb +35 -0
  38. data/vendor/ruby-smpp/lib/smpp/pdu/base.rb +183 -0
  39. data/vendor/ruby-smpp/lib/smpp/pdu/bind_base.rb +25 -0
  40. data/vendor/ruby-smpp/lib/smpp/pdu/bind_receiver.rb +4 -0
  41. data/vendor/ruby-smpp/lib/smpp/pdu/bind_receiver_response.rb +4 -0
  42. data/vendor/ruby-smpp/lib/smpp/pdu/bind_resp_base.rb +17 -0
  43. data/vendor/ruby-smpp/lib/smpp/pdu/bind_transceiver.rb +4 -0
  44. data/vendor/ruby-smpp/lib/smpp/pdu/bind_transceiver_response.rb +4 -0
  45. data/vendor/ruby-smpp/lib/smpp/pdu/deliver_sm.rb +142 -0
  46. data/vendor/ruby-smpp/lib/smpp/pdu/deliver_sm_response.rb +12 -0
  47. data/vendor/ruby-smpp/lib/smpp/pdu/enquire_link.rb +11 -0
  48. data/vendor/ruby-smpp/lib/smpp/pdu/enquire_link_response.rb +11 -0
  49. data/vendor/ruby-smpp/lib/smpp/pdu/generic_nack.rb +20 -0
  50. data/vendor/ruby-smpp/lib/smpp/pdu/submit_multi.rb +68 -0
  51. data/vendor/ruby-smpp/lib/smpp/pdu/submit_multi_response.rb +49 -0
  52. data/vendor/ruby-smpp/lib/smpp/pdu/submit_sm.rb +91 -0
  53. data/vendor/ruby-smpp/lib/smpp/pdu/submit_sm_response.rb +31 -0
  54. data/vendor/ruby-smpp/lib/smpp/pdu/unbind.rb +11 -0
  55. data/vendor/ruby-smpp/lib/smpp/pdu/unbind_response.rb +12 -0
  56. data/vendor/ruby-smpp/lib/smpp/receiver.rb +27 -0
  57. data/vendor/ruby-smpp/lib/smpp/server.rb +223 -0
  58. data/vendor/ruby-smpp/lib/smpp/transceiver.rb +109 -0
  59. data/vendor/ruby-smpp/lib/sms.rb +9 -0
  60. data/vendor/ruby-smpp/ruby-smpp.gemspec +96 -0
  61. data/vendor/ruby-smpp/test/delegate.rb +28 -0
  62. data/vendor/ruby-smpp/test/encoding_test.rb +232 -0
  63. data/vendor/ruby-smpp/test/optional_parameter_test.rb +30 -0
  64. data/vendor/ruby-smpp/test/pdu_parsing_test.rb +111 -0
  65. data/vendor/ruby-smpp/test/receiver_test.rb +232 -0
  66. data/vendor/ruby-smpp/test/responsive_delegate.rb +53 -0
  67. data/vendor/ruby-smpp/test/server.rb +56 -0
  68. data/vendor/ruby-smpp/test/smpp_test.rb +239 -0
  69. data/vendor/ruby-smpp/test/submit_sm_test.rb +40 -0
  70. data/vendor/ruby-smpp/test/transceiver_test.rb +35 -0
  71. data/vendor/smscsim/License.txt +61 -0
  72. data/vendor/smscsim/smpp.jar +0 -0
  73. data/vendor/smscsim/smscsim.jar +0 -0
  74. data/vendor/smscsim/start.sh +3 -0
  75. data/vendor/smscsim/users.txt +46 -0
  76. metadata +299 -0
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+
19
+ /vendor/smscsim/sim.*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in lean_sms.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Marcelo Wiermann
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,29 @@
1
+ # LeanSms
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'lean_sms'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install lean_sms
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rocket_sms"
4
+
5
+ redis_url = ENV['REDIS_URL'] || ARGV[0]
6
+ log_location = ENV['LOG_LOCATION'] || ARGV[1] || STDOUT
7
+
8
+ scheduler = RocketSMS::Scheduler.instance
9
+ scheduler.redis_url = redis_url
10
+ scheduler.log_location = log_location
11
+ scheduler.start
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "rocket_sms"
4
+
5
+ id = ENV['TRANSCEIVER_ID'] || ARGV[0]
6
+ redis_url = ENV['REDIS_URL'] || ARGV[1]
7
+ log_location = ENV['LOG_LOCATION'] || ARGV[2] || STDOUT
8
+
9
+ transceiver = RocketSMS::Transceiver.new(id, redis_url, log_location)
10
+ transceiver.start
@@ -0,0 +1,7 @@
1
+ require 'rocket_sms'
2
+
3
+ RocketSMS.configure do |config|
4
+ config.settings = 'examples/gateway.yml'
5
+ end
6
+
7
+ RocketSMS.start
@@ -0,0 +1,40 @@
1
+ redis:
2
+ url: nil
3
+ log:
4
+ location: STDOUT
5
+ transceivers:
6
+ bind1:
7
+ throughput: 50.0
8
+ connection:
9
+ host: '0.0.0.0'
10
+ port: 6000
11
+ system_id: 'hugo'
12
+ password: 'ggoohu'
13
+ system_type: ''
14
+ interface_version: 52
15
+ source_ton: 0
16
+ source_npi: 1
17
+ destination_ton: 1
18
+ destination_npi: 1
19
+ source_address_range: ''
20
+ destination_address_range: ''
21
+ enquire_link_delay_secs: 10
22
+ registered_delivery: 1
23
+ bind2:
24
+ throughput: 5.0
25
+ connection:
26
+ host: '0.0.0.0'
27
+ port: 6001
28
+ system_id: 'hugo'
29
+ password: 'ggoohu'
30
+ system_type: ''
31
+ interface_version: 52
32
+ source_ton: 0
33
+ source_npi: 1
34
+ destination_ton: 1
35
+ destination_npi: 1
36
+ source_address_range: ''
37
+ destination_address_range: ''
38
+ enquire_link_delay_secs: 10
39
+ registered_delivery: 1
40
+
data/examples/test.rb ADDED
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+ require 'rubygems'
3
+ require 'json'
4
+ require 'redis'
5
+ require 'securerandom'
6
+ r = Redis.new
7
+
8
+ r.del('gateway:queues:mt:pending')
9
+ r.del('gateway:queues:mt:retry')
10
+ r.del('gateway:queues:mt:dispatch')
11
+ r.del('gateway:queues:mt:success')
12
+ r.del('gateway:queues:mt:failure')
13
+
14
+ keys = r.keys('gateway:dids*')
15
+ keys.each{ |k| r.del(k) }
16
+ r.del('gateway:sets:dispatch')
17
+
18
+ dids = []
19
+ 1000000000.upto(1000001000) do |num|
20
+ did = { number: num, throughput: 1 }
21
+ r.set("gateway:dids:#{num}", did.to_json )
22
+ dids << did
23
+ end
24
+
25
+ t = Proc.new do
26
+ 45.times do |i|
27
+ msgs = []
28
+ msgs << {id: "#{SecureRandom.hex(8)}", sender: dids.sample[:number], receiver: '9999999999', body: 'Attention LOs: Rob Huddle will be on vacation from Oct 4th - Oct 12th. In his absence, please contact Rachael Hawk with all urgent matters. Thank you' }
29
+ body = %q{ @$¥èéùìòÇØøÅå_^{}\[~]|ÆæßÉ !"#¤%&(')*+,-./0123456789:;<=>? ¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà"}
30
+ msgs << {id: "#{SecureRandom.hex(8)}", sender: dids.sample[:number], receiver: '9999999999', body: body }
31
+ msgs.each do |m|
32
+ score = Time.now.to_i
33
+ r.zadd('gateway:queues:mt:pending', score, m.to_json)
34
+ end
35
+ end
36
+ end
37
+
38
+ threads = []
39
+ threads << Thread.new do
40
+ while true do
41
+ t.call
42
+ sleep 1
43
+ end
44
+ end
45
+
46
+ threads << Thread.new do
47
+ l = r.llen('gateway:queues:mt:success')
48
+ while true do
49
+ nl = r.llen('gateway:queues:mt:success')
50
+ speed = l - nl
51
+ puts speed
52
+ l = nl
53
+ sleep 1
54
+ end
55
+ end
56
+
57
+ threads.each{ |t| t.join }
58
+
59
+
60
+
data/lib/rocket_sms.rb ADDED
@@ -0,0 +1,111 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'eventmachine'
4
+ require 'em-hiredis'
5
+ require 'oj'
6
+ require 'multi_json'
7
+ require 'singleton'
8
+ require 'securerandom'
9
+ require 'ostruct'
10
+ require 'forwardable'
11
+ #require 'smpp'
12
+
13
+ path = File.expand_path(__FILE__).split('/')
14
+ path.delete_at(-1)
15
+ path.delete_at(-1)
16
+ path = path.join('/')
17
+
18
+ require "#{path}/vendor/ruby-smpp/lib/smpp.rb"
19
+
20
+ require "rocket_sms/version"
21
+
22
+ module RocketSMS
23
+ extend self
24
+
25
+ # Disable ruby-smpp logging
26
+ require 'tempfile'
27
+ Smpp::Base.logger = Logger.new(Tempfile.new('ruby-smpp').path)
28
+
29
+ LIB_PATH = File.dirname(__FILE__) + '/rocket_sms/'
30
+
31
+ %w{ gateway did message transceiver scheduler lock }.each do |dep|
32
+ require LIB_PATH + dep
33
+ end
34
+
35
+ def start
36
+ @pid = Process.pid
37
+ #Process.daemon
38
+ gateway.start
39
+ end
40
+
41
+ def queues
42
+ @queues ||= {
43
+ mt: {
44
+ pending: 'gateway:queues:mt:pending',
45
+ success: 'gateway:queues:mt:success',
46
+ failure: 'gateway:queues:mt:failure'
47
+ },
48
+ mo: 'gateway:queues:mo:received',
49
+ dr: 'gateway:queues:dr'
50
+ }
51
+ end
52
+
53
+ def gateway
54
+ @gateway ||= RocketSMS::Gateway.instance
55
+ end
56
+
57
+ def redis
58
+ @redis ||= EM::Hiredis.connect(redis_url)
59
+ end
60
+
61
+ # Configuration and Setup
62
+ def configure
63
+ yield self
64
+ end
65
+
66
+ def settings=(yaml_file_location)
67
+ @settings = symbolize_keys(YAML.load(IO.read(yaml_file_location)))
68
+ redis_url = @settings[:redis] && @settings[:redis][:url]
69
+ log_location = @settings[:log] && @settings[:log][:location]
70
+ end
71
+
72
+ def settings
73
+ @settings
74
+ end
75
+
76
+ def redis_url
77
+ @redis_url ||= "redis://localhost:6379"
78
+ end
79
+
80
+ def redis_url=(url)
81
+ @redis_url = url unless url.nil?
82
+ end
83
+
84
+ def logger
85
+ @logger ||= Logger.new(log_location)
86
+ end
87
+
88
+ def log_location
89
+ @log_location ||= STDOUT
90
+ end
91
+
92
+ def log_location=(location)
93
+ @log_location = location unless location.nil?
94
+ end
95
+
96
+ def symbolize_keys(hash)
97
+ hash.inject({}){|result, (key, value)|
98
+ new_key = case key
99
+ when String then key.to_sym
100
+ else key
101
+ end
102
+ new_value = case value
103
+ when Hash then symbolize_keys(value)
104
+ else value
105
+ end
106
+ result[new_key] = new_value
107
+ result
108
+ }
109
+ end
110
+
111
+ end
@@ -0,0 +1,25 @@
1
+ module RocketSMS
2
+ class Did
3
+
4
+ attr_reader :params
5
+
6
+ def initialize(params)
7
+ @params = OpenStruct.new(params)
8
+ end
9
+
10
+ def to_json
11
+ MultiJson.dump(@params.marshal_dump)
12
+ end
13
+
14
+ def self.from_json(json)
15
+ params = MultiJson.load(json, symbolize_keys: true)
16
+ did = Did.new(params)
17
+ return did
18
+ end
19
+
20
+ def method_missing(sym, *args, &block)
21
+ @params.send(sym, *args, &block)
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,123 @@
1
+ module RocketSMS
2
+
3
+ class Gateway
4
+ include Singleton
5
+ extend Forwardable
6
+
7
+ def_delegators :RocketSMS, :settings, :redis, :logger, :redis_url, :log_location
8
+
9
+ def initialize
10
+ @scheduler = {}
11
+ @transceivers = {}
12
+ @path = Gem::Specification.find_by_name('rocket_sms').gem_dir
13
+ end
14
+
15
+ def log(msg, level = 'info')
16
+ if EM.reactor_running?
17
+ EM.defer{ logger.send(level, msg) }
18
+ else
19
+ logger.send(level, msg)
20
+ end
21
+ end
22
+
23
+ def start
24
+ EM.run do
25
+ log "Starting Gateway"
26
+ startup
27
+ # Trap exit-related signals
28
+ Signal.trap("INT") { |signal| stop(signal) }
29
+ Signal.trap("TERM") { |signal| stop(signal) }
30
+ end
31
+ end
32
+
33
+ def stop(signal = nil)
34
+ if @kill
35
+ log "Forcing Exit. Check your data for losses."
36
+ shutdown
37
+ else
38
+ log "Stopping. Waiting 5 seconds for pending operations to finish."
39
+ @kill = true
40
+ @active = false
41
+ if @scheduler[:pid]
42
+ Process.kill('TERM', @scheduler[:pid]) rescue nil
43
+ Process.wait(@scheduler[:pid])
44
+ end
45
+ if @transceivers
46
+ @transceivers.each_value do |t|
47
+ if t[:pid]
48
+ Process.kill('TERM', t[:pid]) rescue nil
49
+ Process.wait(t[:pid])
50
+ end
51
+ end
52
+ end
53
+ EM::Timer.new(5){ shutdown }
54
+ end
55
+ end
56
+
57
+ def shutdown
58
+ if @scheduler[:pid]
59
+ Process.kill('TERM', @scheduler[:pid]) rescue nil
60
+ end
61
+ if @transceivers
62
+ @transceivers.each_value do |t|
63
+ if t[:pid]
64
+ Process.kill('TERM', t[:pid]) rescue nil
65
+ end
66
+ end
67
+ end
68
+ log "Gateway DOWN."
69
+ EM.stop
70
+ end
71
+
72
+ def startup
73
+ clean_up_stale_transceivers
74
+ end
75
+
76
+ def clean_up_stale_transceivers
77
+ redis.keys("gateway:transceivers:*") do |keys|
78
+ op = Proc.new do |key, iter|
79
+ redis.del(key) do |resp|
80
+ iter.next
81
+ end
82
+ end
83
+ cb = Proc.new do |responses|
84
+ setup_transceivers
85
+ end
86
+ EM::Iterator.new(keys).each(op,cb)
87
+ end
88
+ end
89
+
90
+ def setup_transceivers
91
+ # Clean up stale transceivers
92
+ op = Proc.new do |tid, iter|
93
+ tsettings = settings[:transceivers][tid]
94
+ redis.multi
95
+ redis.hset("gateway:transceivers:#{tid}", "throughput", tsettings[:throughput])
96
+ redis.hset("gateway:transceivers:#{tid}", "connection", MultiJson.dump(tsettings[:connection]))
97
+ redis.exec do |resp|
98
+ iter.next
99
+ end
100
+ end
101
+ cb = Proc.new do |responses|
102
+ start_scheduler
103
+ start_transceivers
104
+ end
105
+ EM::Iterator.new(settings[:transceivers].keys).each(op,cb)
106
+ end
107
+
108
+ def start_scheduler
109
+ cmd = "bundle exec ruby #{@path}/bin/scheduler_runner.rb"
110
+ @scheduler[:pid] = Process.spawn({ "REDIS_URL" => redis_url, "LOG_LOCATION" => (log_location == STDOUT ? nil : log_location) }, cmd)
111
+ end
112
+
113
+ def start_transceivers
114
+ settings[:transceivers].each do |tid, settings|
115
+ cmd = "bundle exec ruby #{@path}/bin/transceiver_runner.rb"
116
+ @transceivers[tid] = {}
117
+ @transceivers[tid][:pid] = Process.spawn({ "TRANSCEIVER_ID" => tid.to_s ,"REDIS_URL" => redis_url, "LOG_LOCATION" => (log_location == STDOUT ? nil : log_location) }, cmd)
118
+ end
119
+ end
120
+
121
+ end
122
+
123
+ end