librevox 0.2.1 → 0.3
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.
- data/README.md +12 -11
- data/TODO +5 -5
- data/lib/librevox.rb +16 -5
- data/lib/librevox/applications.rb +58 -51
- data/lib/librevox/command_socket.rb +5 -4
- data/lib/librevox/commands.rb +37 -13
- data/lib/librevox/listener/base.rb +28 -24
- data/lib/librevox/listener/inbound.rb +1 -1
- data/lib/librevox/listener/outbound.rb +13 -21
- data/lib/librevox/response.rb +4 -4
- data/librevox.gemspec +8 -5
- data/spec/helper.rb +82 -2
- data/spec/librevox/listener.rb +49 -64
- data/spec/librevox/listener/spec_inbound.rb +2 -2
- data/spec/librevox/listener/spec_outbound.rb +142 -97
- data/spec/librevox/spec_applications.rb +15 -10
- data/spec/librevox/spec_commands.rb +41 -6
- data/spec/librevox/spec_response.rb +1 -1
- metadata +63 -8
- data/spec/librevox/spec_command_socket.rb +0 -111
data/README.md
CHANGED
@@ -7,8 +7,7 @@ Librevox eventually came to life during a major rewrite of
|
|
7
7
|
[Freeswitcher](http://code.rubyists.com/projects/fs/). Not everything would
|
8
8
|
fit into the existing architecture, and I felt that a blank slate was needed.
|
9
9
|
Librevox and Freeswitcher looks much alike on the outside, but Librevox tries
|
10
|
-
to take a simpler approach on the inside.
|
11
|
-
software, and needs some real-life testing before seeing a regular release.
|
10
|
+
to take a simpler approach on the inside.
|
12
11
|
|
13
12
|
## Prerequisites
|
14
13
|
|
@@ -18,6 +17,9 @@ outbound event sockets before proceeding. The
|
|
18
17
|
[wiki page on mod_event_socket](http://wiki.freeswitch.org/wiki/Event_Socket) is
|
19
18
|
a good place to start.
|
20
19
|
|
20
|
+
Librevox doesn't work with Ruby versions below 1.9.1, and probably never will.
|
21
|
+
This is because Librevox uses 1.9-only features such as fibers.
|
22
|
+
|
21
23
|
## Inbound listener
|
22
24
|
|
23
25
|
To create an inbound listener, you should subclass `Librevox::Listener::Inbound`
|
@@ -85,22 +87,21 @@ When using applications that expect a reply, such as `play_and_get_digits`,
|
|
85
87
|
you have to use callbacks to read the value, as the function itself returns
|
86
88
|
immediately due to the async nature of EventMachine:
|
87
89
|
|
88
|
-
|
90
|
+
def session_initiated
|
89
91
|
answer
|
90
92
|
|
91
|
-
play_and_get_digits "enter-number.wav", "error.wav"
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
end
|
93
|
+
digit = play_and_get_digits "enter-number.wav", "error.wav"
|
94
|
+
puts "User pressed #{digit}"
|
95
|
+
playback "thanks-for-the-input.wav"
|
96
|
+
hangup
|
96
97
|
end
|
97
98
|
|
98
99
|
You can also use the commands defined in `Librevox::Command`, which, to avoid
|
99
|
-
namespace clashes, are
|
100
|
+
namespace clashes, are accessed through the `api` object:
|
100
101
|
|
101
|
-
|
102
|
+
def session_initiated
|
102
103
|
answer
|
103
|
-
api
|
104
|
+
api.status
|
104
105
|
end
|
105
106
|
|
106
107
|
They can be used in conjunction with applications, and do also take a block,
|
data/TODO
CHANGED
@@ -7,9 +7,6 @@
|
|
7
7
|
|
8
8
|
- filter events
|
9
9
|
|
10
|
-
- the execute_app/run_app business might be done better, simpler. e.g.
|
11
|
-
having to pass the block for every command becomes a bit tedious.
|
12
|
-
|
13
10
|
- move test suite from bacon -> test/unit.
|
14
11
|
|
15
12
|
- test how this works in async mode - I imagine that it might mess with
|
@@ -21,5 +18,8 @@
|
|
21
18
|
|
22
19
|
- maybe let commands handle the response, so something useful could be returned?
|
23
20
|
|
24
|
-
-
|
25
|
-
|
21
|
+
- extension sugar (0.5)
|
22
|
+
|
23
|
+
- graceful shutdown
|
24
|
+
|
25
|
+
- handle disconnects
|
data/lib/librevox.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
require 'logger'
|
2
|
+
require 'fiber'
|
2
3
|
require 'eventmachine'
|
3
4
|
require 'librevox/listener/inbound'
|
4
5
|
require 'librevox/listener/outbound'
|
6
|
+
require 'librevox/command_socket'
|
5
7
|
|
6
8
|
module Librevox
|
7
|
-
VERSION = "0.
|
9
|
+
VERSION = "0.3"
|
8
10
|
|
9
11
|
def self.options
|
10
12
|
@options ||= {
|
@@ -34,14 +36,18 @@ module Librevox
|
|
34
36
|
# run SomeListener
|
35
37
|
# run OtherListner
|
36
38
|
# end
|
37
|
-
def self.start
|
39
|
+
def self.start klass=nil, args={}, &block
|
38
40
|
logger.info "Starting Librevox"
|
39
|
-
|
41
|
+
|
42
|
+
EM.run do
|
43
|
+
trap("TERM") {stop}
|
44
|
+
trap("INT") {stop}
|
45
|
+
|
40
46
|
block_given? ? instance_eval(&block) : run(klass, args)
|
41
|
-
|
47
|
+
end
|
42
48
|
end
|
43
49
|
|
44
|
-
def self.run
|
50
|
+
def self.run klass, args={}
|
45
51
|
host = args.delete(:host) || "localhost"
|
46
52
|
port = args.delete(:port)
|
47
53
|
|
@@ -51,4 +57,9 @@ module Librevox
|
|
51
57
|
EM.start_server host, port || "8084", klass, args
|
52
58
|
end
|
53
59
|
end
|
60
|
+
|
61
|
+
def self.stop
|
62
|
+
logger.info "Terminating Librevox"
|
63
|
+
EM.stop
|
64
|
+
end
|
54
65
|
end
|
@@ -1,16 +1,15 @@
|
|
1
1
|
module Librevox
|
2
|
-
# All applications should call `
|
2
|
+
# All applications should call `application` with the following parameters:
|
3
3
|
#
|
4
4
|
# `name` - name of the application
|
5
|
-
# `args` - arguments as a string (optional)
|
6
|
-
# `params` -
|
5
|
+
# `args` - arguments as a string to be sent to FreeSWITCH (optional)
|
6
|
+
# `params` - parameters for tweaking the command (optional)
|
7
7
|
#
|
8
|
-
# Applications *must* pass on any eventual block passed to them.
|
9
8
|
module Applications
|
10
9
|
# Answers an incoming call or session.
|
11
10
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_answer
|
12
|
-
def answer
|
13
|
-
|
11
|
+
def answer
|
12
|
+
application "answer"
|
14
13
|
end
|
15
14
|
|
16
15
|
# Make an attended transfer
|
@@ -18,8 +17,8 @@ module Librevox
|
|
18
17
|
# att_xfer("user/davis")
|
19
18
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_att_xfer
|
20
19
|
# @todo Add support for origination_cancel_key
|
21
|
-
def att_xfer endpoint
|
22
|
-
|
20
|
+
def att_xfer endpoint
|
21
|
+
application "att_xfer", endpoint
|
23
22
|
end
|
24
23
|
|
25
24
|
# Binds an application to the specified call legs.
|
@@ -30,12 +29,12 @@ module Librevox
|
|
30
29
|
# :application => "execute_extension",
|
31
30
|
# :parameters => "dx XML features"
|
32
31
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_bind_meta_app
|
33
|
-
def bind_meta_app args={}
|
32
|
+
def bind_meta_app args={}
|
34
33
|
arg_string =
|
35
34
|
args.values_at(:key, :listen_to, :respond_on, :application).join(" ")
|
36
35
|
arg_string += "::#{args[:parameters]}" if args[:parameters]
|
37
36
|
|
38
|
-
|
37
|
+
application "bind_meta_app", arg_string
|
39
38
|
end
|
40
39
|
|
41
40
|
|
@@ -53,8 +52,8 @@ module Librevox
|
|
53
52
|
# @example With failover
|
54
53
|
# bridge ['user/coltrane', 'user/davis'], ['user/sun-ra', 'user/taylor']
|
55
54
|
# #=> user/coltrane,user/davis|user/sun-ra,user/taylor
|
56
|
-
# @see http://wiki.freeswitch.org/wiki/Misc.
|
57
|
-
def bridge *args
|
55
|
+
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_bridge
|
56
|
+
def bridge *args
|
58
57
|
variables = if args.last.is_a? Hash
|
59
58
|
# We need to sort the key/value pairs to facilitate testing.
|
60
59
|
# This can be removed once 1.8-compat is dropped.
|
@@ -71,7 +70,7 @@ module Librevox
|
|
71
70
|
args.join ","
|
72
71
|
end
|
73
72
|
|
74
|
-
|
73
|
+
application "bridge", variables + endpoints
|
75
74
|
end
|
76
75
|
|
77
76
|
# Deflect a call by sending a REFER. Takes a SIP URI as argument, rerouting
|
@@ -84,8 +83,8 @@ module Librevox
|
|
84
83
|
# deflect "sip:miles@davis.com"
|
85
84
|
# @see #redirect
|
86
85
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_deflect
|
87
|
-
def deflect uri
|
88
|
-
|
86
|
+
def deflect uri
|
87
|
+
application "deflect", uri
|
89
88
|
end
|
90
89
|
|
91
90
|
# Exports a channel variable from the A leg to the B leg. Variables and
|
@@ -99,10 +98,10 @@ module Librevox
|
|
99
98
|
# @example Only export to B-leg
|
100
99
|
# export "some_var", :local => false
|
101
100
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_export
|
102
|
-
def export var, args={}
|
101
|
+
def export var, args={}
|
103
102
|
nolocal = args[:local] == false ? "nolocal:" : "" # ugly!!111
|
104
103
|
|
105
|
-
|
104
|
+
application "export", "#{nolocal}#{var}"
|
106
105
|
end
|
107
106
|
|
108
107
|
# Generate TGML tones
|
@@ -111,8 +110,8 @@ module Librevox
|
|
111
110
|
# @example Generate a DTMF string
|
112
111
|
# gentones "0800500005"
|
113
112
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_gentones
|
114
|
-
def gentones tgml
|
115
|
-
|
113
|
+
def gentones tgml
|
114
|
+
application "gentones", tgml
|
116
115
|
end
|
117
116
|
|
118
117
|
# Hang up current channel
|
@@ -121,8 +120,8 @@ module Librevox
|
|
121
120
|
# @example Hang up with a reason
|
122
121
|
# hangup "USER_BUSY"
|
123
122
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_hangup
|
124
|
-
def hangup cause=""
|
125
|
-
|
123
|
+
def hangup cause=""
|
124
|
+
application "hangup", cause
|
126
125
|
end
|
127
126
|
|
128
127
|
# Plays a sound file and reads DTMF presses.
|
@@ -135,53 +134,53 @@ module Librevox
|
|
135
134
|
# :timeout => 5000,
|
136
135
|
# :regexp => '\d+'
|
137
136
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_play_and_get_digits
|
138
|
-
def play_and_get_digits file, invalid_file, args={}
|
137
|
+
def play_and_get_digits file, invalid_file, args={}
|
139
138
|
min = args[:min] || 1
|
140
139
|
max = args[:max] || 2
|
141
140
|
tries = args[:tries] || 3
|
142
141
|
terminators = args[:terminators] || "#"
|
143
142
|
timeout = args[:timeout] || 5000
|
144
|
-
|
143
|
+
variable = args[:variable] || "read_digits_var"
|
145
144
|
regexp = args[:regexp] || "\\d+"
|
146
145
|
|
147
146
|
args = [min, max, tries, timeout, terminators, file, invalid_file,
|
148
|
-
|
147
|
+
variable, regexp].join " "
|
149
148
|
|
150
|
-
params = {:
|
149
|
+
params = {:variable => variable}
|
151
150
|
|
152
|
-
|
151
|
+
application "play_and_get_digits", args, params
|
153
152
|
end
|
154
153
|
|
155
154
|
# Plays a sound file on the current channel.
|
156
155
|
# @example
|
157
156
|
# playback "/path/to/file.wav"
|
158
157
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_playback
|
159
|
-
def playback file
|
160
|
-
|
158
|
+
def playback file
|
159
|
+
application "playback", file
|
161
160
|
end
|
162
161
|
|
163
162
|
# Pre-answer establishes early media but does not answer.
|
164
163
|
# @example
|
165
164
|
# pre_anser
|
166
165
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_pre_answer
|
167
|
-
def pre_answer
|
168
|
-
|
166
|
+
def pre_answer
|
167
|
+
application "pre_answer"
|
169
168
|
end
|
170
169
|
|
171
170
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_read
|
172
|
-
def read file, args={}
|
171
|
+
def read file, args={}
|
173
172
|
min = args[:min] || 1
|
174
173
|
max = args[:max] || 2
|
175
174
|
terminators = args[:terminators] || "#"
|
176
175
|
timeout = args[:timeout] || 5000
|
177
|
-
|
176
|
+
variable = args[:variable] || "read_digits_var"
|
178
177
|
|
179
|
-
arg_string = "%s %s %s %s %s %s" % [min, max, file,
|
178
|
+
arg_string = "%s %s %s %s %s %s" % [min, max, file, variable, timeout,
|
180
179
|
terminators]
|
181
180
|
|
182
|
-
params = {:
|
181
|
+
params = {:variable => variable}
|
183
182
|
|
184
|
-
|
183
|
+
application "read", arg_string, params
|
185
184
|
end
|
186
185
|
|
187
186
|
# Records a message, with an optional limit on the maximum duration of the
|
@@ -191,56 +190,64 @@ module Librevox
|
|
191
190
|
# @example With 20 second limit
|
192
191
|
# record "/path/to/new/file.wac", :limit => 20
|
193
192
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_record
|
194
|
-
def record path, params={}
|
193
|
+
def record path, params={}
|
195
194
|
args = [path, params[:limit]].compact.join(" ")
|
196
|
-
|
195
|
+
application "record", args
|
197
196
|
end
|
198
197
|
|
199
198
|
# Redirect a channel to another endpoint. You must take care to not
|
200
199
|
# redirect incompatible channels, as that wont have the desired effect.
|
201
200
|
# I.e. if you redirect to a SIP URI, it should be a SIP channel.
|
202
201
|
#
|
203
|
-
# #
|
204
|
-
# has not been answered with the #
|
205
|
-
# been answered, use #
|
202
|
+
# {#redirect} can only be used on non-established calls, i.e. calls that
|
203
|
+
# has not been answered with the {#answer} application yet. If the call has
|
204
|
+
# been answered, use {#deflect} instead.
|
206
205
|
# @example
|
207
206
|
# redirect "sip:freddie@hubbard.org"
|
208
|
-
# @see #
|
207
|
+
# @see #deflect
|
209
208
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_redirect
|
210
|
-
def redirect uri
|
211
|
-
|
209
|
+
def redirect uri
|
210
|
+
application "redirect", uri
|
211
|
+
end
|
212
|
+
|
213
|
+
# Send SIP session respond code.
|
214
|
+
# @example Send 403 Forbidden
|
215
|
+
# respond 403
|
216
|
+
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_respond
|
217
|
+
def respond code
|
218
|
+
application "respond", code.to_s
|
212
219
|
end
|
213
220
|
|
214
221
|
# Sets a channel variable.
|
215
222
|
# @example
|
216
223
|
# set "some_var", "some value"
|
217
224
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_set
|
218
|
-
def set variable, value
|
219
|
-
|
225
|
+
def set variable, value
|
226
|
+
application "set", "#{variable}=#{value}"
|
220
227
|
end
|
221
228
|
|
222
229
|
# Transfers the current channel to a new context.
|
223
230
|
# @example
|
224
231
|
# transfer "new_context"
|
225
232
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_transfer
|
226
|
-
def transfer context
|
227
|
-
|
233
|
+
def transfer context
|
234
|
+
application "transfer", context
|
228
235
|
end
|
229
236
|
|
230
237
|
# Unbinds a previously bound key with bind_meta_app
|
231
238
|
# @example
|
232
239
|
# unbind_meta_app 3
|
233
240
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_unbind_meta_app
|
234
|
-
def unbind_meta_app key
|
235
|
-
|
241
|
+
def unbind_meta_app key
|
242
|
+
application "unbind_meta_app", key.to_s
|
236
243
|
end
|
237
244
|
|
238
245
|
# Unset a channel variable.
|
239
246
|
# @example
|
240
247
|
# unset "foo"
|
241
248
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_unset
|
242
|
-
def unset variable
|
243
|
-
|
249
|
+
def unset variable
|
250
|
+
application "unset", variable
|
244
251
|
end
|
245
252
|
end
|
246
253
|
end
|
@@ -7,7 +7,7 @@ module Librevox
|
|
7
7
|
class CommandSocket
|
8
8
|
include Librevox::Commands
|
9
9
|
|
10
|
-
def initialize
|
10
|
+
def initialize args={}
|
11
11
|
@server = args[:server] || "127.0.0.1"
|
12
12
|
@port = args[:port] || "8021"
|
13
13
|
@auth = args[:auth] || "ClueCon"
|
@@ -17,11 +17,12 @@ module Librevox
|
|
17
17
|
|
18
18
|
def connect
|
19
19
|
@socket = TCPSocket.open(@server, @port)
|
20
|
-
|
20
|
+
@socket.print "auth #{@auth}\n\n"
|
21
|
+
read_response
|
21
22
|
end
|
22
23
|
|
23
|
-
def
|
24
|
-
@socket.print "#{
|
24
|
+
def command *args
|
25
|
+
@socket.print "#{super(*args)}\n\n"
|
25
26
|
read_response
|
26
27
|
end
|
27
28
|
|
data/lib/librevox/commands.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Librevox
|
2
|
-
# All commands should call `
|
2
|
+
# All commands should call `command` with the following parameters:
|
3
3
|
#
|
4
4
|
# `name` - name of the command
|
5
5
|
# `args` - arguments as a string (optional)
|
@@ -8,17 +8,25 @@ module Librevox
|
|
8
8
|
module Commands
|
9
9
|
# Executes a generic API command, optionally taking arguments as string.
|
10
10
|
# @example
|
11
|
-
# socket.
|
11
|
+
# socket.command "fsctl", "hupall normal_clearing"
|
12
12
|
# @see http://wiki.freeswitch.org/wiki/Mod_commands
|
13
|
-
def
|
13
|
+
def command name, args=""
|
14
14
|
msg = "api #{name}"
|
15
|
-
msg << " #{args}"
|
15
|
+
msg << " #{args}" if args && !args.empty?
|
16
|
+
msg
|
17
|
+
end
|
16
18
|
|
17
|
-
|
19
|
+
def status
|
20
|
+
command "status"
|
18
21
|
end
|
19
22
|
|
20
|
-
|
21
|
-
|
23
|
+
# Access the hash table that comes with FreeSWITCH.
|
24
|
+
# @example
|
25
|
+
# socket.hash :insert, :realm, :key, "value"
|
26
|
+
# socket.hash :select, :realm, :key
|
27
|
+
# socket.hash :delete, :realm, :key
|
28
|
+
def hash *args
|
29
|
+
command "hash", args.join("/")
|
22
30
|
end
|
23
31
|
|
24
32
|
# Originate a new call.
|
@@ -26,7 +34,7 @@ module Librevox
|
|
26
34
|
# socket.originate 'sofia/user/coltrane', :extension => "1234"
|
27
35
|
# @example With :dialplan and :context
|
28
36
|
# @see http://wiki.freeswitch.org/wiki/Mod_commands#originate
|
29
|
-
def originate
|
37
|
+
def originate url, args={}
|
30
38
|
extension = args.delete(:extension)
|
31
39
|
dialplan = args.delete(:dialplan)
|
32
40
|
context = args.delete(:context)
|
@@ -35,19 +43,35 @@ module Librevox
|
|
35
43
|
|
36
44
|
arg_string = "{#{vars}}" +
|
37
45
|
[url, extension, dialplan, context].compact.join(" ")
|
38
|
-
|
46
|
+
command "originate", arg_string
|
39
47
|
end
|
40
48
|
|
41
49
|
# FreeSWITCH control messages.
|
42
50
|
# @example
|
43
51
|
# socket.fsctl :hupall, :normal_clearing
|
44
52
|
# @see http://wiki.freeswitch.org/wiki/Mod_commands#fsctl
|
45
|
-
def fsctl
|
46
|
-
|
53
|
+
def fsctl *args
|
54
|
+
command "fsctl", args.join(" ")
|
55
|
+
end
|
56
|
+
|
57
|
+
def hupall cause=nil
|
58
|
+
command "hupall", cause
|
47
59
|
end
|
48
60
|
|
49
|
-
|
50
|
-
|
61
|
+
# Park call.
|
62
|
+
# @example
|
63
|
+
# socket.uuid_park "592567a2-1be4-11df-a036-19bfdab2092f"
|
64
|
+
# @see http://wiki.freeswitch.org/wiki/Mod_commands#uuid_park
|
65
|
+
def uuid_park uuid
|
66
|
+
command "uuid_park", uuid
|
67
|
+
end
|
68
|
+
|
69
|
+
# Bridge two call legs together. At least one leg must be anwered.
|
70
|
+
# @example
|
71
|
+
# socket.uuid_bridge "592567a2-1be4-11df-a036-19bfdab2092f", "58b39c3a-1be4-11df-a035-19bfdab2092f"
|
72
|
+
# @see http://wiki.freeswitch.org/wiki/Mod_commands#uuid_bridge
|
73
|
+
def uuid_bridge uuid1, uuid2
|
74
|
+
command "uuid_bridge", "#{uuid1} #{uuid2}"
|
51
75
|
end
|
52
76
|
end
|
53
77
|
end
|