astpicotts/conference.py
2018-07-27 10:30:50 +02:00

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.1
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)