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 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