freec 0.2.6 → 0.2.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +39 -0
- data/LICENSE.txt +21 -0
- data/README.rdoc +1 -1
- data/Rakefile +4 -40
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/freec.gemspec +30 -0
- data/lib/freec.rb +6 -7
- data/lib/freec/version.rb +3 -0
- data/lib/freec_base.rb +45 -46
- data/lib/freeswitch_applications.rb +19 -19
- metadata +82 -35
- data/spec/data/event.rb +0 -56
- data/spec/data/event_with_body.rb +0 -25
- data/spec/event_parser_spec.rb +0 -84
- data/spec/freec_base_spec.rb +0 -213
- data/spec/freeswitch_applications_spec.rb +0 -128
- data/spec/spec_helper.rb +0 -12
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2914222ca5a7510ef0738b9d28eeb18fe5c41e03
|
4
|
+
data.tar.gz: 973b2970fb112031e662cf533f87e922a2e6fa60
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 145213091d5bca8cb8f56211f3cb1539d90a3576f60ac6a5b7c5e8baffb6abf30cf62618c930f20dd4298f28670978c45772a889db0975d7da4276ced64c5287
|
7
|
+
data.tar.gz: a4d1df85b3f5083b3addb6227411fd45cd819b74b16bf3776f428e8671139f4e7beca0e4374961391c0e9cd3cf5b510151e0228c519f926afc100e1d2b27ae05
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
freec (0.2.7)
|
5
|
+
daemons (~> 1.2.4)
|
6
|
+
gserver (~> 0.0.1)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
daemons (1.2.4)
|
12
|
+
diff-lcs (1.3)
|
13
|
+
gserver (0.0.1)
|
14
|
+
rake (10.5.0)
|
15
|
+
rspec (3.5.0)
|
16
|
+
rspec-core (~> 3.5.0)
|
17
|
+
rspec-expectations (~> 3.5.0)
|
18
|
+
rspec-mocks (~> 3.5.0)
|
19
|
+
rspec-core (3.5.4)
|
20
|
+
rspec-support (~> 3.5.0)
|
21
|
+
rspec-expectations (3.5.0)
|
22
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
23
|
+
rspec-support (~> 3.5.0)
|
24
|
+
rspec-mocks (3.5.0)
|
25
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
26
|
+
rspec-support (~> 3.5.0)
|
27
|
+
rspec-support (3.5.0)
|
28
|
+
|
29
|
+
PLATFORMS
|
30
|
+
ruby
|
31
|
+
|
32
|
+
DEPENDENCIES
|
33
|
+
bundler (~> 1.14)
|
34
|
+
freec!
|
35
|
+
rake (~> 10.0)
|
36
|
+
rspec (~> 3.0)
|
37
|
+
|
38
|
+
BUNDLED WITH
|
39
|
+
1.14.4
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Jan Kubr
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.rdoc
CHANGED
@@ -58,7 +58,7 @@ If you return nil or false from the step method, the call will be hungup.
|
|
58
58
|
|
59
59
|
= Stinks?
|
60
60
|
What Freec can do at the moment was enough for me to implement Telfa (http://telfapbx.com). If it doesn't fit your needs, you can:
|
61
|
-
1. Complain to
|
61
|
+
1. Complain to mail@jankubr.com .
|
62
62
|
2. Fork it and change yourself.
|
63
63
|
3. Check Liverpie (http://www.liverpie.com/) or Telegraph (http://code.google.com/p/telegraph/), Freec is inspired by them.
|
64
64
|
4. Write yours :-)
|
data/Rakefile
CHANGED
@@ -1,42 +1,6 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
3
|
|
4
|
-
|
5
|
-
task :clean do |t|
|
6
|
-
FileUtils.rm_rf "tmp"
|
7
|
-
FileUtils.rm_rf "pkg"
|
8
|
-
end
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
9
5
|
|
10
|
-
|
11
|
-
s.name = "freec"
|
12
|
-
s.version = '0.2.6'
|
13
|
-
s.author = "Jan Kubr"
|
14
|
-
s.email = "mail@jankubr.com"
|
15
|
-
s.homepage = "http://github.com/jankubr/freec"
|
16
|
-
s.platform = Gem::Platform::RUBY
|
17
|
-
s.summary = "The layer between your Ruby voice app and FreeSWITCH."
|
18
|
-
s.files = FileList["README*",
|
19
|
-
"Rakefile",
|
20
|
-
"{lib,spec}/**/*"].to_a
|
21
|
-
s.require_path = "lib"
|
22
|
-
s.rubyforge_project = "freec"
|
23
|
-
s.has_rdoc = false
|
24
|
-
s.extra_rdoc_files = FileList["README*"].to_a
|
25
|
-
s.rdoc_options << '--line-numbers' << '--inline-source'
|
26
|
-
s.add_dependency "daemons"
|
27
|
-
s.add_development_dependency 'rspec', '> 2.0.1'
|
28
|
-
end
|
29
|
-
|
30
|
-
desc "Generate a gemspec file"
|
31
|
-
task :gemspec do
|
32
|
-
File.open("#{spec.name}.gemspec", 'w') do |f|
|
33
|
-
f.write spec.to_ruby
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
require 'rspec/core/rake_task'
|
38
|
-
|
39
|
-
desc "Run all specs"
|
40
|
-
RSpec::Core::RakeTask.new(:spec) do |t|
|
41
|
-
t.pattern = FileList['spec/**/*.rb']
|
42
|
-
end
|
6
|
+
task default: :spec
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "freec"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/freec.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'freec/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "freec"
|
8
|
+
spec.version = Freec::VERSION
|
9
|
+
spec.authors = ["Jan Kubr"]
|
10
|
+
spec.email = ["mail@jankubr.com"]
|
11
|
+
|
12
|
+
spec.summary = "The layer between your Ruby voice app and FreeSWITCH."
|
13
|
+
spec.description = "The layer between your Ruby voice app and FreeSWITCH."
|
14
|
+
spec.homepage = "http://github.com/jankubr/freec"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
18
|
+
f.match(%r{^(test|spec|features)/})
|
19
|
+
end
|
20
|
+
spec.bindir = "exe"
|
21
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_runtime_dependency 'gserver', '~> 0.0.1'
|
25
|
+
spec.add_runtime_dependency 'daemons', '~> 1.2.4'
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.14"
|
28
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
29
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
30
|
+
end
|
data/lib/freec.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
require 'freec/version'
|
1
2
|
lib_dir = File.dirname(__FILE__)
|
2
3
|
$LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
|
3
|
-
require
|
4
|
+
require 'gserver'
|
5
|
+
require 'freec_base'
|
4
6
|
require 'listener'
|
5
7
|
|
6
8
|
require 'fileutils'
|
@@ -9,7 +11,7 @@ require 'daemons/daemonize'
|
|
9
11
|
include Daemonize
|
10
12
|
|
11
13
|
def freec_app_file_name
|
12
|
-
$0.sub(/\.[^\.]*$/, '')
|
14
|
+
$0.sub(/\.[^\.]*$/, '')
|
13
15
|
end
|
14
16
|
|
15
17
|
def freec_app_class_name
|
@@ -21,7 +23,7 @@ def freec_app_log_dir
|
|
21
23
|
end
|
22
24
|
|
23
25
|
def create_freec_app_log_dir
|
24
|
-
FileUtils.mkdir_p(freec_app_log_dir)
|
26
|
+
FileUtils.mkdir_p(freec_app_log_dir)
|
25
27
|
end
|
26
28
|
|
27
29
|
def freec_app_log_file
|
@@ -59,9 +61,6 @@ unless defined?(TEST)
|
|
59
61
|
server = Listener.new(freec_app_class_name, @@config)
|
60
62
|
server.audit = true
|
61
63
|
server.start
|
62
|
-
|
63
|
-
break if server.stopped?
|
64
|
-
sleep(1)
|
65
|
-
end
|
64
|
+
server.join
|
66
65
|
end
|
67
66
|
end
|
data/lib/freec_base.rb
CHANGED
@@ -1,28 +1,27 @@
|
|
1
1
|
require 'gserver'
|
2
|
-
require 'rubygems'
|
3
2
|
require 'uri'
|
4
3
|
|
5
4
|
require 'tools'
|
6
|
-
require
|
7
|
-
require
|
5
|
+
require 'freeswitch_applications'
|
6
|
+
require 'call_variables'
|
8
7
|
|
9
|
-
class
|
8
|
+
class FreecBase
|
10
9
|
include FreeswitchApplications
|
11
10
|
include CallVariables
|
12
|
-
|
11
|
+
|
13
12
|
attr_reader :call_vars, :event_body, :log, :config
|
14
|
-
|
13
|
+
|
15
14
|
def initialize(io, log, config) #:nodoc:
|
16
15
|
@call_vars = {}
|
17
16
|
@want_events_from = []
|
18
17
|
@last_app_executed = 'initial_step'
|
19
|
-
@io = io
|
18
|
+
@io = io
|
20
19
|
@log = log
|
21
20
|
@config = config
|
22
21
|
end
|
23
|
-
|
22
|
+
|
24
23
|
def handle_call #:nodoc:
|
25
|
-
call_initialization
|
24
|
+
call_initialization
|
26
25
|
loop do
|
27
26
|
subscribe_to_new_channel_events
|
28
27
|
if last_event_dtmf? && respond_to?(:on_dtmf)
|
@@ -38,7 +37,7 @@ class Freec
|
|
38
37
|
hangup unless @io.closed?
|
39
38
|
send_and_read('exit') unless @io.closed?
|
40
39
|
end
|
41
|
-
|
40
|
+
|
42
41
|
def wait_for(key, value)
|
43
42
|
@waiting_for_key = key && key.to_sym
|
44
43
|
@waiting_for_value = value
|
@@ -46,13 +45,13 @@ class Freec
|
|
46
45
|
|
47
46
|
def reset_wait_for
|
48
47
|
wait_for(nil, nil)
|
49
|
-
true
|
50
|
-
end
|
51
|
-
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
52
51
|
def execute_completed?
|
53
52
|
channel_execute_complete? || channel_destroyed_after_bridge? || disconnect_notice?
|
54
53
|
end
|
55
|
-
|
54
|
+
|
56
55
|
private
|
57
56
|
|
58
57
|
def call_initialization
|
@@ -62,17 +61,17 @@ private
|
|
62
61
|
|
63
62
|
def channel_execute_complete?
|
64
63
|
return true if @last_app_executed == 'initial_step'
|
65
|
-
complete = call_vars[:content_type] == 'text/event-plain' &&
|
64
|
+
complete = call_vars[:content_type] == 'text/event-plain' &&
|
66
65
|
call_vars[:event_name] == 'CHANNEL_EXECUTE_COMPLETE' &&
|
67
66
|
@last_app_executed == call_vars[:application]
|
68
67
|
@last_app_executed = nil if complete
|
69
68
|
complete
|
70
69
|
end
|
71
|
-
|
70
|
+
|
72
71
|
def channel_destroyed_after_bridge?
|
73
72
|
call_vars[:application] == 'bridge' && call_vars[:event_name] == 'CHANNEL_DESTROY'
|
74
73
|
end
|
75
|
-
|
74
|
+
|
76
75
|
def disconnect_notice?
|
77
76
|
@io.closed? || call_vars[:content_type] == 'text/disconnect-notice'
|
78
77
|
end
|
@@ -81,7 +80,7 @@ private
|
|
81
80
|
send(callback_name, *args) if respond_to?(callback_name)
|
82
81
|
rescue StandardError => e
|
83
82
|
log.error e.message
|
84
|
-
e.backtrace.each {|trace_line| log.error(trace_line)}
|
83
|
+
e.backtrace.each {|trace_line| log.error(trace_line)}
|
85
84
|
end
|
86
85
|
|
87
86
|
def reload_application_code
|
@@ -89,7 +88,7 @@ private
|
|
89
88
|
load($0)
|
90
89
|
lib_dir = "#{ROOT}/lib"
|
91
90
|
return unless File.exist?(lib_dir)
|
92
|
-
Dir.open(lib_dir).each do |file|
|
91
|
+
Dir.open(lib_dir).each do |file|
|
93
92
|
full_file_name = File.join(lib_dir, file)
|
94
93
|
next unless File.file?(full_file_name)
|
95
94
|
load(full_file_name)
|
@@ -100,62 +99,62 @@ private
|
|
100
99
|
send_and_read('connect')
|
101
100
|
parse_response
|
102
101
|
end
|
103
|
-
|
102
|
+
|
104
103
|
def subscribe_to_events
|
105
104
|
send_and_read('events plain all')
|
106
|
-
parse_response
|
107
|
-
send_and_read("filter Unique-ID #{@unique_id}")
|
108
|
-
parse_response
|
109
|
-
send_and_read("divert_events on")
|
105
|
+
parse_response
|
106
|
+
send_and_read("filter Unique-ID #{@unique_id}")
|
107
|
+
parse_response
|
108
|
+
send_and_read("divert_events on")
|
110
109
|
parse_response
|
111
110
|
end
|
112
|
-
|
111
|
+
|
113
112
|
def subscribe_to_new_channel_events
|
114
113
|
return unless call_vars[:event_name] == 'CHANNEL_BRIDGE'
|
115
114
|
@want_events_from << call_vars[:other_leg_unique_id]
|
116
115
|
send_and_read("filter Unique-ID #{call_vars[:other_leg_unique_id]}")
|
117
116
|
end
|
118
|
-
|
117
|
+
|
119
118
|
def waiting_for_this_response?
|
120
119
|
@waiting_for_key && @waiting_for_value && call_vars[@waiting_for_key] == @waiting_for_value
|
121
120
|
end
|
122
|
-
|
121
|
+
|
123
122
|
def last_event_dtmf?
|
124
123
|
call_vars[:content_type] == 'text/event-plain' && call_vars[:event_name] == 'DTMF'
|
125
124
|
end
|
126
|
-
|
125
|
+
|
127
126
|
def send_data(data)
|
128
127
|
log.debug "Sending: #{data}"
|
129
128
|
@io.write("#{data}\n\n") unless disconnect_notice?
|
130
129
|
end
|
131
|
-
|
130
|
+
|
132
131
|
def send_and_read(data)
|
133
132
|
send_data(data)
|
134
133
|
read_response
|
135
134
|
end
|
136
|
-
|
135
|
+
|
137
136
|
def read_and_parse_response
|
138
137
|
my_event = false
|
139
138
|
until my_event
|
140
139
|
read_response
|
141
140
|
my_event = parse_response
|
142
|
-
end
|
141
|
+
end
|
143
142
|
end
|
144
|
-
|
143
|
+
|
145
144
|
def read_response
|
146
145
|
return if disconnect_notice?
|
147
146
|
read_response_info
|
148
147
|
read_event_header
|
149
148
|
read_event_body
|
150
149
|
end
|
151
|
-
|
150
|
+
|
152
151
|
def read_response_info
|
153
152
|
@response = ''
|
154
153
|
begin
|
155
154
|
@response += read_line_from_io
|
156
|
-
end until @response[-2..-1] == "\n\n"
|
155
|
+
end until @response[-2..-1] == "\n\n"
|
157
156
|
end
|
158
|
-
|
157
|
+
|
159
158
|
def read_event_header
|
160
159
|
header_length = @response.sub(/^Content-Length: ([0-9]+)$.*/m, '\1').to_i
|
161
160
|
return if header_length == 0
|
@@ -163,23 +162,23 @@ private
|
|
163
162
|
begin
|
164
163
|
header += read_line_from_io
|
165
164
|
end until header[-2..-1] == "\n\n"
|
166
|
-
@response += header
|
165
|
+
@response += header
|
167
166
|
end
|
168
|
-
|
167
|
+
|
169
168
|
def read_event_body
|
170
169
|
body_length = @response.sub(/^Content-Length.*^Content-Length: ([0-9]+)$.*/m, '\1').to_i
|
171
170
|
return if body_length == 0
|
172
171
|
body = ''
|
173
|
-
begin
|
172
|
+
begin
|
174
173
|
body += read_block_from_io(body_length)
|
175
174
|
end until body.length == body_length
|
176
|
-
@response += body
|
175
|
+
@response += body
|
177
176
|
end
|
178
|
-
|
177
|
+
|
179
178
|
def parse_response
|
180
179
|
hash = {}
|
181
180
|
if @response =~ /^Content-Length.*^Content-Length/m
|
182
|
-
@event_body = @response.sub(/.*\n\n.*\n\n(.*)/m, '\1').strip
|
181
|
+
@event_body = @response.sub(/.*\n\n.*\n\n(.*)/m, '\1').strip
|
183
182
|
else
|
184
183
|
@event_body = nil
|
185
184
|
end
|
@@ -198,21 +197,21 @@ private
|
|
198
197
|
@response = ''
|
199
198
|
true
|
200
199
|
end
|
201
|
-
|
200
|
+
|
202
201
|
def read_line_from_io
|
203
202
|
line = @io.gets
|
204
203
|
raise io_disconnected_message unless line
|
205
204
|
line
|
206
205
|
end
|
207
|
-
|
206
|
+
|
208
207
|
def read_block_from_io(length)
|
209
208
|
block = @io.read(length)
|
210
209
|
raise io_disconnected_message unless block
|
211
210
|
block
|
212
211
|
end
|
213
|
-
|
212
|
+
|
214
213
|
def io_disconnected_message
|
215
214
|
"IO disconnected - FreeSWITCH crashed?"
|
216
215
|
end
|
217
|
-
|
218
|
-
end
|
216
|
+
|
217
|
+
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module FreeswitchApplications
|
2
|
-
|
2
|
+
|
3
3
|
# Answers the call.
|
4
4
|
def answer
|
5
5
|
execute_app('answer')
|
6
6
|
end
|
7
7
|
|
8
8
|
# Plays the file in file_name
|
9
|
-
# file_name is either an absolute path or path relative
|
9
|
+
# file_name is either an absolute path or path relative
|
10
10
|
# to the sound_prefix variable set in Freeswitch's vars.xml configuration file.
|
11
11
|
def playback(file_name)
|
12
12
|
execute_app('playback', file_name)
|
@@ -16,36 +16,36 @@ module FreeswitchApplications
|
|
16
16
|
def spell(string)
|
17
17
|
execute_app('phrase', "spell,#{string}")
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
# Says the given string
|
21
21
|
# Don't forget to set up your TTS engine and set variables tts_engine and tts_voice accordingly
|
22
22
|
# See e.g.: http://wiki.freeswitch.org/wiki/Mod_flite
|
23
23
|
def speak(string)
|
24
24
|
execute_app('speak', string)
|
25
25
|
end
|
26
|
-
|
26
|
+
|
27
27
|
# Bridges the call to the given number or numbers (this param can be a number or an array of numbers).
|
28
28
|
def bridge(number_or_numbers, options = {})
|
29
29
|
number_or_numbers = number_or_numbers.join(",") if number_or_numbers.is_a?(Array)
|
30
30
|
execute_app("bridge", "#{number_or_numbers}")
|
31
31
|
end
|
32
|
-
|
32
|
+
|
33
33
|
# Transfers the call to the given extension
|
34
34
|
def transfer(extension)
|
35
35
|
execute_app("transfer", "#{extension}")
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
# Records the call to a file with the given file_name
|
39
|
-
# file_name is either an absolute path or path relative
|
39
|
+
# file_name is either an absolute path or path relative
|
40
40
|
# to the sound_prefix variable set in Freeswitch's vars.xml configuration file.
|
41
41
|
#
|
42
42
|
# Options:
|
43
43
|
# * <tt>:time_limit_secs</tt> overrides the default timeout, which is 600 seconds
|
44
44
|
def record(file_name, options = {})
|
45
|
-
options = {:
|
45
|
+
options = {time_limit_secs: 600}.merge(options) #no reverse_merge, no fun :-)
|
46
46
|
execute_app("record", "#{file_name} #{options[:time_limit_secs]}")
|
47
47
|
end
|
48
|
-
|
48
|
+
|
49
49
|
# Plays the file in file_name and reads input (key presses) from the user.
|
50
50
|
#
|
51
51
|
# Options:
|
@@ -55,10 +55,10 @@ module FreeswitchApplications
|
|
55
55
|
# * <tt>:min and :max</tt> options to override the default maximum and minimum of characters that will be read (both default to 1)
|
56
56
|
def read(file_name, options = {})
|
57
57
|
options[:terminators] = [options[:terminators]] if options[:terminators].is_a?(String)
|
58
|
-
options = {:
|
58
|
+
options = {timeout: 10, variable: 'input', min: 1, max: 1, terminators: ['#']}.merge(options)
|
59
59
|
execute_app("read", "#{options[:min]} #{options[:max]} #{file_name} #{options[:variable]} #{options[:timeout] * 1000} #{options[:terminators].join(',')}")
|
60
60
|
end
|
61
|
-
|
61
|
+
|
62
62
|
# Starts recording the call in file in file_name
|
63
63
|
#
|
64
64
|
def start_recording(file_name)
|
@@ -66,11 +66,11 @@ module FreeswitchApplications
|
|
66
66
|
end
|
67
67
|
|
68
68
|
# Stops recording the call in file in file_name
|
69
|
-
#
|
69
|
+
#
|
70
70
|
def stop_recording(file_name)
|
71
71
|
execute_app('stop_record_session', file_name)
|
72
72
|
end
|
73
|
-
|
73
|
+
|
74
74
|
# Sets a variable with the give name to the given value.
|
75
75
|
def set_variable(name, value)
|
76
76
|
execute_app('set', "#{name}=#{value}")
|
@@ -78,9 +78,9 @@ module FreeswitchApplications
|
|
78
78
|
|
79
79
|
# Hangs up the call.
|
80
80
|
def hangup
|
81
|
-
execute_app('hangup')
|
82
|
-
end
|
83
|
-
|
81
|
+
execute_app('hangup', 'USER_BUSY')
|
82
|
+
end
|
83
|
+
|
84
84
|
# Executes an app using the sendmsg command of Freeswitch.
|
85
85
|
# Use this if there is no method for the application you want to run.
|
86
86
|
#
|
@@ -88,7 +88,7 @@ module FreeswitchApplications
|
|
88
88
|
# * <tt>app</tt> is the application name
|
89
89
|
# * <tt>pars</tt> is a string of arguments of the app
|
90
90
|
# * <tt>lock</tt> can be set to false so Freeswitch won't wait for this app to finish before running the next one
|
91
|
-
def execute_app(app, pars = '', lock = true, unique_id = nil)
|
91
|
+
def execute_app(app, pars = '', lock = true, unique_id = nil)
|
92
92
|
@last_app_executed = app
|
93
93
|
unique_id = @unique_id unless unique_id
|
94
94
|
cmd = "sendmsg #{unique_id}"
|
@@ -97,5 +97,5 @@ module FreeswitchApplications
|
|
97
97
|
cmd << "\nexecute-app-arg: #{pars}" unless pars.blank?
|
98
98
|
cmd << "\nevent-lock:#{lock}"
|
99
99
|
send_data cmd
|
100
|
-
end
|
101
|
-
end
|
100
|
+
end
|
101
|
+
end
|
metadata
CHANGED
@@ -1,84 +1,131 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: freec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
5
|
-
prerelease:
|
4
|
+
version: 0.2.7
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Jan Kubr
|
9
8
|
autorequire:
|
10
|
-
bindir:
|
9
|
+
bindir: exe
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2017-04-01 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: gserver
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.0.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.0.1
|
14
27
|
- !ruby/object:Gem::Dependency
|
15
28
|
name: daemons
|
16
|
-
requirement:
|
17
|
-
none: false
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
18
30
|
requirements:
|
19
|
-
- -
|
31
|
+
- - "~>"
|
20
32
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
33
|
+
version: 1.2.4
|
22
34
|
type: :runtime
|
23
35
|
prerelease: false
|
24
|
-
version_requirements:
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.2.4
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.14'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.14'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '10.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '10.0'
|
25
69
|
- !ruby/object:Gem::Dependency
|
26
70
|
name: rspec
|
27
|
-
requirement:
|
28
|
-
none: false
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
29
72
|
requirements:
|
30
|
-
- -
|
73
|
+
- - "~>"
|
31
74
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
75
|
+
version: '3.0'
|
33
76
|
type: :development
|
34
77
|
prerelease: false
|
35
|
-
version_requirements:
|
36
|
-
|
37
|
-
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.0'
|
83
|
+
description: The layer between your Ruby voice app and FreeSWITCH.
|
84
|
+
email:
|
85
|
+
- mail@jankubr.com
|
38
86
|
executables: []
|
39
87
|
extensions: []
|
40
|
-
extra_rdoc_files:
|
41
|
-
- README.rdoc
|
88
|
+
extra_rdoc_files: []
|
42
89
|
files:
|
90
|
+
- ".gitignore"
|
91
|
+
- Gemfile
|
92
|
+
- Gemfile.lock
|
93
|
+
- LICENSE.txt
|
43
94
|
- README.rdoc
|
44
95
|
- Rakefile
|
96
|
+
- bin/console
|
97
|
+
- bin/setup
|
98
|
+
- freec.gemspec
|
45
99
|
- lib/call_variables.rb
|
46
100
|
- lib/freec.rb
|
101
|
+
- lib/freec/version.rb
|
47
102
|
- lib/freec_base.rb
|
48
103
|
- lib/freec_logger.rb
|
49
104
|
- lib/freeswitch_applications.rb
|
50
105
|
- lib/listener.rb
|
51
106
|
- lib/tools.rb
|
52
|
-
- spec/data/event.rb
|
53
|
-
- spec/data/event_with_body.rb
|
54
|
-
- spec/event_parser_spec.rb
|
55
|
-
- spec/freec_base_spec.rb
|
56
|
-
- spec/freeswitch_applications_spec.rb
|
57
|
-
- spec/spec_helper.rb
|
58
107
|
homepage: http://github.com/jankubr/freec
|
59
|
-
licenses:
|
108
|
+
licenses:
|
109
|
+
- MIT
|
110
|
+
metadata: {}
|
60
111
|
post_install_message:
|
61
|
-
rdoc_options:
|
62
|
-
- --line-numbers
|
63
|
-
- --inline-source
|
112
|
+
rdoc_options: []
|
64
113
|
require_paths:
|
65
114
|
- lib
|
66
115
|
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
-
none: false
|
68
116
|
requirements:
|
69
|
-
- -
|
117
|
+
- - ">="
|
70
118
|
- !ruby/object:Gem::Version
|
71
119
|
version: '0'
|
72
120
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
121
|
requirements:
|
75
|
-
- -
|
122
|
+
- - ">="
|
76
123
|
- !ruby/object:Gem::Version
|
77
124
|
version: '0'
|
78
125
|
requirements: []
|
79
|
-
rubyforge_project:
|
80
|
-
rubygems_version:
|
126
|
+
rubyforge_project:
|
127
|
+
rubygems_version: 2.5.2
|
81
128
|
signing_key:
|
82
|
-
specification_version:
|
129
|
+
specification_version: 4
|
83
130
|
summary: The layer between your Ruby voice app and FreeSWITCH.
|
84
131
|
test_files: []
|
data/spec/data/event.rb
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
unless defined?(EVENT)
|
2
|
-
EVENT=<<STRING
|
3
|
-
Content-Length: 1693
|
4
|
-
Content-Type: text/event-plain
|
5
|
-
|
6
|
-
Event-Name: CHANNEL_EXECUTE
|
7
|
-
Core-UUID: 4a78c88d-38cc-4e35-92af-c155def76f9a
|
8
|
-
FreeSWITCH-Hostname: air.local
|
9
|
-
FreeSWITCH-IPv4: 10.0.1.2
|
10
|
-
FreeSWITCH-IPv6: %3A%3A1
|
11
|
-
Event-Date-Local: 2009-05-14%2019%3A29%3A09
|
12
|
-
Event-Date-GMT: Thu,%2014%20May%202009%2017%3A29%3A09%20GMT
|
13
|
-
Event-Date-Timestamp: 1242322149917313
|
14
|
-
Event-Calling-File: switch_core_session.c
|
15
|
-
Event-Calling-Function: switch_core_session_exec
|
16
|
-
Event-Calling-Line-Number: 1460
|
17
|
-
Channel-State: CS_EXECUTE
|
18
|
-
Channel-State-Number: 4
|
19
|
-
Channel-Name: sofia/internal/jan.kubr.gmail.com%4010.0.1.2
|
20
|
-
Channel-Destination-Number: 886
|
21
|
-
Unique-ID: f3c2d5ee-d064-4f55-9280-5be2a65867e8
|
22
|
-
Call-Direction: inbound
|
23
|
-
Presence-Call-Direction: inbound
|
24
|
-
Answer-State: answered
|
25
|
-
Channel-Read-Codec-Name: GSM
|
26
|
-
Channel-Read-Codec-Rate: 8000
|
27
|
-
Channel-Write-Codec-Name: GSM
|
28
|
-
Channel-Write-Codec-Rate: 8000
|
29
|
-
Caller-Username: jan.kubr.gmail.com
|
30
|
-
Caller-Dialplan: XML
|
31
|
-
Caller-Caller-ID-Name: Jan%20Local
|
32
|
-
Caller-Caller-ID-Number: jan.kubr.gmail.com
|
33
|
-
Caller-Network-Addr: 10.0.1.2
|
34
|
-
Caller-Destination-Number: 0
|
35
|
-
Caller-Unique-ID: f3c2d5ee-d064-4f55-9280-5be2a65867e8
|
36
|
-
Caller-Source: mod_sofia
|
37
|
-
Caller-Context: default
|
38
|
-
Caller-Channel-Name: sofia/internal/jan.kubr.gmail.com%4010.0.1.2
|
39
|
-
Caller-Profile-Index: 1
|
40
|
-
Caller-Profile-Created-Time: 1242322142643329
|
41
|
-
Caller-Channel-Created-Time: 1242322142643329
|
42
|
-
Caller-Channel-Answered-Time: 1242322143798188
|
43
|
-
Caller-Channel-Progress-Time: 0
|
44
|
-
Caller-Channel-Progress-Media-Time: 1242322142657064
|
45
|
-
Caller-Channel-Hangup-Time: 0
|
46
|
-
Caller-Channel-Transfer-Time: 0
|
47
|
-
Caller-Screen-Bit: true
|
48
|
-
Caller-Privacy-Hide-Name: false
|
49
|
-
Caller-Privacy-Hide-Number: false
|
50
|
-
variable_sip_to_user: 886
|
51
|
-
variable_sip_from_user: 1001
|
52
|
-
Application: set
|
53
|
-
Application-Data: continue_on_fail%3Dtrue
|
54
|
-
|
55
|
-
STRING
|
56
|
-
end
|
@@ -1,25 +0,0 @@
|
|
1
|
-
unless defined?(EVENT_WITH_BODY)
|
2
|
-
EVENT_WITH_BODY=<<STRING
|
3
|
-
Content-Length: 460
|
4
|
-
Content-Type: text/event-plain
|
5
|
-
|
6
|
-
Event-Name: DETECTED_SPEECH
|
7
|
-
Core-UUID: b61467b4-6d7c-4a6f-ba87-32ade7b79951
|
8
|
-
FreeSWITCH-Hostname: air.local
|
9
|
-
FreeSWITCH-IPv4: 10.0.1.2
|
10
|
-
FreeSWITCH-IPv6: %3A%3A1
|
11
|
-
Event-Date-Local: 2009-05-04%2020%3A40%3A48
|
12
|
-
Event-Date-GMT: Mon,%2004%20May%202009%2018%3A40%3A48%20GMT
|
13
|
-
Event-Date-Timestamp: 1241462448174337
|
14
|
-
Event-Calling-File: switch_ivr_async.c
|
15
|
-
Event-Calling-Function: speech_thread
|
16
|
-
Event-Calling-Line-Number: 1878
|
17
|
-
Speech-Type: detected-speech
|
18
|
-
Content-Length: 132
|
19
|
-
|
20
|
-
<interpretation grammar="battle" score="100">
|
21
|
-
<result name="match">FREESTYLE</result>
|
22
|
-
<input>FREESTYLE</input>
|
23
|
-
</interpretation>
|
24
|
-
STRING
|
25
|
-
end
|
data/spec/event_parser_spec.rb
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
require "#{File.dirname(__FILE__)}/spec_helper"
|
2
|
-
|
3
|
-
describe Freec do
|
4
|
-
before do
|
5
|
-
io = stub(:io)
|
6
|
-
log = FreecLogger.new(STDOUT)
|
7
|
-
log.level = Logger::FATAL
|
8
|
-
@freec = Freec.new(io, log, {})
|
9
|
-
end
|
10
|
-
|
11
|
-
describe "parses body-less event" do
|
12
|
-
before do
|
13
|
-
@freec.instance_variable_set(:@response, EVENT)
|
14
|
-
@freec.send :parse_response
|
15
|
-
end
|
16
|
-
|
17
|
-
it "should read unique id from response" do
|
18
|
-
@freec.instance_variable_get(:@unique_id).should == 'f3c2d5ee-d064-4f55-9280-5be2a65867e8'
|
19
|
-
end
|
20
|
-
|
21
|
-
it "should include channel unique id to the array of ids event variables are read from" do
|
22
|
-
@freec.instance_variable_get(:@want_events_from).should == ['f3c2d5ee-d064-4f55-9280-5be2a65867e8']
|
23
|
-
end
|
24
|
-
|
25
|
-
it "should parse variables from response" do
|
26
|
-
@freec.call_vars[:call_direction].should == 'inbound'
|
27
|
-
@freec.call_vars[:caller_context].should == 'default'
|
28
|
-
@freec.call_vars[:application].should == 'set'
|
29
|
-
end
|
30
|
-
|
31
|
-
it "should make the value of the sip_from_user variable available as a method" do
|
32
|
-
@freec.sip_from_user.should == '1001'
|
33
|
-
end
|
34
|
-
|
35
|
-
it "should make the value of the sip_to_user variable available as a method" do
|
36
|
-
@freec.sip_to_user.should == '886'
|
37
|
-
end
|
38
|
-
|
39
|
-
it "should make the value of the channel_destination_number variable available as a method" do
|
40
|
-
@freec.channel_destination_number.should == '886'
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
describe "ignores variables from another channel event" do
|
45
|
-
|
46
|
-
it "should not parse variables from event of another channel" do
|
47
|
-
@freec.instance_variable_set(:@response, EVENT)
|
48
|
-
@freec.send(:parse_response).should be_true
|
49
|
-
another_channel_event = EVENT.sub('Unique-ID: f3c2d5ee-d064-4f55-9280-5be2a65867e8', 'Unique-ID: ffff1111-e72d-48bf-9ecf-d71bd4b60617').
|
50
|
-
sub('Application: set', 'Application: bridge')
|
51
|
-
@freec.instance_variable_set(:@response, another_channel_event)
|
52
|
-
@freec.send(:parse_response).should be_false
|
53
|
-
@freec.call_vars[:application].should == 'set'
|
54
|
-
end
|
55
|
-
|
56
|
-
it "parses variables from disconnect notices" do
|
57
|
-
@freec.instance_variable_set(:@response, EVENT)
|
58
|
-
@freec.send(:parse_response).should be_true
|
59
|
-
@freec.call_vars[:content_type].should == 'text/event-plain'
|
60
|
-
disconnect_notice_event = EVENT.sub('Unique-ID: f3c2d5ee-d064-4f55-9280-5be2a65867e8', 'Unique-ID: ').
|
61
|
-
sub('Content-Type: text/event-plain', 'Content-Type: text/disconnect-notice')
|
62
|
-
@freec.instance_variable_set(:@response, disconnect_notice_event)
|
63
|
-
@freec.send(:parse_response).should be_true
|
64
|
-
@freec.call_vars[:content_type].should == 'text/disconnect-notice'
|
65
|
-
end
|
66
|
-
|
67
|
-
end
|
68
|
-
|
69
|
-
describe "parses an event with a body" do
|
70
|
-
before do
|
71
|
-
@freec.instance_variable_set(:@response, EVENT_WITH_BODY)
|
72
|
-
@freec.send :parse_response
|
73
|
-
end
|
74
|
-
|
75
|
-
it "should parse the variables from the event header" do
|
76
|
-
@freec.call_vars[:event_name].should == 'DETECTED_SPEECH'
|
77
|
-
end
|
78
|
-
|
79
|
-
it "makes the body of the response available as a public method" do
|
80
|
-
@freec.event_body.should =~ /<\/interpretation>$/
|
81
|
-
end
|
82
|
-
|
83
|
-
end
|
84
|
-
end
|
data/spec/freec_base_spec.rb
DELETED
@@ -1,213 +0,0 @@
|
|
1
|
-
require "#{File.dirname(__FILE__)}/spec_helper"
|
2
|
-
|
3
|
-
module FreecSpecHelper
|
4
|
-
def event_parts(event=EVENT)
|
5
|
-
@parts = event.split("\n\n").map {|p| "#{p}\n\n"}
|
6
|
-
end
|
7
|
-
|
8
|
-
def initial_response
|
9
|
-
@initial_response ||= EVENT.split("\n\n")[1] + "\n\n"
|
10
|
-
end
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
describe Freec do
|
15
|
-
include FreecSpecHelper
|
16
|
-
|
17
|
-
before do
|
18
|
-
@io = stub(:io)
|
19
|
-
@log = FreecLogger.new(STDOUT)
|
20
|
-
@log.level = Logger::FATAL
|
21
|
-
@config = {:config1 => 'value'}
|
22
|
-
@freec = Freec.new(@io, @log, @config)
|
23
|
-
end
|
24
|
-
|
25
|
-
describe "initialization" do
|
26
|
-
|
27
|
-
it "makes log and config available" do
|
28
|
-
@freec.log.should == @log
|
29
|
-
@freec.config.should == @config
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
describe "call initialization" do
|
35
|
-
|
36
|
-
it "sends 'connect' command to Freeswitch" do
|
37
|
-
@freec.should_receive(:send_data).with("connect")
|
38
|
-
@freec.should_receive(:send_data)
|
39
|
-
@freec.should_receive(:send_data)
|
40
|
-
@freec.should_receive(:send_data)
|
41
|
-
@io.should_receive(:gets).exactly(4).and_return(initial_response)
|
42
|
-
@freec.send :call_initialization
|
43
|
-
end
|
44
|
-
|
45
|
-
it "subscribes to events" do
|
46
|
-
@freec.should_receive(:send_data)
|
47
|
-
@freec.should_receive(:send_data).with('events plain all')
|
48
|
-
@freec.should_receive(:send_data).with('filter Unique-ID f3c2d5ee-d064-4f55-9280-5be2a65867e8')
|
49
|
-
@freec.should_receive(:send_data).with('divert_events on')
|
50
|
-
@io.should_receive(:gets).exactly(4).and_return(initial_response)
|
51
|
-
@freec.send :call_initialization
|
52
|
-
end
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
describe "response reader" do
|
57
|
-
|
58
|
-
it "reads the full body-less event" do
|
59
|
-
@io.should_receive(:gets).and_return(event_parts(EVENT)[0], event_parts(EVENT)[1])
|
60
|
-
@freec.send(:read_response)
|
61
|
-
end
|
62
|
-
|
63
|
-
it "raises exception if IO is disconnected while reading response info" do
|
64
|
-
@io.should_receive(:gets).and_return(nil)
|
65
|
-
lambda { @freec.send(:read_response) }.should raise_error(RuntimeError, 'IO disconnected - FreeSWITCH crashed?')
|
66
|
-
end
|
67
|
-
|
68
|
-
it "raises exception if IO is disconnected while reading event header" do
|
69
|
-
@io.should_receive(:gets).and_return(event_parts(EVENT)[0], nil)
|
70
|
-
lambda { @freec.send(:read_response) }.should raise_error(RuntimeError, 'IO disconnected - FreeSWITCH crashed?')
|
71
|
-
end
|
72
|
-
|
73
|
-
it "reads the full event with body" do
|
74
|
-
@io.should_receive(:gets).and_return(event_parts(EVENT_WITH_BODY)[0],
|
75
|
-
event_parts(EVENT_WITH_BODY)[1])
|
76
|
-
@io.should_receive(:read).and_return(event_parts(EVENT_WITH_BODY)[2].strip.chomp)
|
77
|
-
@freec.send(:read_response)
|
78
|
-
end
|
79
|
-
|
80
|
-
it "raises exception if IO is disconnected while reading body" do
|
81
|
-
@io.should_receive(:gets).and_return(event_parts(EVENT_WITH_BODY)[0],
|
82
|
-
event_parts(EVENT_WITH_BODY)[1])
|
83
|
-
@io.should_receive(:read).and_return(nil)
|
84
|
-
lambda { @freec.send(:read_response) }.should raise_error(RuntimeError, 'IO disconnected - FreeSWITCH crashed?')
|
85
|
-
end
|
86
|
-
|
87
|
-
end
|
88
|
-
|
89
|
-
|
90
|
-
describe "event recognition" do
|
91
|
-
|
92
|
-
it "ignores events for another channel" do
|
93
|
-
@freec.should_receive(:read_response).twice
|
94
|
-
@freec.should_receive(:parse_response).and_return(false, true)
|
95
|
-
@freec.send(:read_and_parse_response)
|
96
|
-
end
|
97
|
-
|
98
|
-
it "should subscribe to events of other leg channel after bridge" do
|
99
|
-
bridge_event = EVENT.sub('CHANNEL_EXECUTE', 'CHANNEL_BRIDGE').sub('Caller-Profile-Index: 1', 'Other-Leg-Unique-ID: 6c75cb42-e72d-48bf-9ecf-d71bd4b60617')
|
100
|
-
@freec.instance_variable_set(:@response, bridge_event)
|
101
|
-
@freec.send(:parse_response)
|
102
|
-
@freec.should_receive(:send_and_read).with("filter Unique-ID 6c75cb42-e72d-48bf-9ecf-d71bd4b60617")
|
103
|
-
@freec.send(:subscribe_to_new_channel_events)
|
104
|
-
@freec.instance_variable_get(:@want_events_from).should == ["f3c2d5ee-d064-4f55-9280-5be2a65867e8", "6c75cb42-e72d-48bf-9ecf-d71bd4b60617"]
|
105
|
-
end
|
106
|
-
|
107
|
-
it "should recognize last event was DTMF to call the on_dtmf callback" do
|
108
|
-
dtmf_event = EVENT.sub('CHANNEL_EXECUTE', 'DTMF')
|
109
|
-
@freec.instance_variable_set(:@response, dtmf_event)
|
110
|
-
@freec.send(:parse_response)
|
111
|
-
@freec.send(:last_event_dtmf?).should be_true
|
112
|
-
end
|
113
|
-
|
114
|
-
it "should recognize an event as a non-DTMF one" do
|
115
|
-
@freec.instance_variable_set(:@response, EVENT)
|
116
|
-
@freec.send(:parse_response)
|
117
|
-
@freec.send(:last_event_dtmf?).should be_false
|
118
|
-
end
|
119
|
-
|
120
|
-
it "should recognize that app execution has been completed for the last run app" do
|
121
|
-
@freec.instance_variable_set(:@last_app_executed, 'set')
|
122
|
-
set_executed_event = EVENT.sub('CHANNEL_EXECUTE', 'CHANNEL_EXECUTE_COMPLETE')
|
123
|
-
@freec.instance_variable_set(:@response, set_executed_event)
|
124
|
-
@freec.send(:parse_response)
|
125
|
-
|
126
|
-
@freec.send(:execute_completed?).should be_true
|
127
|
-
@freec.instance_variable_get(:@last_app_executed).should be_nil
|
128
|
-
end
|
129
|
-
|
130
|
-
it "should recognize that app execution has been completed but not for the last run one" do
|
131
|
-
@freec.instance_variable_set(:@last_app_executed, 'different')
|
132
|
-
set_executed_event = EVENT.sub('CHANNEL_EXECUTE', 'CHANNEL_EXECUTE_COMPLETE')
|
133
|
-
@freec.instance_variable_set(:@response, set_executed_event)
|
134
|
-
@freec.send(:parse_response)
|
135
|
-
|
136
|
-
@freec.send(:execute_completed?).should be_false
|
137
|
-
@freec.instance_variable_get(:@last_app_executed).should == 'different'
|
138
|
-
end
|
139
|
-
|
140
|
-
it "should hangup the call, send exit command to Freeswitch and disconnect from it when step callback returns nil" do
|
141
|
-
@freec.should_receive(:send_data).with("connect")
|
142
|
-
@freec.should_receive(:send_data).with("events plain all")
|
143
|
-
@freec.should_receive(:send_data).with('filter Unique-ID f3c2d5ee-d064-4f55-9280-5be2a65867e8')
|
144
|
-
@freec.should_receive(:send_data).with('divert_events on')
|
145
|
-
|
146
|
-
@io.should_receive(:gets).and_return(initial_response, initial_response, initial_response, initial_response, "bye\n\n")
|
147
|
-
@io.should_receive(:closed?).twice.and_return(false)
|
148
|
-
@freec.should_receive(:step).and_return(nil)
|
149
|
-
@freec.should_receive(:execute_app).with('hangup')
|
150
|
-
@freec.should_receive(:on_hangup)
|
151
|
-
@freec.should_receive(:send_data).with("exit")
|
152
|
-
@freec.handle_call
|
153
|
-
end
|
154
|
-
|
155
|
-
it "should exit when Freeswitch disconnects (e.g. caller hangs up) and neither call the step callback nor send any other data to Freeswitch" do
|
156
|
-
disconnect_event = EVENT.sub('text/event-plain', 'text/disconnect-notice')
|
157
|
-
@freec.should_receive(:send_data).with("connect")
|
158
|
-
@freec.should_receive(:send_data).with("events plain all")
|
159
|
-
@freec.should_receive(:send_data).with('filter Unique-ID f3c2d5ee-d064-4f55-9280-5be2a65867e8')
|
160
|
-
@freec.should_receive(:send_data).with('divert_events on')
|
161
|
-
@io.should_receive(:gets).and_return(initial_response, initial_response, event_parts(disconnect_event)[0], event_parts(disconnect_event)[1])
|
162
|
-
@io.should_receive(:closed?).twice.and_return(true)
|
163
|
-
@freec.should_receive(:step).never
|
164
|
-
@freec.should_receive(:on_hangup)
|
165
|
-
@freec.should_receive(:execute_app).never
|
166
|
-
@freec.should_receive(:send_data).never
|
167
|
-
@freec.handle_call
|
168
|
-
end
|
169
|
-
|
170
|
-
end
|
171
|
-
|
172
|
-
describe "callback exception handling" do
|
173
|
-
it "should catch and log any exception occurred in a callback" do
|
174
|
-
@freec.should_receive(:callback_name).and_raise(RuntimeError)
|
175
|
-
@freec.log.should_receive(:error).with('RuntimeError')
|
176
|
-
@freec.log.should_receive(:error).at_least(1).times #backtrace
|
177
|
-
lambda { @freec.send(:callback, :callback_name) }.should_not raise_error(Exception)
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
describe "custom waiting conditions" do
|
182
|
-
|
183
|
-
# it "should return true from waiting_for_this_response? when the conditions for the response are met" do
|
184
|
-
# @freec.wait_for(:content_type, 'command/reply')
|
185
|
-
# @freec.instance_variable_set(:@response, EVENT)
|
186
|
-
# @freec.send(:parse_response)
|
187
|
-
# @freec.send(:waiting_for_this_response?).should be_true
|
188
|
-
# end
|
189
|
-
#
|
190
|
-
# it "should return false from waiting_for_this_response? when the conditions for the response are not met" do
|
191
|
-
# @freec.wait_for(:content_type, 'text/event-plain')
|
192
|
-
# @freec.instance_variable_set(:@response, EVENT)
|
193
|
-
# @freec.send(:parse_response)
|
194
|
-
# @freec.send(:waiting_for_this_response?).should be_false
|
195
|
-
# end
|
196
|
-
#
|
197
|
-
# it "should reset the waiting conditions after they have been met" do
|
198
|
-
# @freec.wait_for(:content_type, 'command/reply')
|
199
|
-
# @freec.should_receive(:send_data).with("connect")
|
200
|
-
# @freec.should_receive(:send_data).with("myevents")
|
201
|
-
# @io.should_receive(:gets).and_return(EVENT, EVENT, "bye\n\n")
|
202
|
-
# @freec.instance_variable_set(:@last_app_executed, 'set')
|
203
|
-
# @freec.should_receive(:step).and_return(nil)
|
204
|
-
# @freec.should_receive(:execute_app).with('hangup')
|
205
|
-
# @freec.should_receive(:on_hangup)
|
206
|
-
# @freec.should_receive(:send_data).with("exit")
|
207
|
-
# @freec.handle_call
|
208
|
-
# @freec.send(:waiting_for_this_response?).should be_nil
|
209
|
-
# end
|
210
|
-
#
|
211
|
-
end
|
212
|
-
|
213
|
-
end
|
@@ -1,128 +0,0 @@
|
|
1
|
-
require "#{File.dirname(__FILE__)}/spec_helper"
|
2
|
-
|
3
|
-
module FreeswitchApplicationsSpecHelper
|
4
|
-
def freeswitch_command(app, pars = nil)
|
5
|
-
cmd = "sendmsg "
|
6
|
-
cmd += "\ncall-command: execute"
|
7
|
-
cmd += "\nexecute-app-name: #{app}"
|
8
|
-
cmd += "\nexecute-app-arg: #{pars}" unless pars.blank?
|
9
|
-
cmd += "\nevent-lock:true"
|
10
|
-
cmd
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
describe Freec do
|
15
|
-
|
16
|
-
before do
|
17
|
-
io = stub(:io)
|
18
|
-
log = FreecLogger.new(STDOUT)
|
19
|
-
log.level = Logger::FATAL
|
20
|
-
@freec = Freec.new(io, log, {})
|
21
|
-
end
|
22
|
-
|
23
|
-
describe "executes Freeswitch applications" do
|
24
|
-
include FreeswitchApplicationsSpecHelper
|
25
|
-
|
26
|
-
it "should set the last_app_executed variable to last run app" do
|
27
|
-
@freec.should_receive(:send_data).with(freeswitch_command('answer'))
|
28
|
-
@freec.answer
|
29
|
-
@freec.instance_variable_get(:@last_app_executed).should == 'answer'
|
30
|
-
end
|
31
|
-
|
32
|
-
it "should execute the answer app when called the answer method" do
|
33
|
-
@freec.should_receive(:send_data).with(freeswitch_command('answer'))
|
34
|
-
@freec.answer
|
35
|
-
end
|
36
|
-
|
37
|
-
it "should execute the playback app when called the playback method" do
|
38
|
-
@freec.should_receive(:send_data).with(freeswitch_command('playback', 'sounds/file.wav'))
|
39
|
-
@freec.playback('sounds/file.wav')
|
40
|
-
end
|
41
|
-
|
42
|
-
it "should execute the phrase app with spell option when called the spell method" do
|
43
|
-
@freec.should_receive(:send_data).with(freeswitch_command('phrase', 'spell,abcd'))
|
44
|
-
@freec.spell('abcd')
|
45
|
-
end
|
46
|
-
|
47
|
-
it "should execute the speak app when called the speak method" do
|
48
|
-
@freec.should_receive(:send_data).with(freeswitch_command('speak', 'hello'))
|
49
|
-
@freec.speak('hello')
|
50
|
-
end
|
51
|
-
|
52
|
-
it "should execute the bridge app when called the bridge method" do
|
53
|
-
@freec.should_receive(:send_data).with(freeswitch_command('bridge', 'user/brian@10.0.1.2'))
|
54
|
-
@freec.bridge('user/brian@10.0.1.2')
|
55
|
-
end
|
56
|
-
|
57
|
-
it "should execute the transfer app when called the transfer method" do
|
58
|
-
@freec.should_receive(:send_data).with(freeswitch_command('transfer', '1000'))
|
59
|
-
@freec.transfer('1000')
|
60
|
-
end
|
61
|
-
|
62
|
-
it "should pass all numbers passed to the bridge method as params of the bridge separated by comma (thus numbers are called simultaneously)" do
|
63
|
-
@freec.should_receive(:send_data).with(freeswitch_command('bridge', 'user/brian@10.0.1.2,user/karl@10.0.1.2'))
|
64
|
-
@freec.bridge(['user/brian@10.0.1.2', 'user/karl@10.0.1.2'])
|
65
|
-
end
|
66
|
-
|
67
|
-
it "should execute the record app when called the record method" do
|
68
|
-
@freec.should_receive(:send_data).with(freeswitch_command('record', 'recordings/file.mp3 600'))
|
69
|
-
@freec.record('recordings/file.mp3')
|
70
|
-
end
|
71
|
-
|
72
|
-
it "should set the time_limit_secs option send to the record method as the max length of the recording" do
|
73
|
-
@freec.should_receive(:send_data).with(freeswitch_command('record', 'recordings/file.mp3 120'))
|
74
|
-
@freec.record('recordings/file.mp3', :time_limit_secs => 120)
|
75
|
-
end
|
76
|
-
|
77
|
-
it "should execute the read app when called the input method" do
|
78
|
-
@freec.should_receive(:send_data).with(freeswitch_command('read', '1 1 sounds/file.mp3 input 10000 #'))
|
79
|
-
@freec.read('sounds/file.mp3')
|
80
|
-
end
|
81
|
-
|
82
|
-
it "should allow to pass the minimum and maximum digits to be read" do
|
83
|
-
@freec.should_receive(:send_data).with(freeswitch_command('read', '2 5 sounds/file.mp3 input 10000 #'))
|
84
|
-
@freec.read('sounds/file.mp3', :min => 2, :max => 5)
|
85
|
-
end
|
86
|
-
|
87
|
-
it "should allow to pass the timeout in seconds before the read apps before it times out" do
|
88
|
-
@freec.should_receive(:send_data).with(freeswitch_command('read', '1 1 sounds/file.mp3 input 5000 #'))
|
89
|
-
@freec.read('sounds/file.mp3', :timeout => 5)
|
90
|
-
end
|
91
|
-
|
92
|
-
it "should allow to pass the terminator for the read app" do
|
93
|
-
@freec.should_receive(:send_data).with(freeswitch_command('read', '1 1 sounds/file.mp3 input 10000 *'))
|
94
|
-
@freec.read('sounds/file.mp3', :terminators => '*')
|
95
|
-
end
|
96
|
-
|
97
|
-
it "should allow to pass the terminators as an array for the read app" do
|
98
|
-
@freec.should_receive(:send_data).with(freeswitch_command('read', '1 1 sounds/file.mp3 input 10000 #,*'))
|
99
|
-
@freec.read('sounds/file.mp3', :terminators => ['#', '*'])
|
100
|
-
end
|
101
|
-
|
102
|
-
it "should allow to specify the variable name the input is read by the read app" do
|
103
|
-
@freec.should_receive(:send_data).with(freeswitch_command('read', '1 1 sounds/file.mp3 name 10000 #'))
|
104
|
-
@freec.read('sounds/file.mp3', :variable => 'name')
|
105
|
-
end
|
106
|
-
|
107
|
-
it "should execute the record_session app when called the start_recording method" do
|
108
|
-
@freec.should_receive(:send_data).with(freeswitch_command('record_session', 'file.wav'))
|
109
|
-
@freec.start_recording('file.wav')
|
110
|
-
end
|
111
|
-
|
112
|
-
it "should execute the stop_record_session app when called the start_recording method" do
|
113
|
-
@freec.should_receive(:send_data).with(freeswitch_command('stop_record_session', 'file.wav'))
|
114
|
-
@freec.stop_recording('file.wav')
|
115
|
-
end
|
116
|
-
|
117
|
-
it "should call the set app to set a variable" do
|
118
|
-
@freec.should_receive(:send_data).with(freeswitch_command('set', 'name=value'))
|
119
|
-
@freec.set_variable('name', 'value')
|
120
|
-
end
|
121
|
-
|
122
|
-
it "should call the hangup app when caled the hangup method" do
|
123
|
-
@freec.should_receive(:send_data).with(freeswitch_command('hangup'))
|
124
|
-
@freec.hangup
|
125
|
-
end
|
126
|
-
|
127
|
-
end
|
128
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'rspec'
|
3
|
-
|
4
|
-
$:.unshift(File.join(File.dirname(__FILE__), "..", 'lib')).unshift(File.dirname(__FILE__)).
|
5
|
-
unshift(File.join(File.dirname(__FILE__), 'data'))
|
6
|
-
ROOT = File.join(File.dirname(__FILE__), '..') unless defined?(ROOT)
|
7
|
-
@@config = ''
|
8
|
-
ENVIRONMENT = 'test' unless defined?(ENVIRONMENT)
|
9
|
-
TEST = true unless defined?(TEST)
|
10
|
-
require 'freec'
|
11
|
-
require 'event'
|
12
|
-
require 'event_with_body'
|