punchblock 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/CHANGELOG.md +5 -0
  2. data/LICENSE.txt +3 -1
  3. data/bin/punchblock-console +19 -1
  4. data/lib/punchblock.rb +11 -4
  5. data/lib/punchblock/command/reject.rb +6 -2
  6. data/lib/punchblock/component.rb +1 -0
  7. data/lib/punchblock/component/asterisk.rb +10 -0
  8. data/lib/punchblock/component/asterisk/agi.rb +11 -0
  9. data/lib/punchblock/component/asterisk/agi/command.rb +157 -0
  10. data/lib/punchblock/component/asterisk/ami.rb +12 -0
  11. data/lib/punchblock/component/asterisk/ami/action.rb +144 -0
  12. data/lib/punchblock/component/input.rb +2 -2
  13. data/lib/punchblock/connection.rb +1 -0
  14. data/lib/punchblock/connection/asterisk.rb +31 -0
  15. data/lib/punchblock/core_ext/celluloid.rb +11 -0
  16. data/lib/punchblock/event.rb +1 -1
  17. data/lib/punchblock/event/asterisk.rb +9 -0
  18. data/lib/punchblock/event/asterisk/ami.rb +11 -0
  19. data/lib/punchblock/event/asterisk/ami/event.rb +66 -0
  20. data/lib/punchblock/event/complete.rb +20 -0
  21. data/lib/punchblock/event/dtmf.rb +19 -0
  22. data/lib/punchblock/event/end.rb +23 -0
  23. data/lib/punchblock/event/offer.rb +23 -0
  24. data/lib/punchblock/header.rb +4 -44
  25. data/lib/punchblock/key_value_pair_node.rb +50 -0
  26. data/lib/punchblock/rayo_node.rb +1 -1
  27. data/lib/punchblock/ref.rb +6 -0
  28. data/lib/punchblock/translator.rb +7 -0
  29. data/lib/punchblock/translator/asterisk.rb +74 -0
  30. data/lib/punchblock/translator/asterisk/ami_action.rb +86 -0
  31. data/lib/punchblock/translator/asterisk/call.rb +25 -0
  32. data/lib/punchblock/translator/asterisk/component.rb +11 -0
  33. data/lib/punchblock/version.rb +1 -1
  34. data/punchblock.gemspec +3 -1
  35. data/spec/punchblock/command/accept_spec.rb +8 -0
  36. data/spec/punchblock/command/answer_spec.rb +8 -0
  37. data/spec/punchblock/command/dial_spec.rb +22 -2
  38. data/spec/punchblock/command/hangup_spec.rb +8 -0
  39. data/spec/punchblock/command/join_spec.rb +21 -0
  40. data/spec/punchblock/command/mute_spec.rb +8 -0
  41. data/spec/punchblock/command/redirect_spec.rb +21 -0
  42. data/spec/punchblock/command/reject_spec.rb +19 -8
  43. data/spec/punchblock/command/unjoin_spec.rb +17 -0
  44. data/spec/punchblock/command/unmute_spec.rb +8 -0
  45. data/spec/punchblock/component/asterisk/agi/command_spec.rb +102 -0
  46. data/spec/punchblock/component/asterisk/ami/action_spec.rb +118 -0
  47. data/spec/punchblock/component/input_spec.rb +40 -0
  48. data/spec/punchblock/component/output_spec.rb +28 -0
  49. data/spec/punchblock/component/record_spec.rb +27 -0
  50. data/spec/punchblock/connection/asterisk_spec.rb +69 -0
  51. data/spec/punchblock/event/asterisk/ami/event_spec.rb +60 -0
  52. data/spec/punchblock/event/complete_spec.rb +8 -0
  53. data/spec/punchblock/event/dtmf_spec.rb +8 -0
  54. data/spec/punchblock/event/end_spec.rb +8 -0
  55. data/spec/punchblock/event/offer_spec.rb +15 -2
  56. data/spec/punchblock/ref_spec.rb +6 -0
  57. data/spec/punchblock/translator/asterisk/ami_action_spec.rb +149 -0
  58. data/spec/punchblock/translator/asterisk/call_spec.rb +18 -0
  59. data/spec/punchblock/translator/asterisk/component_spec.rb +11 -0
  60. data/spec/punchblock/translator/asterisk_spec.rb +150 -0
  61. data/spec/spec_helper.rb +42 -0
  62. metadata +92 -42
  63. data/lib/punchblock/event/info.rb +0 -15
  64. data/spec/punchblock/event/info_spec.rb +0 -30
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # develop
2
2
 
3
+ # v0.6.0
4
+ * API change: Punchblock consumers now need to instantiate both a Connection and a Client (see the punchblock-console gem for an example)
5
+ * Feature: Added a Connection for Asterisk, utilising RubyAMI to open an AMI connection to Asterisk, and allowing AMI actions to be executed. AMI events are handled by the client event handler.
6
+ * Deprecation: The punchblock-console and the associated DSL are now deprecated and the punchblock-console gem should be used instead
7
+
3
8
  # v0.5.1
4
9
  API change: Connections now raise a Punchblock::Connection::Connected instance as an event, rather than the class itself
5
10
 
data/LICENSE.txt CHANGED
@@ -1,4 +1,6 @@
1
- Copyright (c) 2011 Jason Goecke
1
+ Copyright (C) 2011 Ben Klang
2
+ Copyright (C) 2011 Ben Langfeld
3
+ Copyright (C) 2011 Jason Goecke
2
4
 
3
5
  Permission is hereby granted, free of charge, to any person obtaining
4
6
  a copy of this software and associated documentation files (the
@@ -7,12 +7,20 @@ require 'pry'
7
7
  require 'logger'
8
8
  require 'optparse'
9
9
 
10
+ puts <<-DEPRECATION
11
+ ********************
12
+ WARNING: This utility is deprecated and will be removed from a future version of Punchblock without further notice. You should use the punchblock-console gem instead.
13
+ ********************
14
+ DEPRECATION
15
+
10
16
  include Punchblock
11
17
 
12
18
  Thread.abort_on_exception = true
13
19
 
14
20
  options = { :username => 'usera@127.0.0.1', :password => '1', :wire_log_file => 'log/rayo-wire.log', :transport_log_file => 'log/rayo-transport.log', :auto_reconnect => false }
15
21
 
22
+ connection_class = Connection::XMPP
23
+
16
24
  option_parser = OptionParser.new do |opts|
17
25
  opts.banner = "Usage: punchblock-console [-u usera@127.0.0.1] [-p abc123]"
18
26
  opts.on("-u", "--username USERNAME", String, "Specify the XMPP JID to connect to") do |u|
@@ -30,6 +38,11 @@ option_parser = OptionParser.new do |opts|
30
38
  opts.on("--transport-log-file log/transportlog.log", String, "Specify the file to which the transport log should be written") do |tlf|
31
39
  options[:transport_log_file] = tlf
32
40
  end
41
+ opts.on("--asterisk", "Use Asterisk") do |tlf|
42
+ connection_class = Connection::Asterisk
43
+ options[:host] = '127.0.0.1'
44
+ options[:port] = 5038
45
+ end
33
46
  opts.on_tail("-h", "--help", "Show this message") do
34
47
  puts opts
35
48
  exit
@@ -53,8 +66,9 @@ options[:wire_logger].debug "Starting up..."
53
66
  options[:transport_logger] = Logger.new options.delete(:transport_log_file)
54
67
  options[:transport_logger].level = Logger::DEBUG
55
68
  options[:transport_logger].debug "Starting up..."
69
+ options[:logger] = options[:wire_logger]
56
70
 
57
- connection = Connection::XMPP.new options
71
+ connection = connection_class.new options
58
72
  client = Client.new :connection => connection
59
73
 
60
74
  [:INT, :TERM].each do |signal|
@@ -87,6 +101,10 @@ Thread.new do
87
101
  puts "Waiting for a call..."
88
102
  next
89
103
  end
104
+ unless event.call_id
105
+ puts "Ad-hoc event: #{event.inspect}"
106
+ next
107
+ end
90
108
  puts "#{event.class} event for call: #{event.call_id}"
91
109
  case event
92
110
  when Event::Offer
data/lib/punchblock.rb CHANGED
@@ -34,15 +34,17 @@ module Punchblock
34
34
  autoload :MediaNode
35
35
  autoload :ProtocolError
36
36
  autoload :RayoNode
37
+ autoload :Translator
37
38
 
38
39
  ##
39
40
  # This exception may be raised if a transport error is detected.
40
41
  TransportError = Class.new StandardError
41
42
 
42
- BASE_RAYO_NAMESPACE = 'urn:xmpp:rayo'
43
- BASE_TROPO_NAMESPACE = 'urn:xmpp:tropo'
44
- RAYO_VERSION = '1'
45
- RAYO_NAMESPACES = {:core => [BASE_RAYO_NAMESPACE, RAYO_VERSION].compact.join(':')}
43
+ BASE_RAYO_NAMESPACE = 'urn:xmpp:rayo'
44
+ BASE_TROPO_NAMESPACE = 'urn:xmpp:tropo'
45
+ BASE_ASTERISK_NAMESPACE = 'urn:xmpp:rayo:asterisk'
46
+ RAYO_VERSION = '1'
47
+ RAYO_NAMESPACES = {:core => [BASE_RAYO_NAMESPACE, RAYO_VERSION].compact.join(':')}
46
48
 
47
49
  [:ext, :record, :output, :input].each do |ns|
48
50
  RAYO_NAMESPACES[ns] = [BASE_RAYO_NAMESPACE, ns.to_s, RAYO_VERSION].compact.join(':')
@@ -53,6 +55,11 @@ module Punchblock
53
55
  RAYO_NAMESPACES[ns] = [BASE_TROPO_NAMESPACE, ns.to_s, RAYO_VERSION].compact.join(':')
54
56
  RAYO_NAMESPACES[:"#{ns}_complete"] = [BASE_TROPO_NAMESPACE, ns.to_s, 'complete', RAYO_VERSION].compact.join(':')
55
57
  end
58
+
59
+ [:agi, :ami].each do |ns|
60
+ RAYO_NAMESPACES[ns] = [BASE_ASTERISK_NAMESPACE, ns.to_s, RAYO_VERSION].compact.join(':')
61
+ RAYO_NAMESPACES[:"#{ns}_complete"] = [BASE_ASTERISK_NAMESPACE, ns.to_s, 'complete', RAYO_VERSION].compact.join(':')
62
+ end
56
63
  end
57
64
 
58
65
  require 'punchblock/event'
@@ -26,8 +26,12 @@ module Punchblock
26
26
  #
27
27
  def self.new(options = {})
28
28
  super().tap do |new_node|
29
- new_node.reason = options[:reason] || :decline
30
- new_node.headers = options[:headers]
29
+ case options
30
+ when Nokogiri::XML::Node
31
+ new_node.inherit options
32
+ when Hash
33
+ options.each_pair { |k,v| new_node.send :"#{k}=", v }
34
+ end
31
35
  end
32
36
  end
33
37
 
@@ -2,6 +2,7 @@ module Punchblock
2
2
  module Component
3
3
  extend ActiveSupport::Autoload
4
4
 
5
+ autoload :Asterisk
5
6
  autoload :Input
6
7
  autoload :Output
7
8
  autoload :Record
@@ -0,0 +1,10 @@
1
+ module Punchblock
2
+ module Component
3
+ module Asterisk
4
+ extend ActiveSupport::Autoload
5
+
6
+ autoload :AGI
7
+ autoload :AMI
8
+ end
9
+ end # Command
10
+ end # Punchblock
@@ -0,0 +1,11 @@
1
+ module Punchblock
2
+ module Component
3
+ module Asterisk
4
+ module AGI
5
+ extend ActiveSupport::Autoload
6
+
7
+ autoload :Command
8
+ end # AGI
9
+ end # Asterisk
10
+ end # Component
11
+ end # Punchblock
@@ -0,0 +1,157 @@
1
+ module Punchblock
2
+ module Component
3
+ module Asterisk
4
+ module AGI
5
+ class Command < ComponentNode
6
+ register :command, :agi
7
+
8
+ def self.new(options = {})
9
+ super().tap do |new_node|
10
+ options.each_pair { |k,v| new_node.send :"#{k}=", v }
11
+ end
12
+ end
13
+
14
+ def name
15
+ read_attr :name
16
+ end
17
+
18
+ def name=(other)
19
+ write_attr :name, other
20
+ end
21
+
22
+ ##
23
+ # @return [Array[String]] array of values of params
24
+ #
25
+ def params_array
26
+ params.map &:value
27
+ end
28
+
29
+ ##
30
+ # @return [Array[Param]] params
31
+ #
32
+ def params
33
+ find('//ns:param', :ns => self.class.registered_ns).map do |i|
34
+ Param.new i
35
+ end
36
+ end
37
+
38
+ ##
39
+ # @param [Hash, Array] params A hash of key-value param pairs, or an array of Param objects
40
+ #
41
+ def params=(params)
42
+ find('//ns:param', :ns => self.class.registered_ns).each &:remove
43
+ [params].flatten.each { |i| self << Param.new(i) } if params.is_a? Array
44
+ end
45
+
46
+ def inspect_attributes # :nodoc:
47
+ [:name, :params_array] + super
48
+ end
49
+
50
+ class Param < RayoNode
51
+ ##
52
+ # @param [String] name
53
+ # @param [String] value
54
+ #
55
+ def self.new(value)
56
+ super(:param).tap do |new_node|
57
+ case value
58
+ when Nokogiri::XML::Node
59
+ new_node.inherit value
60
+ else
61
+ new_node.value = value
62
+ end
63
+ end
64
+ end
65
+
66
+ # The Header's value
67
+ # @return [String]
68
+ def value
69
+ read_attr :value
70
+ end
71
+
72
+ # Set the Header's value
73
+ # @param [String] value the new value for the param
74
+ def value=(value)
75
+ write_attr :value, value
76
+ end
77
+
78
+ def inspect_attributes # :nodoc:
79
+ [:value] + super
80
+ end
81
+ end
82
+
83
+ class Complete
84
+ class Success < Event::Complete::Reason
85
+ register :success, :agi_complete
86
+
87
+ def self.new(options = {})
88
+ super().tap do |new_node|
89
+ case options
90
+ when Nokogiri::XML::Node
91
+ new_node.inherit options
92
+ else
93
+ options.each_pair { |k,v| new_node.send :"#{k}=", v }
94
+ end
95
+ end
96
+ end
97
+
98
+ def node_with_name(name)
99
+ n = if self.class.registered_ns
100
+ find_first "ns:#{name}", :ns => self.class.registered_ns
101
+ else
102
+ find_first name
103
+ end
104
+
105
+ unless n
106
+ self << (n = RayoNode.new(name, self.document))
107
+ n.namespace = self.class.registered_ns
108
+ end
109
+ n
110
+ end
111
+
112
+ def code_node
113
+ node_with_name 'code'
114
+ end
115
+
116
+ def result_node
117
+ node_with_name 'result'
118
+ end
119
+
120
+ def data_node
121
+ node_with_name 'data'
122
+ end
123
+
124
+ def code
125
+ code_node.text.to_i
126
+ end
127
+
128
+ def code=(other)
129
+ code_node.content = other
130
+ end
131
+
132
+ def result
133
+ result_node.text.to_i
134
+ end
135
+
136
+ def result=(other)
137
+ result_node.content = other
138
+ end
139
+
140
+ def data
141
+ data_node.text
142
+ end
143
+
144
+ def data=(other)
145
+ data_node.content = other
146
+ end
147
+
148
+ def inspect_attributes
149
+ [:code, :result, :data]
150
+ end
151
+ end
152
+ end # Complete
153
+ end # Command
154
+ end # AGI
155
+ end # Asterisk
156
+ end # Component
157
+ end # Punchblock
@@ -0,0 +1,12 @@
1
+ module Punchblock
2
+ module Component
3
+ module Asterisk
4
+ module AMI
5
+ extend ActiveSupport::Autoload
6
+
7
+ autoload :Action
8
+ end
9
+ end
10
+ end # Command
11
+ end # Punchblock
12
+
@@ -0,0 +1,144 @@
1
+ require 'punchblock/key_value_pair_node'
2
+
3
+ module Punchblock
4
+ module Component
5
+ module Asterisk
6
+ module AMI
7
+ class Action < ComponentNode
8
+ register :action, :ami
9
+
10
+ def self.new(options = {})
11
+ super().tap do |new_node|
12
+ options.each_pair { |k,v| new_node.send :"#{k}=", v }
13
+ end
14
+ end
15
+
16
+ def name
17
+ read_attr :name
18
+ end
19
+
20
+ def name=(other)
21
+ write_attr :name, other
22
+ end
23
+
24
+ ##
25
+ # @return [Hash] hash of key-value pairs of params
26
+ #
27
+ def params_hash
28
+ params.inject({}) do |hash, param|
29
+ hash[param.name] = param.value
30
+ hash
31
+ end
32
+ end
33
+
34
+ ##
35
+ # @return [Array[Param]] params
36
+ #
37
+ def params
38
+ find('//ns:param', :ns => self.class.registered_ns).map do |i|
39
+ Param.new i
40
+ end
41
+ end
42
+
43
+ ##
44
+ # @param [Hash, Array] params A hash of key-value param pairs, or an array of Param objects
45
+ #
46
+ def params=(params)
47
+ find('//ns:param', :ns => self.class.registered_ns).each &:remove
48
+ if params.is_a? Hash
49
+ params.each_pair { |k,v| self << Param.new(k, v) }
50
+ elsif params.is_a? Array
51
+ [params].flatten.each { |i| self << Param.new(i) }
52
+ end
53
+ end
54
+
55
+ def inspect_attributes # :nodoc:
56
+ [:name] + super
57
+ end
58
+
59
+ class Param < RayoNode
60
+ include KeyValuePairNode
61
+ end
62
+
63
+ class Complete
64
+ class Success < Event::Complete::Reason
65
+ register :success, :ami_complete
66
+
67
+ def self.new(options = {})
68
+ super().tap do |new_node|
69
+ case options
70
+ when Nokogiri::XML::Node
71
+ new_node.inherit options
72
+ else
73
+ options.each_pair { |k,v| new_node.send :"#{k}=", v }
74
+ end
75
+ end
76
+ end
77
+
78
+ def message_node
79
+ mn = if self.class.registered_ns
80
+ find_first 'ns:message', :ns => self.class.registered_ns
81
+ else
82
+ find_first 'message'
83
+ end
84
+
85
+ unless mn
86
+ self << (mn = RayoNode.new('message', self.document))
87
+ mn.namespace = self.class.registered_ns
88
+ end
89
+ mn
90
+ end
91
+
92
+ def message
93
+ message_node.text
94
+ end
95
+
96
+ def message=(other)
97
+ message_node.content = other
98
+ end
99
+
100
+ ##
101
+ # @return [Hash] hash of key-value pairs of attributes
102
+ #
103
+ def attributes_hash
104
+ attributes.inject({}) do |hash, attribute|
105
+ hash[attribute.name] = attribute.value
106
+ hash
107
+ end
108
+ end
109
+
110
+ ##
111
+ # @return [Array[Attribute]] attributes
112
+ #
113
+ def attributes
114
+ find('//ns:attribute', :ns => self.class.registered_ns).map do |i|
115
+ Attribute.new i
116
+ end
117
+ end
118
+
119
+ ##
120
+ # @param [Hash, Array] attributes A hash of key-value attribute pairs, or an array of Attribute objects
121
+ #
122
+ def attributes=(attributes)
123
+ find('//ns:attribute', :ns => self.class.registered_ns).each &:remove
124
+ if attributes.is_a? Hash
125
+ attributes.each_pair { |k,v| self << Attribute.new(k, v) }
126
+ elsif attributes.is_a? Array
127
+ [attributes].flatten.each { |i| self << Attribute.new(i) }
128
+ end
129
+ end
130
+
131
+ def inspect_attributes
132
+ [:message, :attributes_hash]
133
+ end
134
+ end
135
+
136
+ class Attribute < RayoNode
137
+ include KeyValuePairNode
138
+ end
139
+ end # Complete
140
+ end # Action
141
+ end # AMI
142
+ end # Asterisk
143
+ end # Component
144
+ end # Punchblock