bougyman-freeswitcher 0.0.9 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -44,11 +44,11 @@ Example of creating an Outbound Eventsocket listener:
44
44
 
45
45
  class OesDemo < FSR::Listener::Outbound
46
46
 
47
- def session_initiated(session)
48
- number = session.headers[:caller_caller_id_number] # Grab the inbound caller id
47
+ def session_initiated
48
+ number = @session.headers[:caller_caller_id_number] # Grab the inbound caller id
49
49
  FSR::Log.info "*** Answering incoming call from #{number}"
50
50
  answer # Answer the call
51
- set "hangup_after_bridge=true" # Set a variable
51
+ set("hangup_after_bridge", "true")# Set a variable
52
52
  speak 'Hello, This is your phone switch. Have a great day' # use mod_flite to speak
53
53
  hangup # Hangup the call
54
54
  end
@@ -59,12 +59,53 @@ Example of creating an Outbound Eventsocket listener:
59
59
 
60
60
 
61
61
 
62
+ Example of creating an Outbound Eventsocket listener that can read DTMF input and keep state:
63
+
64
+ #!/usr/bin/env ruby
65
+
66
+ require 'fsr'
67
+ require 'fsr/listener/outbound'
68
+
69
+ FSR.load_all_applications
70
+ FSR.load_all_commands
71
+
72
+ class DtmfDemo < FSR::Listener::Outbound
73
+
74
+ def session_initiated
75
+ exten = @session.headers[:caller_caller_id_number]
76
+ FSR::Log.info "*** Answering incoming call from #{exten}"
77
+ answer # Answer the call
78
+ end
79
+
80
+ def receive_reply(reply)
81
+ exten = @session.headers[:caller_caller_id_number]
82
+ case @step
83
+ when 1
84
+ FSR::Log.info "*** Reading dtmf for #{exten}"
85
+ read "/home/freeswitch/freeswitch/sounds/music/8000/sweet.wav",4,10,"test",15000 # read test
86
+ when 2
87
+ FSR::Log.info "*** updating session for #{exten}"
88
+ update_session
89
+ when 3
90
+ FSR::Log.info "** Success, grabbed #{@session.headers[:variable_test].strip} from #{exten}"
91
+ FSR::Log.info "*** Hanging up call"
92
+ hangup # Hangup the call
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+ FSR.start_oes! DtmfDemo, :port => 8084, :host => "127.0.0.1"
99
+
100
+
101
+
62
102
  Example of creating an Inbound Eventsocket listener:
63
103
 
64
104
  #!/usr/bin/env ruby
65
105
 
66
106
  require 'fsr'
67
- require "fsr/listener/inbound"
107
+ require 'fsr/listener/inbound'
108
+ require 'pp'
68
109
 
69
110
  class IesDemo < FSR::Listener::Inbound
70
111
 
File without changes
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+ require File.join(File.dirname(__FILE__), "..", 'start_common')
3
+ require 'eventmachine'
4
+ require "fsr/listener/outbound"
5
+ require 'pp'
6
+ $stdout.flush
7
+ FSR.load_all_applications
8
+ FSR.load_all_commands
9
+ class ConsumerLogin < FSR::Listener::Outbound
10
+
11
+ def session_initiated(session, step = 0)
12
+ @step ||= step
13
+ exten = session.headers[:channel_caller_id_number]
14
+ pp session.headers
15
+ FSR::Log.info "*** Answering incoming call from #{exten}"
16
+ answer # Answer the call
17
+ end
18
+
19
+ def receive_reply(reply)
20
+ exten = @session.headers[:channel_caller_id_number]
21
+ @step += 1
22
+ case @step
23
+ when 1
24
+ FSR::Log.info "*** Reading dtmf for #{exten}"
25
+ read "/home/freeswitch/freeswitch/sounds/music/8000/sweet.wav",4,10,"test",15000 # read test
26
+ when 2
27
+ FSR::Log.info "*** updating session for #{exten}"
28
+ update_session
29
+ when 3
30
+ FSR::Log.info "** Success, grabbed #{reply.content[:variable_test].strip} from #{exten}"
31
+ FSR::Log.info "*** Hanging up call"
32
+ hangup # Hangup the call
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ FSR.start_oes! ConsumerLogin, :port => 8084, :host => "127.0.0.1"
File without changes
@@ -20,8 +20,6 @@ end
20
20
  FSL::Inbound.add_event_hook(:CHANNEL_HANGUP) {|event| custom_channel_hangup_handler(event) }
21
21
 
22
22
 
23
-
24
-
25
23
  # Start FSR Inbound Listener
26
24
  FSR.start_ies!(FSL::Inbound, :host => "localhost", :port => 8021)
27
25
 
File without changes
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), "..", 'lib', 'fsr')
4
+ require "fsr/listener/outbound"
5
+ $stdout.flush
6
+
7
+ FSR.load_all_applications
8
+ FSR.load_all_commands
9
+ class DtmfDemo < FSR::Listener::Outbound
10
+
11
+ def session_initiated
12
+ exten = @session.headers[:caller_caller_id_number]
13
+ FSR::Log.info "*** Answering incoming call from #{exten}"
14
+ answer # Answer the call
15
+ end
16
+
17
+ def receive_reply(reply)
18
+ exten = @session.headers[:caller_caller_id_number]
19
+ case @step
20
+ when 1
21
+ FSR::Log.info "*** Reading dtmf for #{exten}"
22
+ read "/home/freeswitch/freeswitch/sounds/music/8000/sweet.wav",4,10,"test",15000 # read test
23
+ when 2
24
+ FSR::Log.info "*** updating session for #{exten}"
25
+ update_session
26
+ when 3
27
+ FSR::Log.info "** Success, grabbed #{@session.headers[:variable_test].strip} from #{exten}"
28
+ FSR::Log.info "*** Hanging up call"
29
+ hangup # Hangup the call
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ FSR.start_oes! DtmfDemo, :port => 8084, :host => "127.0.0.1"
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pp'
4
+ require File.join(File.dirname(__FILE__), "..", 'lib', 'fsr')
5
+ puts $LOAD_PATH.inspect
6
+ $stdout.flush
7
+ require "fsr/listener/inbound"
8
+
9
+
10
+ class IesDemo < FSR::Listener::Inbound
11
+
12
+ def on_event(event)
13
+ pp event.headers
14
+ pp event.content[:event_name]
15
+ end
16
+
17
+ end
18
+
19
+ FSR.start_ies!(IesDemo, :host => "localhost", :port => 8021)
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'pp'
4
+ require File.join(File.dirname(__FILE__), "..", 'lib', 'fsr')
5
+ puts $LOAD_PATH.inspect
6
+ $stdout.flush
7
+ require "fsr/listener/inbound"
8
+
9
+ # EXAMPLE 1
10
+ # This adds a hook on CHANNEL_CREATE events. You can also create a method to handle the event you're after. See the next example
11
+ FSL::Inbound.add_event_hook(:CHANNEL_CREATE) {|event| FSR::Log.info "*** [#{event.content[:unique_id]}] Channel created - greetings from the hook!" }
12
+
13
+ # EXAMPLE 2
14
+ # Define a method to handle CHANNEL_HANGUP events.
15
+ def custom_channel_hangup_handler(event)
16
+ FSR::Log.info "*** [#{event.content[:unique_id]}] Channel hangup. The event:"
17
+ pp event
18
+ end
19
+ # This adds a hook for EXAMPLE 2
20
+ FSL::Inbound.add_event_hook(:CHANNEL_HANGUP) {|event| custom_channel_hangup_handler(event) }
21
+
22
+
23
+ # Start FSR Inbound Listener
24
+ FSR.start_ies!(FSL::Inbound, :host => "localhost", :port => 8021)
25
+
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), "..", 'lib', 'fsr')
4
+ $stdout.flush
5
+ require "fsr/listener/outbound"
6
+
7
+ class OesDemo < FSR::Listener::Outbound
8
+
9
+ def session_initiated
10
+ number = @session.headers[:caller_caller_id_number] # Grab the inbound caller id
11
+ FSR::Log.info "*** Answering incoming call from #{number}"
12
+ answer # Answer the call
13
+ log("1", "Pong from the FSR event socket!")
14
+ set("hangup_after_bridge", "true") # Set a variable
15
+ speak 'Hello, This is your phone switch. Have a great day' # use mod_flite to speak
16
+ hangup # Hangup the call
17
+ end
18
+
19
+ end
20
+
21
+ FSR.start_oes!(OesDemo, :port => 1888, :host => "localhost")
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), "..", 'lib', 'fsr')
4
+ require "fsr/listener/outbound"
5
+ $stdout.flush
6
+
7
+ FSR.load_all_applications
8
+ FSR.load_all_commands
9
+ class PlayAndGetTest < FSR::Listener::Outbound
10
+
11
+ def session_initiated
12
+ exten = @session.headers[:caller_caller_id_number]
13
+ FSR::Log.info "*** Answering incoming call from #{exten}"
14
+ answer # Answer the call
15
+ end
16
+
17
+ def receive_reply(reply)
18
+ exten = @session.headers[:caller_caller_id_number]
19
+ case @step
20
+ when 1
21
+ FSR::Log.info "*** Reading dtmf for #{exten}"
22
+ play_and_get_digits "tone_stream://%(10000,0,350,440)","/home/freeswitch/freeswitch/sounds/en/us/callie/misc/8000/error.wav",2,10,3,7000,["#"],"test", "\d+" # play_and_get_digits test
23
+ when 2
24
+ FSR::Log.info "*** updating session for #{exten}"
25
+ update_session
26
+ when 3
27
+ FSR::Log.info "** Success, grabbed #{@session.headers[:variable_test]} from #{exten}"
28
+ FSR::Log.info "*** Hanging up call"
29
+ hangup # Hangup the call
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ FSR.start_oes! PlayAndGetTest, :port => 8084, :host => "127.0.0.1"
data/lib/fsr.rb CHANGED
@@ -6,7 +6,7 @@ require 'pp'
6
6
  module FSR
7
7
  # Global configuration options
8
8
  #
9
- VERSION = '0.0.8'
9
+ VERSION = '0.1.0'
10
10
  FS_INSTALL_PATHS = ["/usr/local/freeswitch", "/opt/freeswitch", "/usr/freeswitch"]
11
11
  DEFAULT_CALLER_ID_NUMBER = '8675309'
12
12
  DEFAULT_CALLER_ID_NAME = "FSR"
@@ -3,12 +3,12 @@ module FSR
3
3
  module App
4
4
  # http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_hangup
5
5
  class Hangup < Application
6
- def initialize(data = nil)
7
- @data = data
6
+ def initialize(cause = nil)
7
+ @cause = cause
8
8
  end
9
9
 
10
10
  def arguments
11
- @data
11
+ @cause
12
12
  end
13
13
 
14
14
  def sendmsg
@@ -0,0 +1,30 @@
1
+ require "fsr/app"
2
+ module FSR
3
+ #http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_play_and_get_digits
4
+ module App
5
+ class PlayAndGetDigits < Application
6
+ def initialize(sound_file, invalid_file, min = 0, max = 10, tries = 3, timeout = 7000, terminators = ["#"], chan_var = "fsr_read_dtmf", regexp = "\d")
7
+ @sound_file = sound_file
8
+ @invalid_file = invalid_file
9
+ @min = min
10
+ @max = max
11
+ @tries = tries
12
+ @timeout = timeout
13
+ @chan_var = chan_var
14
+ @terminators = terminators
15
+ @regexp = regexp
16
+ end
17
+
18
+ def arguments
19
+ [@min, @max, @tries, @timeout, @terminators.join(","), @sound_file, @invalid_file, @chan_var, @regexp]
20
+ end
21
+
22
+ def sendmsg
23
+ "call-command: execute\nexecute-app-name: %s\nexecute-app-arg: %s\nevent-lock:true\n\n" % ["play_and_get_digits", arguments.join(" ")]
24
+ end
25
+
26
+ end
27
+
28
+ register(:play_and_get_digits, PlayAndGetDigits)
29
+ end
30
+ end
@@ -1,4 +1,3 @@
1
-
2
1
  require "fsr/app"
3
2
  module FSR
4
3
  module App
@@ -10,11 +9,11 @@ module FSR
10
9
  @wavfile = wavfile
11
10
  end
12
11
  def arguments
13
- [@wavfile]
12
+ @wavfile
14
13
  end
15
14
 
16
15
  def sendmsg
17
- "call-command: execute\nexecute-app-name: %s\nexecute-app-arg: %s\nevent-lock:true\n\n" % [app_name, arguments.join(" ")]
16
+ "call-command: execute\nexecute-app-name: %s\nexecute-app-arg: %s\nevent-lock:true\n\n" % [app_name, arguments]
18
17
  end
19
18
  end
20
19
 
@@ -0,0 +1,22 @@
1
+ require "fsr/app"
2
+ module FSR
3
+ # http://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_read
4
+ module App
5
+ class Read < Application
6
+ def initialize(sound_file, min = 0, max = 10, chan_var = "fsr_read_dtmf", timeout = 10000, terminators = ["#"])
7
+ @sound_file, @min, @max, @chan_var, @timeout, @terminators = sound_file, min, max, chan_var, timeout, terminators
8
+ end
9
+
10
+ def arguments
11
+ [@min, @max, @sound_file, @chan_var, @timeout, @terminators.join(",")]
12
+ end
13
+
14
+ def sendmsg
15
+ "call-command: execute\nexecute-app-name: %s\nexecute-app-arg: %s\nevent-lock:true\n\n" % [app_name, arguments.join(" ")]
16
+ end
17
+
18
+ end
19
+
20
+ register(:read, Read)
21
+ end
22
+ end
@@ -6,8 +6,8 @@ module FSR
6
6
 
7
7
  def initialize(destination_number, dialplan = nil, context = nil)
8
8
  @destination_number = destination_number
9
- @dialplan = dialplan || "XML"
10
- @context = context || "default"
9
+ @dialplan = dialplan
10
+ @context = context
11
11
  end
12
12
 
13
13
  def arguments
@@ -0,0 +1,22 @@
1
+ require "fsr/app"
2
+ module FSR
3
+ #http://wiki.freeswitch.org/wiki/Mod_commands#uuid_dump
4
+ module App
5
+ class UuidDump < Application
6
+ def initialize(uuid)
7
+ @uuid = uuid # Unique channel ID
8
+ end
9
+
10
+ def arguments
11
+ [@uuid]
12
+ end
13
+
14
+ def sendmsg
15
+ "call-command: execute\nexecute-app-name: %s\nexecute-app-arg: %s\nevent-lock:true\n\n" % [app_name, arguments.join(" ")]
16
+ end
17
+
18
+ end
19
+
20
+ register(:uuid_dump, UuidDump)
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ require "fsr/app"
2
+ module FSR
3
+ #http://wiki.freeswitch.org/wiki/Mod_commands#uuid_getvar
4
+ module App
5
+ class UuidGetVar < Application
6
+ def initialize(uuid, var)
7
+ @uuid = uuid # Unique channel ID
8
+ @var = var # Channel variable you wish to 'get'
9
+ end
10
+
11
+ def arguments
12
+ [@uuid, @var]
13
+ end
14
+
15
+ def sendmsg
16
+ "call-command: execute\nexecute-app-name: %s\nexecute-app-arg: %s\nevent-lock:true\n\n" % [app_name, arguments.join(" ")]
17
+ end
18
+
19
+ end
20
+
21
+ register(:uuid_getvar, UuidGetVar)
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ require "fsr/app"
2
+ module FSR
3
+ #http://wiki.freeswitch.org/wiki/Mod_commands#uuid_setvar
4
+ module App
5
+ class UuidSetVar < Application
6
+ def initialize(uuid, var, assignment)
7
+ @uuid = uuid # Unique channel ID
8
+ @var = var # Channel variable you wish to 'set'
9
+ @assignment
10
+ end
11
+
12
+ def arguments
13
+ [@uuid, @var, @assignment]
14
+ end
15
+
16
+ def sendmsg
17
+ "call-command: execute\nexecute-app-name: %s\nexecute-app-arg: %s\nevent-lock:true\n\n" % [app_name, arguments.join(" ")]
18
+ end
19
+
20
+ end
21
+
22
+ register(:uuid_setvar, UuidSetVar)
23
+ end
24
+ end
@@ -0,0 +1,46 @@
1
+ require "fsr/app"
2
+ module FSR
3
+ module Cmd
4
+ class Calls < Command
5
+
6
+ include Enumerable
7
+ def each(&block)
8
+ @calls ||= run
9
+ if @calls
10
+ @calls.each { |call| yield call }
11
+ end
12
+ end
13
+
14
+ def initialize(fs_socket = nil)
15
+ @fs_socket = fs_socket # FSR::CommandSocket obj
16
+ end
17
+
18
+ # Send the command to the event socket, using bgapi by default.
19
+ def run(api_method = :api)
20
+ orig_command = "%s %s" % [api_method, raw]
21
+ Log.debug "saying #{orig_command}"
22
+ resp = @fs_socket.say(orig_command)
23
+ unless resp["body"] == "0 total."
24
+ call_info, count = resp["body"].split("\n\n")
25
+ require "fsr/model/call"
26
+ begin
27
+ require "fastercsv"
28
+ calls = FCSV.parse(call_info)
29
+ rescue LoadError
30
+ require "csv"
31
+ calls = CSV.parse(call_info)
32
+ end
33
+ return calls[1 .. -1].map { |c| FSR::Model::Call.new(*c) }
34
+ end
35
+ nil
36
+ end
37
+
38
+ # This method builds the API command to send to the freeswitch event socket
39
+ def raw
40
+ orig_command = "show calls"
41
+ end
42
+ end
43
+
44
+ register(:calls, Calls)
45
+ end
46
+ end
@@ -7,6 +7,7 @@ module FSR
7
7
  load_all_applications
8
8
  module Listener
9
9
  class Outbound < EventMachine::Protocols::HeaderAndContentProtocol
10
+ attr_reader :session
10
11
 
11
12
  # Include FSR::App to get all the applications defined as methods
12
13
  include FSR::App
@@ -15,33 +16,23 @@ module FSR
15
16
  SENDMSG_METHOD_DEFINITION = "def %s(*args, &block); sendmsg super; end"
16
17
  APPLICATIONS.each { |app, obj| module_eval(SENDMSG_METHOD_DEFINITION % app.to_s) }
17
18
 
18
- def post_init
19
- @session = nil # holds the session object
20
- send_data("connect\n\n")
21
- FSR::Log.debug "Accepting connections."
19
+ # session_initiated is called when a @session is first created.
20
+ # Overwrite this in your worker class with the call/channel
21
+ # handling logic you desire
22
+ def session_initiated
23
+ FSR::Log.warn "#{self.class.name}#session_initiated not overwritten"
24
+ FSR::Log.debug session_data.inspect
22
25
  end
23
26
 
24
- def receive_request(header, content)
25
- hash_header = headers_2_hash(header)
26
- hash_content = headers_2_hash(content)
27
- session = HeaderAndContentResponse.new({:headers => hash_header, :content => hash_content})
28
- if @session.nil?
29
- @session = self
30
- session_initiated(session)
31
- else
32
- receive_reply(session)
33
- end
34
- end
35
-
36
- # Received data dispatches the data received by the EM socket,
37
- # Either as a Session, a continuation of a Session, or as a Session's last CommandReply
38
-
39
- def session_initiated(session)
40
- session
41
- end
42
-
43
- def receive_reply(session)
44
- session
27
+ # receive_reply is called when a response is received.
28
+ # Overwrite this in your worker class with the call/channel
29
+ # handling logic you desire, taking @step into account for
30
+ # state management between commands
31
+ # @param reply This HeaderAndContent instance will have the channel variables
32
+ # in #content, if the session has been updated
33
+ def receive_reply(reply)
34
+ FSR::Log.warn "#{self.class.name}#receive_reply not overwritten"
35
+ FSR::Log.debug reply.inspect
45
36
  end
46
37
 
47
38
  # sendmsg sends data to the EM app socket via #send_data, or
@@ -56,7 +47,56 @@ module FSR
56
47
  self.respond_to?(:send_data) ? send_data(message) : message
57
48
  end
58
49
 
50
+ # Update_session
51
+
52
+ def update_session
53
+ send_data("api uuid_dump #{@session.headers[:unique_id]}\n\n")
54
+ end
55
+
56
+ protected
57
+ def post_init
58
+ @session = nil # holds the session object
59
+ send_data("connect\n\n")
60
+ FSR::Log.debug "Accepting connections."
61
+ end
62
+
63
+ # receive_request is called each time data is received by the event machine
64
+ # it will manipulate the received data into either a new session or a reply,
65
+ # to be picked up by #session_initiated or #receive_reply.
66
+ # If your listener is listening for events, this will also renew your @session
67
+ # each time you receive a CHANNEL_DATA event.
68
+ # @param header The header of the request, as passed by HeaderAndContentProtocol
69
+ # @param content The content of the request, as passed by HeaderAndContentProtocol
70
+ #
71
+ # @returns HeaderAndContentResponse
72
+ def receive_request(header, content)
73
+ hash_header = headers_2_hash(header)
74
+ hash_content = headers_2_hash(content)
75
+ session_header_and_content = HeaderAndContentResponse.new({:headers => hash_header, :content => hash_content})
76
+ # If we're a new session, call session initiate
77
+ if @session.nil?
78
+ @session = session_header_and_content
79
+ @step = 0
80
+ session_initiated
81
+ elsif session_header_and_content.content[:event_name] # If content includes an event_name, it must be a response from an api command
82
+ if session_header_and_content.content[:event_name].to_s.match(/CHANNEL_DATA/i) # Anytime we see CHANNEL_DATA event, we want to update our @session
83
+ session_header_and_content = HeaderAndContentResponse.new({:headers => hash_header.merge(hash_content.strip_value_newlines), :content => {}})
84
+ @session = session_header_and_content
85
+ @step += 1
86
+ receive_reply(hash_header)
87
+ end
88
+ else
89
+ @step += 1
90
+ receive_reply(session_header_and_content)
91
+ end
92
+ end
93
+
59
94
  end
95
+ end
96
+ end
60
97
 
98
+ class Hash
99
+ def strip_value_newlines
100
+ Hash[*(self.map { |k,v| v.respond_to?(:to_s) ? [k, v.to_s.strip] : [k, v] }.flatten)]
61
101
  end
62
102
  end
@@ -0,0 +1,131 @@
1
+ require "yaml"
2
+ require "fsr/listener"
3
+ module FSR
4
+ load_all_applications
5
+ module Listener
6
+ module Outbound
7
+ include FSR::Listener
8
+
9
+ # Include FSR::App to get all the applications defined as methods
10
+ include FSR::App
11
+
12
+ # Redefine the FSR::App methods to wrap sendmsg around them
13
+ SENDMSG_METHOD_DEFINITION = "def %s(*args, &block); sendmsg super; end"
14
+ APPLICATIONS.each { |app, obj| module_eval(SENDMSG_METHOD_DEFINITION % app.to_s) }
15
+
16
+ def post_init
17
+ @session = nil # holds the session object
18
+ send_data("connect\n\n")
19
+ FSR::Log.debug "Accepting connections."
20
+ end
21
+
22
+ # Received data dispatches the data received by the EM socket,
23
+ # Either as a Session, a continuation of a Session, or as a Session's last CommandReply
24
+ def receive_data(data)
25
+ FSR::Log.debug("received #{data}")
26
+ if @session.nil? # if @session is nil, create a new Session object
27
+ @session = Session.new(data)
28
+ session_initiated(@session) if @session.initiated?
29
+ else
30
+ # If it's not nil, we add the data to this session, Session knows whether
31
+ # or not to create a CommandReply, complete a CommandReply, or simply add to
32
+ # its own @data array and @headers/@body structures
33
+ if @session.initiated?
34
+ @session << data
35
+ reply_received(@session.replies.last) if @session.replies.last.complete?
36
+ else
37
+ @session << data
38
+ session_initiated(@session) if @session.initiated?
39
+ end
40
+ end
41
+ @session
42
+ end
43
+
44
+ def session_initiated(session)
45
+ session
46
+ end
47
+
48
+ def reply_received(command_reply)
49
+ command_reply
50
+ end
51
+
52
+ alias :receive_response :reply_received
53
+ # sendmsg sends data to the EM app socket via #send_data, or
54
+ # returns the string it would send if #send_data is not defined.
55
+ # It expects an object which responds to either #sendmsg or #to_s,
56
+ # which should return a EM Outbound Event Socket formatted instruction
57
+ def sendmsg(message)
58
+ text = message.respond_to?(:sendmsg) ? message.sendmsg : message.to_s
59
+ FSR::Log.debug "sending #{text}"
60
+ message = "sendmsg\n%s\n" % text
61
+ self.respond_to?(:send_data) ? send_data(message) : message
62
+ end
63
+
64
+
65
+ class SocketResponse
66
+ attr_accessor :headers, :body, :data
67
+ def initialize(data = "")
68
+ @data = [data]
69
+ @headers = {}
70
+ if data.match(/\n$/)
71
+ headers, @body = data.split("\n\n")
72
+ headers.each_line do |line|
73
+ key, value = line.split(":")
74
+ @headers[key] = value.to_s.strip
75
+ end
76
+ end
77
+ @body ||= ""
78
+ FSR::Log.debug("New #{self.class.name} created: #{self}")
79
+ end
80
+
81
+ def <<(data)
82
+ if data.match(/\n$/)
83
+ @data.last.match(/\n$/) ? @data << data : @data.last << data
84
+ extra_headers, more_body = @data.last.split("\n\n")
85
+ extra_headers.each_line do |line|
86
+ key, value = line.split(":")
87
+ @headers[key] = value.to_s.strip
88
+ end
89
+ @body << more_body unless more_body.nil?
90
+ else
91
+ @data.last.match(/\n$/) ? @data << data : @data.last << data
92
+ end
93
+ self
94
+ end
95
+ end
96
+
97
+ class Session < SocketResponse
98
+ attr_accessor :replies
99
+ def initialize(data = "")
100
+ super
101
+ @replies = []
102
+ end
103
+
104
+ def <<(data)
105
+ if initiated?
106
+ if @replies.empty? or @replies.last.complete?
107
+ @replies << CommandReply.new(data)
108
+ else
109
+ @replies.last << data
110
+ end
111
+ else
112
+ super
113
+ end
114
+ end
115
+
116
+ def initiated?
117
+ @headers.keys.include?("Control")
118
+ end
119
+
120
+ end
121
+
122
+ class CommandReply < SocketResponse
123
+ # Set this to true for now, fill it in when we know what completed a reply
124
+ def complete?
125
+ true
126
+ end
127
+ end
128
+ end
129
+
130
+ end
131
+ end
@@ -0,0 +1,48 @@
1
+ module FSR
2
+ module Model
3
+ class Call
4
+ attr_reader :created, :created_epoch, :function, :caller_id_name, :caller_id_number,
5
+ :caller_destination, :caller_channel_name, :caller_uuid, :callee_id_name,
6
+ :callee_id_number, :callee_destination, :callee_channel_name, :callee_uuid
7
+ def initialize(created,
8
+ created_epoch,
9
+ function,
10
+ caller_cid_name,
11
+ caller_cid_num,
12
+ caller_dest_num,
13
+ caller_chan_name,
14
+ caller_uuid,
15
+ callee_cid_name,
16
+ callee_cid_num,
17
+ callee_dest_num,
18
+ callee_chan_name,
19
+ callee_uuid)
20
+ @created,
21
+ @created_epoch,
22
+ @function,
23
+ @caller_id_name,
24
+ @caller_id_number,
25
+ @caller_destination,
26
+ @caller_chan_name,
27
+ @caller_uuid,
28
+ @callee_id_name,
29
+ @callee_id_number,
30
+ @callee_destination,
31
+ @callee_channel_name,
32
+ @callee_uuid = created,
33
+ created_epoch,
34
+ function,
35
+ caller_cid_name,
36
+ caller_cid_num,
37
+ caller_dest_num,
38
+ caller_chan_name,
39
+ caller_uuid,
40
+ callee_cid_name,
41
+ callee_cid_num,
42
+ callee_dest_num,
43
+ callee_chan_name,
44
+ callee_uuid
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec/helper'
2
+ require "fsr/app"
3
+ FSR::App.load_application("hangup")
4
+
5
+ describe "Testing FSR::App::Hangup" do
6
+ it "Hangs up the channel" do
7
+ hangup = FSR::App::Hangup.new
8
+ hangup.sendmsg.should == "call-command: execute\nexecute-app-name: hangup\nexecute-app-arg: \n\n"
9
+ end
10
+
11
+ it "Hangs up the channel using a hangup cause" do
12
+ hangup = FSR::App::Hangup.new("USER_BUSY")
13
+ hangup.sendmsg.should == "call-command: execute\nexecute-app-name: hangup\nexecute-app-arg: USER_BUSY\n\n"
14
+ end
15
+
16
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec/helper'
2
+ require "fsr/app"
3
+ FSR::App.load_application("log")
4
+
5
+ describe "Testing FSR::App::Log" do
6
+ it "Logs to the console" do
7
+ log = FSR::App::Log.new(1, "This is a test! :-)")
8
+ log.sendmsg.should == "call-command: execute\nexecute-app-name: log\nexecute-app-arg: 1 This is a test! :-)\nevent-lock:true\n\n"
9
+ end
10
+
11
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec/helper'
2
+ require "fsr/app"
3
+ FSR::App.load_application("play_and_get_digits")
4
+
5
+ describe "Testing FSR::App::PlayAndGetDigits" do
6
+ # Utilize the [] shortcut to start a conference
7
+ it "Send a play_and_gets_digits command" do
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"
10
+ end
11
+
12
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec/helper'
2
+ require "fsr/app"
3
+ FSR::App.load_application("playback")
4
+
5
+ describe "Testing FSR::App::Playback" do
6
+ it "Plays a file or stream" do
7
+ playback = FSR::App::Playback.new("shout://scfire-ntc-aa01.stream.aol.com/stream/1035")
8
+ playback.sendmsg.should == "call-command: execute\nexecute-app-name: playback\nexecute-app-arg: shout://scfire-ntc-aa01.stream.aol.com/stream/1035\nevent-lock:true\n\n"
9
+ end
10
+
11
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec/helper'
2
+ require "fsr/app"
3
+ FSR::App.load_application("set")
4
+
5
+ describe "Testing FSR::App::Set" do
6
+ # Utilize the [] shortcut to start a conference
7
+ it "Sets a single variable" do
8
+ set = FSR::App::Set.new("hangup_after_bridge", true)
9
+ set.sendmsg.should == "call-command: execute\nexecute-app-name: set\nexecute-app-arg: hangup_after_bridge=true\nevent-lock:true\n\n"
10
+ end
11
+
12
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec/helper'
2
+ require "fsr/app"
3
+ FSR::App.load_application("transfer")
4
+
5
+ describe "Testing FSR::App::Transfer" do
6
+ it "Transfers the call" do
7
+ transfer = FSR::App::Transfer.new("500", "XML", "default")
8
+ transfer.sendmsg.should == "call-command: execute\nexecute-app-name: transfer\nexecute-app-arg: 500 XML default\nevent-lock:true\n\n"
9
+ end
10
+
11
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec/helper'
2
+ require "fsr/cmd"
3
+ FSR::Cmd.load_command("calls")
4
+
5
+ describe "Testing FSR::Cmd::Calls" do
6
+ ## Calls ##
7
+ # Interface to calls
8
+ it "FSR::Cmd::Calls should send show calls" do
9
+ sofia = FSR::Cmd::Calls.new
10
+ sofia.raw.should == "show calls"
11
+ end
12
+
13
+ end
@@ -16,4 +16,11 @@ describe "Testing FSR::Listener::Inbound" do
16
16
  FSR::Listener::Inbound.method_defined?(:post_init).should == true
17
17
  end
18
18
 
19
+ it "adds and deletes hooks" do
20
+ FSL::Inbound.add_event_hook(:CHANNEL_CREATE) {|event| puts event.inspect }
21
+ FSL::Inbound::HOOKS.size.should == 1
22
+ FSL::Inbound.del_event_hook(:CHANNEL_CREATE)
23
+ FSL::Inbound::HOOKS.size.should == 0
24
+ end
25
+
19
26
  end
data/spec/fsr/loading.rb CHANGED
@@ -4,7 +4,7 @@ require 'spec/helper'
4
4
  describe "Testing FSR module loading methods" do
5
5
  # When you add applications you must modify the expected apps_loaded behavior
6
6
  it "Loads all applications" do
7
- all_apps = [:set, :transfer, :speak, :fs_sleep, :playback, :answer, :fifo, :bridge, :hangup, :conference, :fs_break, :log]
7
+ all_apps = [:play_and_get_digits, :uuid_dump, :uuid_setvar, :uuid_getvar, :read, :set, :transfer, :speak, :fs_sleep, :playback, :answer, :fifo, :bridge, :hangup, :conference, :fs_break, :log]
8
8
  # Add any apps which will load to this set
9
9
  apps_loaded = FSR.load_all_applications
10
10
  apps_loaded.kind_of?(Array).should == true
@@ -16,7 +16,7 @@ describe "Testing FSR module loading methods" do
16
16
 
17
17
  # When you add commands you must modify the expected cmds_loaded behavior
18
18
  it "Loads all commands" do
19
- all_commands = [:originate, :sofia, :fsctl, :sofia_contact, :status] # If you add a command add it to this set
19
+ all_commands = [:originate, :sofia, :fsctl, :sofia_contact, :status, :calls] # If you add a command add it to this set
20
20
  cmds_loaded = FSR.load_all_commands
21
21
  cmds_loaded.kind_of?(Array).should == true
22
22
  all_commands.each do |cmd|
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bougyman-freeswitcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jayson Vaughn
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: "0"
27
27
  version:
28
- description: "========================================================= FreeSWITCHeR Copyright (c) 2009 The Rubyists (Jayson Vaughn, Tj Vanderpoel, Michael Fellinger, Kevin Berry) Distributed under the terms of the MIT License. ========================================================== About ----- *** STILL UNDER HEAVY DEVELOPMENT *** A ruby library for interacting with the \"FreeSWITCH\" (http://www.freeswitch.org) opensource telephony platform *** STILL UNDER HEAVY DEVELOPMENT *** Requirements ------------ - ruby (>= 1.8) - eventmachine (If you wish to use Outbound and Inbound listener) Usage ----- Example of originating a new call in 'irb' using FSR::CommandSocket#originate: irb(main):001:0> require 'fsr' => true irb(main):002:0> FSR.load_all_commands => [:sofia, :originate] irb(main):003:0> sock = FSR::CommandSocket.new => #<FSR::CommandSocket:0xb7a89104 @server=\"127.0.0.1\", @socket=#<TCPSocket:0xb7a8908c>, @port=\"8021\", @auth=\"ClueCon\"> irb(main):007:0> sock.originate(:target => 'sofia/gateway/carlos/8179395222', :endpoint => FSR::App::Bridge.new(\"user/bougyman\")).run => {\"Job-UUID\"=>\"732075a4-7dd5-4258-b124-6284a82a5ae7\", \"body\"=>\"\", \"Content-Type\"=>\"command/reply\", \"Reply-Text\"=>\"+OK Job-UUID: 732075a4-7dd5-4258-b124-6284a82a5ae7\"} Example of creating an Outbound Eventsocket listener: #!/usr/bin/env ruby require 'fsr' require \"fsr/listener/outbound\" class OesDemo < FSR::Listener::Outbound def session_initiated(session) number = session.headers[:caller_caller_id_number] # Grab the inbound caller id FSR::Log.info \"*** Answering incoming call from #{number}\" answer # Answer the call set \"hangup_after_bridge=true\" # Set a variable speak 'Hello, This is your phone switch. Have a great day' # use mod_flite to speak hangup # Hangup the call end end FSR.start_oes!(OesDemo, :port => 1888, :host => \"localhost\") Example of creating an Inbound Eventsocket listener: #!/usr/bin/env ruby require 'fsr' require \"fsr/listener/inbound\" class IesDemo < FSR::Listener::Inbound def on_event(event) pp event.headers pp event.content[:event_name] end end FSR.start_ies!(IesDemo, :host => \"localhost\", :port => 8021) Support ------- Home page at http://code.rubyists.com/projects/fs #rubyists on FreeNode"
28
+ description: "========================================================= FreeSWITCHeR Copyright (c) 2009 The Rubyists (Jayson Vaughn, Tj Vanderpoel, Michael Fellinger, Kevin Berry) Distributed under the terms of the MIT License. ========================================================== About ----- *** STILL UNDER HEAVY DEVELOPMENT *** A ruby library for interacting with the \"FreeSWITCH\" (http://www.freeswitch.org) opensource telephony platform *** STILL UNDER HEAVY DEVELOPMENT *** Requirements ------------ - ruby (>= 1.8) - eventmachine (If you wish to use Outbound and Inbound listener) Usage ----- Example of originating a new call in 'irb' using FSR::CommandSocket#originate: irb(main):001:0> require 'fsr' => true irb(main):002:0> FSR.load_all_commands => [:sofia, :originate] irb(main):003:0> sock = FSR::CommandSocket.new => #<FSR::CommandSocket:0xb7a89104 @server=\"127.0.0.1\", @socket=#<TCPSocket:0xb7a8908c>, @port=\"8021\", @auth=\"ClueCon\"> irb(main):007:0> sock.originate(:target => 'sofia/gateway/carlos/8179395222', :endpoint => FSR::App::Bridge.new(\"user/bougyman\")).run => {\"Job-UUID\"=>\"732075a4-7dd5-4258-b124-6284a82a5ae7\", \"body\"=>\"\", \"Content-Type\"=>\"command/reply\", \"Reply-Text\"=>\"+OK Job-UUID: 732075a4-7dd5-4258-b124-6284a82a5ae7\"} Example of creating an Outbound Eventsocket listener: #!/usr/bin/env ruby require 'fsr' require \"fsr/listener/outbound\" class OesDemo < FSR::Listener::Outbound def session_initiated number = @session.headers[:caller_caller_id_number] # Grab the inbound caller id FSR::Log.info \"*** Answering incoming call from #{number}\" answer # Answer the call set(\"hangup_after_bridge\", \"true\")# Set a variable speak 'Hello, This is your phone switch. Have a great day' # use mod_flite to speak hangup # Hangup the call end end FSR.start_oes!(OesDemo, :port => 1888, :host => \"localhost\") Example of creating an Outbound Eventsocket listener that can read DTMF input and keep state: #!/usr/bin/env ruby require 'fsr' require 'fsr/listener/outbound' FSR.load_all_applications FSR.load_all_commands class DtmfDemo < FSR::Listener::Outbound def session_initiated exten = @session.headers[:caller_caller_id_number] FSR::Log.info \"*** Answering incoming call from #{exten}\" answer # Answer the call end def receive_reply(reply) exten = @session.headers[:caller_caller_id_number] case @step when 1 FSR::Log.info \"*** Reading dtmf for #{exten}\" read \"/home/freeswitch/freeswitch/sounds/music/8000/sweet.wav\",4,10,\"test\",15000 # read test when 2 FSR::Log.info \"*** updating session for #{exten}\" update_session when 3 FSR::Log.info \"** Success, grabbed #{@session.headers[:variable_test].strip} from #{exten}\" FSR::Log.info \"*** Hanging up call\" hangup # Hangup the call end end end FSR.start_oes! DtmfDemo, :port => 8084, :host => \"127.0.0.1\" Example of creating an Inbound Eventsocket listener: #!/usr/bin/env ruby require 'fsr' require 'fsr/listener/inbound' require 'pp' class IesDemo < FSR::Listener::Inbound def on_event(event) pp event.headers pp event.content[:event_name] end end FSR.start_ies!(IesDemo, :host => \"localhost\", :port => 8021) Support ------- Home page at http://code.rubyists.com/projects/fs #rubyists on FreeNode"
29
29
  email: FreeSWITCHeR@rubyists.com
30
30
  executables: []
31
31
 
@@ -38,12 +38,18 @@ files:
38
38
  - NEWS
39
39
  - README
40
40
  - Rakefile
41
- - bin
42
- - bin/cmd_demo.rb
43
- - bin/ies_demo.rb
44
- - bin/ies_demo_with_hook.rb
45
- - bin/oes_demo.rb
46
- - bin/oes_demo2.rb
41
+ - examples
42
+ - examples/bin
43
+ - examples/bin/cmd_demo.rb
44
+ - examples/bin/dtmf_test.rb
45
+ - examples/bin/ies_demo.rb
46
+ - examples/bin/ies_demo_with_hook.rb
47
+ - examples/bin/oes_demo.rb
48
+ - examples/dtmf_test.rb
49
+ - examples/ies_demo.rb
50
+ - examples/ies_demo_with_hook.rb
51
+ - examples/oes_demo.rb
52
+ - examples/play_and_get_test.rb
47
53
  - lib
48
54
  - lib/fsr
49
55
  - lib/fsr/app
@@ -55,12 +61,18 @@ files:
55
61
  - lib/fsr/app/fs_sleep.rb
56
62
  - lib/fsr/app/hangup.rb
57
63
  - lib/fsr/app/log.rb
64
+ - lib/fsr/app/play_and_get_digits.rb
58
65
  - lib/fsr/app/playback.rb
66
+ - lib/fsr/app/read.rb
59
67
  - lib/fsr/app/set.rb
60
68
  - lib/fsr/app/speak.rb
61
69
  - lib/fsr/app/transfer.rb
70
+ - lib/fsr/app/uuid_dump.rb
71
+ - lib/fsr/app/uuid_getvar.rb
72
+ - lib/fsr/app/uuid_setvar.rb
62
73
  - lib/fsr/app.rb
63
74
  - lib/fsr/cmd
75
+ - lib/fsr/cmd/calls.rb
64
76
  - lib/fsr/cmd/fsctl.rb
65
77
  - lib/fsr/cmd/originate.rb
66
78
  - lib/fsr/cmd/sofia
@@ -88,13 +100,12 @@ files:
88
100
  - lib/fsr/listener/outbound.rb
89
101
  - lib/fsr/listener/outbound.rb.orig
90
102
  - lib/fsr/listener.rb
103
+ - lib/fsr/model
104
+ - lib/fsr/model/call.rb
91
105
  - lib/fsr.rb
92
106
  - tasks
93
107
  - tasks/package.rake
94
- - tasks/ride.rake
95
- - tasks/rspec.rake
96
108
  - tasks/spec.rake
97
- - tmp
98
109
  has_rdoc: false
99
110
  homepage: http://code.rubyists.com/projects/fs
100
111
  post_install_message: |
@@ -144,11 +155,11 @@ post_install_message: |
144
155
 
145
156
  class OesDemo < FSR::Listener::Outbound
146
157
 
147
- def session_initiated(session)
148
- number = session.headers[:caller_caller_id_number] # Grab the inbound caller id
158
+ def session_initiated
159
+ number = @session.headers[:caller_caller_id_number] # Grab the inbound caller id
149
160
  FSR::Log.info "*** Answering incoming call from #{number}"
150
161
  answer # Answer the call
151
- set "hangup_after_bridge=true" # Set a variable
162
+ set("hangup_after_bridge", "true")# Set a variable
152
163
  speak 'Hello, This is your phone switch. Have a great day' # use mod_flite to speak
153
164
  hangup # Hangup the call
154
165
  end
@@ -159,12 +170,53 @@ post_install_message: |
159
170
 
160
171
 
161
172
 
173
+ Example of creating an Outbound Eventsocket listener that can read DTMF input and keep state:
174
+
175
+ #!/usr/bin/env ruby
176
+
177
+ require 'fsr'
178
+ require 'fsr/listener/outbound'
179
+
180
+ FSR.load_all_applications
181
+ FSR.load_all_commands
182
+
183
+ class DtmfDemo < FSR::Listener::Outbound
184
+
185
+ def session_initiated
186
+ exten = @session.headers[:caller_caller_id_number]
187
+ FSR::Log.info "*** Answering incoming call from #{exten}"
188
+ answer # Answer the call
189
+ end
190
+
191
+ def receive_reply(reply)
192
+ exten = @session.headers[:caller_caller_id_number]
193
+ case @step
194
+ when 1
195
+ FSR::Log.info "*** Reading dtmf for #{exten}"
196
+ read "/home/freeswitch/freeswitch/sounds/music/8000/sweet.wav",4,10,"test",15000 # read test
197
+ when 2
198
+ FSR::Log.info "*** updating session for #{exten}"
199
+ update_session
200
+ when 3
201
+ FSR::Log.info "** Success, grabbed #{@session.headers[:variable_test].strip} from #{exten}"
202
+ FSR::Log.info "*** Hanging up call"
203
+ hangup # Hangup the call
204
+ end
205
+ end
206
+
207
+ end
208
+
209
+ FSR.start_oes! DtmfDemo, :port => 8084, :host => "127.0.0.1"
210
+
211
+
212
+
162
213
  Example of creating an Inbound Eventsocket listener:
163
214
 
164
215
  #!/usr/bin/env ruby
165
216
 
166
217
  require 'fsr'
167
- require "fsr/listener/inbound"
218
+ require 'fsr/listener/inbound'
219
+ require 'pp'
168
220
 
169
221
  class IesDemo < FSR::Listener::Inbound
170
222
 
@@ -214,8 +266,15 @@ test_files:
214
266
  - spec/fsr/app/bridge.rb
215
267
  - spec/fsr/app/conference.rb
216
268
  - spec/fsr/app/fifo.rb
269
+ - spec/fsr/app/hangup.rb
270
+ - spec/fsr/app/log.rb
271
+ - spec/fsr/app/play_and_get_digits.rb
272
+ - spec/fsr/app/playback.rb
273
+ - spec/fsr/app/set.rb
274
+ - spec/fsr/app/transfer.rb
217
275
  - spec/fsr/app.rb
218
276
  - spec/fsr/cmd
277
+ - spec/fsr/cmd/calls.rb
219
278
  - spec/fsr/cmd/originate.rb
220
279
  - spec/fsr/cmd/sofia
221
280
  - spec/fsr/cmd/sofia/profile.rb