freec 0.2.6 → 0.2.7
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.
- 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'
|