starpy.fastagi
index
/home/mcfletch/pylive/starpy/fastagi.py

Asterisk FastAGI server for use from the dialplan
 
You use an asterisk FastAGI like this from extensions.conf:
 
        exten => 1000,3,AGI(agi://127.0.0.1:4573,arg1,arg2)
 
Where 127.0.0.1 is the server and 4573 is the port on which 
the server is listening.
 
Module defines a standard Python logging module log 'FastAGI'

 
Modules
       
twisted.protocols.basic
twisted.internet.defer
starpy.error
logging
twisted.internet.protocol
socket
time
twisted.internet.error

 
Classes
       
object
InSequence
Factory
FastAGIFactory
LineOnlyReceiver(Protocol)
FastAGIProtocol

 
class FastAGIFactory(Factory)
    Factory generating FastAGI server instances
 
  Methods defined here:
__init__(self, mainFunction)
Initialise the factory
 
mainFunction -- function taking a connected FastAGIProtocol instance 
        this is the function that's run when the Asterisk server connects.

Data and other attributes defined here:
protocol = <class starpy.fastagi.FastAGIProtocol>
Protocol for the interfacing with the Asterisk FastAGI application
 
Attributes:
 
        variables -- for  connected protocol, the set of variables passed 
                during initialisation, keys are all-lower-case, set of variables
                returned for an Asterisk 1.2.1 installation on Gentoo on a locally
                connected channel:
                
                        agi_network = 'yes'
                        agi_request = 'agi://localhost'
                        agi_channel = 'SIP/mike-ccca'
                        agi_language = 'en'
                        agi_type = 'SIP'
                        agi_uniqueid = '1139871605.0'
                        agi_callerid = 'mike'
                        agi_calleridname = 'Mike Fletcher'
                        agi_callingpres = '0'
                        agi_callingani2 = '0'
                        agi_callington = '0'
                        agi_callingtns = '0'
                        agi_dnid = '1'
                        agi_rdnis = 'unknown'
                        agi_context = 'testing'
                        agi_extension = '1'
                        agi_priority = '1'
                        agi_enhanced = '0.0'
                        agi_accountcode = ''
 
        # Internal:
        readingVariables -- whether the instance is still in initialising by
                reading the setup variables from the connection 
        messageCache -- stores incoming variables 
        pendingMessages -- set of outstanding messages for which we expect 
                replies
        delimiter -- uses bald newline instead of carriage-return-newline
 
XXX Lots of problems with data-escaping, no docs on how to escape special 
        characters that I can see...

Methods inherited from Factory:
buildProtocol(self, addr)
Create an instance of a subclass of Protocol.
 
The returned instance will handle input on an incoming server
connection, and an attribute "factory" pointing to the creating
factory.
 
Override this method to alter how Protocol instances get created.
 
@param addr: an object implementing L{twisted.internet.interfaces.IAddress}
doStart(self)
Make sure startFactory is called.
 
Users should not call this function themselves!
doStop(self)
Make sure stopFactory is called.
 
Users should not call this function themselves!
startFactory(self)
This will be called before I begin listening on a Port or Connector.
 
It will only be called once, even if the factory is connected
to multiple ports.
 
This can be used to perform 'unserialization' tasks that
are best put off until things are actually running, such
as connecting to a database, opening files, etcetera.
stopFactory(self)
This will be called before I stop listening on all Ports/Connectors.
 
This can be overridden to perform 'shutdown' tasks such as disconnecting
database connections, closing files, etc.
 
It will be called, for example, before an application shuts down,
if it was connected to a port. User code should not call this function
directly.

Data and other attributes inherited from Factory:
__implemented__ = <implementedBy twisted.internet.protocol.Factory>
__implements__ = (<MetaInterface twisted.internet.interfaces.IProtocolFactory>,)
__providedBy__ = <zope.interface.declarations.Declaration object>
noisy = True
numPorts = 0

 
class FastAGIProtocol(LineOnlyReceiver)
    Protocol for the interfacing with the Asterisk FastAGI application
 
Attributes:
 
        variables -- for  connected protocol, the set of variables passed 
                during initialisation, keys are all-lower-case, set of variables
                returned for an Asterisk 1.2.1 installation on Gentoo on a locally
                connected channel:
                
                        agi_network = 'yes'
                        agi_request = 'agi://localhost'
                        agi_channel = 'SIP/mike-ccca'
                        agi_language = 'en'
                        agi_type = 'SIP'
                        agi_uniqueid = '1139871605.0'
                        agi_callerid = 'mike'
                        agi_calleridname = 'Mike Fletcher'
                        agi_callingpres = '0'
                        agi_callingani2 = '0'
                        agi_callington = '0'
                        agi_callingtns = '0'
                        agi_dnid = '1'
                        agi_rdnis = 'unknown'
                        agi_context = 'testing'
                        agi_extension = '1'
                        agi_priority = '1'
                        agi_enhanced = '0.0'
                        agi_accountcode = ''
 
        # Internal:
        readingVariables -- whether the instance is still in initialising by
                reading the setup variables from the connection 
        messageCache -- stores incoming variables 
        pendingMessages -- set of outstanding messages for which we expect 
                replies
        delimiter -- uses bald newline instead of carriage-return-newline
 
XXX Lots of problems with data-escaping, no docs on how to escape special 
        characters that I can see...
 
 
Method resolution order:
FastAGIProtocol
LineOnlyReceiver
Protocol
BaseProtocol

Methods defined here:
__init__(self, *args, **named)
Initialise the AMIProtocol, arguments are ignored
answer(self)
Answer the channel (go off-hook)
 
Returns deferred integer response code
channelStatus(self, channel=None)
Retrieve the current channel's status
 
Result integers (from the wiki):
        0 Channel is down and available
        1 Channel is down, but reserved
        2 Channel is off hook
        3 Digits (or equivalent) have been dialed
        4 Line is ringing
        5 Remote end is ringing
        6 Line is up
        7 Line is busy 
 
Returns deferred integer result code
 
This could be used to decide if we can forward the channel to a given 
user, or whether we need to shunt them off somewhere else.
checkFailure(self, result, failure='-1')
(Internal) Check for a failure-code, raise error if == result
connectionLost(self, reason)
(Internal) Handle loss of the connection (remote hangup)
connectionMade(self)
(Internal) Handle incoming connection (new AGI request)
 
Initiates read of the initial attributes passed by the server
controlStreamFile(self, filename, escapeDigits, skipMS=0, ffChar='*', rewChar='#', pauseChar=None)
Playback specified file with ability to be controlled by user
 
filename -- filename to play (on the asterisk server) 
        (don't use file-type extension!)
escapeDigits -- if provided, 
skipMS -- number of milliseconds to skip on FF/REW
ffChar -- if provided, the set of chars that fast-forward
rewChar -- if provided, the set of chars that rewind
pauseChar -- if provided, the set of chars that pause playback
 
returns deferred (digit,endpos) on success, or errors on failure, 
        note that digit will be 0 if no digit was pressed AFAICS
databaseDel(self, family, key)
Delete the given key from the database
 
Returns deferred integer result code
databaseDeltree(self, family, keyTree=None)
Delete an entire family or a tree within a family from database
 
Returns deferred integer result code
databaseGet(self, family, key)
Retrieve value of the given key from database
 
Returns deferred string value for the key
databasePut = databaseSet(self, family, key, value)
databaseSet(self, family, key, value)
Set value of the given key to database
 
a.k.a databasePut on the asterisk side
 
Returns deferred integer result code
dateAsSeconds(self, date)
(Internal) Convert date to asterisk-compatible format
execute(self, application, *options)
Execute a dialplan application with given options
 
Note: asterisk calls this "exec", which is Python keyword
 
Returns deferred string result for the application, which 
may have failed, result values are application dependant.
finish(self)
Finish the AGI "script" (drop connection)
 
This command simply drops the connection to the Asterisk server,
which the FastAGI protocol interprets as a successful termination.
 
Note: There *should* be a mechanism for sending a "result" code,
but I haven't found any documentation for it.
getData(self, filename, timeout=2.0, maxDigits=None)
Playback file, collecting up to maxDigits or waiting up to timeout
 
filename -- filename without extension to play
timeout -- timeout in seconds (Asterisk uses milliseconds)
maxDigits -- maximum number of digits to collect
 
returns deferred (str(digits), bool(timedOut))
getOption(self, filename, escapeDigits, timeout=None)
Playback file, collect 1 digit or timeout (return 0)
 
filename -- filename to play 
escapeDigits -- digits which cancel playback/recording
timeout -- timeout in seconds (Asterisk uses milliseconds)
 
returns (chr(option) or '' on timeout, endpos)
getVariable(self, variable)
Retrieve the given channel variable
 
From the wiki, variables of interest:
 
        ACCOUNTCODE -- Account code, if specified
        ANSWEREDTIME -- Time call was answered
        BLINDTRANSFER -- Active SIP channel that dialed the number. 
                This will return the SIP Channel that dialed the number when 
                doing blind transfers
        CALLERID -- Current Caller ID (name and number) # deprecated?
        CALLINGPRES -- PRI Call ID Presentation variable for incoming calls 
        CHANNEL -- Current channel name
        CONTEXT -- Current context name
        DATETIME -- Current datetime in format: DDMMYYYY-HH:MM:SS
        DIALEDPEERNAME -- Name of called party (Broken)
        DIALEDPEERNUMBER -- Number of the called party (Broken)
        DIALEDTIME -- Time number was dialed
        DIALSTATUS -- Status of the call
        DNID -- Dialed Number Identifier (limited apparently)
        EPOCH -- UNIX-style epoch-based time (seconds since 1 Jan 1970)
        EXTEN -- Current extension
        HANGUPCAUSE -- Last hangup return code on a Zap channel connected 
                to a PRI interface
        INVALID_EXTEN -- Extension asked for when redirected to the i 
                (invalid) extension
        LANGUAGE -- The current language setting. See Asterisk 
                multi-language
        MEETMESECS -- Number of seconds user participated in a MeetMe 
                conference
        PRIORITY -- Current priority
        RDNIS -- The current redirecting DNIS, Caller ID that redirected 
                the call. Limitations apply.
        SIPDOMAIN -- SIP destination domain of an inbound call 
                (if appropriate)
        SIP_CODEC -- Used to set the SIP codec for a call (apparently 
                broken in Ver 1.0.1, ok in Ver. 1.0.3 & 1.0.4, not sure about 
                1.0.2)
        SIPCALLID -- SIP dialog Call-ID: header
        SIPUSERAGENT -- SIP user agent header (remote agent)
        TIMESTAMP -- Current datetime in the format: YYYYMMDD-HHMMSS
        TXTCIDNAME -- Result of application TXTCIDName
        UNIQUEID -- Current call unique identifier 
        TOUCH_MONITOR -- Used for "one touch record" (see features.conf, 
                and wW dial flags). If is set on either side of the call then 
                that var contains the app_args for app_monitor otherwise the 
                default of WAV||m is used
 
Returns deferred string value for the key
hangup(self, channel=None)
Cause the server to hang up on the channel
 
Returns deferred integer response code
 
Note: This command just doesn't seem to work with Asterisk 1.2.1,
connected channels just remain connected.
jumpOnError(self, reason, difference=100, forErrors=None)
On error, jump to original priority+100
 
This is intended to be registered as an errBack on a deferred for 
an end-user application.  It performs the Asterisk-standard-ish 
jump-on-failure operation, jumping to new priority of 
priority+difference.  It also forces return to the same context and 
extension, in case some other piece of code has changed those.
 
difference -- priority jump to execute 
forErrors -- if specified, a tuple of error classes to which this 
        particular jump is limited (i.e. only errors of this type will 
        generate a jump & disconnect)
 
returns deferred from the InSequence of operations required to reset
the address...
lineReceived(self, line)
(Internal) Handle Twisted's report of an incoming line from the manager
noop(self)
Send a null operation to the server
 
Returns deferred integer response code
onStreamingComplete(self, resultLine, skipMS=0)
(Internal) Handle putative success, watch for failure-on-load problems
receiveChar(self, timeout=None)
Receive a single text char on text-supporting channels (rare)
 
timeout -- timeout in seconds (Asterisk uses milliseconds)
 
returns deferred (char, bool(timeout))
receiveText(self, timeout=None)
Receive text until timeout
 
timeout -- timeout in seconds (Asterisk uses milliseconds)
 
Returns deferred string response value (unaltered)
recordFile(self, filename, format, escapeDigits, timeout=-1, offsetSamples=None, beep=True, silence=None)
Record channel to given filename until escapeDigits or silence
 
filename -- filename on the server to which to save 
format -- encoding format in which to save data
escapeDigits -- digits which end recording 
timeout -- maximum time to record in seconds, -1 gives infinite
        (Asterisk uses milliseconds)
offsetSamples -- move into file this number of samples before recording?
        XXX check semantics here.
beep -- if true, play a Beep on channel to indicate start of recording
silence -- if specified, silence duration to trigger end of recording 
 
returns deferred (str(code/digits), typeOfExit, endpos)
 
Where known typeOfExits include:
        hangup, code='0'
        dtmf, code=digits-pressed 
        timeout, code='0'
resultAsInt(self, result)
(Internal) Convert result to an integer value
resultPlusTimeoutFlag(self, resultLine)
(Internal) Result followed by optional flag declaring timeout
sayAlpha(self, string, escapeDigits=None)
Spell out character string to the user until escapeDigits
 
returns deferred 0 or the digit pressed
sayDate(self, date, escapeDigits=None)
Spell out the date (with somewhat unnatural form)
 
See sayDateTime with format 'ABdY' for a more natural reading
 
returns deferred 0 or digit-pressed as integer
sayDateTime(self, time, escapeDigits='', format=None, timezone=None)
Say given date/time in given format until escapeDigits
 
time -- datetime or float-seconds-since-epoch
escapeDigits -- digits to cancel playback 
format -- strftime-style format for the date to be read 
        'filename' -- filename of a soundfile (single ticks around the filename required)
        A or a -- Day of week (Saturday, Sunday, ...)
        B or b or h -- Month name (January, February, ...)
        d or e -- numeric day of month (first, second, ..., thirty-first)
        Y -- Year
        I or l -- Hour, 12 hour clock
        H -- Hour, 24 hour clock (single digit hours preceded by "oh")
        k -- Hour, 24 hour clock (single digit hours NOT preceded by "oh")
        M -- Minute
        P or p -- AM or PM
        Q -- "today", "yesterday" or ABdY (*note: not standard strftime value)
        q -- "" (for today), "yesterday", weekday, or ABdY (*note: not standard strftime value)
        R -- 24 hour time, including minute
        
        Default format is "ABdY 'digits/at' IMp"
timezone -- optional timezone name from /usr/share/zoneinfo
 
returns deferred 0 or digit-pressed as integer
sayDigits(self, number, escapeDigits=None)
Spell out the number/string as a string of digits
 
returns deferred 0 or digit-pressed as integer
sayNumber(self, number, escapeDigits=None)
Say a number in natural form
 
returns deferred 0 or digit-pressed as integer
sayPhonetic(self, string, escapeDigits=None)
Say string using phonetics
 
returns deferred 0 or digit-pressed as integer
sayTime(self, time, escapeDigits=None)
Say string using phonetics
 
returns deferred 0 or digit-pressed as integer
sayXXX(self, baseCommand, value, escapeDigits='')
Underlying implementation for the common-api sayXXX functions
secondResultItem(self, result)
(Internal) Retrieve the second item on the result-line
sendCommand(self, commandString)
(Internal) Send the given command to the other side
sendImage(self, filename)
Send image on those channels which support sending images (rare)
 
returns deferred integer result code
sendText(self, text)
Send text on text-supporting channels (rare)
 
returns deferred integer result code
setAutoHangup(self, time)
Set channel to automatically hang up after time seconds
 
time -- time in seconds in the future to hang up...
 
returns deferred integer result code
setCallerID(self, number)
Set channel's caller ID to given number
 
returns deferred integer result code
setContext(self, context)
Move channel to given context (no error checking is performed)
 
returns deferred integer result code
setExtension(self, extension)
Move channel to given extension (or 'i' if invalid) or drop if neither there
 
returns deferred integer result code
setMusic(self, on=True, musicClass=None)
Enable/disable and/or choose music class for channel's music-on-hold
 
returns deferred integer result code
setPriority(self, priority)
Move channel to given priority or drop if not there
 
returns deferred integer result code
setVariable(self, variable, value)
Set given channel variable to given value
 
returns deferred integer result code
streamFile(self, filename, escapeDigits='', offset=0)
Stream given file until escapeDigits starting from offset
 
returns deferred (str(digit), int(endpos)) for playback
 
Note: streamFile is apparently unstable in AGI, may want to use
execute( 'PLAYBACK', ... ) instead (according to the Wiki)
tddMode(self, on=True)
Set TDD mode on the channel if possible (ZAP only ATM)
 
on -- ON (True), OFF (False) or MATE (None)
 
returns deferred integer result code
verbose(self, message, level=None)
Send a logging message to the asterisk console for debugging etc
 
message -- text to pass 
level -- 1-4 denoting verbosity level
 
returns deferred integer result code
wait(self, duration)
Wait for X seconds (just a wrapper around callLater, doesn't talk to server)
 
returns deferred which fires some time after duration seconds have 
passed
waitForDigit(self, timeout)
Wait up to timeout seconds for single digit to be pressed 
 
timeout -- timeout in seconds or -1 for infinite timeout 
        (Asterisk uses milliseconds)
 
returns deferred 0 on timeout or digit

Data and other attributes defined here:
delimiter = '\n'
readingVariables = False

Methods inherited from LineOnlyReceiver:
dataReceived(self, data)
Translates bytes into lines, and calls lineReceived.
lineLengthExceeded(self, line)
Called when the maximum line length has been reached.
Override if it needs to be dealt with in some special way.
sendLine(self, line)
Sends a line to the other end of the connection.

Data and other attributes inherited from LineOnlyReceiver:
MAX_LENGTH = 16384

Data and other attributes inherited from Protocol:
__implemented__ = <implementedBy twisted.internet.protocol.Protocol>
__implements__ = (<MetaInterface twisted.internet.interfaces.IProtocol>,)

Methods inherited from BaseProtocol:
makeConnection(self, transport)
Make a connection to a transport and a server.
 
This sets the 'transport' attribute of this Protocol, and calls the
connectionMade() callback.

Data and other attributes inherited from BaseProtocol:
__providedBy__ = <zope.interface.declarations.Declaration object>
connected = 0
transport = None

 
class InSequence(object)
    Single-shot item creating a set of actions to run in sequence
 
  Methods defined here:
__call__(self)
Return deferred that fires when we are finished processing all items
__init__(self)
append(self, function, *args, **named)
Append an action to the set of actions to process
onActionFailure(self, reason, finalDF)
Handle individual-action failure
onActionSuccess(self, result, finalDF)
Handle individual-action success
recordResult(self, result)
Record the result for later

Data and other attributes defined here:
__dict__ = <dictproxy object>
dictionary for instance variables (if defined)
__weakref__ = <attribute '__weakref__' of 'InSequence' objects>
list of weak references to the object (if defined)

 
Data
        FAILURE_CODE = -1
reactor = <twisted.internet.selectreactor.SelectReactor instance>