Merge branch 'release/0.4'

develop
Randall Degges 2011-05-31 17:24:21 -07:00
commit 7493c64c9b
16 changed files with 107 additions and 323 deletions

View File

@ -1,3 +1,7 @@
2011-05-31 Randall Degges <rdegges@gmail.com
* BUGFIX: Fixing issue that prevented manager.status command from returning
proper output.
2007-01-26 Matthew Nicholson <mnicholson@digium.com> 2007-01-26 Matthew Nicholson <mnicholson@digium.com>
* asterisk/manager.py: Make get_header() functions work like * asterisk/manager.py: Make get_header() functions work like
@ -28,14 +32,14 @@
and quoted arguments. and quoted arguments.
2006-10-24 Matthew Nicholson <mnicholson@digium.com> 2006-10-24 Matthew Nicholson <mnicholson@digium.com>
* asterisk/agi.py: Added get_variable_full command. * asterisk/agi.py: Added get_variable_full command.
2006-10-18 Matthew Nicholson <mnicholson@digium.com> 2006-10-18 Matthew Nicholson <mnicholson@digium.com>
* asterisk/agitb.py: Make error output default to sys.stderr instead * asterisk/agitb.py: Make error output default to sys.stderr instead
of sys.stdout. of sys.stdout.
2006-09-19 Matthew Nicholson <mnicholson@digium.com> 2006-09-19 Matthew Nicholson <mnicholson@digium.com>
* debian/control: Removed XS-Python-Versions header to make it default * debian/control: Removed XS-Python-Versions header to make it default

View File

@ -1,11 +1,3 @@
include debian/watch include CHANGELOG
include debian/rules
include debian/changelog
include debian/control
include debian/compat
include debian/copyright
include rpm/python-pyst.spec
include ChangeLog
include README include README
include README.html
include MANIFEST.in include MANIFEST.in

21
README
View File

@ -1,9 +1,3 @@
.. image:: http://sflogo.sourceforge.net/sflogo.php?group_id=134329&type=7
:height: 62
:width: 210
:alt: SourceForge.net Logo
:target: http://sourceforge.net/projects/pyst/
pyst: A Python Interface to Asterisk pyst: A Python Interface to Asterisk
==================================== ====================================
@ -41,10 +35,21 @@ directly on the host where Asterisk is running. Since Asterisk doesn't
run on windows platforms (and probably never will) the agi part of the run on windows platforms (and probably never will) the agi part of the
package can only be run on Asterisk platforms. package can only be run on Asterisk platforms.
.. note::
This project has been forked because it was impossible for me to contact
the project maintainer (after several attempts), and I'd like to bring the
project up-to-date, fix bugs, and make it more usable overall.
My plans immediate plans include adding full documentation, re-writing some
of the core routines, adding a test suite, and accepting pull requests.
If you are one of the current maintainers, and would like to take over the
fork, please contact me: rdegges@gmail.com, so we can get that setup!
Credits Credits
------- -------
Thanks to Karl Putland for writing the original package. Thanks to Karl Putland for writing the original package.
Thanks to Matthew Nicholson for maintaining the package for some years Thanks to Matthew Nicholson for maintaining the package for some years
and for handing over maintenance when he was no longer interested. and for handing over maintenance when he was no longer interested.
@ -154,7 +159,7 @@ Version 0.3: Minor feature enhancements
New maintainer Ralf Schlatterbeck, this is my first release, please New maintainer Ralf Schlatterbeck, this is my first release, please
report any problems via the Sourceforge Bug-Tracker or email me report any problems via the Sourceforge Bug-Tracker or email me
directly. Thanks to Karl Putland for writing the original package. directly. Thanks to Karl Putland for writing the original package.
Thanks to Matthew Nicholson for maintaining the package for some years Thanks to Matthew Nicholson for maintaining the package for some years
and for handing over maintenance when he was no longer interested. and for handing over maintenance when he was no longer interested.
The parsing of answers from asterisk was completely rewritten. This The parsing of answers from asterisk was completely rewritten. This

View File

@ -11,4 +11,4 @@ manager - a module for interacting with the asterisk manager interface
""" """
__all__ = ['agi', 'agitb', 'config', 'manager'] __all__ = ['agi', 'agitb', 'config', 'manager']
__version__ = '0.4'

View File

@ -52,9 +52,9 @@ class AGI:
""" """
This class encapsulates communication between Asterisk an a python script. This class encapsulates communication between Asterisk an a python script.
It handles encoding commands to Asterisk and parsing responses from It handles encoding commands to Asterisk and parsing responses from
Asterisk. Asterisk.
""" """
def __init__(self): def __init__(self):
self._got_sighup = False self._got_sighup = False
signal.signal(signal.SIGHUP, self._handle_sighup) # handle SIGHUP signal.signal(signal.SIGHUP, self._handle_sighup) # handle SIGHUP
@ -93,7 +93,7 @@ class AGI:
"""This function throws AGIHangup if we have recieved a SIGHUP""" """This function throws AGIHangup if we have recieved a SIGHUP"""
if self._got_sighup: if self._got_sighup:
raise AGISIGHUPHangup("Received SIGHUP from Asterisk") raise AGISIGHUPHangup("Received SIGHUP from Asterisk")
def execute(self, command, *args): def execute(self, command, *args):
self.test_hangup() self.test_hangup()
@ -208,17 +208,17 @@ class AGI:
def tdd_mode(self, mode='off'): def tdd_mode(self, mode='off'):
"""agi.tdd_mode(mode='on'|'off') --> None """agi.tdd_mode(mode='on'|'off') --> None
Enable/Disable TDD transmission/reception on a channel. Enable/Disable TDD transmission/reception on a channel.
Throws AGIAppError if channel is not TDD-capable. Throws AGIAppError if channel is not TDD-capable.
""" """
res = self.execute('TDD MODE', mode)['result'][0] res = self.execute('TDD MODE', mode)['result'][0]
if res == '0': if res == '0':
raise AGIAppError('Channel %s is not TDD-capable') raise AGIAppError('Channel %s is not TDD-capable')
def stream_file(self, filename, escape_digits='', sample_offset=0): def stream_file(self, filename, escape_digits='', sample_offset=0):
"""agi.stream_file(filename, escape_digits='', sample_offset=0) --> digit """agi.stream_file(filename, escape_digits='', sample_offset=0) --> digit
Send the given file, allowing playback to be interrupted by the given Send the given file, allowing playback to be interrupted by the given
digits, if any. escape_digits is a string '12345' or a list of digits, if any. escape_digits is a string '12345' or a list of
ints [1,2,3,4,5] or strings ['1','2','3'] or mixed [1,'2',3,'4'] ints [1,2,3,4,5] or strings ['1','2','3'] or mixed [1,'2',3,'4']
If sample offset is provided then the audio will seek to sample If sample offset is provided then the audio will seek to sample
offset before play starts. Returns digit if one was pressed. offset before play starts. Returns digit if one was pressed.
@ -235,11 +235,11 @@ class AGI:
return chr(int(res)) return chr(int(res))
except: except:
raise AGIError('Unable to convert result to char: %s' % res) raise AGIError('Unable to convert result to char: %s' % res)
def control_stream_file(self, filename, escape_digits='', skipms=3000, fwd='', rew='', pause=''): def control_stream_file(self, filename, escape_digits='', skipms=3000, fwd='', rew='', pause=''):
""" """
Send the given file, allowing playback to be interrupted by the given Send the given file, allowing playback to be interrupted by the given
digits, if any. escape_digits is a string '12345' or a list of digits, if any. escape_digits is a string '12345' or a list of
ints [1,2,3,4,5] or strings ['1','2','3'] or mixed [1,'2',3,'4'] ints [1,2,3,4,5] or strings ['1','2','3'] or mixed [1,'2',3,'4']
If sample offset is provided then the audio will seek to sample If sample offset is provided then the audio will seek to sample
offset before play starts. Returns digit if one was pressed. offset before play starts. Returns digit if one was pressed.
@ -270,7 +270,7 @@ class AGI:
def say_digits(self, digits, escape_digits=''): def say_digits(self, digits, escape_digits=''):
"""agi.say_digits(digits, escape_digits='') --> digit """agi.say_digits(digits, escape_digits='') --> digit
Say a given digit string, returning early if any of the given DTMF digits Say a given digit string, returning early if any of the given DTMF digits
are received on the channel. are received on the channel.
Throws AGIError on channel failure Throws AGIError on channel failure
""" """
digits = self._process_digit_list(digits) digits = self._process_digit_list(digits)
@ -287,7 +287,7 @@ class AGI:
def say_number(self, number, escape_digits=''): def say_number(self, number, escape_digits=''):
"""agi.say_number(number, escape_digits='') --> digit """agi.say_number(number, escape_digits='') --> digit
Say a given digit string, returning early if any of the given DTMF digits Say a given digit string, returning early if any of the given DTMF digits
are received on the channel. are received on the channel.
Throws AGIError on channel failure Throws AGIError on channel failure
""" """
number = self._process_digit_list(number) number = self._process_digit_list(number)
@ -304,7 +304,7 @@ class AGI:
def say_alpha(self, characters, escape_digits=''): def say_alpha(self, characters, escape_digits=''):
"""agi.say_alpha(string, escape_digits='') --> digit """agi.say_alpha(string, escape_digits='') --> digit
Say a given character string, returning early if any of the given DTMF Say a given character string, returning early if any of the given DTMF
digits are received on the channel. digits are received on the channel.
Throws AGIError on channel failure Throws AGIError on channel failure
""" """
characters = self._process_digit_list(characters) characters = self._process_digit_list(characters)
@ -321,7 +321,7 @@ class AGI:
def say_phonetic(self, characters, escape_digits=''): def say_phonetic(self, characters, escape_digits=''):
"""agi.say_phonetic(string, escape_digits='') --> digit """agi.say_phonetic(string, escape_digits='') --> digit
Phonetically say a given character string, returning early if any of Phonetically say a given character string, returning early if any of
the given DTMF digits are received on the channel. the given DTMF digits are received on the channel.
Throws AGIError on channel failure Throws AGIError on channel failure
""" """
characters = self._process_digit_list(characters) characters = self._process_digit_list(characters)
@ -364,7 +364,7 @@ class AGI:
return chr(int(res)) return chr(int(res))
except: except:
raise AGIError('Unable to convert result to char: %s' % res) raise AGIError('Unable to convert result to char: %s' % res)
def say_datetime(self, seconds, escape_digits='', format='', zone=''): def say_datetime(self, seconds, escape_digits='', format='', zone=''):
"""agi.say_datetime(seconds, escape_digits='', format='', zone='') --> digit """agi.say_datetime(seconds, escape_digits='', format='', zone='') --> digit
Say a given date in the format specfied (see voicemail.conf), returning Say a given date in the format specfied (see voicemail.conf), returning
@ -389,11 +389,11 @@ class AGI:
result = self.execute('GET DATA', filename, timeout, max_digits) result = self.execute('GET DATA', filename, timeout, max_digits)
res, value = result['result'] res, value = result['result']
return res return res
def get_option(self, filename, escape_digits='', timeout=0): def get_option(self, filename, escape_digits='', timeout=0):
"""agi.get_option(filename, escape_digits='', timeout=0) --> digit """agi.get_option(filename, escape_digits='', timeout=0) --> digit
Send the given file, allowing playback to be interrupted by the given Send the given file, allowing playback to be interrupted by the given
digits, if any. escape_digits is a string '12345' or a list of digits, if any. escape_digits is a string '12345' or a list of
ints [1,2,3,4,5] or strings ['1','2','3'] or mixed [1,'2',3,'4'] ints [1,2,3,4,5] or strings ['1','2','3'] or mixed [1,'2',3,'4']
Returns digit if one was pressed. Returns digit if one was pressed.
Throws AGIError if the channel was disconnected. Remember, the file Throws AGIError if the channel was disconnected. Remember, the file
@ -449,9 +449,9 @@ class AGI:
def record_file(self, filename, format='gsm', escape_digits='#', timeout=DEFAULT_RECORD, offset=0, beep='beep'): def record_file(self, filename, format='gsm', escape_digits='#', timeout=DEFAULT_RECORD, offset=0, beep='beep'):
"""agi.record_file(filename, format, escape_digits, timeout=DEFAULT_TIMEOUT, offset=0, beep='beep') --> None """agi.record_file(filename, format, escape_digits, timeout=DEFAULT_TIMEOUT, offset=0, beep='beep') --> None
Record to a file until a given dtmf digit in the sequence is received Record to a file until a given dtmf digit in the sequence is received
The format will specify what kind of file will be recorded. The timeout The format will specify what kind of file will be recorded. The timeout
is the maximum record time in milliseconds, or -1 for no timeout. Offset is the maximum record time in milliseconds, or -1 for no timeout. Offset
samples is optional, and if provided will seek to the offset without samples is optional, and if provided will seek to the offset without
exceeding the end of the file exceeding the end of the file
""" """
escape_digits = self._process_digit_list(escape_digits) escape_digits = self._process_digit_list(escape_digits)
@ -589,7 +589,7 @@ class AGI:
res, value = result['result'] res, value = result['result']
if res == '0': if res == '0':
raise AGIDBError('Unable to put vaule in databale: family=%s, key=%s, value=%s' % (family, key, value)) raise AGIDBError('Unable to put vaule in databale: family=%s, key=%s, value=%s' % (family, key, value))
def database_del(self, family, key): def database_del(self, family, key):
"""agi.database_del(family, key) --> None """agi.database_del(family, key) --> None
Deletes an entry in the Asterisk database for a Deletes an entry in the Asterisk database for a

View File

@ -12,7 +12,7 @@ at the top of your script. The optional arguments to enable() are:
agi - the agi handle to write verbose messages to agi - the agi handle to write verbose messages to
display - if true, tracebacks are displayed on the asterisk console display - if true, tracebacks are displayed on the asterisk console
(used with the agi option) (used with the agi option)
logdir - if set, tracebacks are written to files in this directory logdir - if set, tracebacks are written to files in this directory
context - number of lines of source code to show for each stack frame context - number of lines of source code to show for each stack frame
@ -194,7 +194,7 @@ class Hook:
self.agi.verbose(msg, 4) self.agi.verbose(msg, 4)
else: else:
self.file.write(msg + '\n') self.file.write(msg + '\n')
try: try:
self.file.flush() self.file.flush()
except: pass except: pass
@ -210,7 +210,7 @@ def enable(agi=None, display=1, logdir=None, context=5):
except_hook = Hook(display=display, logdir=logdir, except_hook = Hook(display=display, logdir=logdir,
context=context, agi=agi) context=context, agi=agi)
sys.excepthook = except_hook sys.excepthook = except_hook
global handler global handler
handler = except_hook.handle handler = except_hook.handle

View File

@ -7,7 +7,7 @@ This module provides parsing functionality for asterisk config files.
import asterisk.config import asterisk.config
import sys import sys
# load and parse the config file # load and parse the config file
try: try:
config = asterisk.config.Config('/etc/asterisk/extensions.conf') config = asterisk.config.Config('/etc/asterisk/extensions.conf')
@ -68,7 +68,7 @@ class Category(Line):
self.items = [] self.items = []
self.comments = [] self.comments = []
def get_line(self): def get_line(self):
if self.comment: if self.comment:
return '[%s]\t;%s' % (self.name, self.comment) return '[%s]\t;%s' % (self.name, self.comment)
@ -86,7 +86,7 @@ class Category(Line):
def remove(self, item): def remove(self, item):
self.items.remove(item) self.items.remove(item)
class Item(Line): class Item(Line):
def __init__(self, line='', num=-1, name=None, value=None): def __init__(self, line='', num=-1, name=None, value=None):
Line.__init__(self, line, num) Line.__init__(self, line, num)
@ -98,7 +98,7 @@ class Item(Line):
self.value = value self.value = value
else: else:
raise Exception("Must provide name or value representing an item") raise Exception("Must provide name or value representing an item")
def parse(self): def parse(self):
try: try:
name, value = self.line.split('=', 1) name, value = self.line.split('=', 1)

View File

@ -13,21 +13,21 @@ This module provides a Python API for interfacing with the asterisk manager.
print "Recieved shutdown event" print "Recieved shutdown event"
manager.close() manager.close()
# we could analize the event and reconnect here # we could analize the event and reconnect here
def handle_event(event, manager): def handle_event(event, manager):
print "Recieved event: %s" % event.name print "Recieved event: %s" % event.name
manager = asterisk.manager.Manager() manager = asterisk.manager.Manager()
try: try:
# connect to the manager # connect to the manager
try: try:
manager.connect('host') manager.connect('host')
manager.login('user', 'secret') manager.login('user', 'secret')
# register some callbacks # register some callbacks
manager.register_event('Shutdown', handle_shutdown) # shutdown manager.register_event('Shutdown', handle_shutdown) # shutdown
manager.register_event('*', handle_event) # catch all manager.register_event('*', handle_event) # catch all
# get a status report # get a status report
response = manager.status() response = manager.status()
@ -41,7 +41,7 @@ This module provides a Python API for interfacing with the asterisk manager.
except asterisk.manager.ManagerException, reason: except asterisk.manager.ManagerException, reason:
print "Error: %s" % reason print "Error: %s" % reason
sys.exit(1) sys.exit(1)
finally: finally:
# remember to clean up # remember to clean up
manager.close() manager.close()
@ -63,14 +63,14 @@ from time import sleep
EOL = '\r\n' EOL = '\r\n'
class ManagerMsg(object): class ManagerMsg(object):
"""A manager interface message""" """A manager interface message"""
def __init__(self, response): def __init__(self, response):
# the raw response, straight from the horse's mouth: # the raw response, straight from the horse's mouth:
self.response = response self.response = response
self.data = '' self.data = ''
self.headers = {} self.headers = {}
# parse the response # parse the response
self.parse(response) self.parse(response)
@ -94,7 +94,7 @@ class ManagerMsg(object):
self.headers['Event'] = 'NoClue' self.headers['Event'] = 'NoClue'
else: else:
self.headers['Response'] = 'Generated Header' self.headers['Response'] = 'Generated Header'
def parse(self, response): def parse(self, response):
"""Parse a manager message""" """Parse a manager message"""
@ -143,7 +143,7 @@ class Event(object):
# get the event name # get the event name
self.name = message.get_header('Event') self.name = message.get_header('Event')
def has_header(self, hname): def has_header(self, hname):
"""Check for a header""" """Check for a header"""
return self.headers.has_key(hname) return self.headers.has_key(hname)
@ -151,11 +151,11 @@ class Event(object):
def get_header(self, hname, defval = None): def get_header(self, hname, defval = None):
"""Return the specfied header""" """Return the specfied header"""
return self.headers.get(hname, defval) return self.headers.get(hname, defval)
def __getitem__(self, hname): def __getitem__(self, hname):
"""Return the specfied header""" """Return the specfied header"""
return self.headers[hname] return self.headers[hname]
def __repr__(self): def __repr__(self):
return self.headers['Event'] return self.headers['Event']
@ -168,7 +168,7 @@ class Manager(object):
self.title = None # set by received greeting self.title = None # set by received greeting
self._connected = threading.Event() self._connected = threading.Event()
self._running = threading.Event() self._running = threading.Event()
# our hostname # our hostname
self.hostname = socket.gethostname() self.hostname = socket.gethostname()
@ -185,11 +185,11 @@ class Manager(object):
# sequence stuff # sequence stuff
self._seqlock = threading.Lock() self._seqlock = threading.Lock()
self._seq = 0 self._seq = 0
# some threads # some threads
self.message_thread = threading.Thread(target=self.message_loop) self.message_thread = threading.Thread(target=self.message_loop)
self.event_dispatch_thread = threading.Thread(target=self.event_dispatch) self.event_dispatch_thread = threading.Thread(target=self.event_dispatch)
self.message_thread.setDaemon(True) self.message_thread.setDaemon(True)
self.event_dispatch_thread.setDaemon(True) self.event_dispatch_thread.setDaemon(True)
@ -211,11 +211,11 @@ class Manager(object):
finally: finally:
self._seq += 1 self._seq += 1
self._seqlock.release() self._seqlock.release()
def send_action(self, cdict={}, **kwargs): def send_action(self, cdict={}, **kwargs):
""" """
Send a command to the manager Send a command to the manager
If a list is passed to the cdict argument, each item in the list will If a list is passed to the cdict argument, each item in the list will
be sent to asterisk under the same header in the following manner: be sent to asterisk under the same header in the following manner:
@ -232,7 +232,7 @@ class Manager(object):
if not self._connected.isSet(): if not self._connected.isSet():
raise ManagerException("Not connected") raise ManagerException("Not connected")
# fill in our args # fill in our args
cdict.update(kwargs) cdict.update(kwargs)
@ -258,7 +258,7 @@ class Manager(object):
self._sock.flush() self._sock.flush()
except socket.error, (errno, reason): except socket.error, (errno, reason):
raise ManagerSocketException(errno, reason) raise ManagerSocketException(errno, reason)
self._reswaiting.insert(0,1) self._reswaiting.insert(0,1)
response = self._response_queue.get() response = self._response_queue.get()
self._reswaiting.pop(0) self._reswaiting.pop(0)
@ -274,6 +274,7 @@ class Manager(object):
""" """
multiline = False multiline = False
status = False
wait_for_marker = False wait_for_marker = False
eolcount = 0 eolcount = 0
# loop while we are sill running and connected # loop while we are sill running and connected
@ -281,7 +282,7 @@ class Manager(object):
try: try:
lines = [] lines = []
for line in self._sock : for line in self._sock :
# check to see if this is the greeting line # check to see if this is the greeting line
if not self.title and '/' in line and not ':' in line: if not self.title and '/' in line and not ':' in line:
# store the title of the manager we are connecting to: # store the title of the manager we are connecting to:
self.title = line.split('/')[0].strip() self.title = line.split('/')[0].strip()
@ -303,6 +304,11 @@ class Manager(object):
break break
# ignore empty lines at start # ignore empty lines at start
continue continue
# If the user executed the status command, it's a special
# case, so we need to look for a marker.
if 'status will follow' in line:
status = True
wait_for_marker = True
lines.append(line) lines.append(line)
# line not ending in \r\n or without ':' isn't a # line not ending in \r\n or without ':' isn't a
# valid header and starts multiline response # valid header and starts multiline response
@ -310,13 +316,17 @@ class Manager(object):
multiline = True multiline = True
# Response: Follows indicates we should wait for end # Response: Follows indicates we should wait for end
# marker --END COMMAND-- # marker --END COMMAND--
if not multiline and line.startswith('Response') and \ if not (multiline or status) and line.startswith('Response') and \
line.split(':', 1)[1].strip() == 'Follows': line.split(':', 1)[1].strip() == 'Follows':
wait_for_marker = True wait_for_marker = True
# same when seeing end of multiline response # same when seeing end of multiline response
if multiline and line.startswith('--END COMMAND--'): if multiline and line.startswith('--END COMMAND--'):
wait_for_marker = False wait_for_marker = False
multiline = False multiline = False
# same when seeing end of status response
if status and 'StatusComplete' in line:
wait_for_marker = False
status = False
if not self._connected.isSet(): if not self._connected.isSet():
break break
else: else:
@ -333,7 +343,7 @@ class Manager(object):
self._connected.clear() self._connected.clear()
self._message_queue.put(None) self._message_queue.put(None)
def register_event(self, event, function): def register_event(self, event, function):
""" """
Register a callback for the specfied event. Register a callback for the specfied event.
@ -395,7 +405,7 @@ class Manager(object):
finally: finally:
# wait for our data receiving thread to exit # wait for our data receiving thread to exit
t.join() t.join()
def event_dispatch(self): def event_dispatch(self):
"""This thread is responsible for dispatching events""" """This thread is responsible for dispatching events"""
@ -408,14 +418,14 @@ class Manager(object):
# if we got None as an event, we are finished # if we got None as an event, we are finished
if not ev: if not ev:
break break
# dispatch our events # dispatch our events
# first build a list of the functions to execute # first build a list of the functions to execute
callbacks = (self._event_callbacks.get(ev.name, []) callbacks = (self._event_callbacks.get(ev.name, [])
+ self._event_callbacks.get('*', [])) + self._event_callbacks.get('*', []))
# now execute the functions # now execute the functions
for callback in callbacks: for callback in callbacks:
if callback(ev, self): if callback(ev, self):
break break
@ -455,11 +465,11 @@ class Manager(object):
def close(self): def close(self):
"""Shutdown the connection to the manager""" """Shutdown the connection to the manager"""
# if we are still running, logout # if we are still running, logout
if self._running.isSet() and self._connected.isSet(): if self._running.isSet() and self._connected.isSet():
self.logoff() self.logoff()
if self._running.isSet(): if self._running.isSet():
# put None in the message_queue to kill our threads # put None in the message_queue to kill our threads
self._message_queue.put(None) self._message_queue.put(None)
@ -471,22 +481,22 @@ class Manager(object):
if threading.currentThread() != self.event_dispatch_thread: if threading.currentThread() != self.event_dispatch_thread:
# wait for the dispatch thread to exit # wait for the dispatch thread to exit
self.event_dispatch_thread.join() self.event_dispatch_thread.join()
self._running.clear() self._running.clear()
# Manager actions # Manager actions
def login(self, username, secret): def login(self, username, secret):
"""Login to the manager, throws ManagerAuthException when login falis""" """Login to the manager, throws ManagerAuthException when login falis"""
cdict = {'Action':'Login'} cdict = {'Action':'Login'}
cdict['Username'] = username cdict['Username'] = username
cdict['Secret'] = secret cdict['Secret'] = secret
response = self.send_action(cdict) response = self.send_action(cdict)
if response.get_header('Response') == 'Error': if response.get_header('Response') == 'Error':
raise ManagerAuthException(response.get_header('Message')) raise ManagerAuthException(response.get_header('Message'))
return response return response
def ping(self): def ping(self):
@ -500,16 +510,16 @@ class Manager(object):
cdict = {'Action':'Logoff'} cdict = {'Action':'Logoff'}
response = self.send_action(cdict) response = self.send_action(cdict)
return response return response
def hangup(self, channel): def hangup(self, channel):
"""Hangup the specified channel""" """Hangup the specified channel"""
cdict = {'Action':'Hangup'} cdict = {'Action':'Hangup'}
cdict['Channel'] = channel cdict['Channel'] = channel
response = self.send_action(cdict) response = self.send_action(cdict)
return response return response
def status(self, channel = ''): def status(self, channel = ''):
@ -518,12 +528,12 @@ class Manager(object):
cdict = {'Action':'Status'} cdict = {'Action':'Status'}
cdict['Channel'] = channel cdict['Channel'] = channel
response = self.send_action(cdict) response = self.send_action(cdict)
return response return response
def redirect(self, channel, exten, priority='1', extra_channel='', context=''): def redirect(self, channel, exten, priority='1', extra_channel='', context=''):
"""Redirect a channel""" """Redirect a channel"""
cdict = {'Action':'Redirect'} cdict = {'Action':'Redirect'}
cdict['Channel'] = channel cdict['Channel'] = channel
cdict['Exten'] = exten cdict['Exten'] = exten
@ -531,7 +541,7 @@ class Manager(object):
if context: cdict['Context'] = context if context: cdict['Context'] = context
if extra_channel: cdict['ExtraChannel'] = extra_channel if extra_channel: cdict['ExtraChannel'] = extra_channel
response = self.send_action(cdict) response = self.send_action(cdict)
return response return response
def originate(self, channel, exten, context='', priority='', timeout='', caller_id='', async=False, account='', variables={}): def originate(self, channel, exten, context='', priority='', timeout='', caller_id='', async=False, account='', variables={}):
@ -550,18 +560,18 @@ class Manager(object):
# with the latest CVS HEAD this is no longer necessary # with the latest CVS HEAD this is no longer necessary
# if variables: cdict['Variable'] = '|'.join(['='.join((str(key), str(value))) for key, value in variables.items()]) # if variables: cdict['Variable'] = '|'.join(['='.join((str(key), str(value))) for key, value in variables.items()])
if variables: cdict['Variable'] = ['='.join((str(key), str(value))) for key, value in variables.items()] if variables: cdict['Variable'] = ['='.join((str(key), str(value))) for key, value in variables.items()]
response = self.send_action(cdict) response = self.send_action(cdict)
return response return response
def mailbox_status(self, mailbox): def mailbox_status(self, mailbox):
"""Get the status of the specfied mailbox""" """Get the status of the specfied mailbox"""
cdict = {'Action':'MailboxStatus'} cdict = {'Action':'MailboxStatus'}
cdict['Mailbox'] = mailbox cdict['Mailbox'] = mailbox
response = self.send_action(cdict) response = self.send_action(cdict)
return response return response
def command(self, command): def command(self, command):
@ -570,7 +580,7 @@ class Manager(object):
cdict = {'Action':'Command'} cdict = {'Action':'Command'}
cdict['Command'] = command cdict['Command'] = command
response = self.send_action(cdict) response = self.send_action(cdict)
return response return response
def extension_state(self, exten, context): def extension_state(self, exten, context):
@ -580,7 +590,7 @@ class Manager(object):
cdict['Exten'] = exten cdict['Exten'] = exten
cdict['Context'] = context cdict['Context'] = context
response = self.send_action(cdict) response = self.send_action(cdict)
return response return response
def playdtmf (self, channel, digit) : def playdtmf (self, channel, digit) :
@ -594,7 +604,7 @@ class Manager(object):
def absolute_timeout(self, channel, timeout): def absolute_timeout(self, channel, timeout):
"""Set an absolute timeout on a channel""" """Set an absolute timeout on a channel"""
cdict = {'Action':'AbsoluteTimeout'} cdict = {'Action':'AbsoluteTimeout'}
cdict['Channel'] = channel cdict['Channel'] = channel
cdict['Timeout'] = timeout cdict['Timeout'] = timeout

63
debian/changelog vendored
View File

@ -1,63 +0,0 @@
pyst (0.2-1) unstable; urgency=low
* New release.
* Package updated to reflect new python policy.
-- Matthew Nicholson <mnicholson@digium.com> Tue, 19 Sep 2006 19:22:51 -0500
pyst (0.1.0-1) unstable; urgency=low
* New release.
-- Matthew Nicholson <mnicholson@digium.com> Mon, 5 Jun 2006 15:11:24 -0500
pyst (0.0.4rc13-1) unstable; urgency=low
* New release.
-- Matthew Nicholson <mnicholson@digium.com> Tue, 21 Mar 2006 12:25:03 -0600
pyst (0.0.4rc12-1) unstable; urgency=low
* New release.
-- Matthew Nicholson <mnicholson@digium.com> Thu, 14 Apr 2005 17:15:46 -0500
pyst (0.0.4rc11-1) unstable; urgency=low
* New release.
-- Matthew Nicholson <mnicholson@digium.com> Thu, 3 Feb 2005 18:46:32 -0600
pyst (0.0.4rc10-1) unstable; urgency=low
* Bug fixes in config.py and more docs.
* Version number correction.
* Added line number tracking to config.py
-- Matthew Nicholson <mnicholson@digium.com> Tue, 11 Jan 2005 11:30:59 -0600
pyst (0.0.4rc9-1) unstable; urgency=low
* Removed DEBUG flag
-- Matthew Nicholson <mnicholson@digium.com> Mon, 10 Jan 2005 18:40:04 -0600
pyst (0.0.4rc8-1) unstable; urgency=low
* More manager.py updates
-- Matthew Nicholson <mnicholson@digium.com> Thu, 6 Jan 2005 12:19:42 -0600
pyst (0.0.4rc7-1) unstable; urgency=low
* A bunch of fixes to make the manager interface useable
-- Matthew Nicholson <mnicholson@digium.com> Wed, 5 Jan 2005 19:02:38 -0600
pyst (0.0.4rc6-1) unstable; urgency=low
* Initial Release.
-- Matthew Nicholson <mnicholson@digium.com> Mon, 29 Nov 2004 14:06:42 -0600

1
debian/compat vendored
View File

@ -1 +0,0 @@
4

21
debian/control vendored
View File

@ -1,21 +0,0 @@
Source: pyst
Section: python
Priority: optional
Maintainer: Matthew Nicholson <mnicholson@digium.com>
Build-Depends-Indep: debhelper (>= 5.0.37.2), cdbs (>= 0.4.43), python-all-dev (>= 2.3.5-11), python-support (>= 0.3)
Standards-Version: 3.6.1
Package: python-pyst
Architecture: all
Depends: ${python:Depends}
Provides: ${python:Provides}
Replaces: python2.3-pyst (<< 0.2)
Conflicts: python2.3-pyst (<< 0.2)
XB-Python-Version: ${python:Versions}
Suggests: asterisk
Description: Python module for interacting with the Asterisk PBX (dummy)
Pyst is a python module for interacting with the asterisk pbx through
the manager interface and agi interface. This package also includes
debugging tools such as the agitb module, and tools to parse asterisk
config files.

79
debian/copyright vendored
View File

@ -1,79 +0,0 @@
This package was debianized by Matthew Nicholson <mnicholson@digium.com> on
Mon, 29 Nov 2004 14:06:42 -0600.
It was downloaded from http://ftp1.sourceforge.net/pyst/pyst-0.0.4.tar.gz
Copyright (C) 2004 Karl Putland
Upstream Author: Karl Putland <kputland@users.sourceforge.net>
License:
Most of pyst is licensed under the terms of the GNU LGPL.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
The agitb.py modules is licensed under the terms of the Python Software
Foundation License:
PSF LICENSE AGREEMENT FOR PYTHON 2.3
------------------------------------
1. This LICENSE AGREEMENT is between the Python Software Foundation
("PSF"), and the Individual or Organization ("Licensee") accessing and
otherwise using Python 2.3 software in source or binary form and its
associated documentation.
2. Subject to the terms and conditions of this License Agreement, PSF
hereby grants Licensee a nonexclusive, royalty-free, world-wide
license to reproduce, analyze, test, perform and/or display publicly,
prepare derivative works, distribute, and otherwise use Python 2.3
alone or in any derivative version, provided, however, that PSF's
License Agreement and PSF's notice of copyright, i.e., "Copyright (c)
2001, 2002 Python Software Foundation; All Rights Reserved" are
retained in Python 2.3 alone or in any derivative version prepared by
Licensee.
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python 2.3 or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python 2.3.
4. PSF is making Python 2.3 available to Licensee on an "AS IS"
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 2.3 WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
2.3 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 2.3,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. Nothing in this License Agreement shall be deemed to create any
relationship of agency, partnership, or joint venture between PSF and
Licensee. This License Agreement does not grant permission to use PSF
trademarks or trade name in a trademark sense to endorse or promote
products or services of Licensee, or any third party.
8. By copying, installing or otherwise using Python 2.3, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.

10
debian/rules vendored
View File

@ -1,10 +0,0 @@
#!/usr/bin/make -f
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
DEB_PYTHON_SYSTEM=pysupport
include /usr/share/cdbs/1/rules/debhelper.mk
include /usr/share/cdbs/1/class/python-distutils.mk

3
debian/watch vendored
View File

@ -1,3 +0,0 @@
# Site Directory Pattern Version Script
version=2
http://ftp1.sourceforge.net/pyst/pyst-(.*)\.tar\.gz debian uupdate

View File

@ -1,48 +0,0 @@
Summary: An interface to AGI
Name: python-pyst
Version: 0.0.5
Release: 2.centos4.0
Source0: http://prdownloads.sourceforge.net/pyst/pyst-%{version}.tar.gz
License: LGPL
Group: Development/Libraries
BuildRoot: %{_tmppath}/%{name}-buildroot
URL: http://sourceforge.net/projects/pyst
Requires: python
BuildRequires: python-devel
BuildRequires: python Distutils
%description
Pyst consists of a set of interfaces and libraries to allow
programming of Asterisk from python. The library currently
supports AGI, AMI, and the parsing of Asterisk configuration
files. The library also includes debugging facilities for AGI.
%prep
%setup -q -n pyst-%{version}
%build
CFLAGS="$RPM_OPT_FLAGS" python setup.py build
%install
python setup.py install --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES
mkdir -p $RPM_BUILD_ROOT/usr/share/doc/python-pyst
cp debian/copyright $RPM_BUILD_ROOT/usr/share/doc/python-pyst
%clean
rm -rf $RPM_BUILD_ROOT
%files -f INSTALLED_FILES
%defattr(-,root,root)
%doc /usr/share/doc/python-pyst/*
%{_libdir}/python*/site-packages/asterisk/
%changelog
* Tue Mar 21 2006 Matthew Nicholson <mnicholson@digium.com> el4.3
- Bumped version number.
* Thu Feb 23 2006 Antoine Brenner <http://www.gymglish.com> el4.2
- Fixed source0 line
* Tue Feb 9 2006 Antoine Brenner <http://www.gymglish.com> el4.1
- Initial Package

View File

@ -2,10 +2,8 @@
from distutils.core import setup from distutils.core import setup
try : from asterisk import __version__ as version
from asterisk.Version import VERSION
except :
VERSION = None
description = [] description = []
f = open ('README') f = open ('README')
@ -22,14 +20,14 @@ licenses = ( 'Python Software Foundation License'
setup \ setup \
( name = 'pyst' ( name = 'pyst2'
, version = VERSION , version = version
, description = 'A Python Interface to Asterisk' , description = 'A Python Interface to Asterisk'
, long_description = ''.join (description) , long_description = ''.join (description)
, author = 'Karl Putland' , author = 'Karl Putland'
, author_email = 'kputland@users.sourceforge.net' , author_email = 'kputland@users.sourceforge.net'
, maintainer = 'Ralf Schlatterbeck' , maintainer = 'Randall Degges'
, maintainer_email = 'rsc@runtux.com' , maintainer_email = 'rdegges@gmail.com'
, url = 'http://www.sourceforge.net/projects/pyst/' , url = 'http://www.sourceforge.net/projects/pyst/'
, packages = ['asterisk'] , packages = ['asterisk']
, license = ', '.join (licenses) , license = ', '.join (licenses)