bougyman-freeswitcher 0.0.9 → 0.1.0

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