punchblock 1.3.0 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +5 -0
- data/lib/punchblock.rb +1 -1
- data/lib/punchblock/connection.rb +1 -0
- data/lib/punchblock/connection/asterisk.rb +0 -1
- data/lib/punchblock/connection/freeswitch.rb +49 -0
- data/lib/punchblock/event/offer.rb +1 -1
- data/lib/punchblock/translator.rb +5 -0
- data/lib/punchblock/translator/asterisk.rb +16 -28
- data/lib/punchblock/translator/asterisk/call.rb +4 -21
- data/lib/punchblock/translator/asterisk/component.rb +0 -5
- data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +0 -3
- data/lib/punchblock/translator/asterisk/component/asterisk/ami_action.rb +0 -1
- data/lib/punchblock/translator/asterisk/component/input.rb +7 -97
- data/lib/punchblock/translator/asterisk/component/output.rb +0 -4
- data/lib/punchblock/translator/asterisk/component/record.rb +0 -2
- data/lib/punchblock/translator/freeswitch.rb +153 -0
- data/lib/punchblock/translator/freeswitch/call.rb +265 -0
- data/lib/punchblock/translator/freeswitch/component.rb +92 -0
- data/lib/punchblock/translator/freeswitch/component/abstract_output.rb +57 -0
- data/lib/punchblock/translator/freeswitch/component/flite_output.rb +17 -0
- data/lib/punchblock/translator/freeswitch/component/input.rb +29 -0
- data/lib/punchblock/translator/freeswitch/component/output.rb +56 -0
- data/lib/punchblock/translator/freeswitch/component/record.rb +79 -0
- data/lib/punchblock/translator/freeswitch/component/tts_output.rb +26 -0
- data/lib/punchblock/translator/input_component.rb +108 -0
- data/lib/punchblock/version.rb +1 -1
- data/punchblock.gemspec +3 -2
- data/spec/punchblock/connection/freeswitch_spec.rb +90 -0
- data/spec/punchblock/translator/asterisk/call_spec.rb +23 -2
- data/spec/punchblock/translator/asterisk/component/input_spec.rb +3 -3
- data/spec/punchblock/translator/asterisk_spec.rb +1 -1
- data/spec/punchblock/translator/freeswitch/call_spec.rb +922 -0
- data/spec/punchblock/translator/freeswitch/component/flite_output_spec.rb +279 -0
- data/spec/punchblock/translator/freeswitch/component/input_spec.rb +312 -0
- data/spec/punchblock/translator/freeswitch/component/output_spec.rb +369 -0
- data/spec/punchblock/translator/freeswitch/component/record_spec.rb +373 -0
- data/spec/punchblock/translator/freeswitch/component/tts_output_spec.rb +285 -0
- data/spec/punchblock/translator/freeswitch/component_spec.rb +118 -0
- data/spec/punchblock/translator/freeswitch_spec.rb +597 -0
- data/spec/punchblock_spec.rb +11 -0
- data/spec/spec_helper.rb +1 -0
- metadata +52 -7
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# [develop](https://github.com/adhearsion/punchblock)
|
|
2
2
|
|
|
3
|
+
# [v1.4.0](https://github.com/adhearsion/punchblock/compare/v1.3.0...v1.4.0) - [2012-08-07](https://rubygems.org/gems/punchblock/versions/1.4.0)
|
|
4
|
+
* Feature: FreeSWITCH support (mostly complete, experimental, proceed with caution)
|
|
5
|
+
* Bugfix: Report the correct caller ID in offers from Asterisk
|
|
6
|
+
* Bugfix: Strip out caller ID name from dial commands on Asterisk
|
|
7
|
+
|
|
3
8
|
# [v1.3.0](https://github.com/adhearsion/punchblock/compare/v1.2.0...v1.3.0) - [2012-07-22](https://rubygems.org/gems/punchblock/versions/1.3.0)
|
|
4
9
|
* Change: Asterisk output now uses Playback rather than STREAM FILE
|
|
5
10
|
* Feature: The recordings dir is now checked for existence on startup, and logs an error if it is not there. Asterisk only.
|
data/lib/punchblock.rb
CHANGED
|
@@ -44,7 +44,7 @@ module Punchblock
|
|
|
44
44
|
#
|
|
45
45
|
# Get a new Punchblock client with a connection attached
|
|
46
46
|
#
|
|
47
|
-
# @param [Symbol] type the connection type (eg :XMPP, :asterisk)
|
|
47
|
+
# @param [Symbol] type the connection type (eg :XMPP, :asterisk, :freeswitch)
|
|
48
48
|
# @param [Hash] options the options to pass to the connection (credentials, etc
|
|
49
49
|
#
|
|
50
50
|
# @return [Punchblock::Client] a punchblock client object
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'ruby_fs'
|
|
4
|
+
|
|
5
|
+
module Punchblock
|
|
6
|
+
module Connection
|
|
7
|
+
class Freeswitch < GenericConnection
|
|
8
|
+
attr_reader :translator, :stream
|
|
9
|
+
attr_accessor :event_handler
|
|
10
|
+
|
|
11
|
+
def initialize(options = {})
|
|
12
|
+
@translator = Translator::Freeswitch.new self, options[:media_engine], options[:default_voice]
|
|
13
|
+
@stream_options = options.values_at(:host, :port, :password)
|
|
14
|
+
@stream = new_fs_stream
|
|
15
|
+
super()
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run
|
|
19
|
+
pb_logger.debug "Starting the RubyFS stream"
|
|
20
|
+
start_stream
|
|
21
|
+
raise DisconnectedError
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def stop
|
|
25
|
+
stream.shutdown
|
|
26
|
+
translator.terminate
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def write(command, options)
|
|
30
|
+
translator.execute_command! command, options
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def handle_event(event)
|
|
34
|
+
event_handler.call event if event_handler.respond_to?(:call)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def new_fs_stream
|
|
40
|
+
RubyFS::Stream.new(*@stream_options, lambda { |e| translator.handle_es_event! e })
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def start_stream
|
|
44
|
+
@stream = new_fs_stream unless @stream.alive?
|
|
45
|
+
@stream.run
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -28,7 +28,7 @@ module Punchblock
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def inspect
|
|
31
|
-
"#<Punchblock::Event::Offer to=\"#{to}\", from=\"#{from}\", call_id=\"#{target_call_id}\""
|
|
31
|
+
"#<Punchblock::Event::Offer to=\"#{to}\", from=\"#{from}\", headers=#{headers_hash.inspect}, call_id=\"#{target_call_id}\""
|
|
32
32
|
end
|
|
33
33
|
end # Offer
|
|
34
34
|
end
|
|
@@ -22,7 +22,6 @@ module Punchblock
|
|
|
22
22
|
trap_exit :actor_died
|
|
23
23
|
|
|
24
24
|
def initialize(ami_client, connection, media_engine = nil)
|
|
25
|
-
pb_logger.debug "Starting up..."
|
|
26
25
|
@ami_client, @connection, @media_engine = ami_client, connection, media_engine
|
|
27
26
|
@calls, @components, @channel_to_call_id = {}, {}, {}
|
|
28
27
|
@fully_booted_count = 0
|
|
@@ -55,42 +54,38 @@ module Punchblock
|
|
|
55
54
|
end
|
|
56
55
|
|
|
57
56
|
def shutdown
|
|
58
|
-
pb_logger.debug "Shutting down"
|
|
59
57
|
@calls.values.each(&:shutdown!)
|
|
60
58
|
current_actor.terminate!
|
|
61
59
|
end
|
|
62
60
|
|
|
63
61
|
def handle_ami_event(event)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
@fully_booted_count = 0
|
|
73
|
-
run_at_fully_booted
|
|
74
|
-
end
|
|
75
|
-
return
|
|
62
|
+
return unless event.is_a? RubyAMI::Event
|
|
63
|
+
|
|
64
|
+
if event.name.downcase == "fullybooted"
|
|
65
|
+
@fully_booted_count += 1
|
|
66
|
+
if @fully_booted_count >= 2
|
|
67
|
+
handle_pb_event Connection::Connected.new
|
|
68
|
+
@fully_booted_count = 0
|
|
69
|
+
run_at_fully_booted
|
|
76
70
|
end
|
|
71
|
+
return
|
|
72
|
+
end
|
|
77
73
|
|
|
78
|
-
|
|
74
|
+
handle_varset_ami_event event
|
|
79
75
|
|
|
80
|
-
|
|
76
|
+
ami_dispatch_to_or_create_call event
|
|
81
77
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
end
|
|
78
|
+
unless ami_event_known_call?(event)
|
|
79
|
+
handle_pb_event Event::Asterisk::AMI::Event.new(:name => event.name, :attributes => event.headers)
|
|
85
80
|
end
|
|
86
81
|
end
|
|
82
|
+
exclusive :handle_ami_event
|
|
87
83
|
|
|
88
84
|
def handle_pb_event(event)
|
|
89
85
|
connection.handle_event event
|
|
90
86
|
end
|
|
91
87
|
|
|
92
88
|
def execute_command(command, options = {})
|
|
93
|
-
pb_logger.trace "Executing command #{command.inspect}"
|
|
94
89
|
command.request!
|
|
95
90
|
|
|
96
91
|
command.target_call_id ||= options[:call_id]
|
|
@@ -144,7 +139,6 @@ module Punchblock
|
|
|
144
139
|
send_ami_action('Command', {
|
|
145
140
|
'Command' => "dialplan add extension #{REDIRECT_EXTENSION},#{REDIRECT_PRIORITY},AGI,agi:async into #{REDIRECT_CONTEXT}"
|
|
146
141
|
})
|
|
147
|
-
pb_logger.trace "Added extension extension #{REDIRECT_EXTENSION},#{REDIRECT_PRIORITY},AGI,agi:async into #{REDIRECT_CONTEXT}"
|
|
148
142
|
send_ami_action('Command', {
|
|
149
143
|
'Command' => "dialplan show #{REDIRECT_CONTEXT}"
|
|
150
144
|
}) do |result|
|
|
@@ -160,9 +154,7 @@ module Punchblock
|
|
|
160
154
|
|
|
161
155
|
def actor_died(actor, reason)
|
|
162
156
|
return unless reason
|
|
163
|
-
pb_logger.error "A linked actor (#{actor.inspect}) died due to #{reason.inspect}"
|
|
164
157
|
if id = @calls.key(actor)
|
|
165
|
-
pb_logger.info "Dead actor was a call we know about, with ID #{id}. Removing it from the registry..."
|
|
166
158
|
@calls.delete id
|
|
167
159
|
end_event = Punchblock::Event::End.new :target_call_id => id,
|
|
168
160
|
:reason => :error
|
|
@@ -175,9 +167,7 @@ module Punchblock
|
|
|
175
167
|
def handle_varset_ami_event(event)
|
|
176
168
|
return unless event.name == 'VarSet' && event['Variable'] == 'punchblock_call_id' && (call = call_with_id event['Value'])
|
|
177
169
|
|
|
178
|
-
pb_logger.trace "Received a VarSet event indicating the full channel for call #{call}"
|
|
179
170
|
@channel_to_call_id.delete call.channel
|
|
180
|
-
pb_logger.trace "Removed call with old channel from channel map: #{@channel_to_call_id}"
|
|
181
171
|
call.channel = event['Channel']
|
|
182
172
|
register_call call
|
|
183
173
|
end
|
|
@@ -206,10 +196,8 @@ module Punchblock
|
|
|
206
196
|
def handle_async_agi_start_event(event)
|
|
207
197
|
env = RubyAMI::AsyncAGIEnvironmentParser.new(event['Env']).to_hash
|
|
208
198
|
|
|
209
|
-
return
|
|
210
|
-
return pb_logger.warn "Ignoring AsyncAGI Start event because it is for an 'Kill' type" if env[:agi_type] == 'Kill'
|
|
199
|
+
return if env[:agi_extension] == 'h' || env[:agi_type] == 'Kill'
|
|
211
200
|
|
|
212
|
-
pb_logger.trace "Handling AsyncAGI Start event by creating a new call"
|
|
213
201
|
call = Call.new event['Channel'], current_actor, env
|
|
214
202
|
link call
|
|
215
203
|
register_call call
|
|
@@ -28,7 +28,6 @@ module Punchblock
|
|
|
28
28
|
@id, @components = Punchblock.new_uuid, {}
|
|
29
29
|
@answered = false
|
|
30
30
|
@pending_joins = {}
|
|
31
|
-
pb_logger.debug "Starting up call with channel #{channel}, id #{@id}"
|
|
32
31
|
@progress_sent = false
|
|
33
32
|
end
|
|
34
33
|
|
|
@@ -46,7 +45,6 @@ module Punchblock
|
|
|
46
45
|
end
|
|
47
46
|
|
|
48
47
|
def shutdown
|
|
49
|
-
pb_logger.debug "Shutting down"
|
|
50
48
|
current_actor.terminate!
|
|
51
49
|
end
|
|
52
50
|
|
|
@@ -57,10 +55,12 @@ module Punchblock
|
|
|
57
55
|
|
|
58
56
|
def dial(dial_command)
|
|
59
57
|
@direction = :outbound
|
|
58
|
+
channel = dial_command.to || ''
|
|
59
|
+
channel.match(/.* <(?<channel>.*)>/) { |m| channel = m[:channel] }
|
|
60
60
|
params = { :async => true,
|
|
61
61
|
:application => 'AGI',
|
|
62
62
|
:data => 'agi:async',
|
|
63
|
-
:channel =>
|
|
63
|
+
:channel => channel,
|
|
64
64
|
:callerid => dial_command.from,
|
|
65
65
|
:variable => "punchblock_call_id=#{id}"
|
|
66
66
|
}
|
|
@@ -87,13 +87,11 @@ module Punchblock
|
|
|
87
87
|
|
|
88
88
|
def send_progress
|
|
89
89
|
return if answered? || outbound? || @progress_sent
|
|
90
|
-
pb_logger.debug "Sending Progress to start early media"
|
|
91
90
|
@progress_sent = true
|
|
92
91
|
send_agi_action "EXEC Progress"
|
|
93
92
|
end
|
|
94
93
|
|
|
95
94
|
def channel=(other)
|
|
96
|
-
pb_logger.info "Channel is changing from #{channel} to #{other}."
|
|
97
95
|
@channel = other
|
|
98
96
|
end
|
|
99
97
|
|
|
@@ -102,7 +100,6 @@ module Punchblock
|
|
|
102
100
|
|
|
103
101
|
case ami_event.name
|
|
104
102
|
when 'Hangup'
|
|
105
|
-
pb_logger.trace "Received a Hangup AMI event. Sending End event."
|
|
106
103
|
@components.dup.each_pair do |id, component|
|
|
107
104
|
safe_from_dead_actors do
|
|
108
105
|
component.call_ended if component.alive?
|
|
@@ -110,14 +107,10 @@ module Punchblock
|
|
|
110
107
|
end
|
|
111
108
|
send_end_event HANGUP_CAUSE_TO_END_REASON[ami_event['Cause'].to_i]
|
|
112
109
|
when 'AsyncAGI'
|
|
113
|
-
pb_logger.trace "Received an AsyncAGI event. Looking for matching AGICommand component."
|
|
114
110
|
if component = component_with_id(ami_event['CommandID'])
|
|
115
111
|
component.handle_ami_event ami_event
|
|
116
|
-
else
|
|
117
|
-
pb_logger.trace "Could not find component for AMI event: #{ami_event.inspect}"
|
|
118
112
|
end
|
|
119
113
|
when 'Newstate'
|
|
120
|
-
pb_logger.trace "Received a Newstate AMI event with state #{ami_event['ChannelState']}: #{ami_event['ChannelStateDesc']}"
|
|
121
114
|
case ami_event['ChannelState']
|
|
122
115
|
when '5'
|
|
123
116
|
send_pb_event Event::Ringing.new
|
|
@@ -127,7 +120,6 @@ module Punchblock
|
|
|
127
120
|
end
|
|
128
121
|
when 'OriginateResponse'
|
|
129
122
|
if ami_event['Response'] == 'Failure' && ami_event['Uniqueid'] == '<null>'
|
|
130
|
-
pb_logger.info "Outbound call could not be established!"
|
|
131
123
|
send_end_event :error
|
|
132
124
|
end
|
|
133
125
|
when 'BridgeExec'
|
|
@@ -162,7 +154,6 @@ module Punchblock
|
|
|
162
154
|
end
|
|
163
155
|
|
|
164
156
|
def execute_command(command)
|
|
165
|
-
pb_logger.debug "Executing command: #{command.inspect}"
|
|
166
157
|
if command.component_id
|
|
167
158
|
if component = component_with_id(command.component_id)
|
|
168
159
|
component.execute_command command
|
|
@@ -173,10 +164,8 @@ module Punchblock
|
|
|
173
164
|
case command
|
|
174
165
|
when Command::Accept
|
|
175
166
|
if outbound?
|
|
176
|
-
pb_logger.trace "Attempting to accept an outbound call. Skipping RINGING."
|
|
177
167
|
command.response = true
|
|
178
168
|
else
|
|
179
|
-
pb_logger.trace "Attempting to accept an inbound call. Executing RINGING."
|
|
180
169
|
send_agi_action 'EXEC RINGING' do |response|
|
|
181
170
|
command.response = true
|
|
182
171
|
end
|
|
@@ -231,11 +220,9 @@ module Punchblock
|
|
|
231
220
|
end
|
|
232
221
|
|
|
233
222
|
def send_agi_action(command, *params, &block)
|
|
234
|
-
pb_logger.trace "Sending AGI action #{command}"
|
|
235
223
|
@current_agi_command = Punchblock::Component::Asterisk::AGI::Command.new :name => command, :params => params, :target_call_id => id
|
|
236
224
|
@current_agi_command.request!
|
|
237
225
|
@current_agi_command.register_handler :internal, Punchblock::Event::Complete do |e|
|
|
238
|
-
pb_logger.trace "AGI action received complete event #{e.inspect}"
|
|
239
226
|
block.call e if block
|
|
240
227
|
end
|
|
241
228
|
execute_component Component::Asterisk::AGICommand, @current_agi_command, :internal => true
|
|
@@ -244,7 +231,6 @@ module Punchblock
|
|
|
244
231
|
def send_ami_action(name, headers = {}, &block)
|
|
245
232
|
(name.is_a?(RubyAMI::Action) ? name : RubyAMI::Action.new(name, headers, &block)).tap do |action|
|
|
246
233
|
@current_ami_action = action
|
|
247
|
-
pb_logger.trace "Sending AMI action #{action.inspect}"
|
|
248
234
|
translator.send_ami_action action
|
|
249
235
|
end
|
|
250
236
|
end
|
|
@@ -271,9 +257,7 @@ module Punchblock
|
|
|
271
257
|
|
|
272
258
|
def actor_died(actor, reason)
|
|
273
259
|
return unless reason
|
|
274
|
-
pb_logger.error "A linked actor (#{actor.inspect}) died due to #{reason.inspect}"
|
|
275
260
|
if id = @components.key(actor)
|
|
276
|
-
pb_logger.info "Dead actor was a component we know about, with ID #{id}. Removing it from the registry..."
|
|
277
261
|
@components.delete id
|
|
278
262
|
complete_event = Punchblock::Event::Complete.new :component_id => id, :reason => Punchblock::Event::Complete::Error.new
|
|
279
263
|
send_pb_event complete_event
|
|
@@ -298,13 +282,12 @@ module Punchblock
|
|
|
298
282
|
|
|
299
283
|
def send_pb_event(event)
|
|
300
284
|
event.target_call_id = id
|
|
301
|
-
pb_logger.trace "Sending Punchblock event: #{event.inspect}"
|
|
302
285
|
translator.handle_pb_event event
|
|
303
286
|
end
|
|
304
287
|
|
|
305
288
|
def offer_event
|
|
306
289
|
Event::Offer.new :to => agi_env.values_at(:agi_dnid, :agi_extension).detect { |e| e && e != 'unknown' },
|
|
307
|
-
:from => "#{agi_env[:agi_calleridname]} <#{[agi_env[:agi_type]
|
|
290
|
+
:from => "#{agi_env[:agi_calleridname]} <#{[agi_env[:agi_type], agi_env[:agi_callerid]].join('/')}>",
|
|
308
291
|
:headers => sip_headers
|
|
309
292
|
end
|
|
310
293
|
|
|
@@ -13,8 +13,6 @@ module Punchblock
|
|
|
13
13
|
autoload :StopByRedirect
|
|
14
14
|
|
|
15
15
|
class Component
|
|
16
|
-
OptionError = Class.new Punchblock::Error
|
|
17
|
-
|
|
18
16
|
include Celluloid
|
|
19
17
|
include DeadActorSafety
|
|
20
18
|
|
|
@@ -27,7 +25,6 @@ module Punchblock
|
|
|
27
25
|
@id = Punchblock.new_uuid
|
|
28
26
|
@complete = false
|
|
29
27
|
setup
|
|
30
|
-
pb_logger.debug "Starting up..."
|
|
31
28
|
end
|
|
32
29
|
|
|
33
30
|
def setup
|
|
@@ -51,7 +48,6 @@ module Punchblock
|
|
|
51
48
|
def send_event(event)
|
|
52
49
|
event.component_id = id
|
|
53
50
|
event.target_call_id = call_id
|
|
54
|
-
pb_logger.debug "Sending event #{event}"
|
|
55
51
|
if internal
|
|
56
52
|
@component_node.add_event event
|
|
57
53
|
else
|
|
@@ -74,7 +70,6 @@ module Punchblock
|
|
|
74
70
|
end
|
|
75
71
|
|
|
76
72
|
def set_node_response(value)
|
|
77
|
-
pb_logger.debug "Setting response on component node to #{value}"
|
|
78
73
|
@component_node.response = value
|
|
79
74
|
end
|
|
80
75
|
|
|
@@ -20,17 +20,14 @@ module Punchblock
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def handle_ami_event(event)
|
|
23
|
-
pb_logger.debug "Handling AMI event: #{event.inspect}"
|
|
24
23
|
if event.name == 'AsyncAGI'
|
|
25
24
|
if event['SubEvent'] == 'Exec'
|
|
26
|
-
pb_logger.debug "Received AsyncAGI:Exec event, sending complete event."
|
|
27
25
|
send_complete_event success_reason(event)
|
|
28
26
|
end
|
|
29
27
|
end
|
|
30
28
|
end
|
|
31
29
|
|
|
32
30
|
def handle_response(response)
|
|
33
|
-
pb_logger.debug "Handling response: #{response.inspect}"
|
|
34
31
|
case response
|
|
35
32
|
when RubyAMI::Error
|
|
36
33
|
set_node_response false
|
|
@@ -6,114 +6,24 @@ module Punchblock
|
|
|
6
6
|
module Component
|
|
7
7
|
class Input < Component
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def setup
|
|
12
|
-
@buffer = ""
|
|
13
|
-
end
|
|
9
|
+
include InputComponent
|
|
14
10
|
|
|
15
11
|
def execute
|
|
16
12
|
@call.send_progress
|
|
17
|
-
|
|
18
|
-
@inter_digit_timeout = @component_node.inter_digit_timeout || -1
|
|
19
|
-
|
|
20
|
-
raise OptionError, 'A grammar document is required.' unless @component_node.grammar
|
|
21
|
-
raise OptionError, 'A mode value other than DTMF is unsupported on Asterisk.' unless @component_node.mode == :dtmf
|
|
22
|
-
raise OptionError, 'An initial timeout value that is negative (and not -1) is invalid.' unless initial_timeout >= -1
|
|
23
|
-
raise OptionError, 'An inter-digit timeout value that is negative (and not -1) is invalid.' unless @inter_digit_timeout >= -1
|
|
24
|
-
|
|
25
|
-
send_ref
|
|
26
|
-
|
|
27
|
-
@grammar = @component_node.grammar.value.clone
|
|
28
|
-
grammar.inline!
|
|
29
|
-
grammar.tokenize!
|
|
30
|
-
grammar.normalize_whitespace
|
|
31
|
-
|
|
32
|
-
begin_initial_timer initial_timeout/1000 unless initial_timeout == -1
|
|
33
|
-
|
|
34
|
-
component = current_actor
|
|
35
|
-
|
|
36
|
-
@dtmf_handler_id = call.register_handler :ami, :name => 'DTMF' do |event|
|
|
37
|
-
component.process_dtmf! event['Digit'] if event['End'] == 'Yes'
|
|
38
|
-
end
|
|
39
|
-
rescue OptionError => e
|
|
40
|
-
with_error 'option error', e.message
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
def process_dtmf(digit)
|
|
44
|
-
pb_logger.trace "Processing incoming DTMF digit #{digit}"
|
|
45
|
-
buffer << digit
|
|
46
|
-
cancel_initial_timer
|
|
47
|
-
case (match = grammar.match buffer.dup)
|
|
48
|
-
when RubySpeech::GRXML::Match
|
|
49
|
-
pb_logger.trace "Found a match against buffer #{buffer}"
|
|
50
|
-
complete success_reason(match)
|
|
51
|
-
when RubySpeech::GRXML::NoMatch
|
|
52
|
-
pb_logger.trace "Buffer #{buffer} does not match grammar"
|
|
53
|
-
complete Punchblock::Component::Input::Complete::NoMatch.new
|
|
54
|
-
when RubySpeech::GRXML::PotentialMatch
|
|
55
|
-
pb_logger.trace "Buffer #{buffer} potentially matches grammar. Waiting..."
|
|
56
|
-
reset_inter_digit_timer
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def execute_command(command)
|
|
61
|
-
case command
|
|
62
|
-
when Punchblock::Component::Stop
|
|
63
|
-
command.response = true
|
|
64
|
-
complete Punchblock::Event::Complete::Stop.new
|
|
65
|
-
else
|
|
66
|
-
super
|
|
67
|
-
end
|
|
13
|
+
super
|
|
68
14
|
end
|
|
69
15
|
|
|
70
16
|
private
|
|
71
17
|
|
|
72
|
-
def
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
complete Punchblock::Component::Input::Complete::NoInput.new
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def cancel_initial_timer
|
|
81
|
-
return unless instance_variable_defined?(:@initial_timer) && @initial_timer
|
|
82
|
-
@initial_timer.cancel
|
|
83
|
-
@initial_timer = nil
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def reset_inter_digit_timer
|
|
87
|
-
return if @inter_digit_timeout == -1
|
|
88
|
-
@inter_digit_timer ||= begin
|
|
89
|
-
pb_logger.trace "Setting inter-digit timer for #{@inter_digit_timeout/1000} seconds"
|
|
90
|
-
after @inter_digit_timeout/1000 do
|
|
91
|
-
pb_logger.trace "Inter digit-timer expired."
|
|
92
|
-
complete Punchblock::Component::Input::Complete::NoMatch.new
|
|
93
|
-
end
|
|
18
|
+
def register_dtmf_event_handler
|
|
19
|
+
component = current_actor
|
|
20
|
+
call.register_handler :ami, :name => 'DTMF' do |event|
|
|
21
|
+
component.process_dtmf! event['Digit'] if event['End'] == 'Yes'
|
|
94
22
|
end
|
|
95
|
-
pb_logger.trace "Resetting inter-digit timer"
|
|
96
|
-
@inter_digit_timer.reset
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def cancel_inter_digit_timer
|
|
100
|
-
return unless instance_variable_defined?(:@inter_digit_timer) && @inter_digit_timer
|
|
101
|
-
@inter_digit_timer.cancel
|
|
102
|
-
@inter_digit_timer = nil
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
def success_reason(match)
|
|
106
|
-
Punchblock::Component::Input::Complete::Success.new :mode => match.mode,
|
|
107
|
-
:confidence => match.confidence,
|
|
108
|
-
:utterance => match.utterance,
|
|
109
|
-
:interpretation => match.interpretation
|
|
110
23
|
end
|
|
111
24
|
|
|
112
|
-
def
|
|
25
|
+
def unregister_dtmf_event_handler
|
|
113
26
|
call.unregister_handler :ami, @dtmf_handler_id if instance_variable_defined?(:@dtmf_handler_id)
|
|
114
|
-
cancel_initial_timer
|
|
115
|
-
cancel_inter_digit_timer
|
|
116
|
-
send_complete_event reason
|
|
117
27
|
end
|
|
118
28
|
end
|
|
119
29
|
end
|