Asynchronous client for working with Asterisk Manager Interface
To get started, you need to enable AMI by configuring the /etc/asterisk/manager.conf file:
; Enable AMI and have it accept connections only from localhost.
[general]
enabled = yes
; Enable HTTP server, useful for connecting via HTTP
webenabled = yes
bindaddr = 127.0.0.1
; Create an account "hello" with a password "world"
[hello]
secret = world
; Receive all types of events
read = all
; Allow this user to perform all actions.
write = all
To work with HTTP, enable the built-in HTTP server by configuring the /etc/asterisk/http.conf file:
; Включить встроенный HTTP-сервер и слушать только соединения на localhost.
[general]
enabled = yes
bindaddr = 127.0.0.1
Import the HTTPClient
or TCPClient
class from the ami.client
module:
from ami.client import HTTPClient
client = HTTPClient('localhost')
or
from ami.client import TCPClient
client = TCPClient('localhost')
Create a client instance by passing the Asterisk server address as a parameter:
from ami.client import HTTPClient, TCPClient
# Basic connection without encryption
client = HTTPClient('localhost')
client = TCPClient('localhost')
# Connection using SSL/TLS encryption and specifying the CA certificate
client = HTTPClient('localhost', ssl_enabled=True, cert_ca='/path/to/ca.crt')
client = TCPClient('localhost', ssl_enabled=True, cert_ca='/path/to/ca.crt')
Note: If the
cert_ca
andssl_enabled
parameters are specified, make sure that SSL/TLS encryption is enabled (ssl_enabled=True
), otherwise anAttributeError
exception will be raised.
Establish a connection with the Asterisk server by providing the username and password:
connect_resp = await client.connect(username='hello', password='world')
Note that the response of the request will be represented as a list of dictionaries, for example:
[
{
"Response": "Success",
"Message": "Authentication accepted"
}
]
Send a request to the Asterisk server to initiate a call from the FROM
number to the DESTINATION
number:
call_resp = await client.originate(originator=FROM, extension=DESTINATION)
Note that the result of the request will be represented as a list of dictionaries, for example:
[
{
"Response": "Success",
"Message": "Originate successfully queued"
}
]
To get a list of active channels, execute the following request:
channels_resp = await client.channels()
The result will be represented as a list of dictionaries, where each dictionary contains information about a channel:
[
{
"Response": "Success",
"EventList": "start",
"Message": "Channels will follow"
}, {
"Event": "CoreShowChannel",
"Channel": "SIP/trunk",
"ChannelState": "5",
"ChannelStateDesc": "Ringing",
"CallerIDNum": "89999999999",
"CallerIDName": "CID:999999",
"ConnectedLineNum": "999999",
"ConnectedLineName": "<unknown>",
"Language": "ru",
"AccountCode": "",
"Context": "from-trunk",
"Exten": "89999999999",
"Priority": "1",
"Uniqueid": "1694584278.23846",
"Linkedid": "1694584277.23843",
"Application": "AppDial",
"ApplicationData": "(Outgoing Line)",
"Duration": "00:00:05",
"BridgeId": ""
},
...
{"Event": "CoreShowChannelsComplete", "EventList": "Complete", "ListItems": "N"}
]
To execute the ping command and check the server availability, use the following code:
ping_resp = await client.ping()
The result will be a list of dictionaries indicating the successful execution of the ping command:
[
{
"Response": "Success",
"Ping": "Pong",
"Timestamp": "1696496997.515802"
}
]
To perform an attended transfer with an operator on the specified channel
to the REDIRECT
extension, use the following code:
transfer_resp = await client.attended_transfer(channel=channel, extension=REDIRECT)
The result will be a list of dictionaries indicating the successful addition of the transfer request to the queue:
[
{
"Response": "Success",
"Message": "Atxfer successfully queued"
}
]
To perform a blind transfer on the specified channel
to the REDIRECT
extension, use the following code:
transfer_resp = await client.blind_transfer(channel=channel, extension=REDIRECT)
The result will be a list of dictionaries indicating the successful execution of the transfer:
[
{
"Response": "Success",
"Message": "Transfer succeeded"
}
]
To redirect a call on the specified channel
to the REDIRECT
extension, use the following code:
redirect_resp = await client.redirect(extension=REDIRECT, channel=channel)
The result will be a list of dictionaries indicating the successful execution of the redirect:
[
{
"Response": "Success",
"Message": "Redirect successful"
}
]
To disconnect from the Asterisk server, use the following code:
logoff_resp = await client.logoff()
The result will be a list of dictionaries confirming the disconnection:
[
{
"Response": "Goodbye",
"Message": "Thanks for all the fish."
}
]
You can also make custom requests manually using the ami_request
method. Here is an example of how to use this method:
data = {
"Action": "BlindTransfer",
"Channel": channel,
"Exten": extension,
"Context": context
}
response = await client.ami_request(data)
The result of the request will be a list of dictionaries with the response from the Asterisk server:
[
{
"Response": "Success",
"Message": "Transfer succeeded"
}
]
You need to pass a dictionary with the request parameters to the ami_request
method. In this case, BlindTransfer
is used in the Action
field, and specific values are provided for Channel
, Exten
, and Context
.
For more information about actions, you can refer to the Asterisk documentation: AMI Actions
You can use this method to make requests with different parameters based on your needs.
To register a callback for a specific event, you can use the register_callback
method. Here is an example of how to use this method:
async def callback(event: dict, client: HTTPClient | TCPClient):
# Действия, выполняемые при получении события
# Регистрация обратного вызова
await client.register_callback('EventName', callback)
In the provided example, we created a callback
function that takes two parameters: event
(a dictionary with the event data) and client
(the client instance).
We then call the register_callback
method on the client instance, passing it the event name (in this case, EventName
) and the callback
function.
Now, when the client receives an event with the name EventName
, the callback
callback will be executed, where you can define the necessary actions to be performed upon receiving that event.
For more information about events, you can refer to the Asterisk documentation: AMI Events
Distributed under the Apache License 2.0. See LICENSE for more information.
- XpycTee