adhearsion-cw 1.0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. data/CHANGELOG +109 -0
  2. data/EVENTS +11 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE +456 -0
  5. data/Rakefile +134 -0
  6. data/adhearsion.gemspec +174 -0
  7. data/app_generators/ahn/USAGE +5 -0
  8. data/app_generators/ahn/ahn_generator.rb +97 -0
  9. data/app_generators/ahn/templates/.ahnrc +34 -0
  10. data/app_generators/ahn/templates/Gemfile +7 -0
  11. data/app_generators/ahn/templates/README +8 -0
  12. data/app_generators/ahn/templates/Rakefile +27 -0
  13. data/app_generators/ahn/templates/components/ami_remote/ami_remote.rb +15 -0
  14. data/app_generators/ahn/templates/components/disabled/HOW_TO_ENABLE +7 -0
  15. data/app_generators/ahn/templates/components/disabled/restful_rpc/README.markdown +11 -0
  16. data/app_generators/ahn/templates/components/disabled/restful_rpc/example-client.rb +48 -0
  17. data/app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.rb +91 -0
  18. data/app_generators/ahn/templates/components/disabled/restful_rpc/restful_rpc.yml +34 -0
  19. data/app_generators/ahn/templates/components/disabled/restful_rpc/spec/restful_rpc_spec.rb +251 -0
  20. data/app_generators/ahn/templates/components/disabled/sandbox/sandbox.rb +104 -0
  21. data/app_generators/ahn/templates/components/disabled/sandbox/sandbox.yml +2 -0
  22. data/app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown +47 -0
  23. data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb +34 -0
  24. data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.yml +12 -0
  25. data/app_generators/ahn/templates/components/disabled/xmpp_gateway/README.markdown +3 -0
  26. data/app_generators/ahn/templates/components/disabled/xmpp_gateway/xmpp_gateway.rb +11 -0
  27. data/app_generators/ahn/templates/components/disabled/xmpp_gateway/xmpp_gateway.yml +0 -0
  28. data/app_generators/ahn/templates/components/simon_game/simon_game.rb +56 -0
  29. data/app_generators/ahn/templates/config/startup.rb +74 -0
  30. data/app_generators/ahn/templates/dialplan.rb +3 -0
  31. data/app_generators/ahn/templates/events.rb +32 -0
  32. data/bin/ahn +29 -0
  33. data/bin/ahnctl +68 -0
  34. data/bin/jahn +43 -0
  35. data/examples/asterisk_manager_interface/standalone.rb +51 -0
  36. data/lib/adhearsion/cli.rb +296 -0
  37. data/lib/adhearsion/component_manager/component_tester.rb +53 -0
  38. data/lib/adhearsion/component_manager/spec_framework.rb +18 -0
  39. data/lib/adhearsion/component_manager.rb +272 -0
  40. data/lib/adhearsion/events_support.rb +84 -0
  41. data/lib/adhearsion/foundation/all.rb +15 -0
  42. data/lib/adhearsion/foundation/blank_slate.rb +3 -0
  43. data/lib/adhearsion/foundation/custom_daemonizer.rb +45 -0
  44. data/lib/adhearsion/foundation/event_socket.rb +205 -0
  45. data/lib/adhearsion/foundation/future_resource.rb +36 -0
  46. data/lib/adhearsion/foundation/metaprogramming.rb +17 -0
  47. data/lib/adhearsion/foundation/numeric.rb +13 -0
  48. data/lib/adhearsion/foundation/pseudo_guid.rb +10 -0
  49. data/lib/adhearsion/foundation/relationship_properties.rb +42 -0
  50. data/lib/adhearsion/foundation/string.rb +26 -0
  51. data/lib/adhearsion/foundation/synchronized_hash.rb +96 -0
  52. data/lib/adhearsion/foundation/thread_safety.rb +7 -0
  53. data/lib/adhearsion/host_definitions.rb +67 -0
  54. data/lib/adhearsion/initializer/asterisk.rb +87 -0
  55. data/lib/adhearsion/initializer/configuration.rb +321 -0
  56. data/lib/adhearsion/initializer/database.rb +60 -0
  57. data/lib/adhearsion/initializer/drb.rb +31 -0
  58. data/lib/adhearsion/initializer/freeswitch.rb +22 -0
  59. data/lib/adhearsion/initializer/ldap.rb +57 -0
  60. data/lib/adhearsion/initializer/rails.rb +41 -0
  61. data/lib/adhearsion/initializer/xmpp.rb +42 -0
  62. data/lib/adhearsion/initializer.rb +394 -0
  63. data/lib/adhearsion/logging.rb +92 -0
  64. data/lib/adhearsion/tasks/components.rb +32 -0
  65. data/lib/adhearsion/tasks/database.rb +5 -0
  66. data/lib/adhearsion/tasks/deprecations.rb +59 -0
  67. data/lib/adhearsion/tasks/generating.rb +20 -0
  68. data/lib/adhearsion/tasks/lint.rb +4 -0
  69. data/lib/adhearsion/tasks/testing.rb +37 -0
  70. data/lib/adhearsion/tasks.rb +17 -0
  71. data/lib/adhearsion/version.rb +35 -0
  72. data/lib/adhearsion/voip/asterisk/agi_server.rb +115 -0
  73. data/lib/adhearsion/voip/asterisk/commands.rb +1581 -0
  74. data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +140 -0
  75. data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +102 -0
  76. data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +250 -0
  77. data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +240 -0
  78. data/lib/adhearsion/voip/asterisk/config_manager.rb +71 -0
  79. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +1681 -0
  80. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +341 -0
  81. data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +78 -0
  82. data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +87 -0
  83. data/lib/adhearsion/voip/asterisk/manager_interface.rb +705 -0
  84. data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +80 -0
  85. data/lib/adhearsion/voip/asterisk/super_manager.rb +19 -0
  86. data/lib/adhearsion/voip/asterisk.rb +4 -0
  87. data/lib/adhearsion/voip/call.rb +498 -0
  88. data/lib/adhearsion/voip/call_routing.rb +64 -0
  89. data/lib/adhearsion/voip/commands.rb +9 -0
  90. data/lib/adhearsion/voip/constants.rb +39 -0
  91. data/lib/adhearsion/voip/conveniences.rb +18 -0
  92. data/lib/adhearsion/voip/dial_plan.rb +250 -0
  93. data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +37 -0
  94. data/lib/adhearsion/voip/dsl/dialing_dsl.rb +151 -0
  95. data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +27 -0
  96. data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +124 -0
  97. data/lib/adhearsion/voip/dsl/dialplan/parser.rb +69 -0
  98. data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +16 -0
  99. data/lib/adhearsion/voip/dsl/numerical_string.rb +128 -0
  100. data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +48 -0
  101. data/lib/adhearsion/voip/freeswitch/event_handler.rb +58 -0
  102. data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +129 -0
  103. data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +38 -0
  104. data/lib/adhearsion/voip/freeswitch/oes_server.rb +195 -0
  105. data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +80 -0
  106. data/lib/adhearsion/voip/menu_state_machine/matchers.rb +123 -0
  107. data/lib/adhearsion/voip/menu_state_machine/menu_builder.rb +57 -0
  108. data/lib/adhearsion/voip/menu_state_machine/menu_class.rb +149 -0
  109. data/lib/adhearsion/xmpp/connection.rb +61 -0
  110. data/lib/adhearsion.rb +46 -0
  111. data/lib/theatre/README.markdown +64 -0
  112. data/lib/theatre/callback_definition_loader.rb +84 -0
  113. data/lib/theatre/guid.rb +23 -0
  114. data/lib/theatre/invocation.rb +121 -0
  115. data/lib/theatre/namespace_manager.rb +153 -0
  116. data/lib/theatre/version.rb +2 -0
  117. data/lib/theatre.rb +151 -0
  118. metadata +323 -0
@@ -0,0 +1,34 @@
1
+ # Use path_nesting to specify an arbitrarily nested. Could be used for additional security or in HTTP reverse proxy server.
2
+ path_nesting: /
3
+
4
+ port: 5000
5
+
6
+ # The "handler" option here can be any valid Rack::Handler constant name.
7
+ # Other options: WEBrick, EventedMongrel.
8
+ # If you don't know the differences between these, "Mongrel" is definitely a good choice.
9
+ handler: Mongrel
10
+
11
+ # In a production system, you should make this "false" since
12
+ show_exceptions: true
13
+
14
+ # The "authentication" config option can either be "false" or key/value pairs representing allowed usernames and passwords.
15
+
16
+ #authentication: false
17
+ authentication:
18
+ jicksta: roflcopterz
19
+ foo: bar6213671682
20
+
21
+ access: everyone # When allowing "everyone" access, no IPs are blocked.
22
+ #access: whitelist # When using a whitelist, the "whitelist" data below will be used.
23
+ #access: blacklist # When using a blacklist, the "blacklist" data below will be used.
24
+
25
+ # This is a list of IPs which are exclusively allowed to call this web service.
26
+ # Note: whitelists are far more secure than blacklists.
27
+ whitelist:
28
+ - 127.0.0.1
29
+ - 192.168.*.*
30
+
31
+ # This is a list of the IPs which are explicitly NOT allowed to call this web service. This will only be used if "access" is
32
+ # set to "blacklist" above.
33
+ blacklist:
34
+ - 100.200.100.200
@@ -0,0 +1,251 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup
4
+ Bundler.require
5
+
6
+ require 'adhearsion/component_manager/spec_framework'
7
+
8
+ RESTFUL_RPC = ComponentTester.new("restful_rpc", File.dirname(__FILE__) + "/../..")
9
+
10
+ ##### This is here for a reference
11
+ #{"CONTENT_LENGTH" => "12",
12
+ # "CONTENT_TYPE" => "application/x-www-form-urlencoded",
13
+ # "GATEWAY_INTERFACE" => "CGI/1.1",
14
+ # "HTTP_ACCEPT" => "application/xml",
15
+ # "HTTP_ACCEPT_ENCODING" => "gzip, deflate",
16
+ # "HTTP_AUTHORIZATION" => "Basic amlja3N0YTpyb2ZsY29wdGVyeg==",
17
+ # "HTTP_HOST" => "localhost:5000",
18
+ # "HTTP_VERSION" => "HTTP/1.1",
19
+ # "PATH_INFO" => "/rofl",
20
+ # "QUERY_STRING" => "",
21
+ # "rack.errors" => StringIO.new(""),
22
+ # "rack.input" => StringIO.new('["o","hai!"]'),
23
+ # "rack.multiprocess" => false,
24
+ # "rack.multithread" => true,
25
+ # "rack.run_once" => false,
26
+ # "rack.url_scheme" => "http",
27
+ # "rack.version" => [0, 1],
28
+ # "REMOTE_ADDR" => "::1",
29
+ # "REMOTE_HOST" => "localhost",
30
+ # "REMOTE_USER" => "jicksta",
31
+ # "REQUEST_METHOD" => "POST"
32
+ # "REQUEST_PATH" => "/",
33
+ # "REQUEST_URI" => "http://localhost:5000/rofl",
34
+ # "SCRIPT_NAME" => "",
35
+ # "SERVER_NAME" => "localhost",
36
+ # "SERVER_PORT" => "5000",
37
+ # "SERVER_PROTOCOL" => "HTTP/1.1",
38
+ # "SERVER_SOFTWARE" => "WEBrick/1.3.1 (Ruby/1.8.6/2008-03-03)"}
39
+
40
+ describe "The VALID_IP_ADDRESS regular expression" do
41
+
42
+ it "should match only valid IP addresses" do
43
+ valid_ip_addresses = ["192.168.1.98", "10.0.1.200", "255.255.255.0", "123.*.4.*"]
44
+ invalid_ip_addresses = ["10.0.1.1 foo", "bar 255.255.255.0", "0*0*0*0", "1234"]
45
+
46
+ valid_ip_addresses. each { |ip| RESTFUL_RPC::VALID_IP_ADDRESS.should =~ ip }
47
+ invalid_ip_addresses.each { |ip| RESTFUL_RPC::VALID_IP_ADDRESS.should_not =~ ip }
48
+ end
49
+ end
50
+
51
+ describe "The initialization block" do
52
+
53
+ it "should create a new Thread" do
54
+ mock_component_config_with :restful_rpc => {}
55
+ mock(Thread).new { nil }
56
+ RESTFUL_RPC.initialize!
57
+ end
58
+
59
+ it "should run the Rack adapter specified in the configuration" do
60
+ mock(Thread).new.yields
61
+ mock_component_config_with :restful_rpc => {"adapter" => "Mongrel"}
62
+ mock(Rack::Handler::Mongrel).run is_a(Proc), :Port => 5000
63
+ RESTFUL_RPC.initialize!
64
+ end
65
+
66
+ it "should wrap the RESTFUL_API_HANDLER in an Rack::Auth::Basic object if authentication is enabled" do
67
+ mock(Thread).new.yields
68
+ mock_component_config_with :restful_rpc => {"authentication" => {"foo" => "bar"}}
69
+
70
+ proper_authenticator = lambda do |obj|
71
+ request = OpenStruct.new :credentials => ["foo", "bar"]
72
+ obj.is_a?(Rack::Auth::Basic) && obj.send(:valid?, request)
73
+ end
74
+
75
+ mock(Rack::Handler::Mongrel).run(satisfy(&proper_authenticator), :Port => 5000)
76
+ RESTFUL_RPC.initialize!
77
+ end
78
+
79
+ it 'should wrap the RESTFUL_API_HANDLER in ShowStatus and ShowExceptions objects when show_exceptions is enabled' do
80
+ mock(Thread).new.yields
81
+ mock_component_config_with :restful_rpc => {"show_exceptions" => true}
82
+
83
+ mock.proxy(Rack::ShowExceptions).new(is_a(Proc))
84
+ mock.proxy(Rack::ShowStatus).new is_a(Rack::ShowExceptions)
85
+
86
+ mock(Rack::Handler::Mongrel).run is_a(Rack::ShowStatus), :Port => 5000
87
+ RESTFUL_RPC.initialize!
88
+ end
89
+
90
+ end
91
+
92
+ describe 'Private helper methods' do
93
+
94
+ describe "the RESTFUL_API_HANDLER lambda" do
95
+
96
+ it "should return a 200 for requests which execute a method that has been defined in the methods_for(:rpc) context" do
97
+ component_manager = Adhearsion::Components::ComponentManager.new('/path/shouldnt/matter')
98
+
99
+ mock(Adhearsion::Components).component_manager { component_manager }
100
+ component_manager.load_code <<-RUBY
101
+ methods_for(:rpc) do
102
+ def testing_123456(one,two)
103
+ [two.reverse, one.reverse]
104
+ end
105
+ end
106
+ RUBY
107
+
108
+ input = StringIO.new %w[jay phillips].to_json
109
+
110
+ mock_component_config_with :restful_rpc => {"path_nesting" => "/"}
111
+
112
+ env = {"PATH_INFO" => "/testing_123456", "rack.input" => input}
113
+
114
+ response = RESTFUL_RPC::RESTFUL_API_HANDLER.call(env)
115
+ response.should be_kind_of(Array)
116
+ response.should have(3).items
117
+ response.first.should equal(200)
118
+ JSON.parse(response.last).should eql(%w[jay phillips].map(&:reverse).reverse)
119
+ end
120
+
121
+ it "should return a 400 when no data is POSTed" do
122
+ env = {"rack.input" => StringIO.new(""), "REQUEST_URI" => "/foobar"}
123
+ RESTFUL_RPC::RESTFUL_API_HANDLER.call(env).first.should equal(400)
124
+ end
125
+
126
+ it "should work with a high level test of a successful method invocation" do
127
+
128
+ component_manager = Adhearsion::Components::ComponentManager.new('/path/shouldnt/matter')
129
+
130
+ mock(Adhearsion::Components).component_manager { component_manager }
131
+
132
+ component_manager.load_code '
133
+ methods_for(:rpc) do
134
+ def rofl(one,two)
135
+ "Hai! #{one} #{two}"
136
+ end
137
+ end'
138
+
139
+ env = {
140
+ "CONTENT_LENGTH" => "12",
141
+ "CONTENT_TYPE" => "application/x-www-form-urlencoded",
142
+ "GATEWAY_INTERFACE" => "CGI/1.1",
143
+ "HTTP_ACCEPT" => "application/xml",
144
+ "HTTP_ACCEPT_ENCODING" => "gzip, deflate",
145
+ "HTTP_AUTHORIZATION" => "Basic amlja3N0YTpyb2ZsY29wdGVyeg==",
146
+ "HTTP_HOST" => "localhost:5000",
147
+ "HTTP_VERSION" => "HTTP/1.1",
148
+ "PATH_INFO" => "/rofl",
149
+ "QUERY_STRING" => "",
150
+ "rack.errors" => StringIO.new(""),
151
+ "rack.input" => StringIO.new('["o","hai!"]'),
152
+ "rack.multiprocess" => false,
153
+ "rack.multithread" => true,
154
+ "rack.run_once" => false,
155
+ "rack.url_scheme" => "http",
156
+ "rack.version" => [0, 1],
157
+ "REMOTE_ADDR" => "::1",
158
+ "REMOTE_HOST" => "localhost",
159
+ "REMOTE_USER" => "jicksta",
160
+ "REQUEST_METHOD" => "POST",
161
+ "REQUEST_PATH" => "/",
162
+ "REQUEST_URI" => "http://localhost:5000/rofl",
163
+ "SCRIPT_NAME" => "",
164
+ "SERVER_NAME" => "localhost",
165
+ "SERVER_PORT" => "5000",
166
+ "SERVER_PROTOCOL" => "HTTP/1.1",
167
+ "SERVER_SOFTWARE" => "WEBrick/1.3.1 (Ruby/1.8.6/2008-03-03)" }
168
+
169
+ response = RESTFUL_RPC::RESTFUL_API_HANDLER.call(env)
170
+ JSON.parse(response.last).should == ["Hai! o hai!"]
171
+
172
+ end
173
+
174
+ it "should contain backtrace information when show_errors is enabled and an exception occurs" do
175
+ mock_component_config_with :restful_api => {"show_errors" => true}
176
+ pending
177
+ end
178
+
179
+ end
180
+
181
+ describe 'the ip_allowed?() method' do
182
+
183
+ before :each do
184
+ @method = RESTFUL_RPC.helper_method :ip_allowed?
185
+ end
186
+
187
+ it 'should raise a ConfigurationError if "access" is not one of "everyone", "whitelist" or "blacklist"' do
188
+ good_access_values = %w[everyone whitelist blacklist]
189
+ bad_access_values = %w[foo bar qaz qwerty everone blaclist whitlist]
190
+
191
+ good_access_values.each do |access_value|
192
+ mock_component_config_with :restful_rpc => {"access" => access_value, "whitelist" => [], "blacklist" => []}
193
+ lambda { @method.call("10.0.0.1") }.should_not raise_error
194
+ end
195
+
196
+ bad_access_values.each do |access_value|
197
+ mock_component_config_with :restful_rpc => {"access" => access_value, "authentication" => false}
198
+ lambda { @method.call("10.0.0.1") }.should raise_error(Adhearsion::Components::ConfigurationError)
199
+ end
200
+ end
201
+
202
+ describe 'whitelists' do
203
+
204
+ it "should parse *'s as wildcards" do
205
+ mock_component_config_with :restful_rpc => {"access" => "whitelist", "whitelist" => ["10.*.*.*"]}
206
+ @method.call("10.1.2.3").should be_true
207
+ end
208
+
209
+ it "should allow IPs which are explictly specified" do
210
+ mock_component_config_with :restful_rpc => {"access" => "whitelist", "whitelist" => ["4.3.2.1"]}
211
+ @method.call("4.3.2.1").should be_true
212
+ end
213
+
214
+ it "should not allow IPs which are not explicitly specified" do
215
+ mock_component_config_with :restful_rpc => {"access" => "whitelist", "whitelist" => %w[ 1.2.3.4 4.3.2.1]}
216
+ @method.call("2.2.2.2").should be_false
217
+ end
218
+
219
+ end
220
+
221
+ describe 'blacklists' do
222
+
223
+ it "should parse *'s as wildcards" do
224
+ mock_component_config_with :restful_rpc => {"access" => "blacklist", "blacklist" => ["10.*.*.*"]}
225
+ @method.call("10.1.2.3").should be_false
226
+ end
227
+
228
+ it "should not allow IPs which are explicitly specified" do
229
+ mock_component_config_with :restful_rpc => {"access" => "blacklist", "blacklist" => ["9.8.7.6", "9.8.7.5"]}
230
+ @method.call("9.8.7.5").should be_false
231
+ end
232
+
233
+ it "should allow IPs which are not explicitly specified" do
234
+ mock_component_config_with :restful_rpc => {"access" => "blacklist", "blacklist" => ["10.20.30.40"]}
235
+ @method.call("1.1.1.1").should be_true
236
+ end
237
+
238
+ end
239
+
240
+ describe '"everyone" access' do
241
+ it "should return true for any IP given, irrespective of the configuration" do
242
+ ip_addresses = %w[100.200.100.200 0.0.0.0 *.0.0.*]
243
+ ip_addresses.each do |address|
244
+ RESTFUL_RPC.helper_method(:ip_allowed?).call(address).should equal(true)
245
+ end
246
+ end
247
+ end
248
+
249
+ end
250
+
251
+ end
@@ -0,0 +1,104 @@
1
+ require 'md5'
2
+ require 'open-uri'
3
+
4
+ SANDBOX_VERSION = 1.0
5
+
6
+ initialization do
7
+ # We shouldn't start initializing until after the AGI server has initialized.
8
+ Events.register_callback(:after_initialized) do
9
+
10
+ config = if COMPONENTS.sandbox.has_key? "connect_to"
11
+ {"connect_to" => COMPONENTS.sandbox["connect_to"]}
12
+ else
13
+ begin
14
+ yaml_data = open("http://sandbox.adhearsion.com/component/#{SANDBOX_VERSION}").read
15
+ YAML.load yaml_data
16
+ rescue SocketError
17
+ ahn_log.sandbox.error "Could not connect to the sandbox server! Skipping sandbox initialization!"
18
+ next
19
+ rescue => e
20
+ ahn_log.sandbox.error "COULD NOT RETRIEVE SANDBOX CONNECTION INFORMATION! Not initializing sandbox component!"
21
+ next
22
+ end
23
+ end
24
+
25
+ begin
26
+ # The "connect_to" key is what this version supports
27
+ if config.kind_of?(Hash) && config.has_key?("connect_to")
28
+ config = config['connect_to']
29
+
30
+ host, port = config.values_at "host", "port"
31
+
32
+ username, password = COMPONENTS.sandbox["username"].to_s, COMPONENTS.sandbox["password"].to_s
33
+
34
+ if username.blank? || password.blank? || username == "user123"
35
+ ahn_log.sandbox.error "You must specify your username and password in this component's config file!"
36
+ next
37
+ end
38
+
39
+ # Part of the AGI-superset protocol we use to log in.
40
+ identifying_hash = MD5.md5(username + ":" + password).to_s
41
+
42
+ if host.nil? || port.nil?
43
+ ahn_log.sandbox.error "Invalid YAML returned from server! Skipping sandbox initialization!"
44
+ next
45
+ end
46
+
47
+ Thread.new do
48
+ loop do
49
+ begin
50
+ ahn_log.sandbox.debug "Establishing outbound AGI socket"
51
+ socket = TCPSocket.new(host, port)
52
+ socket.puts identifying_hash
53
+ response = socket.gets
54
+ unless response
55
+ next
56
+ end
57
+ response.chomp!
58
+ case response
59
+ when "authentication accepted"
60
+ ahn_log.sandbox "Authentication accepted"
61
+
62
+ start_signal = socket.gets
63
+ next unless start_signal
64
+ start_signal.chomp!
65
+
66
+ if start_signal
67
+ ahn_log.sandbox "Incoming call from remote sandbox server!"
68
+ begin
69
+ Adhearsion::Initializer::AsteriskInitializer.agi_server.server.serve(socket)
70
+ rescue => e
71
+ ahn_log.error "Non-fatal exception in the AGI server: #{e.inspect} \n" + e.backtrace.join("\n")
72
+ ensure
73
+ socket.close rescue nil
74
+ end
75
+ ahn_log.sandbox "AGI server finished serving call. Reconnecting to sandbox."
76
+ else
77
+ ahn_log.sandbox "Remote Asterisk server received no call. Reconnecting..."
78
+ end
79
+ when "authentication failed"
80
+ ahn_log.sandbox.error "Your username or password is invalid! Skipping sandbox initialization..."
81
+ break
82
+ when /^wait (\d+)$/
83
+ sleep response[/^wait (\d+)$/,1].to_i
84
+ else
85
+ ahn_log.sandbox.error "Invalid login acknowledgement! Skipping sandbox initialization!"
86
+ break
87
+ end
88
+ rescue Errno::ECONNREFUSED
89
+ ahn_log.sandbox.error "Could not connect to the sandbox server! Sandbox component stopping..."
90
+ break
91
+ rescue => e
92
+ ahn_log.error "Unrecognized error: #{e.inspect} \n" + e.backtrace.join("\n")
93
+ end
94
+ end
95
+ end
96
+
97
+ else
98
+ ahn_log.sandbox.error "COULD NOT RETRIEVE SANDBOX CONNECTION INFORMATION! Not initializing sandbox component!"
99
+ end
100
+ rescue => e
101
+ ahn_log.sandbox.error "Encountered an error when connecting to the sandbox! #{e.message}\n" + e.backtrace.join("\n")
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,2 @@
1
+ username: user123
2
+ password: pass123
@@ -0,0 +1,47 @@
1
+ What is Stomp?
2
+ ==============
3
+
4
+ Stomp is a very simple message-queue protocol with which two separate systems can communicate. Because the protocol is so simple, there are many Stomp server implementations from which you can choose. Some of these include
5
+
6
+ - ActiveMQ (http://activemq.com)
7
+ - Ruby "stompserver" gem (gem install stompserver)
8
+ - RabbitMQ (http://rabbitmq.com)
9
+
10
+ If you wish to get up and running with a development environment, the Ruby stompserver gem is a fantastic starting point. For a critical production system, ActiveMQ should probably be used but it bears the cumbersome paradigm of many "enterprisey" Java applications.
11
+
12
+ How does it work?
13
+ =================
14
+
15
+ Stomp is used when certain processes have defined responsibilities. For example, your Adhearsion application's responsibility is to communicate with your Asterisk machine. Other processes (e.g. a Rails web application) will probably need to instruct Adhearsion to do something. Instructions may include
16
+
17
+ - Start a new call between two given phone numbers
18
+ - Have a particular call do something based on a new event
19
+ - Hangup a call
20
+
21
+ Below is a diagram which should give you a better idea of how it works.
22
+
23
+ Process Process Process (e.g. Rails)
24
+ \ | /
25
+ \ | /
26
+ Stomp Server (e.g. ActiveMQ)
27
+ |
28
+ |
29
+ Process (e.g. Adhearsion)
30
+
31
+ Note: Adhearsion could also be the sender of messages through the Stomp server which are consumed by a number of handlers.
32
+
33
+ Setting up a Ruby Stomp server
34
+ ==============================
35
+
36
+ Install the pure-Ruby Stomp server by doing "gem install stompserver". This will add the "stompserver" command to your system. When running it without any parameters, it starts without requiring authentication. If you're wanting to get a quick experiment running, I recommend simply doing that.
37
+
38
+ Open the `stomp_gateway.yml` file in the stomp_gateway component folder. Comment out the four settings at the top of the file named "user", "pass", "host" and "port" by prepending a "#" to their line. This will cause the component to choose defaults for those properties. The component's defaults will match the expected credentials for the experimental stompserver you're already running on your computer.
39
+
40
+ You also need specify a subscription name in
41
+
42
+ events.stomp.start_call.each do |event|
43
+ # The "event" variable holds a Stomp::Message object.
44
+ name = event.headers
45
+ end
46
+
47
+ You a
@@ -0,0 +1,34 @@
1
+ require 'stomp'
2
+
3
+ # TODO: Recover from a disconnect!
4
+
5
+ initialization do
6
+ user = COMPONENTS.stomp_gateway[:user] || ""
7
+ pass = COMPONENTS.stomp_gateway[:pass] || ""
8
+ host = COMPONENTS.stomp_gateway[:host] || "localhost"
9
+ port = COMPONENTS.stomp_gateway[:port] || 61613
10
+
11
+ ::StompGatewayConnection = Stomp::Client.open(user, pass, host, port)
12
+
13
+ subscriptions = COMPONENTS.stomp_gateway["subscriptions"]
14
+
15
+ ahn_log.stomp_gateway "Connection established. Subscriptions: #{subscriptions.inspect}"
16
+
17
+ Events.register_namespace_name "/stomp"
18
+
19
+ subscriptions.each do |subscription|
20
+ Events.register_namespace_name "/stomp/#{subscription}"
21
+ ::StompGatewayConnection.subscribe subscription do |event|
22
+ Adhearsion::Events.trigger ["stomp", subscription], event
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ methods_for :global do
29
+ def send_stomp(destination, message, headers={})
30
+ ::StompGatewayConnection.send(destination, message, headers)
31
+ end
32
+ end
33
+
34
+ # In the future, I may add a methods_for(:events) method which allows synchronous messaging.
@@ -0,0 +1,12 @@
1
+ # Comment out any of these properties to use a default.
2
+
3
+ user: stomp_user
4
+ pass: secret_password
5
+ host: localhost
6
+ port: 61613
7
+
8
+ ### Add your list of subscriptions below that this gateway should proxy to events.rb
9
+
10
+ # subscriptions:
11
+ # - start_call
12
+ # - hungup_call
@@ -0,0 +1,3 @@
1
+ In order to use this component, you must have an XMPP connection enabled in config/startup.rb. It is recommended this be done as an XMPP component rather than a standard client.
2
+
3
+ You can use all elements of the Blather DSL scoped to XMPP::Connection
@@ -0,0 +1,11 @@
1
+ initialization do
2
+
3
+ XMPP::Connection.message :body do |m|
4
+ XMPP::Connection.say m.from, "You said: #{m.body}"
5
+ end
6
+
7
+ XMPP::Connection.subscription :request? do |s|
8
+ XMPP::Connection.write_to_stream s.approve!
9
+ end
10
+
11
+ end
@@ -0,0 +1,56 @@
1
+ methods_for :dialplan do
2
+ def simon_game
3
+ SimonGame.new(self).start
4
+ end
5
+ end
6
+
7
+ class SimonGame
8
+
9
+ def initialize(call)
10
+ @call = call
11
+ reset
12
+ end
13
+
14
+ def start
15
+ loop do
16
+ say_number
17
+ collect_attempt
18
+ verify_attempt
19
+ end
20
+ end
21
+
22
+ def random_number
23
+ rand(10).to_s
24
+ end
25
+
26
+ def update_number
27
+ @number << random_number
28
+ end
29
+
30
+ def say_number
31
+ update_number
32
+ @call.say_digits @number
33
+ end
34
+
35
+ def collect_attempt
36
+ @attempt = @call.input @number.length
37
+ end
38
+
39
+ def verify_attempt
40
+ if attempt_correct?
41
+ @call.play 'good'
42
+ else
43
+ @call.play %W[#{@number.length-1} times wrong-try-again-smarty]
44
+ reset
45
+ end
46
+ end
47
+
48
+ def attempt_correct?
49
+ @attempt == @number
50
+ end
51
+
52
+ def reset
53
+ @attempt, @number = '', ''
54
+ end
55
+
56
+ end
@@ -0,0 +1,74 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup
4
+ Bundler.require
5
+
6
+ Adhearsion::Configuration.configure do |config|
7
+
8
+ # Components to load from the system.
9
+ # All components that are activated in components/ will be automatically
10
+ # loaded and made available.
11
+ # This configuration option allows you to load components provided by gems.
12
+ # List the gem names here:
13
+ # config.add_component "ahn_test_component"
14
+
15
+ # Supported levels (in increasing severity) -- :debug < :info < :warn < :error < :fatal
16
+ config.logging :level => :info
17
+
18
+ # Whether incoming calls be automatically answered. Defaults to true.
19
+ # config.automatically_answer_incoming_calls = false
20
+
21
+ # Whether the other end hanging up should end the call immediately. Defaults to true.
22
+ # config.end_call_on_hangup = false
23
+
24
+ # Whether to end the call immediately if an unrescued exception is caught. Defaults to true.
25
+ # config.end_call_on_error = false
26
+
27
+ # NOTE: Pay special attention to the argument_delimiter field below:
28
+ # For Asterisk <= 1.4, use "|" (default)
29
+ # For Asterisk >= 1.6, use ","
30
+ # The delimiter can also be specified in Asterisk's asterisk.conf.
31
+ # This setting applies only to AGI. The AMI delimiter is auto-detected.
32
+ config.enable_asterisk :argument_delimiter => '|'
33
+ # config.asterisk.enable_ami :host => "127.0.0.1", :username => "admin", :password => "password", :events => true
34
+
35
+ # config.enable_drb
36
+
37
+ # Streamlined Rails integration! The first argument should be a relative or absolute path to
38
+ # the Rails app folder with which you're integrating. The second argument must be one of the
39
+ # the following: :development, :production, or :test.
40
+
41
+ # config.enable_rails :path => 'gui', :env => :development
42
+
43
+ # Note: You CANNOT do enable_rails and enable_database at the same time. When you enable Rails,
44
+ # it will automatically connect to same database Rails does and load the Rails app's models.
45
+
46
+ # Configure a database to use ActiveRecord-backed models. See ActiveRecord::Base.establish_connection
47
+ # for the appropriate settings here.
48
+ # You can also override the default log destination by supplying an alternate
49
+ # logging object with :logger. The default is ahn_log.db.
50
+ # config.enable_database :adapter => 'mysql',
51
+ # :username => 'joe',
52
+ # :password => 'secret',
53
+ # :host => 'db.example.org'
54
+
55
+ # Configure an LDAP connection using ActiveLdap. See ActiveLdap::Base.establish_connect
56
+ # for the appropriate settings here.
57
+ # config.enable_ldap :host => 'ldap.dataspill.org',
58
+ # :port => 389,
59
+ # :base => 'dc=dataspill,dc=org',
60
+ # :logger => ahn_log.ldap,
61
+ # :bind_dn => "uid=drewry,ou=People,dc=dataspill,dc=org",
62
+ # :password => 'password12345',
63
+ # :allow_anonymous => false,
64
+ # :try_sasl => false
65
+
66
+ # Configure XMPP call controller
67
+ # config.enable_xmpp :jid => 'active-calls.xmpp.example.com',
68
+ # :password => 'passwd',
69
+ # :server => 'xmpp.example.com',
70
+ # :port => 5347
71
+
72
+ end
73
+
74
+ Adhearsion::Initializer.start_from_init_file(__FILE__, File.dirname(__FILE__) + "/..")
@@ -0,0 +1,3 @@
1
+ adhearsion {
2
+ simon_game
3
+ }
@@ -0,0 +1,32 @@
1
+ ##
2
+ # In this file you can define callbacks for different aspects of the framework. Below is an example:
3
+ ##
4
+ #
5
+ # events.asterisk.before_call.each do |call|
6
+ # # This simply logs the extension for all calls going through this Adhearsion app.
7
+ # extension = call.variables[:extension]
8
+ # ahn_log "Got a new call with extension #{extension}"
9
+ # end
10
+ #
11
+ ##
12
+ # Asterisk Manager Interface example:
13
+ #
14
+ # events.asterisk.manager_interface.each do |event|
15
+ # ahn_log.events event.inspect
16
+ # end
17
+ #
18
+ # This assumes you gave :events => true to the config.asterisk.enable_ami method in config/startup.rb
19
+ #
20
+ ##
21
+ # Here is a list of the events included by default:
22
+ #
23
+ # - events.asterisk.manager_interface
24
+ # - events.after_initialized
25
+ # - events.shutdown
26
+ # - events.asterisk.before_call
27
+ # - events.asterisk.failed_call
28
+ # - events.asterisk.hungup_call
29
+ #
30
+ #
31
+ # Note: events are mostly for components to register and expose to you.
32
+ ##