punchblock 0.7.2 → 0.8.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 +6 -0
- data/lib/punchblock.rb +2 -1
- data/lib/punchblock/component/tropo.rb +0 -3
- data/lib/punchblock/connection/xmpp.rb +9 -16
- data/lib/punchblock/disconnected_error.rb +16 -0
- data/lib/punchblock/version.rb +1 -1
- data/punchblock.gemspec +1 -1
- data/spec/punchblock/connection/xmpp_spec.rb +12 -12
- data/spec/punchblock/event/complete_spec.rb +2 -2
- metadata +43 -51
- data/lib/punchblock/component/tropo/ask.rb +0 -199
- data/lib/punchblock/component/tropo/say.rb +0 -113
- data/lib/punchblock/component/tropo/transfer.rb +0 -178
- data/spec/punchblock/component/tropo/ask_spec.rb +0 -181
- data/spec/punchblock/component/tropo/say_spec.rb +0 -172
- data/spec/punchblock/component/tropo/transfer_spec.rb +0 -154
|
@@ -1,199 +0,0 @@
|
|
|
1
|
-
module Punchblock
|
|
2
|
-
module Component
|
|
3
|
-
module Tropo
|
|
4
|
-
class Ask < ComponentNode
|
|
5
|
-
register :ask, :ask
|
|
6
|
-
|
|
7
|
-
##
|
|
8
|
-
# Create an ask message
|
|
9
|
-
#
|
|
10
|
-
# @param [Hash] options for asking/prompting a specific call
|
|
11
|
-
# @option options [Choices, Hash] :choices to allow the user to input
|
|
12
|
-
# @option options [Prompt, Hash, Optional] :prompt to play/read to the caller as the question
|
|
13
|
-
# @option options [Symbol, Optional] :mode by which to accept input. Can be :speech, :dtmf or :any
|
|
14
|
-
# @option options [Integer, Optional] :timeout to wait for user input
|
|
15
|
-
# @option options [Boolean, Optional] :bargein wether or not to allow the caller to begin their response before the prompt finishes
|
|
16
|
-
# @option options [String, Optional] :recognizer to use for speech recognition
|
|
17
|
-
# @option options [String, Optional] :terminator by which to signal the end of input
|
|
18
|
-
# @option options [Float, Optional] :min_confidence with which to consider a response acceptable
|
|
19
|
-
#
|
|
20
|
-
# @return [Command::Ask] a formatted Rayo ask command
|
|
21
|
-
#
|
|
22
|
-
# @example
|
|
23
|
-
# ask :prompt => {:text => 'Please enter your postal code.', :voice => 'simon'},
|
|
24
|
-
# :choices => {:value => '[5 DIGITS]'},
|
|
25
|
-
# :timeout => 30,
|
|
26
|
-
# :recognizer => 'es-es'
|
|
27
|
-
#
|
|
28
|
-
# returns:
|
|
29
|
-
# <ask xmlns="urn:xmpp:tropo:ask:1" timeout="30" recognizer="es-es">
|
|
30
|
-
# <prompt voice='simon'>Please enter your postal code.</prompt>
|
|
31
|
-
# <choices content-type="application/grammar+voxeo">[5 DIGITS]</choices>
|
|
32
|
-
# </ask>
|
|
33
|
-
#
|
|
34
|
-
def self.new(options = {})
|
|
35
|
-
super().tap do |new_node|
|
|
36
|
-
options.each_pair { |k,v| new_node.send :"#{k}=", v }
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
##
|
|
41
|
-
# @return [Boolean] wether or not to allow the caller to begin their response before the prompt finishes
|
|
42
|
-
#
|
|
43
|
-
def bargein
|
|
44
|
-
read_attr(:bargein) == "true"
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
##
|
|
48
|
-
# @param [Boolean] bargein wether or not to allow the caller to begin their response before the prompt finishes
|
|
49
|
-
#
|
|
50
|
-
def bargein=(bargein)
|
|
51
|
-
write_attr :bargein, bargein.to_s
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
##
|
|
55
|
-
# @return [Float] Confidence with which to consider a response acceptable
|
|
56
|
-
#
|
|
57
|
-
def min_confidence
|
|
58
|
-
read_attr 'min-confidence', :to_f
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
##
|
|
62
|
-
# @param [Float] min_confidence with which to consider a response acceptable
|
|
63
|
-
#
|
|
64
|
-
def min_confidence=(min_confidence)
|
|
65
|
-
write_attr 'min-confidence', min_confidence
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
##
|
|
69
|
-
# @return [Symbol] mode by which to accept input. Can be :speech, :dtmf or :any
|
|
70
|
-
#
|
|
71
|
-
def mode
|
|
72
|
-
read_attr :mode, :to_sym
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
##
|
|
76
|
-
# @param [Symbol] mode by which to accept input. Can be :speech, :dtmf or :any
|
|
77
|
-
#
|
|
78
|
-
def mode=(mode)
|
|
79
|
-
write_attr :mode, mode
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
##
|
|
83
|
-
# @return [String] recognizer to use for speech recognition
|
|
84
|
-
#
|
|
85
|
-
def recognizer
|
|
86
|
-
read_attr :recognizer
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
##
|
|
90
|
-
# @param [String] recognizer to use for speech recognition
|
|
91
|
-
#
|
|
92
|
-
def recognizer=(recognizer)
|
|
93
|
-
write_attr :recognizer, recognizer
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
##
|
|
97
|
-
# @return [String] terminator by which to signal the end of input
|
|
98
|
-
#
|
|
99
|
-
def terminator
|
|
100
|
-
read_attr :terminator
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
##
|
|
104
|
-
# @param [String] terminator by which to signal the end of input
|
|
105
|
-
#
|
|
106
|
-
def terminator=(terminator)
|
|
107
|
-
write_attr :terminator, terminator
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
##
|
|
111
|
-
# @return [Integer] timeout to wait for user input
|
|
112
|
-
#
|
|
113
|
-
def timeout
|
|
114
|
-
read_attr :timeout, :to_i
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
##
|
|
118
|
-
# @param [Integer] timeout to wait for user input
|
|
119
|
-
#
|
|
120
|
-
def timeout=(timeout)
|
|
121
|
-
write_attr :timeout, timeout
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
##
|
|
125
|
-
# @return [Prompt] the prompt by which to introduce the question
|
|
126
|
-
#
|
|
127
|
-
def prompt
|
|
128
|
-
Prompt.new find_first('//ns:prompt', :ns => self.registered_ns)
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
##
|
|
132
|
-
# @param [Hash] p
|
|
133
|
-
# @option p [String] :text to read the caller via TTS as the question
|
|
134
|
-
# @option p [String] :voice to use for speech synthesis
|
|
135
|
-
#
|
|
136
|
-
def prompt=(p)
|
|
137
|
-
remove_children :prompt
|
|
138
|
-
p = Prompt.new(p) unless p.is_a?(Prompt)
|
|
139
|
-
self << p
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
class Prompt < MediaNode
|
|
143
|
-
register :prompt, :ask
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
##
|
|
147
|
-
# @return [Choices] the choices available
|
|
148
|
-
#
|
|
149
|
-
def choices
|
|
150
|
-
node = find_first 'ns:choices', :ns => self.class.registered_ns
|
|
151
|
-
Choices.new node if node
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
##
|
|
155
|
-
# @param [Hash] choices
|
|
156
|
-
# @option choices [String] :content_type
|
|
157
|
-
# @option choices [String] :value the choices available
|
|
158
|
-
#
|
|
159
|
-
def choices=(choices)
|
|
160
|
-
return unless choices
|
|
161
|
-
remove_children :choices
|
|
162
|
-
choices = Choices.new(choices) unless choices.is_a?(Choices)
|
|
163
|
-
self << choices
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
def inspect_attributes # :nodoc:
|
|
167
|
-
[:bargein, :min_confidence, :mode, :recognizer, :terminator, :timeout, :prompt, :choices] + super
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
class Choices < Input::Grammar
|
|
171
|
-
##
|
|
172
|
-
# @param [Hash] options
|
|
173
|
-
# @option options [String] :content_type
|
|
174
|
-
# @option options [String] :value the choices available
|
|
175
|
-
#
|
|
176
|
-
def self.new(options = {})
|
|
177
|
-
super(options).tap do |new_node|
|
|
178
|
-
new_node.name = 'choices'
|
|
179
|
-
end
|
|
180
|
-
end
|
|
181
|
-
end # Choices
|
|
182
|
-
|
|
183
|
-
class Complete
|
|
184
|
-
class Success < Input::Complete::Success
|
|
185
|
-
register :success, :ask_complete
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
class NoMatch < Input::Complete::NoMatch
|
|
189
|
-
register :nomatch, :ask_complete
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
class NoInput < Input::Complete::NoInput
|
|
193
|
-
register :noinput, :ask_complete
|
|
194
|
-
end
|
|
195
|
-
end # Complete
|
|
196
|
-
end # Ask
|
|
197
|
-
end # Tropo
|
|
198
|
-
end # Command
|
|
199
|
-
end # Punchblock
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
module Punchblock
|
|
2
|
-
module Component
|
|
3
|
-
module Tropo
|
|
4
|
-
class Say < ComponentNode
|
|
5
|
-
register :say, :say
|
|
6
|
-
|
|
7
|
-
include MediaContainer
|
|
8
|
-
|
|
9
|
-
##
|
|
10
|
-
# Creates an Rayo Say command
|
|
11
|
-
#
|
|
12
|
-
# @param [Hash] options
|
|
13
|
-
# @option options [String, Optional] :text to speak back
|
|
14
|
-
# @option options [String, Optional] :voice with which to render TTS
|
|
15
|
-
# @option options [String, Optional] :ssml document to render TTS
|
|
16
|
-
#
|
|
17
|
-
# @return [Command::Say] an Rayo "say" command
|
|
18
|
-
#
|
|
19
|
-
# @example
|
|
20
|
-
# say :text => 'Hello brown cow.'
|
|
21
|
-
#
|
|
22
|
-
# returns:
|
|
23
|
-
# <say xmlns="urn:xmpp:tropo:say:1">Hello brown cow.</say>
|
|
24
|
-
#
|
|
25
|
-
def self.new(options = {})
|
|
26
|
-
super().tap do |new_node|
|
|
27
|
-
case options
|
|
28
|
-
when Hash
|
|
29
|
-
new_node.voice = options.delete(:voice) if options[:voice]
|
|
30
|
-
new_node.ssml = options.delete(:ssml) if options[:ssml]
|
|
31
|
-
new_node << options.delete(:text) if options[:text]
|
|
32
|
-
when Nokogiri::XML::Element
|
|
33
|
-
new_node.inherit options
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
state_machine :state do
|
|
39
|
-
event :paused do
|
|
40
|
-
transition :executing => :paused
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
event :resumed do
|
|
44
|
-
transition :paused => :executing
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
# Pauses a running Say
|
|
49
|
-
#
|
|
50
|
-
# @return [Command::Say::Pause] an Rayo pause message for the current Say
|
|
51
|
-
#
|
|
52
|
-
# @example
|
|
53
|
-
# say_obj.pause_action.to_xml
|
|
54
|
-
#
|
|
55
|
-
# returns:
|
|
56
|
-
# <pause xmlns="urn:xmpp:tropo:say:1"/>
|
|
57
|
-
def pause_action
|
|
58
|
-
Pause.new :component_id => component_id, :call_id => call_id
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
##
|
|
62
|
-
# Sends an Rayo pause message for the current Say
|
|
63
|
-
#
|
|
64
|
-
def pause!
|
|
65
|
-
raise InvalidActionError, "Cannot pause a Say that is not executing" unless executing?
|
|
66
|
-
pause_action.tap do |action|
|
|
67
|
-
result = write_action action
|
|
68
|
-
paused! if result
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
##
|
|
73
|
-
# Create an Rayo resume message for the current Say
|
|
74
|
-
#
|
|
75
|
-
# @return [Command::Say::Resume] an Rayo resume message
|
|
76
|
-
#
|
|
77
|
-
# @example
|
|
78
|
-
# say_obj.resume_action.to_xml
|
|
79
|
-
#
|
|
80
|
-
# returns:
|
|
81
|
-
# <resume xmlns="urn:xmpp:tropo:say:1"/>
|
|
82
|
-
def resume_action
|
|
83
|
-
Resume.new :component_id => component_id, :call_id => call_id
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
##
|
|
87
|
-
# Sends an Rayo resume message for the current Say
|
|
88
|
-
#
|
|
89
|
-
def resume!
|
|
90
|
-
raise InvalidActionError, "Cannot resume a Say that is not paused." unless paused?
|
|
91
|
-
resume_action.tap do |action|
|
|
92
|
-
result = write_action action
|
|
93
|
-
resumed! if result
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
class Pause < CommandNode # :nodoc:
|
|
98
|
-
register :pause, :say
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
class Resume < CommandNode # :nodoc:
|
|
102
|
-
register :resume, :say
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
class Complete
|
|
106
|
-
class Success < Event::Complete::Reason
|
|
107
|
-
register :success, :say_complete
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
end # Say
|
|
111
|
-
end # Tropo
|
|
112
|
-
end # Command
|
|
113
|
-
end # Punchblock
|
|
@@ -1,178 +0,0 @@
|
|
|
1
|
-
module Punchblock
|
|
2
|
-
module Component
|
|
3
|
-
module Tropo
|
|
4
|
-
class Transfer < ComponentNode
|
|
5
|
-
register :transfer, :transfer
|
|
6
|
-
|
|
7
|
-
include HasHeaders
|
|
8
|
-
|
|
9
|
-
##
|
|
10
|
-
# Creates an Rayo transfer command
|
|
11
|
-
#
|
|
12
|
-
# @param [Hash] options for transferring a call
|
|
13
|
-
# @option options [String, Array[String]] :to The destination(s) for the call transfer (ie - tel:+14155551212 or sip:you@sip.tropo.com). Can be an array to hunt.
|
|
14
|
-
# @option options [String] :from The caller ID for the call transfer (ie - tel:+14155551212 or sip:you@sip.tropo.com)
|
|
15
|
-
# @option options [String, Optional] :terminator The string key press required to abort the transfer.
|
|
16
|
-
# @option options [Integer, Optional] :timeout How long to wait - in seconds - for an answer, busy signal, or other event to occur.
|
|
17
|
-
# @option options [Boolean, Optional] :answer_on_media If set to true, the call will be considered "answered" and audio will begin playing as soon as media is received from the far end (ringing / busy signal / etc)
|
|
18
|
-
# @option options [Symbol, Optional] :media Rules for handling media. Can be :direct, where parties negotiate media directly, or :bridge where the media server will bridge audio, allowing media features like recording and ASR.
|
|
19
|
-
# @option options [Ring, Hash, Optional] :ring to play to the caller untill connected
|
|
20
|
-
#
|
|
21
|
-
# @return [Message::Transfer] an Rayo "transfer" message
|
|
22
|
-
#
|
|
23
|
-
# @example
|
|
24
|
-
# Transfer.new(:to => 'sip:you@yourdomain.com', :from => 'sip:myapp@mydomain.com', :terminator => '#').to_xml
|
|
25
|
-
#
|
|
26
|
-
# returns:
|
|
27
|
-
# <transfer xmlns="urn:xmpp:tropo:transfer:1" from="sip:myapp@mydomain.com" terminator="#">
|
|
28
|
-
# <to>sip:you@yourdomain.com</to>
|
|
29
|
-
# </transfer>
|
|
30
|
-
#
|
|
31
|
-
def self.new(options = {})
|
|
32
|
-
super().tap do |new_node|
|
|
33
|
-
options.each_pair { |k,v| new_node.send :"#{k}=", v }
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
##
|
|
38
|
-
# @return [Array[String]] The destination(s) for the call transfer
|
|
39
|
-
#
|
|
40
|
-
def to
|
|
41
|
-
find('ns:to', :ns => self.class.registered_ns).map &:text
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
##
|
|
45
|
-
# @param [String, Array[String]] :to The destination(s) for the call transfer (ie - tel:+14155551212 or sip:you@sip.tropo.com). Can be an array to hunt.
|
|
46
|
-
#
|
|
47
|
-
def to=(transfer_to)
|
|
48
|
-
find('//ns:to', :ns => self.class.registered_ns).each &:remove
|
|
49
|
-
if transfer_to
|
|
50
|
-
[transfer_to].flatten.each do |i|
|
|
51
|
-
to = RayoNode.new :to
|
|
52
|
-
to << i
|
|
53
|
-
self << to
|
|
54
|
-
end
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
##
|
|
59
|
-
# @return [String] The caller ID for the call transfer
|
|
60
|
-
#
|
|
61
|
-
def from
|
|
62
|
-
read_attr :from
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
##
|
|
66
|
-
# @param [String, Array[String]] :to The destination(s) for the call transfer (ie - tel:+14155551212 or sip:you@sip.tropo.com). Can be an array to hunt.
|
|
67
|
-
#
|
|
68
|
-
def from=(transfer_from)
|
|
69
|
-
write_attr :from, transfer_from
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
##
|
|
73
|
-
# @return [String] The string key press required to abort the transfer.
|
|
74
|
-
#
|
|
75
|
-
def terminator
|
|
76
|
-
read_attr :terminator
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
##
|
|
80
|
-
# @param [String] terminator The string key press required to abort the transfer.
|
|
81
|
-
#
|
|
82
|
-
def terminator=(terminator)
|
|
83
|
-
write_attr :terminator, terminator
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
##
|
|
87
|
-
# @return [Integer] How long to wait - in seconds - for an answer, busy signal, or other event to occur.
|
|
88
|
-
#
|
|
89
|
-
def timeout
|
|
90
|
-
read_attr :timeout, :to_i
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
##
|
|
94
|
-
# @param [Integer] timeout How long to wait - in seconds - for an answer, busy signal, or other event to occur.
|
|
95
|
-
#
|
|
96
|
-
def timeout=(timeout)
|
|
97
|
-
write_attr :timeout, timeout
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
##
|
|
101
|
-
# @return [Boolean] If true, the call will be considered "answered" and audio will begin playing as soon as media is received from the far end (ringing / busy signal / etc)
|
|
102
|
-
#
|
|
103
|
-
def answer_on_media
|
|
104
|
-
read_attr('answer-on-media') == 'true'
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
##
|
|
108
|
-
# @param [Boolean] aom If set to true, the call will be considered "answered" and audio will begin playing as soon as media is received from the far end (ringing / busy signal / etc)
|
|
109
|
-
#
|
|
110
|
-
def answer_on_media=(aom)
|
|
111
|
-
write_attr 'answer-on-media', aom.to_s
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
##
|
|
115
|
-
# @return [Symbol] Rules for handling media. Can be :direct, where parties negotiate media directly, or :bridge where the media server will bridge audio, allowing media features like recording and ASR.
|
|
116
|
-
#
|
|
117
|
-
def media
|
|
118
|
-
read_attr 'media', :to_sym
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
##
|
|
122
|
-
# @param [Symbol] media Rules for handling media. Can be :direct, where parties negotiate media directly, or :bridge where the media server will bridge audio, allowing media features like recording and ASR.
|
|
123
|
-
#
|
|
124
|
-
def media=(media)
|
|
125
|
-
write_attr 'media', media
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
##
|
|
129
|
-
# @return [Ring] the ringer to play to the caller while transferring
|
|
130
|
-
#
|
|
131
|
-
def ring
|
|
132
|
-
node = find_first '//ns:ring', :ns => self.registered_ns
|
|
133
|
-
Ring.new node if node
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
##
|
|
137
|
-
# @param [Hash] ring
|
|
138
|
-
# @option ring [String] :text Text to speak to the caller as an announcement
|
|
139
|
-
# @option ring [String] :url URL to play to the caller as an announcement
|
|
140
|
-
#
|
|
141
|
-
def ring=(ring)
|
|
142
|
-
ring = Ring.new(ring) unless ring.is_a?(Ring)
|
|
143
|
-
self << ring
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
class Ring < Say
|
|
147
|
-
register :ring, :transfer
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def inspect_attributes # :nodoc:
|
|
151
|
-
[:to, :from, :terminator, :timeout, :answer_on_media] + super
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
class Complete
|
|
155
|
-
class Success < Event::Complete::Reason
|
|
156
|
-
register :success, :transfer_complete
|
|
157
|
-
end
|
|
158
|
-
|
|
159
|
-
class Timeout < Event::Complete::Reason
|
|
160
|
-
register :timeout, :transfer_complete
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
class Terminator < Event::Complete::Reason
|
|
164
|
-
register :terminator, :transfer_complete
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
class Busy < Event::Complete::Reason
|
|
168
|
-
register :busy, :transfer_complete
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
class Reject < Event::Complete::Reason
|
|
172
|
-
register :reject, :transfer_complete
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
end # Transfer
|
|
176
|
-
end # Tropo
|
|
177
|
-
end # Command
|
|
178
|
-
end # Punchblock
|