Module: Adhearsion::VoIP::Asterisk::Commands
- Defined in:
- lib/adhearsion/voip/asterisk/commands.rb
Defined Under Namespace
Modules: MenuDigitResponse, SpeechEngines Classes: QueueProxy
Constant Summary collapse
- RESPONSE_PREFIX =
"200 result="
- DIAL_STATUSES =
These are the status messages that asterisk will issue after a dial command is executed.
Here is a current list of dial status messages which are not all necessarily supported by adhearsion:
ANSWER: Call is answered. A successful dial. The caller reached the callee. BUSY: Busy signal. The dial command reached its number but the number is busy. NOANSWER: No answer. The dial command reached its number, the number rang for too long, then the dial timed out. CANCEL: Call is cancelled. The dial command reached its number but the caller hung up before the callee picked up. CONGESTION: Congestion. This status is usually a sign that the dialled number is not recognised. CHANUNAVAIL: Channel unavailable. On SIP, peer may not be registered. DONTCALL: Privacy mode, callee rejected the call TORTURE: Privacy mode, callee chose to send caller to torture menu INVALIDARGS: Error parsing Dial command arguments (added for Asterisk 1.4.1, SVN r53135-53136)
Hash.new(:unknown).merge(:answer => :answered, #:doc: :congestion => :congested, :busy => :busy, :cancel => :cancelled, :noanswer => :unanswered, :cancelled => :cancelled, :chanunavail => :channel_unavailable)
- DYNAMIC_FEATURE_EXTENSIONS =
{ :attended_transfer => lambda do || variable "TRANSFER_CONTEXT" => [:context] if && .has_key?(:context) extend_dynamic_features_with "atxfer" end, :blind_transfer => lambda do variable "TRANSFER_CONTEXT" => [:context] if && .has_key?(:context) extend_dynamic_features_with 'blindxfer' end }
Instance Method Summary collapse
-
#answer ⇒ Object
This must be called first before any other commands can be issued.
- #check_voicemail ⇒ Object
-
#dial(number, options = {}) ⇒ Object
Dial an extension or “phone number” in asterisk.
-
#disable_feature(feature_name) ⇒ Object
Disables a feature name specified in features.conf.
-
#dtmf(digits) ⇒ Object
Simulates pressing the specified digits over the current channel.
-
#duration_of ⇒ Float
Get the number of seconds the given block takes to execute.
-
#enable_feature(feature_name, optional_options = nil) ⇒ Object
A high-level way of enabling features you create/uncomment from features.conf.
-
#execute(application, *arguments) ⇒ Object
This asterisk dialplan command allows you to instruct Asterisk to start applications which are typically run from extensions.conf.
-
#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.
-
#hangup ⇒ Object
Hangs up the current channel.
-
#input(*args) ⇒ Object
obviously override this by passing in a new key with :accept_key.
-
#interruptible_play(*files) ⇒ String?
Play a sequence of files, stopping the playback if a digit is pressed.
-
#join(conference_id, options = {}) ⇒ Object
Used to join a particular conference with the MeetMe application.
-
#jump_to(context, overrides = {}) ⇒ Object
Jumps to a context.
-
#last_dial_status ⇒ Object
:busy, :no_answer, :cancelled, :congested, and :channel_unavailable.
-
#last_dial_successful? ⇒ Boolean
as reported by Asterisk.
-
#last_dial_unsuccessful? ⇒ Boolean
Opposite of last_dial_successful?().
-
#menu(*args, &block) ⇒ Object
Creates an interactive menu for the caller.
-
#messages_waiting? ⇒ Boolean
This command should be used to check if a message is waiting on the Asterisk Comedian Voicemail application.
-
#next_message ⇒ Object
This command should be used to advance to the next message in the Asterisk Comedian Voicemail application.
-
#play(*arguments) ⇒ Object
Plays the specified sound file names.
- #queue(queue_name) ⇒ Object
-
#raw_response(message = nil) ⇒ Object
The underlying method executed by nearly all the command methods in this module.
-
#read ⇒ Object
Utility method to read from pbx.
-
#record(*args) ⇒ Object
Records a sound file with the given name.
- #response(command, *arguments) ⇒ Object
-
#say_digits(digits) ⇒ Object
Speaks the digits given as an argument.
-
#set_variable(variable_name, value) ⇒ Object
Pass information back to the asterisk dial plan.
-
#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”).
-
#sip_get_header(header) ⇒ String
(also: #sip_header)
Issue the command to fetch a SIP header from the current call channel example use: sip_get_header(“x-ahn-test”).
-
#speak(text, engine = :none) ⇒ Object
This feature is presently experimental! Do not use it!.
-
#variable(*args) ⇒ Object
Allows you to either set or get a channel variable from Asterisk.
-
#verbose(message, level = nil) ⇒ Object
Sends a message to the console via the verbose message system.
-
#voicemail(*args) ⇒ Object
Send a caller to a voicemail box to leave a message.
-
#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.
-
#with_next_message(&block) ⇒ Object
The with_next_message method…
-
#write(message) ⇒ Object
Utility method to write to pbx.
Instance Method Details
#answer ⇒ Object
This must be called first before any other commands can be issued. In typical Adhearsion applications this is called by default as soon as a call is transfered to a valid context in dialplan.rb. If you do not want your Adhearsion application to automatically issue an answer command, then you must edit your startup.rb file and configure this setting. Keep in mind that you should not need to issue another answer command after one has already been issued either explicitly by your code or implicitly by the standard adhearsion configuration.
123 124 125 126 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 123 def answer response "ANSWER" true end |
#check_voicemail ⇒ Object
805 806 807 808 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 805 def check_voicemail ahn_log.agi.warn "THE check_voicemail() DIALPLAN METHOD WILL SOON BE DEPRECATED! CHANGE THIS TO voicemail_main() INSTEAD" voicemail_main end |
#dial(number, options = {}) ⇒ Object
Dial an extension or “phone number” in asterisk. Maps to the Asterisk DIAL command in the asterisk dialplan.
Be careful to not just specify a number like 5001, 9095551001 You must specify a properly formatted string as Asterisk would expect to use in order to understand whether the call should be dialed using SIP, IAX, or some other means.
:caller_id
- the caller id number to be used when the call is placed. It is advised you properly adhere to the policy of VoIP termination providers with respect to caller id values.
:name
- this is the name which should be passed with the caller ID information if :name=>“John Doe” and :caller_id => “444-333-1000” then the compelete CID and name would be “John Doe” <4443331000> support for caller id information varies from country to country and from one VoIP termination provider to another.
:for
- this option can be thought of best as a timeout. i.e. timeout after :for if no one answers the call For example, dial(“SIP/jay-desk-650&SIP/jay-desk-601&SIP/jay-desk-601-2”, :for => 15.seconds, :caller_id => callerid) this call will timeout after 15 seconds if 1 of the 3 extensions being dialed do not pick prior to the 15 second time limit
:options
- This is a string of options like “Tr” which are supported by the asterisk DIAL application. for a complete list of these options and their usage please check the link below.
:confirm
- ?
for this call specified by the variable my_callerid
dial "SIP/jay-desk-650&SIP/jay-desk-601&SIP/jay-desk-601-2", :for => 15.seconds, :caller_id => my_callerid
847 848 849 850 851 852 853 854 855 856 857 858 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 847 def dial(number, ={}) * = :caller_id, :name, :for, :options, :confirm = .keys - raise ArgumentError, "Unknown dial options: #{.to_sentence}" if .any? set_caller_id_name [:name] set_caller_id_number [:caller_id] confirm_option = dial_macro_option_compiler [:confirm] = [:options] = ? + confirm_option : confirm_option execute "Dial", number, [:for], 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().
621 622 623 624 625 626 627 628 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 621 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 |
#dtmf(digits) ⇒ Object
Simulates pressing the specified digits over the current channel. Can be used to traverse a phone menu.
256 257 258 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 256 def dtmf(digits) execute "SendDTMF", digits.to_s end |
#duration_of ⇒ Float
Get the number of seconds the given block takes to execute. This is particularly useful in dialplans for tracking billable time. Note that if the call is hung up during the block, you will need to rescue the exception if you have some mission-critical logic after it with which you’re recording this return-value.
895 896 897 898 899 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 895 def duration_of start_time = Time.now yield Time.now - start_time end |
#enable_feature(feature_name, optional_options = nil) ⇒ 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.
607 608 609 610 611 612 613 614 615 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 607 def enable_feature(feature_name, =nil) if DYNAMIC_FEATURE_EXTENSIONS.has_key? feature_name instance_exec(, &DYNAMIC_FEATURE_EXTENSIONS[feature_name]) else raise ArgumentError, "You cannot supply optional options when the feature name is " + "not internally recognized!" if extend_dynamic_features_with feature_name end end |
#execute(application, *arguments) ⇒ Object
This asterisk dialplan command allows you to instruct Asterisk to start applications which are typically run from extensions.conf.
The most common commands are already made available through the FAGI interface provided by this code base. For commands that do not fall into this category, then exec is what you should use.
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 used EXEC.
142 143 144 145 146 147 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 142 def execute(application, *arguments) result = raw_response(%{EXEC %s "%s"} % [ application, arguments.join(%{"#{AHN_CONFIG.asterisk.argument_delimiter}"}) ]) return false if error?(result) result 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
660 661 662 663 664 665 666 667 668 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 660 def get_variable(variable_name) result = response("GET VARIABLE", variable_name) case result when "200 result=0" return nil when /^200 result=1 \((.*)\)$/ return $LAST_PAREN_MATCH end end |
#hangup ⇒ Object
Hangs up the current channel. After this command is issued, you will not be able to send any more AGI commands but the dialplan Thread will still continue, allowing you to do any post-call work.
170 171 172 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 170 def hangup response 'HANGUP' end |
#input(*args) ⇒ Object
obviously override this by passing in a new key with :accept_key.
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 471 def input(*args) = args.last.kind_of?(Hash) ? args.pop : {} number_of_digits = args.shift sound_files = Array .delete(:play) timeout = .delete(:timeout) terminating_key = .delete(:accept_key) terminating_key = if terminating_key terminating_key.to_s elsif number_of_digits.nil? && !terminating_key.equal?(false) '#' end if number_of_digits && number_of_digits < 0 ahn_log.agi.warn "Giving -1 to input() is now deprecated. Don't specify a first " + "argument to simulate unlimited digits." if number_of_digits == -1 raise ArgumentError, "The number of digits must be positive!" end buffer = '' key = sound_files.any? ? interruptible_play(*sound_files) || '' : wait_for_digit(timeout || -1) loop do return buffer if key.nil? if terminating_key if key == terminating_key return buffer else buffer << key return buffer if number_of_digits && number_of_digits == buffer.length end else buffer << key return buffer if number_of_digits && number_of_digits == buffer.length end key = wait_for_digit(timeout || -1) end end |
#interruptible_play(*files) ⇒ String?
Play a sequence of files, stopping the playback if a digit is pressed.
906 907 908 909 910 911 912 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 906 def interruptible_play(*files) files.flatten.each do |file| result = result_digit_from response("STREAM FILE", file, "1234567890*#") return result if result != 0.chr end nil end |
#join(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.
638 639 640 641 642 643 644 645 646 647 648 649 650 651 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 638 def join(conference_id, ={}) conference_id = conference_id.to_s.scan(/\w/).join command_flags = [:options].to_s # This is a passthrough string straight to Asterisk pin = [: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 = .has_key?(:use_static_conf) ? [:use_static_conf] : false # The 'd' option of MeetMe creates conferences dynamically. command_flags += 'd' unless (command_flags.include?('d') or use_static_conf) execute "MeetMe", conference_id, command_flags, [:pin] end |
#jump_to(context, overrides = {}) ⇒ Object
Jumps to a context. An alternative to DialplanContextProc#+@. When jumping to a context, it will not resume executing the former context when the jumped-to context has finished executing. Make sure you don’t have any ensure
closures which you expect to execute when the call has finished, as they will run when this method is called.
You can optionally override certain dialplan variables when jumping to the context. A popular use of this is to redefine extension
(which this method automatically boxes with a PhoneNumber object) so you can effectively “restart” a call (from the perspective of the jumped-to context). When you override variables here, you’re effectively blowing away the old variables. If you need them for some reason, you should assign the important ones to an instance variable first before calling this method.
519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 519 def jump_to(context, overrides={}) context = lookup_context_with_name(context) if context.kind_of?(Symbol) || (context.kind_of?(String) && context =~ /^[\w_]+$/) # JRuby has a bug that prevents us from correctly determining the class name. # See: http://jira.codehaus.org/browse/JRUBY-5026 if !(context.kind_of?(Adhearsion::DialPlan::DialplanContextProc) || context.kind_of?(Proc)) raise Adhearsion::VoIP::DSL::Dialplan::ContextNotFoundException end if overrides.any? overrides = overrides.symbolize_keys if overrides.has_key?(:extension) && !overrides[:extension].kind_of?(Adhearsion::VoIP::DSL::PhoneNumber) overrides[:extension] = Adhearsion::VoIP::DSL::PhoneNumber.new overrides[:extension] end overrides.each_pair do |key, value| (key) { value } end end raise Adhearsion::VoIP::DSL::Dialplan::ControlPassingException.new(context) end |
#last_dial_status ⇒ Object
:busy, :no_answer, :cancelled, :congested, and :channel_unavailable. If :cancel is returned, the caller hung up before the callee picked up. If :congestion is returned, the dialed extension probably doesn’t exist. If :channel_unavailable, the callee phone may not be registered.
569 570 571 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 569 def last_dial_status DIAL_STATUSES[get_dial_status] end |
#last_dial_successful? ⇒ Boolean
as reported by Asterisk. false otherwise
575 576 577 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 575 def last_dial_successful? last_dial_status == :answered end |
#last_dial_unsuccessful? ⇒ Boolean
Opposite of last_dial_successful?()
580 581 582 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 580 def last_dial_unsuccessful? not last_dial_successful? end |
#menu(*args, &block) ⇒ Object
Creates an interactive menu for the caller.
The following documentation was derived from a post on Jay Phillips’ blog (see below).
The menu() command solves the problem of building enormous input-fetching state machines in Ruby without first-class message passing facilities or an external DSL.
Here is an example dialplan which uses the menu() command effectively.
from_pstn {
'welcome', 'for-spanish-press-8', 'main-ivr',
:timeout => 8.seconds, :tries => 3 do |link|
link.shipment_status 1
link.ordering 2
link.representative 4
link.spanish 8
link.employee 900..999
link.on_invalid { play 'invalid' }
link.on_premature_timeout do |str|
play 'sorry'
end
link.on_failure do
play 'goodbye'
hangup
end
end
}
shipment_status {
# Fetch a tracking number and pass it to a web service.
}
ordering {
# Enter another menu that lets them enter credit card
# information and place their order over the phone.
}
representative {
# Place the caller into a queue
}
spanish {
# Special options for the spanish menu.
}
employee {
dial "SIP/#{extension}" # Overly simplistic
}
The main detail to note is the declarations within the menu() command’s block. Each line seems to refer to a link object executing a seemingly arbitrary method with an argument that’s either a number or a Range of numbers. The link
object collects these arbitrary method invocations and assembles a set of rules. The seemingly arbitrary method name is the name of the context to which the menu should jump in case its argument (the pattern) is found to be a match.
With these context names and patterns defined, the menu() command plays in sequence the sound files you supply as arguments, stopping playback abruptly if the user enters a digit. If no digits were pressed when the files finish playing, it waits :timeout
seconds. If no digits are pressed after the timeout, it executes the on_premature_timeout
hook you define (if any) and then tries again a maximum of :tries
times. If digits are pressed that result in no possible match, it executes the on_invalid
hook. When/if all tries are exhausted with no positive match, it executes the on_failure
hook after the other hook (e.g. on_invalid
, then on_failure
).
When the menu() state machine runs through the defined rules, it must distinguish between exact and potential matches. It’s important to understand the differences between these and how they affect the overall outcome:
|---------------|-------------------|------------------------------------------------------|
| exact matches | potential matches | result |
|---------------|-------------------|------------------------------------------------------|
| 0 | 0 | Fail and start over |
| 1 | 0 | Match found! |
| 0 | >0 | Get another digit |
| >1 | 0 | Go with the first exact match |
| 1 | >0 | Get another digit. If timeout, use exact match |
| >1 | >0 | Get another digit. If timeout, use first exact match |
|---------------|-------------------|------------------------------------------------------|
Database integration
To do database integration, I recommend programatically executing methods on the link object within the block. For example:
do |link|
for employee in Employee.find(:all)
link.internal employee.extension
end
end
or this more efficient and Rubyish way
do |link|
link.internal *Employee.find(:all).map(&:extension)
end
If this second example seems like too much Ruby magic, let me explain — Employee.find(:all) effectively does a “SELECT * FROM employees” on the database with ActiveRecord, returning (what you’d think is) an Array. The map(&:extension) is fanciness that means “replace every instance in this Array with the result of calling extension on that object”. Now we have an Array of every extension in the database. The splat operator (*) before the argument changes the argument from being one argument (an Array) into a sequence of n arguments, where n is the number of items in the Array it’s “splatting”. Lastly, these arguments are passed to the internal method, the name of a context which will handle dialing this user if one of the supplied patterns matches.
Handling a successful pattern match
Which brings me to another important note. Let’s say that the user’s input successfully matched one of the patterns returned by that Employe.find… magic. When it jumps to the internal context, that context can access the variable entered through the extension variable. This was a tricky design decision that I think, overall, works great. It makes the menu() command feel much more first-class in the Adhearsion dialplan grammar and decouples the receiving context from the menu that caused the jump. After all, the context doesn’t necessary need to be the endpoint from a menu; it can be its own entry point, making menu() effectively a pipeline of re-creating the call.
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 388 def (*args, &block) = args.last.kind_of?(Hash) ? args.pop : {} sound_files = args.flatten = Menu.new(, &block) initial_digit_prompt = sound_files.any? # This method is basically one big begin/rescue block. When we start the Menu state machine by continue()ing, the state # machine will pass messages back to this method in the form of Exceptions. This decoupling allows the menu system to # work on, say, Freeswitch and Asterisk both. begin if .should_continue? .continue else .execute_failure_hook return :failed end rescue Menu::MenuResult => case when Menu::MenuResultInvalid .execute_invalid_hook .restart! when Menu::MenuGetAnotherDigit next_digit = (, sound_files) if next_digit << next_digit else # The user timed out entering another digit! case when Menu::MenuGetAnotherDigitOrFinish # This raises a ControlPassingException jump_to .match_payload, :extension => .new_extension when Menu::MenuGetAnotherDigitOrTimeout # This should execute premature_timeout AND reset if the number of retries # has not been exhausted. .execute_timeout_hook .restart! end end when Menu::MenuResultFound jump_to .match_payload, :extension => .new_extension else raise "Unrecognized MenuResult! This may be a bug!" end # Retry will re-execute the begin block, preserving our changes to the menu_instance object. retry end end |
#messages_waiting? ⇒ Boolean
This command should be used to check if a message is waiting on the Asterisk Comedian Voicemail application.
272 273 274 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 272 def not @call.inbox.empty? end |
#next_message ⇒ Object
This command should be used to advance to the next message in the Asterisk Comedian Voicemail application
267 268 269 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 267 def @call.inbox.pop end |
#play(*arguments) ⇒ Object
Plays the specified sound file names. This method will handle Time/DateTime objects (e.g. Time.now), Fixnums (e.g. 1000), Strings which are valid Fixnums (e.g “123”), and direct sound files. When playing numbers, Adhearsion assumes you’re saying the number, not the digits. For example, play(“100”) is pronounced as “one hundred” instead of “one zero zero”.
Note: it is not necessary to supply a sound file extension; Asterisk will try to find a sound file encoded using the current channel’s codec, if one exists. If not, it will transcode from the default codec (GSM). Asterisk stores its sound files in /var/lib/asterisk/sounds.
192 193 194 195 196 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 192 def play(*arguments) arguments.flatten.each do |argument| play_time(argument) || play_numeric(argument) || play_string(argument) end end |
#queue(queue_name) ⇒ Object
549 550 551 552 553 554 555 556 557 558 559 560 561 562 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 549 def queue(queue_name) queue_name = queue_name.to_s @queue_proxy_hash_lock = Mutex.new unless defined? @queue_proxy_hash_lock @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 |
#raw_response(message = nil) ⇒ Object
The underlying method executed by nearly all the command methods in this module. Used to send the plaintext commands in the proper AGI format over TCP/IP back to an Asterisk server via the FAGI protocol.
It is not recommended that you call this method directly unless you plan to write a new command method in which case use this to communicate directly with an Asterisk server via the FAGI protocol.
96 97 98 99 100 101 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 96 def raw_response( = nil) raise ArgumentError.new("illegal NUL in message #{.inspect}") if =~ /\0/ ahn_log.agi.debug ">>> #{}" write if read end |
#read ⇒ Object
Utility method to read from pbx. Hangup if nil.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 52 def read from_pbx.gets.tap do || # AGI has many conditions that might indicate a hangup raise Hangup if .nil? ahn_log.agi.debug "<<< #{}" code, rest = *.split(' ', 2) case code.to_i when 510 # This error is non-fatal for the call ahn_log.agi.warn "510: Invalid or unknown AGI command" when 511 # 511 Command Not Permitted on a dead channel ahn_log.agi.debug "511: Dead channel. Raising Hangup" raise Hangup when 520 # This error is non-fatal for the call ahn_log.agi.warn "520: Invalid command syntax" when (500..599) # Assume this error is non-fatal for the call and try to keep running ahn_log.agi.warn "#{code}: Unknown AGI protocol error." end # If the message starts with HANGUP it's a silly 1.6 OOB message case when /^HANGUP/, /^HANGUP\n?$/i, /^HANGUP\s?\d{3}/i ahn_log.agi.debug "AGI HANGUP. Raising hangup" raise Hangup end end end |
#record(*args) ⇒ Object
Records a sound file with the given name. If no filename is specified a file named by Asterisk will be created and returned. Else the given filename will be returned. If a relative path is given, the file will be saved in the default Asterisk sound directory, /var/lib/spool/asterisk by default.
Silence and maxduration is specified in seconds.
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 212 def record(*args) = args.last.kind_of?(Hash) ? args.pop : {} filename = args.shift || "/tmp/recording_%d" if filename.index("%d") if @call.variables.has_key?(:recording_counter) @call.variables[:recording_counter] += 1 else @call.variables[:recording_counter] = 0 end filename = filename % @call.variables[:recording_counter] end if (!.has_key?(:format)) format = filename.slice!(/\.[^\.]+$/) if (format.nil?) ahn_log.agi.warn "Format not specified and not detected. Defaulting to \"gsm\"" format = "gsm" end format.sub!(/^\./, "") else format = .delete(:format) end # maxduration must be in milliseconds when using RECORD FILE maxduration = .delete(:maxduration) || -1 maxduration = maxduration * 1000 if maxduration > 0 escapedigits = .delete(:escapedigits) || "#" silence = .delete(:silence) || 0 if (silence > 0) response("RECORD FILE", filename, format, escapedigits, maxduration, 0, "BEEP", "s=#{silence}") else response("RECORD FILE", filename, format, escapedigits, maxduration, 0, "BEEP") end # If the user hangs up before the recording is entered, -1 is returned and RECORDED_FILE # will not contain the name of the file, even though it IS in fact recorded. filename + "." + format end |
#response(command, *arguments) ⇒ Object
103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 103 def response(command, *arguments) # Arguments surrounded by quotes; quotes backslash-escaped. # See parse_args in asterisk/res/res_agi.c (Asterisk 1.4.21.1) quote_arg = lambda { |arg| '"' + arg.gsub(/["\\]/) { |m| "\\#{m}" } + '"' } if arguments.empty? raw_response("#{command}") else raw_response("#{command} " + arguments.map{ |arg| quote_arg.call(arg.to_s) }.join(' ')) end end |
#say_digits(digits) ⇒ Object
Speaks the digits given as an argument. For example, “123” is spoken as “one two three”.
884 885 886 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 884 def say_digits(digits) execute "saydigits", validate_digits(digits) 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.
681 682 683 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 681 def set_variable(variable_name, value) response("SET VARIABLE", variable_name, value) == "200 result=1" end |
#sip_add_header(header, value) ⇒ String
694 695 696 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 694 def sip_add_header(header, value) execute("SIPAddHeader", "#{header}: #{value}") == "200 result=1" end |
#sip_get_header(header) ⇒ String Also known as: sip_header
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
706 707 708 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 706 def sip_get_header(header) get_variable("SIP_HEADER(#{header})") end |
#speak(text, engine = :none) ⇒ Object
This feature is presently experimental! Do not use it!
585 586 587 588 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 585 def speak(text, engine=:none) engine = AHN_CONFIG.asterisk.speech_engine || engine execute SpeechEngines.send(engine, text) 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
714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 714 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
161 162 163 164 165 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 161 def verbose(, level = nil) result = raw_response("VERBOSE \"#{}\" #{level}") return false if error?(result) result 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.
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 736 def voicemail(*args) = args.last.kind_of?(Hash) ? args.pop : {} mailbox_number = args.shift greeting_option = .delete(:greeting) skip_option = .delete(:skip) raise ArgumentError, 'You supplied too many arguments!' if mailbox_number && .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' = "#{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!" if .size != 1 context_name, mailboxes = .to_a.first Array(mailboxes).map do |mailbox| raise ArgumentError, "Mailbox numbers must be numerical!" unless mailbox.to_s =~ /^\d+$/ "#{mailbox}@#{context_name}" end.join('&') end execute('voicemail', number_with_context, ) 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.
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 774 def voicemail_main(={}) mailbox, context, folder = .values_at :mailbox, :context, :folder authenticate = .has_key?(:authenticate) ? [: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 "Folder name cannot be an empty String!" else nil end real_mailbox = "" real_mailbox << "#{mailbox}" unless mailbox.blank? real_mailbox << "@#{context}" unless context.blank? = "" << "s" if !authenticate << folder unless folder.blank? command_args = [real_mailbox] command_args << unless .blank? command_args.clear if command_args == [""] execute 'VoiceMailMain', *command_args end |
#with_next_message(&block) ⇒ Object
The with_next_message method…
261 262 263 264 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 261 def (&block) raise LocalJumpError, "Must supply a block" unless block_given? block.call() end |
#write(message) ⇒ Object
Utility method to write to pbx.
47 48 49 |
# File 'lib/adhearsion/voip/asterisk/commands.rb', line 47 def write() to_pbx.print( + "\n") end |