I'm not a big fan of line-oriented interfaces, so when I found the new (well, new-ish) Asterisk AJAX Manager Interface, AKA "AJAM," I figured it was a better fit for Ruby than the original.
My needs right now are very simple - set variables and redirect to a new extension. The variables that are set send Asterisk off to a new RESTPhone menu, where all the heavy lifting is done.
At some point this should become a gem, but here's the current, very simple, incarnation of how to talk to Asterisk via AJAM.
require 'rubygems'
require 'net/http'
require 'uri'
require 'xmlsimple'
require 'cgi'
require File.dirname(__FILE__) + '/rest_phone_utilities/uri_mixin'
require 'pp'
require 'pathname'
class AsteriskAjaxManager
# The authorization cookie from the manager.
attr_accessor :auth_cookie
# The URI to send commands to.
attr_accessor :uri
# The Asterisk username
attr_accessor :username
# The Asterisk secret
attr_accessor :secret
# Takes:
#
# :uri => The URI for the Asterisk http server, not including /asterisk
# :path => The AJAM path (default: '/asterisk')
# :username => The Asterisk manager username (default: mark)
# :secret => The Asterisk manager password (default: mysecret)
#
# (mark/mysecret is the default user in the Asterisk configuration file
# samples.)
def initialize(args = {})
final_uri = args[:uri]
final_uri ||= 'http://localhost:8088'
final_uri = URI.parse(final_uri) unless URI === final_uri
final_uri.path = '/asterisk' if final_uri.path.empty?
final_uri.path = '/' if final_uri.path.empty?
self.uri = final_uri
self.username = args[:username] || 'mark'
self.secret = args[:secret] || 'mysecret'
end
def login
login_uri = uri
login_uri.path = (Pathname.new(uri.path) + 'manager').to_s
login_uri.query = "action=login&username=#{username}&secret=#{secret}"
response = Net::HTTP.get_response(login_uri)
# The response should include an authorization cookie that we use
# on future requests.
self.auth_cookie = response.response['set-cookie']
end
def execute
login
yield self
logout
end
def logout
end
# ActionQuery is the http query sent off to AJAM.
class ActionQuery
attr_accessor :query
# Takes:
#
# [<tt>:uri</tt>]
# The uri (usually 'http://localhost:8088')
# [<tt>:action</tt>]
# The action. (Case-insensitive string or symbol)
# [<tt>:params</tt>]
# Parameters for the action
#
# Actions are:
#
# status::
# sippeers::
def initialize(args = {})
u = args[:uri]
u = UriWithQueryCreation.parse(u.to_s)
u.path = '/asterisk/mxml'
u.append_hash_to_query 'action' => args[:action]
u.append_hash_to_query args[:params]
self.query = u
end
def to_s
query.to_s
end
def net_http_obj
Net::HTTP::Get.new("#{query.path}?#{query.query}")
end
end
def sip_peers
args = append_standard_arguments(:action => :sippeers)
result = do_ajam_cmd_return_xml(args)
result['response'][1..-2].map {|m| m['generic'].first}
end
def extension_state args = {}
args = append_standard_arguments(:action => :extensionstate, :params => args)
result = do_ajam_cmd_return_xml(args)
result['response'].first['generic'].first
end
def redirect args = {}
args = append_standard_arguments(
:action => :redirect,
:params => args
)
result = do_ajam_cmd_return_xml(args)
result = result['response'].first['generic'].first
if result['response'] =~ /Error/
raise AsteriskAjaxManagerError, result['message']
end
end
def setvar args = {}
args = append_standard_arguments(
:action => :setvar,
:params => args
)
result = do_ajam_cmd_return_xml(args)
result = result['response'].first['generic'].first
if result['response'] =~ /Error/
raise AsteriskAjaxManagerError, result['message']
end
end
def append_standard_arguments(args = {})
args.merge(:uri => uri, :cookie => auth_cookie)
end
# Takes:
# :uri => the uri (usually http://localhost) as a String or URI object
# :action => the action (status, SIPpeers, etc) as a symbol
# :cookie => the authorization cookie
def do_ajam_cmd(args = {})
u = args[:uri]
u = UriWithQueryCreation.parse(u.to_s)
result = Net::HTTP.start(u.host, u.port) do |h|
q = ActionQuery.new :uri => 'http://localhost:8088/asterisk/mxml', :action => args[:action].to_s,
:params => args[:params]
req = q.net_http_obj
req.add_field('Cookie', args[:cookie])
h.request(req)
end
result.body
end
def do_ajam_cmd_return_xml(args = {})
result = do_ajam_cmd(args)
XmlSimple.xml_in(result)
end
class AsteriskAjaxManagerError < RuntimeError
class LoginError < AsteriskAjaxManagerError
end
end
end