323 lines
11 KiB
Python
Executable file
323 lines
11 KiB
Python
Executable file
#!/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)
|