adhearsion-asterisk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.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
|