Last active
October 3, 2020 18:23
-
-
Save karthicraghupathi/9666ff29407301e3e2a5630402bc6f37 to your computer and use it in GitHub Desktop.
Asterisk AMI over TCP - Dialer & OriginateResponse Listener with pystrix
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pystrix | |
CHANNEL_DRIVER_SIP = 'SIP' | |
CHANNEL_DRIVER_PJIP = 'PJSIP' | |
class Dialer(object): | |
_host = _username = _password = _manager = _channel_driver = None | |
def __init__( | |
self, host, username, password, channel_driver=CHANNEL_DRIVER_PJIP | |
): | |
self._host = host | |
self._username = username | |
self._password = password | |
self._channel_driver = channel_driver | |
self._manager = pystrix.ami.Manager() | |
self._connect() | |
challenge_response = self._challenge() | |
if not challenge_response or not challenge_response.success: | |
raise ConnectionError( | |
"Asterisk did not provide an MD5 challenge token" | |
+ (challenge_response is None and ": timed out" or "") | |
) | |
login_response = self._login(challenge_response) | |
if not login_response or not login_response.success: | |
raise ConnectionError( | |
"Attempt to login to Asterisk server failed" | |
+ (login_response is None and ": timed out" or "") | |
) | |
def _connect(self): | |
self._manager.connect(self._host) | |
def _challenge(self): | |
return self._manager.send_action(pystrix.ami.core.Challenge()) | |
def _login(self, challenge_response): | |
return self._manager.send_action(pystrix.ami.core.Login( | |
self._username, | |
self._password, | |
events=False, | |
challenge=challenge_response.result["Challenge"] | |
)) | |
def originate( | |
self, action_id, callerid, phone=None, | |
channel=None, context='default', priority=1, extension=None, | |
timeout_secs=60, variables=None, account=None, async_=True, | |
carrier_context='Outbound' | |
): | |
if not phone and not channel: | |
raise Exception('Either phone or channel should be provided.') | |
if phone: | |
channel = '{}/{}@{}'.format( | |
self._channel_driver, phone, carrier_context | |
) | |
if not extension: | |
extension = callerid | |
if not variables: | |
variables = dict() | |
return self._manager.send_action( | |
pystrix.ami.core.Originate_Context( | |
channel, | |
context, | |
extension, | |
priority, | |
timeout=timeout_secs * 1000, | |
callerid=callerid, | |
variables=variables, | |
account=account, | |
async_=async_ | |
), | |
action_id=action_id | |
) | |
if __name__ == "__main__": | |
host = "" | |
username = "" | |
password = "" | |
busy_action_id = '5f78b020b7326f279dde3ce1' | |
busy_phone = '8005559939' | |
good_action_id = '5f78b020b7326f279dde3ce2' | |
good_phone = '' | |
callerid = '' | |
dialer = Dialer(host, username, password) | |
result = dialer.originate(busy_action_id, callerid, phone=busy_phone) | |
print(result) | |
result = dialer.originate(good_action_id, callerid, phone=good_phone) | |
print(result) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import time | |
import pystrix | |
_HOST = "" | |
_USERNAME = "" | |
_PASSWORD = "" | |
class AMICore(object): | |
_manager = None | |
_kill_flag = False | |
def __init__(self): | |
self._manager = pystrix.ami.Manager() | |
self._register_callbacks() | |
try: | |
self._manager.connect(_HOST) | |
challenge_response = self._manager.send_action(pystrix.ami.core.Challenge()) | |
if challenge_response and challenge_response.success: | |
action = pystrix.ami.core.Login( | |
_USERNAME, | |
_PASSWORD, | |
challenge=challenge_response.result["Challenge"], | |
) | |
self._manager.send_action(action) | |
else: | |
self._kill_flag = True | |
raise ConnectionError( | |
"Asterisk did not provide an MD5 challenge token" | |
+ (challenge_response is None and ": timed out" or "") | |
) | |
except pystrix.ami.ManagerSocketError as e: | |
self._kill_flag = True | |
raise ConnectionError( | |
"Unable to connect to Asterisk server: %(error)s" | |
% { | |
"error": str(e), | |
} | |
) | |
except pystrix.ami.core.ManagerAuthError as reason: | |
self._kill_flag = True | |
raise ConnectionError( | |
"Unable to authenticate to Asterisk server: %(reason)s" | |
% { | |
"reason": reason, | |
} | |
) | |
except pystrix.ami.ManagerError as reason: | |
self._kill_flag = True | |
raise ConnectionError( | |
"An unexpected Asterisk error occurred: %(reason)s" | |
% { | |
"reason": reason, | |
} | |
) | |
self._manager.monitor_connection() | |
def _register_callbacks(self): | |
self._manager.register_callback( | |
pystrix.ami.core_events.FullyBooted, self._handle_event | |
) | |
self._manager.register_callback( | |
pystrix.ami.core_events.OriginateResponse, self._handle_event | |
) | |
self._manager.register_callback(None, self._handle_event) | |
self._manager.register_callback("Shutdown", self._handle_shutdown) | |
def _handle_shutdown(self, event, manager): | |
self._kill_flag = True | |
def _handle_event(self, event, manager): | |
if 'OriginateResponse' == event['Event']: | |
headers, data = event.process() | |
headers['Reason'] = pystrix.ami.core.ORIGINATE_RESULT_MAP.get( | |
headers['Reason'], headers['Reason'] | |
) | |
print(headers) | |
def is_alive(self): | |
return not self._kill_flag | |
def kill(self): | |
self._manager.close() | |
class Error(Exception): | |
pass | |
class ConnectionError(Error): | |
pass | |
if __name__ == "__main__": | |
ami_core = AMICore() | |
while ami_core.is_alive(): | |
time.sleep(1) | |
ami_core.kill() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment