Module: Adhearsion::Asterisk::CallControllerMethods

Defined in:
lib/adhearsion/asterisk/call_controller_methods.rb

Defined Under Namespace

Classes: GenerateSilenceProxy

Constant Summary collapse

DYNAMIC_FEATURE_EXTENSIONS =
{
  :attended_transfer => lambda do |options|
    variable "TRANSFER_CONTEXT" => options[:context] if options && options.has_key?(:context)
    extend_dynamic_features_with "atxfer"
  end,
  :blind_transfer => lambda do |options|
    variable "TRANSFER_CONTEXT" => options[:context] if options && options.has_key?(:context)
    extend_dynamic_features_with 'blindxfer'
  end
}

Instance Method Summary collapse

Instance Method Details

#agi(name, *params) ⇒ Object

Raises:

  • (Adhearsion::Call::Hangup)


19
20
21
22
23
24
25
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 19

def agi(name, *params)
  component = Punchblock::Component::Asterisk::AGI::Command.new :name => name, :params => params
  execute_component_and_await_completion component
  complete_reason = component.complete_event.reason
  raise Adhearsion::Call::Hangup if complete_reason.is_a?(Punchblock::Event::Complete::Hangup)
  [:code, :result, :data].map { |p| complete_reason.send p }
end

#disable_feature(feature_name) ⇒ Object

Disables a feature name specified in features.conf. If you’re disabling it, it was probably set by enable_feature().

Parameters:

  • feature_name (String)


102
103
104
105
106
107
108
109
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 102

def disable_feature(feature_name)
  enabled_features_variable = variable 'DYNAMIC_FEATURES'
  enabled_features = enabled_features_variable.split('#')
  if enabled_features.include? feature_name
    enabled_features.delete feature_name
    variable 'DYNAMIC_FEATURES' => enabled_features.join('#')
  end
end

#enable_feature(*args) ⇒ Object

A high-level way of enabling features you create/uncomment from features.conf.

Certain Symbol features you enable (as defined in DYNAMIC_FEATURE_EXTENSIONS) have optional arguments that you can also specify here. The usage examples show how to do this.

Usage examples:

enable_feature :attended_transfer                        # Enables "atxfer"

enable_feature :attended_transfer, :context => "my_dial" # Enables "atxfer" and then
                                                         # sets "TRANSFER_CONTEXT" to :context's value

enable_feature :blind_transfer, :context => 'my_dial'    # Enables 'blindxfer' and sets TRANSFER_CONTEXT

enable_feature "foobar"                                  # Enables "foobar"

enable_feature("dup"); enable_feature("dup")             # Enables "dup" only once.

def voicemail(*args)

options_hash    = args.last.kind_of?(Hash) ? args.pop : {}
mailbox_number  = args.shift
greeting_option = options_hash.delete :greeting


84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 84

def enable_feature(*args)
  feature_name, optional_options = args.flatten

  if DYNAMIC_FEATURE_EXTENSIONS.has_key? feature_name
    instance_exec(optional_options, &DYNAMIC_FEATURE_EXTENSIONS[feature_name])
  else
    unless optional_options.nil? or optional_options.empty?
      raise ArgumentError, "You cannot supply optional options when the feature name is " +
                           "not internally recognized!"
    end
    extend_dynamic_features_with feature_name
  end
end

#execute(name, *params) ⇒ Object

This asterisk dialplan command allows you to instruct Asterisk to start applications which are typically run from extensions.conf and do not have AGI command equivalents.

For example, if there are specific asterisk modules you have loaded that will not be available through the standard commands provided through FAGI - then you can use EXEC.

Examples:

Using execute in this way will add a header to an existing SIP call.

execute 'SIPAddHeader', "Call-Info: answer-after=0"

See Also:



39
40
41
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 39

def execute(name, *params)
  agi "EXEC #{name}", params.join(',')
end

#extend_dynamic_features_with(feature_name) ⇒ Object

helper method that should probably should private…



112
113
114
115
116
117
118
119
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 112

def extend_dynamic_features_with(feature_name)
  current_variable = variable("DYNAMIC_FEATURES") || ''
  enabled_features = current_variable.split '#'
  unless enabled_features.include? feature_name
    enabled_features << feature_name
    variable "DYNAMIC_FEATURES" => enabled_features.join('#')
  end
end

#generate_silence(&block) ⇒ Object

Generates silence in the background, just once until some other sound is generated, or continuously for the duration of a given block. Silence is normally only generated under specific circumstances but this method will explicitly generate it, which can be useful in some scenarios.

Note that the Playtones command must be available and the transmit_silence option must be enabled in asterisk.conf. Also note that the given block is executed using instance_eval and that imposes one important restriction. If the silence is interrupted outside the scope of the block (e.g. calling play in another method) then it won’t be restarted until execution returns to the scope. However, it is safe to call generate_silence again when outside the scope. Instance variables may be used as they are copied and copied back but be careful handling immutable objects outside the scope. If you’re unsure, don’t use a block.



419
420
421
422
423
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 419

def generate_silence(&block)
  component = Punchblock::Component::Asterisk::AGI::Command.new :name => "EXEC Playtones", :params => ["0"]
  execute_component_and_await_completion component
  GenerateSilenceProxy.proxy_for(self, &block) if block_given?
end

#get_variable(variable_name) ⇒ Object

Issue this command to access a channel variable that exists in the asterisk dialplan (i.e. extensions.conf) Use get_variable to pass information from other modules or high level configurations from the asterisk dialplan to the adhearsion dialplan.

@see: www.voip-info.org/wiki/view/get+variable Asterisk Get Variable

Parameters:

  • variable_name (String)


130
131
132
133
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 130

def get_variable(variable_name)
  code, result, data = agi "GET VARIABLE", variable_name
  data
end

#goto(context, extension = :nothing, priority = :nothing) ⇒ Object

Go to a specified context, extension and priority This requires us to relinquish control of the call. Execution will continue until the user hangs up, but the channel will be no longer available



430
431
432
433
434
435
436
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 430

def goto(context, extension = :nothing, priority = :nothing)
  call[:ahn_prevent_hangup] = true
  args = ['Goto', context, extension, priority].reject { |v| v == :nothing }
  execute *args
  set_variable 'PUNCHBLOCK_END_ON_ASYNCAGI_BREAK', 'true'
  agi "ASYNCAGI BREAK"
end

#meetme(conference_id, options = {}) ⇒ Object

Used to join a particular conference with the MeetMe application. To use MeetMe, be sure you have a proper timing device configured on your Asterisk box. MeetMe is Asterisk’s built-in conferencing program.

Parameters:

  • conference_id (String)
  • options (Hash) (defaults to: {})

Raises:

  • (ArgumentError)

See Also:



212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 212

def meetme(conference_id, options = {})
  conference_id = conference_id.to_s.scan(/\w/).join
  command_flags = options[:options].to_s # This is a passthrough string straight to Asterisk
  pin = options[:pin]
  raise ArgumentError, "A conference PIN number must be numerical!" if pin && pin.to_s !~ /^\d+$/

  # To disable dynamic conference creation set :use_static_conf => true
  use_static_conf = options.has_key?(:use_static_conf) ? options[:use_static_conf] : false

  # The 'd' option of MeetMe creates conferences dynamically.
  command_flags += 'd' unless command_flags.include?('d') || use_static_conf

  execute "MeetMe", conference_id, command_flags, options[:pin]
end

#play_digits(argument) ⇒ Object

Executes SayDigits with the passed argument.

Parameters:

  • Numeric (Numeric|String)

    argument, or a string contanining numbers.



373
374
375
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 373

def play_digits(argument)
  execute "SayDigits", argument
end

#play_numeric(argument) ⇒ Object

Executes SayNumber with the passed argument.

Parameters:

  • Numeric (Numeric|String)

    argument, or a string contanining numbers.



366
367
368
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 366

def play_numeric(argument)
  execute "SayNumber", argument
end

#play_soundfile(argument) ⇒ Boolean

Instruct Asterisk to play a sound file to the channel.

Parameters:

  • File (String)

    name to play in the Asterisk convention, without extension.

Returns:

  • (Boolean)

    Returns false if the argument could not be played.



400
401
402
403
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 400

def play_soundfile(argument)
  execute "Playback", argument
  get_variable('PLAYBACKSTATUS') == PLAYBACK_SUCCESS
end

#play_time(*args) ⇒ Object

Plays the given Date, Time, or Integer (seconds since epoch) using the given timezone and format.

:timezone - Sends a timezone to asterisk. See /usr/share/zoneinfo for a list. Defaults to the machine timezone. :format - This is the format the time is to be said in. Defaults to “ABdY ‘digits/at’ IMp”

Parameters:

  • Time (Date|Time|DateTime)

    to be said.

  • Additional (Hash)

    options to specify how exactly to say time specified.

See Also:



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 342

def play_time(*args)
  argument, options = args.flatten
  options ||= {}

  return false unless options.is_a? Hash

  timezone = options.delete(:timezone) || ''
  format   = options.delete(:format)   || ''
  epoch    = case argument
             when Time || DateTime
               argument.to_i
             when Date
               format = 'BdY' unless format.present?
               argument.to_time.to_i
             end

  return false if epoch.nil?

  execute "SayUnixTime", epoch, timezone, format
end

#play_tones(argument, wait = false) ⇒ Object

Executes Playtones with the passed argument.

Parameters:

  • Array (String|Array)

    or comma-separated string of tones.

  • Whether (Boolean)

    to wait for the tones to finish (defaults to false).



389
390
391
392
393
394
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 389

def play_tones(argument, wait = false)
  tones = [*argument].join(",")
  execute("Playtones", tones).tap do
    sleep tones.scan(/(?<=\/)\d+/).map(&:to_i).sum.to_f / 1000 if wait
  end
end

#queue(queue_name) ⇒ Adhearsion::Asterisk::QueueProxy

Place a call in a queue to be answered by a registered agent. You must then call #join!

Parameters:

  • queue_name (String)

    the queue name to place the caller in

Returns:

See Also:



317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 317

def queue(queue_name)
  queue_name = queue_name.to_s

  @queue_proxy_hash_lock ||= Mutex.new
  @queue_proxy_hash_lock.synchronize do
    @queue_proxy_hash ||= {}
    if @queue_proxy_hash.has_key? queue_name
      return @queue_proxy_hash[queue_name]
    else
      proxy = @queue_proxy_hash[queue_name] = QueueProxy.new(queue_name, self)
      return proxy
    end
  end
end

#say_characters(argument) ⇒ Object

Executes SayDigits with the passed argument. This will override adhearsion’s native #say_characters method, which does not currently work with asterisk

Parameters:

  • Numeric (Numeric|String)

    argument, or a string contanining numbers.



381
382
383
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 381

def say_characters(argument)
  play_digits argument
end

#set_variable(variable_name, value) ⇒ Object

Pass information back to the asterisk dial plan.

Keep in mind that the variables are not global variables. These variables only exist for the channel related to the call that is being serviced by the particular instance of your adhearsion application. You will not be able to pass information back to the asterisk dialplan for other instances of your adhearsion application to share. Once the channel is “hungup” then the variables are cleared and their information is gone.

Parameters:

  • variable_name (String)
  • value (String)

See Also:



148
149
150
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 148

def set_variable(variable_name, value)
  agi "SET VARIABLE", variable_name, value
end

#sip_add_header(header, value) ⇒ String

Issue the command to add a custom SIP header to the current call channel example use: sip_add_header(“x-ahn-test”, “rubyrox”)

@param the name of the SIP header @param the value of the SIP header

Returns:

  • (String)

    the Asterisk response

See Also:



163
164
165
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 163

def sip_add_header(header, value)
  execute "SIPAddHeader", "#{header}: #{value}"
end

#sip_get_header(header) ⇒ String

Issue the command to fetch a SIP header from the current call channel example use: sip_get_header(“x-ahn-test”)

@param the name of the SIP header to get

Returns:

  • (String)

    the Asterisk response

See Also:



177
178
179
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 177

def sip_get_header(header)
  get_variable "SIP_HEADER(#{header})"
end

#variable(*args) ⇒ Object

Allows you to either set or get a channel variable from Asterisk. The method takes a hash key/value pair if you would like to set a variable Or a single string with the variable to get from Asterisk



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 186

def variable(*args)
  if args.last.kind_of? Hash
    assignments = args.pop
    raise ArgumentError, "Can't mix variable setting and fetching!" if args.any?
    assignments.each_pair do |key, value|
      set_variable key, value
    end
  else
    if args.size == 1
      get_variable args.first
    else
      args.map { |var| get_variable var }
    end
  end
end

#verbose(message, level = nil) ⇒ Object

Sends a message to the console via the verbose message system.

of actions happening within Adhearsion.

verbose 'Processing call with Adhearsion' 3

Examples:

Use this command to inform someone watching the Asterisk console

Parameters:

  • message (String)
  • level (Integer) (defaults to: nil)

Returns:

  • the result of the command

See Also:



57
58
59
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 57

def verbose(message, level = nil)
  agi 'VERBOSE', message, level
end

#voicemail(*args) ⇒ Object

Send a caller to a voicemail box to leave a message.

The method takes the mailbox_number of the user to leave a message for and a greeting_option that will determine which message gets played to the caller.

Raises:

  • (ArgumentError)

See Also:



235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 235

def voicemail(*args)
  options_hash    = args.last.kind_of?(Hash) ? args.pop : {}
  mailbox_number  = args.shift
  greeting_option = options_hash.delete :greeting
  skip_option     = options_hash.delete :skip
  raise ArgumentError, 'You supplied too many arguments!' if mailbox_number && options_hash.any?

  greeting_option = case greeting_option
  when :busy then 'b'
  when :unavailable then 'u'
  when nil then nil
  else raise ArgumentError, "Unrecognized greeting #{greeting_option}"
  end
  skip_option &&= 's'
  options = "#{greeting_option}#{skip_option}"

  raise ArgumentError, "Mailbox cannot be blank!" if !mailbox_number.nil? && mailbox_number.blank?
  number_with_context = if mailbox_number then mailbox_number else
    raise ArgumentError, "You must supply ONE context name!" unless options_hash.size == 1
    context_name, mailboxes = options_hash.to_a.first
    Array(mailboxes).map do |mailbox|
      raise ArgumentError, "Mailbox numbers must be numerical!" unless mailbox.to_s =~ /^\d+$/
      [mailbox, context_name].join '@'
    end.join '&'
  end

  execute 'voicemail', number_with_context, options
  case variable('VMSTATUS')
  when 'SUCCESS' then true
  when 'USEREXIT' then false
  else nil
  end
end

#voicemail_main(options = {}) ⇒ Object

The voicemail_main method puts a caller into the voicemail system to fetch their voicemail or set options for their voicemail box.

Parameters:

  • options (Hash) (defaults to: {})

See Also:



277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/adhearsion/asterisk/call_controller_methods.rb', line 277

def voicemail_main(options = {})
  mailbox, context, folder = options.values_at :mailbox, :context, :folder
  authenticate = options.has_key?(:authenticate) ? options[:authenticate] : true

  folder = if folder
    if folder.to_s =~ /^[\w_]+$/
      "a(#{folder})"
    else
      raise ArgumentError, "Voicemail folder must be alphanumerical/underscore characters only!"
    end
  elsif folder == ''
    raise ArgumentError, "Folder name cannot be an empty String!"
  else
    nil
  end

  real_mailbox = ""
  real_mailbox << "#{mailbox}"  unless mailbox.blank?
  real_mailbox << "@#{context}" unless context.blank?

  real_options = ""
  real_options << "s" if !authenticate
  real_options << folder unless folder.blank?

  command_args = [real_mailbox]
  command_args << real_options unless real_options.blank?
  command_args.clear if command_args == [""]

  execute 'VoiceMailMain', *command_args
end