Add record_message.py script

This commit is contained in:
Benjamin Renard 2019-06-07 13:22:15 +02:00
parent b8d814aa02
commit 9939dbd41a
2 changed files with 295 additions and 0 deletions

View file

@ -96,6 +96,12 @@ def playback(asterisk_agi, filepath, simulate_play=False, read=False, read_timeo
logging.debug('User enter "%s"' % result) logging.debug('User enter "%s"' % result)
return result return result
def beep(asterisk_agi):
if check_simulate_mode():
logging.debug('Beep')
else:
playback(asterisk_agi, 'beep')
def check_answered(asterisk_agi): def check_answered(asterisk_agi):
if check_simulate_mode(): if check_simulate_mode():
print('Simulate mode : Channel answered') print('Simulate mode : Channel answered')
@ -111,3 +117,19 @@ def hangup(asterisk_agi):
print('Simulate mode : Hangup') print('Simulate mode : Hangup')
else: else:
asterisk_agi.hangup() asterisk_agi.hangup()
def record_file(asterisk_agi, filepath, fileformat='wav', escape_digits='#', max_duration=-1, simulate_record=False, simulate_record_duration=5):
if check_simulate_mode():
if not simulate_record:
raw_input('Simulate mode : record %s file "%s" until you press on touch [enter]' % (fileformat, filepath))
else:
assert fileformat == 'wav', "Only wav file format in support in simulate record mode"
logging.debug('Simulate mode : Record file %s', filepath)
raw_input('The record will start after you press [entre] key. In this mode, the record will stop after %d seconds.' % simulate_record_duration)
try:
arecord_path = get_path('arecord')
subprocess.check_output([arecord_path, '-vv', '-fdat', '-d %d' % simulate_record_duration, filepath])
except Exception, e:
logging.warning('Fail to record %s file', filepath, exc_info=True)
else:
return asterisk_agi.record_file(filepath, format=fileformat, escape_digits=escape_digits, timeout=max_duration)

273
record_message.py Executable file
View file

@ -0,0 +1,273 @@
#!/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, 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}-{CALLERID(num)}.{fileformat}'
#######
# 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 specials 'date' and 'datetime' and 'fileformat' variables (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)")
(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_tmp():
if 'picotts' in globals():
global picotts
picotts.clean_tmp()
global asterisk_agi
hangup(asterisk_agi)
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):
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_tmp()
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 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':
variables['date'] = datetime.datetime.now().strftime('%Y%m%d')
elif var == 'datetime':
variables['datetime'] = datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
elif var == 'fileformat':
variables['fileformat'] = options.fileformat
else:
variables[var] = get_var(asterisk_agi, var)
filename = options.nameformat.format(**variables)
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.")
beep(asterisk_agi)
# 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
if not os.path.exists(filepath):
logging.warning("Output file '%s' does not exists after the record !", 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)
play_msg_and_hangup(u"Au revoir")
sys.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()
sys.exit(1)