adammck-rubysms 0.8.1 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/rubysms-drb-client +26 -151
- data/bin/rubysms-gtk-drb-client +168 -0
- data/lib/rubysms/application.rb +85 -0
- data/lib/rubysms/backend/clickatell.rb +95 -0
- data/lib/rubysms/backend/gsm.rb +36 -35
- data/lib/rubysms/backend/http.rb +3 -2
- data/lib/rubysms/logger.rb +1 -0
- data/lib/rubysms/router.rb +57 -25
- data/rubysms.gemspec +6 -3
- metadata +5 -2
data/bin/rubysms-drb-client
CHANGED
@@ -1,168 +1,43 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require "libglade2.rb"
|
4
3
|
require "drb.rb"
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
class SmsGui
|
9
|
-
include GetText
|
10
|
-
attr :glade
|
5
|
+
class Client
|
6
|
+
SERVER_PORT = "1370"
|
11
7
|
|
12
8
|
def initialize
|
13
|
-
|
14
|
-
# if this file is a symlink (which is probably is, since rubygems
|
15
|
-
# automatically symlinks bin files into /usr/bin), find the original
|
16
|
-
# source file, so we can find the glade src relatively
|
17
|
-
this_file = File.symlink?(__FILE__) ? \
|
18
|
-
File.readlink(__FILE__) : __FILE__
|
19
|
-
|
20
|
-
# find and load up the glade src for the gui
|
21
|
-
glade_src = File.expand_path(File.dirname(this_file) + "/../lib/drb-client.glade")
|
22
|
-
@glade = GladeXML.new(glade_src) do |handler|
|
23
|
-
method(handler)
|
24
|
-
end
|
25
|
-
|
26
|
-
# for storing outgoing
|
27
|
-
# messages for recall
|
28
|
-
@history = []
|
29
|
-
@hist_pos = 0
|
30
|
-
|
31
|
-
# fetch references to the gtk widgets
|
32
|
-
@source = @glade.get_widget("entry_source")
|
33
|
-
@entry = @glade.get_widget("entry_message")
|
34
|
-
@log = @glade.get_widget("textview_log")
|
35
|
-
@lb = @log.buffer
|
36
|
-
|
37
|
-
# create a text mark to keep at the end of
|
38
|
-
# the log, so we can keep scrolling to it
|
39
|
-
@lb.create_mark("end", @lb.end_iter, true)
|
40
|
-
|
41
|
-
# create colors for incoming and outgoing messages
|
42
|
-
@lb.create_tag("incoming", "family"=>"monospace", "foreground"=>"#AA0000")
|
43
|
-
@lb.create_tag("outgoing", "family"=>"monospace", "foreground"=>"#0000AA")
|
44
|
-
@lb.create_tag("error", "family"=>"monospace", "foreground"=>"#FFFFFF", "background"=>"#FF0000")
|
45
|
-
|
46
|
-
# prepopulate the source phone number with six digits
|
47
9
|
@src = (1111 + rand(8888)).to_s
|
48
|
-
@
|
49
|
-
|
50
|
-
# fire up drb, to send outgoing messages to rubysms
|
51
|
-
@injector = DRbObject.new_with_uri("druby://localhost:#{SERVER_PORT}")
|
52
|
-
puts "Connected to RubySMS at: #{@injector.__drburi}"
|
53
|
-
|
54
|
-
# start listening for incoming messages from rubysms
|
55
|
-
@drb = DRb.start_service("druby://localhost:#{SERVER_PORT}#{@src}", self)
|
56
|
-
puts "Started DRb client at: #{@drb.uri}"
|
57
|
-
|
58
|
-
# display the GUI
|
59
|
-
@glade.get_widget("window").show
|
60
|
-
@entry.grab_focus
|
61
|
-
end
|
62
|
-
|
63
|
-
|
64
|
-
# user closed the window,
|
65
|
-
# so terminate the program
|
66
|
-
def on_quit
|
67
|
-
Gtk.main_quit
|
10
|
+
@outgoing = DRbObject.new_with_uri("druby://localhost:#{SERVER_PORT}")
|
11
|
+
@incoming = DRb.start_service("druby://localhost:#{SERVER_PORT}#{@src}", self)
|
68
12
|
end
|
69
|
-
|
70
|
-
|
71
|
-
# user clicked "send", or pressed
|
72
|
-
# enter while in the entry field
|
73
|
-
def on_button_send_clicked
|
74
|
-
msg = @entry.text
|
75
|
-
|
76
|
-
# do nothing if the message is blank
|
77
|
-
return nil if msg.empty?
|
78
|
-
log ">> #{msg}", "incoming"
|
79
13
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
# were a real incoming sms
|
84
|
-
@injector.incoming(@src, msg)
|
85
|
-
|
86
|
-
@hist_pos = 0
|
87
|
-
@history.push(msg)
|
88
|
-
@entry.text = ""
|
89
|
-
|
90
|
-
# couldn't connect!
|
91
|
-
rescue DRb::DRbConnError
|
92
|
-
log("Connection to RubySMS failed!", "error")
|
93
|
-
#log("Tried URI: #{@injector.__drburi}", "error")
|
94
|
-
rescue => err
|
95
|
-
log(err.message, "error")
|
96
|
-
log(" " + err.backtrace.join("\n "), "error")
|
97
|
-
end
|
14
|
+
def print(str)
|
15
|
+
$stdout.print(str)
|
16
|
+
$stdout.flush
|
98
17
|
end
|
99
|
-
|
100
|
-
def
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
# which bumps the focus
|
110
|
-
# upwards to the log
|
111
|
-
return true
|
112
|
-
end
|
113
|
-
|
114
|
-
if event.keyval == 65364 # down arrow
|
115
|
-
if @hist_pos < -1
|
116
|
-
@hist_pos += 1
|
117
|
-
@entry.text = @history[@hist_pos]
|
118
|
-
@entry.grab_focus
|
119
|
-
end
|
120
|
-
|
121
|
-
# prevent default
|
122
|
-
return true
|
18
|
+
|
19
|
+
def start
|
20
|
+
while(true)
|
21
|
+
print "< "
|
22
|
+
str = $stdin.gets.strip
|
23
|
+
@outgoing.incoming(@src, str)
|
24
|
+
|
25
|
+
# to allow time for a message to
|
26
|
+
# come back; yes, this is horrible
|
27
|
+
sleep 0.5
|
123
28
|
end
|
124
|
-
|
125
|
-
# any other key resets
|
126
|
-
# the position in history
|
127
|
-
@hist_pos = 0
|
128
|
-
return false
|
129
|
-
end
|
130
|
-
|
131
|
-
|
132
|
-
def log(msg, tag)
|
133
|
-
# prepend a newline, unless this
|
134
|
-
# is the first entry (to avoid a
|
135
|
-
# one-line gap at the top)
|
136
|
-
msg = "\n" + msg\
|
137
|
-
if @lb.char_count > 0
|
138
|
-
|
139
|
-
# add a single entry to the message log
|
140
|
-
# (using the colors defined in #initialize)
|
141
|
-
@lb.insert(@lb.end_iter, msg, tag)
|
142
|
-
|
143
|
-
# scroll to the end of the *previous*
|
144
|
-
# message, to bring the top of this
|
145
|
-
# message into view, in case it is long
|
146
|
-
@log.scroll_to_mark(@lb.get_mark("end"), 0, true, 1, 0)
|
147
|
-
|
148
|
-
# update the position of the end marker,
|
149
|
-
# which we will scroll to (above), when
|
150
|
-
# the next message is logged
|
151
|
-
@lb.move_mark(@lb.get_mark("end"), @lb.end_iter)
|
152
|
-
@lb.end_iter.line_offset = 0
|
153
29
|
end
|
154
|
-
|
155
|
-
|
30
|
+
|
156
31
|
def incoming(msg)
|
157
|
-
|
158
|
-
# except add it to the message log
|
159
|
-
msg.gsub!(/\n/, "\n ")
|
160
|
-
log "<< #{msg}", "outgoing"
|
32
|
+
print "> #{msg}\n"
|
161
33
|
end
|
162
34
|
end
|
163
35
|
|
36
|
+
# exit gracefully if
|
37
|
+
# ctrl+c is received
|
38
|
+
trap("INT") do
|
39
|
+
puts
|
40
|
+
exit
|
41
|
+
end
|
164
42
|
|
165
|
-
|
166
|
-
# until GTK terminates
|
167
|
-
gui = SmsGui.new
|
168
|
-
Gtk.main
|
43
|
+
Client.new.start
|
@@ -0,0 +1,168 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "libglade2.rb"
|
4
|
+
require "drb.rb"
|
5
|
+
|
6
|
+
SERVER_PORT = "1370"
|
7
|
+
|
8
|
+
class SmsGui
|
9
|
+
include GetText
|
10
|
+
attr :glade
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
|
14
|
+
# if this file is a symlink (which is probably is, since rubygems
|
15
|
+
# automatically symlinks bin files into /usr/bin), find the original
|
16
|
+
# source file, so we can find the glade src relatively
|
17
|
+
this_file = File.symlink?(__FILE__) ? \
|
18
|
+
File.readlink(__FILE__) : __FILE__
|
19
|
+
|
20
|
+
# find and load up the glade src for the gui
|
21
|
+
glade_src = File.expand_path(File.dirname(this_file) + "/../lib/drb-client.glade")
|
22
|
+
@glade = GladeXML.new(glade_src) do |handler|
|
23
|
+
method(handler)
|
24
|
+
end
|
25
|
+
|
26
|
+
# for storing outgoing
|
27
|
+
# messages for recall
|
28
|
+
@history = []
|
29
|
+
@hist_pos = 0
|
30
|
+
|
31
|
+
# fetch references to the gtk widgets
|
32
|
+
@source = @glade.get_widget("entry_source")
|
33
|
+
@entry = @glade.get_widget("entry_message")
|
34
|
+
@log = @glade.get_widget("textview_log")
|
35
|
+
@lb = @log.buffer
|
36
|
+
|
37
|
+
# create a text mark to keep at the end of
|
38
|
+
# the log, so we can keep scrolling to it
|
39
|
+
@lb.create_mark("end", @lb.end_iter, true)
|
40
|
+
|
41
|
+
# create colors for incoming and outgoing messages
|
42
|
+
@lb.create_tag("incoming", "family"=>"monospace", "foreground"=>"#AA0000")
|
43
|
+
@lb.create_tag("outgoing", "family"=>"monospace", "foreground"=>"#0000AA")
|
44
|
+
@lb.create_tag("error", "family"=>"monospace", "foreground"=>"#FFFFFF", "background"=>"#FF0000")
|
45
|
+
|
46
|
+
# prepopulate the source phone number with six digits
|
47
|
+
@src = (1111 + rand(8888)).to_s
|
48
|
+
@source.text = @src
|
49
|
+
|
50
|
+
# fire up drb, to send outgoing messages to rubysms
|
51
|
+
@injector = DRbObject.new_with_uri("druby://localhost:#{SERVER_PORT}")
|
52
|
+
puts "Connected to RubySMS at: #{@injector.__drburi}"
|
53
|
+
|
54
|
+
# start listening for incoming messages from rubysms
|
55
|
+
@drb = DRb.start_service("druby://localhost:#{SERVER_PORT}#{@src}", self)
|
56
|
+
puts "Started DRb client at: #{@drb.uri}"
|
57
|
+
|
58
|
+
# display the GUI
|
59
|
+
@glade.get_widget("window").show
|
60
|
+
@entry.grab_focus
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
# user closed the window,
|
65
|
+
# so terminate the program
|
66
|
+
def on_quit
|
67
|
+
Gtk.main_quit
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
# user clicked "send", or pressed
|
72
|
+
# enter while in the entry field
|
73
|
+
def on_button_send_clicked
|
74
|
+
msg = @entry.text
|
75
|
+
|
76
|
+
# do nothing if the message is blank
|
77
|
+
return nil if msg.empty?
|
78
|
+
log ">> #{msg}", "incoming"
|
79
|
+
|
80
|
+
begin
|
81
|
+
# attempt to send the message
|
82
|
+
# to rubysms via drb, as if it
|
83
|
+
# were a real incoming sms
|
84
|
+
@injector.incoming(@src, msg)
|
85
|
+
|
86
|
+
@hist_pos = 0
|
87
|
+
@history.push(msg)
|
88
|
+
@entry.text = ""
|
89
|
+
|
90
|
+
# couldn't connect!
|
91
|
+
rescue DRb::DRbConnError
|
92
|
+
log("Connection to RubySMS failed!", "error")
|
93
|
+
#log("Tried URI: #{@injector.__drburi}", "error")
|
94
|
+
rescue => err
|
95
|
+
log(err.message, "error")
|
96
|
+
log(" " + err.backtrace.join("\n "), "error")
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def on_entry_message_key_press(widget, event)
|
101
|
+
if event.keyval == 65362 # up arrow
|
102
|
+
if @hist_pos > -@history.length
|
103
|
+
@hist_pos -= 1
|
104
|
+
@entry.text = @history[@hist_pos]
|
105
|
+
@entry.grab_focus
|
106
|
+
end
|
107
|
+
|
108
|
+
# prevent default event,
|
109
|
+
# which bumps the focus
|
110
|
+
# upwards to the log
|
111
|
+
return true
|
112
|
+
end
|
113
|
+
|
114
|
+
if event.keyval == 65364 # down arrow
|
115
|
+
if @hist_pos < -1
|
116
|
+
@hist_pos += 1
|
117
|
+
@entry.text = @history[@hist_pos]
|
118
|
+
@entry.grab_focus
|
119
|
+
end
|
120
|
+
|
121
|
+
# prevent default
|
122
|
+
return true
|
123
|
+
end
|
124
|
+
|
125
|
+
# any other key resets
|
126
|
+
# the position in history
|
127
|
+
@hist_pos = 0
|
128
|
+
return false
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
def log(msg, tag)
|
133
|
+
# prepend a newline, unless this
|
134
|
+
# is the first entry (to avoid a
|
135
|
+
# one-line gap at the top)
|
136
|
+
msg = "\n" + msg\
|
137
|
+
if @lb.char_count > 0
|
138
|
+
|
139
|
+
# add a single entry to the message log
|
140
|
+
# (using the colors defined in #initialize)
|
141
|
+
@lb.insert(@lb.end_iter, msg, tag)
|
142
|
+
|
143
|
+
# scroll to the end of the *previous*
|
144
|
+
# message, to bring the top of this
|
145
|
+
# message into view, in case it is long
|
146
|
+
@log.scroll_to_mark(@lb.get_mark("end"), 0, true, 1, 0)
|
147
|
+
|
148
|
+
# update the position of the end marker,
|
149
|
+
# which we will scroll to (above), when
|
150
|
+
# the next message is logged
|
151
|
+
@lb.move_mark(@lb.get_mark("end"), @lb.end_iter)
|
152
|
+
@lb.end_iter.line_offset = 0
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
def incoming(msg)
|
157
|
+
# we received a message! do nothing
|
158
|
+
# except add it to the message log
|
159
|
+
msg.gsub!(/\n/, "\n ")
|
160
|
+
log "<< #{msg}", "outgoing"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
# initialize, and block
|
166
|
+
# until GTK terminates
|
167
|
+
gui = SmsGui.new
|
168
|
+
Gtk.main
|
data/lib/rubysms/application.rb
CHANGED
@@ -4,6 +4,14 @@
|
|
4
4
|
module SMS
|
5
5
|
class App < Thing
|
6
6
|
|
7
|
+
NAMED_PRIORITY = {
|
8
|
+
:highest => 100,
|
9
|
+
:high => 90,
|
10
|
+
:normal => 50,
|
11
|
+
:low => 10,
|
12
|
+
:lowest => 0
|
13
|
+
}
|
14
|
+
|
7
15
|
# Creates and starts a router to serve only
|
8
16
|
# this application. Handy during development.
|
9
17
|
#
|
@@ -54,6 +62,83 @@ module SMS
|
|
54
62
|
router.serve_forever
|
55
63
|
end
|
56
64
|
|
65
|
+
# Sets or returns the priority of this application **class**. Returning
|
66
|
+
# this value isn't tremendously useful by itself, and mostly exists for
|
67
|
+
# the sake of completeness, and to be called by Application#priority.
|
68
|
+
# The value returned is obtained by finding the first ancestor of this
|
69
|
+
# class which has a @priority (yes, it looks inside other classes
|
70
|
+
# instance variables. I'm sorry.), and converts it to a number via
|
71
|
+
# the SMS::App::NAMED_PRIORITY constant.
|
72
|
+
#
|
73
|
+
# class One < SMS::App
|
74
|
+
# priority :high
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# class Two < One
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# class Three < Two
|
81
|
+
# priority 36
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# One.priority => 90 # set via NAMED_PRIORITY
|
85
|
+
# Two.priority => 90 # inherited from One
|
86
|
+
# Three.priority => 36 # set literally
|
87
|
+
#
|
88
|
+
def self.priority(priority=nil)
|
89
|
+
|
90
|
+
# set the priority of this class if an argument
|
91
|
+
# were provided, and allow execution to continue
|
92
|
+
# to check it's validity
|
93
|
+
unless priority.nil?
|
94
|
+
@priority = priority
|
95
|
+
end
|
96
|
+
|
97
|
+
# find the first ancestor with a priority
|
98
|
+
# (Class.ancestors *includes* self)
|
99
|
+
self.ancestors.each do |klass|
|
100
|
+
if klass.instance_variable_defined?(:@priority)
|
101
|
+
prio = klass.instance_variable_get(:@priority)
|
102
|
+
|
103
|
+
# literal numbers are okay, although
|
104
|
+
# that probably isn't such a good idea
|
105
|
+
if prio.is_a?(Numeric)
|
106
|
+
return prio
|
107
|
+
|
108
|
+
# if this class has a named priority,
|
109
|
+
# resolve and return it's value
|
110
|
+
elsif prio.is_a?(Symbol)
|
111
|
+
if NAMED_PRIORITY.has_key?(prio)
|
112
|
+
return NAMED_PRIORITY[prio]
|
113
|
+
|
114
|
+
# don't allow invalid named priorites.
|
115
|
+
# i can't think of a use case, especially
|
116
|
+
# since the constant can be monkey-patched
|
117
|
+
# if it's really necessary
|
118
|
+
else
|
119
|
+
raise(
|
120
|
+
NameError,
|
121
|
+
"Invalid named priority #{prio.inspect} " +\
|
122
|
+
"of {klass}. Valid named priorties are: " +\
|
123
|
+
NAMED_PRIORITY.keys.join(", "))
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# no ancestor has a priority, so assume
|
130
|
+
# that this app is of "normal" priority
|
131
|
+
return NAMED_PRIORITY[:normal]
|
132
|
+
end
|
133
|
+
|
134
|
+
def priority=(level)
|
135
|
+
@priority = level
|
136
|
+
end
|
137
|
+
|
138
|
+
def priority
|
139
|
+
@priority or self.class.priority
|
140
|
+
end
|
141
|
+
|
57
142
|
def incoming(msg)
|
58
143
|
if services = self.class.instance_variable_get(:@services)
|
59
144
|
|
@@ -0,0 +1,95 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# vim: noet
|
3
|
+
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
require "clickatell"
|
7
|
+
require "mongrel"
|
8
|
+
require "rack"
|
9
|
+
|
10
|
+
|
11
|
+
module SMS::Backend
|
12
|
+
class Clickatell < Base
|
13
|
+
PORT = 1280
|
14
|
+
|
15
|
+
# just store the arguments until the
|
16
|
+
# backend is ready to be started
|
17
|
+
def initialize(key, username, password)
|
18
|
+
@username = username
|
19
|
+
@password = password
|
20
|
+
@key = key
|
21
|
+
end
|
22
|
+
|
23
|
+
def start
|
24
|
+
@api = ::Clickatell::API.authenticate(@key, @username, @password)
|
25
|
+
balance = @api.account_balance
|
26
|
+
|
27
|
+
# start a separate thread to receive
|
28
|
+
# the incoming messages from clickatell
|
29
|
+
@rack_thread = Thread.new do
|
30
|
+
Rack::Handler::Mongrel.run(
|
31
|
+
method(:rack_app), :Port=>PORT)
|
32
|
+
end
|
33
|
+
|
34
|
+
# nothing went wrong this time
|
35
|
+
# so dump some useful info
|
36
|
+
log [
|
37
|
+
"Started #{label} Backend",
|
38
|
+
" Account balance: #{balance}"
|
39
|
+
], :init
|
40
|
+
end
|
41
|
+
|
42
|
+
def send_sms(msg)
|
43
|
+
begin
|
44
|
+
@api.send_message(msg.phone_number, msg.text)
|
45
|
+
super
|
46
|
+
|
47
|
+
# sending failed, for some reason. i've
|
48
|
+
# never seen this happen, so just log it
|
49
|
+
rescue Clickatell::API::Error => err
|
50
|
+
log_exception err, "Message sending FAILED"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def rack_app(env)
|
55
|
+
req = Rack::Request.new(env)
|
56
|
+
post = req.GET
|
57
|
+
|
58
|
+
# only a single (obscure) url is valid, which
|
59
|
+
# must be entered into the clickatell admin
|
60
|
+
return resp(404, "Not Found") unless\
|
61
|
+
req.path_info == "/sms/receive"
|
62
|
+
|
63
|
+
# check that the required parameters were
|
64
|
+
# provided, and abort if any are missing
|
65
|
+
return resp(500, "Missing Parameters") unless\
|
66
|
+
post["from"] && post["timestamp"] && post["text"]
|
67
|
+
|
68
|
+
begin
|
69
|
+
# attempt to parse the timestamp
|
70
|
+
# (it's a mySQL timestamp... ?!)
|
71
|
+
time = Date.strptime(
|
72
|
+
post["timestamp"],
|
73
|
+
"%Y-%m-%d%H:%M:%S")
|
74
|
+
|
75
|
+
# timestamp parsing fail
|
76
|
+
rescue Exception => err
|
77
|
+
log_exception err, "Invalid timestamp: #{post["timestamp"]}"
|
78
|
+
return resp(500, "Invalid timestamp")
|
79
|
+
end
|
80
|
+
|
81
|
+
# everything looks fine, so notify rubysms of
|
82
|
+
# the incoming message, and acknowledge clickatell
|
83
|
+
router.incoming(
|
84
|
+
SMS::Incoming.new(
|
85
|
+
self, post["from"], time, post["text"]))
|
86
|
+
resp(200, "Message Accepted")
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def resp(code, text)
|
92
|
+
[code, {"content-type" => "text/plain"}, text]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/lib/rubysms/backend/gsm.rb
CHANGED
@@ -10,7 +10,8 @@
|
|
10
10
|
# - rubygsms
|
11
11
|
# - rubygsm
|
12
12
|
begin
|
13
|
-
|
13
|
+
dir = File.dirname(__FILE__)
|
14
|
+
dev_dir = "#{dir}/../../../../rubygsm"
|
14
15
|
dev_path = "#{dev_dir}/lib/rubygsm.rb"
|
15
16
|
require File.expand_path(dev_path)
|
16
17
|
|
@@ -52,40 +53,37 @@ module SMS::Backend
|
|
52
53
|
end
|
53
54
|
|
54
55
|
def start
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
log "Signal strength is: #{str}"
|
71
|
-
|
72
|
-
# couldn't open the port. this usually means
|
73
|
-
# that the modem isn't plugged in to it...
|
74
|
-
rescue Errno::ENOENT, ArgumentError
|
75
|
-
log "Couldn't open #{@port}", :err
|
76
|
-
raise IOError
|
77
|
-
|
78
|
-
# something else went wrong
|
79
|
-
# while initializing the modem
|
80
|
-
rescue ::Gsm::Modem::Error => err
|
81
|
-
log ["Couldn't initialize the modem",
|
82
|
-
"RubyGSM Says: #{err.desc}"], :err
|
83
|
-
raise RuntimeError
|
84
|
-
end
|
56
|
+
begin
|
57
|
+
@gsm = ::Gsm::Modem.new(@port)
|
58
|
+
@gsm.use_pin(@pin) unless @pin.nil?
|
59
|
+
@gsm.receive method(:incoming)
|
60
|
+
str = @gsm.wait_for_network
|
61
|
+
|
62
|
+
#bands = @gsm.bands_available.join(", ")
|
63
|
+
#log "Modem supports: #{bands}"
|
64
|
+
|
65
|
+
# couldn't open the port. this usually means
|
66
|
+
# that the modem isn't plugged in to it...
|
67
|
+
rescue Errno::ENOENT, ArgumentError => err
|
68
|
+
log_exception err,\
|
69
|
+
"Couldn't connect to " +\
|
70
|
+
"modem on port: #{@port}"
|
85
71
|
|
86
|
-
|
87
|
-
|
72
|
+
# something else went wrong
|
73
|
+
# while initializing the modem
|
74
|
+
rescue ::Gsm::Modem::Error => err
|
75
|
+
log_exception err,\
|
76
|
+
"Couldn't initialize the " +\
|
77
|
+
"modem on port: #{@port}"
|
88
78
|
end
|
79
|
+
|
80
|
+
# nothing went wrong this time
|
81
|
+
# so dump some useful info
|
82
|
+
log [
|
83
|
+
"Started #{label} Backend",
|
84
|
+
" Signal strength: #{str}",
|
85
|
+
" Port: #{@gsm.port}"
|
86
|
+
], :init
|
89
87
|
end
|
90
88
|
|
91
89
|
def send_sms(msg)
|
@@ -94,8 +92,11 @@ module SMS::Backend
|
|
94
92
|
# send the message to the modem via rubygsm, and log
|
95
93
|
# if it failed. TODO: needs moar info from rubygsm
|
96
94
|
# on *why* sending failed
|
97
|
-
|
98
|
-
|
95
|
+
begin
|
96
|
+
@gsm.send_sms!(msg.recipient.phone_number, msg.text)
|
97
|
+
|
98
|
+
rescue => err
|
99
|
+
log_exception err, "Message sending FAILED"
|
99
100
|
end
|
100
101
|
end
|
101
102
|
|
data/lib/rubysms/backend/http.rb
CHANGED
@@ -41,7 +41,7 @@ module SMS::Backend
|
|
41
41
|
|
42
42
|
# add a screen log message, which is kind of
|
43
43
|
# a lie, because we haven't started anything yet
|
44
|
-
uri = "http://localhost:#{@port}"
|
44
|
+
uri = "http://localhost:#{@port}/"
|
45
45
|
log ["Started HTTP Offline Backend", "URI: #{uri}"], :init
|
46
46
|
|
47
47
|
# this is goodbye
|
@@ -390,7 +390,8 @@ SMS::Backend::HTTP::HTML = <<EOF
|
|
390
390
|
* reload the load to include it */
|
391
391
|
$("send").set("send", {
|
392
392
|
"url": "/" + session_id + "/send",
|
393
|
-
"onComplete":
|
393
|
+
"onComplete": function() {
|
394
|
+
update.delay(100); }
|
394
395
|
|
395
396
|
/* submit the form via ajax,
|
396
397
|
* and cancel the full-page */
|
data/lib/rubysms/logger.rb
CHANGED
data/lib/rubysms/router.rb
CHANGED
@@ -27,13 +27,6 @@ module SMS
|
|
27
27
|
def log_exception(error, prefix_message=nil)
|
28
28
|
msgs = [error.class, error.message]
|
29
29
|
|
30
|
-
# if a prefix was provided (to give a litle
|
31
|
-
# more info on what went wrong), prepend it
|
32
|
-
# to the output with a blank line
|
33
|
-
unless prefix_message.nil?
|
34
|
-
msg.shift prefix_message, ""
|
35
|
-
end
|
36
|
-
|
37
30
|
# add each line until the current frame is within
|
38
31
|
# rubysms (the remainder will just be from gems)
|
39
32
|
catch(:done) do
|
@@ -48,6 +41,15 @@ module SMS
|
|
48
41
|
end
|
49
42
|
end
|
50
43
|
|
44
|
+
# if a prefix was provided (to give a litle
|
45
|
+
# more info on what went wrong), prepend it
|
46
|
+
# to the output and indent the rest
|
47
|
+
unless prefix_message.nil?
|
48
|
+
msgs = [prefix_message] + msgs.collect do |msg|
|
49
|
+
" " + msg.to_s
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
51
53
|
@log.event msgs, :warn
|
52
54
|
end
|
53
55
|
|
@@ -74,7 +76,7 @@ module SMS
|
|
74
76
|
# the output, disable the "echoctl" option in your terminal
|
75
77
|
# (i added "stty -echoctl" to my .bashrc)
|
76
78
|
trap("INT") do
|
77
|
-
log "Shutting down", :
|
79
|
+
log "Shutting down", :stop
|
78
80
|
|
79
81
|
# fire the "stop" method of
|
80
82
|
# each application and backend
|
@@ -142,8 +144,9 @@ module SMS
|
|
142
144
|
# router.add_backend(:GSM, "/dev/ttyS0", 1234)
|
143
145
|
# router.add_backend(:GSM, "/dev/ttyS1", 5678)
|
144
146
|
# router.serve_forever
|
147
|
+
#
|
145
148
|
def add_backend(backend, *args)
|
146
|
-
|
149
|
+
|
147
150
|
# if a backend object was given, add it to this router
|
148
151
|
# TODO: this modifies the argument just slightly. would
|
149
152
|
# it be better to duplicate the object first?
|
@@ -153,8 +156,15 @@ module SMS
|
|
153
156
|
|
154
157
|
# if it's a named backend, spawn it (along
|
155
158
|
# with the optional arguments) and recurse
|
156
|
-
elsif backend.is_a?(Symbol)
|
157
|
-
add_backend SMS::Backend.create(backend, *args)
|
159
|
+
elsif backend.is_a?(Symbol) or backend.is_a?(String)
|
160
|
+
add_backend SMS::Backend.create(backend.to_sym, nil, *args)
|
161
|
+
|
162
|
+
# no idea what this
|
163
|
+
# backend is = boom
|
164
|
+
else
|
165
|
+
raise RuntimeError,
|
166
|
+
"Router#add_backend doesn't know what " +\
|
167
|
+
"to do with #{backend} (#{backend.klass})"
|
158
168
|
end
|
159
169
|
end
|
160
170
|
|
@@ -163,20 +173,38 @@ module SMS
|
|
163
173
|
def incoming(msg)
|
164
174
|
log_with_time "[#{msg.backend.label}] #{msg.sender.key}: #{msg.text} (#{msg.text.length})", :in
|
165
175
|
|
176
|
+
# iterate apps starting with
|
177
|
+
# the highest numeric priority
|
178
|
+
sorted = @apps.sort_by { |a| a.priority }.reverse
|
179
|
+
|
166
180
|
# notify each application of the message.
|
167
|
-
# they may or may not respond to it
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
181
|
+
# they may or may not respond to it, and
|
182
|
+
# may throw the :halt symbol to stop the
|
183
|
+
# notifying further apps. this is useful
|
184
|
+
# in conjunction with App.priority
|
185
|
+
catch(:halt) do
|
186
|
+
sorted.each do |app|
|
187
|
+
begin
|
188
|
+
catch(:continue) do
|
189
|
+
app.incoming msg
|
190
|
+
|
191
|
+
# if the app responded to the message, cancel
|
192
|
+
# further processing - unless :continue was
|
193
|
+
# thrown, which jumps over this check
|
194
|
+
unless msg.responses.empty?
|
195
|
+
throw :halt
|
196
|
+
end
|
197
|
+
end
|
176
198
|
|
177
|
-
|
178
|
-
|
179
|
-
|
199
|
+
# something went boom in the app
|
200
|
+
# log it, and continue with the next
|
201
|
+
rescue StandardError => err
|
202
|
+
log_exception(err)
|
203
|
+
|
204
|
+
# if msg.responses.empty?
|
205
|
+
# msg.respond("Sorry, there was an error while processing your message.")
|
206
|
+
# end
|
207
|
+
end
|
180
208
|
end
|
181
209
|
end
|
182
210
|
end
|
@@ -186,11 +214,15 @@ module SMS
|
|
186
214
|
def outgoing(msg)
|
187
215
|
log_with_time "[#{msg.backend.label}] #{msg.recipient.key}: #{msg.text} (#{msg.text.length})", :out
|
188
216
|
log("Outgoing message exceeds 140 characters", :warn) if msg.text.length > 140
|
189
|
-
|
217
|
+
|
218
|
+
# iterate apps starting with the loest numeric priority (the
|
219
|
+
# opposite to #incoming,so a :highest priority app gets the
|
220
|
+
# first look at incoming, and the last word on what goes out)
|
221
|
+
sorted = @apps.sort_by { |a| a.priority }
|
190
222
|
|
191
223
|
# notify each app of the outgoing sms
|
192
224
|
# note that the sending can still fail
|
193
|
-
|
225
|
+
sorted.each do |app|
|
194
226
|
app.outgoing msg
|
195
227
|
end
|
196
228
|
end
|
data/rubysms.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "rubysms"
|
3
|
-
s.version = "0.8.
|
4
|
-
s.date = "2009-
|
3
|
+
s.version = "0.8.2"
|
4
|
+
s.date = "2009-04-10"
|
5
5
|
s.summary = "Develop and deploy SMS applications with Ruby"
|
6
6
|
s.email = "amckaig@unicef.org"
|
7
7
|
s.homepage = "http://github.com/adammck/rubysms"
|
@@ -13,7 +13,8 @@ Gem::Specification.new do |s|
|
|
13
13
|
"README.rdoc",
|
14
14
|
"lib/rubysms.rb",
|
15
15
|
|
16
|
-
#
|
16
|
+
# drb clients
|
17
|
+
"bin/rubysms-gtk-drb-client",
|
17
18
|
"bin/rubysms-drb-client",
|
18
19
|
"lib/drb-client.glade",
|
19
20
|
|
@@ -28,6 +29,7 @@ Gem::Specification.new do |s|
|
|
28
29
|
|
29
30
|
# backends
|
30
31
|
"lib/rubysms/backend/cellphone.ico",
|
32
|
+
"lib/rubysms/backend/clickatell.rb",
|
31
33
|
"lib/rubysms/backend/drb.rb",
|
32
34
|
"lib/rubysms/backend/gsm.rb",
|
33
35
|
"lib/rubysms/backend/http.rb",
|
@@ -38,6 +40,7 @@ Gem::Specification.new do |s|
|
|
38
40
|
]
|
39
41
|
|
40
42
|
s.executables = [
|
43
|
+
"rubysms-gtk-drb-client",
|
41
44
|
"rubysms-drb-client"
|
42
45
|
]
|
43
46
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: adammck-rubysms
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Mckaig
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-04-10 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -45,6 +45,7 @@ dependencies:
|
|
45
45
|
description:
|
46
46
|
email: amckaig@unicef.org
|
47
47
|
executables:
|
48
|
+
- rubysms-gtk-drb-client
|
48
49
|
- rubysms-drb-client
|
49
50
|
extensions: []
|
50
51
|
|
@@ -54,6 +55,7 @@ files:
|
|
54
55
|
- rubysms.gemspec
|
55
56
|
- README.rdoc
|
56
57
|
- lib/rubysms.rb
|
58
|
+
- bin/rubysms-gtk-drb-client
|
57
59
|
- bin/rubysms-drb-client
|
58
60
|
- lib/drb-client.glade
|
59
61
|
- lib/rubysms/application.rb
|
@@ -64,6 +66,7 @@ files:
|
|
64
66
|
- lib/rubysms/person.rb
|
65
67
|
- lib/rubysms/thing.rb
|
66
68
|
- lib/rubysms/backend/cellphone.ico
|
69
|
+
- lib/rubysms/backend/clickatell.rb
|
67
70
|
- lib/rubysms/backend/drb.rb
|
68
71
|
- lib/rubysms/backend/gsm.rb
|
69
72
|
- lib/rubysms/backend/http.rb
|