punchblock 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +3 -1
- data/bin/punchblock-console +19 -1
- data/lib/punchblock.rb +11 -4
- data/lib/punchblock/command/reject.rb +6 -2
- data/lib/punchblock/component.rb +1 -0
- data/lib/punchblock/component/asterisk.rb +10 -0
- data/lib/punchblock/component/asterisk/agi.rb +11 -0
- data/lib/punchblock/component/asterisk/agi/command.rb +157 -0
- data/lib/punchblock/component/asterisk/ami.rb +12 -0
- data/lib/punchblock/component/asterisk/ami/action.rb +144 -0
- data/lib/punchblock/component/input.rb +2 -2
- data/lib/punchblock/connection.rb +1 -0
- data/lib/punchblock/connection/asterisk.rb +31 -0
- data/lib/punchblock/core_ext/celluloid.rb +11 -0
- data/lib/punchblock/event.rb +1 -1
- data/lib/punchblock/event/asterisk.rb +9 -0
- data/lib/punchblock/event/asterisk/ami.rb +11 -0
- data/lib/punchblock/event/asterisk/ami/event.rb +66 -0
- data/lib/punchblock/event/complete.rb +20 -0
- data/lib/punchblock/event/dtmf.rb +19 -0
- data/lib/punchblock/event/end.rb +23 -0
- data/lib/punchblock/event/offer.rb +23 -0
- data/lib/punchblock/header.rb +4 -44
- data/lib/punchblock/key_value_pair_node.rb +50 -0
- data/lib/punchblock/rayo_node.rb +1 -1
- data/lib/punchblock/ref.rb +6 -0
- data/lib/punchblock/translator.rb +7 -0
- data/lib/punchblock/translator/asterisk.rb +74 -0
- data/lib/punchblock/translator/asterisk/ami_action.rb +86 -0
- data/lib/punchblock/translator/asterisk/call.rb +25 -0
- data/lib/punchblock/translator/asterisk/component.rb +11 -0
- data/lib/punchblock/version.rb +1 -1
- data/punchblock.gemspec +3 -1
- data/spec/punchblock/command/accept_spec.rb +8 -0
- data/spec/punchblock/command/answer_spec.rb +8 -0
- data/spec/punchblock/command/dial_spec.rb +22 -2
- data/spec/punchblock/command/hangup_spec.rb +8 -0
- data/spec/punchblock/command/join_spec.rb +21 -0
- data/spec/punchblock/command/mute_spec.rb +8 -0
- data/spec/punchblock/command/redirect_spec.rb +21 -0
- data/spec/punchblock/command/reject_spec.rb +19 -8
- data/spec/punchblock/command/unjoin_spec.rb +17 -0
- data/spec/punchblock/command/unmute_spec.rb +8 -0
- data/spec/punchblock/component/asterisk/agi/command_spec.rb +102 -0
- data/spec/punchblock/component/asterisk/ami/action_spec.rb +118 -0
- data/spec/punchblock/component/input_spec.rb +40 -0
- data/spec/punchblock/component/output_spec.rb +28 -0
- data/spec/punchblock/component/record_spec.rb +27 -0
- data/spec/punchblock/connection/asterisk_spec.rb +69 -0
- data/spec/punchblock/event/asterisk/ami/event_spec.rb +60 -0
- data/spec/punchblock/event/complete_spec.rb +8 -0
- data/spec/punchblock/event/dtmf_spec.rb +8 -0
- data/spec/punchblock/event/end_spec.rb +8 -0
- data/spec/punchblock/event/offer_spec.rb +15 -2
- data/spec/punchblock/ref_spec.rb +6 -0
- data/spec/punchblock/translator/asterisk/ami_action_spec.rb +149 -0
- data/spec/punchblock/translator/asterisk/call_spec.rb +18 -0
- data/spec/punchblock/translator/asterisk/component_spec.rb +11 -0
- data/spec/punchblock/translator/asterisk_spec.rb +150 -0
- data/spec/spec_helper.rb +42 -0
- metadata +92 -42
- data/lib/punchblock/event/info.rb +0 -15
- data/spec/punchblock/event/info_spec.rb +0 -30
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
require 'celluloid'
|
|
2
|
+
require 'punchblock/core_ext/celluloid'
|
|
3
|
+
require 'ruby_ami'
|
|
4
|
+
|
|
5
|
+
module Punchblock
|
|
6
|
+
module Translator
|
|
7
|
+
class Asterisk
|
|
8
|
+
include Celluloid
|
|
9
|
+
|
|
10
|
+
extend ActiveSupport::Autoload
|
|
11
|
+
|
|
12
|
+
autoload :AMIAction
|
|
13
|
+
autoload :Call
|
|
14
|
+
autoload :Component
|
|
15
|
+
|
|
16
|
+
attr_reader :ami_client, :connection
|
|
17
|
+
|
|
18
|
+
def initialize(ami_client, connection)
|
|
19
|
+
@ami_client, @connection = ami_client, connection
|
|
20
|
+
@calls, @components = {}, {}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def register_call(call)
|
|
24
|
+
@calls[call.id] ||= call
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def call_with_id(call_id)
|
|
28
|
+
@calls[call_id]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def register_component(component)
|
|
32
|
+
@components[component.id] ||= component
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def component_with_id(component_id)
|
|
36
|
+
@components[component_id]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def handle_ami_event(event)
|
|
40
|
+
return unless event.is_a? RubyAMI::Event
|
|
41
|
+
connection.handle_event Event::Asterisk::AMI::Event.new(:name => event.name, :attributes => event.headers)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def execute_command(command, options = {})
|
|
45
|
+
command.request!
|
|
46
|
+
if command.call_id || options[:call_id]
|
|
47
|
+
command.call_id ||= options[:call_id]
|
|
48
|
+
if command.component_id || options[:component_id]
|
|
49
|
+
command.component_id ||= options[:component_id]
|
|
50
|
+
execute_component_command command
|
|
51
|
+
else
|
|
52
|
+
execute_call_command command
|
|
53
|
+
end
|
|
54
|
+
else
|
|
55
|
+
execute_global_command command
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def execute_call_command(command)
|
|
60
|
+
call_with_id(command.call_id).execute_command command
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def execute_component_command(command)
|
|
64
|
+
call_with_id(command.call_id).execute_component_command command
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def execute_global_command(command)
|
|
68
|
+
component = AMIAction.new command, ami_client
|
|
69
|
+
# register_component component
|
|
70
|
+
component.execute!
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
module Punchblock
|
|
2
|
+
module Translator
|
|
3
|
+
class Asterisk
|
|
4
|
+
class AMIAction < Component
|
|
5
|
+
attr_reader :action
|
|
6
|
+
|
|
7
|
+
def initialize(component_node, ami_client)
|
|
8
|
+
@component_node, @ami_client = component_node, ami_client
|
|
9
|
+
@action = create_action
|
|
10
|
+
@id = @action.action_id
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def execute
|
|
14
|
+
send_action
|
|
15
|
+
send_ref
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def create_action
|
|
21
|
+
headers = {}
|
|
22
|
+
@component_node.params_hash.each_pair do |key, value|
|
|
23
|
+
headers[key.to_s.capitalize] = value
|
|
24
|
+
end
|
|
25
|
+
RubyAMI::Action.new @component_node.name, headers do |response|
|
|
26
|
+
handle_response response
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def send_action
|
|
31
|
+
@ami_client.send_action @action
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def send_ref
|
|
35
|
+
@component_node.response = Ref.new :id => @action.action_id
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def handle_response(response)
|
|
39
|
+
case response
|
|
40
|
+
when RubyAMI::Error
|
|
41
|
+
send_event error_event(response)
|
|
42
|
+
when RubyAMI::Response
|
|
43
|
+
send_events
|
|
44
|
+
send_event complete_event(response)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def error_event(response)
|
|
49
|
+
Punchblock::Event::Complete.new.tap do |c|
|
|
50
|
+
c.reason = Punchblock::Event::Complete::Error.new :details => response.message
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def complete_event(response)
|
|
55
|
+
headers = response.headers
|
|
56
|
+
headers.merge! @extra_complete_attributes if @extra_complete_attributes
|
|
57
|
+
headers.delete 'ActionID'
|
|
58
|
+
Punchblock::Event::Complete.new.tap do |c|
|
|
59
|
+
c.reason = Punchblock::Component::Asterisk::AMI::Action::Complete::Success.new :message => headers.delete('Message'), :attributes => headers
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def send_events
|
|
64
|
+
return unless @action.has_causal_events?
|
|
65
|
+
@action.events.each do |e|
|
|
66
|
+
if e.name.downcase == @action.causal_event_terminator_name
|
|
67
|
+
@extra_complete_attributes = e.headers
|
|
68
|
+
else
|
|
69
|
+
send_event pb_event_from_ami_event(e)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def pb_event_from_ami_event(ami_event)
|
|
75
|
+
headers = ami_event.headers
|
|
76
|
+
headers.delete 'ActionID'
|
|
77
|
+
Event::Asterisk::AMI::Event.new :name => ami_event.name, :attributes => headers
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def send_event(event)
|
|
81
|
+
@component_node.add_event event
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Punchblock
|
|
2
|
+
module Translator
|
|
3
|
+
class Asterisk
|
|
4
|
+
class Call
|
|
5
|
+
include Celluloid
|
|
6
|
+
|
|
7
|
+
def initialize
|
|
8
|
+
@components = {}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def register_component(component)
|
|
12
|
+
@components[component.id] ||= component
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def component_with_id(component_id)
|
|
16
|
+
@components[component_id]
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def execute_component_command(command)
|
|
20
|
+
component_with_id(command.component_id).execute_command command
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
data/lib/punchblock/version.rb
CHANGED
data/punchblock.gemspec
CHANGED
|
@@ -29,9 +29,11 @@ Gem::Specification.new do |s|
|
|
|
29
29
|
s.add_runtime_dependency %q<state_machine>, [">= 1.0.1"]
|
|
30
30
|
s.add_runtime_dependency %q<future-resource>, [">= 0.0.2"]
|
|
31
31
|
s.add_runtime_dependency %q<has-guarded-handlers>, [">= 0.1.0"]
|
|
32
|
+
s.add_runtime_dependency %q<celluloid>, [">= 0.5.0"]
|
|
33
|
+
s.add_runtime_dependency %q<ruby_ami>, [">= 0.1.2"]
|
|
32
34
|
|
|
33
35
|
s.add_development_dependency %q<bundler>, ["~> 1.0.0"]
|
|
34
|
-
s.add_development_dependency %q<rspec>, ["
|
|
36
|
+
s.add_development_dependency %q<rspec>, [">= 2.5.0"]
|
|
35
37
|
s.add_development_dependency %q<ci_reporter>, [">= 1.6.3"]
|
|
36
38
|
s.add_development_dependency %q<yard>, ["~> 0.6.0"]
|
|
37
39
|
s.add_development_dependency %q<rcov>, [">= 0"]
|
|
@@ -8,6 +8,14 @@ module Punchblock
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
it_should_behave_like 'command_headers'
|
|
11
|
+
|
|
12
|
+
describe "from a stanza" do
|
|
13
|
+
let(:stanza) { '<accept xmlns="urn:xmpp:rayo:1"/>' }
|
|
14
|
+
|
|
15
|
+
subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
|
|
16
|
+
|
|
17
|
+
it { should be_instance_of Accept }
|
|
18
|
+
end
|
|
11
19
|
end
|
|
12
20
|
end
|
|
13
21
|
end # Punchblock
|
|
@@ -8,6 +8,14 @@ module Punchblock
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
it_should_behave_like 'command_headers'
|
|
11
|
+
|
|
12
|
+
describe "from a stanza" do
|
|
13
|
+
let(:stanza) { '<answer xmlns="urn:xmpp:rayo:1"/>' }
|
|
14
|
+
|
|
15
|
+
subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
|
|
16
|
+
|
|
17
|
+
it { should be_instance_of Answer }
|
|
18
|
+
end
|
|
11
19
|
end
|
|
12
20
|
end
|
|
13
21
|
end # Punchblock
|
|
@@ -14,9 +14,9 @@ module Punchblock
|
|
|
14
14
|
RayoNode.class_from_registration(:dial, 'urn:xmpp:rayo:1').should == Dial
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
let(:join_params) { {:other_call_id => 'abc123'} }
|
|
17
|
+
let(:join_params) { {:other_call_id => 'abc123'} }
|
|
19
18
|
|
|
19
|
+
describe "when setting options in initializer" do
|
|
20
20
|
subject { Dial.new :to => 'tel:+14155551212', :from => 'tel:+13035551212', :headers => { :x_skill => 'agent', :x_customer_id => 8877 }, :join => join_params }
|
|
21
21
|
|
|
22
22
|
it_should_behave_like 'command_headers'
|
|
@@ -26,6 +26,26 @@ module Punchblock
|
|
|
26
26
|
its(:join) { should == Join.new(join_params) }
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
describe "from a stanza" do
|
|
30
|
+
let :stanza do
|
|
31
|
+
<<-MESSAGE
|
|
32
|
+
<dial to='tel:+14155551212' from='tel:+13035551212' xmlns='urn:xmpp:rayo:1'>
|
|
33
|
+
<join call-id="abc123" />
|
|
34
|
+
<header name="x-skill" value="agent" />
|
|
35
|
+
<header name="x-customer-id" value="8877" />
|
|
36
|
+
</dial>
|
|
37
|
+
MESSAGE
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
|
|
41
|
+
|
|
42
|
+
it { should be_instance_of Dial }
|
|
43
|
+
|
|
44
|
+
its(:to) { should == 'tel:+14155551212' }
|
|
45
|
+
its(:from) { should == 'tel:+13035551212' }
|
|
46
|
+
its(:join) { should == Join.new(join_params) }
|
|
47
|
+
end
|
|
48
|
+
|
|
29
49
|
describe "#response=" do
|
|
30
50
|
before { subject.request! }
|
|
31
51
|
|
|
@@ -8,6 +8,14 @@ module Punchblock
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
it_should_behave_like 'command_headers'
|
|
11
|
+
|
|
12
|
+
describe "from a stanza" do
|
|
13
|
+
let(:stanza) { '<hangup xmlns="urn:xmpp:rayo:1"/>' }
|
|
14
|
+
|
|
15
|
+
subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
|
|
16
|
+
|
|
17
|
+
it { should be_instance_of Hangup }
|
|
18
|
+
end
|
|
11
19
|
end
|
|
12
20
|
end
|
|
13
21
|
end # Punchblock
|
|
@@ -16,6 +16,27 @@ module Punchblock
|
|
|
16
16
|
its(:direction) { should == :duplex }
|
|
17
17
|
its(:media) { should == :bridge }
|
|
18
18
|
end
|
|
19
|
+
|
|
20
|
+
describe "from a stanza" do
|
|
21
|
+
let :stanza do
|
|
22
|
+
<<-MESSAGE
|
|
23
|
+
<join xmlns="urn:xmpp:rayo:1"
|
|
24
|
+
call-id="abc123"
|
|
25
|
+
mixer-id="blah"
|
|
26
|
+
direction="duplex"
|
|
27
|
+
media="bridge" />
|
|
28
|
+
MESSAGE
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
|
|
32
|
+
|
|
33
|
+
it { should be_instance_of Join }
|
|
34
|
+
|
|
35
|
+
its(:other_call_id) { should == 'abc123' }
|
|
36
|
+
its(:mixer_id) { should == 'blah' }
|
|
37
|
+
its(:direction) { should == :duplex }
|
|
38
|
+
its(:media) { should == :bridge }
|
|
39
|
+
end
|
|
19
40
|
end
|
|
20
41
|
end
|
|
21
42
|
end # Punchblock
|
|
@@ -6,6 +6,14 @@ module Punchblock
|
|
|
6
6
|
it 'registers itself' do
|
|
7
7
|
RayoNode.class_from_registration(:mute, 'urn:xmpp:rayo:1').should == Mute
|
|
8
8
|
end
|
|
9
|
+
|
|
10
|
+
describe "from a stanza" do
|
|
11
|
+
let(:stanza) { '<mute xmlns="urn:xmpp:rayo:1"/>' }
|
|
12
|
+
|
|
13
|
+
subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
|
|
14
|
+
|
|
15
|
+
it { should be_instance_of Mute }
|
|
16
|
+
end
|
|
9
17
|
end
|
|
10
18
|
end
|
|
11
19
|
end # Punchblock
|
|
@@ -14,6 +14,27 @@ module Punchblock
|
|
|
14
14
|
|
|
15
15
|
its(:to) { should == 'tel:+14045551234' }
|
|
16
16
|
end
|
|
17
|
+
|
|
18
|
+
describe "from a stanza" do
|
|
19
|
+
let :stanza do
|
|
20
|
+
<<-MESSAGE
|
|
21
|
+
<redirect xmlns='urn:xmpp:rayo:1'
|
|
22
|
+
to='tel:+14045551234'>
|
|
23
|
+
<!-- Signaling (e.g. SIP) Headers -->
|
|
24
|
+
<header name="x-skill" value="agent" />
|
|
25
|
+
<header name="x-customer-id" value="8877" />
|
|
26
|
+
</redirect>
|
|
27
|
+
MESSAGE
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
|
|
31
|
+
|
|
32
|
+
it { should be_instance_of Redirect }
|
|
33
|
+
|
|
34
|
+
it_should_behave_like 'command_headers'
|
|
35
|
+
|
|
36
|
+
its(:to) { should == 'tel:+14045551234' }
|
|
37
|
+
end
|
|
17
38
|
end # Redirect
|
|
18
39
|
end # Command
|
|
19
40
|
end # Punchblock
|
|
@@ -15,19 +15,30 @@ module Punchblock
|
|
|
15
15
|
its(:reason) { should == :busy }
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
describe "from a stanza" do
|
|
19
|
+
let :stanza do
|
|
20
|
+
<<-MESSAGE
|
|
21
|
+
<reject xmlns='urn:xmpp:rayo:1'>
|
|
22
|
+
<busy />
|
|
23
|
+
<!-- Sample Headers (optional) -->
|
|
24
|
+
<header name="x-reason-internal" value="bad-skill" />
|
|
25
|
+
</reject>
|
|
26
|
+
MESSAGE
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
|
|
30
|
+
|
|
31
|
+
it { should be_instance_of Reject }
|
|
32
|
+
|
|
33
|
+
its(:reason) { should == :busy }
|
|
34
|
+
its(:headers_hash) { should == { :x_reason_internal => 'bad-skill' } }
|
|
35
|
+
end
|
|
36
|
+
|
|
18
37
|
describe "with the reason" do
|
|
19
38
|
[:decline, :busy, :error].each do |reason|
|
|
20
39
|
describe reason do
|
|
21
40
|
subject { Reject.new :reason => reason }
|
|
22
41
|
|
|
23
|
-
let :expected_message do
|
|
24
|
-
<<-MESSAGE
|
|
25
|
-
<reject xmlns="urn:xmpp:rayo:1">
|
|
26
|
-
<#{reason}/>
|
|
27
|
-
</reject>
|
|
28
|
-
MESSAGE
|
|
29
|
-
end
|
|
30
|
-
|
|
31
42
|
its(:reason) { should == reason }
|
|
32
43
|
end
|
|
33
44
|
end
|
|
@@ -14,6 +14,23 @@ module Punchblock
|
|
|
14
14
|
its(:other_call_id) { should == 'abc123' }
|
|
15
15
|
its(:mixer_id) { should == 'blah' }
|
|
16
16
|
end
|
|
17
|
+
|
|
18
|
+
describe "from a stanza" do
|
|
19
|
+
let :stanza do
|
|
20
|
+
<<-MESSAGE
|
|
21
|
+
<unjoin xmlns="urn:xmpp:rayo:1"
|
|
22
|
+
call-id="abc123"
|
|
23
|
+
mixer-id="blah" />
|
|
24
|
+
MESSAGE
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
subject { RayoNode.import parse_stanza(stanza).root, '9f00061', '1' }
|
|
28
|
+
|
|
29
|
+
it { should be_instance_of Unjoin }
|
|
30
|
+
|
|
31
|
+
its(:other_call_id) { should == 'abc123' }
|
|
32
|
+
its(:mixer_id) { should == 'blah' }
|
|
33
|
+
end
|
|
17
34
|
end
|
|
18
35
|
end
|
|
19
36
|
end # Punchblock
|