punchblock 2.3.1 → 2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/lib/punchblock/client.rb +1 -1
- data/lib/punchblock/command/dial.rb +4 -1
- data/lib/punchblock/component/component_node.rb +14 -9
- data/lib/punchblock/connection/asterisk.rb +4 -0
- data/lib/punchblock/connection/xmpp.rb +4 -0
- data/lib/punchblock/core_ext/blather/stanza.rb +9 -1
- data/lib/punchblock/rayo_node.rb +3 -1
- data/lib/punchblock/translator/asterisk.rb +7 -3
- data/lib/punchblock/translator/asterisk/call.rb +16 -13
- data/lib/punchblock/translator/asterisk/component/asterisk/agi_command.rb +1 -1
- data/lib/punchblock/version.rb +1 -1
- data/punchblock.gemspec +1 -1
- data/spec/punchblock/client_spec.rb +7 -0
- data/spec/punchblock/command/dial_spec.rb +5 -2
- data/spec/punchblock/connection/asterisk_spec.rb +7 -0
- data/spec/punchblock/connection/xmpp_spec.rb +44 -0
- data/spec/punchblock/translator/asterisk/call_spec.rb +224 -0
- data/spec/punchblock/translator/asterisk/component/asterisk/agi_command_spec.rb +23 -0
- data/spec/punchblock/translator/asterisk_spec.rb +38 -1
- data/spec/spec_helper.rb +4 -0
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b59ef9a626a919b1db35af9603f8d5d73edae6a8
|
|
4
|
+
data.tar.gz: 47f018bf7cc1d9ba4e6d14acb0d4bc18e64622e2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8f143c3727b0bf9d205d3c9f8a09eec1eda96ad294af4dcca99b962de39c6aa47cce84ec24080a40ab4ca8ff78e244a444c66fa6571ba0e892871b475798549c
|
|
7
|
+
data.tar.gz: 55b015a502dc38e174c59707701046c0138ccf8e0db6afd318da38b7e7672a4271023cef5fca0f7b90bf22eaf6d2c695b6d5aee1a5acc0223a5147a410c9f77c
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# [develop](https://github.com/adhearsion/punchblock)
|
|
2
2
|
|
|
3
|
+
# [v2.4.0](https://github.com/adhearsion/punchblock/compare/v2.3.1...v2.4.0) - [2014-03-01](https://rubygems.org/gems/punchblock/versions/2.4.0)
|
|
4
|
+
* Feature: Add support for requesting calls with a specific URI
|
|
5
|
+
* Feature: Allow generation of a random call URI for a client
|
|
6
|
+
* Feature: Rayo events should be timestamped with dispatch or receipt time (#213)
|
|
7
|
+
|
|
3
8
|
# [v2.3.1](https://github.com/adhearsion/punchblock/compare/v2.3.0...v2.3.1) - [2014-02-13](https://rubygems.org/gems/punchblock/versions/2.3.1)
|
|
4
9
|
* Bugfix: Ensure commands can be associated on the wire even before they're executed
|
|
5
10
|
* Bugfix: Ensure a command is always transitioned to is requested state prior to receiving a response
|
data/lib/punchblock/client.rb
CHANGED
|
@@ -10,7 +10,7 @@ module Punchblock
|
|
|
10
10
|
|
|
11
11
|
attr_reader :connection, :component_registry
|
|
12
12
|
|
|
13
|
-
delegate :run, :stop, :send_message, :to => :connection
|
|
13
|
+
delegate :run, :stop, :send_message, :new_call_uri, :to => :connection
|
|
14
14
|
|
|
15
15
|
# @param [Hash] options
|
|
16
16
|
# @option options [Connection::XMPP] :connection The Punchblock connection to use for this session
|
|
@@ -13,6 +13,9 @@ module Punchblock
|
|
|
13
13
|
# @return [String] the caller ID
|
|
14
14
|
attribute :from
|
|
15
15
|
|
|
16
|
+
# @return [String] the requested URI for the resulting call
|
|
17
|
+
attribute :uri
|
|
18
|
+
|
|
16
19
|
# @return [Integer] timeout in milliseconds
|
|
17
20
|
attribute :timeout, Integer
|
|
18
21
|
|
|
@@ -27,7 +30,7 @@ module Punchblock
|
|
|
27
30
|
end
|
|
28
31
|
|
|
29
32
|
def rayo_attributes
|
|
30
|
-
{to: to, from: from, timeout: timeout}
|
|
33
|
+
{to: to, from: from, uri: uri, timeout: timeout}
|
|
31
34
|
end
|
|
32
35
|
|
|
33
36
|
def rayo_children(root)
|
|
@@ -8,6 +8,7 @@ module Punchblock
|
|
|
8
8
|
def initialize(*args)
|
|
9
9
|
super
|
|
10
10
|
@complete_event_resource = FutureResource.new
|
|
11
|
+
@mutex = Mutex.new
|
|
11
12
|
register_internal_handlers
|
|
12
13
|
end
|
|
13
14
|
|
|
@@ -36,12 +37,14 @@ module Punchblock
|
|
|
36
37
|
end
|
|
37
38
|
|
|
38
39
|
def response=(other)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
@mutex.synchronize do
|
|
41
|
+
if other.is_a?(Ref)
|
|
42
|
+
@component_id = other.component_id
|
|
43
|
+
@source_uri = other.uri.to_s
|
|
44
|
+
client.register_component self if client
|
|
45
|
+
end
|
|
46
|
+
super
|
|
43
47
|
end
|
|
44
|
-
super
|
|
45
48
|
end
|
|
46
49
|
|
|
47
50
|
def complete_event(timeout = nil)
|
|
@@ -49,10 +52,12 @@ module Punchblock
|
|
|
49
52
|
end
|
|
50
53
|
|
|
51
54
|
def complete_event=(other)
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
@mutex.synchronize do
|
|
56
|
+
return if @complete_event_resource.set_yet?
|
|
57
|
+
client.delete_component_registration self if client
|
|
58
|
+
complete!
|
|
59
|
+
@complete_event_resource.resource = other
|
|
60
|
+
end
|
|
56
61
|
rescue StateMachine::InvalidTransition => e
|
|
57
62
|
e.message << " for component #{self}"
|
|
58
63
|
raise e
|
|
@@ -10,7 +10,7 @@ module Blather
|
|
|
10
10
|
def rayo_node
|
|
11
11
|
@rayo_node ||= begin
|
|
12
12
|
first_child = at_xpath RAYO_NODE_PATH, Punchblock::RAYO_NAMESPACES
|
|
13
|
-
Punchblock::RayoNode.from_xml first_child, nil, component_id, "xmpp:#{from}" if first_child
|
|
13
|
+
Punchblock::RayoNode.from_xml first_child, nil, component_id, "xmpp:#{from}", delay_timestamp if first_child
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
|
|
@@ -27,5 +27,13 @@ module Blather
|
|
|
27
27
|
def component_id
|
|
28
28
|
from.resource
|
|
29
29
|
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def delay_timestamp
|
|
34
|
+
if delay = self.at_xpath('ns:delay', ns: 'urn:xmpp:delay')
|
|
35
|
+
DateTime.parse(delay[:stamp])
|
|
36
|
+
end
|
|
37
|
+
end
|
|
30
38
|
end
|
|
31
39
|
end
|
data/lib/punchblock/rayo_node.rb
CHANGED
|
@@ -17,6 +17,7 @@ module Punchblock
|
|
|
17
17
|
attribute :source_uri
|
|
18
18
|
attribute :domain
|
|
19
19
|
attribute :transport
|
|
20
|
+
attribute :timestamp, DateTime, default: ->(*) { DateTime.now }
|
|
20
21
|
|
|
21
22
|
attr_accessor :connection, :client, :original_component
|
|
22
23
|
|
|
@@ -49,7 +50,7 @@ module Punchblock
|
|
|
49
50
|
# elements of the XML::Node
|
|
50
51
|
# @param [XML::Node] node the node to import
|
|
51
52
|
# @return the appropriate object based on the node name and namespace
|
|
52
|
-
def self.from_xml(node, call_id = nil, component_id = nil, uri = nil)
|
|
53
|
+
def self.from_xml(node, call_id = nil, component_id = nil, uri = nil, timestamp = nil)
|
|
53
54
|
ns = (node.namespace.href if node.namespace)
|
|
54
55
|
klass = class_from_registration(node.name, ns)
|
|
55
56
|
if klass && klass != self
|
|
@@ -60,6 +61,7 @@ module Punchblock
|
|
|
60
61
|
event.target_call_id = call_id
|
|
61
62
|
event.component_id = component_id
|
|
62
63
|
event.source_uri = uri
|
|
64
|
+
event.timestamp = timestamp if timestamp
|
|
63
65
|
end
|
|
64
66
|
end
|
|
65
67
|
|
|
@@ -140,9 +140,13 @@ module Punchblock
|
|
|
140
140
|
register_component component
|
|
141
141
|
component.execute
|
|
142
142
|
when Punchblock::Command::Dial
|
|
143
|
-
call =
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
if call = call_with_id(command.uri)
|
|
144
|
+
command.response = ProtocolError.new.setup(:conflict, 'Call ID already in use')
|
|
145
|
+
else
|
|
146
|
+
call = Call.new command.to, current_actor, ami_client, connection, nil, command.uri
|
|
147
|
+
register_call call
|
|
148
|
+
call.dial command
|
|
149
|
+
end
|
|
146
150
|
else
|
|
147
151
|
command.response = ProtocolError.new.setup 'command-not-acceptable', "Did not understand command"
|
|
148
152
|
end
|
|
@@ -25,10 +25,11 @@ module Punchblock
|
|
|
25
25
|
HANGUP_CAUSE_TO_END_REASON[22] = :reject
|
|
26
26
|
HANGUP_CAUSE_TO_END_REASON[102] = :timeout
|
|
27
27
|
|
|
28
|
-
def initialize(channel, translator, ami_client, connection, agi_env = nil)
|
|
28
|
+
def initialize(channel, translator, ami_client, connection, agi_env = nil, id = nil)
|
|
29
29
|
@channel, @translator, @ami_client, @connection = channel, translator, ami_client, connection
|
|
30
30
|
@agi_env = agi_env || {}
|
|
31
|
-
@id
|
|
31
|
+
@id = id || Punchblock.new_uuid
|
|
32
|
+
@components = {}
|
|
32
33
|
@answered = false
|
|
33
34
|
@pending_joins = {}
|
|
34
35
|
@progress_sent = false
|
|
@@ -113,7 +114,7 @@ module Punchblock
|
|
|
113
114
|
|
|
114
115
|
case ami_event.name
|
|
115
116
|
when 'Hangup'
|
|
116
|
-
handle_hangup_event ami_event['Cause'].to_i
|
|
117
|
+
handle_hangup_event ami_event['Cause'].to_i, ami_event.best_time
|
|
117
118
|
when 'AsyncAGI'
|
|
118
119
|
if component = component_with_id(ami_event['CommandID'])
|
|
119
120
|
component.handle_ami_event ami_event
|
|
@@ -121,16 +122,16 @@ module Punchblock
|
|
|
121
122
|
|
|
122
123
|
if @answered == false && ami_event['SubEvent'] == 'Start'
|
|
123
124
|
@answered = true
|
|
124
|
-
send_pb_event Event::Answered.new
|
|
125
|
+
send_pb_event Event::Answered.new(timestamp: ami_event.best_time)
|
|
125
126
|
end
|
|
126
127
|
when 'Newstate'
|
|
127
128
|
case ami_event['ChannelState']
|
|
128
129
|
when '5'
|
|
129
|
-
send_pb_event Event::Ringing.new
|
|
130
|
+
send_pb_event Event::Ringing.new(timestamp: ami_event.best_time)
|
|
130
131
|
end
|
|
131
132
|
when 'OriginateResponse'
|
|
132
133
|
if ami_event['Response'] == 'Failure' && ami_event['Uniqueid'] == '<null>'
|
|
133
|
-
send_end_event :error
|
|
134
|
+
send_end_event :error, nil, ami_event.best_time
|
|
134
135
|
end
|
|
135
136
|
when 'BridgeExec'
|
|
136
137
|
join_command = @pending_joins.delete ami_event['Channel1']
|
|
@@ -141,16 +142,16 @@ module Punchblock
|
|
|
141
142
|
if other_call = translator.call_for_channel(other_call_channel)
|
|
142
143
|
event = case ami_event['Bridgestate']
|
|
143
144
|
when 'Link'
|
|
144
|
-
Event::Joined.new call_uri: other_call.id
|
|
145
|
+
Event::Joined.new call_uri: other_call.id, timestamp: ami_event.best_time
|
|
145
146
|
when 'Unlink'
|
|
146
|
-
Event::Unjoined.new call_uri: other_call.id
|
|
147
|
+
Event::Unjoined.new call_uri: other_call.id, timestamp: ami_event.best_time
|
|
147
148
|
end
|
|
148
149
|
send_pb_event event
|
|
149
150
|
end
|
|
150
151
|
when 'Unlink'
|
|
151
152
|
other_call_channel = ([ami_event['Channel1'], ami_event['Channel2']] - [channel]).first
|
|
152
153
|
if other_call = translator.call_for_channel(other_call_channel)
|
|
153
|
-
send_pb_event Event::Unjoined.new(call_uri: other_call.id)
|
|
154
|
+
send_pb_event Event::Unjoined.new(call_uri: other_call.id, timestamp: ami_event.best_time)
|
|
154
155
|
end
|
|
155
156
|
when 'VarSet'
|
|
156
157
|
@channel_variables[ami_event['Variable']] = ami_event['Value']
|
|
@@ -278,13 +279,14 @@ module Punchblock
|
|
|
278
279
|
send_ami_action 'Redirect', redirect_options
|
|
279
280
|
end
|
|
280
281
|
|
|
281
|
-
def handle_hangup_event(code =
|
|
282
|
+
def handle_hangup_event(code = nil, timestamp = nil)
|
|
283
|
+
code ||= 16
|
|
282
284
|
reason = @hangup_cause || HANGUP_CAUSE_TO_END_REASON[code]
|
|
283
285
|
@block_commands = true
|
|
284
286
|
@components.each_pair do |id, component|
|
|
285
287
|
component.call_ended
|
|
286
288
|
end
|
|
287
|
-
send_end_event reason, code
|
|
289
|
+
send_end_event reason, code, timestamp
|
|
288
290
|
end
|
|
289
291
|
|
|
290
292
|
def after(*args, &block)
|
|
@@ -306,8 +308,9 @@ module Punchblock
|
|
|
306
308
|
AMIErrorConverter.convert { @ami_client.send_action name, headers }
|
|
307
309
|
end
|
|
308
310
|
|
|
309
|
-
def send_end_event(reason, code = nil)
|
|
310
|
-
|
|
311
|
+
def send_end_event(reason, code = nil, timestamp = nil)
|
|
312
|
+
end_event = Event::End.new(reason: reason, platform_code: code, timestamp: timestamp)
|
|
313
|
+
send_pb_event end_event
|
|
311
314
|
translator.deregister_call id, channel
|
|
312
315
|
end
|
|
313
316
|
|
|
@@ -23,7 +23,7 @@ module Punchblock
|
|
|
23
23
|
if event.name == 'AsyncAGI' && event['SubEvent'] == 'Exec'
|
|
24
24
|
send_complete_event success_reason(event)
|
|
25
25
|
if @component_node.name == 'ASYNCAGI BREAK' && @call.channel_var('PUNCHBLOCK_END_ON_ASYNCAGI_BREAK')
|
|
26
|
-
@call.handle_hangup_event
|
|
26
|
+
@call.handle_hangup_event nil, event.best_time
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
end
|
data/lib/punchblock/version.rb
CHANGED
data/punchblock.gemspec
CHANGED
|
@@ -29,7 +29,7 @@ Gem::Specification.new do |s|
|
|
|
29
29
|
s.add_runtime_dependency %q<future-resource>, ["~> 1.0"]
|
|
30
30
|
s.add_runtime_dependency %q<has-guarded-handlers>, ["~> 1.5"]
|
|
31
31
|
s.add_runtime_dependency %q<celluloid>, ["~> 0.14"]
|
|
32
|
-
s.add_runtime_dependency %q<ruby_ami>, ["~> 2.
|
|
32
|
+
s.add_runtime_dependency %q<ruby_ami>, ["~> 2.2"]
|
|
33
33
|
s.add_runtime_dependency %q<ruby_fs>, ["~> 1.1"]
|
|
34
34
|
s.add_runtime_dependency %q<ruby_speech>, ["~> 2.3"]
|
|
35
35
|
s.add_runtime_dependency %q<virtus>, ["~> 1.0"]
|
|
@@ -40,6 +40,13 @@ module Punchblock
|
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
describe '#new_call_uri' do
|
|
44
|
+
it 'should return the connection-specific fresh call ID' do
|
|
45
|
+
stub_uuids 'foobar'
|
|
46
|
+
subject.new_call_uri.should == 'xmpp:foobar@call.rayo.net'
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
43
50
|
it 'should handle connection events' do
|
|
44
51
|
subject.should_receive(:handle_event).with(mock_event).once
|
|
45
52
|
connection.event_handler.call mock_event
|
|
@@ -13,10 +13,11 @@ module Punchblock
|
|
|
13
13
|
let(:join_params) { {:call_uri => 'abc123'} }
|
|
14
14
|
|
|
15
15
|
describe "when setting options in initializer" do
|
|
16
|
-
subject { described_class.new to: 'tel:+14155551212', from: 'tel:+13035551212', timeout: 30000, headers: { 'X-skill' => 'agent', 'X-customer-id' => '8877' }, join: join_params }
|
|
16
|
+
subject { described_class.new to: 'tel:+14155551212', from: 'tel:+13035551212', uri: 'xmpp:foo@bar.com', timeout: 30000, headers: { 'X-skill' => 'agent', 'X-customer-id' => '8877' }, join: join_params }
|
|
17
17
|
|
|
18
18
|
its(:to) { should be == 'tel:+14155551212' }
|
|
19
19
|
its(:from) { should be == 'tel:+13035551212' }
|
|
20
|
+
its(:uri) { should be == 'xmpp:foo@bar.com' }
|
|
20
21
|
its(:timeout) { should be == 30000 }
|
|
21
22
|
its(:join) { should be == Join.new(join_params) }
|
|
22
23
|
its(:headers) { should be == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
|
|
@@ -27,6 +28,7 @@ module Punchblock
|
|
|
27
28
|
new_instance.should be_instance_of described_class
|
|
28
29
|
new_instance.to.should == 'tel:+14155551212'
|
|
29
30
|
new_instance.from.should == 'tel:+13035551212'
|
|
31
|
+
new_instance.uri.should == 'xmpp:foo@bar.com'
|
|
30
32
|
new_instance.timeout.should == 30000
|
|
31
33
|
new_instance.join.should == Join.new(join_params)
|
|
32
34
|
new_instance.headers.should == { 'X-skill' => 'agent', 'X-customer-id' => '8877' }
|
|
@@ -54,7 +56,7 @@ module Punchblock
|
|
|
54
56
|
describe "from a stanza" do
|
|
55
57
|
let :stanza do
|
|
56
58
|
<<-MESSAGE
|
|
57
|
-
<dial to='tel:+14155551212' from='tel:+13035551212' timeout='30000' xmlns='urn:xmpp:rayo:1'>
|
|
59
|
+
<dial to='tel:+14155551212' from='tel:+13035551212' uri='xmpp:foo@bar.com' timeout='30000' xmlns='urn:xmpp:rayo:1'>
|
|
58
60
|
<join call-uri="abc123" />
|
|
59
61
|
<header name="X-skill" value="agent" />
|
|
60
62
|
<header name="X-customer-id" value="8877" />
|
|
@@ -68,6 +70,7 @@ module Punchblock
|
|
|
68
70
|
|
|
69
71
|
its(:to) { should be == 'tel:+14155551212' }
|
|
70
72
|
its(:from) { should be == 'tel:+13035551212' }
|
|
73
|
+
its(:uri) { should be == 'xmpp:foo@bar.com' }
|
|
71
74
|
its(:timeout) { should be == 30000 }
|
|
72
75
|
its(:join) { should be == Join.new(join_params) }
|
|
73
76
|
its(:headers) { should be == { 'X-skill' => 'agent', 'X-customer-id' => '8877' } }
|
|
@@ -17,22 +17,42 @@ module Punchblock
|
|
|
17
17
|
subject { connection }
|
|
18
18
|
|
|
19
19
|
describe "rayo domains" do
|
|
20
|
+
before { stub_uuids 'randomcallid' }
|
|
21
|
+
|
|
20
22
|
context "with no domains specified, and a JID of 1@app.rayo.net" do
|
|
21
23
|
let(:options) { { :username => '1@app.rayo.net' } }
|
|
22
24
|
|
|
23
25
|
its(:root_domain) { should be == 'app.rayo.net' }
|
|
26
|
+
|
|
27
|
+
describe '#new_call_uri' do
|
|
28
|
+
it "should return an appropriate random call URI" do
|
|
29
|
+
subject.new_call_uri.should == 'xmpp:randomcallid@app.rayo.net'
|
|
30
|
+
end
|
|
31
|
+
end
|
|
24
32
|
end
|
|
25
33
|
|
|
26
34
|
context "with only a rayo domain set" do
|
|
27
35
|
let(:options) { { :rayo_domain => 'rayo.org' } }
|
|
28
36
|
|
|
29
37
|
its(:root_domain) { should be == 'rayo.org' }
|
|
38
|
+
|
|
39
|
+
describe '#new_call_uri' do
|
|
40
|
+
it "should return an appropriate random call URI" do
|
|
41
|
+
subject.new_call_uri.should == 'xmpp:randomcallid@rayo.org'
|
|
42
|
+
end
|
|
43
|
+
end
|
|
30
44
|
end
|
|
31
45
|
|
|
32
46
|
context "with only a root domain set" do
|
|
33
47
|
let(:options) { { :root_domain => 'rayo.org' } }
|
|
34
48
|
|
|
35
49
|
its(:root_domain) { should be == 'rayo.org' }
|
|
50
|
+
|
|
51
|
+
describe '#new_call_uri' do
|
|
52
|
+
it "should return an appropriate random call URI" do
|
|
53
|
+
subject.new_call_uri.should == 'xmpp:randomcallid@rayo.org'
|
|
54
|
+
end
|
|
55
|
+
end
|
|
36
56
|
end
|
|
37
57
|
end
|
|
38
58
|
|
|
@@ -217,6 +237,11 @@ module Punchblock
|
|
|
217
237
|
MSG
|
|
218
238
|
end
|
|
219
239
|
|
|
240
|
+
before do
|
|
241
|
+
@now = DateTime.now
|
|
242
|
+
DateTime.stub now: @now
|
|
243
|
+
end
|
|
244
|
+
|
|
220
245
|
let(:example_event) { import_stanza offer_xml }
|
|
221
246
|
|
|
222
247
|
it { example_event.should be_a Blather::Stanza::Presence }
|
|
@@ -228,9 +253,28 @@ module Punchblock
|
|
|
228
253
|
event.source_uri.should be == 'xmpp:9f00061@call.rayo.net'
|
|
229
254
|
event.domain.should be == 'call.rayo.net'
|
|
230
255
|
event.transport.should be == 'xmpp'
|
|
256
|
+
event.timestamp.should be == @now
|
|
231
257
|
end
|
|
232
258
|
handle_presence
|
|
233
259
|
end
|
|
260
|
+
|
|
261
|
+
context "with a delayed delivery timestamp" do
|
|
262
|
+
let :offer_xml do
|
|
263
|
+
<<-MSG
|
|
264
|
+
<presence to='16577@app.rayo.net/1' from='9f00061@call.rayo.net'>
|
|
265
|
+
<offer xmlns="urn:xmpp:rayo:1" to="sip:whatever@127.0.0.1" from="sip:ylcaomxb@192.168.1.9"/>
|
|
266
|
+
<delay xmlns='urn:xmpp:delay' stamp='2002-09-10T23:08:25Z'/>
|
|
267
|
+
</presence>
|
|
268
|
+
MSG
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
it 'should stamp that time on the rayo event' do
|
|
272
|
+
mock_event_handler.should_receive(:call).once.with do |event|
|
|
273
|
+
event.timestamp.should be == DateTime.new(2002, 9, 10, 23, 8, 25, 0)
|
|
274
|
+
end
|
|
275
|
+
handle_presence
|
|
276
|
+
end
|
|
277
|
+
end
|
|
234
278
|
end
|
|
235
279
|
|
|
236
280
|
describe "from something that's not a real event" do
|
|
@@ -327,6 +327,27 @@ module Punchblock
|
|
|
327
327
|
comp_command.response(0.1).should == ProtocolError.new.setup(:item_not_found, "Could not find a call with ID #{call_id}", call_id)
|
|
328
328
|
end
|
|
329
329
|
|
|
330
|
+
context "when the AMI event has a timestamp" do
|
|
331
|
+
let :ami_event do
|
|
332
|
+
RubyAMI::Event.new 'Hangup',
|
|
333
|
+
'Uniqueid' => "1320842458.8",
|
|
334
|
+
'Cause' => cause,
|
|
335
|
+
'Cause-txt' => cause_txt,
|
|
336
|
+
'Channel' => "SIP/1234-00000000",
|
|
337
|
+
'Timestamp' => '1393368380.572575'
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
it "should use the AMI timestamp for the Rayo event" do
|
|
341
|
+
expected_end_event = Punchblock::Event::End.new reason: :hangup,
|
|
342
|
+
platform_code: cause,
|
|
343
|
+
target_call_id: subject.id,
|
|
344
|
+
timestamp: DateTime.new(2014, 2, 25, 22, 46, 20)
|
|
345
|
+
translator.should_receive(:handle_pb_event).with expected_end_event
|
|
346
|
+
|
|
347
|
+
subject.process_ami_event ami_event
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
330
351
|
context "after processing a hangup command" do
|
|
331
352
|
let(:command) { Command::Hangup.new }
|
|
332
353
|
|
|
@@ -534,6 +555,24 @@ module Punchblock
|
|
|
534
555
|
subject.process_ami_event ami_event
|
|
535
556
|
end
|
|
536
557
|
end
|
|
558
|
+
|
|
559
|
+
context "when the AMI event has a timestamp" do
|
|
560
|
+
let :ami_event do
|
|
561
|
+
RubyAMI::Event.new "AsyncAGI",
|
|
562
|
+
"SubEvent" => "Start",
|
|
563
|
+
"Channel" => "SIP/1234-00000000",
|
|
564
|
+
"Env" => "agi_request%3A%20async%0Aagi_channel%3A%20SIP%2Fuserb-00000006%0Aagi_language%3A%20en%0Aagi_type%3A%20SIP%0Aagi_uniqueid%3A%201390303636.6%0Aagi_version%3A%2011.7.0%0Aagi_callerid%3A%20userb%0Aagi_calleridname%3A%20User%20B%0Aagi_callingpres%3A%200%0Aagi_callingani2%3A%200%0Aagi_callington%3A%200%0Aagi_callingtns%3A%200%0Aagi_dnid%3A%20unknown%0Aagi_rdnis%3A%20unknown%0Aagi_context%3A%20adhearsion-redirect%0Aagi_extension%3A%201%0Aagi_priority%3A%201%0Aagi_enhanced%3A%200.0%0Aagi_accountcode%3A%20%0Aagi_threadid%3A%20139696536876800%0A%0A",
|
|
565
|
+
'Timestamp' => '1393368380.572575'
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
it "should use the AMI timestamp for the Rayo event" do
|
|
569
|
+
expected_answered = Punchblock::Event::Answered.new target_call_id: subject.id,
|
|
570
|
+
timestamp: DateTime.new(2014, 2, 25, 22, 46, 20)
|
|
571
|
+
translator.should_receive(:handle_pb_event).with expected_answered
|
|
572
|
+
|
|
573
|
+
subject.process_ami_event ami_event
|
|
574
|
+
end
|
|
575
|
+
end
|
|
537
576
|
end
|
|
538
577
|
|
|
539
578
|
context 'with a Newstate event' do
|
|
@@ -565,6 +604,25 @@ module Punchblock
|
|
|
565
604
|
subject.process_ami_event ami_event
|
|
566
605
|
subject.answered?.should be_false
|
|
567
606
|
end
|
|
607
|
+
|
|
608
|
+
context "when the AMI event has a timestamp" do
|
|
609
|
+
let :ami_event do
|
|
610
|
+
RubyAMI::Event.new 'Newstate',
|
|
611
|
+
'Channel' => 'SIP/1234-00000000',
|
|
612
|
+
'ChannelState' => channel_state,
|
|
613
|
+
'ChannelStateDesc' => channel_state_desc,
|
|
614
|
+
'Uniqueid' => '1326194671.0',
|
|
615
|
+
'Timestamp' => '1393368380.572575'
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
it "should use the AMI timestamp for the Rayo event" do
|
|
619
|
+
expected_ringing = Punchblock::Event::Ringing.new target_call_id: subject.id,
|
|
620
|
+
timestamp: DateTime.new(2014, 2, 25, 22, 46, 20)
|
|
621
|
+
translator.should_receive(:handle_pb_event).with expected_ringing
|
|
622
|
+
|
|
623
|
+
subject.process_ami_event ami_event
|
|
624
|
+
end
|
|
625
|
+
end
|
|
568
626
|
end
|
|
569
627
|
end
|
|
570
628
|
|
|
@@ -613,6 +671,32 @@ module Punchblock
|
|
|
613
671
|
translator.should_receive(:handle_pb_event).with expected_end_event
|
|
614
672
|
subject.process_ami_event ami_event
|
|
615
673
|
end
|
|
674
|
+
|
|
675
|
+
context "when the AMI event has a timestamp" do
|
|
676
|
+
let :ami_event do
|
|
677
|
+
RubyAMI::Event.new 'OriginateResponse',
|
|
678
|
+
'Privilege' => 'call,all',
|
|
679
|
+
'ActionID' => '9d0c1aa4-5e3b-4cae-8aef-76a6119e2909',
|
|
680
|
+
'Response' => response,
|
|
681
|
+
'Channel' => 'SIP/15557654321',
|
|
682
|
+
'Context' => '',
|
|
683
|
+
'Exten' => '',
|
|
684
|
+
'Reason' => '0',
|
|
685
|
+
'Uniqueid' => uniqueid,
|
|
686
|
+
'CallerIDNum' => 'sip:5551234567',
|
|
687
|
+
'CallerIDName' => 'Bryan 100',
|
|
688
|
+
'Timestamp' => '1393368380.572575'
|
|
689
|
+
end
|
|
690
|
+
|
|
691
|
+
it "should use the AMI timestamp for the Rayo event" do
|
|
692
|
+
expected_end_event = Punchblock::Event::End.new reason: :error,
|
|
693
|
+
target_call_id: subject.id,
|
|
694
|
+
timestamp: DateTime.new(2014, 2, 25, 22, 46, 20)
|
|
695
|
+
translator.should_receive(:handle_pb_event).with expected_end_event
|
|
696
|
+
|
|
697
|
+
subject.process_ami_event ami_event
|
|
698
|
+
end
|
|
699
|
+
end
|
|
616
700
|
end
|
|
617
701
|
end
|
|
618
702
|
|
|
@@ -749,6 +833,54 @@ module Punchblock
|
|
|
749
833
|
translator.should_receive(:handle_pb_event).with expected_joined
|
|
750
834
|
subject.process_ami_event switched_ami_event
|
|
751
835
|
end
|
|
836
|
+
|
|
837
|
+
context "when the AMI event has a timestamp" do
|
|
838
|
+
let :ami_event do
|
|
839
|
+
RubyAMI::Event.new 'Bridge',
|
|
840
|
+
'Privilege' => "call,all",
|
|
841
|
+
'Bridgestate' => state,
|
|
842
|
+
'Bridgetype' => "core",
|
|
843
|
+
'Channel1' => channel,
|
|
844
|
+
'Channel2' => other_channel,
|
|
845
|
+
'Uniqueid1' => "1319717537.11",
|
|
846
|
+
'Uniqueid2' => "1319717537.10",
|
|
847
|
+
'CallerID1' => "1234",
|
|
848
|
+
'CallerID2' => "5678",
|
|
849
|
+
'Timestamp' => '1393368380.572575'
|
|
850
|
+
end
|
|
851
|
+
|
|
852
|
+
let :switched_ami_event do
|
|
853
|
+
RubyAMI::Event.new 'Bridge',
|
|
854
|
+
'Privilege' => "call,all",
|
|
855
|
+
'Bridgestate' => state,
|
|
856
|
+
'Bridgetype' => "core",
|
|
857
|
+
'Channel1' => other_channel,
|
|
858
|
+
'Channel2' => channel,
|
|
859
|
+
'Uniqueid1' => "1319717537.11",
|
|
860
|
+
'Uniqueid2' => "1319717537.10",
|
|
861
|
+
'CallerID1' => "1234",
|
|
862
|
+
'CallerID2' => "5678",
|
|
863
|
+
'Timestamp' => '1393368380.572575'
|
|
864
|
+
end
|
|
865
|
+
|
|
866
|
+
before { expected_joined.timestamp = DateTime.new(2014, 2, 25, 22, 46, 20) }
|
|
867
|
+
|
|
868
|
+
context "when the call is the first channel" do
|
|
869
|
+
it "should use the AMI timestamp for the Rayo event" do
|
|
870
|
+
translator.should_receive(:handle_pb_event).with expected_joined
|
|
871
|
+
|
|
872
|
+
subject.process_ami_event ami_event
|
|
873
|
+
end
|
|
874
|
+
end
|
|
875
|
+
|
|
876
|
+
context "when the call is the second channel" do
|
|
877
|
+
it "should use the AMI timestamp for the Rayo event" do
|
|
878
|
+
translator.should_receive(:handle_pb_event).with expected_joined
|
|
879
|
+
|
|
880
|
+
subject.process_ami_event switched_ami_event
|
|
881
|
+
end
|
|
882
|
+
end
|
|
883
|
+
end
|
|
752
884
|
end
|
|
753
885
|
|
|
754
886
|
context "of state 'Unlink'" do
|
|
@@ -768,6 +900,54 @@ module Punchblock
|
|
|
768
900
|
translator.should_receive(:handle_pb_event).with expected_unjoined
|
|
769
901
|
subject.process_ami_event switched_ami_event
|
|
770
902
|
end
|
|
903
|
+
|
|
904
|
+
context "when the AMI event has a timestamp" do
|
|
905
|
+
let :ami_event do
|
|
906
|
+
RubyAMI::Event.new 'Bridge',
|
|
907
|
+
'Privilege' => "call,all",
|
|
908
|
+
'Bridgestate' => state,
|
|
909
|
+
'Bridgetype' => "core",
|
|
910
|
+
'Channel1' => channel,
|
|
911
|
+
'Channel2' => other_channel,
|
|
912
|
+
'Uniqueid1' => "1319717537.11",
|
|
913
|
+
'Uniqueid2' => "1319717537.10",
|
|
914
|
+
'CallerID1' => "1234",
|
|
915
|
+
'CallerID2' => "5678",
|
|
916
|
+
'Timestamp' => '1393368380.572575'
|
|
917
|
+
end
|
|
918
|
+
|
|
919
|
+
let :switched_ami_event do
|
|
920
|
+
RubyAMI::Event.new 'Bridge',
|
|
921
|
+
'Privilege' => "call,all",
|
|
922
|
+
'Bridgestate' => state,
|
|
923
|
+
'Bridgetype' => "core",
|
|
924
|
+
'Channel1' => other_channel,
|
|
925
|
+
'Channel2' => channel,
|
|
926
|
+
'Uniqueid1' => "1319717537.11",
|
|
927
|
+
'Uniqueid2' => "1319717537.10",
|
|
928
|
+
'CallerID1' => "1234",
|
|
929
|
+
'CallerID2' => "5678",
|
|
930
|
+
'Timestamp' => '1393368380.572575'
|
|
931
|
+
end
|
|
932
|
+
|
|
933
|
+
before { expected_unjoined.timestamp = DateTime.new(2014, 2, 25, 22, 46, 20) }
|
|
934
|
+
|
|
935
|
+
context "when the call is the first channel" do
|
|
936
|
+
it "should use the AMI timestamp for the Rayo event" do
|
|
937
|
+
translator.should_receive(:handle_pb_event).with expected_unjoined
|
|
938
|
+
|
|
939
|
+
subject.process_ami_event ami_event
|
|
940
|
+
end
|
|
941
|
+
end
|
|
942
|
+
|
|
943
|
+
context "when the call is the second channel" do
|
|
944
|
+
it "should use the AMI timestamp for the Rayo event" do
|
|
945
|
+
translator.should_receive(:handle_pb_event).with expected_unjoined
|
|
946
|
+
|
|
947
|
+
subject.process_ami_event switched_ami_event
|
|
948
|
+
end
|
|
949
|
+
end
|
|
950
|
+
end
|
|
771
951
|
end
|
|
772
952
|
end
|
|
773
953
|
|
|
@@ -820,6 +1000,50 @@ module Punchblock
|
|
|
820
1000
|
translator.should_receive(:handle_pb_event).with expected_unjoined
|
|
821
1001
|
subject.process_ami_event switched_ami_event
|
|
822
1002
|
end
|
|
1003
|
+
|
|
1004
|
+
context "when the AMI event has a timestamp" do
|
|
1005
|
+
let :ami_event do
|
|
1006
|
+
RubyAMI::Event.new 'Unlink',
|
|
1007
|
+
'Privilege' => "call,all",
|
|
1008
|
+
'Channel1' => channel,
|
|
1009
|
+
'Channel2' => other_channel,
|
|
1010
|
+
'Uniqueid1' => "1319717537.11",
|
|
1011
|
+
'Uniqueid2' => "1319717537.10",
|
|
1012
|
+
'CallerID1' => "1234",
|
|
1013
|
+
'CallerID2' => "5678",
|
|
1014
|
+
'Timestamp' => '1393368380.572575'
|
|
1015
|
+
end
|
|
1016
|
+
|
|
1017
|
+
let :switched_ami_event do
|
|
1018
|
+
RubyAMI::Event.new 'Unlink',
|
|
1019
|
+
'Privilege' => "call,all",
|
|
1020
|
+
'Channel1' => other_channel,
|
|
1021
|
+
'Channel2' => channel,
|
|
1022
|
+
'Uniqueid1' => "1319717537.11",
|
|
1023
|
+
'Uniqueid2' => "1319717537.10",
|
|
1024
|
+
'CallerID1' => "1234",
|
|
1025
|
+
'CallerID2' => "5678",
|
|
1026
|
+
'Timestamp' => '1393368380.572575'
|
|
1027
|
+
end
|
|
1028
|
+
|
|
1029
|
+
before { expected_unjoined.timestamp = DateTime.new(2014, 2, 25, 22, 46, 20) }
|
|
1030
|
+
|
|
1031
|
+
context "when the call is the first channel" do
|
|
1032
|
+
it "should use the AMI timestamp for the Rayo event" do
|
|
1033
|
+
translator.should_receive(:handle_pb_event).with expected_unjoined
|
|
1034
|
+
|
|
1035
|
+
subject.process_ami_event ami_event
|
|
1036
|
+
end
|
|
1037
|
+
end
|
|
1038
|
+
|
|
1039
|
+
context "when the call is the second channel" do
|
|
1040
|
+
it "should use the AMI timestamp for the Rayo event" do
|
|
1041
|
+
translator.should_receive(:handle_pb_event).with expected_unjoined
|
|
1042
|
+
|
|
1043
|
+
subject.process_ami_event switched_ami_event
|
|
1044
|
+
end
|
|
1045
|
+
end
|
|
1046
|
+
end
|
|
823
1047
|
end
|
|
824
1048
|
|
|
825
1049
|
context 'with a VarSet event' do
|
|
@@ -168,6 +168,29 @@ module Punchblock
|
|
|
168
168
|
translator.should_receive(:handle_pb_event).once.with expected_end_event
|
|
169
169
|
subject.handle_ami_event ami_event
|
|
170
170
|
end
|
|
171
|
+
|
|
172
|
+
context "when the AMI event has a timestamp" do
|
|
173
|
+
let :ami_event do
|
|
174
|
+
RubyAMI::Event.new 'AsyncAGI',
|
|
175
|
+
"SubEvent" => "Exec",
|
|
176
|
+
"Channel" => channel,
|
|
177
|
+
"CommandId" => component_id,
|
|
178
|
+
"Command" => "EXEC ANSWER",
|
|
179
|
+
"Result" => "200%20result=123%20(timeout)%0A",
|
|
180
|
+
'Timestamp' => '1393368380.572575'
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
it "should use the AMI timestamp for the Rayo event" do
|
|
184
|
+
expected_end_event = Punchblock::Event::End.new reason: :hangup,
|
|
185
|
+
platform_code: 16,
|
|
186
|
+
target_call_id: mock_call.id,
|
|
187
|
+
timestamp: DateTime.new(2014, 2, 25, 22, 46, 20)
|
|
188
|
+
translator.should_receive(:handle_pb_event).once.with kind_of(Punchblock::Event::Complete)
|
|
189
|
+
translator.should_receive(:handle_pb_event).once.with expected_end_event
|
|
190
|
+
|
|
191
|
+
subject.handle_ami_event ami_event
|
|
192
|
+
end
|
|
193
|
+
end
|
|
171
194
|
end
|
|
172
195
|
end
|
|
173
196
|
end
|
|
@@ -7,7 +7,7 @@ module Punchblock
|
|
|
7
7
|
module Translator
|
|
8
8
|
describe Asterisk do
|
|
9
9
|
let(:ami_client) { double 'RubyAMI::Client' }
|
|
10
|
-
let(:connection) {
|
|
10
|
+
let(:connection) { Connection::Asterisk.new }
|
|
11
11
|
|
|
12
12
|
let(:translator) { Asterisk.new ami_client, connection }
|
|
13
13
|
|
|
@@ -16,6 +16,10 @@ module Punchblock
|
|
|
16
16
|
its(:ami_client) { should be ami_client }
|
|
17
17
|
its(:connection) { should be connection }
|
|
18
18
|
|
|
19
|
+
before do
|
|
20
|
+
connection.event_handler = ->(*) {}
|
|
21
|
+
end
|
|
22
|
+
|
|
19
23
|
after { translator.terminate if translator.alive? }
|
|
20
24
|
|
|
21
25
|
describe '#execute_command' do
|
|
@@ -210,6 +214,39 @@ module Punchblock
|
|
|
210
214
|
mock_call.should_receive(:dial).once.with command
|
|
211
215
|
subject.execute_global_command command
|
|
212
216
|
end
|
|
217
|
+
|
|
218
|
+
context 'when requesting a specific URI' do
|
|
219
|
+
let(:requested_uri) { connection.new_call_uri }
|
|
220
|
+
|
|
221
|
+
before do
|
|
222
|
+
command.uri = requested_uri
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
it "should assign the requested URI to the call" do
|
|
226
|
+
subject.execute_global_command command
|
|
227
|
+
subject.call_with_id(requested_uri).id.should == requested_uri
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
context 'and the requested URI already represents a known call' do
|
|
231
|
+
before do
|
|
232
|
+
earlier_command = Command::Dial.new to: 'SIP/1234', uri: requested_uri
|
|
233
|
+
earlier_command.request!
|
|
234
|
+
|
|
235
|
+
subject.execute_global_command earlier_command
|
|
236
|
+
|
|
237
|
+
@first_call = subject.call_with_id(requested_uri)
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
it "should set the command response to a conflict error" do
|
|
241
|
+
subject.execute_global_command command
|
|
242
|
+
command.response(0.1).should == ProtocolError.new.setup(:conflict, 'Call ID already in use')
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
it "should not replace the original call in the registry" do
|
|
246
|
+
subject.call_with_id(requested_uri).should be @first_call
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
end
|
|
213
250
|
end
|
|
214
251
|
|
|
215
252
|
context 'with an AMI action' do
|
data/spec/spec_helper.rb
CHANGED
|
@@ -5,6 +5,7 @@ require 'countdownlatch'
|
|
|
5
5
|
require 'logger'
|
|
6
6
|
require 'celluloid'
|
|
7
7
|
require 'coveralls'
|
|
8
|
+
require 'ruby_ami'
|
|
8
9
|
Coveralls.wear!
|
|
9
10
|
|
|
10
11
|
Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f}
|
|
@@ -27,6 +28,9 @@ RSpec.configure do |config|
|
|
|
27
28
|
config.before do
|
|
28
29
|
@uuid = SecureRandom.uuid
|
|
29
30
|
Punchblock.stub new_request_id: @uuid
|
|
31
|
+
|
|
32
|
+
@current_datetime = DateTime.now
|
|
33
|
+
DateTime.stub now: @current_datetime
|
|
30
34
|
end
|
|
31
35
|
|
|
32
36
|
config.after :each do
|
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: 2.
|
|
4
|
+
version: 2.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jason Goecke
|
|
@@ -10,7 +10,7 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date: 2014-
|
|
13
|
+
date: 2014-03-01 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: nokogiri
|
|
@@ -128,14 +128,14 @@ dependencies:
|
|
|
128
128
|
requirements:
|
|
129
129
|
- - "~>"
|
|
130
130
|
- !ruby/object:Gem::Version
|
|
131
|
-
version: '2.
|
|
131
|
+
version: '2.2'
|
|
132
132
|
type: :runtime
|
|
133
133
|
prerelease: false
|
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
|
135
135
|
requirements:
|
|
136
136
|
- - "~>"
|
|
137
137
|
- !ruby/object:Gem::Version
|
|
138
|
-
version: '2.
|
|
138
|
+
version: '2.2'
|
|
139
139
|
- !ruby/object:Gem::Dependency
|
|
140
140
|
name: ruby_fs
|
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|