librevox 0.2.1 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
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. Eventually this is still beta
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
- session do
90
+ def session_initiated
89
91
  answer
90
92
 
91
- play_and_get_digits "enter-number.wav", "error.wav" do |digit|
92
- puts "User pressed #{digit}"
93
- playback "thanks-for-the-input.wav"
94
- hangup
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 wrapped in the `api` command:
100
+ namespace clashes, are accessed through the `api` object:
100
101
 
101
- session do
102
+ def session_initiated
102
103
  answer
103
- api :status
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
- - possibly catch all exceptions, so everything doesn't die? or should this be
25
- be the users responsibilty?
21
+ - extension sugar (0.5)
22
+
23
+ - graceful shutdown
24
+
25
+ - handle disconnects
@@ -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.2.1"
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(klass=nil, args={}, &block)
39
+ def self.start klass=nil, args={}, &block
38
40
  logger.info "Starting Librevox"
39
- EM.run {
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(klass, args={})
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 `execute_app` with the following parameters:
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` - optional hash (optional)
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 &b
13
- execute_app "answer", &b
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, &b
22
- execute_app "att_xfer", endpoint, &b
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={}, &b
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
- execute_app "bind_meta_app", arg_string, &b
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._Dialplan_Tools_bridgecall
57
- def bridge *args, &b
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
- execute_app "bridge", variables + endpoints, &b
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, &b
88
- execute_app "deflect", uri, &b
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={}, &b
101
+ def export var, args={}
103
102
  nolocal = args[:local] == false ? "nolocal:" : "" # ugly!!111
104
103
 
105
- execute_app "export", "#{nolocal}#{var}", &b
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, &b
115
- execute_app "gentones", tgml, &b
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="", &b
125
- execute_app "hangup", cause, &b
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={}, &b
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
- read_var = args[:read_var] || "read_digits_var"
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
- read_var, regexp].join " "
147
+ variable, regexp].join " "
149
148
 
150
- params = {:read_var => read_var}
149
+ params = {:variable => variable}
151
150
 
152
- execute_app "play_and_get_digits", args, params, &b
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, &b
160
- execute_app "playback", file, &b
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 &b
168
- execute_app "pre_answer", &b
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={}, &b
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
- read_var = args[:read_var] || "read_digits_var"
176
+ variable = args[:variable] || "read_digits_var"
178
177
 
179
- arg_string = "%s %s %s %s %s %s" % [min, max, file, read_var, timeout,
178
+ arg_string = "%s %s %s %s %s %s" % [min, max, file, variable, timeout,
180
179
  terminators]
181
180
 
182
- params = {:read_var => read_var}
181
+ params = {:variable => variable}
183
182
 
184
- execute_app "read", arg_string, params, &b
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={}, &b
193
+ def record path, params={}
195
194
  args = [path, params[:limit]].compact.join(" ")
196
- execute_app "record", args, &b
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
- # #{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.
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 #{deflect}
207
+ # @see #deflect
209
208
  # @see http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_redirect
210
- def redirect uri, &b
211
- execute_app "redirect", uri, &b
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, &b
219
- execute_app "set", "#{variable}=#{value}", &b
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, &b
227
- execute_app "transfer", context, &b
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, &b
235
- execute_app "unbind_meta_app", key.to_s, &b
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, &b
243
- execute_app "unset", variable, &b
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(args={})
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
- run_cmd "auth #{@auth}"
20
+ @socket.print "auth #{@auth}\n\n"
21
+ read_response
21
22
  end
22
23
 
23
- def run_cmd(cmd)
24
- @socket.print "#{cmd}\n\n"
24
+ def command *args
25
+ @socket.print "#{super(*args)}\n\n"
25
26
  read_response
26
27
  end
27
28
 
@@ -1,5 +1,5 @@
1
1
  module Librevox
2
- # All commands should call `execute_cmd` with the following parameters:
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.execute_cmd "fsctl", "hupall normal_clearing"
11
+ # socket.command "fsctl", "hupall normal_clearing"
12
12
  # @see http://wiki.freeswitch.org/wiki/Mod_commands
13
- def execute_cmd(name, args="", &block)
13
+ def command name, args=""
14
14
  msg = "api #{name}"
15
- msg << " #{args}" unless args.empty?
15
+ msg << " #{args}" if args && !args.empty?
16
+ msg
17
+ end
16
18
 
17
- run_cmd(msg, &block)
19
+ def status
20
+ command "status"
18
21
  end
19
22
 
20
- def status(&b)
21
- execute_cmd "status", &b
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(url, args={}, &b)
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
- execute_cmd "originate", arg_string, &b
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(*args, &b)
46
- execute_cmd "fsctl", args.join(" "), &b
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
- def hupall(cause=nil, &b)
50
- execute_cmd "hupall", cause, &b
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