FreeSWITCHeR 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/License.txt +20 -0
- data/NEWS +3 -0
- data/README +85 -0
- data/Rakefile +82 -0
- data/bin/cmd_demo.rb +19 -0
- data/bin/ies_demo.rb +19 -0
- data/bin/ies_demo_with_hook.rb +27 -0
- data/bin/oes_demo.rb +22 -0
- data/lib/fsr.rb +86 -0
- data/lib/fsr/app.rb +72 -0
- data/lib/fsr/app/answer.rb +21 -0
- data/lib/fsr/app/bridge.rb +33 -0
- data/lib/fsr/app/conference.rb +29 -0
- data/lib/fsr/app/fifo.rb +38 -0
- data/lib/fsr/app/fs_break.rb +21 -0
- data/lib/fsr/app/fs_sleep.rb +24 -0
- data/lib/fsr/app/hangup.rb +22 -0
- data/lib/fsr/app/log.rb +24 -0
- data/lib/fsr/app/playback.rb +23 -0
- data/lib/fsr/app/set.rb +22 -0
- data/lib/fsr/app/speak.rb +25 -0
- data/lib/fsr/app/transfer.rb +24 -0
- data/lib/fsr/cmd.rb +57 -0
- data/lib/fsr/cmd/fsctl.rb +41 -0
- data/lib/fsr/cmd/originate.rb +46 -0
- data/lib/fsr/cmd/sofia.rb +40 -0
- data/lib/fsr/cmd/sofia/profile.rb +75 -0
- data/lib/fsr/cmd/sofia/status.rb +39 -0
- data/lib/fsr/cmd/sofia_contact.rb +31 -0
- data/lib/fsr/cmd/status.rb +25 -0
- data/lib/fsr/command_socket.rb +29 -0
- data/lib/fsr/database.rb +7 -0
- data/lib/fsr/database/call_limit.rb +21 -0
- data/lib/fsr/database/core.rb +30 -0
- data/lib/fsr/database/sofia_reg_external.rb +0 -0
- data/lib/fsr/database/sofia_reg_internal.rb +0 -0
- data/lib/fsr/database/voicemail_default.rb +0 -0
- data/lib/fsr/event_socket.rb +41 -0
- data/lib/fsr/fake_socket.rb +68 -0
- data/lib/fsr/listener.rb +10 -0
- data/lib/fsr/listener/header_and_content_response.rb +21 -0
- data/lib/fsr/listener/inbound.rb +61 -0
- data/lib/fsr/listener/inbound/event.rb +42 -0
- data/lib/fsr/listener/outbound.rb +62 -0
- data/spec/fsr/app.rb +8 -0
- data/spec/fsr/app/bridge.rb +17 -0
- data/spec/fsr/app/conference.rb +12 -0
- data/spec/fsr/app/fifo.rb +29 -0
- data/spec/fsr/cmd.rb +8 -0
- data/spec/fsr/cmd/originate.rb +12 -0
- data/spec/fsr/cmd/sofia.rb +69 -0
- data/spec/fsr/cmd/sofia/profile.rb +58 -0
- data/spec/fsr/listener.rb +12 -0
- data/spec/fsr/listener/inbound.rb +19 -0
- data/spec/fsr/listener/outbound.rb +19 -0
- data/spec/fsr/loading.rb +27 -0
- data/spec/helper.rb +14 -0
- data/tasks/package.rake +29 -0
- data/tasks/spec.rake +59 -0
- metadata +224 -0
data/License.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 rubyists.com (TJ Vanderpoel, Jayson Vaughn, Michael Fellinger, Kevin Berry)
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/NEWS
ADDED
data/README
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
=========================================================
|
2
|
+
FreeSWITCHeR
|
3
|
+
Copyright (c) 2009 The Rubyists (Jayson Vaughn, Tj Vanderpoel, Michael Fellinger, Kevin Berry)
|
4
|
+
Distributed under the terms of the MIT License.
|
5
|
+
==========================================================
|
6
|
+
|
7
|
+
About
|
8
|
+
-----
|
9
|
+
*** STILL UNDER HEAVY DEVELOPMENT ***
|
10
|
+
|
11
|
+
A ruby library for interacting with the "FreeSWITCH" (http://www.freeswitch.org) opensource telephony platform
|
12
|
+
|
13
|
+
*** STILL UNDER HEAVY DEVELOPMENT ***
|
14
|
+
|
15
|
+
Requirements
|
16
|
+
------------
|
17
|
+
- ruby (>= 1.8)
|
18
|
+
- eventmachine (If you wish to use Outbound and Inbound listener)
|
19
|
+
|
20
|
+
Usage
|
21
|
+
-----
|
22
|
+
|
23
|
+
Example of originating a new call in 'irb' using FSR::CommandSocket#originate:
|
24
|
+
|
25
|
+
irb(main):001:0> require 'fsr'
|
26
|
+
=> true
|
27
|
+
|
28
|
+
irb(main):002:0> FSR.load_all_commands
|
29
|
+
=> [:sofia, :originate]
|
30
|
+
|
31
|
+
irb(main):003:0> sock = FSR::CommandSocket.new
|
32
|
+
=> #<FSR::CommandSocket:0xb7a89104 @server="127.0.0.1", @socket=#<TCPSocket:0xb7a8908c>, @port="8021", @auth="ClueCon">
|
33
|
+
|
34
|
+
irb(main):007:0> sock.originate(:target => 'sofia/gateway/carlos/8179395222', :endpoint => FSR::App::Bridge.new("user/bougyman")).run
|
35
|
+
=> {"Job-UUID"=>"732075a4-7dd5-4258-b124-6284a82a5ae7", "body"=>"", "Content-Type"=>"command/reply", "Reply-Text"=>"+OK Job-UUID: 732075a4-7dd5-4258-b124-6284a82a5ae7"}
|
36
|
+
|
37
|
+
|
38
|
+
Example of creating an Outbound Eventsocket listener:
|
39
|
+
|
40
|
+
#!/usr/bin/env ruby
|
41
|
+
|
42
|
+
require 'fsr'
|
43
|
+
require "fsr/listener/outbound"
|
44
|
+
|
45
|
+
class OesDemo < FSR::Listener::Outbound
|
46
|
+
|
47
|
+
def session_initiated(session)
|
48
|
+
number = session.headers[:caller_caller_id_number] # Grab the inbound caller id
|
49
|
+
FSR::Log.info "*** Answering incoming call from #{number}"
|
50
|
+
answer # Answer the call
|
51
|
+
set "hangup_after_bridge=true" # Set a variable
|
52
|
+
speak 'Hello, This is your phone switch. Have a great day' # use mod_flite to speak
|
53
|
+
hangup # Hangup the call
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
FSR.start_oes!(OesDemo, :port => 1888, :host => "localhost")
|
59
|
+
|
60
|
+
|
61
|
+
|
62
|
+
Example of creating an Inbound Eventsocket listener:
|
63
|
+
|
64
|
+
#!/usr/bin/env ruby
|
65
|
+
|
66
|
+
require 'fsr'
|
67
|
+
require "fsr/listener/inbound"
|
68
|
+
|
69
|
+
class IesDemo < FSR::Listener::Inbound
|
70
|
+
|
71
|
+
def on_event(event)
|
72
|
+
pp event.headers
|
73
|
+
pp event.content[:event_name]
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
FSR.start_ies!(IesDemo, :host => "localhost", :port => 8021)
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
Support
|
83
|
+
-------
|
84
|
+
Home page at http://code.rubyists.com/projects/fs
|
85
|
+
#rubyists on FreeNode
|
data/Rakefile
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'rake/clean'
|
2
|
+
require "rubygems"
|
3
|
+
|
4
|
+
import(*Dir['tasks/*rake'])
|
5
|
+
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
desc 'install dependencies'
|
9
|
+
task :setup do
|
10
|
+
GemSetup.new do
|
11
|
+
github = 'http://gems.github.com'
|
12
|
+
Gem.sources << github
|
13
|
+
|
14
|
+
gem('bacon')
|
15
|
+
setup
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class GemSetup
|
20
|
+
def initialize(options = {}, &block)
|
21
|
+
@gems = []
|
22
|
+
@options = options
|
23
|
+
|
24
|
+
run(&block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def run(&block)
|
28
|
+
instance_eval(&block) if block_given?
|
29
|
+
end
|
30
|
+
|
31
|
+
def gem(name, version = nil, options = {})
|
32
|
+
if version.respond_to?(:merge!)
|
33
|
+
options = version
|
34
|
+
else
|
35
|
+
options[:version] = version
|
36
|
+
end
|
37
|
+
|
38
|
+
@gems << [name, options]
|
39
|
+
end
|
40
|
+
|
41
|
+
def setup
|
42
|
+
require 'rubygems'
|
43
|
+
require 'rubygems/dependency_installer'
|
44
|
+
|
45
|
+
@gems.each do |name, options|
|
46
|
+
setup_gem(name, options)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def setup_gem(name, options, try_install = true)
|
51
|
+
print "activating #{name} ... "
|
52
|
+
Gem.activate(name, *[options[:version]].compact)
|
53
|
+
require(options[:lib] || name)
|
54
|
+
puts "success."
|
55
|
+
rescue LoadError => error
|
56
|
+
puts error
|
57
|
+
install_gem(name, options) if try_install
|
58
|
+
setup_gem(name, options, try_install = false)
|
59
|
+
end
|
60
|
+
|
61
|
+
def install_gem(name, options)
|
62
|
+
installer = Gem::DependencyInstaller.new(options)
|
63
|
+
|
64
|
+
temp_argv(options[:extconf]) do
|
65
|
+
print "Installing #{name} ... "
|
66
|
+
installer.install(name, options[:version])
|
67
|
+
puts "done."
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def temp_argv(extconf)
|
72
|
+
if extconf ||= @options[:extconf]
|
73
|
+
old_argv = ARGV.clone
|
74
|
+
ARGV.replace(extconf.split(' '))
|
75
|
+
end
|
76
|
+
|
77
|
+
yield
|
78
|
+
|
79
|
+
ensure
|
80
|
+
ARGV.replace(old_argv) if extconf
|
81
|
+
end
|
82
|
+
end
|
data/bin/cmd_demo.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'pp'
|
4
|
+
require File.join(File.dirname(__FILE__), "..", 'lib', 'fsr')
|
5
|
+
require "fsr/cmd"
|
6
|
+
|
7
|
+
FSR.load_all_commands
|
8
|
+
sock = FSR::CommandSocket.new
|
9
|
+
|
10
|
+
# Check the status of our server
|
11
|
+
pp sock.status.run
|
12
|
+
|
13
|
+
# Check max sessions
|
14
|
+
pp sock.fsctl.max_sessions
|
15
|
+
# Set max sessions
|
16
|
+
pp sock.fsctl.max_sessions = 3000
|
17
|
+
|
18
|
+
# Check up a sofia user
|
19
|
+
pp sock.sofia_contact(:contact => "internal/user@domain.com").run
|
data/bin/ies_demo.rb
ADDED
@@ -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,27 @@
|
|
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
|
+
|
24
|
+
|
25
|
+
# Start FSR Inbound Listener
|
26
|
+
FSR.start_ies!(FSL::Inbound, :host => "localhost", :port => 8021)
|
27
|
+
|
data/bin/oes_demo.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), "..", 'lib', 'fsr')
|
4
|
+
puts $LOAD_PATH.inspect
|
5
|
+
$stdout.flush
|
6
|
+
require "fsr/listener/outbound"
|
7
|
+
|
8
|
+
class OesDemo < FSR::Listener::Outbound
|
9
|
+
|
10
|
+
def session_initiated(session)
|
11
|
+
number = session.headers[:caller_caller_id_number] # Grab the inbound caller id
|
12
|
+
FSR::Log.info "*** Answering incoming call from #{number}"
|
13
|
+
answer # Answer the call
|
14
|
+
log("1", "Pong from the FSR event socket!")
|
15
|
+
set("hangup_after_bridge", "true") # Set a variable
|
16
|
+
speak 'Hello, This is your phone switch. Have a great day' # use mod_flite to speak
|
17
|
+
hangup # Hangup the call
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
FSR.start_oes!(OesDemo, :port => 1888, :host => "localhost")
|
data/lib/fsr.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'socket'
|
3
|
+
require 'pathname'
|
4
|
+
require 'pp'
|
5
|
+
|
6
|
+
module FSR
|
7
|
+
# Global configuration options
|
8
|
+
#
|
9
|
+
VERSION = '0.0.8'
|
10
|
+
FS_INSTALL_PATHS = ["/usr/local/freeswitch", "/opt/freeswitch", "/usr/freeswitch"]
|
11
|
+
DEFAULT_CALLER_ID_NUMBER = '8675309'
|
12
|
+
DEFAULT_CALLER_ID_NAME = "FSR"
|
13
|
+
|
14
|
+
# Usage:
|
15
|
+
#
|
16
|
+
# Log.info('foo')
|
17
|
+
# Log.debug('bar')
|
18
|
+
# Log.warn('foobar')
|
19
|
+
# Log.error('barfoo')
|
20
|
+
Log = Logger.new($stdout)
|
21
|
+
Log.level = Logger::INFO
|
22
|
+
|
23
|
+
ROOT = Pathname(__FILE__).dirname.expand_path.freeze
|
24
|
+
$LOAD_PATH.unshift(FSR::ROOT)
|
25
|
+
|
26
|
+
# Load all FSR::Cmd classes
|
27
|
+
def self.load_all_commands(retrying = false)
|
28
|
+
require 'fsr/command_socket'
|
29
|
+
load_all_applications
|
30
|
+
Cmd.load_all
|
31
|
+
end
|
32
|
+
|
33
|
+
# Load all FSR::App classes
|
34
|
+
def self.load_all_applications
|
35
|
+
require "fsr/app"
|
36
|
+
App.load_all
|
37
|
+
end
|
38
|
+
|
39
|
+
# Method to start EM for Outbound Event Socket
|
40
|
+
def self.start_oes!(klass, args = {})
|
41
|
+
port = args[:port] || "8084"
|
42
|
+
host = args[:host] || "localhost"
|
43
|
+
EM.run do
|
44
|
+
EventMachine::start_server(host, port, klass)
|
45
|
+
FSR::Log.info "*** FreeSWITCHer Outbound EventSocket Listener on #{host}:#{port} ***"
|
46
|
+
FSR::Log.info "*** http://code.rubyists.com/projects/fs"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Method to start EM for Inbound Event Socket
|
51
|
+
def self.start_ies!(klass, args = {})
|
52
|
+
port = args[:port] || "8021"
|
53
|
+
host = args[:host] || "localhost"
|
54
|
+
EM.run do
|
55
|
+
EventMachine::connect(host, port, klass)
|
56
|
+
FSR::Log.info "*** FreeSWITCHer Inbound EventSocket Listener connected to #{host}:#{port} ***"
|
57
|
+
FSR::Log.info "*** http://code.rubyists.com/projects/fs"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Find the FreeSWITCH install path if running FSR on a local box with FreeSWITCH installed.
|
64
|
+
# This will enable sqlite db access
|
65
|
+
def self.find_freeswitch_install
|
66
|
+
good_path = FS_INSTALL_PATHS.find do |fs_path|
|
67
|
+
Log.warn("#{fs_path} is not a directory!") if File.exists?(fs_path) && !File.directory?(fs_path)
|
68
|
+
Log.warn("#{fs_path} is not readable by this user!") if File.exists?(fs_path) && !File.readable?(fs_path)
|
69
|
+
Dir["#{fs_path}/{conf,db}/"].size == 2
|
70
|
+
end
|
71
|
+
if good_path.nil?
|
72
|
+
Log.warn("No FreeSWITCH install found, database and configuration functionality disabled")
|
73
|
+
return nil
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
FS_ROOT = find_freeswitch_install # FreeSWITCH $${base_dir}
|
78
|
+
|
79
|
+
if FS_ROOT
|
80
|
+
FS_CONFIG_PATH = (FS_ROOT + 'conf').freeze # FreeSWITCH conf dir
|
81
|
+
FS_DB_PATH = (FS_ROOT + 'db').freeze # FreeSWITCH db dir
|
82
|
+
else
|
83
|
+
FS_CONFIG_PATH = FS_DB_PATH = nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
data/lib/fsr/app.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
module FSR
|
2
|
+
module App
|
3
|
+
class Application
|
4
|
+
def to_s
|
5
|
+
sendmsg
|
6
|
+
end
|
7
|
+
|
8
|
+
def app_name
|
9
|
+
self.class.name.split("::").last.downcase
|
10
|
+
end
|
11
|
+
|
12
|
+
# This method builds the API command to send to freeswitch
|
13
|
+
def raw
|
14
|
+
"%s(%s)" % [app_name, arguments.join(" ")]
|
15
|
+
end
|
16
|
+
|
17
|
+
def sendmsg
|
18
|
+
"call-command: execute\nexecute-app-name: %s\nexecute-app-arg: %s\n\n" % [app_name, arguments.join(" ")]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
APPLICATIONS = {}
|
23
|
+
LOAD_PATH = [FSR::ROOT.join("fsr/app")]
|
24
|
+
REGISTER_CODE = "def %s(*args, &block); APPLICATIONS[%p].new(*args, &block); end"
|
25
|
+
|
26
|
+
def self.register(application, obj)
|
27
|
+
APPLICATIONS[application.to_sym] = obj
|
28
|
+
|
29
|
+
code = REGISTER_CODE % [application, application]
|
30
|
+
App.module_eval(code)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.list
|
34
|
+
APPLICATIONS.keys
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.load_application(application, force_reload = false)
|
38
|
+
exception = nil
|
39
|
+
|
40
|
+
if Pathname(application).absolute?
|
41
|
+
glob = application
|
42
|
+
else
|
43
|
+
glob = "{#{LOAD_PATH.join(',')}}/#{application}.{so,rb,bundle}"
|
44
|
+
end
|
45
|
+
|
46
|
+
Dir[glob].each do |file|
|
47
|
+
begin
|
48
|
+
return force_reload ? load(file) : require(file)
|
49
|
+
rescue LoadError => exception
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
raise("Couldn't find %s in %p" % [application, LOAD_PATH])
|
54
|
+
end
|
55
|
+
|
56
|
+
# Load all of the applications we find in App::LOAD_PATH
|
57
|
+
def self.load_all(force_reload = false)
|
58
|
+
glob = "{#{LOAD_PATH.join(',')}}/*.{so,rb,bundle}"
|
59
|
+
|
60
|
+
Dir[glob].each do |file|
|
61
|
+
force_reload ? load(file) : require(file)
|
62
|
+
end
|
63
|
+
|
64
|
+
list
|
65
|
+
end
|
66
|
+
|
67
|
+
def applications
|
68
|
+
FSR::App.list
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
FSA = FSR::App
|