freeswitcher 0.4.2 → 0.4.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/lib/fsr.rb CHANGED
@@ -67,11 +67,11 @@ module FSR
67
67
  # <tt>:host [String]</tt> The host/ip to bind to (Default: "localhost")
68
68
  # <tt>:port [Integer]</tt> the port to listen on (Default: 8021)
69
69
  def self.start_ies!(klass, args = {})
70
- port = args.delete(:port) || "8084"
71
- host = args.delete(:host) || "localhost"
70
+ args[:port] ||= 8021
71
+ args[:host] ||= "localhost"
72
72
  EM.run do
73
- EventMachine::connect(host, port, klass, args)
74
- FSR::Log.info "*** FreeSWITCHer Inbound EventSocket Listener connected to #{host}:#{port} ***"
73
+ EventMachine::connect(args[:host], args[:port], klass, args)
74
+ FSR::Log.info "*** FreeSWITCHer Inbound EventSocket Listener connected to #{args[:host]}:#{args[:port]} ***"
75
75
  FSR::Log.info "*** http://code.rubyists.com/projects/fs"
76
76
  end
77
77
  end
@@ -0,0 +1,26 @@
1
+
2
+ require "fsr/app"
3
+ module FSR
4
+ module App
5
+ class BindMetaApp < Application
6
+ attr_reader :options
7
+
8
+ def initialize(args)
9
+ @options = args
10
+ end
11
+
12
+ # It would be better to fix App#app_name to transform
13
+ # CamelCase to snake_case, but that'll be later.
14
+ def app_name
15
+ "bind_meta_app"
16
+ end
17
+
18
+ def arguments
19
+ parameters = options[:parameters] ? "::#{options[:parameters]}" : ""
20
+ [options[:key], options[:listen_to], options[:respond_on], options[:application] + parameters]
21
+ end
22
+ end
23
+
24
+ register(:bind_meta_app, BindMetaApp)
25
+ end
26
+ end
@@ -5,14 +5,15 @@ module FSR
5
5
  class Bridge < Application
6
6
  attr_reader :options
7
7
 
8
- def initialize(target, opts = {})
9
- # These are options that will precede the target address
10
- @target = target
11
- @options = opts || {}
8
+ def initialize(*params)
9
+ @options = params.last.is_a?(Hash) ? params.pop : {}
10
+ @sequential = @options.delete(:sequential)
11
+ @targets = params
12
12
  end
13
13
 
14
14
  def arguments
15
- [@target]
15
+ delimeter = @sequential ? "|" : ","
16
+ [@targets.join(delimeter)]
16
17
  end
17
18
 
18
19
  def modifiers
@@ -0,0 +1,15 @@
1
+ require "fsr/app"
2
+ module FSR
3
+ module App
4
+ # http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_log
5
+ class ExecuteApp < Application
6
+ attr_reader :app_name, :arguments
7
+ def initialize(app, *args)
8
+ @app_name = app
9
+ @arguments = args
10
+ end
11
+ end
12
+
13
+ register(:execute_app, ExecuteApp)
14
+ end
15
+ end
@@ -4,17 +4,32 @@ module FSR
4
4
  module App
5
5
  class PlayAndGetDigits < Application
6
6
  attr_reader :chan_var
7
+ DEFAULT_ARGS = {:min => 0, :max => 10, :tries => 3, :timeout => 7000, :terminators => ["#"], :chan_var => "fsr_read_dtmf", :regexp => '\d'}
7
8
 
8
- def initialize(sound_file, invalid_file, min = 0, max = 10, tries = 3, timeout = 7000, terminators = ["#"], chan_var = "fsr_read_dtmf", regexp = "\d")
9
+ # args format for array:
10
+ # sound_file, invalid_file, min = 0, max = 10, tries = 3, timeout = 7000, terminators = ["#"], chan_var = "fsr_read_dtmf", regexp = '\d'
11
+ def initialize(sound_file, invalid_file, *args)
12
+ #puts args.inspect
13
+ if args.size == 1 and args.first.kind_of?(Hash)
14
+ arg_hash = DEFAULT_ARGS.merge(args.first)
15
+ elsif args.size > 0
16
+ # The array used to .zip the args here can be replaced with DEFAULT_ARGS.keys if Hash keys are ordered (1.9)
17
+ # For now we'll hard code them to preserve order in 1.8/jruby
18
+ arg_hash = DEFAULT_ARGS.merge(Hash[[:min, :max, :tries, :timeout, :terminators, :chan_var, :regexp][0 .. (args.size-1)].zip(args)])
19
+ elsif args.size == 0
20
+ arg_hash = DEFAULT_ARGS
21
+ else
22
+ raise "Invalid Arguments for PlayAndGetDigits#new (must pass (sound_file, invalid_file, hash) or (sound_file, invalid_file, min = 0, max = 10, tries = 3, timeout = 7000, terminators = ['#'], chan_var = 'fsr_read_dtmf', regexp = '\d'))"
23
+ end
9
24
  @sound_file = sound_file
10
25
  @invalid_file = invalid_file
11
- @min = min
12
- @max = max
13
- @tries = tries
14
- @timeout = timeout
15
- @chan_var = chan_var
16
- @terminators = terminators
17
- @regexp = regexp
26
+ @min = arg_hash[:min]
27
+ @max = arg_hash[:max]
28
+ @tries = arg_hash[:tries]
29
+ @timeout = arg_hash[:timeout]
30
+ @chan_var = arg_hash[:chan_var]
31
+ @terminators = arg_hash[:terminators]
32
+ @regexp = arg_hash[:regexp]
18
33
  end
19
34
 
20
35
  def arguments
@@ -30,7 +45,7 @@ module FSR
30
45
  @read_var = "variable_#{me.chan_var}"
31
46
  sendmsg me
32
47
  @queue.unshift Proc.new { update_session }
33
- @queue.unshift block if block_given?
48
+ @queue.unshift(block_given? ? block : lambda {})
34
49
  end
35
50
  |
36
51
  end
data/lib/fsr/app/read.rb CHANGED
@@ -21,7 +21,7 @@ module FSR
21
21
  @read_var = "variable_#{me.chan_var}"
22
22
  sendmsg me
23
23
  @queue.unshift Proc.new { update_session }
24
- @queue.unshift block if block_given?
24
+ @queue.unshift(block_given? ? block : lambda {})
25
25
  end
26
26
  |
27
27
  end
data/lib/fsr/app/speak.rb CHANGED
@@ -9,10 +9,11 @@ module FSR
9
9
  # wav file you wish to play, full path
10
10
  @message = message
11
11
  @voice = opts[:voice] || "slt"
12
+ @engine = opts[:engine] || "flite"
12
13
  end
13
14
 
14
15
  def arguments
15
- ["flite", @voice, @message]
16
+ [@engine, @voice, @message]
16
17
  end
17
18
 
18
19
  def sendmsg
@@ -10,7 +10,7 @@ module FSR
10
10
  def initialize(fs_socket = nil, contact = {})
11
11
  @fs_socket = fs_socket # FSR::CommandSocket obj
12
12
  @contact = contact[:contact]
13
- puts @contact
13
+ #puts @contact
14
14
  end
15
15
 
16
16
  # Send the command to the event socket, using api by default.
@@ -9,6 +9,11 @@ module FSR
9
9
  def initialize(args = {})
10
10
  @headers = args[:headers]
11
11
  @content = args[:content]
12
+ strip_newlines
13
+ end
14
+
15
+ def strip_newlines
16
+ @content.each {|k,v| v.chomp! if v.is_a?(String)}
12
17
  end
13
18
 
14
19
  # Keep backward compat with the other 2 people who use FSR
@@ -6,21 +6,53 @@ require 'fsr/listener/header_and_content_response.rb'
6
6
  module FSR
7
7
  module Listener
8
8
  class Inbound < EventMachine::Protocols::HeaderAndContentProtocol
9
- attr_reader :auth
9
+ attr_reader :auth, :hooks, :event, :server, :port
10
10
 
11
11
  HOOKS = {}
12
12
 
13
13
  def initialize(args = {})
14
14
  super
15
15
  @auth = args[:auth] || "ClueCon"
16
+ @host = args[:host]
17
+ @port = args[:port]
18
+ @hooks = {}
16
19
  end
17
20
 
18
- # post_init is called upon each "new" socket connection
21
+ # post_init is called upon each "new" socket connection.
22
+ #
23
+ # If Freeswitcher hasn't started listening for inbound socket connections
24
+ # yet, EventMachine will silently do nothing. A periodic timer is added
25
+ # to check wether the connection has been initiated yet, otherwise tries
26
+ # again in five seconds.
19
27
  def post_init
28
+ if error?
29
+ reconnect_until_succeeding
30
+ else
31
+ authorize_and_register_for_events
32
+ end
33
+ end
34
+
35
+ def reconnect_until_succeeding
36
+ timer = EM::PeriodicTimer.new(5) {
37
+ if error?
38
+ FSR::Log.info "Couldn't establish connection. Trying again..."
39
+ reconnect @host, @port
40
+ else
41
+ timer.cancel
42
+ authorize_and_register_for_events
43
+ end
44
+ }
45
+ end
46
+
47
+ def authorize_and_register_for_events
48
+ FSR::Log.info "Connection established. Authorizing..."
20
49
  say("auth #{@auth}")
21
50
  say('event plain ALL')
22
51
  end
23
52
 
53
+ def before_session
54
+ end
55
+
24
56
  # receive_request is the callback method when data is recieved from the socket
25
57
  #
26
58
  # param header headers from standard Header and Content protocol
@@ -28,12 +60,13 @@ module FSR
28
60
  def receive_request(header, content)
29
61
  hash_header = headers_2_hash(header)
30
62
  hash_content = headers_2_hash(content)
31
- event = HeaderAndContentResponse.new({:headers => hash_header, :content => hash_content})
63
+ @event = HeaderAndContentResponse.new({:headers => hash_header, :content => hash_content})
32
64
  event_name = event.content[:event_name].to_s.strip
33
65
  unless event_name.empty?
34
- HOOKS[event_name.to_sym].call(event) unless HOOKS[event_name.to_sym].nil?
66
+ instance_eval &HOOKS[event_name.to_sym] unless HOOKS[event_name.to_sym].nil?
67
+ instance_eval &@hooks[event_name.to_sym] unless @hooks[event_name.to_sym].nil?
35
68
  end
36
- on_event(event)
69
+ on_event
37
70
  end
38
71
 
39
72
  # say encapsulates #send_data for the user
@@ -42,12 +75,19 @@ module FSR
42
75
  def say(line)
43
76
  send_data("#{line}\r\n\r\n")
44
77
  end
78
+
79
+ # api encapsulates #say("api blah") for the user
80
+ #
81
+ # param line Line of text to send to the socket proceeding api
82
+ def api(line)
83
+ say("api #{line}")
84
+ end
45
85
 
46
86
  # on_event is the callback method when an event is triggered
47
87
  #
48
88
  # param event The triggered event object
49
89
  # return event The triggered event object
50
- def on_event(event)
90
+ def on_event
51
91
  event
52
92
  end
53
93
 
@@ -66,6 +106,23 @@ module FSR
66
106
  HOOKS.delete(event)
67
107
  end
68
108
 
109
+ # add_event_hook adds an Event to listen for. When that Event is triggered, it will call the defined block
110
+ #
111
+ # @param event The event to trigger the block on. Examples, :CHANNEL_CREATE, :CHANNEL_DESTROY, etc
112
+ # @param block The block to execute when the event is triggered
113
+ def add_event(event, &block)
114
+ @hooks[event] = block
115
+ end
116
+
117
+ # del_event_hook removes an Event.
118
+ #
119
+ # @param event The event to remove. Examples, :CHANNEL_CREATE, :CHANNEL_DESTROY, etc
120
+ def del_event(event)
121
+ @hooks.delete(event)
122
+ end
123
+
124
+
125
+
69
126
  end
70
127
 
71
128
  end
@@ -16,7 +16,7 @@ module FSR
16
16
  SENDMSG_METHOD_DEFINITION = [
17
17
  "def %s(*args, &block)",
18
18
  " sendmsg super",
19
- " @queue.unshift block if block_given?",
19
+ " @queue.unshift(block_given? ? block : lambda {})",
20
20
  "end"
21
21
  ].join("\n")
22
22
 
@@ -70,6 +70,13 @@ module FSR
70
70
  @queue.unshift block if block_given?
71
71
  end
72
72
 
73
+ # Scheduel Hangup
74
+
75
+ def sched_hangup(secs, &block)
76
+ send_data("api sched_hangup +#{secs} #{@session.headers[:unique_id]} alotted_timeout\n\n")
77
+ @queue.unshift block if block_given?
78
+ end
79
+
73
80
  def next_step
74
81
  @step += 1
75
82
  receive_reply(@session)
data/lib/fsr/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module FSR
2
- VERSION = "0.4.2"
2
+ VERSION = "0.4.3"
3
3
  end
@@ -0,0 +1,18 @@
1
+ require 'spec/helper'
2
+ require "fsr/app"
3
+ FSR::App.load_application("bind_meta_app")
4
+
5
+ describe "Testing FSR::App::BindMetaApp" do
6
+ it "binds a meta app" do
7
+ meta_app = FSR::App::BindMetaApp.new :key => 1, :listen_to => "a",
8
+ :respond_on => "s", :application => "hangup"
9
+ meta_app.sendmsg.should == "call-command: execute\nexecute-app-name: bind_meta_app\nexecute-app-arg: 1 a s hangup\n\n"
10
+ end
11
+
12
+ it "binds a meta app with parameters" do
13
+ meta_app = FSR::App::BindMetaApp.new :key => 1, :listen_to => "a",
14
+ :respond_on => "s", :application => "execute_extension",
15
+ :parameters => "dx XML features"
16
+ meta_app.sendmsg.should == "call-command: execute\nexecute-app-name: bind_meta_app\nexecute-app-arg: 1 a s execute_extension::dx XML features\n\n"
17
+ end
18
+ end
@@ -3,15 +3,46 @@ require "fsr/app"
3
3
  FSR::App.load_application("bridge")
4
4
 
5
5
  describe "Testing FSR::App::Bridge" do
6
- # Utilize the [] shortcut to start a conference
7
- it "Bridges a call, for FSR::Listener::Inbound" do
8
- bridge = FSR::App::Bridge.new("user/bougyman")
9
- bridge.raw.should == "bridge({}user/bougyman)"
6
+ describe "with a single endpoint" do
7
+ before do
8
+ @bridge = FSR::App::Bridge.new("user/bougyman")
9
+ end
10
+
11
+ # Utilize the [] shortcut to start a conference
12
+ it "bridges a call, for FSR::Listener::Inbound" do
13
+ @bridge.raw.should == "bridge({}user/bougyman)"
14
+ end
15
+
16
+ it "bridges a call, for FSR::Listener::Outbound" do
17
+ @bridge.sendmsg.should == "call-command: execute\nexecute-app-name: bridge\nexecute-app-arg: user/bougyman\n\n"
18
+ end
10
19
  end
11
20
 
12
- it "Bridges a call, for FSR::Listener::Outbound" do
13
- bridge = FSR::App::Bridge.new("user/bougyman")
14
- bridge.sendmsg.should == "call-command: execute\nexecute-app-name: bridge\nexecute-app-arg: user/bougyman\n\n"
21
+ describe "with multiple simultaneous endpoints" do
22
+ before do
23
+ @bridge = FSR::App::Bridge.new("user/bougyman", "user/coltrane")
24
+ end
25
+
26
+ it "bridges a call, for FSR::Listener::Inbound" do
27
+ @bridge.raw.should == "bridge({}user/bougyman,user/coltrane)"
28
+ end
29
+
30
+ it "bridges a call, for FSR::Listener::Outbound" do
31
+ @bridge.sendmsg.should == "call-command: execute\nexecute-app-name: bridge\nexecute-app-arg: user/bougyman,user/coltrane\n\n"
32
+ end
15
33
  end
34
+
35
+ describe "with multiple sequential endpoints" do
36
+ before do
37
+ @bridge = FSR::App::Bridge.new("user/bougyman", "user/coltrane", :sequential => true)
38
+ end
39
+
40
+ it "bridges a call, for FSR::Listener::Inbound" do
41
+ @bridge.raw.should == "bridge({}user/bougyman|user/coltrane)"
42
+ end
16
43
 
44
+ it "bridges a call, for FSR::Listener::Outbound" do
45
+ @bridge.sendmsg.should == "call-command: execute\nexecute-app-name: bridge\nexecute-app-arg: user/bougyman|user/coltrane\n\n"
46
+ end
47
+ end
17
48
  end
@@ -0,0 +1,15 @@
1
+ require 'spec/helper'
2
+ require "fsr/app"
3
+ FSR::App.load_application("execute_app")
4
+
5
+ describe "Testing FSR::App::ExecuteApp" do
6
+ it "executes arbitrary app with one argument" do
7
+ log = FSR::App::ExecuteApp.new("att_xfer", "sofia/user/1")
8
+ log.sendmsg.should == "call-command: execute\nexecute-app-name: att_xfer\nexecute-app-arg: sofia/user/1\n\n"
9
+ end
10
+
11
+ it "executes arbitrary app with multiple argument" do
12
+ log = FSR::App::ExecuteApp.new("log", "INFO", "foo bar")
13
+ log.sendmsg.should == "call-command: execute\nexecute-app-name: log\nexecute-app-arg: INFO foo bar\n\n"
14
+ end
15
+ end
@@ -4,9 +4,48 @@ FSR::App.load_application("play_and_get_digits")
4
4
 
5
5
  describe "Testing FSR::App::PlayAndGetDigits" do
6
6
  # Utilize the [] shortcut to start a conference
7
- it "Send a play_and_gets_digits command" do
7
+ it "Send a play_and_gets_digits command, default args" do
8
8
  tmp = FSR::App::PlayAndGetDigits.new("soundfile.wav", "invalid.wav")
9
- tmp.sendmsg.should == "call-command: execute\nexecute-app-name: play_and_get_digits\nexecute-app-arg: 0 10 3 7000 # soundfile.wav invalid.wav fsr_read_dtmf d\nevent-lock:true\n\n"
9
+ tmp.sendmsg.should == "call-command: execute\nexecute-app-name: play_and_get_digits\nexecute-app-arg: 0 10 3 7000 # soundfile.wav invalid.wav fsr_read_dtmf \\d\nevent-lock:true\n\n"
10
+ end
11
+
12
+ it "Send a play_and_gets_digits command with array args, one arg" do
13
+ tmp = FSR::App::PlayAndGetDigits.new("soundfile.wav", "invalid.wav", "4")
14
+ tmp.sendmsg.should == "call-command: execute\nexecute-app-name: play_and_get_digits\nexecute-app-arg: 4 10 3 7000 # soundfile.wav invalid.wav fsr_read_dtmf \\d\nevent-lock:true\n\n"
15
+ end
16
+
17
+ it "Send a play_and_gets_digits command with array args, two args" do
18
+ tmp = FSR::App::PlayAndGetDigits.new("soundfile.wav", "invalid.wav", "4", "4")
19
+ tmp.sendmsg.should == "call-command: execute\nexecute-app-name: play_and_get_digits\nexecute-app-arg: 4 4 3 7000 # soundfile.wav invalid.wav fsr_read_dtmf \\d\nevent-lock:true\n\n"
20
+ end
21
+
22
+ it "Send a play_and_gets_digits command with array args, three args" do
23
+ tmp = FSR::App::PlayAndGetDigits.new("soundfile.wav", "invalid.wav", "4", "4", "2")
24
+ tmp.sendmsg.should == "call-command: execute\nexecute-app-name: play_and_get_digits\nexecute-app-arg: 4 4 2 7000 # soundfile.wav invalid.wav fsr_read_dtmf \\d\nevent-lock:true\n\n"
25
+ end
26
+ it "Send a play_and_gets_digits command with array args, four args" do
27
+ tmp = FSR::App::PlayAndGetDigits.new("soundfile.wav", "invalid.wav", "4", "4", "2", "10000")
28
+ tmp.sendmsg.should == "call-command: execute\nexecute-app-name: play_and_get_digits\nexecute-app-arg: 4 4 2 10000 # soundfile.wav invalid.wav fsr_read_dtmf \\d\nevent-lock:true\n\n"
29
+ end
30
+
31
+ it "Send a play_and_gets_digits command with array args, five args" do
32
+ tmp = FSR::App::PlayAndGetDigits.new("soundfile.wav", "invalid.wav", "4", "4", "2", "10000", ['*'])
33
+ tmp.sendmsg.should == "call-command: execute\nexecute-app-name: play_and_get_digits\nexecute-app-arg: 4 4 2 10000 * soundfile.wav invalid.wav fsr_read_dtmf \\d\nevent-lock:true\n\n"
34
+ end
35
+
36
+ it "Send a play_and_gets_digits command with array args, six args" do
37
+ tmp = FSR::App::PlayAndGetDigits.new("soundfile.wav", "invalid.wav", "4", "4", "2", "10000", ['*'], "read_var")
38
+ tmp.sendmsg.should == "call-command: execute\nexecute-app-name: play_and_get_digits\nexecute-app-arg: 4 4 2 10000 * soundfile.wav invalid.wav read_var \\d\nevent-lock:true\n\n"
39
+ end
40
+
41
+ it "Send a play_and_gets_digits command with array args, seven args" do
42
+ tmp = FSR::App::PlayAndGetDigits.new("soundfile.wav", "invalid.wav", "4", "4", "2", "10000", ['*'], "read_var", '\w')
43
+ tmp.sendmsg.should == "call-command: execute\nexecute-app-name: play_and_get_digits\nexecute-app-arg: 4 4 2 10000 * soundfile.wav invalid.wav read_var \\w\nevent-lock:true\n\n"
44
+ end
45
+
46
+ it "Send a play_and_gets_digits command with hash args, just max" do
47
+ tmp = FSR::App::PlayAndGetDigits.new("soundfile.wav", "invalid.wav", :max => "4")
48
+ tmp.sendmsg.should == "call-command: execute\nexecute-app-name: play_and_get_digits\nexecute-app-arg: 0 4 3 7000 # soundfile.wav invalid.wav fsr_read_dtmf \\d\nevent-lock:true\n\n"
10
49
  end
11
50
 
12
51
  end