adhearsion 0.7.7 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. data/CHANGELOG +9 -42
  2. data/EVENTS +11 -0
  3. data/README.txt +5 -0
  4. data/Rakefile +94 -84
  5. data/adhearsion.gemspec +148 -0
  6. data/app_generators/ahn/USAGE +5 -0
  7. data/app_generators/ahn/ahn_generator.rb +87 -0
  8. data/app_generators/ahn/templates/.ahnrc +34 -0
  9. data/app_generators/ahn/templates/README +8 -0
  10. data/app_generators/ahn/templates/Rakefile +23 -0
  11. data/app_generators/ahn/templates/components/ami_remote/ami_remote.rb +15 -0
  12. data/app_generators/ahn/templates/components/disabled/HOW_TO_ENABLE +7 -0
  13. data/app_generators/ahn/templates/components/disabled/stomp_gateway/README.markdown +47 -0
  14. data/app_generators/ahn/templates/components/disabled/stomp_gateway/config.yml +12 -0
  15. data/app_generators/ahn/templates/components/disabled/stomp_gateway/stomp_gateway.rb +34 -0
  16. data/app_generators/ahn/templates/components/restful_rpc/README.markdown +11 -0
  17. data/app_generators/ahn/templates/components/restful_rpc/config.yml +34 -0
  18. data/app_generators/ahn/templates/components/restful_rpc/example-client.rb +48 -0
  19. data/app_generators/ahn/templates/components/restful_rpc/restful_rpc.rb +87 -0
  20. data/app_generators/ahn/templates/components/restful_rpc/spec/restful_rpc_spec.rb +263 -0
  21. data/app_generators/ahn/templates/components/simon_game/simon_game.rb +56 -0
  22. data/app_generators/ahn/templates/config/startup.rb +50 -0
  23. data/app_generators/ahn/templates/dialplan.rb +3 -0
  24. data/app_generators/ahn/templates/events.rb +32 -0
  25. data/bin/ahn +28 -0
  26. data/bin/ahnctl +68 -0
  27. data/bin/jahn +42 -0
  28. data/examples/asterisk_manager_interface/standalone.rb +51 -0
  29. data/lib/adhearsion.rb +35 -953
  30. data/lib/adhearsion/cli.rb +223 -0
  31. data/lib/adhearsion/component_manager.rb +208 -0
  32. data/lib/adhearsion/component_manager/component_tester.rb +55 -0
  33. data/lib/adhearsion/component_manager/spec_framework.rb +24 -0
  34. data/lib/adhearsion/events_support.rb +84 -0
  35. data/lib/adhearsion/foundation/all.rb +9 -0
  36. data/lib/adhearsion/foundation/blank_slate.rb +5 -0
  37. data/lib/adhearsion/foundation/custom_daemonizer.rb +45 -0
  38. data/lib/adhearsion/foundation/event_socket.rb +203 -0
  39. data/lib/adhearsion/foundation/future_resource.rb +36 -0
  40. data/lib/adhearsion/foundation/global.rb +1 -0
  41. data/lib/adhearsion/foundation/metaprogramming.rb +17 -0
  42. data/lib/adhearsion/foundation/numeric.rb +13 -0
  43. data/lib/adhearsion/foundation/pseudo_guid.rb +10 -0
  44. data/lib/adhearsion/foundation/relationship_properties.rb +42 -0
  45. data/lib/adhearsion/foundation/string.rb +26 -0
  46. data/lib/adhearsion/foundation/synchronized_hash.rb +96 -0
  47. data/lib/adhearsion/foundation/thread_safety.rb +7 -0
  48. data/lib/adhearsion/host_definitions.rb +67 -0
  49. data/lib/adhearsion/initializer.rb +373 -0
  50. data/lib/adhearsion/initializer/asterisk.rb +81 -0
  51. data/lib/adhearsion/initializer/configuration.rb +254 -0
  52. data/lib/adhearsion/initializer/database.rb +49 -0
  53. data/lib/adhearsion/initializer/drb.rb +31 -0
  54. data/lib/adhearsion/initializer/freeswitch.rb +22 -0
  55. data/lib/adhearsion/initializer/rails.rb +40 -0
  56. data/lib/adhearsion/logging.rb +92 -0
  57. data/lib/adhearsion/tasks.rb +16 -0
  58. data/lib/adhearsion/tasks/database.rb +5 -0
  59. data/lib/adhearsion/tasks/deprecations.rb +59 -0
  60. data/lib/adhearsion/tasks/generating.rb +20 -0
  61. data/lib/adhearsion/tasks/lint.rb +4 -0
  62. data/lib/adhearsion/tasks/testing.rb +37 -0
  63. data/lib/adhearsion/version.rb +9 -0
  64. data/lib/adhearsion/voip/asterisk.rb +4 -0
  65. data/lib/adhearsion/voip/asterisk/agi_server.rb +81 -0
  66. data/lib/adhearsion/voip/asterisk/commands.rb +1284 -0
  67. data/lib/adhearsion/voip/asterisk/config_generators/agents.conf.rb +140 -0
  68. data/lib/adhearsion/voip/asterisk/config_generators/config_generator.rb +101 -0
  69. data/lib/adhearsion/voip/asterisk/config_generators/queues.conf.rb +250 -0
  70. data/lib/adhearsion/voip/asterisk/config_generators/voicemail.conf.rb +240 -0
  71. data/lib/adhearsion/voip/asterisk/config_manager.rb +71 -0
  72. data/lib/adhearsion/voip/asterisk/manager_interface.rb +562 -0
  73. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rb +1754 -0
  74. data/lib/adhearsion/voip/asterisk/manager_interface/ami_lexer.rl.rb +286 -0
  75. data/lib/adhearsion/voip/asterisk/manager_interface/ami_messages.rb +78 -0
  76. data/lib/adhearsion/voip/asterisk/manager_interface/ami_protocol_lexer_machine.rl +87 -0
  77. data/lib/adhearsion/voip/asterisk/special_dial_plan_managers.rb +80 -0
  78. data/lib/adhearsion/voip/asterisk/super_manager.rb +19 -0
  79. data/lib/adhearsion/voip/call.rb +440 -0
  80. data/lib/adhearsion/voip/call_routing.rb +64 -0
  81. data/lib/adhearsion/voip/commands.rb +9 -0
  82. data/lib/adhearsion/voip/constants.rb +39 -0
  83. data/lib/adhearsion/voip/conveniences.rb +18 -0
  84. data/lib/adhearsion/voip/dial_plan.rb +218 -0
  85. data/lib/adhearsion/voip/dsl/dialing_dsl.rb +151 -0
  86. data/lib/adhearsion/voip/dsl/dialing_dsl/dialing_dsl_monkey_patches.rb +37 -0
  87. data/lib/adhearsion/voip/dsl/dialplan/control_passing_exception.rb +27 -0
  88. data/lib/adhearsion/voip/dsl/dialplan/dispatcher.rb +124 -0
  89. data/lib/adhearsion/voip/dsl/dialplan/parser.rb +71 -0
  90. data/lib/adhearsion/voip/dsl/dialplan/thread_mixin.rb +16 -0
  91. data/lib/adhearsion/voip/dsl/numerical_string.rb +117 -0
  92. data/lib/adhearsion/voip/freeswitch/basic_connection_manager.rb +48 -0
  93. data/lib/adhearsion/voip/freeswitch/event_handler.rb +58 -0
  94. data/lib/adhearsion/voip/freeswitch/freeswitch_dialplan_command_factory.rb +129 -0
  95. data/lib/adhearsion/voip/freeswitch/inbound_connection_manager.rb +38 -0
  96. data/lib/adhearsion/voip/freeswitch/oes_server.rb +195 -0
  97. data/lib/adhearsion/voip/menu_state_machine/calculated_match.rb +80 -0
  98. data/lib/adhearsion/voip/menu_state_machine/matchers.rb +123 -0
  99. data/lib/adhearsion/voip/menu_state_machine/menu_builder.rb +58 -0
  100. data/lib/adhearsion/voip/menu_state_machine/menu_class.rb +149 -0
  101. data/lib/theatre.rb +151 -0
  102. data/lib/theatre/README.markdown +64 -0
  103. data/lib/theatre/callback_definition_loader.rb +84 -0
  104. data/lib/theatre/guid.rb +23 -0
  105. data/lib/theatre/invocation.rb +121 -0
  106. data/lib/theatre/namespace_manager.rb +153 -0
  107. data/lib/theatre/version.rb +2 -0
  108. metadata +160 -140
  109. data/.version +0 -1
  110. data/TODO +0 -71
  111. data/ahn +0 -223
  112. data/apps/default/Rakefile +0 -65
  113. data/apps/default/config/adhearsion.sqlite3 +0 -0
  114. data/apps/default/config/adhearsion.yml +0 -95
  115. data/apps/default/config/database.rb +0 -50
  116. data/apps/default/config/database.yml +0 -12
  117. data/apps/default/config/helpers/drb_server.yml +0 -43
  118. data/apps/default/config/helpers/factorial.alien.c.yml +0 -1
  119. data/apps/default/config/helpers/growler.yml +0 -21
  120. data/apps/default/config/helpers/lookup.yml +0 -1
  121. data/apps/default/config/helpers/manager_proxy.yml +0 -8
  122. data/apps/default/config/helpers/micromenus.yml +0 -1
  123. data/apps/default/config/helpers/micromenus/collab.rb +0 -60
  124. data/apps/default/config/helpers/micromenus/images/arrow-off.gif +0 -0
  125. data/apps/default/config/helpers/micromenus/images/arrow-on.gif +0 -0
  126. data/apps/default/config/helpers/micromenus/images/error.gif +0 -0
  127. data/apps/default/config/helpers/micromenus/images/folder-off.gif +0 -0
  128. data/apps/default/config/helpers/micromenus/images/folder-on.gif +0 -0
  129. data/apps/default/config/helpers/micromenus/images/folder.png +0 -0
  130. data/apps/default/config/helpers/micromenus/images/ggbridge.jpg +0 -0
  131. data/apps/default/config/helpers/micromenus/images/green.png +0 -0
  132. data/apps/default/config/helpers/micromenus/images/microbrowser.bg.gif +0 -0
  133. data/apps/default/config/helpers/micromenus/images/red.png +0 -0
  134. data/apps/default/config/helpers/micromenus/images/tux.bmp +0 -0
  135. data/apps/default/config/helpers/micromenus/images/url-off.gif +0 -0
  136. data/apps/default/config/helpers/micromenus/images/url-on.gif +0 -0
  137. data/apps/default/config/helpers/micromenus/images/yellow.png +0 -0
  138. data/apps/default/config/helpers/micromenus/javascripts/animation.js +0 -1341
  139. data/apps/default/config/helpers/micromenus/javascripts/carousel.js +0 -1238
  140. data/apps/default/config/helpers/micromenus/javascripts/columnav.js +0 -306
  141. data/apps/default/config/helpers/micromenus/javascripts/connection.js +0 -965
  142. data/apps/default/config/helpers/micromenus/javascripts/container.js +0 -4727
  143. data/apps/default/config/helpers/micromenus/javascripts/container_core.js +0 -2915
  144. data/apps/default/config/helpers/micromenus/javascripts/dom.js +0 -892
  145. data/apps/default/config/helpers/micromenus/javascripts/dragdrop.js +0 -2958
  146. data/apps/default/config/helpers/micromenus/javascripts/event.js +0 -1771
  147. data/apps/default/config/helpers/micromenus/javascripts/yahoo.js +0 -433
  148. data/apps/default/config/helpers/micromenus/stylesheets/carousel.css +0 -78
  149. data/apps/default/config/helpers/micromenus/stylesheets/columnav.css +0 -135
  150. data/apps/default/config/helpers/micromenus/stylesheets/microbrowsers.css +0 -42
  151. data/apps/default/config/helpers/multi_messenger.yml +0 -9
  152. data/apps/default/config/helpers/weather.yml +0 -1
  153. data/apps/default/config/helpers/xbmc.yml +0 -2
  154. data/apps/default/config/migration.rb +0 -59
  155. data/apps/default/extensions.rb +0 -41
  156. data/apps/default/helpers/factorial.alien.c +0 -32
  157. data/apps/default/helpers/growler.rb +0 -53
  158. data/apps/default/helpers/lookup.rb +0 -44
  159. data/apps/default/helpers/manager_proxy.rb +0 -112
  160. data/apps/default/helpers/micromenus.rb +0 -514
  161. data/apps/default/helpers/multi_messenger.rb +0 -53
  162. data/apps/default/helpers/oscar_wilde_quotes.rb +0 -197
  163. data/apps/default/helpers/weather.rb +0 -85
  164. data/apps/default/helpers/xbmc.rb +0 -39
  165. data/apps/default/logs/adhearsion.log +0 -0
  166. data/apps/default/logs/database.log +0 -0
  167. data/lib/constants.rb +0 -24
  168. data/lib/core_extensions.rb +0 -180
  169. data/lib/drb_server.rb +0 -101
  170. data/lib/logging.rb +0 -85
  171. data/lib/phone_number.rb +0 -85
  172. data/lib/rami.rb +0 -823
  173. data/lib/servlet_container.rb +0 -174
  174. data/lib/sexy_migrations.rb +0 -70
  175. data/test/asterisk_module_test.rb +0 -14
  176. data/test/core_extensions_test.rb +0 -26
  177. data/test/dial_test.rb +0 -43
  178. data/test/specs/numerical_string_spec.rb +0 -53
  179. data/test/test_micromenus.rb +0 -0
@@ -0,0 +1,140 @@
1
+ require File.join(File.dirname(__FILE__), 'config_generator')
2
+
3
+ module Adhearsion
4
+ module VoIP
5
+ module Asterisk
6
+ module ConfigFileGenerators
7
+ class Agents < AsteriskConfigGenerator
8
+
9
+ attr_accessor :general_section, :agent_section, :agent_definitions, :agent_section_special
10
+ def initialize
11
+ @general_section = {}
12
+ @agent_section = {}
13
+ @agent_section_special = {} # Uses => separator
14
+ @agent_definitions = []
15
+
16
+ super
17
+ end
18
+
19
+ def to_s
20
+ AsteriskConfigGenerator.warning_message +
21
+ general_section.inject("[general]") { |section,(key,value)| section + "\n#{key}=#{value}" } +
22
+ agent_section.inject("\n[agents]") { |section,(key,value)| section + "\n#{key}=#{value}" } +
23
+ agent_section_special.inject("") { |section,(key,value)| section + "\n#{key} => #{value}" } +
24
+ agent_definitions.inject("\n") do |section,properties|
25
+ section + "\nagent => #{properties[:id]},#{properties[:password]},#{properties[:name]}"
26
+ end
27
+ end
28
+ alias conf to_s # Allows "agents.conf" if agents.kind_of?(Agents)
29
+
30
+ def agent(id, properties)
31
+ agent_definitions << {:id => id}.merge(properties)
32
+ end
33
+
34
+ # Group memberships for agents (may change in mid-file)
35
+ # WHAT DOES GROUPING ACCOMPLISH?
36
+ def groups(*args)
37
+ agent_section[:group] = args.join(",")
38
+ end
39
+
40
+ # Define whether callbacklogins should be stored in astdb for
41
+ # persistence. Persistent logins will be reloaded after
42
+ # Asterisk restarts.
43
+ def persistent_agents(yes_or_no)
44
+ general_section[:persistentagents] = boolean_to_yes_no yes_or_no
45
+ end
46
+
47
+ # enable or disable a single extension from longing in as multiple
48
+ # agents, defaults to enabled
49
+ def allow_multiple_logins_per_extension(yes_or_no)
50
+ general_section[:multiplelogin] = boolean_to_yes_no yes_or_no
51
+ end
52
+
53
+ # Define maxlogintries to allow agent to try max logins before
54
+ # failed. Default to 3
55
+ def max_login_tries(number_of_tries)
56
+ agent_section[:maxlogintries] = number_of_tries
57
+ end
58
+
59
+ # Define autologoff times if appropriate. This is how long
60
+ # the phone has to ring with no answer before the agent is
61
+ # automatically logged off (in seconds)
62
+ def log_off_after_duration(time_in_seconds)
63
+ agent_section[:autologoff] = time_in_seconds
64
+ end
65
+
66
+ # Define autologoffunavail to have agents automatically logged
67
+ # out when the extension that they are at returns a CHANUNAVAIL
68
+ # status when a call is attempted to be sent there.
69
+ # Default is "no".
70
+ def log_off_if_unavailable(yes_or_no)
71
+ agent_section[:autologoffunavail] = boolean_to_yes_no yes_or_no
72
+ end
73
+
74
+ # Define ackcall to require an acknowledgement by '#' when
75
+ # an agent logs in using agentcallbacklogin. Default is "no".
76
+ def require_hash_to_acknowledge(yes_or_no)
77
+ agent_section[:ackcall] = boolean_to_yes_no yes_or_no
78
+ end
79
+
80
+ # Define endcall to allow an agent to hangup a call by '*'.
81
+ # Default is "yes". Set this to "no" to ignore '*'.
82
+ def allow_star_to_hangup(yes_or_no)
83
+ agent_section[:endcall] = boolean_to_yes_no yes_or_no
84
+ end
85
+
86
+ # Define wrapuptime. This is the minimum amount of time when
87
+ # after disconnecting before the caller can receive a new call
88
+ # note this is in milliseconds.
89
+ def time_between_calls(time_in_seconds)
90
+ agent_section[:wrapuptime] = (time_in_seconds * 1000).to_i
91
+ end
92
+
93
+ # Define the default musiconhold for agents
94
+ # musiconhold => music_class
95
+ def hold_music_class(music_class)
96
+ agent_section_special[:musiconhold] = music_class.to_s
97
+ end
98
+
99
+ # TODO: I'm not exactly sure what this even does....
100
+
101
+ # Define the default good bye sound file for agents
102
+ # default to vm-goodbye
103
+ def play_on_agent_goodbye(sound_file_name)
104
+ agent_section_special[:agentgoodbye] = sound_file_name
105
+ end
106
+
107
+ # Define updatecdr. This is whether or not to change the source
108
+ # channel in the CDR record for this call to agent/agent_id so
109
+ # that we know which agent generates the call
110
+ def change_cdr_source(yes_or_no)
111
+ agent_section[:updatecdr] = boolean_to_yes_no yes_or_no
112
+ end
113
+
114
+ # An optional custom beep sound file to play to always-connected agents.
115
+ def play_for_waiting_keep_alive(sound_file)
116
+ agent_section[:custom_beep] = sound_file
117
+ end
118
+
119
+ def record_agent_calls(yes_or_no)
120
+ agent_section[:recordagentcalls] = boolean_to_yes_no yes_or_no
121
+ end
122
+
123
+ def recording_format(symbol)
124
+ raise ArgumentError, "Unrecognized format #{symbol}" unless [:wav, :wav49, :gsm].include? symbol
125
+ agent_section[:recordformat] = symbol
126
+ end
127
+
128
+ def recording_prefix(string)
129
+ agent_section[:urlprefix] = string
130
+ end
131
+
132
+ def save_recordings_in(path_to_directory)
133
+ agent_section[:savecallsin] = path_to_directory
134
+ end
135
+
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,101 @@
1
+ module Adhearsion
2
+ module VoIP
3
+ module Asterisk
4
+ module ConfigFileGenerators
5
+ class AsteriskConfigGenerator
6
+
7
+ SECTION_TITLE = /(\[[\w_-]+\])/
8
+
9
+ class << self
10
+
11
+ # Converts a config file into a Hash of contexts mapping to two dimensional array of pairs
12
+ def create_sanitary_hash_from(config_file_content)
13
+ almost_sanitized = Hash[*config_file_content.
14
+ grep(/^\s*[^;\s]/). # Grep lines that aren't commented out
15
+ join. # Convert them into one String again
16
+ split(SECTION_TITLE). # Separate them into sections
17
+ map(&:strip). # Remove all whitespace
18
+ reject(&:empty?). # Get rid of indices that were only whitespace
19
+ # Lastly, separate the keys/value pairs for the Hash
20
+ map { |token| token =~ /^#{SECTION_TITLE}$/ ? token : token.split(/\n+/).sort }
21
+ ]
22
+ end
23
+
24
+ def warning_message
25
+ %{;; THIS FILE WAS GENERATED BY ADHEARSION ON #{Time.now.ctime}!\n} +
26
+ %{;; ANY CHANGES MADE BELOW WILL BE BLOWN AWAY WHEN THE FILE IS REGENERATED!\n\n}
27
+ end
28
+
29
+ end
30
+
31
+ def initialize
32
+ yield self if block_given?
33
+ end
34
+
35
+ def to_sanitary_hash
36
+ self.class.create_sanitary_hash_from to_s
37
+ end
38
+
39
+ protected
40
+
41
+ def boolean(options)
42
+ cache = options.delete(:with) || properties
43
+ options.each_pair do |key, value|
44
+ cache[key] = boolean_to_yes_no value
45
+ end
46
+ end
47
+
48
+ def string(options)
49
+ cache = options.delete(:with) || properties
50
+ options.each_pair do |key, value|
51
+ cache[key] = value
52
+ end
53
+ end
54
+
55
+ def int(options)
56
+ cache = options.delete(:with) || properties
57
+ options.each_pair do |property,number|
58
+ number = number.to_i if (number.kind_of?(String) && number =~ /^\d+$/) || number.kind_of?(Numeric)
59
+ raise ArgumentError, "#{number.inspect} must be an integer" unless number.kind_of?(Fixnum)
60
+ cache[property] = number.to_i
61
+ end
62
+ end
63
+
64
+ def one_of(criteria, options)
65
+ cache = options.delete(:with) || properties
66
+ options.each_pair do |key, value|
67
+ search = !criteria.find { |criterion| criterion === value }.nil?
68
+ unless search
69
+ msg = "Didn't recognize #{value.inspect}! Must be one of " + criteria.map(&:inspect).to_sentence
70
+ raise ArgumentError, msg
71
+ end
72
+ cache[key] = [true, false].include?(value) ? boolean_to_yes_no(value) : value
73
+ end
74
+ end
75
+
76
+ def one_of_and_translate(criteria, options)
77
+ cache = options.delete(:with) || properties
78
+ options.each_pair do |key, value|
79
+ search = criteria.keys.find { |criterion| criterion === value }
80
+ unless search
81
+ msg = "Didn't recognize #{value.inspect}! Must be one of " + criteria.keys.map(&:inspect).to_sentence
82
+ raise ArgumentError, msg
83
+ end
84
+ cache[key] = criteria[value]
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ def boolean_to_yes_no(boolean)
91
+ unless boolean.equal?(boolean) || boolean.equal?(boolean)
92
+ raise "#{boolean.inspect} is not true/false!"
93
+ end
94
+ boolean ? 'yes' : 'no'
95
+ end
96
+
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,250 @@
1
+ require File.join(File.dirname(__FILE__), 'config_generator')
2
+
3
+ module Adhearsion
4
+ module VoIP
5
+ module Asterisk
6
+ module ConfigFileGenerators
7
+
8
+ # This will generate a queues.conf file. If there is no documentation on what a method
9
+ # actually does, take a look at the documentation for its original key/value pair in
10
+ # an unedited queues.conf file. WARNING! Don't get too embedded with these method names.
11
+ # I'm still not satisfied. These settings will be greatly abstracted eventually.
12
+ class Queues < AsteriskConfigGenerator
13
+
14
+ DEFAULT_GENERAL_SECTION = {
15
+ :autofill => "yes"
16
+ }
17
+
18
+ attr_reader :general_section, :queue_definitions, :properties
19
+ def initialize
20
+ @general_section = DEFAULT_GENERAL_SECTION.clone
21
+ @properties = {}
22
+ @queue_definitions = []
23
+ super
24
+ end
25
+
26
+ def queue(name)
27
+ new_queue = QueueDefinition.new name
28
+ yield new_queue if block_given?
29
+ queue_definitions << new_queue
30
+ new_queue
31
+ end
32
+
33
+ def to_s
34
+ AsteriskConfigGenerator.warning_message +
35
+ general_section.inject("[general]") { |section,(key,value)| section + "\n#{key}=#{value}" } + "\n\n" +
36
+ queue_definitions.map(&:to_s).join("\n\n")
37
+ end
38
+ alias conf to_s
39
+
40
+ def persistent_members(yes_no)
41
+ boolean :persistentmembers => yes_no, :with => general_section
42
+ end
43
+
44
+ def monitor_type(symbol)
45
+ criteria = {:monitor => "Monitor", :mix_monitor => "MixMonitor"}
46
+ one_of_and_translate criteria, 'monitor-type' => symbol, :with => general_section
47
+ end
48
+
49
+
50
+ class QueueDefinition < AsteriskConfigGenerator
51
+
52
+ DEFAULT_QUEUE_PROPERTIES = {
53
+ :autofill => 'yes',
54
+ :eventwhencalled => 'vars',
55
+ :eventmemberstatus => 'yes',
56
+ :setinterfacevar => 'yes'
57
+ }
58
+
59
+ SUPPORTED_RING_STRATEGIES = [:ringall, :roundrobin, :leastrecent, :fewestcalls, :random, :rrmemory]
60
+
61
+ DEFAULT_SOUND_FILES = {
62
+ 'queue-youarenext' => 'queue-youarenext',
63
+ 'queue-thereare' => 'queue-thereare',
64
+ 'queue-callswaiting' => 'queue-callswaiting',
65
+ 'queue-holdtime' => 'queue-holdtime',
66
+ 'queue-minutes' => 'queue-minutes',
67
+ 'queue-seconds' => 'queue-seconds',
68
+ 'queue-thankyou' => 'queue-thankyou',
69
+ 'queue-lessthan' => 'queue-less-than',
70
+ 'queue-reporthold' => 'queue-reporthold',
71
+ 'periodic-announce' => 'queue-periodic-announce'
72
+ }
73
+
74
+ SOUND_FILE_SYMBOL_INTERPRETATIONS = {
75
+ :you_are_next => 'queue-youarenext',
76
+ :there_are => 'queue-thereare',
77
+ :calls_waiting => 'queue-callswaiting',
78
+ :hold_time => 'queue-holdtime',
79
+ :minutes => 'queue-minutes',
80
+ :seconds => 'queue-seconds',
81
+ :thank_you => 'queue-thankyou',
82
+ :less_than => 'queue-lessthan',
83
+ :report_hold => 'queue-reporthold',
84
+ :periodic_announcement => 'periodic-announce'
85
+ }
86
+
87
+ attr_reader :members, :name, :properties
88
+ def initialize(name)
89
+ @name = name
90
+ @members = []
91
+ @properties = DEFAULT_QUEUE_PROPERTIES.clone
92
+ @sound_files = DEFAULT_SOUND_FILES.clone
93
+ end
94
+
95
+ def to_s
96
+ "[#{name}]\n" +
97
+ properties.merge(@sound_files).map { |key, value| "#{key}=#{value}" }.sort.join("\n") + "\n\n" +
98
+ members.map { |member| "member => #{member}" }.join("\n")
99
+ end
100
+
101
+ def music_class(moh_identifier)
102
+ string :musicclass => moh_identifier
103
+ end
104
+
105
+ def play_on_connect(sound_file)
106
+ string :announce => sound_file
107
+ end
108
+
109
+ def strategy(symbol)
110
+ one_of SUPPORTED_RING_STRATEGIES, :strategy => symbol
111
+ end
112
+
113
+ def service_level(seconds)
114
+ int :servicelevel => seconds
115
+ end
116
+
117
+ # A context may be specified, in which if the user types a SINGLE
118
+ # digit extension while they are in the queue, they will be taken out
119
+ # of the queue and sent to that extension in this context. This context
120
+ # should obviously be a different context other than the one that
121
+ # normally forwards to Adhearsion (though the context that handles
122
+ # these digits should probably go out to Adhearsion too).
123
+ def exit_to_context_on_digit_press(context_name)
124
+ string :context => context_name
125
+ end
126
+
127
+ # Ex: ring_timeout 15.seconds
128
+ def ring_timeout(seconds)
129
+ int :timeout => seconds
130
+ end
131
+
132
+ # Ex: retry_after_waiting 5.seconds
133
+ def retry_after_waiting(seconds)
134
+ int :retry => seconds
135
+ end
136
+
137
+ # THIS IS UNSUPPORTED
138
+ def weight(number)
139
+ int :weight => number
140
+ end
141
+
142
+ # Ex: wrapup_time 1.minute
143
+ def wrapup_time(seconds)
144
+ int :wrapuptime => seconds
145
+ end
146
+
147
+
148
+ def autopause(yes_no)
149
+ boolean :autopause => yes_no
150
+ end
151
+
152
+ def maximum_length(number)
153
+ int :maxlen => number
154
+ end
155
+
156
+ def queue_status_announce_frequency(seconds)
157
+ int "announce-frequency" => seconds
158
+ end
159
+
160
+ def periodically_announce(sound_file, options={})
161
+ frequency = options.delete(:every) || 1.minute
162
+
163
+ string 'periodic-announce' => sound_file
164
+ int 'periodic-announce-frequency' => frequency
165
+ end
166
+
167
+ def announce_hold_time(seconds)
168
+ one_of [true, false, :once], "announce-holdtime" => seconds
169
+ end
170
+
171
+ def announce_round_seconds(yes_no_or_once)
172
+ int "announce-round-seconds" => yes_no_or_once
173
+ end
174
+
175
+ def monitor_format(symbol)
176
+ one_of [:wav, :gsm, :wav49], 'monitor-format' => symbol
177
+ end
178
+
179
+ def monitor_type(symbol)
180
+ criteria = {:monitor => "Monitor", :mix_monitor => "MixMonitor"}
181
+ one_of_and_translate criteria, 'monitor-type' => symbol
182
+ end
183
+
184
+ # Ex: join_empty true
185
+ # Ex: join_empty :strict
186
+ def join_empty(yes_no_or_strict)
187
+ one_of [true, false, :strict], :joinempty => yes_no_or_strict
188
+ end
189
+
190
+ def leave_when_empty(yes_no)
191
+ boolean :leavewhenempty => yes_no
192
+ end
193
+
194
+ def report_hold_time(yes_no)
195
+ boolean :reportholdtime => yes_no
196
+ end
197
+
198
+ def ring_in_use(yes_no)
199
+ boolean :ringinuse => yes_no
200
+ end
201
+
202
+ # Number of seconds to wait when an agent is to be bridged with
203
+ # a caller. Normally you'd want this to be zero.
204
+ def delay_connection_by(seconds)
205
+ int :memberdelay => seconds
206
+ end
207
+
208
+ def timeout_restart(yes_no)
209
+ boolean :timeoutrestart => yes_no
210
+ end
211
+
212
+ # Give a Hash argument here to override the default sound files for this queue.
213
+ #
214
+ # Usage:
215
+ #
216
+ # queue.sound_files :you_are_next => 'queue-youarenext',
217
+ # :there_are => 'queue-thereare',
218
+ # :calls_waiting => 'queue-callswaiting',
219
+ # :hold_time => 'queue-holdtime',
220
+ # :minutes => 'queue-minutes',
221
+ # :seconds => 'queue-seconds',
222
+ # :thank_you => 'queue-thankyou',
223
+ # :less_than => 'queue-less-than',
224
+ # :report_hold => 'queue-reporthold',
225
+ # :periodic_announcement => 'queue-periodic-announce'
226
+ #
227
+ # Note: the Hash values are the defaults. You only need to specify the ones you
228
+ # wish to override.
229
+ def sound_files(hash_of_files)
230
+ hash_of_files.each_pair do |key, value|
231
+ unless SOUND_FILE_SYMBOL_INTERPRETATIONS.has_key? key
232
+ message = "Unrecogized sound file identifier #{key.inspect}. " +
233
+ "Supported: " + SOUND_FILE_SYMBOL_INTERPRETATIONS.keys.map(&:inspect).to_sentence
234
+ raise ArgumentError, message
235
+ end
236
+ @sound_files[SOUND_FILE_SYMBOL_INTERPRETATIONS[key]] = value
237
+ end
238
+ end
239
+
240
+ def member(driver)
241
+ members << (driver.kind_of?(String) && driver =~ %r'/' ? driver : "Agent/#{driver}")
242
+ end
243
+
244
+ end
245
+
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end