punchblock 0.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.
Files changed (94) hide show
  1. data/.document +5 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/Gemfile +2 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.markdown +31 -0
  7. data/Rakefile +23 -0
  8. data/assets/ozone/ask-1.0.xsd +56 -0
  9. data/assets/ozone/conference-1.0.xsd +17 -0
  10. data/assets/ozone/ozone-1.0.xsd +127 -0
  11. data/assets/ozone/say-1.0.xsd +24 -0
  12. data/assets/ozone/transfer-1.0.xsd +32 -0
  13. data/bin/punchblock-console +125 -0
  14. data/lib/punchblock/command/accept.rb +30 -0
  15. data/lib/punchblock/command/answer.rb +30 -0
  16. data/lib/punchblock/command/dial.rb +88 -0
  17. data/lib/punchblock/command/hangup.rb +25 -0
  18. data/lib/punchblock/command/join.rb +81 -0
  19. data/lib/punchblock/command/mute.rb +7 -0
  20. data/lib/punchblock/command/redirect.rb +49 -0
  21. data/lib/punchblock/command/reject.rb +61 -0
  22. data/lib/punchblock/command/unjoin.rb +50 -0
  23. data/lib/punchblock/command/unmute.rb +7 -0
  24. data/lib/punchblock/command.rb +16 -0
  25. data/lib/punchblock/command_node.rb +46 -0
  26. data/lib/punchblock/component/input.rb +320 -0
  27. data/lib/punchblock/component/output.rb +449 -0
  28. data/lib/punchblock/component/record.rb +216 -0
  29. data/lib/punchblock/component/tropo/ask.rb +197 -0
  30. data/lib/punchblock/component/tropo/conference.rb +328 -0
  31. data/lib/punchblock/component/tropo/say.rb +113 -0
  32. data/lib/punchblock/component/tropo/transfer.rb +178 -0
  33. data/lib/punchblock/component/tropo.rb +12 -0
  34. data/lib/punchblock/component.rb +73 -0
  35. data/lib/punchblock/connection.rb +209 -0
  36. data/lib/punchblock/core_ext/blather/stanza/presence.rb +11 -0
  37. data/lib/punchblock/core_ext/blather/stanza.rb +26 -0
  38. data/lib/punchblock/dsl.rb +46 -0
  39. data/lib/punchblock/event/answered.rb +7 -0
  40. data/lib/punchblock/event/complete.rb +65 -0
  41. data/lib/punchblock/event/dtmf.rb +19 -0
  42. data/lib/punchblock/event/end.rb +15 -0
  43. data/lib/punchblock/event/info.rb +15 -0
  44. data/lib/punchblock/event/joined.rb +50 -0
  45. data/lib/punchblock/event/offer.rb +29 -0
  46. data/lib/punchblock/event/ringing.rb +7 -0
  47. data/lib/punchblock/event/unjoined.rb +50 -0
  48. data/lib/punchblock/event.rb +16 -0
  49. data/lib/punchblock/generic_connection.rb +18 -0
  50. data/lib/punchblock/has_headers.rb +34 -0
  51. data/lib/punchblock/header.rb +47 -0
  52. data/lib/punchblock/media_container.rb +39 -0
  53. data/lib/punchblock/media_node.rb +17 -0
  54. data/lib/punchblock/protocol_error.rb +16 -0
  55. data/lib/punchblock/rayo_node.rb +88 -0
  56. data/lib/punchblock/ref.rb +26 -0
  57. data/lib/punchblock/version.rb +3 -0
  58. data/lib/punchblock.rb +42 -0
  59. data/log/.gitkeep +0 -0
  60. data/punchblock.gemspec +42 -0
  61. data/spec/punchblock/command/accept_spec.rb +13 -0
  62. data/spec/punchblock/command/answer_spec.rb +13 -0
  63. data/spec/punchblock/command/dial_spec.rb +54 -0
  64. data/spec/punchblock/command/hangup_spec.rb +13 -0
  65. data/spec/punchblock/command/join_spec.rb +21 -0
  66. data/spec/punchblock/command/mute_spec.rb +11 -0
  67. data/spec/punchblock/command/redirect_spec.rb +19 -0
  68. data/spec/punchblock/command/reject_spec.rb +43 -0
  69. data/spec/punchblock/command/unjoin_spec.rb +19 -0
  70. data/spec/punchblock/command/unmute_spec.rb +11 -0
  71. data/spec/punchblock/command_node_spec.rb +80 -0
  72. data/spec/punchblock/component/input_spec.rb +188 -0
  73. data/spec/punchblock/component/output_spec.rb +531 -0
  74. data/spec/punchblock/component/record_spec.rb +235 -0
  75. data/spec/punchblock/component/tropo/ask_spec.rb +183 -0
  76. data/spec/punchblock/component/tropo/conference_spec.rb +360 -0
  77. data/spec/punchblock/component/tropo/say_spec.rb +171 -0
  78. data/spec/punchblock/component/tropo/transfer_spec.rb +153 -0
  79. data/spec/punchblock/component_spec.rb +126 -0
  80. data/spec/punchblock/connection_spec.rb +194 -0
  81. data/spec/punchblock/event/answered_spec.rb +23 -0
  82. data/spec/punchblock/event/complete_spec.rb +80 -0
  83. data/spec/punchblock/event/dtmf_spec.rb +24 -0
  84. data/spec/punchblock/event/end_spec.rb +30 -0
  85. data/spec/punchblock/event/info_spec.rb +30 -0
  86. data/spec/punchblock/event/joined_spec.rb +32 -0
  87. data/spec/punchblock/event/offer_spec.rb +35 -0
  88. data/spec/punchblock/event/ringing_spec.rb +23 -0
  89. data/spec/punchblock/event/unjoined_spec.rb +32 -0
  90. data/spec/punchblock/header_spec.rb +44 -0
  91. data/spec/punchblock/protocol_error_spec.rb +9 -0
  92. data/spec/punchblock/ref_spec.rb +21 -0
  93. data/spec/spec_helper.rb +43 -0
  94. metadata +353 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ .*.swp
2
+ .*.swo
3
+ *.gem
4
+ .bundle
5
+ Gemfile.lock
6
+ pkg/*
7
+ .rvmrc
8
+ .yardoc
9
+ doc
10
+ log/*
11
+ spec/reports
12
+ vendor
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --colour
3
+ --tty
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Jason Goecke
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,31 @@
1
+ PUNCHBLOCK
2
+ ==========
3
+
4
+ Punchblock is a middleware library for telephony applications. Like Rack is to
5
+ Rails and Sinatra, Punchblock provides a consistent API on top of several
6
+ underlying third-party call control protocols.
7
+
8
+ Design
9
+ ------
10
+
11
+ In the same spirit that inspired Rack, the Punchblock library is envisioned to
12
+ be the single library for call control wiring. Frameworks and applications may
13
+ take advantage of Punchblock's single API to write powerful code for managing
14
+ calls. Punchblock is not and will not be an application framework; rather it
15
+ only surfaces the various protocols and presents a consistent interface to its
16
+ consumers.
17
+
18
+ Supported Protocols
19
+ -------------------
20
+
21
+ Punchblock is still in very early stages so this list
22
+ is what we plan to support:
23
+
24
+ * Rayo (Tropo, Voxeo Prism)
25
+ * AGI/AMI (Asterisk)
26
+ * EventSocket (FreeSWITCH, maybe)
27
+
28
+ Installation
29
+ ------------
30
+
31
+ gem install punchblock
data/Rakefile ADDED
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ require 'rspec/core'
6
+ require 'rspec/core/rake_task'
7
+ require 'ci/reporter/rake/rspec'
8
+ RSpec::Core::RakeTask.new(:spec) do |spec|
9
+ spec.pattern = 'spec/**/*_spec.rb'
10
+ spec.rspec_opts = '--color'
11
+ end
12
+
13
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
14
+ spec.pattern = 'spec/**/*_spec.rb'
15
+ spec.rcov = true
16
+ spec.rspec_opts = '--color'
17
+ end
18
+
19
+ task :default => :spec
20
+ task :hudson => ['ci:setup:rspec', :spec]
21
+
22
+ require 'yard'
23
+ YARD::Rake::YardocTask.new
@@ -0,0 +1,56 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <schema xmlns="http://www.w3.org/2001/XMLSchema"
3
+ targetNamespace="urn:xmpp:ozone:ask:1"
4
+ xmlns:tns="urn:xmpp:ozone:ask:1"
5
+ elementFormDefault="qualified">
6
+
7
+ <element name="ask" type="tns:askType" />
8
+
9
+ <complexType name="askType">
10
+ <sequence>
11
+ <element name="prompt" type="tns:promptItemsType" />
12
+ <element name="choices" type="tns:choicesType" />
13
+ </sequence>
14
+ <attribute name="bargein" type="boolean" />
15
+ <attribute name="min-confidence" type="tns:percentageType" />
16
+ <attribute name="mode" type="tns:choicesModeType" />
17
+ <attribute name="recognizer" type="string" />
18
+ <attribute name="voice" type="string" />
19
+ <attribute name="terminator" type="string" />
20
+ <attribute name="timeout" type="float" />
21
+ </complexType>
22
+
23
+ <!-- Utility Types -->
24
+
25
+ <complexType name="promptItemsType">
26
+ <choice minOccurs="1" maxOccurs="unbounded">
27
+ <element name="audio" type="tns:audioReferenceType" />
28
+ <any namespace="http://www.w3.org/2001/10/synthesis" />
29
+ </choice>
30
+ </complexType>
31
+
32
+ <complexType name="audioReferenceType">
33
+ <attribute name="url" type="anyURI" />
34
+ </complexType>
35
+
36
+ <complexType name="choicesType" mixed="true">
37
+ <attribute name="content-type" type="string" />
38
+ <attribute name="url" type="anyURI" />
39
+ </complexType>
40
+
41
+ <simpleType name="percentageType">
42
+ <restriction base="int">
43
+ <minInclusive value="0" />
44
+ <maxInclusive value="100" />
45
+ </restriction>
46
+ </simpleType>
47
+
48
+ <simpleType name="choicesModeType">
49
+ <restriction base="NCName">
50
+ <enumeration value="DTMF" />
51
+ <enumeration value="SPEECH" />
52
+ <enumeration value="ANY" />
53
+ </restriction>
54
+ </simpleType>
55
+
56
+ </schema>
@@ -0,0 +1,17 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <schema xmlns="http://www.w3.org/2001/XMLSchema"
3
+ targetNamespace="urn:xmpp:ozone:conference:1"
4
+ xmlns:tns="urn:xmpp:ozone:conference:1"
5
+ elementFormDefault="qualified">
6
+
7
+ <element name="conference" type="tns:conferenceType" />
8
+
9
+ <complexType name="conferenceType">
10
+ <attribute name="id" type="string" use="required" />
11
+ <attribute name="beep" type="boolean" use="optional" />
12
+ <attribute name="mute" type="boolean" use="optional" />
13
+ <attribute name="tone-passthrough" type="boolean" use="optional" />
14
+ <attribute name="terminator" type="string" use="optional" />
15
+ </complexType>
16
+
17
+ </schema>
@@ -0,0 +1,127 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <schema xmlns="http://www.w3.org/2001/XMLSchema"
3
+ targetNamespace="urn:xmpp:ozone:1"
4
+ xmlns:tns="urn:xmpp:ozone:1"
5
+ elementFormDefault="qualified">
6
+
7
+
8
+ <!-- Events -->
9
+
10
+ <element name="offer" type="tns:offerType" />
11
+ <element name="info" type="tns:infoType" />
12
+ <element name="end" type="tns:endType" />
13
+
14
+ <complexType name="offerType">
15
+ <sequence>
16
+ <element name="header" type="tns:headerType" minOccurs="0" maxOccurs="unbounded" />
17
+ </sequence>
18
+ <attribute name="callId" type="anyURI" use="required" />
19
+ <attribute name="timestamp" type="dateTime" use="required" />
20
+ <attribute name="to" type="anyURI" use="required" />
21
+ <attribute name="from" type="anyURI" use="optional" />
22
+ </complexType>
23
+
24
+ <complexType name="infoType">
25
+ <choice>
26
+ <element name="answer" type="tns:empty" />
27
+ <element name="ring" type="tns:empty" />
28
+ </choice>
29
+ </complexType>
30
+
31
+ <complexType name="endType">
32
+ <sequence>
33
+ <element name="header" type="tns:headerType" minOccurs="0" maxOccurs="unbounded" />
34
+ <choice>
35
+ <element name="hangup" type="tns:empty" />
36
+ <element name="timeout" type="tns:empty" />
37
+ <element name="busy" type="tns:empty" />
38
+ <element name="reject" type="tns:empty" />
39
+ <element name="error" type="tns:endErrorType" />
40
+ </choice>
41
+ </sequence>
42
+ </complexType>
43
+
44
+ <complexType name="endErrorType" mixed="true">
45
+ <attribute name="code" type="int" />
46
+ </complexType>
47
+
48
+ <!-- Commands -->
49
+
50
+ <element name="bind" type="tns:empty" />
51
+ <element name="unbind" type="tns:empty" />
52
+
53
+ <element name="answer" type="tns:answerType" />
54
+ <element name="redirect" type="tns:redirectType" />
55
+ <element name="reject" type="tns:rejectType" />
56
+ <element name="hangup" type="tns:hangupType" />
57
+ <element name="call" type="tns:callType" />
58
+
59
+ <complexType name="answerType">
60
+ <attribute name="timeout" type="float" />
61
+ </complexType>
62
+
63
+ <complexType name="redirectType">
64
+ <attribute name="to" type="anyURI" />
65
+ </complexType>
66
+
67
+ <complexType name="rejectType" mixed="true">
68
+ <attribute name="code" type="int" />
69
+ </complexType>
70
+
71
+ <complexType name="hangupType">
72
+ <sequence>
73
+ <element name="header" type="tns:headerType" minOccurs="0" maxOccurs="unbounded" />
74
+ </sequence>
75
+ </complexType>
76
+
77
+ <complexType name="callType">
78
+ <sequence>
79
+ <element name="header" type="tns:headerType" minOccurs="0" maxOccurs="unbounded" />
80
+ <element name="recording" type="tns:recordingType" minOccurs="0" maxOccurs="1" />
81
+ </sequence>
82
+ <attribute name="jid" type="string" use="optional">
83
+ <annotation>
84
+ <documentation>
85
+ Required when used as an IQ result
86
+ </documentation>
87
+ </annotation>
88
+ </attribute>
89
+ <attribute name="to" type="anyURI" use="required" />
90
+ <attribute name="from" type="anyURI" use="optional" />
91
+ <attribute name="timeout" type="float" use="optional" />
92
+ </complexType>
93
+
94
+ <!-- Utility Types -->
95
+
96
+ <complexType name="recordingType">
97
+ <sequence>
98
+ <element name="credentials" type="tns:credentialsType" minOccurs="0" />
99
+ </sequence>
100
+ <attribute name="uri" type="anyURI" use="required" />
101
+ <attribute name="format" type="string" use="optional" />
102
+ <attribute name="httpMethod" type="tns:httpMethodType" use="optional" />
103
+ </complexType>
104
+
105
+ <complexType name="credentialsType">
106
+ <attribute name="username" type="string" use="required" />
107
+ <attribute name="password" type="string" use="required" />
108
+ </complexType>
109
+
110
+ <simpleType name="httpMethodType">
111
+ <restriction base="NCName">
112
+ <enumeration value="PUT" />
113
+ <enumeration value="POST" />
114
+ </restriction>
115
+ </simpleType>
116
+
117
+ <complexType name="headerType" mixed="true">
118
+ <attribute name="name" type="anyURI" />
119
+ </complexType>
120
+
121
+ <simpleType name="empty">
122
+ <restriction base="string">
123
+ <enumeration value='' />
124
+ </restriction>
125
+ </simpleType>
126
+
127
+ </schema>
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <schema xmlns="http://www.w3.org/2001/XMLSchema"
3
+ targetNamespace="urn:xmpp:ozone:say:1"
4
+ xmlns:tns="urn:xmpp:ozone:say:1"
5
+ elementFormDefault="qualified">
6
+
7
+ <element name="say" type="tns:sayType" />
8
+ <element name="complete" />
9
+ <element name="stop" />
10
+
11
+ <complexType name="sayType">
12
+ <choice minOccurs="1" maxOccurs="unbounded">
13
+ <element name="audio" type="tns:audioReferenceType" />
14
+ <any namespace="http://www.w3.org/2001/10/synthesis" />
15
+ </choice>
16
+ <attribute name="id" type="string" />
17
+ <attribute name="voice" type="string" />
18
+ </complexType>
19
+
20
+ <complexType name="audioReferenceType">
21
+ <attribute name="url" type="anyURI" />
22
+ </complexType>
23
+
24
+ </schema>
@@ -0,0 +1,32 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <schema xmlns="http://www.w3.org/2001/XMLSchema"
3
+ targetNamespace="urn:xmpp:ozone:say:1"
4
+ xmlns:tns="urn:xmpp:ozone:say:1"
5
+ elementFormDefault="qualified">
6
+
7
+ <element name="transfer" type="tns:transferType" />
8
+
9
+ <complexType name="transferType">
10
+ <sequence>
11
+ <element name="header" type="tns:headerType" minOccurs="0" maxOccurs="unbounded" />
12
+ <choice minOccurs="1" maxOccurs="unbounded">
13
+ <element name="audio" type="tns:audioReferenceType" />
14
+ <any namespace="http://www.w3.org/2001/10/synthesis" />
15
+ </choice>
16
+ </sequence>
17
+ <attribute name="to" type="anyURI" use="required" />
18
+ <attribute name="from" type="anyURI" use="optional" />
19
+ <attribute name="timeout" type="float" use="optional" />
20
+ <attribute name="answer-on-media" type="boolean" use="optional" />
21
+ <attribute name="terminator" type="string" use="optional" />
22
+ </complexType>
23
+
24
+ <complexType name="audioReferenceType">
25
+ <attribute name="url" type="anyURI" />
26
+ </complexType>
27
+
28
+ <complexType name="headerType" mixed="true">
29
+ <attribute name="name" type="anyURI" />
30
+ </complexType>
31
+
32
+ </schema>
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env ruby
2
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+ require 'punchblock'
6
+ require 'pry'
7
+ require 'logger'
8
+ require 'optparse'
9
+
10
+ include Punchblock
11
+
12
+ Thread.abort_on_exception = true
13
+
14
+ 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
+
16
+ option_parser = OptionParser.new do |opts|
17
+ opts.banner = "Usage: punchblock-console [-u usera@127.0.0.1] [-p abc123]"
18
+ opts.on("-u", "--username USERNAME", String, "Specify the XMPP JID to connect to") do |u|
19
+ options[:username] = u
20
+ end
21
+ opts.on("-p", "--password PASSWORD", String, "Specify the XMPP password to use") do |p|
22
+ options[:password] = p
23
+ end
24
+ opts.on("-d", "--rayo-domain DOMAIN", String, "Specify the domain Rayo is running on") do |d|
25
+ options[:rayo_domain] = d
26
+ end
27
+ opts.on("--wire-log-file log/wirelog.log", String, "Specify the file to which the wire log should be written") do |wlf|
28
+ options[:wire_log_file] = wlf
29
+ end
30
+ opts.on("--transport-log-file log/transportlog.log", String, "Specify the file to which the transport log should be written") do |tlf|
31
+ options[:transport_log_file] = tlf
32
+ end
33
+ opts.on_tail("-h", "--help", "Show this message") do
34
+ puts opts
35
+ exit
36
+ end
37
+ opts.on_tail("-v", "--version", "Show version") do
38
+ puts VERSION
39
+ exit
40
+ end
41
+ end
42
+
43
+ begin
44
+ option_parser.parse!
45
+ rescue
46
+ puts $!
47
+ option_parser.parse '--help'
48
+ end
49
+
50
+ options[:wire_logger] = Logger.new options.delete(:wire_log_file)
51
+ options[:wire_logger].level = Logger::DEBUG
52
+ options[:wire_logger].debug "Starting up..."
53
+ options[:transport_logger] = Logger.new options.delete(:transport_log_file)
54
+ options[:transport_logger].level = Logger::DEBUG
55
+ options[:transport_logger].debug "Starting up..."
56
+
57
+ connection = Connection.new options
58
+
59
+ [:INT, :TERM].each do |signal|
60
+ trap signal do
61
+ puts "Shutting down!"
62
+ connection.stop
63
+ end
64
+ end
65
+
66
+ connection_thread = Thread.new do
67
+ begin
68
+ connection.run
69
+ rescue => e
70
+ puts "Exception in XMPP thread! #{e.message}"
71
+ puts e.backtrace.join("\t\n")
72
+ end
73
+ end
74
+
75
+ CALL_QUEUES = {}
76
+ INFO_EVENT_QUEUE = Queue.new
77
+
78
+ ### DISPATCHER THREAD
79
+ # This thread multiplexes the event stream from the underlying connection handler and routes them
80
+ # to the correct queue for each call. It also starts a call handler, the run_call method) after creating
81
+ # the queue.
82
+ Thread.new do
83
+ loop do
84
+ event = connection.event_queue.pop
85
+ if event == connection.connected
86
+ puts event
87
+ puts "Waiting for a call..."
88
+ next
89
+ end
90
+ puts "#{event.class} event for call: #{event.call_id}"
91
+ case event
92
+ when Event::Offer
93
+ raise "Duplicate call ID for #{event.call_id}" if CALL_QUEUES.has_key?(event.call_id)
94
+ CALL_QUEUES[event.call_id] = Queue.new
95
+ CALL_QUEUES[event.call_id].push event
96
+ run_call connection, event
97
+ when Event::Info
98
+ INFO_EVENT_QUEUE.push event
99
+ when Event::Answered, Event::Complete, Event::End, Event::Ringing
100
+ CALL_QUEUES[event.call_id].push event
101
+ else
102
+ puts "Unknown event: #{event.inspect}"
103
+ end
104
+
105
+ end
106
+ end
107
+
108
+ def run_call(connection, offer)
109
+ ### CALL THREAD
110
+ # One thread is spun up to handle each call.
111
+ Thread.new do
112
+ raise "Unknown call #{offer.call_id}" unless CALL_QUEUES.has_key?(offer.call_id)
113
+ queue = CALL_QUEUES[offer.call_id]
114
+ call = queue.pop
115
+ dsl = DSL.new connection, offer.call_id, queue
116
+
117
+ puts "Incoming offer to #{offer.to} from #{offer.headers_hash[:from]} #{offer}"
118
+ dsl.pry
119
+
120
+ # Clean up the queue.
121
+ CALL_QUEUES[offer.call_id] = nil
122
+ end
123
+ end
124
+
125
+ connection_thread.join
@@ -0,0 +1,30 @@
1
+ module Punchblock
2
+ module Command
3
+ class Accept < CommandNode
4
+ register :accept, :core
5
+
6
+ include HasHeaders
7
+
8
+ ##
9
+ # Create an Rayo accept command. This is equivalent to a SIP "180 Trying"
10
+ #
11
+ # @param [Hash] options
12
+ # @option options [Array[Header], Hash, Optional] :headers SIP headers to attach to
13
+ # the call. Can be either a hash of key-value pairs, or an array of
14
+ # Header objects.
15
+ #
16
+ # @return [Command::Accept] a formatted Rayo accept command
17
+ #
18
+ # @example
19
+ # Accept.new.to_xml
20
+ #
21
+ # returns:
22
+ # <accept xmlns="urn:xmpp:rayo:1"/>
23
+ def self.new(options = {})
24
+ super().tap do |new_node|
25
+ new_node.headers = options[:headers]
26
+ end
27
+ end
28
+ end # Accept
29
+ end # Command
30
+ end # Punchblock
@@ -0,0 +1,30 @@
1
+ module Punchblock
2
+ module Command
3
+ class Answer < CommandNode
4
+ register :answer, :core
5
+
6
+ include HasHeaders
7
+
8
+ ##
9
+ # Create an Rayo answer command. This is equivalent to a SIP "200 OK"
10
+ #
11
+ # @param [Hash] options
12
+ # @option options [Array[Header], Hash, Optional] :headers SIP headers to attach to
13
+ # the call. Can be either a hash of key-value pairs, or an array of
14
+ # Header objects.
15
+ #
16
+ # @return [Command::Answer] a formatted Rayo answer command
17
+ #
18
+ # @example
19
+ # Answer.new.to_xml
20
+ #
21
+ # returns:
22
+ # <answer xmlns="urn:xmpp:rayo:1"/>
23
+ def self.new(options = {})
24
+ super().tap do |new_node|
25
+ new_node.headers = options[:headers]
26
+ end
27
+ end
28
+ end # Answer
29
+ end # Command
30
+ end # Punchblock
@@ -0,0 +1,88 @@
1
+ module Punchblock
2
+ module Command
3
+ class Dial < CommandNode
4
+ register :dial, :core
5
+
6
+ include HasHeaders
7
+
8
+ ##
9
+ # Create a dial message
10
+ #
11
+ # @param [Hash] options
12
+ # @option options [String] :to destination to dial
13
+ # @option options [String, Optional] :from what to set the Caller ID to
14
+ # @option options [Array[Header], Hash, Optional] :headers SIP headers to attach to
15
+ # the new call. Can be either a hash of key-value pairs, or an array of
16
+ # Header objects.
17
+ # @option options [Join, Hash, Optional] :join a join (or set of join parameters) to
18
+ # nest within the dial
19
+ #
20
+ # @return [Command::Dial] a formatted Rayo dial command
21
+ #
22
+ # @example
23
+ # dial :to => 'tel:+14155551212', :from => 'tel:+13035551212'
24
+ #
25
+ # returns:
26
+ # <dial to='tel:+13055195825' from='tel:+14152226789' xmlns='urn:xmpp:rayo:1' />
27
+ #
28
+ def self.new(options = {})
29
+ super().tap do |new_node|
30
+ options.each_pair { |k,v| new_node.send :"#{k}=", v }
31
+ end
32
+ end
33
+
34
+ ##
35
+ # @return [String] destination to dial
36
+ def to
37
+ read_attr :to
38
+ end
39
+
40
+ ##
41
+ # @param [String] dial_to destination to dial
42
+ def to=(dial_to)
43
+ write_attr :to, dial_to
44
+ end
45
+
46
+ ##
47
+ # @return [String] the caller ID
48
+ def from
49
+ read_attr :from
50
+ end
51
+
52
+ ##
53
+ # @param [String] dial_from what to set the caller ID to
54
+ def from=(dial_from)
55
+ write_attr :from, dial_from
56
+ end
57
+
58
+ ##
59
+ # @return [Join] the nested join
60
+ #
61
+ def join
62
+ element = find_first 'ns:join', :ns => Join.registered_ns
63
+ Join.new element if element
64
+ end
65
+
66
+ ##
67
+ # @param [Hash, Join] other a join or hash of join options
68
+ #
69
+ def join=(other)
70
+ remove_children :join
71
+ join = Join.new(other) unless other.is_a?(Join)
72
+ self << join
73
+ end
74
+
75
+ def response=(other)
76
+ super
77
+ if other.is_a?(Blather::Stanza::Iq)
78
+ ref = other.rayo_node
79
+ @call_id = ref.id if ref.is_a?(Ref)
80
+ end
81
+ end
82
+
83
+ def inspect_attributes # :nodoc:
84
+ [:to, :from, :join] + super
85
+ end
86
+ end # Dial
87
+ end # Command
88
+ end # Punchblock
@@ -0,0 +1,25 @@
1
+ module Punchblock
2
+ module Command
3
+ class Hangup < CommandNode
4
+ register :hangup, :core
5
+
6
+ include HasHeaders
7
+
8
+ ##
9
+ # Create an Rayo hangup message
10
+ #
11
+ # @param [Hash] options
12
+ # @option options [Array[Header], Hash, Optional] :headers SIP headers to attach to
13
+ # the call. Can be either a hash of key-value pairs, or an array of
14
+ # Header objects.
15
+ #
16
+ # @return [Command::Hangup] a formatted Rayo redirect command
17
+ #
18
+ def self.new(options = {})
19
+ super().tap do |new_node|
20
+ new_node.headers = options[:headers]
21
+ end
22
+ end
23
+ end # Hangup
24
+ end # Command
25
+ end # Punchblock