librevox 0.1.1 → 0.2
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 -4
- data/TODO +9 -8
- data/lib/librevox.rb +22 -5
- data/lib/librevox/applications.rb +139 -20
- data/lib/librevox/listener/base.rb +17 -13
- data/lib/librevox/listener/outbound.rb +16 -23
- data/librevox.gemspec +2 -2
- data/spec/librevox/listener.rb +26 -1
- data/spec/librevox/listener/spec_outbound.rb +8 -8
- data/spec/librevox/spec_applications.rb +115 -24
- metadata +2 -2
data/README.md
CHANGED
@@ -37,8 +37,8 @@ techniques:
|
|
37
37
|
require 'librevox'
|
38
38
|
|
39
39
|
class MyInbound < Librevox::Listener::Inbound
|
40
|
-
def on_event
|
41
|
-
puts "Got event: #{
|
40
|
+
def on_event e
|
41
|
+
puts "Got event: #{e.content[:event_name]}"
|
42
42
|
end
|
43
43
|
|
44
44
|
# You can add a hook for a certain event:
|
@@ -46,6 +46,12 @@ techniques:
|
|
46
46
|
# It is instance_eval'ed, so you can use your instance methods etc:
|
47
47
|
do_something
|
48
48
|
end
|
49
|
+
|
50
|
+
# If your hook block takes an argument, a Librevox::Response object for
|
51
|
+
# the given event is passed on:
|
52
|
+
event :channel_bridge do |e|
|
53
|
+
...
|
54
|
+
end
|
49
55
|
|
50
56
|
def do_something
|
51
57
|
...
|
@@ -64,15 +70,17 @@ but it only receives events related to that given session.
|
|
64
70
|
### Dialplan
|
65
71
|
|
66
72
|
When a call is made and Freeswitch connects to the outbound event listener,
|
67
|
-
|
73
|
+
`session_initiated` is called. This is where you set up your dialplan:
|
68
74
|
|
69
|
-
|
75
|
+
def session_initiated
|
70
76
|
answer
|
71
77
|
set "some_var", "some value"
|
72
78
|
playback "path/to/file"
|
73
79
|
hangup
|
74
80
|
end
|
75
81
|
|
82
|
+
All channel variables are available as a hash named `session`.
|
83
|
+
|
76
84
|
When using applications that expect a reply, such as `play_and_get_digits`,
|
77
85
|
you have to use callbacks to read the value, as the function itself returns
|
78
86
|
immediately due to the async nature of EventMachine:
|
data/TODO
CHANGED
@@ -1,10 +1,17 @@
|
|
1
|
+
- inbound listener: only subscribe to events w/ hooks, unless `events :all`
|
2
|
+
specified
|
3
|
+
|
4
|
+
- map more commands/applications.
|
5
|
+
|
6
|
+
- check command/application input and raise ArgumentError, possibly.
|
7
|
+
|
8
|
+
- filter events
|
9
|
+
|
1
10
|
- the execute_app/run_app business might be done better, simpler. e.g.
|
2
11
|
having to pass the block for every command becomes a bit tedious.
|
3
12
|
|
4
13
|
- move test suite from bacon -> test/unit.
|
5
14
|
|
6
|
-
- map more commands/applications.
|
7
|
-
|
8
15
|
- test how this works in async mode - I imagine that it might mess with
|
9
16
|
@command_queue
|
10
17
|
|
@@ -12,13 +19,7 @@
|
|
12
19
|
do e.g. originate("user/coltrane", :endpoint => bridge("user/davis")) and
|
13
20
|
it would translate to "originate user/coltrane &bridge(user/davis)"
|
14
21
|
|
15
|
-
- add logger.
|
16
|
-
|
17
|
-
- check command/application input and raise ArgumentError.
|
18
|
-
|
19
22
|
- maybe let commands handle the response, so something useful could be returned?
|
20
23
|
|
21
24
|
- possibly catch all exceptions, so everything doesn't die? or should this be
|
22
25
|
be the users responsibilty?
|
23
|
-
|
24
|
-
- filter events
|
data/lib/librevox.rb
CHANGED
@@ -1,9 +1,27 @@
|
|
1
|
+
require 'logger'
|
1
2
|
require 'eventmachine'
|
2
3
|
require 'librevox/listener/inbound'
|
3
4
|
require 'librevox/listener/outbound'
|
4
5
|
|
5
6
|
module Librevox
|
6
|
-
VERSION = "0.
|
7
|
+
VERSION = "0.2"
|
8
|
+
|
9
|
+
def self.options
|
10
|
+
@options ||= {
|
11
|
+
:log_file => STDOUT,
|
12
|
+
:log_level => Logger::INFO
|
13
|
+
}
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.logger
|
17
|
+
@logger ||= logger!
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.logger!
|
21
|
+
logger = Logger.new(options[:log_file])
|
22
|
+
logger.level = options[:log_level]
|
23
|
+
logger
|
24
|
+
end
|
7
25
|
|
8
26
|
# When called without a block, it will start the listener that is passed as
|
9
27
|
# first argument:
|
@@ -17,6 +35,7 @@ module Librevox
|
|
17
35
|
# run OtherListner
|
18
36
|
# end
|
19
37
|
def self.start(klass=nil, args={}, &block)
|
38
|
+
logger.info "Starting Librevox"
|
20
39
|
EM.run {
|
21
40
|
block_given? ? instance_eval(&block) : run(klass, args)
|
22
41
|
}
|
@@ -27,11 +46,9 @@ module Librevox
|
|
27
46
|
port = args.delete(:port)
|
28
47
|
|
29
48
|
if klass.ancestors.include? Librevox::Listener::Inbound
|
30
|
-
port
|
31
|
-
EM.connect host, port, klass, args
|
49
|
+
EM.connect host, port || "8021", klass, args
|
32
50
|
elsif klass.ancestors.include? Librevox::Listener::Outbound
|
33
|
-
port
|
34
|
-
EM.start_server host, port, klass, args
|
51
|
+
EM.start_server host, port || "8084", klass, args
|
35
52
|
end
|
36
53
|
end
|
37
54
|
end
|
@@ -8,10 +8,20 @@ module Librevox
|
|
8
8
|
# Applications *must* pass on any eventual block passed to them.
|
9
9
|
module Applications
|
10
10
|
# Answers an incoming call or session.
|
11
|
-
|
11
|
+
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_answer
|
12
|
+
def answer &b
|
12
13
|
execute_app "answer", &b
|
13
14
|
end
|
14
15
|
|
16
|
+
# Make an attended transfer
|
17
|
+
# @example
|
18
|
+
# att_xfer("user/davis")
|
19
|
+
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_att_xfer
|
20
|
+
# @todo Add support for origination_cancel_key
|
21
|
+
def att_xfer endpoint, &b
|
22
|
+
execute_app "att_xfer", endpoint, &b
|
23
|
+
end
|
24
|
+
|
15
25
|
# Binds an application to the specified call legs.
|
16
26
|
# @example
|
17
27
|
# bind_meta_app :key => 2,
|
@@ -20,28 +30,98 @@ module Librevox
|
|
20
30
|
# :application => "execute_extension",
|
21
31
|
# :parameters => "dx XML features"
|
22
32
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_bind_meta_app
|
23
|
-
def bind_meta_app
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
application = args[:application]
|
28
|
-
parameters = args[:parameters] ? "::#{args[:parameters]}" : ""
|
29
|
-
|
30
|
-
arg_string = "%s %s %s %s%s" % [key, listen_to, respond_on, application,
|
31
|
-
parameters]
|
33
|
+
def bind_meta_app args={}, &b
|
34
|
+
arg_string =
|
35
|
+
args.values_at(:key, :listen_to, :respond_on, :application).join(" ")
|
36
|
+
arg_string += "::#{args[:parameters]}" if args[:parameters]
|
32
37
|
|
33
38
|
execute_app "bind_meta_app", arg_string, &b
|
34
39
|
end
|
35
40
|
|
36
|
-
|
41
|
+
|
42
|
+
# Bridges an incoming call to an endpoint, optionally taking an array of
|
43
|
+
# channel variables to set. If given an array of arrays, each contained
|
44
|
+
# array of endpoints will be called simultaneously, with the next array
|
45
|
+
# of endpoints as failover. See the examples below for different constructs
|
46
|
+
# and the callstring it sends to FreeSWITCH.
|
37
47
|
# @example
|
38
48
|
# bridge "user/coltrane", "user/backup-office"
|
49
|
+
# #=> user/coltrane,user/backup-office
|
50
|
+
# @example With channel variables
|
51
|
+
# bridge "user/coltrane", "user/backup-office", :some_var => "value"
|
52
|
+
# #=> {some_var=value}user/coltrane,user/backup-office
|
53
|
+
# @example With failover
|
54
|
+
# bridge ['user/coltrane', 'user/davis'], ['user/sun-ra', 'user/taylor']
|
55
|
+
# #=> user/coltrane,user/davis|user/sun-ra,user/taylor
|
39
56
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_bridgecall
|
40
|
-
def bridge
|
41
|
-
|
57
|
+
def bridge *args, &b
|
58
|
+
variables = if args.last.is_a? Hash
|
59
|
+
# We need to sort the key/value pairs to facilitate testing.
|
60
|
+
# This can be removed once 1.8-compat is dropped.
|
61
|
+
key_value_pairs = args.pop.sort {|x,y| x.to_s <=> y.to_s}
|
62
|
+
key_value_pairs.map! {|k,v| "#{k}=#{v}"}
|
63
|
+
"{#{key_value_pairs.join(",")}}"
|
64
|
+
else
|
65
|
+
""
|
66
|
+
end
|
67
|
+
|
68
|
+
endpoints = if args.first.is_a? Array
|
69
|
+
args.map {|e| e.join(",")}.join("|")
|
70
|
+
else
|
71
|
+
args.join ","
|
72
|
+
end
|
73
|
+
|
74
|
+
execute_app "bridge", variables + endpoints, &b
|
75
|
+
end
|
76
|
+
|
77
|
+
# Deflect a call by sending a REFER. Takes a SIP URI as argument, rerouting
|
78
|
+
# the call to that SIP URI.
|
79
|
+
#
|
80
|
+
# Beware that REFER only can be used on established calls. If a call hasn't
|
81
|
+
# been established with e.g. the {#answer} application, you should use
|
82
|
+
# {#redirect} instead.
|
83
|
+
# @example
|
84
|
+
# deflect "sip:miles@davis.com"
|
85
|
+
# @see #redirect
|
86
|
+
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_deflect
|
87
|
+
def deflect uri, &b
|
88
|
+
execute_app "deflect", uri, &b
|
89
|
+
end
|
90
|
+
|
91
|
+
# Exports a channel variable from the A leg to the B leg. Variables and
|
92
|
+
# their values will be replicated in any new channels created from the one
|
93
|
+
# export was called.
|
94
|
+
#
|
95
|
+
# Set :local => false if the variable should only be exported to the B-leg.
|
96
|
+
#
|
97
|
+
# @example
|
98
|
+
# export "some_var"
|
99
|
+
# @example Only export to B-leg
|
100
|
+
# export "some_var", :local => false
|
101
|
+
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_export
|
102
|
+
def export var, args={}, &b
|
103
|
+
nolocal = args[:local] == false ? "nolocal:" : "" # ugly!!111
|
104
|
+
|
105
|
+
execute_app "export", "#{nolocal}#{var}", &b
|
106
|
+
end
|
107
|
+
|
108
|
+
# Generate TGML tones
|
109
|
+
# @example Generate a 500ms beep at 800Hz
|
110
|
+
# gentones "%(500,0,800)"
|
111
|
+
# @example Generate a DTMF string
|
112
|
+
# gentones "0800500005"
|
113
|
+
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_gentones
|
114
|
+
def gentones tgml, &b
|
115
|
+
execute_app "gentones", tgml, &b
|
42
116
|
end
|
43
117
|
|
44
|
-
|
118
|
+
# Hang up current channel
|
119
|
+
# @example
|
120
|
+
# hangup
|
121
|
+
# @example Hang up with a reason
|
122
|
+
# hangup "USER_BUSY"
|
123
|
+
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_hangup
|
124
|
+
def hangup cause="", &b
|
45
125
|
execute_app "hangup", cause, &b
|
46
126
|
end
|
47
127
|
|
@@ -55,7 +135,7 @@ module Librevox
|
|
55
135
|
# :timeout => 5000,
|
56
136
|
# :regexp => '\d+'
|
57
137
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_play_and_get_digits
|
58
|
-
def play_and_get_digits
|
138
|
+
def play_and_get_digits file, invalid_file, args={}, &b
|
59
139
|
min = args[:min] || 1
|
60
140
|
max = args[:max] || 2
|
61
141
|
tries = args[:tries] || 3
|
@@ -76,12 +156,20 @@ module Librevox
|
|
76
156
|
# @example
|
77
157
|
# playback "/path/to/file.wav"
|
78
158
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_playback
|
79
|
-
def playback
|
159
|
+
def playback file, &b
|
80
160
|
execute_app "playback", file, &b
|
81
161
|
end
|
82
162
|
|
163
|
+
# Pre-answer establishes early media but does not answer.
|
164
|
+
# @example
|
165
|
+
# pre_anser
|
166
|
+
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_pre_answer
|
167
|
+
def pre_answer &b
|
168
|
+
execute_app "pre_answer", &b
|
169
|
+
end
|
170
|
+
|
83
171
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_read
|
84
|
-
def read
|
172
|
+
def read file, args={}, &b
|
85
173
|
min = args[:min] || 1
|
86
174
|
max = args[:max] || 2
|
87
175
|
terminators = args[:terminators] || "#"
|
@@ -103,16 +191,31 @@ module Librevox
|
|
103
191
|
# @example With 20 second limit
|
104
192
|
# record "/path/to/new/file.wac", :limit => 20
|
105
193
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_record
|
106
|
-
def record
|
194
|
+
def record path, params={}, &b
|
107
195
|
args = [path, params[:limit]].compact.join(" ")
|
108
196
|
execute_app "record", args, &b
|
109
197
|
end
|
110
198
|
|
199
|
+
# Redirect a channel to another endpoint. You must take care to not
|
200
|
+
# redirect incompatible channels, as that wont have the desired effect.
|
201
|
+
# I.e. if you redirect to a SIP URI, it should be a SIP channel.
|
202
|
+
#
|
203
|
+
# #{redirect} can only be used on non-established calls, i.e. calls that
|
204
|
+
# has not been answered with the #{answer} application yet. If the call has
|
205
|
+
# been answered, use #{deflect} instead.
|
206
|
+
# @example
|
207
|
+
# redirect "sip:freddie@hubbard.org"
|
208
|
+
# @see #{deflect}
|
209
|
+
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_redirect
|
210
|
+
def redirect uri, &b
|
211
|
+
execute_app "redirect", uri, &b
|
212
|
+
end
|
213
|
+
|
111
214
|
# Sets a channel variable.
|
112
215
|
# @example
|
113
216
|
# set "some_var", "some value"
|
114
217
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_set
|
115
|
-
def set
|
218
|
+
def set variable, value, &b
|
116
219
|
execute_app "set", "#{variable}=#{value}", &b
|
117
220
|
end
|
118
221
|
|
@@ -120,8 +223,24 @@ module Librevox
|
|
120
223
|
# @example
|
121
224
|
# transfer "new_context"
|
122
225
|
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_transfer
|
123
|
-
def transfer
|
226
|
+
def transfer context, &b
|
124
227
|
execute_app "transfer", context, &b
|
125
228
|
end
|
229
|
+
|
230
|
+
# Unbinds a previously bound key with bind_meta_app
|
231
|
+
# @example
|
232
|
+
# unbind_meta_app 3
|
233
|
+
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_unbind_meta_app
|
234
|
+
def unbind_meta_app key, &b
|
235
|
+
execute_app "unbind_meta_app", key.to_s, &b
|
236
|
+
end
|
237
|
+
|
238
|
+
# Unset a channel variable.
|
239
|
+
# @example
|
240
|
+
# unset "foo"
|
241
|
+
# @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_unset
|
242
|
+
def unset variable, &b
|
243
|
+
execute_app "unset", variable, &b
|
244
|
+
end
|
126
245
|
end
|
127
246
|
end
|
@@ -38,7 +38,7 @@ module Librevox
|
|
38
38
|
|
39
39
|
def run_cmd(cmd, &block)
|
40
40
|
send_data "#{cmd}\n\n"
|
41
|
-
@
|
41
|
+
@command_queue << (block || lambda {})
|
42
42
|
end
|
43
43
|
|
44
44
|
attr_accessor :response
|
@@ -46,33 +46,37 @@ module Librevox
|
|
46
46
|
|
47
47
|
def post_init
|
48
48
|
@command_delegate = CommandDelegate.new(self)
|
49
|
-
@
|
49
|
+
@command_queue = []
|
50
50
|
end
|
51
51
|
|
52
52
|
def receive_request(header, content)
|
53
53
|
@response = Librevox::Response.new(header, content)
|
54
54
|
|
55
55
|
if response.event?
|
56
|
-
on_event
|
57
|
-
|
58
|
-
elsif response.api_response? && @
|
59
|
-
|
56
|
+
on_event response.dup
|
57
|
+
invoke_event_hooks
|
58
|
+
elsif response.api_response? && @command_queue.any?
|
59
|
+
invoke_command_queue
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
63
|
# override
|
64
|
-
def on_event
|
64
|
+
def on_event(event)
|
65
65
|
end
|
66
66
|
|
67
|
+
alias :done :close_connection_after_writing
|
68
|
+
|
67
69
|
private
|
68
|
-
def
|
69
|
-
self.class.hooks.each
|
70
|
-
|
71
|
-
|
70
|
+
def invoke_event_hooks
|
71
|
+
self.class.hooks.each {|name,block|
|
72
|
+
if name == response.event.downcase.to_sym
|
73
|
+
instance_exec response.dup, &block
|
74
|
+
end
|
75
|
+
}
|
72
76
|
end
|
73
77
|
|
74
|
-
def
|
75
|
-
block = @
|
78
|
+
def invoke_command_queue
|
79
|
+
block = @command_queue.shift
|
76
80
|
block.call(response)
|
77
81
|
end
|
78
82
|
end
|
@@ -4,14 +4,6 @@ require 'librevox/applications'
|
|
4
4
|
module Librevox
|
5
5
|
module Listener
|
6
6
|
class Outbound < Base
|
7
|
-
class << self
|
8
|
-
attr_reader :session_callback
|
9
|
-
|
10
|
-
def session(&block)
|
11
|
-
@session_callback = block
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
7
|
include Librevox::Applications
|
16
8
|
|
17
9
|
def execute_app(app, args="", params={}, &block)
|
@@ -25,10 +17,10 @@ module Librevox
|
|
25
17
|
@read_channel_var = params[:read_var]
|
26
18
|
|
27
19
|
if @read_channel_var
|
28
|
-
@
|
20
|
+
@application_queue << lambda {update_session}
|
29
21
|
end
|
30
22
|
|
31
|
-
@
|
23
|
+
@application_queue << (block || lambda {})
|
32
24
|
end
|
33
25
|
|
34
26
|
# This should probably be in Application#sendmsg instead.
|
@@ -37,46 +29,47 @@ module Librevox
|
|
37
29
|
end
|
38
30
|
|
39
31
|
attr_accessor :session
|
32
|
+
|
33
|
+
# Called when a new session is initiated.
|
34
|
+
def session_initiated
|
35
|
+
end
|
40
36
|
|
41
37
|
def post_init
|
42
38
|
super
|
43
39
|
@session = nil
|
44
|
-
@
|
40
|
+
@application_queue = []
|
45
41
|
|
46
42
|
send_data "connect\n\n"
|
47
43
|
send_data "myevents\n\n"
|
48
|
-
@
|
44
|
+
@application_queue << lambda {}
|
49
45
|
send_data "linger\n\n"
|
50
|
-
@
|
46
|
+
@application_queue << lambda {}
|
51
47
|
end
|
52
48
|
|
53
49
|
def receive_request(*args)
|
54
50
|
super(*args)
|
55
51
|
|
56
52
|
if session.nil?
|
57
|
-
@session = response
|
58
|
-
|
53
|
+
@session = response.headers
|
54
|
+
session_initiated
|
59
55
|
elsif response.event? && response.event == "CHANNEL_DATA"
|
60
|
-
@session = response
|
56
|
+
@session = response.content
|
61
57
|
resume_with_channel_var
|
62
58
|
elsif response.command_reply? && !response.event?
|
63
|
-
@
|
59
|
+
@application_queue.shift.call if @application_queue.any?
|
64
60
|
end
|
65
61
|
end
|
66
62
|
|
67
63
|
def resume_with_channel_var
|
68
64
|
if @read_channel_var
|
69
65
|
variable = "variable_#{@read_channel_var}".to_sym
|
70
|
-
value = @session
|
71
|
-
@
|
66
|
+
value = @session[variable]
|
67
|
+
@application_queue.shift.call(value) if @application_queue.any?
|
72
68
|
end
|
73
69
|
end
|
74
70
|
|
75
71
|
def update_session
|
76
|
-
send_data("api uuid_dump #{session
|
77
|
-
end
|
78
|
-
|
79
|
-
def session_initiated
|
72
|
+
send_data("api uuid_dump #{session[:unique_id]}\n\n")
|
80
73
|
end
|
81
74
|
end
|
82
75
|
end
|
data/librevox.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "librevox"
|
3
|
-
s.version = "0.
|
4
|
-
s.date = "2010-01-
|
3
|
+
s.version = "0.2"
|
4
|
+
s.date = "2010-01-26"
|
5
5
|
s.summary = "Ruby library for interacting with FreeSWITCH."
|
6
6
|
s.email = "harry@vangberg.name"
|
7
7
|
s.homepage = "http://github.com/ichverstehe/librevox"
|
data/spec/librevox/listener.rb
CHANGED
@@ -25,13 +25,18 @@ shared "events" do
|
|
25
25
|
|
26
26
|
@class.event(:some_event) {send_data "something"}
|
27
27
|
@class.event(:other_event) {send_data "something else"}
|
28
|
+
@class.event(:hook_with_arg) {|e| send_data "got event arg: #{e.object_id}"}
|
29
|
+
|
30
|
+
def @listener.on_event(e)
|
31
|
+
send_data "from on_event: #{e.object_id}"
|
32
|
+
end
|
28
33
|
|
29
34
|
# Establish session
|
30
35
|
@listener.receive_data("Content-Length: 0\nTest: Testing\n\n")
|
31
36
|
end
|
32
37
|
|
33
38
|
should "add event hook" do
|
34
|
-
@class.hooks.size.should ==
|
39
|
+
@class.hooks.size.should == 3
|
35
40
|
end
|
36
41
|
|
37
42
|
should "execute callback for event" do
|
@@ -42,11 +47,28 @@ shared "events" do
|
|
42
47
|
@listener.read_data.should == "something"
|
43
48
|
end
|
44
49
|
|
50
|
+
should "pass response duplicate as arg to hook block" do
|
51
|
+
@listener.receive_data("Content-Length: 25\n\nEvent-Name: HOOK_WITH_ARG\n\n")
|
52
|
+
reply = @listener.read_data
|
53
|
+
reply.should =~ /^got event arg: /
|
54
|
+
reply.should.not =~ /^got event arg: #{@listener.response.object_id}$/
|
55
|
+
end
|
56
|
+
|
45
57
|
should "expose response as event" do
|
46
58
|
@listener.receive_data("Content-Length: 23\n\nEvent-Name: OTHER_EVENT\n\n")
|
47
59
|
@listener.event.class.should == Librevox::Response
|
48
60
|
@listener.event.content[:event_name].should == "OTHER_EVENT"
|
49
61
|
end
|
62
|
+
|
63
|
+
should "call on_event" do
|
64
|
+
@listener.receive_data("Content-Length: 23\n\nEvent-Name: THIRD_EVENT\n\n")
|
65
|
+
@listener.read_data.should =~ /^from on_event/
|
66
|
+
end
|
67
|
+
|
68
|
+
should "call on_event with response duplicate as argument" do
|
69
|
+
@listener.receive_data("Content-Length: 23\n\nEvent-Name: THIRD_EVENT\n\n")
|
70
|
+
@listener.read_data.should.not =~ /^from on_event: #{@listener.response.object_id}$/
|
71
|
+
end
|
50
72
|
end
|
51
73
|
|
52
74
|
module Librevox::Commands
|
@@ -66,6 +88,9 @@ shared "api commands" do
|
|
66
88
|
describe "multiple api commands" do
|
67
89
|
before do
|
68
90
|
@listener.outgoing_data.clear
|
91
|
+
|
92
|
+
def @listener.on_event(e) end # Don't send anything, kthx.
|
93
|
+
|
69
94
|
@class.event(:api_test) {
|
70
95
|
api :sample_cmd, "foo" do
|
71
96
|
api :sample_cmd, "foo", "bar baz"
|
@@ -10,7 +10,7 @@ module Librevox::Applications
|
|
10
10
|
end
|
11
11
|
|
12
12
|
class OutboundTestListener < Librevox::Listener::Outbound
|
13
|
-
|
13
|
+
def session_initiated
|
14
14
|
send_data "session was initiated"
|
15
15
|
end
|
16
16
|
end
|
@@ -34,7 +34,7 @@ describe "Outbound listener" do
|
|
34
34
|
end
|
35
35
|
|
36
36
|
should "establish a session" do
|
37
|
-
@listener.session.class.should.equal
|
37
|
+
@listener.session.class.should.equal Hash
|
38
38
|
end
|
39
39
|
|
40
40
|
should "call session callback after establishing new session" do
|
@@ -42,7 +42,7 @@ describe "Outbound listener" do
|
|
42
42
|
end
|
43
43
|
|
44
44
|
should "make channel variables available through session" do
|
45
|
-
@listener.session
|
45
|
+
@listener.session[:caller_caller_id_number].should.equal "8675309"
|
46
46
|
end
|
47
47
|
|
48
48
|
behaves_like "events"
|
@@ -54,7 +54,7 @@ describe "Outbound listener" do
|
|
54
54
|
end
|
55
55
|
|
56
56
|
class OutboundListenerWithNestedApps < Librevox::Listener::Outbound
|
57
|
-
|
57
|
+
def session_initiated
|
58
58
|
sample_app "foo" do
|
59
59
|
sample_app "bar"
|
60
60
|
end
|
@@ -106,7 +106,7 @@ module Librevox::Applications
|
|
106
106
|
end
|
107
107
|
|
108
108
|
class OutboundListenerWithReader < Librevox::Listener::Outbound
|
109
|
-
|
109
|
+
def session_initiated
|
110
110
|
reader_app do |data|
|
111
111
|
send_data "read this: #{data}"
|
112
112
|
end
|
@@ -136,7 +136,7 @@ describe "Outbound listener with app reading data" do
|
|
136
136
|
should "update session with new data" do
|
137
137
|
@listener.receive_data("Content-Type: command/reply\nContent-Length: 3\n\n+OK\n\n")
|
138
138
|
@listener.receive_data("Content-Type: command/reply\nContent-Length: 44\n\nEvent-Name: CHANNEL_DATA\nSession-Var: Second\n\n")
|
139
|
-
@listener.session
|
139
|
+
@listener.session[:session_var].should == "Second"
|
140
140
|
end
|
141
141
|
|
142
142
|
should "pass value of channel variable to block" do
|
@@ -148,7 +148,7 @@ end
|
|
148
148
|
|
149
149
|
class OutboundListenerWithNonNestedApps < Librevox::Listener::Outbound
|
150
150
|
attr_reader :queue
|
151
|
-
|
151
|
+
def session_initiated
|
152
152
|
sample_app "foo"
|
153
153
|
reader_app do |data|
|
154
154
|
send_data "the end: #{data}"
|
@@ -190,7 +190,7 @@ module Librevox::Commands
|
|
190
190
|
end
|
191
191
|
|
192
192
|
class OutboundListenerWithAppsAndApi < Librevox::Listener::Outbound
|
193
|
-
|
193
|
+
def session_initiated
|
194
194
|
sample_app "foo" do
|
195
195
|
api :sample_cmd, "bar" do
|
196
196
|
sample_app "baz"
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec/helper'
|
2
2
|
require 'librevox/applications'
|
3
3
|
|
4
|
-
module
|
4
|
+
module AppTest
|
5
5
|
include Librevox::Applications
|
6
6
|
|
7
7
|
extend self
|
@@ -17,16 +17,24 @@ module ApplicationTest
|
|
17
17
|
end
|
18
18
|
|
19
19
|
describe Librevox::Applications do
|
20
|
-
|
20
|
+
describe "answer" do
|
21
|
+
should "answer" do
|
22
|
+
app = AppTest.answer
|
23
|
+
app[:name].should == "answer"
|
24
|
+
end
|
25
|
+
end
|
21
26
|
|
22
|
-
|
23
|
-
|
24
|
-
|
27
|
+
describe "att_xfer" do
|
28
|
+
should "transfer to endpoint" do
|
29
|
+
app = AppTest.att_xfer("user/davis")
|
30
|
+
app[:name].should == "att_xfer"
|
31
|
+
app[:args].should == "user/davis"
|
32
|
+
end
|
25
33
|
end
|
26
34
|
|
27
35
|
describe "bind_meta_app" do
|
28
36
|
should "bind meta app" do
|
29
|
-
app =
|
37
|
+
app = AppTest.bind_meta_app :key => "2",
|
30
38
|
:listen_to => :a,
|
31
39
|
:respond_on => :s,
|
32
40
|
:application => "hangup"
|
@@ -36,7 +44,7 @@ describe Librevox::Applications do
|
|
36
44
|
end
|
37
45
|
|
38
46
|
should "bind meta app with parameters" do
|
39
|
-
app =
|
47
|
+
app = AppTest.bind_meta_app :key => "2",
|
40
48
|
:listen_to => :a,
|
41
49
|
:respond_on => :s,
|
42
50
|
:application => "execute_extension",
|
@@ -47,33 +55,85 @@ describe Librevox::Applications do
|
|
47
55
|
end
|
48
56
|
end
|
49
57
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
58
|
+
describe "bridge" do
|
59
|
+
should "bridge to endpoints" do
|
60
|
+
app = AppTest.bridge('user/coltrane')
|
61
|
+
app[:name].should == "bridge"
|
62
|
+
app[:args].should == 'user/coltrane'
|
63
|
+
|
64
|
+
app = AppTest.bridge('user/coltrane', 'user/davis')
|
65
|
+
app[:args].should == 'user/coltrane,user/davis'
|
66
|
+
end
|
67
|
+
|
68
|
+
should "bridge with variables" do
|
69
|
+
app = AppTest.bridge('user/coltrane', 'user/davis', :foo => 'bar', :lol => 'cat')
|
70
|
+
app[:name].should == "bridge"
|
71
|
+
|
72
|
+
# fragile. hashes are not ordered in ruby 1.8
|
73
|
+
app[:args].should == "{foo=bar,lol=cat}user/coltrane,user/davis"
|
74
|
+
end
|
75
|
+
|
76
|
+
should "bridge with failover" do
|
77
|
+
app = AppTest.bridge(
|
78
|
+
['user/coltrane', 'user/davis'], ['user/sun-ra', 'user/taylor']
|
79
|
+
)
|
80
|
+
|
81
|
+
app[:name].should == "bridge"
|
82
|
+
app[:args].should == "user/coltrane,user/davis|user/sun-ra,user/taylor"
|
83
|
+
end
|
84
|
+
|
85
|
+
# should "bridge with per endpoint variables" do
|
86
|
+
# end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "deflect" do
|
90
|
+
should "deflect call" do
|
91
|
+
app = AppTest.deflect "sip:miles@davis.org"
|
92
|
+
app[:name].should == "deflect"
|
93
|
+
app[:args].should == "sip:miles@davis.org"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
describe "export" do
|
98
|
+
should "export variable" do
|
99
|
+
app = AppTest.export 'some_var'
|
100
|
+
app[:name].should == "export"
|
101
|
+
app[:args].should == "some_var"
|
102
|
+
end
|
54
103
|
|
55
|
-
|
56
|
-
|
104
|
+
should "only export to b-leg " do
|
105
|
+
app = AppTest.export 'some_var', :local => false
|
106
|
+
app[:name].should == "export"
|
107
|
+
app[:args].should == "nolocal:some_var"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "gentones" do
|
112
|
+
should "generate tones" do
|
113
|
+
app = AppTest.gentones("%(500,0,800)")
|
114
|
+
app[:name].should == "gentones"
|
115
|
+
app[:args].should == "%(500,0,800)"
|
116
|
+
end
|
57
117
|
end
|
58
118
|
|
59
119
|
should "hangup" do
|
60
|
-
app =
|
120
|
+
app = AppTest.hangup
|
61
121
|
app[:name].should == "hangup"
|
62
122
|
|
63
|
-
app =
|
123
|
+
app = AppTest.hangup("some cause")
|
64
124
|
app[:args].should == "some cause"
|
65
125
|
end
|
66
126
|
|
67
127
|
describe "play_and_get_digits" do
|
68
128
|
should "have defaults" do
|
69
|
-
app =
|
129
|
+
app = AppTest.play_and_get_digits "please-enter", "wrong-try-again"
|
70
130
|
app[:name].should == "play_and_get_digits"
|
71
131
|
app[:args].should == "1 2 3 5000 # please-enter wrong-try-again read_digits_var \\d+"
|
72
132
|
app[:params][:read_var].should == "read_digits_var"
|
73
133
|
end
|
74
134
|
|
75
135
|
should "take params" do
|
76
|
-
app =
|
136
|
+
app = AppTest.play_and_get_digits "please-enter", "invalid-choice",
|
77
137
|
:min => 2,
|
78
138
|
:max => 3,
|
79
139
|
:tries => 4,
|
@@ -88,21 +148,28 @@ describe Librevox::Applications do
|
|
88
148
|
end
|
89
149
|
|
90
150
|
should "playback" do
|
91
|
-
app =
|
151
|
+
app = AppTest.playback("uri://some/file.wav")
|
92
152
|
app[:name].should == "playback"
|
93
153
|
app[:args].should == "uri://some/file.wav"
|
94
154
|
end
|
95
155
|
|
156
|
+
describe "pre_answer" do
|
157
|
+
should "pre_answer" do
|
158
|
+
app = AppTest.pre_answer
|
159
|
+
app[:name].should == "pre_answer"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
96
163
|
describe "read" do
|
97
164
|
should "read with defaults" do
|
98
|
-
app =
|
165
|
+
app = AppTest.read "please-enter.wav"
|
99
166
|
app[:name].should == "read"
|
100
167
|
app[:args].should == "1 2 please-enter.wav read_digits_var 5000 #"
|
101
168
|
app[:params][:read_var].should == "read_digits_var"
|
102
169
|
end
|
103
170
|
|
104
171
|
should "take params" do
|
105
|
-
app =
|
172
|
+
app = AppTest.read "please-enter.wav",
|
106
173
|
:min => 2,
|
107
174
|
:max => 3,
|
108
175
|
:terminators => "0",
|
@@ -116,27 +183,51 @@ describe Librevox::Applications do
|
|
116
183
|
|
117
184
|
describe "record" do
|
118
185
|
should "start recording" do
|
119
|
-
app =
|
186
|
+
app = AppTest.record "/path/to/file.mp3"
|
120
187
|
app[:name].should == "record"
|
121
188
|
app[:args].should == "/path/to/file.mp3"
|
122
189
|
end
|
123
190
|
|
124
191
|
should "start recording with time limit" do
|
125
|
-
app =
|
192
|
+
app = AppTest.record "/path/to/file.mp3", :limit => 15
|
126
193
|
app[:name].should == "record"
|
127
194
|
app[:args].should == "/path/to/file.mp3 15"
|
128
195
|
end
|
129
196
|
end
|
130
197
|
|
198
|
+
describe "redirect" do
|
199
|
+
should "redirect to URI" do
|
200
|
+
app = AppTest.redirect("sip:miles@davis.org")
|
201
|
+
app[:name].should == "redirect"
|
202
|
+
app[:args].should == "sip:miles@davis.org"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
131
206
|
should "set" do
|
132
|
-
app =
|
207
|
+
app = AppTest.set("foo", "bar")
|
133
208
|
app[:name].should == "set"
|
134
209
|
app[:args].should == "foo=bar"
|
135
210
|
end
|
136
211
|
|
137
212
|
should "transfer" do
|
138
|
-
app =
|
213
|
+
app = AppTest.transfer "new_extension"
|
139
214
|
app[:name].should == "transfer"
|
140
215
|
app[:args].should == "new_extension"
|
141
216
|
end
|
217
|
+
|
218
|
+
describe "unbind_meta_app" do
|
219
|
+
should "unbind" do
|
220
|
+
app = AppTest.unbind_meta_app 3
|
221
|
+
app[:name].should == "unbind_meta_app"
|
222
|
+
app[:args].should == "3"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
describe "unset" do
|
227
|
+
should "unset a variable" do
|
228
|
+
app = AppTest.unset('foo')
|
229
|
+
app[:name].should == "unset"
|
230
|
+
app[:args].should == "foo"
|
231
|
+
end
|
232
|
+
end
|
142
233
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: librevox
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: "0.2"
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Harry Vangberg
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-01-
|
12
|
+
date: 2010-01-26 00:00:00 +01:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|