First commit

This commit is contained in:
Benjamin Renard 2018-01-22 01:39:24 +01:00
commit 1c20764e66
5 changed files with 845 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.pyc
*~

323
conference.py Executable file
View File

@ -0,0 +1,323 @@
#!/usr/bin/env python2
# coding: utf8
import sys
import os
import tempfile
import re
from asterisk import agi
import logging
from optparse import OptionParser
from picotts import PicoTTS
from helpers import playback, enable_simulate_mode, get_var, set_var, hangup, check_answered
import traceback
default_logfile = '/var/log/asterisk/conference.agi.log'
default_lang = 'fr-FR'
default_intkey = "any"
default_result_varname = "CONFID"
default_speed = 1.2
default_cachedir = '/var/cache/asterisk/picotts'
default_read_timeout = 3000
default_read_maxdigits = 20
default_read_maxtry = 3
#######
# RUN #
#######
# Options parser
parser = OptionParser()
parser.add_option('-d', '--debug',
action="store_true",
dest="debug",
help="Enable debug mode")
parser.add_option('-v', '--verbose',
action="store_true",
dest="verbose",
help="Enable verbose mode")
parser.add_option('--simulate',
action="store_true",
dest="simulate",
help="Simulate AGI mode")
parser.add_option('--simulate-play',
action="store_true",
dest="simulate_play",
help="Simulate mode : play file using mplayer")
parser.add_option('-t', '--read-timeout',
action="store", type="int",
dest="read_timeout", default=default_read_timeout,
help="Read timeout in ms (Default : %i)" % default_read_timeout)
parser.add_option('-m', '--read-max-digits',
action="store", type="int",
dest="read_maxdigits", default=default_read_maxdigits,
help="Read max digits (Default : %i)" % default_read_maxdigits)
parser.add_option('-T', '--read-max-try',
action="store", type="int",
dest="read_maxtry", default=default_read_maxtry,
help="Read max try (Default : %i)" % default_read_maxtry)
parser.add_option('--can-create',
action="store_true",
dest="can_create",
help="User can create a conference")
parser.add_option('-n', '--name',
action="store", type="string",
dest="varname", default=default_result_varname,
help="User input result variable name (Default : %s)" % default_result_varname)
parser.add_option('-L', '--log-file',
action="store", type="string",
dest="logfile", default=default_logfile,
help="pico2wave path (Default : %s)" % default_logfile)
parser.add_option('-l', '--lang',
action="store", type="string",
dest="lang", default=default_lang,
help="Language (Default : %s)" % default_lang)
parser.add_option('-i', '--intkey',
action="store", type="string",
dest="intkey", default=default_intkey,
help="Interrupt key(s) (Default : Any)")
parser.add_option('-s', '--speed',
action="store", type="float",
dest="speed", default=default_speed,
help="Speed factor (Default : %i)" % default_speed)
parser.add_option('-S', '--sample-rate',
action="store", type="int",
dest="samplerate",
help="Sample rate (Default : auto-detect)")
parser.add_option('-c', '--cache',
action="store_true",
dest="cache",
help="Enable cache")
parser.add_option('-C', '--cache-dir',
action="store", type="string",
dest="cachedir", default=default_cachedir,
help="Cache directory path (Default : %s)" % default_cachedir)
parser.add_option('--sox-path',
action="store", type="string",
dest="sox_path",
help="sox path (Default : auto-detec in PATH)")
parser.add_option('--pico2wave-path',
action="store", type="string",
dest="pico2wave_path",
help="pico2wave path (Default : auto-detec in PATH)")
(options, args) = parser.parse_args()
# Enable logs
logformat = '%(levelname)s - %(message)s'
if options.simulate:
logging.basicConfig(format=logformat, level=logging.DEBUG)
enable_simulate_mode()
else:
if options.debug:
loglevel = logging.DEBUG
elif options.verbose:
loglevel = logging.INFO
else:
loglevel = logging.WARNING
logging.basicConfig(filename=options.logfile, level=loglevel,
format=logformat)
# Valid intkey parameter
if options.intkey != "any" and not re.match('^[0-9#*]*$', options.intkey):
logging.warning('Invalid interrupt key(s) provided ("%s"), use any.' % options.intkey)
options.intkey = "any"
if options.speed <= 0:
logging.warning('Invalid speed provided, use default')
options.speed=default_speed
logging.debug('Call parameters (lang = {lang}, intkey = {intkey}, speed = {speed} and varname :\n{varname}'.format(
lang=options.lang, intkey=options.intkey,
speed=options.speed, varname=options.varname)
)
#############
# Functions #
#############
def check_confid(confid):
if re.match('^[0-9]{1,4}$', confid):
return True
return False
def set_return(result):
set_var(asterisk_agi, options.varname, result)
def clean_tmp():
if 'picotts' in globals():
global picotts
picotts.clean_tmp()
def play_msg(msg, read=False, max_digits=False):
global asterisk_agi, options
filepath = picotts.getAudioFile(msg)
if not max_digits:
max_digits = options.read_maxdigits
# Playback message
logging.debug('Play file %s' % filepath)
result = playback(asterisk_agi, filepath, simulate_play=options.simulate_play,
intkey=options.intkey, read=read,
read_timeout=options.read_timeout,
read_maxdigits=max_digits)
return result
def play_msg_and_hangup(msg):
global asterisk_agi
play_msg(msg)
clean_tmp()
set_return(None)
hangup(asterisk_agi)
sys.exit(0)
try:
picotts_args = {}
# Start Asterisk AGI client
if not options.simulate:
asterisk_agi = agi.AGI()
picotts_args['asterisk_agi'] = asterisk_agi
else:
asterisk_agi = None
if options.cache:
picotts_args['cachedir']=options.cachedir
# Start PicoTTS engine
picotts = PicoTTS(lang = options.lang, speed=options.speed, **picotts_args)
# Check call is answered
check_answered(asterisk_agi)
authorized = False
confid = None
start = True
while not authorized:
nb_confid_try =0
while not confid:
if nb_confid_try >= options.read_maxtry:
play_msg_and_hangup(u"Vous avez atteint le nombre maximum d'essai. Peut-être avez un problème avec votre clavier de téléphone ? Nous en sommes désolé. Au revoir.")
msg = u"Merci de saisir votre numéro de conférence en terminant par la touche dièse."
if start:
start = False
msg = u"Bonjour et bienvenue sur le service de conférence téléphonique. " + msg
confid = play_msg(msg, read=True)
if not check_confid(confid):
confid = None
play_msg(u"Ce numéro de conférence est invalide. Il doit comporté entre 1 et 4 chiffres.")
nb_confid_try += 1
logging.info('User choice conference %s' % confid)
# Check number of current calls in this conference
nb_calls = int(get_var(asterisk_agi, '${GROUP_COUNT(%s@conference)}' % confid))
logging.debug('Nb current calls in conference %s : %s' % (confid, nb_calls))
if nb_calls > 0:
logging.info('Conference %s already exist' % confid)
# Check PIN
pin = get_var(asterisk_agi, '${DB(conf/%s/pin)}' % confid)
logging.info('Current PIN of conference %s : "%s"' % (confid, pin))
if pin:
nb_pin_try = 0
while not authorized:
if nb_pin_try >= options.read_maxtry:
play_msg_and_hangup(u"Vous avez atteint le nombre maximum d'essai. Merci de vérifier le mot de passe d'accès à la conférence auprès de l'organisateur avant de rééssayer. Au revoir.")
if nb_pin_try == 0:
check_pin = play_msg(u"Cette conférence est protégé par un mot de passe. Merci de le saisir en terminant par la touche dièse.", read=True)
else:
check_pin = play_msg(u"Merci de saisir le mot de passe d'accès de la conférence en terminant par la touche dièse.", read=True)
if check_pin != pin:
play_msg(u"Le mot de passe saisi est invalide.")
nb_pin_try += 1
else:
authorized = True
else:
authorized = True
set_var(asterisk_agi, 'CONFBRIDGE(user,admin)', 'no')
set_var(asterisk_agi, 'CONF_CREATOR', 'no')
elif options.can_create:
logging.info('Conference %s does not exist.' % confid)
choice = play_msg(u"Cette conférence n'existe pas. Pour la créer, appuyer sur la touche 1, sinon, merci de patienter ou d'appuyer sur une autre touche pour saisir un autre numéro de conférence.", read=True, max_digits=1)
if choice != "1":
confid = None
continue
pin = None
nb_pin_try = 0
while not pin:
if nb_pin_try >= options.read_maxtry:
play_msg_and_hangup(u"Vous avez atteint le nombre maximum d'essai. Vous avez peut-être un problème avec le clavier de votre téléphone. Nous en sommes désolé. Au revoir")
if nb_pin_try == 0:
pin = play_msg(u"Si vous souhaitez protéger votre conférence par un mot de passe, merci de le saisir en terminant par la touche dièse. Sinon, merci de patienter ou d'appuyer sur le touche dièse.", read=True)
else:
pin = play_msg(u"Merci de saisir un nouveau mot de passe en terminant par la touche dièse. Si vous ne souhaitez finalement pas protéger votre conférence, merci de patienter ou d'appuyer sur la touche dièse.", read=True)
if pin:
verif_pin = play_msg(u"Merci de confirmer le mot de passe de votre conférence en terminant par la touche dièse.", read=True)
if verif_pin != pin:
play_msg(u"Les mots de passe saisies ne correspondent pas.")
nb_pin_try += 1
pin = None
continue
logging.info('Conference %s created with PIN "%s"' % (confid, pin))
set_var(asterisk_agi, 'DB(conf/%s/pin)' % confid, pin)
else:
logging.info('Conference %s created without PIN' % confid)
set_var(asterisk_agi, 'DB(conf/%s/pin)' % confid, "")
break
play_msg(u"Votre conférence a été créé. Vous pouvez désormais communiquer son numéro à vos invités, à savoir, le numéro %s." % confid)
if pin:
play_msg(u"N'oubliez pas de leur communiquer également le mot de passe d'accès.")
set_var(asterisk_agi, 'CONFBRIDGE(user,admin)', 'yes')
set_var(asterisk_agi, 'CONF_CREATOR', 'yes')
authorized = True
else:
logging.info('Conference %s does not exist and user can not create it.' % confid)
choice = play_msg(u"La conférence numéro %s n'existe pas ou n'a pas encore commencé. Si vous pensez avoir fait une erreur, appuyer sur la touche 1 pour saisir un autre numéro de conférence. Sinon, merci de raccrocher, de vérifier le numéro de votre conférence et de rééssayer ultèrieurement." % confid, read=True, max_digits=1)
if choice == "1":
confid = None
continue
play_msg_and_hangup(u"Au revoir.")
play_msg(u"Vous allez maintenant entrer en conférence. Vous pourrez accéder au menu en appuyant sur la touche étoile.")
set_var(asterisk_agi, "GROUP(conference)", confid)
set_return(confid)
clean_tmp()
sys.exit(0)
except agi.AGIAppError as e:
logging.info('An AGI error stop script : %s' % e)
clean_tmp()
sys.exit(0)
except Exception as e:
logging.error(traceback.format_exc())
set_return(None)
clean_tmp()
sys.exit(1)

View File

@ -0,0 +1,25 @@
[conference]
exten => s,1,GotoIf($[${CONF_EXT_CALL} == "1"]?extcall)
exten => s,n,AGI(/home/bn8/dev/astpicotts/conference.py,--cache,-d,--can-create)
exten => s,n,Goto(enterconf)
exten => s,n(extcall),AGI(/home/bn8/dev/astpicotts/conference.py,--cache)
exten => s,n,Goto(enterconf)
exten => s,n(enterconf),Verbose(Conf ID : ${CONFID})
exten => s,n,ExecIf($[${CONFID} == "None"]?Hangup())
exten => s,n,Goto(${CONFID},1)
exten => _.,1,Verbose(User is creator : ${CONF_CREATOR})
exten => _.,n,Set(CONFBRIDGE(user,marked)=no)
exten => _.,n,Set(CONFBRIDGE(user,announce_join_leave)=yes)
exten => _.,n,Set(CONFBRIDGE(user,music_on_hold_when_empty)=yes)
exten => _.,n,Set(CONFBRIDGE(user,music_on_hold_class)=default)
exten => _.,n,Set(CONFBRIDGE(user,end_marked)=yes)
exten => _.,n,ConfBridge(${CONFID})
; Macro(ext2conf)
[macro-ext2conf]
exten => s,1,Set(CONF_EXT_CALL=1)
exten => s,n,Macro(conference)

113
helpers.py Normal file
View File

@ -0,0 +1,113 @@
#!/usr/bin/env python2
import re
import subprocess
import logging
import time
def get_path(soft):
try:
path = subprocess.check_output(['which', soft]).strip()
return path
except subprocess.CalledProcessError, e:
logging.fatal('%s not found' % soft)
return False
def get_sox_ver(sox_path=None):
global sox_ver
if 'sox_ver' not in globals() or not sox_ver:
if not sox_path:
sox_path = get_path('sox')
try:
result = subprocess.check_output([sox_path, '--version'])
logging.debug('Sox accept --version parameter : consider as version 14')
sox_ver = 14
except subprocess.CalledProcessError:
logging.debug('Sox not accept --version parameter : consider as version 12')
sox_ver = 12
return sox_ver
def enable_simulate_mode():
global SIMULATE_MODE
SIMULATE_MODE = True
def check_simulate_mode():
return 'SIMULATE_MODE' in globals() and SIMULATE_MODE
def get_var(asterisk_agi, varname):
if check_simulate_mode():
result = raw_input('Simulate mode : please enter "%s" variable value =>> ' % varname)
else:
result = asterisk_agi.get_full_variable(varname)
return result
def set_var(asterisk_agi, varname, value):
if check_simulate_mode():
print('Simulate mode : set variable %s to "%s"' % (varname, value))
else:
logging.info('Set variable %s to "%s"' % (varname, value))
asterisk_agi.set_variable(varname, value)
def detect_format(asterisk_agi):
if check_simulate_mode():
return ("sln", 8000)
nativeformat = asterisk_agi.get_full_variable('${CHANNEL(audionativeformat)}')
logging.debug('Native audio format : %s' % nativeformat)
if re.match('(silk|sln)12', nativeformat):
return ("sln12", 12000)
elif re.match('(speex|slin|silk)16|g722|siren7', nativeformat):
return ("sln16", 16000)
elif re.match('(speex|slin|celt)32|siren14', nativeformat):
return ("sln32", 32000)
elif re.match('(celt|slin)44', nativeformat):
return ("sln44", 44100)
elif re.match('(celt|slin)48', nativeformat):
return ("sln48", 48000)
else:
return ("sln", 8000)
def remove_ext(filepath):
return re.sub('\.[a-zA-Z0-9]+$','',filepath)
any_intkeys = "0123456789#*"
def playback(asterisk_agi, filepath, simulate_play=False, read=False, read_timeout=3000, read_maxdigits=20, intkey=None):
if check_simulate_mode():
logging.debug('Simulate mode : Play file %s' % filepath)
try:
mplayer_path = get_path('mplayer')
subprocess.check_output([mplayer_path, filepath])
except Exception, e:
logging.warning('Fail to play %s file : %s' % (filepath, e))
if read:
result = raw_input('=>> ')
else:
# Simulate empty result
result = ''
else:
play_filepath = remove_ext(filepath)
logging.debug('Asterisk play file path : %s' % play_filepath)
if read:
result = asterisk_agi.get_data(play_filepath, timeout=read_timeout, max_digits=read_maxdigits)
else:
if intkey == "any":
global any_intkeys
intkey = any_intkeys
result = asterisk_agi.stream_file(play_filepath, escape_digits=intkey)
logging.debug('User enter "%s"' % result)
return result
def check_answered(asterisk_agi):
if check_simulate_mode():
print('Simulate mode : Channel answered')
else:
status = asterisk_agi.channel_status()
if status == 4:
logging.debug('Call is riging. Answer it and wait one second.')
asterisk_agi.answer()
time.sleep(1)
def hangup(asterisk_agi):
if check_simulate_mode():
print('Simulate mode : Hangup')
else:
asterisk_agi.hangup()

382
picotts.py Executable file
View File

@ -0,0 +1,382 @@
#!/usr/bin/env python2
import sys
import os
import shutil
import re
from asterisk import agi
import subprocess
import logging
import hashlib
import uuid
from helpers import get_path, get_sox_ver, detect_format, check_simulate_mode
class PicoTTS:
cache = False
cachedir = None
cache_prefix = 'picotts_'
tmp_filepaths = []
def __init__(self, lang=u'fr-FR', tmpdir='/tmp', pico2wave_path=None, sox_path=None, samplerate=None, speed=1, cachedir=None, asterisk_agi=None):
if not pico2wave_path:
pico2wave_path=get_path('pico2wave')
if not pico2wave_path:
raise Exception('pico2wave not found')
self.pico2wave_path = pico2wave_path
if not sox_path:
sox_path=get_path('sox')
if not sox_path:
raise Exception('sox not found')
self.sox_path = sox_path
self.asterisk_agi = asterisk_agi
if not samplerate:
if not asterisk_agi and not check_simulate_mode():
logging.warning('You must provide samplerate or asterisk_agi parameter to correctly handle sample rate.')
(self.samplerate, self.fext) = (8000, 'sln')
else:
(self.fext, self.samplerate) = detect_format(asterisk_agi)
else:
self.samplerate = samplerate
self.fext = self.samplerate2fext(samplerate)
self.speed = speed
if cachedir:
self.cachedir = cachedir
self.check_or_create_cachedir()
self.tmpdir = tmpdir
self.lang = lang
def samplerate2fext(self, samplerate):
# Check/detect sample-rate and final output format
if samplerate == 8000:
return "sln"
elif samplerate == 12000:
return "sln12"
elif samplerate == 16000:
return "sln16"
elif samplerate == 32000:
return "sln32"
elif samplerate == 44100:
return "sln44"
elif samplerate == 48000:
return "sln48"
else:
raise Exception('Invalid sample rate value')
def check_or_create_cachedir(self):
self.cache = True
if not os.path.exists(self.cachedir):
try:
logging.info('Create cache directory %s' % self.cachedir)
os.mkdir(self.cachedir)
except Exception, e:
logging.warning("Fail to create cache directory (%s) : %s" % (self.cachedir, e))
logging.info('Disable cache')
self.cache = False
else:
if not os.path.isdir(self.cachedir):
logging.warning("Cache directory %s is not a directory : disable cache" % self.cachedir)
self.cache = False
elif not os.access(self.cachedir, os.W_OK):
logging.warning("Cache directory %s is not writable : disable cache" % self.cachedir)
self.cache = False
else:
logging.debug("Cache directory %s already exists" % self.cachedir)
if self.cache:
logging.debug('Cache is enabled')
else:
logging.debug('Cache is disabled')
return self.cache
def _getCachePath(self, text, lang=None):
md5sum=hashlib.md5()
if isinstance(text, str):
text = text.decode('utf-8', 'ignore')
cache_key = u'%s--%s--%s' % (text, (lang or self.lang), self.speed)
logging.debug('Cache key : "%s"' % cache_key)
md5sum.update(cache_key.encode('utf-8'))
cache_md5key = md5sum.hexdigest()
logging.debug('Cache MD5 key : "%s"' % cache_md5key)
cache_filename=self.cache_prefix + cache_md5key
logging.debug('Cache filename : %s' % cache_filename)
cache_filepath = os.path.join(self.cachedir, cache_filename)
logging.debug('Cache filepath : %s' % cache_filepath)
return cache_filepath
def _getAudioFileFromCache(self, text, lang=None):
if not self.cache:
return False
cache_filepath = self._getCachePath(text, lang=lang) + "." + self.fext
if os.path.isfile(cache_filepath):
logging.debug('File already exists in cache. Use it')
return cache_filepath
logging.debug('File does not exists in cache.')
return False
def getAudioFile(self, text, lang=None):
if self.cache:
cache_filepath = self._getAudioFileFromCache(text, lang=lang)
if cache_filepath:
return cache_filepath
# Create temp files
logging.debug('Temporary directory : %s' % self.tmpdir)
tmpfile = os.path.join(self.tmpdir, 'picotts_%s' % str(uuid.uuid4()))
tmpwavfile = tmpfile + ".wav"
logging.debug('Temporary wav file : %s' % tmpwavfile)
tmpoutfile = tmpfile + "." + self.fext
logging.debug('Temporary out file : %s' % tmpoutfile)
# Convert text to autio wav file using pico2wave
cmd = [ self.pico2wave_path, '-l', (lang or self.lang), '-w', tmpwavfile, text ]
try:
logging.debug('Run command : %s' % cmd)
result = subprocess.check_output(cmd)
logging.debug('Command return : %s' % result)
except subprocess.CalledProcessError, e:
raise Exception('Fail to convert text to audio file using pico2wave : %s' % e)
# Convert wav file to final output format using sox
cmd = [ self.sox_path, tmpwavfile, "-q", "-r", str(self.samplerate), "-t", "raw", tmpoutfile ]
# Handle speed change
if self.speed != 1:
if get_sox_ver(sox_path=self.sox_path) >= 14:
cmd += ['tempo', '-s', str(self.speed)]
else:
cmd += ['stretch', str(1/self.speed), "80"]
try:
logging.debug('Run command : %s' % cmd)
result = subprocess.check_output(cmd)
logging.debug('Command return : %s' % result)
except subprocess.CalledProcessError, e:
logging.fatal('Fail to convert text to audio file using pico2wave : %s' % e)
sys.exit(1)
try:
logging.debug('Remove tmp wav file')
os.remove(tmpwavfile)
except Exception, e:
logging.warning('Fail to remove temporary WAV audio file')
# Move audio file in cache
if self.cache:
cache_filepath = self._getCachePath(text, lang=lang) + "." + self.fext
try:
logging.debug('Move audio file in cache directory')
shutil.move(tmpoutfile, cache_filepath)
except Exception,e:
logging.warning('Fail to move audio file in cache directory : %s' % e)
return cache_filepath
else:
self.tmp_filepaths.append(tmpoutfile)
logging.debug('Cache disabled. Directly play tmp file.')
return tmpoutfile
def clean_tmp(self):
try:
logging.debug('Clean temporaries files and directory')
for filepath in self.tmp_filepaths:
if os.path.exists(filepath):
os.remove(filepath)
except Exception as e:
logging.warning('Fail to remove temporaries files and directory : %s' % e)
if __name__ == "__main__":
from optparse import OptionParser
from helpers import playback, enable_simulate_mode
import traceback
default_logfile = '/var/log/asterisk/picotts.log'
default_lang = 'fr-FR'
default_intkey = ""
default_result_varname = "USER_INPUT"
default_speed = 1
default_cachedir = '/tmp/'
default_read_timeout = 3000
default_read_maxdigits = 20
any_intkeys = "0123456789#*"
#######
# RUN #
#######
# Options parser
parser = OptionParser()
parser.add_option('-d', '--debug',
action="store_true",
dest="debug",
help="Enable debug mode")
parser.add_option('-v', '--verbose',
action="store_true",
dest="verbose",
help="Enable verbose mode")
parser.add_option('--simulate',
action="store_true",
dest="simulate",
help="Simulate AGI mode")
parser.add_option('--simulate-play',
action="store_true",
dest="simulate_play",
help="Simulate mode : play file using mplayer")
parser.add_option('-r', '--read',
action="store_true",
dest="read",
help="Enable read mode")
parser.add_option('-t', '--read-timeout',
action="store", type="int",
dest="read_timeout", default=default_read_timeout,
help="Read timeout in ms (Default : %i)" % default_read_timeout)
parser.add_option('-m', '--read-max-digits',
action="store", type="int",
dest="read_maxdigits", default=default_read_maxdigits,
help="Read max digits (Default : %i)" % default_read_maxdigits)
parser.add_option('-n', '--name',
action="store", type="string",
dest="varname", default=default_result_varname,
help="User input result variable name (Default : %s)" % default_result_varname)
parser.add_option('-L', '--log-file',
action="store", type="string",
dest="logfile", default=default_logfile,
help="pico2wave path (Default : %s)" % default_logfile)
parser.add_option('-l', '--lang',
action="store", type="string",
dest="lang", default=default_lang,
help="Language (Default : %s)" % default_lang)
parser.add_option('-i', '--intkey',
action="store", type="string",
dest="intkey", default=default_intkey,
help="Interrupt key(s) (Default : No)")
parser.add_option('-s', '--speed',
action="store", type="float",
dest="speed", default=default_speed,
help="Speed factor (Default : %i)" % default_speed)
parser.add_option('-S', '--sample-rate',
action="store", type="int",
dest="samplerate",
help="Sample rate (Default : auto-detect)")
parser.add_option('-c', '--cache',
action="store_true",
dest="cache",
help="Enable cache")
parser.add_option('-C', '--cache-dir',
action="store", type="string",
dest="cachedir", default=default_cachedir,
help="Cache directory path (Default : %s)" % default_cachedir)
parser.add_option('--sox-path',
action="store", type="string",
dest="sox_path",
help="sox path (Default : auto-detec in PATH)")
parser.add_option('--pico2wave-path',
action="store", type="string",
dest="pico2wave_path",
help="pico2wave path (Default : auto-detec in PATH)")
(options, args) = parser.parse_args()
try:
# Enable logs
logformat = '%(levelname)s - %(message)s'
if options.simulate:
logging.basicConfig(format=logformat, level=logging.DEBUG)
enable_simulate_mode()
else:
if options.debug:
loglevel = logging.DEBUG
elif options.verbose:
loglevel = logging.INFO
else:
loglevel = logging.WARNING
logging.basicConfig(filename=options.logfile, level=loglevel,
format=logformat)
text=" ".join(args).strip()
if len(text) == 0:
usage(msg="You provide text to say as first parameter.")
# Valid intkey parameter
if options.intkey == "any":
options.intkey = any_intkeys
elif not re.match('^[0-9#*]*$', options.intkey):
logging.warning('Invalid interrupt key(s) provided ("%s"), use any.' % options.intkey)
options.intkey = any_intkeys
if options.speed <= 0:
logging.warning('Invalid speed provided, use default')
options.speed=default_speed
logging.debug('Call parameters (lang = {lang}, intkey = {intkey}, speed = {speed} and text :\n{text}'.format(
lang=options.lang, intkey=options.intkey,
speed=options.speed, text=text)
)
picotts_args = {}
# Start Asterisk AGI client
if not options.simulate:
a = agi.AGI()
picotts_args['asterisk_agi'] = a
else:
a = None
if options.cache:
picotts_args['cachedir']=options.cachedir
picotts = PicoTTS(lang = options.lang, speed=options.speed, **picotts_args)
filepath = picotts.getAudioFile(text)
# Playback message
result = playback(a, filepath, simulate_play=options.simulate_play,
intkey=options.intkey, read=options.read,
read_timeout=options.read_timeout,
read_maxdigits=options.read_maxdigits)
logging.debug('User enter : "%s"' % result)
if options.simulate:
logging.debug('Simulate mode : set variable %s to "%s"' % (options.varname, result))
else:
logging.info('Set variable %s to "%s"' % (options.varname, result))
a.set_variable(options.varname, result)
picotts.clean_tmp()
except agi.AGIAppError as e:
logging.info('An AGI error stop script : %s' % e)
if 'picotts' in globals():
picotts.clean_tmp()
sys.exit(0)
except Exception as e:
logging.error(traceback.format_exc())
if 'picotts' in globals():
picotts.clean_tmp()
sys.exit(1)
sys.exit(0)