punchblock 0.8.1 → 0.8.2
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 +4 -0
- data/lib/punchblock/component/asterisk/ami/action.rb +1 -1
- data/lib/punchblock/translator/asterisk.rb +24 -6
- data/lib/punchblock/translator/asterisk/call.rb +60 -3
- data/lib/punchblock/version.rb +1 -1
- data/spec/punchblock/component/asterisk/ami/action_spec.rb +20 -0
- data/spec/punchblock/translator/asterisk/call_spec.rb +203 -7
- data/spec/punchblock/translator/asterisk_spec.rb +86 -1
- data/spec/spec_helper.rb +1 -0
- metadata +41 -41
data/CHANGELOG.md
CHANGED
|
@@ -30,7 +30,9 @@ module Punchblock
|
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def call_for_channel(channel)
|
|
33
|
-
call_with_id @channel_to_call_id[channel]
|
|
33
|
+
call = call_with_id @channel_to_call_id[channel]
|
|
34
|
+
pb_logger.trace "Looking up call for channel #{channel} from #{@channel_to_call_id}. Found: #{call || 'none'}"
|
|
35
|
+
call
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
def register_component(component)
|
|
@@ -53,13 +55,22 @@ module Punchblock
|
|
|
53
55
|
end
|
|
54
56
|
return
|
|
55
57
|
end
|
|
56
|
-
|
|
57
|
-
|
|
58
|
+
|
|
59
|
+
if event.name == 'VarSet' && event['Variable'] == 'punchblock_call_id' && (call = call_with_id event['Value'])
|
|
60
|
+
pb_logger.trace "Received a VarSet event indicating the full channel for call #{call}"
|
|
61
|
+
@channel_to_call_id.delete call.channel
|
|
62
|
+
pb_logger.trace "Removed call with old channel from channel map: #{@channel_to_call_id}"
|
|
63
|
+
call.channel = event['Channel']
|
|
64
|
+
register_call call
|
|
58
65
|
end
|
|
66
|
+
|
|
59
67
|
if call = call_for_channel(event['Channel'])
|
|
60
68
|
pb_logger.trace "Found call by channel matching this event. Sending to call #{call.id}"
|
|
61
69
|
call.process_ami_event! event
|
|
70
|
+
elsif event.name.downcase == "asyncagi" && event['SubEvent'] == "Start"
|
|
71
|
+
handle_async_agi_start_event event
|
|
62
72
|
end
|
|
73
|
+
|
|
63
74
|
handle_pb_event Event::Asterisk::AMI::Event.new(:name => event.name, :attributes => event.headers)
|
|
64
75
|
end
|
|
65
76
|
|
|
@@ -92,9 +103,16 @@ module Punchblock
|
|
|
92
103
|
end
|
|
93
104
|
|
|
94
105
|
def execute_global_command(command)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
106
|
+
case command
|
|
107
|
+
when Punchblock::Component::Asterisk::AMI::Action
|
|
108
|
+
component = Component::Asterisk::AMIAction.new command, current_actor
|
|
109
|
+
register_component component
|
|
110
|
+
component.execute!
|
|
111
|
+
when Punchblock::Command::Dial
|
|
112
|
+
call = Call.new command.to, current_actor
|
|
113
|
+
register_call call
|
|
114
|
+
call.dial! command
|
|
115
|
+
end
|
|
98
116
|
end
|
|
99
117
|
|
|
100
118
|
def send_ami_action(name, headers = {}, &block)
|
|
@@ -7,7 +7,16 @@ module Punchblock
|
|
|
7
7
|
include HasGuardedHandlers
|
|
8
8
|
include Celluloid
|
|
9
9
|
|
|
10
|
-
attr_reader :id, :channel, :translator, :agi_env
|
|
10
|
+
attr_reader :id, :channel, :translator, :agi_env, :direction
|
|
11
|
+
|
|
12
|
+
HANGUP_CAUSE_TO_END_REASON = Hash.new { :error }
|
|
13
|
+
HANGUP_CAUSE_TO_END_REASON[16] = :hangup
|
|
14
|
+
HANGUP_CAUSE_TO_END_REASON[17] = :busy
|
|
15
|
+
HANGUP_CAUSE_TO_END_REASON[18] = :timeout
|
|
16
|
+
HANGUP_CAUSE_TO_END_REASON[19] = :reject
|
|
17
|
+
HANGUP_CAUSE_TO_END_REASON[21] = :reject
|
|
18
|
+
HANGUP_CAUSE_TO_END_REASON[22] = :reject
|
|
19
|
+
HANGUP_CAUSE_TO_END_REASON[102] = :timeout
|
|
11
20
|
|
|
12
21
|
def initialize(channel, translator, agi_env = '')
|
|
13
22
|
@channel, @translator = channel, translator
|
|
@@ -25,15 +34,49 @@ module Punchblock
|
|
|
25
34
|
end
|
|
26
35
|
|
|
27
36
|
def send_offer
|
|
37
|
+
@direction = :inbound
|
|
28
38
|
send_pb_event offer_event
|
|
29
39
|
end
|
|
30
40
|
|
|
41
|
+
def to_s
|
|
42
|
+
"#<#{self.class}:#{id} Channel: #{channel.inspect}>"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def dial(dial_command)
|
|
46
|
+
@direction = :outbound
|
|
47
|
+
originate_action = Punchblock::Component::Asterisk::AMI::Action.new :name => 'Originate',
|
|
48
|
+
:params => {
|
|
49
|
+
:async => true,
|
|
50
|
+
:application => 'AGI',
|
|
51
|
+
:data => 'agi:async',
|
|
52
|
+
:channel => dial_command.to,
|
|
53
|
+
:callerid => dial_command.from,
|
|
54
|
+
:variable => "punchblock_call_id=#{id}"
|
|
55
|
+
}
|
|
56
|
+
originate_action.request!
|
|
57
|
+
translator.execute_global_command! originate_action
|
|
58
|
+
dial_command.response = Ref.new :id => id
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def outbound?
|
|
62
|
+
direction == :outbound
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def inbound?
|
|
66
|
+
direction == :inbound
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def channel=(other)
|
|
70
|
+
pb_logger.info "Channel is changing from #{channel} to #{other}."
|
|
71
|
+
@channel = other
|
|
72
|
+
end
|
|
73
|
+
|
|
31
74
|
def process_ami_event(ami_event)
|
|
32
75
|
pb_logger.trace "Processing AMI event #{ami_event.inspect}"
|
|
33
76
|
case ami_event.name
|
|
34
77
|
when 'Hangup'
|
|
35
78
|
pb_logger.debug "Received a Hangup AMI event. Sending End event."
|
|
36
|
-
send_pb_event Event::End.new(:reason =>
|
|
79
|
+
send_pb_event Event::End.new(:reason => HANGUP_CAUSE_TO_END_REASON[ami_event['Cause'].to_i])
|
|
37
80
|
when 'AsyncAGI'
|
|
38
81
|
pb_logger.debug "Received an AsyncAGI event. Looking for matching AGICommand component."
|
|
39
82
|
if component = component_with_id(ami_event['CommandID'])
|
|
@@ -42,6 +85,14 @@ module Punchblock
|
|
|
42
85
|
else
|
|
43
86
|
pb_logger.debug "Could not find component for AMI event: #{ami_event}"
|
|
44
87
|
end
|
|
88
|
+
when 'Newstate'
|
|
89
|
+
pb_logger.debug "Received a Newstate AMI event with state #{ami_event['ChannelState']}: #{ami_event['ChannelStateDesc']}"
|
|
90
|
+
case ami_event['ChannelState']
|
|
91
|
+
when '5'
|
|
92
|
+
send_pb_event Event::Ringing.new
|
|
93
|
+
when '6'
|
|
94
|
+
send_pb_event Event::Answered.new
|
|
95
|
+
end
|
|
45
96
|
end
|
|
46
97
|
trigger_handler :ami, ami_event
|
|
47
98
|
end
|
|
@@ -53,8 +104,14 @@ module Punchblock
|
|
|
53
104
|
end
|
|
54
105
|
case command
|
|
55
106
|
when Command::Accept
|
|
56
|
-
|
|
107
|
+
if outbound?
|
|
108
|
+
pb_logger.trace "Attempting to accept an outbound call. Skipping RINGING."
|
|
57
109
|
command.response = true
|
|
110
|
+
else
|
|
111
|
+
pb_logger.trace "Attempting to accept an inbound call. Executing RINGING."
|
|
112
|
+
send_agi_action 'EXEC RINGING' do |response|
|
|
113
|
+
command.response = true
|
|
114
|
+
end
|
|
58
115
|
end
|
|
59
116
|
when Command::Answer
|
|
60
117
|
send_agi_action 'EXEC ANSWER' do |response|
|
data/lib/punchblock/version.rb
CHANGED
|
@@ -52,6 +52,26 @@ module Punchblock
|
|
|
52
52
|
:async => '1'} }
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
+
describe "testing equality" do
|
|
56
|
+
context "with the same name and params" do
|
|
57
|
+
it "should be equal" do
|
|
58
|
+
Action.new(:name => 'Originate', :params => { :channel => 'SIP/101test' }).should == Action.new(:name => 'Originate', :params => { :channel => 'SIP/101test' })
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context "with the same name and different params" do
|
|
63
|
+
it "should be equal" do
|
|
64
|
+
Action.new(:name => 'Originate', :params => { :channel => 'SIP/101' }).should_not == Action.new(:name => 'Originate', :params => { :channel => 'SIP/101test' })
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
context "with a different name and the same params" do
|
|
69
|
+
it "should be equal" do
|
|
70
|
+
Action.new(:name => 'Hangup', :params => { :channel => 'SIP/101test' }).should_not == Action.new(:name => 'Originate', :params => { :channel => 'SIP/101test' })
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
55
75
|
describe "when setting options in initializer" do
|
|
56
76
|
subject do
|
|
57
77
|
Action.new :name => 'Originate',
|
|
@@ -82,6 +82,58 @@ module Punchblock
|
|
|
82
82
|
translator.expects(:handle_pb_event!).with expected_offer
|
|
83
83
|
subject.send_offer
|
|
84
84
|
end
|
|
85
|
+
|
|
86
|
+
it 'should make the call identify as inbound' do
|
|
87
|
+
subject.send_offer
|
|
88
|
+
subject.direction.should == :inbound
|
|
89
|
+
subject.inbound?.should be true
|
|
90
|
+
subject.outbound?.should be false
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
describe '#dial' do
|
|
95
|
+
let :dial_command do
|
|
96
|
+
Punchblock::Command::Dial.new :to => 'SIP/1234', :from => 'sip:foo@bar.com'
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
before { dial_command.request! }
|
|
100
|
+
|
|
101
|
+
it 'sends an Originate AMI action' do
|
|
102
|
+
expected_action = Punchblock::Component::Asterisk::AMI::Action.new :name => 'Originate',
|
|
103
|
+
:params => {
|
|
104
|
+
:async => true,
|
|
105
|
+
:application => 'AGI',
|
|
106
|
+
:data => 'agi:async',
|
|
107
|
+
:channel => 'SIP/1234',
|
|
108
|
+
:callerid => 'sip:foo@bar.com',
|
|
109
|
+
:variable => "punchblock_call_id=#{subject.id}"
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
translator.expects(:execute_global_command!).once.with expected_action
|
|
113
|
+
subject.dial dial_command
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
it 'sends the call ID as a response to the Dial' do
|
|
117
|
+
subject.dial dial_command
|
|
118
|
+
dial_command.response
|
|
119
|
+
dial_command.call_id.should == subject.id
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'should make the call identify as outbound' do
|
|
123
|
+
subject.dial dial_command
|
|
124
|
+
subject.direction.should == :outbound
|
|
125
|
+
subject.outbound?.should be true
|
|
126
|
+
subject.inbound?.should be false
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it 'causes accepting the call to be a null operation' do
|
|
130
|
+
subject.dial dial_command
|
|
131
|
+
accept_command = Command::Accept.new
|
|
132
|
+
accept_command.request!
|
|
133
|
+
subject.wrapped_object.expects(:send_agi_action).never
|
|
134
|
+
subject.execute_command accept_command
|
|
135
|
+
accept_command.response(0.5).should be true
|
|
136
|
+
end
|
|
85
137
|
end
|
|
86
138
|
|
|
87
139
|
describe '#process_ami_event' do
|
|
@@ -91,17 +143,121 @@ module Punchblock
|
|
|
91
143
|
e['Uniqueid'] = "1320842458.8"
|
|
92
144
|
e['Calleridnum'] = "5678"
|
|
93
145
|
e['Calleridname'] = "Jane Smith"
|
|
94
|
-
e['Cause'] =
|
|
95
|
-
e['Cause-txt'] =
|
|
146
|
+
e['Cause'] = cause
|
|
147
|
+
e['Cause-txt'] = cause_txt
|
|
96
148
|
e['Channel'] = "SIP/1234-00000000"
|
|
97
149
|
end
|
|
98
150
|
end
|
|
99
151
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
152
|
+
context "with a normal clearing cause" do
|
|
153
|
+
let(:cause) { '16' }
|
|
154
|
+
let(:cause_txt) { 'Normal Clearing' }
|
|
155
|
+
|
|
156
|
+
it 'should send an end (hangup) event to the translator' do
|
|
157
|
+
expected_end_event = Punchblock::Event::End.new :reason => :hangup,
|
|
158
|
+
:call_id => subject.id
|
|
159
|
+
translator.expects(:handle_pb_event!).with expected_end_event
|
|
160
|
+
subject.process_ami_event ami_event
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
context "with a user busy cause" do
|
|
165
|
+
let(:cause) { '17' }
|
|
166
|
+
let(:cause_txt) { 'User Busy' }
|
|
167
|
+
|
|
168
|
+
it 'should send an end (busy) event to the translator' do
|
|
169
|
+
expected_end_event = Punchblock::Event::End.new :reason => :busy,
|
|
170
|
+
:call_id => subject.id
|
|
171
|
+
translator.expects(:handle_pb_event!).with expected_end_event
|
|
172
|
+
subject.process_ami_event ami_event
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
{
|
|
177
|
+
18 => 'No user response',
|
|
178
|
+
102 => 'Recovery on timer expire'
|
|
179
|
+
}.each_pair do |cause, cause_txt|
|
|
180
|
+
context "with a #{cause_txt} cause" do
|
|
181
|
+
let(:cause) { cause.to_s }
|
|
182
|
+
let(:cause_txt) { cause_txt }
|
|
183
|
+
|
|
184
|
+
it 'should send an end (timeout) event to the translator' do
|
|
185
|
+
expected_end_event = Punchblock::Event::End.new :reason => :timeout,
|
|
186
|
+
:call_id => subject.id
|
|
187
|
+
translator.expects(:handle_pb_event!).with expected_end_event
|
|
188
|
+
subject.process_ami_event ami_event
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
{
|
|
194
|
+
19 => 'No Answer',
|
|
195
|
+
21 => 'Call Rejected',
|
|
196
|
+
22 => 'Number Changed'
|
|
197
|
+
}.each_pair do |cause, cause_txt|
|
|
198
|
+
context "with a #{cause_txt} cause" do
|
|
199
|
+
let(:cause) { cause.to_s }
|
|
200
|
+
let(:cause_txt) { cause_txt }
|
|
201
|
+
|
|
202
|
+
it 'should send an end (reject) event to the translator' do
|
|
203
|
+
expected_end_event = Punchblock::Event::End.new :reason => :reject,
|
|
204
|
+
:call_id => subject.id
|
|
205
|
+
translator.expects(:handle_pb_event!).with expected_end_event
|
|
206
|
+
subject.process_ami_event ami_event
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
{
|
|
212
|
+
1 => 'AST_CAUSE_UNALLOCATED',
|
|
213
|
+
2 => 'NO_ROUTE_TRANSIT_NET',
|
|
214
|
+
3 => 'NO_ROUTE_DESTINATION',
|
|
215
|
+
6 => 'CHANNEL_UNACCEPTABLE',
|
|
216
|
+
7 => 'CALL_AWARDED_DELIVERED',
|
|
217
|
+
27 => 'DESTINATION_OUT_OF_ORDER',
|
|
218
|
+
28 => 'INVALID_NUMBER_FORMAT',
|
|
219
|
+
29 => 'FACILITY_REJECTED',
|
|
220
|
+
30 => 'RESPONSE_TO_STATUS_ENQUIRY',
|
|
221
|
+
31 => 'NORMAL_UNSPECIFIED',
|
|
222
|
+
34 => 'NORMAL_CIRCUIT_CONGESTION',
|
|
223
|
+
38 => 'NETWORK_OUT_OF_ORDER',
|
|
224
|
+
41 => 'NORMAL_TEMPORARY_FAILURE',
|
|
225
|
+
42 => 'SWITCH_CONGESTION',
|
|
226
|
+
43 => 'ACCESS_INFO_DISCARDED',
|
|
227
|
+
44 => 'REQUESTED_CHAN_UNAVAIL',
|
|
228
|
+
45 => 'PRE_EMPTED',
|
|
229
|
+
50 => 'FACILITY_NOT_SUBSCRIBED',
|
|
230
|
+
52 => 'OUTGOING_CALL_BARRED',
|
|
231
|
+
54 => 'INCOMING_CALL_BARRED',
|
|
232
|
+
57 => 'BEARERCAPABILITY_NOTAUTH',
|
|
233
|
+
58 => 'BEARERCAPABILITY_NOTAVAIL',
|
|
234
|
+
65 => 'BEARERCAPABILITY_NOTIMPL',
|
|
235
|
+
66 => 'CHAN_NOT_IMPLEMENTED',
|
|
236
|
+
69 => 'FACILITY_NOT_IMPLEMENTED',
|
|
237
|
+
81 => 'INVALID_CALL_REFERENCE',
|
|
238
|
+
88 => 'INCOMPATIBLE_DESTINATION',
|
|
239
|
+
95 => 'INVALID_MSG_UNSPECIFIED',
|
|
240
|
+
96 => 'MANDATORY_IE_MISSING',
|
|
241
|
+
97 => 'MESSAGE_TYPE_NONEXIST',
|
|
242
|
+
98 => 'WRONG_MESSAGE',
|
|
243
|
+
99 => 'IE_NONEXIST',
|
|
244
|
+
100 => 'INVALID_IE_CONTENTS',
|
|
245
|
+
101 => 'WRONG_CALL_STATE',
|
|
246
|
+
103 => 'MANDATORY_IE_LENGTH_ERROR',
|
|
247
|
+
111 => 'PROTOCOL_ERROR',
|
|
248
|
+
127 => 'INTERWORKING'
|
|
249
|
+
}.each_pair do |cause, cause_txt|
|
|
250
|
+
context "with a #{cause_txt} cause" do
|
|
251
|
+
let(:cause) { cause.to_s }
|
|
252
|
+
let(:cause_txt) { cause_txt }
|
|
253
|
+
|
|
254
|
+
it 'should send an end (error) event to the translator' do
|
|
255
|
+
expected_end_event = Punchblock::Event::End.new :reason => :error,
|
|
256
|
+
:call_id => subject.id
|
|
257
|
+
translator.expects(:handle_pb_event!).with expected_end_event
|
|
258
|
+
subject.process_ami_event ami_event
|
|
259
|
+
end
|
|
260
|
+
end
|
|
105
261
|
end
|
|
106
262
|
end
|
|
107
263
|
|
|
@@ -131,6 +287,46 @@ module Punchblock
|
|
|
131
287
|
end
|
|
132
288
|
end
|
|
133
289
|
|
|
290
|
+
context 'with a Newstate event' do
|
|
291
|
+
let :ami_event do
|
|
292
|
+
RubyAMI::Event.new('Newstate').tap do |e|
|
|
293
|
+
e['Privilege'] = 'call,all'
|
|
294
|
+
e['Channel'] = 'SIP/1234-00000000'
|
|
295
|
+
e['ChannelState'] = channel_state
|
|
296
|
+
e['ChannelStateDesc'] = channel_state_desc
|
|
297
|
+
e['CallerIDNum'] = ''
|
|
298
|
+
e['CallerIDName'] = ''
|
|
299
|
+
e['ConnectedLineNum'] = ''
|
|
300
|
+
e['ConnectedLineName'] = ''
|
|
301
|
+
e['Uniqueid'] = '1326194671.0'
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
context 'ringing' do
|
|
306
|
+
let(:channel_state) { '5' }
|
|
307
|
+
let(:channel_state_desc) { 'Ringing' }
|
|
308
|
+
|
|
309
|
+
it 'should send a ringing event' do
|
|
310
|
+
expected_ringing = Punchblock::Event::Ringing.new
|
|
311
|
+
expected_ringing.call_id = subject.id
|
|
312
|
+
translator.expects(:handle_pb_event!).with expected_ringing
|
|
313
|
+
subject.process_ami_event ami_event
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
context 'up' do
|
|
318
|
+
let(:channel_state) { '6' }
|
|
319
|
+
let(:channel_state_desc) { 'Up' }
|
|
320
|
+
|
|
321
|
+
it 'should send a ringing event' do
|
|
322
|
+
expected_answered = Punchblock::Event::Answered.new
|
|
323
|
+
expected_answered.call_id = subject.id
|
|
324
|
+
translator.expects(:handle_pb_event!).with expected_answered
|
|
325
|
+
subject.process_ami_event ami_event
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
|
|
134
330
|
context 'with a handler registered for a matching event' do
|
|
135
331
|
let :ami_event do
|
|
136
332
|
RubyAMI::Event.new('DTMF').tap do |e|
|
|
@@ -126,7 +126,26 @@ module Punchblock
|
|
|
126
126
|
|
|
127
127
|
describe '#execute_global_command' do
|
|
128
128
|
context 'with a Dial' do
|
|
129
|
-
|
|
129
|
+
let :command do
|
|
130
|
+
Command::Dial.new :to => 'SIP/1234', :from => 'abc123'
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
before { command.request! }
|
|
134
|
+
|
|
135
|
+
let(:mock_action) { stub_everything 'Asterisk::Component::Asterisk::AMIAction' }
|
|
136
|
+
|
|
137
|
+
it 'should be able to look up the call by channel ID' do
|
|
138
|
+
subject.execute_global_command command
|
|
139
|
+
call_actor = subject.call_for_channel('SIP/1234')
|
|
140
|
+
call_actor.wrapped_object.should be_a Asterisk::Call
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
it 'should instruct the call to send a dial' do
|
|
144
|
+
mock_call = stub_everything 'Asterisk::Call'
|
|
145
|
+
Asterisk::Call.expects(:new).once.returns mock_call
|
|
146
|
+
mock_call.expects(:dial!).once.with command
|
|
147
|
+
subject.execute_global_command command
|
|
148
|
+
end
|
|
130
149
|
end
|
|
131
150
|
|
|
132
151
|
context 'with an AMI action' do
|
|
@@ -232,6 +251,72 @@ module Punchblock
|
|
|
232
251
|
mock_call.expects(:send_offer!).once
|
|
233
252
|
subject.handle_ami_event ami_event
|
|
234
253
|
end
|
|
254
|
+
|
|
255
|
+
context 'if a call already exists for a matching channel' do
|
|
256
|
+
let(:call) { Asterisk::Call.new "SIP/1234-00000000", subject }
|
|
257
|
+
|
|
258
|
+
before do
|
|
259
|
+
subject.register_call call
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
it "should not create a new call" do
|
|
263
|
+
Asterisk::Call.expects(:new).never
|
|
264
|
+
subject.handle_ami_event ami_event
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
describe 'with a VarSet event including a punchblock_call_id' do
|
|
270
|
+
let :ami_event do
|
|
271
|
+
RubyAMI::Event.new('VarSet').tap do |e|
|
|
272
|
+
e["Privilege"] = "dialplan,all"
|
|
273
|
+
e["Channel"] = "SIP/1234-00000000"
|
|
274
|
+
e["Variable"] = "punchblock_call_id"
|
|
275
|
+
e["Value"] = call_id
|
|
276
|
+
e["Uniqueid"] = "1326210224.0"
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
before do
|
|
281
|
+
ami_client.stub_everything
|
|
282
|
+
subject.wrapped_object.stubs :handle_pb_event
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
context "matching a call that was created by a Dial command" do
|
|
286
|
+
let(:dial_command) { Punchblock::Command::Dial.new :to => 'SIP/1234', :from => 'abc123' }
|
|
287
|
+
|
|
288
|
+
before do
|
|
289
|
+
dial_command.request!
|
|
290
|
+
subject.execute_global_command dial_command
|
|
291
|
+
call
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
let(:call) { subject.call_for_channel 'SIP/1234' }
|
|
295
|
+
let(:call_id) { call.id }
|
|
296
|
+
|
|
297
|
+
it "should set the correct channel on the call" do
|
|
298
|
+
subject.handle_ami_event ami_event
|
|
299
|
+
call.channel.should == 'SIP/1234-00000000'
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
it "should make it possible to look up the call by the full channel name" do
|
|
303
|
+
subject.handle_ami_event ami_event
|
|
304
|
+
subject.call_for_channel("SIP/1234-00000000").should be call
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
it "should make looking up the channel by the requested channel name impossible" do
|
|
308
|
+
subject.handle_ami_event ami_event
|
|
309
|
+
subject.call_for_channel('SIP/1234').should be_nil
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
context "for a call that doesn't exist" do
|
|
314
|
+
let(:call_id) { 'foobarbaz' }
|
|
315
|
+
|
|
316
|
+
it "should not raise" do
|
|
317
|
+
lambda { subject.handle_ami_event ami_event }.should_not raise_error
|
|
318
|
+
end
|
|
319
|
+
end
|
|
235
320
|
end
|
|
236
321
|
|
|
237
322
|
describe 'with an AMI event for a known channel' do
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: punchblock
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.8.
|
|
4
|
+
version: 0.8.2
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -11,11 +11,11 @@ authors:
|
|
|
11
11
|
autorequire:
|
|
12
12
|
bindir: bin
|
|
13
13
|
cert_chain: []
|
|
14
|
-
date: 2012-01-
|
|
14
|
+
date: 2012-01-10 00:00:00.000000000 Z
|
|
15
15
|
dependencies:
|
|
16
16
|
- !ruby/object:Gem::Dependency
|
|
17
17
|
name: niceogiri
|
|
18
|
-
requirement: &
|
|
18
|
+
requirement: &2168470140 !ruby/object:Gem::Requirement
|
|
19
19
|
none: false
|
|
20
20
|
requirements:
|
|
21
21
|
- - ! '>='
|
|
@@ -23,10 +23,10 @@ dependencies:
|
|
|
23
23
|
version: 0.0.4
|
|
24
24
|
type: :runtime
|
|
25
25
|
prerelease: false
|
|
26
|
-
version_requirements: *
|
|
26
|
+
version_requirements: *2168470140
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: blather
|
|
29
|
-
requirement: &
|
|
29
|
+
requirement: &2168468720 !ruby/object:Gem::Requirement
|
|
30
30
|
none: false
|
|
31
31
|
requirements:
|
|
32
32
|
- - ! '>='
|
|
@@ -34,10 +34,10 @@ dependencies:
|
|
|
34
34
|
version: 0.5.12
|
|
35
35
|
type: :runtime
|
|
36
36
|
prerelease: false
|
|
37
|
-
version_requirements: *
|
|
37
|
+
version_requirements: *2168468720
|
|
38
38
|
- !ruby/object:Gem::Dependency
|
|
39
39
|
name: activesupport
|
|
40
|
-
requirement: &
|
|
40
|
+
requirement: &2168467360 !ruby/object:Gem::Requirement
|
|
41
41
|
none: false
|
|
42
42
|
requirements:
|
|
43
43
|
- - ! '>='
|
|
@@ -45,10 +45,10 @@ dependencies:
|
|
|
45
45
|
version: 2.1.0
|
|
46
46
|
type: :runtime
|
|
47
47
|
prerelease: false
|
|
48
|
-
version_requirements: *
|
|
48
|
+
version_requirements: *2168467360
|
|
49
49
|
- !ruby/object:Gem::Dependency
|
|
50
50
|
name: state_machine
|
|
51
|
-
requirement: &
|
|
51
|
+
requirement: &2168466140 !ruby/object:Gem::Requirement
|
|
52
52
|
none: false
|
|
53
53
|
requirements:
|
|
54
54
|
- - ! '>='
|
|
@@ -56,10 +56,10 @@ dependencies:
|
|
|
56
56
|
version: 1.0.1
|
|
57
57
|
type: :runtime
|
|
58
58
|
prerelease: false
|
|
59
|
-
version_requirements: *
|
|
59
|
+
version_requirements: *2168466140
|
|
60
60
|
- !ruby/object:Gem::Dependency
|
|
61
61
|
name: future-resource
|
|
62
|
-
requirement: &
|
|
62
|
+
requirement: &2168464520 !ruby/object:Gem::Requirement
|
|
63
63
|
none: false
|
|
64
64
|
requirements:
|
|
65
65
|
- - ! '>='
|
|
@@ -67,10 +67,10 @@ dependencies:
|
|
|
67
67
|
version: 0.0.2
|
|
68
68
|
type: :runtime
|
|
69
69
|
prerelease: false
|
|
70
|
-
version_requirements: *
|
|
70
|
+
version_requirements: *2168464520
|
|
71
71
|
- !ruby/object:Gem::Dependency
|
|
72
72
|
name: has-guarded-handlers
|
|
73
|
-
requirement: &
|
|
73
|
+
requirement: &2168508400 !ruby/object:Gem::Requirement
|
|
74
74
|
none: false
|
|
75
75
|
requirements:
|
|
76
76
|
- - ! '>='
|
|
@@ -78,10 +78,10 @@ dependencies:
|
|
|
78
78
|
version: 0.1.0
|
|
79
79
|
type: :runtime
|
|
80
80
|
prerelease: false
|
|
81
|
-
version_requirements: *
|
|
81
|
+
version_requirements: *2168508400
|
|
82
82
|
- !ruby/object:Gem::Dependency
|
|
83
83
|
name: celluloid
|
|
84
|
-
requirement: &
|
|
84
|
+
requirement: &2168506560 !ruby/object:Gem::Requirement
|
|
85
85
|
none: false
|
|
86
86
|
requirements:
|
|
87
87
|
- - ! '>='
|
|
@@ -89,10 +89,10 @@ dependencies:
|
|
|
89
89
|
version: 0.6.0
|
|
90
90
|
type: :runtime
|
|
91
91
|
prerelease: false
|
|
92
|
-
version_requirements: *
|
|
92
|
+
version_requirements: *2168506560
|
|
93
93
|
- !ruby/object:Gem::Dependency
|
|
94
94
|
name: ruby_ami
|
|
95
|
-
requirement: &
|
|
95
|
+
requirement: &2168504680 !ruby/object:Gem::Requirement
|
|
96
96
|
none: false
|
|
97
97
|
requirements:
|
|
98
98
|
- - ! '>='
|
|
@@ -100,10 +100,10 @@ dependencies:
|
|
|
100
100
|
version: 0.1.3
|
|
101
101
|
type: :runtime
|
|
102
102
|
prerelease: false
|
|
103
|
-
version_requirements: *
|
|
103
|
+
version_requirements: *2168504680
|
|
104
104
|
- !ruby/object:Gem::Dependency
|
|
105
105
|
name: ruby_speech
|
|
106
|
-
requirement: &
|
|
106
|
+
requirement: &2168503400 !ruby/object:Gem::Requirement
|
|
107
107
|
none: false
|
|
108
108
|
requirements:
|
|
109
109
|
- - ! '>='
|
|
@@ -111,10 +111,10 @@ dependencies:
|
|
|
111
111
|
version: 0.5.1
|
|
112
112
|
type: :runtime
|
|
113
113
|
prerelease: false
|
|
114
|
-
version_requirements: *
|
|
114
|
+
version_requirements: *2168503400
|
|
115
115
|
- !ruby/object:Gem::Dependency
|
|
116
116
|
name: bundler
|
|
117
|
-
requirement: &
|
|
117
|
+
requirement: &2168500560 !ruby/object:Gem::Requirement
|
|
118
118
|
none: false
|
|
119
119
|
requirements:
|
|
120
120
|
- - ~>
|
|
@@ -122,10 +122,10 @@ dependencies:
|
|
|
122
122
|
version: 1.0.0
|
|
123
123
|
type: :development
|
|
124
124
|
prerelease: false
|
|
125
|
-
version_requirements: *
|
|
125
|
+
version_requirements: *2168500560
|
|
126
126
|
- !ruby/object:Gem::Dependency
|
|
127
127
|
name: rspec
|
|
128
|
-
requirement: &
|
|
128
|
+
requirement: &2168497720 !ruby/object:Gem::Requirement
|
|
129
129
|
none: false
|
|
130
130
|
requirements:
|
|
131
131
|
- - ~>
|
|
@@ -133,10 +133,10 @@ dependencies:
|
|
|
133
133
|
version: 2.7.0
|
|
134
134
|
type: :development
|
|
135
135
|
prerelease: false
|
|
136
|
-
version_requirements: *
|
|
136
|
+
version_requirements: *2168497720
|
|
137
137
|
- !ruby/object:Gem::Dependency
|
|
138
138
|
name: ci_reporter
|
|
139
|
-
requirement: &
|
|
139
|
+
requirement: &2152104200 !ruby/object:Gem::Requirement
|
|
140
140
|
none: false
|
|
141
141
|
requirements:
|
|
142
142
|
- - ! '>='
|
|
@@ -144,10 +144,10 @@ dependencies:
|
|
|
144
144
|
version: 1.6.3
|
|
145
145
|
type: :development
|
|
146
146
|
prerelease: false
|
|
147
|
-
version_requirements: *
|
|
147
|
+
version_requirements: *2152104200
|
|
148
148
|
- !ruby/object:Gem::Dependency
|
|
149
149
|
name: yard
|
|
150
|
-
requirement: &
|
|
150
|
+
requirement: &2152102500 !ruby/object:Gem::Requirement
|
|
151
151
|
none: false
|
|
152
152
|
requirements:
|
|
153
153
|
- - ~>
|
|
@@ -155,10 +155,10 @@ dependencies:
|
|
|
155
155
|
version: 0.6.0
|
|
156
156
|
type: :development
|
|
157
157
|
prerelease: false
|
|
158
|
-
version_requirements: *
|
|
158
|
+
version_requirements: *2152102500
|
|
159
159
|
- !ruby/object:Gem::Dependency
|
|
160
160
|
name: rcov
|
|
161
|
-
requirement: &
|
|
161
|
+
requirement: &2152101300 !ruby/object:Gem::Requirement
|
|
162
162
|
none: false
|
|
163
163
|
requirements:
|
|
164
164
|
- - ! '>='
|
|
@@ -166,10 +166,10 @@ dependencies:
|
|
|
166
166
|
version: '0'
|
|
167
167
|
type: :development
|
|
168
168
|
prerelease: false
|
|
169
|
-
version_requirements: *
|
|
169
|
+
version_requirements: *2152101300
|
|
170
170
|
- !ruby/object:Gem::Dependency
|
|
171
171
|
name: rake
|
|
172
|
-
requirement: &
|
|
172
|
+
requirement: &2152100160 !ruby/object:Gem::Requirement
|
|
173
173
|
none: false
|
|
174
174
|
requirements:
|
|
175
175
|
- - ! '>='
|
|
@@ -177,10 +177,10 @@ dependencies:
|
|
|
177
177
|
version: '0'
|
|
178
178
|
type: :development
|
|
179
179
|
prerelease: false
|
|
180
|
-
version_requirements: *
|
|
180
|
+
version_requirements: *2152100160
|
|
181
181
|
- !ruby/object:Gem::Dependency
|
|
182
182
|
name: mocha
|
|
183
|
-
requirement: &
|
|
183
|
+
requirement: &2152097280 !ruby/object:Gem::Requirement
|
|
184
184
|
none: false
|
|
185
185
|
requirements:
|
|
186
186
|
- - ! '>='
|
|
@@ -188,10 +188,10 @@ dependencies:
|
|
|
188
188
|
version: '0'
|
|
189
189
|
type: :development
|
|
190
190
|
prerelease: false
|
|
191
|
-
version_requirements: *
|
|
191
|
+
version_requirements: *2152097280
|
|
192
192
|
- !ruby/object:Gem::Dependency
|
|
193
193
|
name: i18n
|
|
194
|
-
requirement: &
|
|
194
|
+
requirement: &2152084420 !ruby/object:Gem::Requirement
|
|
195
195
|
none: false
|
|
196
196
|
requirements:
|
|
197
197
|
- - ! '>='
|
|
@@ -199,10 +199,10 @@ dependencies:
|
|
|
199
199
|
version: '0'
|
|
200
200
|
type: :development
|
|
201
201
|
prerelease: false
|
|
202
|
-
version_requirements: *
|
|
202
|
+
version_requirements: *2152084420
|
|
203
203
|
- !ruby/object:Gem::Dependency
|
|
204
204
|
name: countdownlatch
|
|
205
|
-
requirement: &
|
|
205
|
+
requirement: &2152079640 !ruby/object:Gem::Requirement
|
|
206
206
|
none: false
|
|
207
207
|
requirements:
|
|
208
208
|
- - ! '>='
|
|
@@ -210,10 +210,10 @@ dependencies:
|
|
|
210
210
|
version: '0'
|
|
211
211
|
type: :development
|
|
212
212
|
prerelease: false
|
|
213
|
-
version_requirements: *
|
|
213
|
+
version_requirements: *2152079640
|
|
214
214
|
- !ruby/object:Gem::Dependency
|
|
215
215
|
name: guard-rspec
|
|
216
|
-
requirement: &
|
|
216
|
+
requirement: &2152075260 !ruby/object:Gem::Requirement
|
|
217
217
|
none: false
|
|
218
218
|
requirements:
|
|
219
219
|
- - ! '>='
|
|
@@ -221,7 +221,7 @@ dependencies:
|
|
|
221
221
|
version: '0'
|
|
222
222
|
type: :development
|
|
223
223
|
prerelease: false
|
|
224
|
-
version_requirements: *
|
|
224
|
+
version_requirements: *2152075260
|
|
225
225
|
description: Like Rack is to Rails and Sinatra, Punchblock provides a consistent API
|
|
226
226
|
on top of several underlying third-party call control protocols.
|
|
227
227
|
email: punchblock@adhearsion.com
|
|
@@ -363,7 +363,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
363
363
|
version: '0'
|
|
364
364
|
segments:
|
|
365
365
|
- 0
|
|
366
|
-
hash: -
|
|
366
|
+
hash: -2044158183945900193
|
|
367
367
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
368
368
|
none: false
|
|
369
369
|
requirements:
|