def __init__(self, **kwargs): super(AmiCmd, self).__init__(**kwargs) # Response timeout between retries self._sec_towait = 1 self._re_timeout = .2 # Stream to python object parser self.parser = AmiReg()
class AmiCmd(AmiCtl): """ AMI built-in Commands (Actions). """ # Cache for commands that return list _cache = {} # List of Command IDs waiting for response _pending = set() # End event headers of the commands that return list _evend = {"RegistrationsComplete", "PeerlistComplete", "ParkedCallsComplete", "AgentsComplete", "StatusComplete", "ShowDialPlanComplete", "CoreShowChannelsComplete"} def __init__(self, **kwargs): super(AmiCmd, self).__init__(**kwargs) # Response timeout between retries self._sec_towait = 1 self._re_timeout = .2 # Stream to python object parser self.parser = AmiReg() def reactor(self, recv, *a, **kw): """ React, when data is received. """ cache = self._cache pending = self._pending evend = self._evend # Feed data to parser self.parser.feed(recv) for event in self.parser.events: # Event as OrderedDict od = event.od # Command ID cid = od.get("ActionID") if od.get("Event") in evend and cid in pending and cid in cache: for x in cache[cid]: print x print del cache[cid] pending.discard(cid) elif cid in pending and cid in cache: cache[cid].append(event.od) elif od.get("Response")=="Success" and cid in pending and cid not in cache: for x in event.d: print x print self._pending.discard(cid) def __query(self, action, required, optional, a, kw, evend=None): """ Private helper method to handle multi-argument input. """ args_ini = {k:"" for k in required + optional} # If there are required args and none are supplied via 'a' or 'kw' if required and not any([a, kw]): raise ValueError("Err :-: Please supply all required arguments: (%s)" % ', '.join(required)) if self.soc.connected: if all([a,kw]): return # disallow argument mixing # Positional arguments can only be used for the required args input if there are any if a and len(a) == len(required): args_ini.update({k: a[required.index(k)] for k in required}) # Make sure that all required args are supplied via kw, if kw is the input method if kw and len(kw) >= len(required): keys = kw.keys() if required and not all([k in keys for k in required]): raise ValueError("Err :-: Please supply all required arguments: (%s)" % ', '.join(required)) args_ini.update({k: kw.get(k) for k in required + optional}) # Extract final set of arguments args = {k:v for k,v in args_ini.items() if v} # Send command to AMI and capture request id req_id = self.cmd(action, **args) self._pending.add(req_id) return req_id def Ping(self, *a, **kw): """ A 'Ping' action will ellicit a 'Pong' response. Used to keep the manager connection open. """ action = "Ping" if self.soc.connected: # Send command to AMI and capture request id req_id = self.cmd(action) self._pending.add(req_id) return req_id def ListCommands(self, *a, **kw): """ Returns the action name and synopsis for every action that is available to the user. """ action = "ListCommands" if self.soc.connected: # Send command to AMI and capture request id req_id = self.cmd(action) self._pending.add(req_id) return req_id def SIPshowregistry(self, *a, **kw): """ Show SIP registrations (text format). """ action = "SIPshowregistry" if self.soc.connected: # Send command to AMI and capture request id req_id = self.cmd(action) self._pending.add(req_id) self._cache[req_id] = [] return req_id def SIPpeers(self, *a, **kw): """ Lists SIP peers in text format with details on current status. """ action = "SIPpeers" if self.soc.connected: # Send command to AMI and capture request id req_id = self.cmd(action) self._pending.add(req_id) self._cache[req_id] = [] return req_id def SIPshowpeer(self, *a, **kw): """ Show one SIP peer with details on current status. Required args: - Peer: The peer name you want to check. """ action = "SIPshowpeer" required = ["Peer"] optional = [] req_id = self.__query(action, required, optional, a=a, kw=kw) return req_id def SIPqualifypeer(self, *a, **kw): """ Qualify a SIP peer. # Required args: - Peer: The peer name you want to qualify. """ action = "SIPqualifypeer" required = ["Peer"] optional = [] req_id = self.__query(action, required, optional, a=a, kw=kw) return req_id def ShowDialPlan(self, *a, **kw): """ Show dialplan contexts and extensions. Be aware that showing the full dialplan may take a lot of capacity. # Optional args: - Extension: Show a specific extension. - Context: Show a specific context. """ action = "ShowDialPlan" required = [] optional = ["Extension", "Context"] req_id = self.__query(action, required, optional, a=a, kw=kw, evend='ShowDialPlanComplete') self._cache[req_id] = [] return req_id def Context(self, *a, **kw): """ Custom method. List unique dialplan contexts. # Optional args: - Extension: Show a specific extension. """ return sorted({x.od['Context'] for x in self.ShowDialPlan(*a, **kw) if x.od.get('Context')}) def Originate(self, *a, **kw): """ Generates an outgoing call to a <Extension>/<Context>/<Priority> or <Application>/<Data>. # Required args: - Channel: Channel name to call. # Optional args: - Exten: Extension to use (requires 'Context' and 'Priority') - Context: Context to use (requires 'Exten' and 'Priority') - Priority: Priority to use (requires 'Exten' and 'Context') - Application: Application to execute. - Data: Data to use (requires 'Application'). - Timeout: How long to wait for call to be answered (in ms.). - CallerID: Caller ID to be set on the outgoing channel. - Variable: Channel variable to set, multiple Variable: headers are allowed. - Account: Account code. - Async: Set to 'true' for fast origination. - Codecs: Comma-separated list of codecs to use for this call. e.g. dict(Channel="SIP/965", Exten="965", Context="default", Priority="1", CallerID="666", Timeout="10000", Async="Yes") dict(Channel="SIP/965", CallerID="666", Timeout="10000", Async="Yes") """ action = "Originate" required = ["Channel"] optional = ["Exten", "Context", "Priority", "Application", "Data", "Timeout", "CallerID", "Variable", "Account", "Async", "Codecs"] req_id = self.__query(action, required, optional, a=a, kw=kw) return req_id def Hangup(self, *a, **kw): """ Hangup channel. # Required args: - Channel: Channel name to be hangup. # Optional args: - Cause: Numeric hangup cause. """ action = "Hangup" required = ["Channel"] optional = ["Cause"] req_id = self.__query(action, required, optional, a=a, kw=kw) return req_id def Redirect(self, *a, **kw): """ Redirect (transfer) a call. # Required args: - Channel: Channel to redirect. - Exten: Extension to transfer to. - Context: Context to transfer to. - Priority: Priority to transfer to. # Optional args: - ExtraChannel: Second call leg to transfer (optional). - ExtraExten: Extension to transfer extrachannel to (optional). - ExtraContext: Context to transfer extrachannel to (optional). - ExtraPriority: Priority to transfer extrachannel to (optional). """ action = "Redirect" required = ["Channel", "Exten", "Context", "Priority"] optional = ["ExtraChannel", "ExtraExten", "ExtraContext", "ExtraPriority"] req_id = self.__query(action, required, optional, a=a, kw=kw) return req_id def Atxfer(self, *a, **kw): """ Attended transfer. # Required args: - Channel: Transferer's channel. - Exten: Extension to transfer to. - Context: Context to transfer to. - Priority: Priority to transfer to. """ action = "Atxfer" required = ["Channel", "Exten", "Context", "Priority"] optional = [] req_id = self.__query(action, required, optional, a=a, kw=kw) return req_id def PlayDTMF(self, *a, **kw): """ Play DTMF digit (signal) on a specific channel. # Required args: - Channel: Channel name to send digit to. - Digit: The DTMF digit to play. """ action = "PlayDTMF" required = ["Channel", "Digit"] optional = [] req_id = self.__query(action, required, optional, a=a, kw=kw) return req_id def Bridge(self, *a, **kw): """ Bridge together two channels already in the PBX. # Required args: - Channel1: Channel to Bridge to Channel2. - Channel2: Channel to Bridge to Channel1. # Optional args: - Tone: Play courtesy tone to Channel2 (yes/no). """ action = "Bridge" required = ["Channel1", "Channel2"] optional = ["Tone"] req_id = self.__query(action, required, optional, a=a, kw=kw) return req_id def Park(self, *a, **kw): """ Park a channel. # Required args: - Channel: Channel name to park. - Channel2: Channel to return to if timeout. # Optional args: - Timeout: Number of milliseconds to wait before callback. - Parkinglot: Specify in which parking lot to park the channel. """ action = "Park" required = ["Channel", "Channel2"] optional = ["Timeout", "Parkinglot"] req_id = self.__query(action, required, optional, a=a, kw=kw) return req_id def ParkedCalls(self, *a, **kw): """ List parked calls. """ action = "ParkedCalls" if self.soc.connected: # Send command to AMI and capture request id req_id = self.cmd(action) self._pending.add(req_id) self._cache[req_id] = [] return req_id def Queues(self, *a, **kw): """ Show queues information. Check the log for the output """ action = "Queues" if self.soc.connected: # Send command to AMI and capture request id req_id = self.cmd(action) self._pending.add(req_id) def Agents(self, *a, **kw): """ Will list info about all possible agents. """ if self.soc.connected: action = "Agents" # Send command to AMI and capture request id req_id = self.cmd(action) self._pending.add(req_id) self._cache[req_id] = [] return req_id def CoreShowChannels(self, *a, **kw): """ List currently defined channels and some information about them. """ action = "CoreShowChannels" if self.soc.connected: # Send command to AMI and capture request id req_id = self.cmd(action) self._pending.add(req_id) self._cache[req_id] = [] return req_id def CoreStatus(self, *a, **kw): """ Show PBX core status variables. """ action = "CoreStatus" if self.soc.connected: # Send command to AMI and capture request id req_id = self.cmd(action) self._pending.add(req_id) return req_id def CoreSettings(self, *a, **kw): """ Show PBX core settings (version etc). """ action = "CoreSettings" if self.soc.connected: # Send command to AMI and capture request id req_id = self.cmd(action) self._pending.add(req_id) return req_id def Status(self, *a, **kw): """ Will return the status information of each channel along with the value for the specified channel variables. # Optional args: - Channel: The name of the channel to query for status. - Variables: Comma ',' separated list of variable to include. """ action = "Status" required = [] optional = ["Channel", "Variables"] req_id = self.__query(action, required, optional, a=a, kw=kw, evend="StatusComplete") self._cache[req_id] = [] return req_id def GetConfig(self, *a, **kw): """ This action will dump the contents of a configuration file by category and contents or optionally by specified category only. # Required args: - Filename: Configuration filename (e.g. "foo.conf"). # Optional args: - Category: Category in configuration file. """ action = "GetConfig" required = ["Filename"] optional = ["Category"] req_id = self.__query(action, required, optional, a=a, kw=kw) return req_id def GetConfigJSON(self, *a, **kw): """ This action will dump the contents of a configuration file by category and contents in JSON format. This only makes sense to be used using rawman over the HTTP interface. # Required args: - Filename: Configuration filename (e.g. "foo.conf"). """ action = "GetConfigJSON" required = ["Filename"] optional = [] req_id = self.__query(action, required, optional, a=a, kw=kw) return req_id