jicksta-adhearsion 0.7.999
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +6 -0
- data/EVENTS +11 -0
- data/LICENSE +456 -0
- data/README.txt +5 -0
- data/Rakefile +120 -0
- data/adhearsion.gemspec +146 -0
- data/app_generators/ahn/USAGE +5 -0
- data/app_generators/ahn/ahn_generator.rb +87 -0
- data/app_generators/ahn/templates/.ahnrc +34 -0
- data/app_generators/ahn/templates/README +8 -0
- data/app_generators/ahn/templates/Rakefile +23 -0
- data/app_generators/ahn/templates/components/ami_remote/ami_remote.rb +15 -0
- data/app_generators/ahn/templates/components/disabled/HOW_TO_ENABLE +7 -0
- data/app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown +47 -0
- data/app_generators/ahn/templates/components/disabled/stomp_gateway/config.yml +12 -0
- data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb +34 -0
- data/app_generators/ahn/templates/components/restful_rpc/README.markdown +11 -0
- data/app_generators/ahn/templates/components/restful_rpc/config.yml +34 -0
- data/app_generators/ahn/templates/components/restful_rpc/example-client.rb +48 -0
- data/app_generators/ahn/templates/components/restful_rpc/restful_rpc.rb +87 -0
- data/app_generators/ahn/templates/components/simon_game/simon_game.rb +56 -0
- data/app_generators/ahn/templates/config/startup.rb +53 -0
- data/app_generators/ahn/templates/dialplan.rb +3 -0
- data/app_generators/ahn/templates/events.rb +32 -0
- data/bin/ahn +28 -0
- data/bin/ahnctl +68 -0
- data/bin/jahn +42 -0
- data/examples/asterisk_manager_interface/standalone.rb +51 -0
- data/lib/adhearsion/cli.rb +223 -0
- data/lib/adhearsion/component_manager/spec_framework.rb +24 -0
- data/lib/adhearsion/component_manager.rb +208 -0
- data/lib/adhearsion/events_support.rb +84 -0
- data/lib/adhearsion/foundation/all.rb +9 -0
- data/lib/adhearsion/foundation/blank_slate.rb +5 -0
- data/lib/adhearsion/foundation/custom_daemonizer.rb +45 -0
- data/lib/adhearsion/foundation/event_socket.rb +203 -0
- data/lib/adhearsion/foundation/future_resource.rb +36 -0
- data/lib/adhearsion/foundation/global.rb +1 -0
- data/lib/adhearsion/foundation/metaprogramming.rb +17 -0
- data/lib/adhearsion/foundation/numeric.rb +13 -0
- data/lib/adhearsion/foundation/pseudo_guid.rb +10 -0
- data/lib/adhearsion/foundation/relationship_properties.rb +42 -0
- data/lib/adhearsion/foundation/string.rb +26 -0
- data/lib/adhearsion/foundation/synchronized_hash.rb +96 -0
- data/lib/adhearsion/foundation/thread_safety.rb +7 -0
- data/lib/adhearsion/host_definitions.rb +67 -0
- data/lib/adhearsion/initializer/asterisk.rb +81 -0
- data/lib/adhearsion/initializer/configuration.rb +254 -0
- data/lib/adhearsion/initializer/database.rb +49 -0
- data/lib/adhearsion/initializer/drb.rb +31 -0
- data/lib/adhearsion/initializer/freeswitch.rb +22 -0
- data/lib/adhearsion/initializer/rails.rb +40 -0
- data/lib/adhearsion/initializer.rb +373 -0
- data/lib/adhearsion/logging.rb +92 -0
- data/lib/adhearsion/tasks/database.rb +5 -0
- data/lib/adhearsion/tasks/deprecations.rb +59 -0
- data/lib/adhearsion/tasks/generating.rb +20 -0
- data/lib/adhearsion/tasks/lint.rb +4 -0
- data/lib/adhearsion/tasks/testing.rb +37 -0
- data/lib/adhearsion/tasks.rb +16 -0
- data/lib/adhearsion/version.rb +9 -0
- data/lib/adhearsion/voip/asterisk/agi_server.rb +81 -0
- data/lib/adhearsion/voip/asterisk/commands.rb +1284 -0
- data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +140 -0
- data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +101 -0
- data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +250 -0
- data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +240 -0
- data/lib/adhearsion/voip/asterisk/config_manager.rb +71 -0
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +1754 -0
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +286 -0
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +78 -0
- data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +87 -0
- data/lib/adhearsion/voip/asterisk/manager_interface.rb +562 -0
- data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +80 -0
- data/lib/adhearsion/voip/asterisk/super_manager.rb +19 -0
- data/lib/adhearsion/voip/asterisk.rb +4 -0
- data/lib/adhearsion/voip/call.rb +440 -0
- data/lib/adhearsion/voip/call_routing.rb +64 -0
- data/lib/adhearsion/voip/commands.rb +9 -0
- data/lib/adhearsion/voip/constants.rb +39 -0
- data/lib/adhearsion/voip/conveniences.rb +18 -0
- data/lib/adhearsion/voip/dial_plan.rb +218 -0
- data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +37 -0
- data/lib/adhearsion/voip/dsl/dialing_dsl.rb +151 -0
- data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +27 -0
- data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +124 -0
- data/lib/adhearsion/voip/dsl/dialplan/parser.rb +71 -0
- data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +16 -0
- data/lib/adhearsion/voip/dsl/numerical_string.rb +117 -0
- data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +48 -0
- data/lib/adhearsion/voip/freeswitch/event_handler.rb +58 -0
- data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +129 -0
- data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +38 -0
- data/lib/adhearsion/voip/freeswitch/oes_server.rb +195 -0
- data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +80 -0
- data/lib/adhearsion/voip/menu_state_machine/matchers.rb +123 -0
- data/lib/adhearsion/voip/menu_state_machine/menu_builder.rb +58 -0
- data/lib/adhearsion/voip/menu_state_machine/menu_class.rb +149 -0
- data/lib/adhearsion.rb +37 -0
- data/lib/theatre/README.markdown +64 -0
- data/lib/theatre/callback_definition_loader.rb +84 -0
- data/lib/theatre/guid.rb +23 -0
- data/lib/theatre/invocation.rb +121 -0
- data/lib/theatre/namespace_manager.rb +153 -0
- data/lib/theatre/version.rb +2 -0
- data/lib/theatre.rb +151 -0
- metadata +177 -0
@@ -0,0 +1,218 @@
|
|
1
|
+
# Hardcoding require for now since for some reason it's not being loaded
|
2
|
+
require 'adhearsion/voip/dsl/dialplan/control_passing_exception'
|
3
|
+
|
4
|
+
module Adhearsion
|
5
|
+
class DialPlan
|
6
|
+
attr_accessor :loader, :entry_points
|
7
|
+
def initialize(loader = Loader)
|
8
|
+
@loader = loader
|
9
|
+
@entry_points = @loader.load_dialplans.contexts
|
10
|
+
end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Lookup and return an entry point by context name
|
14
|
+
#
|
15
|
+
def lookup(context_name)
|
16
|
+
entry_points[context_name]
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# Executable environment for a dial plan in the scope of a call. This class has all the dialplan methods mixed into it.
|
21
|
+
#
|
22
|
+
class ExecutionEnvironment
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def create(*args)
|
26
|
+
returning(new(*args)) { |instance| instance.stage! }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
attr_reader :call
|
31
|
+
def initialize(call, entry_point)
|
32
|
+
@call, @entry_point = call, entry_point
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Adds the methods to this ExecutionEnvironment which make it useful. e.g. dialplan-related methods, call variables,
|
37
|
+
# and component methods.
|
38
|
+
#
|
39
|
+
def stage!
|
40
|
+
extend_with_voip_commands!
|
41
|
+
extend_with_call_variables!
|
42
|
+
extend_with_dialplan_component_methods!
|
43
|
+
end
|
44
|
+
|
45
|
+
def run
|
46
|
+
raise "Cannot run ExecutionEnvironment without an entry point!" unless entry_point
|
47
|
+
current_context = entry_point
|
48
|
+
answer if AHN_CONFIG.automatically_answer_incoming_calls
|
49
|
+
begin
|
50
|
+
instance_eval(¤t_context)
|
51
|
+
rescue Adhearsion::VoIP::DSL::Dialplan::ControlPassingException => exception
|
52
|
+
current_context = exception.target
|
53
|
+
retry
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
attr_reader :entry_point
|
60
|
+
def extend_with_voip_commands!
|
61
|
+
extend Adhearsion::VoIP::Conveniences
|
62
|
+
extend Adhearsion::VoIP::Commands.for(call.originating_voip_platform)
|
63
|
+
end
|
64
|
+
|
65
|
+
def extend_with_call_variables!
|
66
|
+
call.define_variable_accessors self
|
67
|
+
end
|
68
|
+
|
69
|
+
def extend_with_dialplan_component_methods!
|
70
|
+
Components.component_manager.extend_object_with(self, :dialplan) if Components.component_manager
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
class Manager
|
76
|
+
|
77
|
+
class NoContextError < Exception; end
|
78
|
+
|
79
|
+
class << self
|
80
|
+
def handle(call)
|
81
|
+
new.handle(call)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
attr_accessor :dial_plan, :context
|
86
|
+
def initialize
|
87
|
+
@dial_plan = DialPlan.new
|
88
|
+
end
|
89
|
+
|
90
|
+
def handle(call)
|
91
|
+
if call.failed_call?
|
92
|
+
environment = ExecutionEnvironment.create(call, nil)
|
93
|
+
call.extract_failed_reason_from environment
|
94
|
+
raise FailedExtensionCallException.new(environment)
|
95
|
+
end
|
96
|
+
|
97
|
+
if call.hungup_call?
|
98
|
+
raise HungupExtensionCallException.new(ExecutionEnvironment.new(call, nil))
|
99
|
+
end
|
100
|
+
|
101
|
+
starting_entry_point = entry_point_for call
|
102
|
+
raise NoContextError, "No dialplan entry point for call context '#{call.context}' -- Ignoring call!" unless starting_entry_point
|
103
|
+
@context = ExecutionEnvironment.create(call, starting_entry_point)
|
104
|
+
inject_context_names_into_environment @context
|
105
|
+
@context.run
|
106
|
+
end
|
107
|
+
|
108
|
+
# Find the dialplan by the context name from the call or from the
|
109
|
+
# first path entry in the AGI URL
|
110
|
+
def entry_point_for(call)
|
111
|
+
if entry_point = dial_plan.lookup(call.context.to_sym)
|
112
|
+
entry_point
|
113
|
+
elsif call.respond_to?(:request) && m = call.request.path.match(%r{/([^/]+)})
|
114
|
+
dial_plan.lookup(m[1].to_sym)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
protected
|
119
|
+
|
120
|
+
def inject_context_names_into_environment(environment)
|
121
|
+
return unless dial_plan.entry_points
|
122
|
+
dial_plan.entry_points.each do |name, context|
|
123
|
+
environment.meta_def(name) { context }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
class Loader
|
130
|
+
class << self
|
131
|
+
attr_accessor :default_dial_plan_file_name
|
132
|
+
|
133
|
+
def load(dial_plan_as_string)
|
134
|
+
string_io = StringIO.new dial_plan_as_string
|
135
|
+
def string_io.path
|
136
|
+
"(eval)"
|
137
|
+
end
|
138
|
+
load_dialplans string_io
|
139
|
+
end
|
140
|
+
|
141
|
+
def load_dialplans(*files)
|
142
|
+
files = Adhearsion::AHN_CONFIG.files_from_setting("paths", "dialplan") if files.empty?
|
143
|
+
files = Array files
|
144
|
+
files.map! do |file|
|
145
|
+
case file
|
146
|
+
when File, StringIO
|
147
|
+
file
|
148
|
+
when String
|
149
|
+
File.new file
|
150
|
+
else
|
151
|
+
raise ArgumentError, "Unrecognized type of file #{file.inspect}"
|
152
|
+
end
|
153
|
+
end
|
154
|
+
returning new do |loader|
|
155
|
+
files.each do |file|
|
156
|
+
loader.load file
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
self.default_dial_plan_file_name ||= 'dialplan.rb'
|
164
|
+
|
165
|
+
def initialize
|
166
|
+
@context_collector = ContextNameCollector.new
|
167
|
+
end
|
168
|
+
|
169
|
+
def contexts
|
170
|
+
@context_collector.contexts
|
171
|
+
end
|
172
|
+
|
173
|
+
def load(dialplan_file)
|
174
|
+
dialplan_code = dialplan_file.read
|
175
|
+
@context_collector.instance_eval(dialplan_code, dialplan_file.path)
|
176
|
+
nil
|
177
|
+
end
|
178
|
+
|
179
|
+
class ContextNameCollector# < ::BlankSlate
|
180
|
+
|
181
|
+
class << self
|
182
|
+
|
183
|
+
def const_missing(name)
|
184
|
+
super
|
185
|
+
rescue ArgumentError
|
186
|
+
raise NameError, %(undefined constant "#{name}")
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
attr_reader :contexts
|
192
|
+
def initialize
|
193
|
+
@contexts = {}
|
194
|
+
end
|
195
|
+
|
196
|
+
def method_missing(name, *args, &block)
|
197
|
+
super if !block_given? || args.any?
|
198
|
+
contexts[name] = DialplanContextProc.new(name, &block)
|
199
|
+
end
|
200
|
+
|
201
|
+
end
|
202
|
+
end
|
203
|
+
class DialplanContextProc < Proc
|
204
|
+
|
205
|
+
attr_reader :name
|
206
|
+
|
207
|
+
def initialize(name, &block)
|
208
|
+
super(&block)
|
209
|
+
@name = name
|
210
|
+
end
|
211
|
+
|
212
|
+
def +@
|
213
|
+
raise Adhearsion::VoIP::DSL::Dialplan::ControlPassingException.new(self)
|
214
|
+
end
|
215
|
+
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Adhearsion
|
2
|
+
module VoIP
|
3
|
+
module DSL
|
4
|
+
class DialingDSL
|
5
|
+
module MonkeyPatches
|
6
|
+
module RegexpMonkeyPatch
|
7
|
+
|
8
|
+
def |(other)
|
9
|
+
case other
|
10
|
+
when RouteRule
|
11
|
+
other.unshift_pattern self
|
12
|
+
other
|
13
|
+
when Regexp
|
14
|
+
RouteRule.new :patterns => [self, other]
|
15
|
+
else
|
16
|
+
raise ArgumentError, "Unsupported pattern type #{other.inspect}"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def >>(other)
|
21
|
+
case other
|
22
|
+
when ProviderDefinition
|
23
|
+
RouteRule.new :patterns => self, :providers => other
|
24
|
+
when RouteRule
|
25
|
+
returning other do |route|
|
26
|
+
route.unshift_pattern self
|
27
|
+
end
|
28
|
+
else raise ArgumentError, "Unsupported route definition #{other.inspect}"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# Adhearsion, open source collaboration framework
|
2
|
+
# Copyright (C) 2006,2007,2008 Jay Phillips
|
3
|
+
#
|
4
|
+
# This library is free software; you can redistribute it and/or
|
5
|
+
# modify it under the terms of the GNU Lesser General Public
|
6
|
+
# License as published by the Free Software Foundation; either
|
7
|
+
# version 2.1 of the License, or (at your option) any later version.
|
8
|
+
#
|
9
|
+
# This library is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
12
|
+
# Lesser General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Lesser General Public
|
15
|
+
# License along with this library; if not, write to the Free Software
|
16
|
+
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
17
|
+
|
18
|
+
require 'adhearsion/foundation/metaprogramming'
|
19
|
+
require 'adhearsion/voip/conveniences'
|
20
|
+
require 'adhearsion/voip/constants'
|
21
|
+
require 'adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches'
|
22
|
+
|
23
|
+
module Adhearsion
|
24
|
+
module VoIP
|
25
|
+
module DSL
|
26
|
+
class DialingDSL
|
27
|
+
|
28
|
+
extend Conveniences
|
29
|
+
include Constants
|
30
|
+
|
31
|
+
Regexp.class_eval do
|
32
|
+
include Adhearsion::VoIP::DSL::DialingDSL::MonkeyPatches::RegexpMonkeyPatch
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.inherited(klass)
|
36
|
+
klass.class_eval do
|
37
|
+
[:@@providers, :@@routes].each do |var|
|
38
|
+
class_variable_set(var, [])
|
39
|
+
cattr_reader var.to_s.gsub('@@', '')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.calculate_routes_for(destination)
|
45
|
+
destination = destination.to_s
|
46
|
+
routes.select { |defined_route| defined_route === destination }.map &:providers
|
47
|
+
end
|
48
|
+
|
49
|
+
class ProviderDefinition < OpenStruct
|
50
|
+
|
51
|
+
def initialize(name)
|
52
|
+
super()
|
53
|
+
self.name = name.to_s.to_sym
|
54
|
+
end
|
55
|
+
|
56
|
+
def >>(other)
|
57
|
+
RouteRule.new :providers => [self, other]
|
58
|
+
end
|
59
|
+
|
60
|
+
def format_number_for_platform(number, platform=:asterisk)
|
61
|
+
case platform
|
62
|
+
when :asterisk
|
63
|
+
[protocol || "SIP", name || "default", number].join '/'
|
64
|
+
else
|
65
|
+
raise "Unsupported platform #{platform}!"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def format_number_for_platform(number, platform=:asterisk)
|
70
|
+
case platform
|
71
|
+
when :asterisk
|
72
|
+
[protocol || "SIP", name || "default", number].join '/'
|
73
|
+
else
|
74
|
+
raise "Unsupported platform #{platform}!"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def defined_properties_without_name
|
79
|
+
returning @table.clone do |copy|
|
80
|
+
copy.delete :name
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
protected
|
87
|
+
|
88
|
+
def self.provider(name, &block)
|
89
|
+
raise ArgumentError, "no block given" unless block_given?
|
90
|
+
|
91
|
+
options = ProviderDefinition.new name
|
92
|
+
yield options
|
93
|
+
|
94
|
+
providers << options
|
95
|
+
meta_def(name) { options }
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.route(route)
|
99
|
+
routes << route
|
100
|
+
end
|
101
|
+
|
102
|
+
class RouteRule
|
103
|
+
|
104
|
+
attr_reader :patterns, :providers
|
105
|
+
|
106
|
+
def initialize(hash={})
|
107
|
+
@patterns = Array hash[:patterns]
|
108
|
+
@providers = Array hash[:providers]
|
109
|
+
end
|
110
|
+
|
111
|
+
def merge!(other)
|
112
|
+
providers.concat other.providers
|
113
|
+
patterns.concat other.patterns
|
114
|
+
self
|
115
|
+
end
|
116
|
+
|
117
|
+
def >>(other)
|
118
|
+
case other
|
119
|
+
when RouteRule: merge! other
|
120
|
+
when ProviderDefinition: providers << other
|
121
|
+
else raise RouteException, "Unrecognized object in route definition: #{other.inspect}"
|
122
|
+
end
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
def |(other)
|
127
|
+
case other
|
128
|
+
when RouteRule: merge! other
|
129
|
+
when Regexp
|
130
|
+
patterns << other
|
131
|
+
self
|
132
|
+
else raise other.inspect
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def ===(other)
|
137
|
+
patterns.each { |pattern| return true if pattern === other }
|
138
|
+
false
|
139
|
+
end
|
140
|
+
|
141
|
+
def unshift_pattern(pattern)
|
142
|
+
patterns.unshift pattern
|
143
|
+
end
|
144
|
+
|
145
|
+
class RouteException < Exception; end
|
146
|
+
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Adhearsion
|
2
|
+
module VoIP
|
3
|
+
module DSL
|
4
|
+
module Dialplan
|
5
|
+
# A ControlPassingException is used internally to stop execution of one
|
6
|
+
# dialplan context and begin execution of another proc instead. It is
|
7
|
+
# most notably used by the ~@ unary operator that can be called on a
|
8
|
+
# context name within a dialplan to transfer control entirely to that
|
9
|
+
# particular context. The serve() method in the servlet_container actually
|
10
|
+
# rescues these exceptions specifically and then does +e.target to execute
|
11
|
+
# that code.
|
12
|
+
class ControlPassingException < Exception
|
13
|
+
|
14
|
+
attr_reader :target
|
15
|
+
|
16
|
+
def initialize(target)
|
17
|
+
super()
|
18
|
+
@target = target
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
class ContextNotFoundException < Exception; end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
module Adhearsion
|
2
|
+
module VoIP
|
3
|
+
module DSL
|
4
|
+
module Dialplan
|
5
|
+
|
6
|
+
class ReturnValue < Exception
|
7
|
+
attr_reader :obj
|
8
|
+
def initialize(obj)
|
9
|
+
@obj = obj
|
10
|
+
super
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Hangup < Exception; end
|
15
|
+
|
16
|
+
# Instantiated and returned in every dialplan command
|
17
|
+
class EventCommand
|
18
|
+
|
19
|
+
attr_accessor :app, :args, :response_block, :returns, :on_keypress
|
20
|
+
|
21
|
+
def initialize(app, *args, &block)
|
22
|
+
@hash = args.pop if args.last.kind_of?(Hash)
|
23
|
+
@app, @args = app, args
|
24
|
+
|
25
|
+
if @hash
|
26
|
+
@returns = @hash[:returns] || :raw
|
27
|
+
@on_keypress = @hash[:on_keypress]
|
28
|
+
end
|
29
|
+
|
30
|
+
@response_block = block if block_given?
|
31
|
+
end
|
32
|
+
|
33
|
+
def on_keypress(&block)
|
34
|
+
block_given? ? @on_keypress = block : @on_keypress
|
35
|
+
end
|
36
|
+
|
37
|
+
def on_break(&block)
|
38
|
+
block_given? ? @on_break = block : @on_break
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
class NoOpEventCommand < EventCommand
|
44
|
+
attr_reader :timeout, :on_keypress
|
45
|
+
def initialize(timeout=nil, hash={})
|
46
|
+
@timeout = timeout
|
47
|
+
@on_keypress = hash[:on_keypress]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class ExitingEventCommand < EventCommand; end
|
52
|
+
|
53
|
+
# Serves as a EventCommand proxy for handling EventCommands. Useful
|
54
|
+
# if doing any pre-processing.
|
55
|
+
#
|
56
|
+
# For example, when sending commands over the event socket to FreeSwitch,
|
57
|
+
# every command must start with "api", but, when doing an originate, it
|
58
|
+
# must begin with an ampersand. These two pre-processors would be developed
|
59
|
+
# as separate CommandDispatchers.
|
60
|
+
class CommandDispatcher
|
61
|
+
|
62
|
+
attr_reader :factory, :context
|
63
|
+
|
64
|
+
def initialize(factory, context=nil)
|
65
|
+
@context = context
|
66
|
+
@factory = factory.new context
|
67
|
+
end
|
68
|
+
|
69
|
+
def dispatch!(event_command)
|
70
|
+
raise NotImplementedError, "Must subclass #{self.class} and override this!"
|
71
|
+
end
|
72
|
+
|
73
|
+
def method_missing(name, *args, &block)
|
74
|
+
*commands = *@factory.send(name, *args, &block)
|
75
|
+
commands.map do |command|
|
76
|
+
if command.kind_of? Proc
|
77
|
+
instance_eval(&command)
|
78
|
+
elsif command.kind_of? EventCommand
|
79
|
+
dispatched_command = dispatch! command
|
80
|
+
if command.response_block
|
81
|
+
while (new_cmd = command.response_block.call(dispatched_command)).kind_of? EventCommand
|
82
|
+
dispatched_command = dispatch! new_cmd
|
83
|
+
end
|
84
|
+
end
|
85
|
+
dispatched_command
|
86
|
+
else
|
87
|
+
command
|
88
|
+
end
|
89
|
+
end.last
|
90
|
+
rescue ReturnValue => r
|
91
|
+
return r.obj
|
92
|
+
end
|
93
|
+
|
94
|
+
def return!(obj)
|
95
|
+
raise DSL::Dialplan::ReturnValue.new(obj)
|
96
|
+
end
|
97
|
+
|
98
|
+
def break!(uuid=@context)
|
99
|
+
raise NotImplementedError, "Must subclass #{self.class} and override this!"
|
100
|
+
end
|
101
|
+
|
102
|
+
# Takes a Hash and meta_def()'s a method for each key that returns
|
103
|
+
# the key's value in the Hash.
|
104
|
+
def def_keys!(hash)
|
105
|
+
hash.each_pair do |k,v|
|
106
|
+
meta_def(k) { v } rescue nil
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def clone
|
111
|
+
returning super do |nemesis|
|
112
|
+
instance_variables.each do |iv|
|
113
|
+
value = instance_variable_get(iv)
|
114
|
+
nemesis.instance_variable_set iv, value.clone if value
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Adhearsion
|
4
|
+
module VoIP
|
5
|
+
module DSL
|
6
|
+
module Dialplan
|
7
|
+
#TODO: This is obsolete, but we still need it for Freeswitch until we port that to the new 0.8.0 APIs
|
8
|
+
module DialplanParser
|
9
|
+
|
10
|
+
# Create a container and then clone that. when the container is created
|
11
|
+
# it should have all the contexts meta_def'd into it. for each call coming
|
12
|
+
# in, the cloned copy has the variables meta_def'd and set as instance
|
13
|
+
# variables
|
14
|
+
|
15
|
+
#TODO: separate into smaller pieces
|
16
|
+
def self.get_contexts
|
17
|
+
envelope = ContextsEnvelope.new
|
18
|
+
|
19
|
+
dialplans = AHN_CONFIG.files_from_setting "paths", "dialplan"
|
20
|
+
ahn_log.dialplan.warn "No dialplan files were found!" if dialplans.empty?
|
21
|
+
|
22
|
+
returning({}) do |contexts|
|
23
|
+
dialplans.each do |file|
|
24
|
+
raise "Dialplan file #{file} does not exist!" unless File.exists? file
|
25
|
+
envelope.instance_eval do
|
26
|
+
eval File.read(file)
|
27
|
+
end
|
28
|
+
current_contexts = envelope.parsed_contexts
|
29
|
+
current_contexts.each_pair do |name, block|
|
30
|
+
if contexts.has_key? name
|
31
|
+
warn %'Dialplan context "#{name}" exists in both #{contexts[name].file} and #{file}.' +
|
32
|
+
%' Using the "#{name}" context from #{contexts[name].file}.'
|
33
|
+
else
|
34
|
+
contexts[name] = returning OpenStruct.new do |metadata|
|
35
|
+
metadata.file = file
|
36
|
+
metadata.name = name
|
37
|
+
metadata.block = block
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class ContextsEnvelope
|
47
|
+
|
48
|
+
keep = %w"__send__ __id__ define_method instance_eval meta_def meta_eval metaclass"
|
49
|
+
(instance_methods - keep).each do |m|
|
50
|
+
undef_method m
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize
|
54
|
+
@parsed_contexts = {}
|
55
|
+
end
|
56
|
+
|
57
|
+
attr_reader :parsed_contexts
|
58
|
+
|
59
|
+
def method_missing(name, *args, &block)
|
60
|
+
super unless block_given?
|
61
|
+
@parsed_contexts[name] = block
|
62
|
+
meta_def(name) { block }
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|