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