punchblock 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +12 -0
- data/lib/punchblock.rb +1 -0
- data/lib/punchblock/command/join.rb +6 -6
- data/lib/punchblock/command/unjoin.rb +6 -6
- data/lib/punchblock/command_node.rb +1 -0
- data/lib/punchblock/component/input.rb +24 -4
- data/lib/punchblock/component/output.rb +5 -1
- data/lib/punchblock/component/tropo/ask.rb +3 -1
- data/lib/punchblock/connection/xmpp.rb +28 -10
- data/lib/punchblock/event/joined.rb +6 -6
- data/lib/punchblock/event/unjoined.rb +6 -6
- data/lib/punchblock/media_container.rb +6 -5
- data/lib/punchblock/protocol_error.rb +5 -0
- data/lib/punchblock/rayo_node.rb +1 -1
- data/lib/punchblock/translator/asterisk.rb +3 -3
- data/lib/punchblock/translator/asterisk/call.rb +9 -3
- data/lib/punchblock/translator/asterisk/component.rb +35 -0
- data/lib/punchblock/translator/asterisk/component/asterisk.rb +1 -0
- data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +14 -23
- data/lib/punchblock/translator/asterisk/component/asterisk/ami_action.rb +5 -18
- data/lib/punchblock/translator/asterisk/component/asterisk/output.rb +94 -0
- data/lib/punchblock/version.rb +1 -1
- data/punchblock.gemspec +1 -0
- data/spec/punchblock/command/join_spec.rb +4 -4
- data/spec/punchblock/command/unjoin_spec.rb +4 -4
- data/spec/punchblock/component/input_spec.rb +28 -31
- data/spec/punchblock/component/output_spec.rb +23 -5
- data/spec/punchblock/component/tropo/ask_spec.rb +31 -34
- data/spec/punchblock/connection/xmpp_spec.rb +105 -3
- data/spec/punchblock/event/joined_spec.rb +4 -4
- data/spec/punchblock/event/unjoined_spec.rb +4 -4
- data/spec/punchblock/protocol_error_spec.rb +32 -1
- data/spec/punchblock/translator/asterisk/call_spec.rb +17 -3
- data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +17 -0
- data/spec/punchblock/translator/asterisk/component/asterisk/output_spec.rb +489 -0
- data/spec/punchblock/translator/asterisk_spec.rb +14 -3
- metadata +53 -44
- data/assets/ozone/ask-1.0.xsd +0 -56
- data/assets/ozone/conference-1.0.xsd +0 -17
- data/assets/ozone/ozone-1.0.xsd +0 -127
- data/assets/ozone/say-1.0.xsd +0 -24
- data/assets/ozone/transfer-1.0.xsd +0 -32
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
# develop
|
2
2
|
|
3
|
+
# v0.7.2 - 2011-12-28
|
4
|
+
* Feature: Allow sending commands to mixers easily
|
5
|
+
* Feature: Allow configuration of Rayo XMPP domains (root, call and mixer)
|
6
|
+
* Feature: Log Blather messages to the trace log level
|
7
|
+
* Feature: Return an error when trying to execute an Output on Asterisk with unsupported options set
|
8
|
+
* Feature: Add basic support for media output via MRCPSynth on Asterisk
|
9
|
+
* API change: Rename mixer_id to mixer_name to align with change to Rayo
|
10
|
+
* Bugfix: Handle and expose RubySpeech GRXML documents on Input/Ask properly
|
11
|
+
* Bugfix: Compare ProtocolErrors correctly
|
12
|
+
* Bugfix: Asterisk media output should default to Asterisk native output (STREAM FILE)
|
13
|
+
* Bugfix: An Output node's default max_time value should be nil rather than zero
|
14
|
+
|
3
15
|
# v0.7.1 - 2011-11-24
|
4
16
|
* [FEATURE] Add `Connection#not_ready!`, to instruct the server not to send any more offers.
|
5
17
|
* [BUGFIX] Translate all exceptions raised by the XMPP connection into a ProtocolError
|
data/lib/punchblock.rb
CHANGED
@@ -8,7 +8,7 @@ module Punchblock
|
|
8
8
|
#
|
9
9
|
# @param [Hash] options
|
10
10
|
# @option options [String, Optional] :other_call_id the call ID to join
|
11
|
-
# @option options [String, Optional] :
|
11
|
+
# @option options [String, Optional] :mixer_name the mixer name to join
|
12
12
|
# @option options [Symbol, Optional] :direction the direction in which media should flow
|
13
13
|
# @option options [Symbol, Optional] :media the method by which to negotiate media
|
14
14
|
#
|
@@ -39,14 +39,14 @@ module Punchblock
|
|
39
39
|
|
40
40
|
##
|
41
41
|
# @return [String] the mixer name to join
|
42
|
-
def
|
43
|
-
read_attr :'mixer-
|
42
|
+
def mixer_name
|
43
|
+
read_attr :'mixer-name'
|
44
44
|
end
|
45
45
|
|
46
46
|
##
|
47
47
|
# @param [String] other the mixer name to join
|
48
|
-
def
|
49
|
-
write_attr :'mixer-
|
48
|
+
def mixer_name=(other)
|
49
|
+
write_attr :'mixer-name', other
|
50
50
|
end
|
51
51
|
|
52
52
|
##
|
@@ -74,7 +74,7 @@ module Punchblock
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def inspect_attributes # :nodoc:
|
77
|
-
[:other_call_id, :
|
77
|
+
[:other_call_id, :mixer_name, :direction, :media] + super
|
78
78
|
end
|
79
79
|
end # Join
|
80
80
|
end # Command
|
@@ -8,7 +8,7 @@ module Punchblock
|
|
8
8
|
#
|
9
9
|
# @param [Hash] options
|
10
10
|
# @option options [String, Optional] :other_call_id the call ID to unjoin
|
11
|
-
# @option options [String, Optional] :
|
11
|
+
# @option options [String, Optional] :mixer_name the mixer name to unjoin
|
12
12
|
#
|
13
13
|
# @return [Command::Unjoin] a formatted Rayo unjoin command
|
14
14
|
#
|
@@ -32,18 +32,18 @@ module Punchblock
|
|
32
32
|
|
33
33
|
##
|
34
34
|
# @return [String] the mixer name to unjoin
|
35
|
-
def
|
36
|
-
read_attr :'mixer-
|
35
|
+
def mixer_name
|
36
|
+
read_attr :'mixer-name'
|
37
37
|
end
|
38
38
|
|
39
39
|
##
|
40
40
|
# @param [String] other the mixer name to unjoin
|
41
|
-
def
|
42
|
-
write_attr :'mixer-
|
41
|
+
def mixer_name=(other)
|
42
|
+
write_attr :'mixer-name', other
|
43
43
|
end
|
44
44
|
|
45
45
|
def inspect_attributes # :nodoc:
|
46
|
-
[:other_call_id, :
|
46
|
+
[:other_call_id, :mixer_name] + super
|
47
47
|
end
|
48
48
|
end # Unjoin
|
49
49
|
end # Command
|
@@ -208,7 +208,8 @@ module Punchblock
|
|
208
208
|
# @return [Choices] the choices available
|
209
209
|
#
|
210
210
|
def grammar
|
211
|
-
|
211
|
+
node = find_first 'ns:grammar', :ns => self.class.registered_ns
|
212
|
+
Grammar.new node if node
|
212
213
|
end
|
213
214
|
|
214
215
|
##
|
@@ -217,6 +218,7 @@ module Punchblock
|
|
217
218
|
# @option choices [String] :value the choices available
|
218
219
|
#
|
219
220
|
def grammar=(other)
|
221
|
+
return unless other
|
220
222
|
remove_children :grammar
|
221
223
|
grammar = Grammar.new(other) unless other.is_a?(Grammar)
|
222
224
|
self << grammar
|
@@ -252,21 +254,29 @@ module Punchblock
|
|
252
254
|
end
|
253
255
|
|
254
256
|
##
|
255
|
-
# @param [String] content_type Defaults to
|
257
|
+
# @param [String] content_type Defaults to GRXML
|
256
258
|
#
|
257
259
|
def content_type=(content_type)
|
258
|
-
write_attr 'content-type', content_type ||
|
260
|
+
write_attr 'content-type', content_type || grxml_content_type
|
259
261
|
end
|
260
262
|
|
261
263
|
##
|
262
264
|
# @return [String] the choices available
|
263
265
|
def value
|
264
|
-
|
266
|
+
if grxml?
|
267
|
+
RubySpeech::GRXML.import content
|
268
|
+
else
|
269
|
+
content
|
270
|
+
end
|
265
271
|
end
|
266
272
|
|
267
273
|
##
|
268
274
|
# @param [String] value the choices available
|
269
275
|
def value=(value)
|
276
|
+
return unless value
|
277
|
+
if grxml? && !value.is_a?(RubySpeech::GRXML::Element)
|
278
|
+
value = RubySpeech::GRXML.import value
|
279
|
+
end
|
270
280
|
Nokogiri::XML::Builder.with(self) do |xml|
|
271
281
|
xml.cdata " #{value} "
|
272
282
|
end
|
@@ -282,6 +292,16 @@ module Punchblock
|
|
282
292
|
def inspect_attributes # :nodoc:
|
283
293
|
[:content_type, :value] + super
|
284
294
|
end
|
295
|
+
|
296
|
+
private
|
297
|
+
|
298
|
+
def grxml_content_type
|
299
|
+
'application/grammar+grxml'
|
300
|
+
end
|
301
|
+
|
302
|
+
def grxml?
|
303
|
+
content_type == grxml_content_type
|
304
|
+
end
|
285
305
|
end # Choices
|
286
306
|
|
287
307
|
class Complete
|
@@ -109,7 +109,7 @@ module Punchblock
|
|
109
109
|
# @return [String] the TTS voice to use
|
110
110
|
#
|
111
111
|
def max_time
|
112
|
-
read_attr
|
112
|
+
read_attr :'max-time', :to_i
|
113
113
|
end
|
114
114
|
|
115
115
|
##
|
@@ -119,6 +119,10 @@ module Punchblock
|
|
119
119
|
write_attr :'max-time', other
|
120
120
|
end
|
121
121
|
|
122
|
+
def inspect_attributes
|
123
|
+
super + [:interrupt_on, :start_offset, :start_paused, :repeat_interval, :repeat_times, :max_time]
|
124
|
+
end
|
125
|
+
|
122
126
|
state_machine :state do
|
123
127
|
event :paused do
|
124
128
|
transition :executing => :paused
|
@@ -147,7 +147,8 @@ module Punchblock
|
|
147
147
|
# @return [Choices] the choices available
|
148
148
|
#
|
149
149
|
def choices
|
150
|
-
|
150
|
+
node = find_first 'ns:choices', :ns => self.class.registered_ns
|
151
|
+
Choices.new node if node
|
151
152
|
end
|
152
153
|
|
153
154
|
##
|
@@ -156,6 +157,7 @@ module Punchblock
|
|
156
157
|
# @option choices [String] :value the choices available
|
157
158
|
#
|
158
159
|
def choices=(choices)
|
160
|
+
return unless choices
|
159
161
|
remove_children :choices
|
160
162
|
choices = Choices.new(choices) unless choices.is_a?(Choices)
|
161
163
|
self << choices
|
@@ -9,7 +9,7 @@ module Punchblock
|
|
9
9
|
module Connection
|
10
10
|
class XMPP < GenericConnection
|
11
11
|
include Blather::DSL
|
12
|
-
attr_accessor :event_handler
|
12
|
+
attr_accessor :event_handler, :root_domain, :calls_domain, :mixers_domain
|
13
13
|
|
14
14
|
##
|
15
15
|
# Initialize the required connection attributes
|
@@ -27,7 +27,9 @@ module Punchblock
|
|
27
27
|
|
28
28
|
setup *[:username, :password, :host, :port, :certs].map { |key| options.delete key }
|
29
29
|
|
30
|
-
@
|
30
|
+
@root_domain = Blather::JID.new(options[:root_domain] || options[:rayo_domain] || @username).domain
|
31
|
+
@calls_domain = options[:calls_domain] || "calls.#{@root_domain}"
|
32
|
+
@mixers_domain = options[:mixers_domain] || "mixers.#{@root_domain}"
|
31
33
|
|
32
34
|
@callmap = {} # This hash maps call IDs to their XMPP domain.
|
33
35
|
|
@@ -37,6 +39,7 @@ module Punchblock
|
|
37
39
|
@ping_period = options.has_key?(:ping_period) ? options[:ping_period] : 60
|
38
40
|
|
39
41
|
Blather.logger = pb_logger
|
42
|
+
Blather.default_log_level = :trace if Blather.respond_to? :default_log_level
|
40
43
|
|
41
44
|
super()
|
42
45
|
end
|
@@ -54,12 +57,11 @@ module Punchblock
|
|
54
57
|
end
|
55
58
|
|
56
59
|
def prep_command_for_execution(command, options = {})
|
57
|
-
|
58
|
-
command.
|
59
|
-
command.
|
60
|
-
|
61
|
-
|
62
|
-
create_iq(jid).tap do |iq|
|
60
|
+
command.connection = self
|
61
|
+
command.call_id ||= options[:call_id]
|
62
|
+
command.mixer_name ||= options[:mixer_name]
|
63
|
+
command.component_id ||= options[:component_id]
|
64
|
+
create_iq(jid_for_command(command)).tap do |iq|
|
63
65
|
pb_logger.debug "Sending IQ ID #{iq.id} #{command.inspect} to #{jid}"
|
64
66
|
iq << command
|
65
67
|
end
|
@@ -102,9 +104,25 @@ module Punchblock
|
|
102
104
|
|
103
105
|
private
|
104
106
|
|
107
|
+
def jid_for_command(command)
|
108
|
+
return root_domain if command.is_a?(Command::Dial)
|
109
|
+
|
110
|
+
if command.call_id
|
111
|
+
node = command.call_id
|
112
|
+
domain = @callmap[command.call_id] || calls_domain
|
113
|
+
elsif command.mixer_name
|
114
|
+
node = command.mixer_name
|
115
|
+
domain = @callmap[command.mixer_name] || mixers_domain
|
116
|
+
else
|
117
|
+
domain = calls_domain
|
118
|
+
end
|
119
|
+
|
120
|
+
Blather::JID.new(node, domain, command.component_id).to_s
|
121
|
+
end
|
122
|
+
|
105
123
|
def send_presence(presence)
|
106
124
|
status = Blather::Stanza::Presence::Status.new presence
|
107
|
-
status.to =
|
125
|
+
status.to = root_domain
|
108
126
|
client.write status
|
109
127
|
end
|
110
128
|
|
@@ -160,7 +178,7 @@ module Punchblock
|
|
160
178
|
end
|
161
179
|
|
162
180
|
def ping_rayo
|
163
|
-
client.write_with_handler Blather::Stanza::Iq::Ping.new(:set,
|
181
|
+
client.write_with_handler Blather::Stanza::Iq::Ping.new(:set, root_domain) do |response|
|
164
182
|
begin
|
165
183
|
handle_error response if response.is_a? Blather::BlatherError
|
166
184
|
rescue ProtocolError => e
|
@@ -8,7 +8,7 @@ module Punchblock
|
|
8
8
|
#
|
9
9
|
# @param [Hash] options
|
10
10
|
# @option options [String, Optional] :other_call_id the call ID that was joined
|
11
|
-
# @option options [String, Optional] :
|
11
|
+
# @option options [String, Optional] :mixer_name the mixer name that was joined
|
12
12
|
#
|
13
13
|
# @return [Event::Joined] a formatted Rayo joined event
|
14
14
|
#
|
@@ -32,18 +32,18 @@ module Punchblock
|
|
32
32
|
|
33
33
|
##
|
34
34
|
# @return [String] the mixer name that was joined
|
35
|
-
def
|
36
|
-
read_attr :'mixer-
|
35
|
+
def mixer_name
|
36
|
+
read_attr :'mixer-name'
|
37
37
|
end
|
38
38
|
|
39
39
|
##
|
40
40
|
# @param [String] other the mixer name that was joined
|
41
|
-
def
|
42
|
-
write_attr :'mixer-
|
41
|
+
def mixer_name=(other)
|
42
|
+
write_attr :'mixer-name', other
|
43
43
|
end
|
44
44
|
|
45
45
|
def inspect_attributes # :nodoc:
|
46
|
-
[:other_call_id, :
|
46
|
+
[:other_call_id, :mixer_name] + super
|
47
47
|
end
|
48
48
|
end # Joined
|
49
49
|
end
|
@@ -8,7 +8,7 @@ module Punchblock
|
|
8
8
|
#
|
9
9
|
# @param [Hash] options
|
10
10
|
# @option options [String, Optional] :other_call_id the call ID that was unjoined
|
11
|
-
# @option options [String, Optional] :
|
11
|
+
# @option options [String, Optional] :mixer_name the mixer name that was unjoined
|
12
12
|
#
|
13
13
|
# @return [Event::Unjoined] a formatted Rayo unjoined event
|
14
14
|
#
|
@@ -32,18 +32,18 @@ module Punchblock
|
|
32
32
|
|
33
33
|
##
|
34
34
|
# @return [String] the mixer name that was unjoined
|
35
|
-
def
|
36
|
-
read_attr :'mixer-
|
35
|
+
def mixer_name
|
36
|
+
read_attr :'mixer-name'
|
37
37
|
end
|
38
38
|
|
39
39
|
##
|
40
40
|
# @param [String] other the mixer name that was unjoined
|
41
|
-
def
|
42
|
-
write_attr :'mixer-
|
41
|
+
def mixer_name=(other)
|
42
|
+
write_attr :'mixer-name', other
|
43
43
|
end
|
44
44
|
|
45
45
|
def inspect_attributes # :nodoc:
|
46
|
-
[:other_call_id, :
|
46
|
+
[:other_call_id, :mixer_name] + super
|
47
47
|
end
|
48
48
|
end # Unjoined
|
49
49
|
end
|
@@ -18,18 +18,19 @@ module Punchblock
|
|
18
18
|
# @return [String] the SSML document to render TTS
|
19
19
|
#
|
20
20
|
def ssml
|
21
|
-
children.
|
21
|
+
node = children.first
|
22
|
+
RubySpeech::SSML.import node if node
|
22
23
|
end
|
23
24
|
|
24
25
|
##
|
25
26
|
# @param [String] ssml the SSML document to render TTS
|
26
27
|
#
|
27
28
|
def ssml=(ssml)
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
29
|
+
return unless ssml
|
30
|
+
unless ssml.is_a?(RubySpeech::SSML::Element)
|
31
|
+
ssml = RubySpeech::SSML.import ssml
|
32
32
|
end
|
33
|
+
self << ssml
|
33
34
|
end
|
34
35
|
|
35
36
|
def inspect_attributes # :nodoc:
|
@@ -12,5 +12,10 @@ module Punchblock
|
|
12
12
|
"#<#{self.class}: name=#{name.inspect} text=#{text.inspect} call_id=#{call_id.inspect} component_id=#{component_id.inspect}>"
|
13
13
|
end
|
14
14
|
alias :inspect :to_s
|
15
|
+
|
16
|
+
def eql?(other)
|
17
|
+
other.is_a?(self.class) && [:name, :text, :call_id, :component_id].all? { |f| self.__send__(f) == other.__send__(f) }
|
18
|
+
end
|
19
|
+
alias :== :eql?
|
15
20
|
end
|
16
21
|
end
|
data/lib/punchblock/rayo_node.rb
CHANGED
@@ -7,7 +7,7 @@ module Punchblock
|
|
7
7
|
|
8
8
|
class_attribute :registered_ns, :registered_name
|
9
9
|
|
10
|
-
attr_accessor :call_id, :component_id, :domain, :connection, :client, :original_component
|
10
|
+
attr_accessor :call_id, :mixer_name, :component_id, :domain, :connection, :client, :original_component
|
11
11
|
|
12
12
|
# Register a new stanza class to a name and/or namespace
|
13
13
|
#
|
@@ -11,11 +11,11 @@ module Punchblock
|
|
11
11
|
autoload :Call
|
12
12
|
autoload :Component
|
13
13
|
|
14
|
-
attr_reader :ami_client, :connection, :calls
|
14
|
+
attr_reader :ami_client, :connection, :media_engine, :calls
|
15
15
|
|
16
|
-
def initialize(ami_client, connection)
|
16
|
+
def initialize(ami_client, connection, media_engine = nil)
|
17
17
|
pb_logger.debug "Starting up..."
|
18
|
-
@ami_client, @connection = ami_client, connection
|
18
|
+
@ami_client, @connection, @media_engine = ami_client, connection, media_engine
|
19
19
|
@calls, @components, @channel_to_call_id = {}, {}, {}
|
20
20
|
@fully_booted_count = 0
|
21
21
|
end
|
@@ -64,12 +64,14 @@ module Punchblock
|
|
64
64
|
end
|
65
65
|
when Punchblock::Component::Asterisk::AGI::Command
|
66
66
|
execute_agi_command command
|
67
|
+
when Punchblock::Component::Output
|
68
|
+
execute_component Component::Asterisk::Output, command
|
67
69
|
end
|
68
70
|
end
|
69
71
|
|
70
|
-
def send_agi_action(command, &block)
|
72
|
+
def send_agi_action(command, *params, &block)
|
71
73
|
pb_logger.debug "Sending AGI action #{command}"
|
72
|
-
@current_agi_command = Punchblock::Component::Asterisk::AGI::Command.new :name => command, :call_id => id
|
74
|
+
@current_agi_command = Punchblock::Component::Asterisk::AGI::Command.new :name => command, :params => params, :call_id => id
|
73
75
|
@current_agi_command.request!
|
74
76
|
@current_agi_command.register_handler :internal, Punchblock::Event::Complete do |e|
|
75
77
|
pb_logger.debug "AGI action received complete event #{e.inspect}"
|
@@ -89,7 +91,11 @@ module Punchblock
|
|
89
91
|
private
|
90
92
|
|
91
93
|
def execute_agi_command(command)
|
92
|
-
Component::Asterisk::AGICommand
|
94
|
+
execute_component Component::Asterisk::AGICommand, command
|
95
|
+
end
|
96
|
+
|
97
|
+
def execute_component(type, command)
|
98
|
+
type.new(command, current_actor).tap do |component|
|
93
99
|
register_component component
|
94
100
|
component.execute!
|
95
101
|
end
|