adhearsion-asterisk 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +10 -0
- data/.rspec +3 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile +6 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +143 -0
- data/Rakefile +23 -0
- data/adhearsion-asterisk.gemspec +35 -0
- data/lib/adhearsion-asterisk.rb +1 -0
- data/lib/adhearsion/asterisk.rb +12 -0
- data/lib/adhearsion/asterisk/config_generator.rb +103 -0
- data/lib/adhearsion/asterisk/config_generator/agents.rb +138 -0
- data/lib/adhearsion/asterisk/config_generator/queues.rb +247 -0
- data/lib/adhearsion/asterisk/config_generator/voicemail.rb +238 -0
- data/lib/adhearsion/asterisk/config_manager.rb +60 -0
- data/lib/adhearsion/asterisk/plugin.rb +464 -0
- data/lib/adhearsion/asterisk/queue_proxy.rb +177 -0
- data/lib/adhearsion/asterisk/queue_proxy/agent_proxy.rb +81 -0
- data/lib/adhearsion/asterisk/queue_proxy/queue_agents_list_proxy.rb +132 -0
- data/lib/adhearsion/asterisk/version.rb +5 -0
- data/spec/adhearsion/asterisk/config_generators/agents_spec.rb +258 -0
- data/spec/adhearsion/asterisk/config_generators/queues_spec.rb +322 -0
- data/spec/adhearsion/asterisk/config_generators/voicemail_spec.rb +306 -0
- data/spec/adhearsion/asterisk/config_manager_spec.rb +125 -0
- data/spec/adhearsion/asterisk/plugin_spec.rb +618 -0
- data/spec/adhearsion/asterisk/queue_proxy/agent_proxy_spec.rb +90 -0
- data/spec/adhearsion/asterisk/queue_proxy/queue_agents_list_proxy_spec.rb +145 -0
- data/spec/adhearsion/asterisk/queue_proxy_spec.rb +156 -0
- data/spec/adhearsion/asterisk_spec.rb +9 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/the_following_code.rb +3 -0
- metadata +229 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'enumerator'
|
2
|
+
|
3
|
+
module Adhearsion
|
4
|
+
module Asterisk
|
5
|
+
class ConfigurationManager
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def normalize_configuration(file_contents)
|
9
|
+
# cat sip.conf | sed -e 's/\s*;.*$//g' | sed -e '/^;.*$/d' | sed -e '/^\s*$/d'
|
10
|
+
file_contents.split(/\n+/).map do |line|
|
11
|
+
line.sub(/;.+$/, '').strip
|
12
|
+
end.join("\n").squeeze("\n")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :filename
|
17
|
+
|
18
|
+
def initialize(filename)
|
19
|
+
@filename = filename
|
20
|
+
end
|
21
|
+
|
22
|
+
def sections
|
23
|
+
@sections ||= read_configuration
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](section_name)
|
27
|
+
result = sections.find { |(name, *rest)| section_name == name }
|
28
|
+
result.last if result
|
29
|
+
end
|
30
|
+
|
31
|
+
def delete_section(section_name)
|
32
|
+
sections.reject! { |(name, *rest)| section_name == name }
|
33
|
+
end
|
34
|
+
|
35
|
+
def new_section(name, properties={})
|
36
|
+
sections << [name, properties]
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def read_configuration
|
42
|
+
normalized_file = self.class.normalize_configuration File.open(@filename, 'r'){|f| f.read}
|
43
|
+
sections = normalized_file.split(/^\[([-_\w]+)\]$/)[1..-1]
|
44
|
+
return [] if sections.nil?
|
45
|
+
sections.each_slice(2).map do |(name,properties)|
|
46
|
+
[name, hash_from_properties(properties)]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def hash_from_properties(properties)
|
51
|
+
properties.split(/\n+/).inject({}) do |property_hash,property|
|
52
|
+
all, name, value = *property.match(/^\s*([^=]+?)\s*=\s*(.+)\s*$/)
|
53
|
+
next property_hash unless name && value
|
54
|
+
property_hash[name] = value
|
55
|
+
property_hash
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,464 @@
|
|
1
|
+
module Adhearsion
|
2
|
+
module Asterisk
|
3
|
+
PLAYBACK_SUCCESS = 'SUCCESS' unless defined? PLAYBACK_SUCCESS
|
4
|
+
|
5
|
+
|
6
|
+
class Plugin < Adhearsion::Plugin
|
7
|
+
DYNAMIC_FEATURE_EXTENSIONS = {
|
8
|
+
:attended_transfer => lambda do |options|
|
9
|
+
variable "TRANSFER_CONTEXT" => options[:context] if options && options.has_key?(:context)
|
10
|
+
extend_dynamic_features_with "atxfer"
|
11
|
+
end,
|
12
|
+
:blind_transfer => lambda do |options|
|
13
|
+
variable "TRANSFER_CONTEXT" => options[:context] if options && options.has_key?(:context)
|
14
|
+
extend_dynamic_features_with 'blindxfer'
|
15
|
+
end
|
16
|
+
} unless defined? DYNAMIC_FEATURE_EXTENSIONS
|
17
|
+
|
18
|
+
dialplan :agi do |name, *params|
|
19
|
+
component = Punchblock::Component::Asterisk::AGI::Command.new :name => name, :params => params
|
20
|
+
execute_component_and_await_completion component
|
21
|
+
complete_reason = component.complete_event.resource.reason
|
22
|
+
[:code, :result, :data].map { |p| complete_reason.send p }
|
23
|
+
end
|
24
|
+
|
25
|
+
#
|
26
|
+
# This asterisk dialplan command allows you to instruct Asterisk to start applications
|
27
|
+
# which are typically run from extensions.conf and do not have AGI command equivalents.
|
28
|
+
#
|
29
|
+
# For example, if there are specific asterisk modules you have loaded that will not be
|
30
|
+
# available through the standard commands provided through FAGI - then you can use EXEC.
|
31
|
+
#
|
32
|
+
# @example Using execute in this way will add a header to an existing SIP call.
|
33
|
+
# execute 'SIPAddHeader', "Call-Info: answer-after=0"
|
34
|
+
#
|
35
|
+
# @see http://www.voip-info.org/wiki/view/Asterisk+-+documentation+of+application+commands Asterisk Dialplan Commands
|
36
|
+
#
|
37
|
+
dialplan :execute do |name, *params|
|
38
|
+
agi "EXEC #{name}", *params
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
42
|
+
# Sends a message to the console via the verbose message system.
|
43
|
+
#
|
44
|
+
# @param [String] message
|
45
|
+
# @param [Integer] level
|
46
|
+
#
|
47
|
+
# @return the result of the command
|
48
|
+
#
|
49
|
+
# @example Use this command to inform someone watching the Asterisk console
|
50
|
+
# of actions happening within Adhearsion.
|
51
|
+
# verbose 'Processing call with Adhearsion' 3
|
52
|
+
#
|
53
|
+
# @see http://www.voip-info.org/wiki/view/verbose
|
54
|
+
#
|
55
|
+
dialplan :verbose do |message, level = nil|
|
56
|
+
agi 'VERBOSE', message, level
|
57
|
+
end
|
58
|
+
|
59
|
+
# A high-level way of enabling features you create/uncomment from features.conf.
|
60
|
+
#
|
61
|
+
# Certain Symbol features you enable (as defined in DYNAMIC_FEATURE_EXTENSIONS) have optional
|
62
|
+
# arguments that you can also specify here. The usage examples show how to do this.
|
63
|
+
#
|
64
|
+
# Usage examples:
|
65
|
+
#
|
66
|
+
# enable_feature :attended_transfer # Enables "atxfer"
|
67
|
+
#
|
68
|
+
# enable_feature :attended_transfer, :context => "my_dial" # Enables "atxfer" and then
|
69
|
+
# # sets "TRANSFER_CONTEXT" to :context's value
|
70
|
+
#
|
71
|
+
# enable_feature :blind_transfer, :context => 'my_dial' # Enables 'blindxfer' and sets TRANSFER_CONTEXT
|
72
|
+
#
|
73
|
+
# enable_feature "foobar" # Enables "foobar"
|
74
|
+
#
|
75
|
+
# enable_feature("dup"); enable_feature("dup") # Enables "dup" only once.
|
76
|
+
#
|
77
|
+
# dialplan :voicemail do |*args|
|
78
|
+
# options_hash = args.last.kind_of?(Hash) ? args.pop : {}
|
79
|
+
# mailbox_number = args.shift
|
80
|
+
# greeting_option = options_hash.delete :greeting
|
81
|
+
#
|
82
|
+
dialplan :enable_feature do |*args|
|
83
|
+
feature_name, optional_options = args.flatten
|
84
|
+
|
85
|
+
if DYNAMIC_FEATURE_EXTENSIONS.has_key? feature_name
|
86
|
+
instance_exec(optional_options, &DYNAMIC_FEATURE_EXTENSIONS[feature_name])
|
87
|
+
else
|
88
|
+
unless optional_options.nil? or optional_options.empty?
|
89
|
+
raise ArgumentError, "You cannot supply optional options when the feature name is " +
|
90
|
+
"not internally recognized!"
|
91
|
+
end
|
92
|
+
extend_dynamic_features_with feature_name
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Disables a feature name specified in features.conf. If you're disabling it, it was probably
|
97
|
+
# set by enable_feature().
|
98
|
+
#
|
99
|
+
# @param [String] feature_name
|
100
|
+
dialplan :disable_feature do |feature_name|
|
101
|
+
enabled_features_variable = variable 'DYNAMIC_FEATURES'
|
102
|
+
enabled_features = enabled_features_variable.split('#')
|
103
|
+
if enabled_features.include? feature_name
|
104
|
+
enabled_features.delete feature_name
|
105
|
+
variable 'DYNAMIC_FEATURES' => enabled_features.join('#')
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# helper method that should probably should private...
|
110
|
+
dialplan :extend_dynamic_features_with do |feature_name|
|
111
|
+
current_variable = variable("DYNAMIC_FEATURES") || ''
|
112
|
+
enabled_features = current_variable.split '#'
|
113
|
+
unless enabled_features.include? feature_name
|
114
|
+
enabled_features << feature_name
|
115
|
+
variable "DYNAMIC_FEATURES" => enabled_features.join('#')
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# Issue this command to access a channel variable that exists in the asterisk dialplan (i.e. extensions.conf)
|
121
|
+
# Use get_variable to pass information from other modules or high level configurations from the asterisk dialplan
|
122
|
+
# to the adhearsion dialplan.
|
123
|
+
#
|
124
|
+
# @param [String] variable_name
|
125
|
+
#
|
126
|
+
# @see: http://www.voip-info.org/wiki/view/get+variable Asterisk Get Variable
|
127
|
+
#
|
128
|
+
dialplan :get_variable do |variable_name|
|
129
|
+
code, result, data = agi "GET VARIABLE", variable_name
|
130
|
+
data
|
131
|
+
end
|
132
|
+
|
133
|
+
#
|
134
|
+
# Pass information back to the asterisk dial plan.
|
135
|
+
#
|
136
|
+
# Keep in mind that the variables are not global variables. These variables only exist for the channel
|
137
|
+
# related to the call that is being serviced by the particular instance of your adhearsion application.
|
138
|
+
# You will not be able to pass information back to the asterisk dialplan for other instances of your adhearsion
|
139
|
+
# application to share. Once the channel is "hungup" then the variables are cleared and their information is gone.
|
140
|
+
#
|
141
|
+
# @param [String] variable_name
|
142
|
+
# @param [String] value
|
143
|
+
#
|
144
|
+
# @see http://www.voip-info.org/wiki/view/set+variable Asterisk Set Variable
|
145
|
+
#
|
146
|
+
dialplan :set_variable do |variable_name, value|
|
147
|
+
agi "SET VARIABLE", variable_name, value
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# Issue the command to add a custom SIP header to the current call channel
|
152
|
+
# example use: sip_add_header("x-ahn-test", "rubyrox")
|
153
|
+
#
|
154
|
+
# @param[String] the name of the SIP header
|
155
|
+
# @param[String] the value of the SIP header
|
156
|
+
#
|
157
|
+
# @return [String] the Asterisk response
|
158
|
+
#
|
159
|
+
# @see http://www.voip-info.org/wiki/index.php?page=Asterisk+cmd+SIPAddHeader Asterisk SIPAddHeader
|
160
|
+
#
|
161
|
+
dialplan :sip_add_header do |header, value|
|
162
|
+
execute "SIPAddHeader", "#{header}: #{value}"
|
163
|
+
end
|
164
|
+
|
165
|
+
#
|
166
|
+
# Issue the command to fetch a SIP header from the current call channel
|
167
|
+
# example use: sip_get_header("x-ahn-test")
|
168
|
+
#
|
169
|
+
# @param[String] the name of the SIP header to get
|
170
|
+
#
|
171
|
+
# @return [String] the Asterisk response
|
172
|
+
#
|
173
|
+
# @see http://www.voip-info.org/wiki/index.php?page=Asterisk+cmd+SIPGetHeader Asterisk SIPGetHeader
|
174
|
+
#
|
175
|
+
dialplan :sip_get_header do |header|
|
176
|
+
get_variable "SIP_HEADER(#{header})"
|
177
|
+
end
|
178
|
+
|
179
|
+
#
|
180
|
+
# Allows you to either set or get a channel variable from Asterisk.
|
181
|
+
# The method takes a hash key/value pair if you would like to set a variable
|
182
|
+
# Or a single string with the variable to get from Asterisk
|
183
|
+
#
|
184
|
+
dialplan :variable do |*args|
|
185
|
+
if args.last.kind_of? Hash
|
186
|
+
assignments = args.pop
|
187
|
+
raise ArgumentError, "Can't mix variable setting and fetching!" if args.any?
|
188
|
+
assignments.each_pair do |key, value|
|
189
|
+
set_variable key, value
|
190
|
+
end
|
191
|
+
else
|
192
|
+
if args.size == 1
|
193
|
+
get_variable args.first
|
194
|
+
else
|
195
|
+
args.map { |var| get_variable var }
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
#
|
201
|
+
# Used to join a particular conference with the MeetMe application. To use MeetMe, be sure you
|
202
|
+
# have a proper timing device configured on your Asterisk box. MeetMe is Asterisk's built-in
|
203
|
+
# conferencing program.
|
204
|
+
#
|
205
|
+
# @param [String] conference_id
|
206
|
+
# @param [Hash] options
|
207
|
+
#
|
208
|
+
# @see http://www.voip-info.org/wiki-Asterisk+cmd+MeetMe Asterisk Meetme Application Information
|
209
|
+
#
|
210
|
+
dialplan :meetme do |conference_id, options = {}|
|
211
|
+
conference_id = conference_id.to_s.scan(/\w/).join
|
212
|
+
command_flags = options[:options].to_s # This is a passthrough string straight to Asterisk
|
213
|
+
pin = options[:pin]
|
214
|
+
raise ArgumentError, "A conference PIN number must be numerical!" if pin && pin.to_s !~ /^\d+$/
|
215
|
+
|
216
|
+
# To disable dynamic conference creation set :use_static_conf => true
|
217
|
+
use_static_conf = options.has_key?(:use_static_conf) ? options[:use_static_conf] : false
|
218
|
+
|
219
|
+
# The 'd' option of MeetMe creates conferences dynamically.
|
220
|
+
command_flags += 'd' unless command_flags.include?('d') || use_static_conf
|
221
|
+
|
222
|
+
execute "MeetMe", conference_id, command_flags, options[:pin]
|
223
|
+
end
|
224
|
+
|
225
|
+
#
|
226
|
+
# Send a caller to a voicemail box to leave a message.
|
227
|
+
#
|
228
|
+
# The method takes the mailbox_number of the user to leave a message for and a
|
229
|
+
# greeting_option that will determine which message gets played to the caller.
|
230
|
+
#
|
231
|
+
# @see http://www.voip-info.org/tiki-index.php?page=Asterisk+cmd+VoiceMail Asterisk Voicemail
|
232
|
+
#
|
233
|
+
dialplan :voicemail do |*args|
|
234
|
+
options_hash = args.last.kind_of?(Hash) ? args.pop : {}
|
235
|
+
mailbox_number = args.shift
|
236
|
+
greeting_option = options_hash.delete :greeting
|
237
|
+
skip_option = options_hash.delete :skip
|
238
|
+
raise ArgumentError, 'You supplied too many arguments!' if mailbox_number && options_hash.any?
|
239
|
+
|
240
|
+
greeting_option = case greeting_option
|
241
|
+
when :busy then 'b'
|
242
|
+
when :unavailable then 'u'
|
243
|
+
when nil then nil
|
244
|
+
else raise ArgumentError, "Unrecognized greeting #{greeting_option}"
|
245
|
+
end
|
246
|
+
skip_option &&= 's'
|
247
|
+
options = "#{greeting_option}#{skip_option}"
|
248
|
+
|
249
|
+
raise ArgumentError, "Mailbox cannot be blank!" if !mailbox_number.nil? && mailbox_number.blank?
|
250
|
+
number_with_context = if mailbox_number then mailbox_number else
|
251
|
+
raise ArgumentError, "You must supply ONE context name!" unless options_hash.size == 1
|
252
|
+
context_name, mailboxes = options_hash.to_a.first
|
253
|
+
Array(mailboxes).map do |mailbox|
|
254
|
+
raise ArgumentError, "Mailbox numbers must be numerical!" unless mailbox.to_s =~ /^\d+$/
|
255
|
+
[mailbox, context_name].join '@'
|
256
|
+
end.join '&'
|
257
|
+
end
|
258
|
+
|
259
|
+
execute 'voicemail', number_with_context, options
|
260
|
+
case variable('VMSTATUS')
|
261
|
+
when 'SUCCESS' then true
|
262
|
+
when 'USEREXIT' then false
|
263
|
+
else nil
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
#
|
268
|
+
# The voicemail_main method puts a caller into the voicemail system to fetch their voicemail
|
269
|
+
# or set options for their voicemail box.
|
270
|
+
#
|
271
|
+
# @param [Hash] options
|
272
|
+
#
|
273
|
+
# @see http://www.voip-info.org/wiki-Asterisk+cmd+VoiceMailMain Asterisk VoiceMailMain Command
|
274
|
+
#
|
275
|
+
dialplan :voicemail_main do |options = {}|
|
276
|
+
mailbox, context, folder = options.values_at :mailbox, :context, :folder
|
277
|
+
authenticate = options.has_key?(:authenticate) ? options[:authenticate] : true
|
278
|
+
|
279
|
+
folder = if folder
|
280
|
+
if folder.to_s =~ /^[\w_]+$/
|
281
|
+
"a(#{folder})"
|
282
|
+
else
|
283
|
+
raise ArgumentError, "Voicemail folder must be alphanumerical/underscore characters only!"
|
284
|
+
end
|
285
|
+
elsif folder == ''
|
286
|
+
raise ArgumentError, "Folder name cannot be an empty String!"
|
287
|
+
else
|
288
|
+
nil
|
289
|
+
end
|
290
|
+
|
291
|
+
real_mailbox = ""
|
292
|
+
real_mailbox << "#{mailbox}" unless mailbox.blank?
|
293
|
+
real_mailbox << "@#{context}" unless context.blank?
|
294
|
+
|
295
|
+
real_options = ""
|
296
|
+
real_options << "s" if !authenticate
|
297
|
+
real_options << folder unless folder.blank?
|
298
|
+
|
299
|
+
command_args = [real_mailbox]
|
300
|
+
command_args << real_options unless real_options.blank?
|
301
|
+
command_args.clear if command_args == [""]
|
302
|
+
|
303
|
+
execute 'VoiceMailMain', *command_args
|
304
|
+
end
|
305
|
+
|
306
|
+
#
|
307
|
+
# Place a call in a queue to be answered by a registered agent. You must then call #join!
|
308
|
+
#
|
309
|
+
# @param [String] queue_name the queue name to place the caller in
|
310
|
+
# @return [Adhearsion::Asterisk::QueueProxy] a queue proxy object
|
311
|
+
#
|
312
|
+
# @see http://www.voip-info.org/wiki-Asterisk+cmd+Queue Full information on the Asterisk Queue
|
313
|
+
# @see Adhearsion::Asterisk":QueueProxy#join! for further details
|
314
|
+
#
|
315
|
+
dialplan :queue do |queue_name|
|
316
|
+
queue_name = queue_name.to_s
|
317
|
+
|
318
|
+
@queue_proxy_hash_lock ||= Mutex.new
|
319
|
+
@queue_proxy_hash_lock.synchronize do
|
320
|
+
@queue_proxy_hash ||= {}
|
321
|
+
if @queue_proxy_hash.has_key? queue_name
|
322
|
+
return @queue_proxy_hash[queue_name]
|
323
|
+
else
|
324
|
+
proxy = @queue_proxy_hash[queue_name] = QueueProxy.new(queue_name, self)
|
325
|
+
return proxy
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
# Plays the specified sound file names. This method will handle Time/DateTime objects (e.g. Time.now),
|
331
|
+
# Fixnums (e.g. 1000), Strings which are valid Fixnums (e.g "123"), and direct sound files. When playing
|
332
|
+
# numbers, Adhearsion assumes you're saying the number, not the digits. For example, play("100")
|
333
|
+
# is pronounced as "one hundred" instead of "one zero zero". To specify how the Date/Time objects are said
|
334
|
+
# pass in as an array with the first parameter as the Date/Time/DateTime object along with a hash with the
|
335
|
+
# additional options. See play_time for more information.
|
336
|
+
#
|
337
|
+
# Note: it is not necessary to supply a sound file extension; Asterisk will try to find a sound
|
338
|
+
# file encoded using the current channel's codec, if one exists. If not, it will transcode from
|
339
|
+
# the default codec (GSM). Asterisk stores its sound files in /var/lib/asterisk/sounds.
|
340
|
+
#
|
341
|
+
# @example Play file hello-world.???
|
342
|
+
# play 'hello-world'
|
343
|
+
# @example Speak current time
|
344
|
+
# play Time.now
|
345
|
+
# @example Speak today's date
|
346
|
+
# play Date.today
|
347
|
+
# @example Speak today's date in a specific format
|
348
|
+
# play [Date.today, {:format => 'BdY'}]
|
349
|
+
# @example Play sound file, speak number, play two more sound files
|
350
|
+
# play %w"a-connect-charge-of 22 cents-per-minute will-apply"
|
351
|
+
# @example Play two sound files
|
352
|
+
# play "you-sound-cute", "what-are-you-wearing"
|
353
|
+
#
|
354
|
+
# @return [Boolean] true is returned if everything was successful. Otherwise, false indicates that
|
355
|
+
# some sound file(s) could not be played.
|
356
|
+
dialplan :play do |*arguments|
|
357
|
+
begin
|
358
|
+
play! arguments
|
359
|
+
rescue Adhearsion::PlaybackError => e
|
360
|
+
return false
|
361
|
+
end
|
362
|
+
true
|
363
|
+
end
|
364
|
+
|
365
|
+
# Same as {#play}, but immediately raises an exception if a sound file cannot be played.
|
366
|
+
#
|
367
|
+
# @return [true]
|
368
|
+
# @raise [Adhearsion::VoIP::PlaybackError] If a sound file cannot be played
|
369
|
+
dialplan :play! do |*arguments|
|
370
|
+
result = true
|
371
|
+
unless play_time(arguments)
|
372
|
+
arguments.flatten.each do |argument|
|
373
|
+
result &= play_numeric(argument) || play_soundfile(argument)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
raise Adhearsion::PlaybackError if !result
|
377
|
+
end
|
378
|
+
|
379
|
+
# Plays the given Date, Time, or Integer (seconds since epoch)
|
380
|
+
# using the given timezone and format.
|
381
|
+
#
|
382
|
+
# @param [Date|Time|DateTime] Time to be said.
|
383
|
+
# @param [Hash] Additional options to specify how exactly to say time specified.
|
384
|
+
#
|
385
|
+
# +:timezone+ - Sends a timezone to asterisk. See /usr/share/zoneinfo for a list. Defaults to the machine timezone.
|
386
|
+
# +:format+ - This is the format the time is to be said in. Defaults to "ABdY 'digits/at' IMp"
|
387
|
+
#
|
388
|
+
# @see http://www.voip-info.org/wiki/view/Asterisk+cmd+SayUnixTime
|
389
|
+
dialplan :play_time do |*args|
|
390
|
+
argument, options = args.flatten
|
391
|
+
options ||= {}
|
392
|
+
|
393
|
+
return false unless options.is_a? Hash
|
394
|
+
|
395
|
+
timezone = options.delete(:timezone) || ''
|
396
|
+
format = options.delete(:format) || ''
|
397
|
+
epoch = case argument
|
398
|
+
when Time || DateTime
|
399
|
+
argument.to_i
|
400
|
+
when Date
|
401
|
+
format = 'BdY' unless format.present?
|
402
|
+
argument.to_time.to_i
|
403
|
+
end
|
404
|
+
|
405
|
+
return false if epoch.nil?
|
406
|
+
|
407
|
+
execute "SayUnixTime", epoch, timezone, format
|
408
|
+
end
|
409
|
+
|
410
|
+
#Executes SayNumber with the passed argument.
|
411
|
+
#
|
412
|
+
# @param [Numeric|String] Numeric argument, or a string contanining numbers.
|
413
|
+
# @return [Boolean] Returns false if the argument could not be played.
|
414
|
+
dialplan :play_numeric do |argument|
|
415
|
+
if argument.kind_of?(Numeric) || argument =~ /^\d+$/
|
416
|
+
execute "SayNumber", argument
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
# Instruct Asterisk to play a sound file to the channel.
|
421
|
+
#
|
422
|
+
# @param [String] File name to play in the Asterisk convention, without extension.
|
423
|
+
# @return [Boolean] Returns false if the argument could not be played.
|
424
|
+
dialplan :play_soundfile do |argument|
|
425
|
+
execute "Playback", argument
|
426
|
+
get_variable('PLAYBACKSTATUS') == PLAYBACK_SUCCESS
|
427
|
+
end
|
428
|
+
|
429
|
+
#
|
430
|
+
# Plays a single output, not only files, accepting interruption by one of the digits specified
|
431
|
+
# Currently still stops execution, will be fixed soon in Punchblock
|
432
|
+
#
|
433
|
+
# @param [Object] String or Hash specifying output and options
|
434
|
+
# @param [String] String with the digits that are allowed to interrupt output
|
435
|
+
# @return [String|nil] The pressed digit, or nil if nothing was pressed
|
436
|
+
#
|
437
|
+
dialplan :stream_file do |argument, digits = '0123456789#*'|
|
438
|
+
begin
|
439
|
+
output_component = ::Punchblock::Component::Asterisk::AGI::Command.new :name => "STREAM FILE",
|
440
|
+
:params => [
|
441
|
+
argument,
|
442
|
+
digits
|
443
|
+
]
|
444
|
+
execute_component_and_await_completion output_component
|
445
|
+
|
446
|
+
reason = output_component.complete_event.reason
|
447
|
+
|
448
|
+
case reason.result
|
449
|
+
when 0
|
450
|
+
raise Adhearsion::PlaybackError if reason.data == "endpos=0"
|
451
|
+
nil
|
452
|
+
when -1
|
453
|
+
raise Adhearsion::PlaybackError
|
454
|
+
else
|
455
|
+
[reason.result].pack 'U*'
|
456
|
+
end
|
457
|
+
rescue StandardError => e
|
458
|
+
raise Adhearsion::PlaybackError, "Output failed for argument #{argument.inspect}"
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
end#class
|
463
|
+
end#module
|
464
|
+
end#module
|