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.
- data/.document +5 -0
- data/.gitignore +12 -0
- data/.rspec +3 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +20 -0
- data/README.markdown +31 -0
- data/Rakefile +23 -0
- data/assets/ozone/ask-1.0.xsd +56 -0
- data/assets/ozone/conference-1.0.xsd +17 -0
- data/assets/ozone/ozone-1.0.xsd +127 -0
- data/assets/ozone/say-1.0.xsd +24 -0
- data/assets/ozone/transfer-1.0.xsd +32 -0
- data/bin/punchblock-console +125 -0
- data/lib/punchblock/command/accept.rb +30 -0
- data/lib/punchblock/command/answer.rb +30 -0
- data/lib/punchblock/command/dial.rb +88 -0
- data/lib/punchblock/command/hangup.rb +25 -0
- data/lib/punchblock/command/join.rb +81 -0
- data/lib/punchblock/command/mute.rb +7 -0
- data/lib/punchblock/command/redirect.rb +49 -0
- data/lib/punchblock/command/reject.rb +61 -0
- data/lib/punchblock/command/unjoin.rb +50 -0
- data/lib/punchblock/command/unmute.rb +7 -0
- data/lib/punchblock/command.rb +16 -0
- data/lib/punchblock/command_node.rb +46 -0
- data/lib/punchblock/component/input.rb +320 -0
- data/lib/punchblock/component/output.rb +449 -0
- data/lib/punchblock/component/record.rb +216 -0
- data/lib/punchblock/component/tropo/ask.rb +197 -0
- data/lib/punchblock/component/tropo/conference.rb +328 -0
- data/lib/punchblock/component/tropo/say.rb +113 -0
- data/lib/punchblock/component/tropo/transfer.rb +178 -0
- data/lib/punchblock/component/tropo.rb +12 -0
- data/lib/punchblock/component.rb +73 -0
- data/lib/punchblock/connection.rb +209 -0
- data/lib/punchblock/core_ext/blather/stanza/presence.rb +11 -0
- data/lib/punchblock/core_ext/blather/stanza.rb +26 -0
- data/lib/punchblock/dsl.rb +46 -0
- data/lib/punchblock/event/answered.rb +7 -0
- data/lib/punchblock/event/complete.rb +65 -0
- data/lib/punchblock/event/dtmf.rb +19 -0
- data/lib/punchblock/event/end.rb +15 -0
- data/lib/punchblock/event/info.rb +15 -0
- data/lib/punchblock/event/joined.rb +50 -0
- data/lib/punchblock/event/offer.rb +29 -0
- data/lib/punchblock/event/ringing.rb +7 -0
- data/lib/punchblock/event/unjoined.rb +50 -0
- data/lib/punchblock/event.rb +16 -0
- data/lib/punchblock/generic_connection.rb +18 -0
- data/lib/punchblock/has_headers.rb +34 -0
- data/lib/punchblock/header.rb +47 -0
- data/lib/punchblock/media_container.rb +39 -0
- data/lib/punchblock/media_node.rb +17 -0
- data/lib/punchblock/protocol_error.rb +16 -0
- data/lib/punchblock/rayo_node.rb +88 -0
- data/lib/punchblock/ref.rb +26 -0
- data/lib/punchblock/version.rb +3 -0
- data/lib/punchblock.rb +42 -0
- data/log/.gitkeep +0 -0
- data/punchblock.gemspec +42 -0
- data/spec/punchblock/command/accept_spec.rb +13 -0
- data/spec/punchblock/command/answer_spec.rb +13 -0
- data/spec/punchblock/command/dial_spec.rb +54 -0
- data/spec/punchblock/command/hangup_spec.rb +13 -0
- data/spec/punchblock/command/join_spec.rb +21 -0
- data/spec/punchblock/command/mute_spec.rb +11 -0
- data/spec/punchblock/command/redirect_spec.rb +19 -0
- data/spec/punchblock/command/reject_spec.rb +43 -0
- data/spec/punchblock/command/unjoin_spec.rb +19 -0
- data/spec/punchblock/command/unmute_spec.rb +11 -0
- data/spec/punchblock/command_node_spec.rb +80 -0
- data/spec/punchblock/component/input_spec.rb +188 -0
- data/spec/punchblock/component/output_spec.rb +531 -0
- data/spec/punchblock/component/record_spec.rb +235 -0
- data/spec/punchblock/component/tropo/ask_spec.rb +183 -0
- data/spec/punchblock/component/tropo/conference_spec.rb +360 -0
- data/spec/punchblock/component/tropo/say_spec.rb +171 -0
- data/spec/punchblock/component/tropo/transfer_spec.rb +153 -0
- data/spec/punchblock/component_spec.rb +126 -0
- data/spec/punchblock/connection_spec.rb +194 -0
- data/spec/punchblock/event/answered_spec.rb +23 -0
- data/spec/punchblock/event/complete_spec.rb +80 -0
- data/spec/punchblock/event/dtmf_spec.rb +24 -0
- data/spec/punchblock/event/end_spec.rb +30 -0
- data/spec/punchblock/event/info_spec.rb +30 -0
- data/spec/punchblock/event/joined_spec.rb +32 -0
- data/spec/punchblock/event/offer_spec.rb +35 -0
- data/spec/punchblock/event/ringing_spec.rb +23 -0
- data/spec/punchblock/event/unjoined_spec.rb +32 -0
- data/spec/punchblock/header_spec.rb +44 -0
- data/spec/punchblock/protocol_error_spec.rb +9 -0
- data/spec/punchblock/ref_spec.rb +21 -0
- data/spec/spec_helper.rb +43 -0
- metadata +353 -0
data/.document
ADDED
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
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
|