astpicotts/record_message.py
2019-06-07 16:12:49 +02:00

282 lines
8.4 KiB
Python
Executable file

#!/usr/bin/env python2
# coding: utf8
import datetime
import logging
import os
import re
import sys
import tempfile
import traceback
from asterisk import agi
from optparse import OptionParser
from picotts import PicoTTS
from helpers import playback, enable_simulate_mode, get_var, get_env_var, set_var, hangup, check_answered, beep, record_file
default_logfile = '/var/log/asterisk/record_message.agi.log'
default_lang = 'fr-FR'
default_intkey = "any"
default_speed = 1.1
default_cachedir = '/var/cache/asterisk/picotts'
default_outputdir = '/var/lib/asterisk/sounds/records'
default_fileformat = 'wav'
default_nameformat = '{datetime}-{calleridname}'
default_endmessage = u"Au revoir"
#######
# 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('--simulate-record',
action="store_true",
dest="simulate_record",
help="Simulate mode : record file using arecord")
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)")
parser.add_option('-f', '--format',
action="store", type="string",
dest="fileformat", default=default_fileformat,
help="Output file format (Default : %s)" % default_fileformat)
parser.add_option('-O', '--output-dir',
action="store", type="string",
dest="outputdir", default=default_outputdir,
help="Output directory path (Default : %s)" % default_outputdir)
parser.add_option('-n', '--name-format',
action="store", type="string",
dest="nameformat", default=default_nameformat,
help="Record file name format composed using channel variables and some specials variables : 'date', 'datetime', 'callerid', 'calleridname' (Default : %s)" % default_outputdir)
parser.add_option('-m', '--max-duration',
action="store", type="int",
dest="maxduration", default=-1,
help="Max duration (in seconds, Default : no limit)")
parser.add_option('-e', '--end-message',
action="store", type="string",
dest="endmessage", default=default_endmessage,
help="End message that will be read to user at the end (Default : %s)" % default_endmessage)
(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 output dir/format :\n{outputdir}/{nameformat}'.format(
lang=options.lang, intkey=options.intkey,
speed=options.speed, outputdir=options.outputdir,
nameformat=options.nameformat)
)
#############
# Functions #
#############
def clean_exit(exit_code):
if 'picotts' in globals():
global picotts
picotts.clean_tmp()
global asterisk_agi
hangup(asterisk_agi)
sys.exit(exit_code)
def play_msg(msg):
global asterisk_agi, options
filepath = picotts.getAudioFile(msg)
# Playback message
logging.debug('Play file %s' % filepath)
playback(asterisk_agi, filepath, simulate_play=options.simulate_play,
intkey=options.intkey)
def play_file(filepath):
global asterisk_agi, options
logging.debug('Play %s file', filepath)
playback(asterisk_agi, filepath, simulate_play=options.simulate_play,
intkey=options.intkey)
def play_msg_and_hangup(msg=None, exit_code=1):
global asterisk_agi
if not msg:
msg = u"Une erreur empêche ce service de fonctionner correctement. Si le problème persiste, merci de prendre contact avec le support."
play_msg(msg)
clean_exit(exit_code)
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 output directory
if not os.path.isdir(options.outputdir):
logging.error("Ouput directory '%s' doesn't exists !", options.outputdir)
play_msg_and_hangup()
# Compose output file name and path
variables = {}
for var in re.findall('\{([^\}]+)\}', options.nameformat):
if var == 'date':
value = datetime.datetime.now().strftime('%Y-%m-%d')
elif var == 'datetime':
value = datetime.datetime.now().strftime('%Y-%m-%d-%Hh%Mm%Ss')
elif var in ['callerid', 'calleridname']:
value = get_env_var(asterisk_agi, 'agi_%s' % var)
else:
value = get_var(asterisk_agi, var)
logging.debug('%s = "%s"', var, value)
variables[var] = value
filename = options.nameformat.format(**variables).replace(' ', '-')
logging.debug(u"Output file name : '%s'", filename)
filepath = "{0}/{1}".format(options.outputdir, filename)
logging.debug(u"Output file path : '%s'", filename)
# Check destination file does not already exist
if os.path.exists(filepath):
logging.warning("Output file '%s' already exists !", filepath)
play_msg_and_hangup()
# Say hello :)
play_msg(u"Bonjour et bienvenue sur le service d'enregistrement de messages")
# Check call is answered
check_answered(asterisk_agi)
# Intro
play_msg(u"Vous allez pouvoir enregistrer un message après le bip sonore. Une fois votre message enregistré, appuyer sur la touche dièse pour terminer l'enregistrement.")
# Record file
record_file(asterisk_agi, filepath, options.fileformat, escape_digits='#', max_duration=options.maxduration, simulate_record=options.simulate_record)
beep(asterisk_agi)
# Check destination file now exist
full_filepath = "{0}.{1}".format(filepath, options.fileformat.lower())
if not os.path.exists(full_filepath):
logging.warning("Output file '%s' does not exists after the record !", full_filepath)
play_msg_and_hangup(u"Une erreur est survenue durant l'enregistrement de votre message. Si le problème persiste, merci de prendre contact avec le support.")
# Replay message to the caller
play_msg(u"Voilà le message que vous avez enregistré :")
beep(asterisk_agi)
play_file(filepath)
beep(asterisk_agi)
if options.endmessage:
play_msg(options.endmessage)
clean_exit(0)
except agi.AGIAppError:
logging.info('An AGI error stop script', exc_info=True)
play_msg_and_hangup()
except Exception:
logging.error('Unexcepted error occured', exc_info=True)
play_msg_and_hangup(exit_code=1)