Add record_message.py script
This commit is contained in:
parent
b8d814aa02
commit
9939dbd41a
2 changed files with 295 additions and 0 deletions
22
helpers.py
22
helpers.py
|
@ -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
273
record_message.py
Executable 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)
|
||||||
|
|
Loading…
Reference in a new issue