astpicotts/record_message.py

282 lines
8.4 KiB
Python
Raw Normal View History

2019-06-07 13:22:15 +02:00
#!/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
2019-06-07 16:12:46 +02:00
from helpers import playback, enable_simulate_mode, get_var, get_env_var, set_var, hangup, check_answered, beep, record_file
2019-06-07 13:22:15 +02:00
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'
2019-06-07 16:12:46 +02:00
default_outputdir = '/var/lib/asterisk/sounds/records'
2019-06-07 13:22:15 +02:00
default_fileformat = 'wav'
2019-06-07 16:12:46 +02:00
default_nameformat = '{datetime}-{calleridname}'
default_endmessage = u"Au revoir"
2019-06-07 13:22:15 +02:00
#######
# 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,
2019-06-07 16:12:46 +02:00
help="Record file name format composed using channel variables and some specials variables : 'date', 'datetime', 'callerid', 'calleridname' (Default : %s)" % default_outputdir)
2019-06-07 13:22:15 +02:00
parser.add_option('-m', '--max-duration',
action="store", type="int",
dest="maxduration", default=-1,
help="Max duration (in seconds, Default : no limit)")
2019-06-07 16:12:46 +02:00
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)
2019-06-07 13:22:15 +02:00
(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 #
#############
2019-06-07 16:12:46 +02:00
def clean_exit(exit_code):
2019-06-07 13:22:15 +02:00
if 'picotts' in globals():
global picotts
picotts.clean_tmp()
global asterisk_agi
hangup(asterisk_agi)
2019-06-07 16:12:46 +02:00
sys.exit(exit_code)
2019-06-07 13:22:15 +02:00
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)
2019-06-07 16:12:46 +02:00
def play_msg_and_hangup(msg=None, exit_code=1):
2019-06-07 13:22:15 +02:00
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)
2019-06-07 16:12:46 +02:00
clean_exit(exit_code)
2019-06-07 13:22:15 +02:00
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':
2019-06-07 16:12:46 +02:00
value = datetime.datetime.now().strftime('%Y-%m-%d')
2019-06-07 13:22:15 +02:00
elif var == 'datetime':
2019-06-07 16:12:46 +02:00
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)
2019-06-07 13:22:15 +02:00
else:
2019-06-07 16:12:46 +02:00
value = get_var(asterisk_agi, var)
logging.debug('%s = "%s"', var, value)
variables[var] = value
filename = options.nameformat.format(**variables).replace(' ', '-')
2019-06-07 13:22:15 +02:00
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
2019-06-07 16:12:46 +02:00
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)
2019-06-07 13:22:15 +02:00
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)
2019-06-07 16:12:46 +02:00
if options.endmessage:
play_msg(options.endmessage)
clean_exit(0)
2019-06-07 13:22:15 +02:00
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)
2019-06-07 16:12:46 +02:00
play_msg_and_hangup(exit_code=1)
2019-06-07 13:22:15 +02:00