177 lines
5.6 KiB
Python
177 lines
5.6 KiB
Python
"""capisuite.voice
|
|
|
|
Module for voice interfacing and interactions of capisuite.
|
|
|
|
"""
|
|
|
|
__author__ = "Hartmut Goebel <h.goebel@crazy-compilers.com>"
|
|
__copyright__ = "Copyright (c) 2004 by Hartmut Goebel"
|
|
__version__ = "$Revision: 0.0 $"
|
|
__credits__ = "This file is part of www.capisuite.de; thanks to Gernot Hillier"
|
|
|
|
import os, re, errno
|
|
|
|
# capisuite stuff
|
|
import fileutils
|
|
from capisuite.config import JobDescription, createDescriptionFor
|
|
from capisuite.exceptions import InvalidJob, JobLockedError
|
|
#from capisuite.consts import *
|
|
|
|
_job_pattern = re.compile("voice-([0-9]+)\.txt")
|
|
_la_pattern = re.compile("voice-([0-9]+)\.la")
|
|
|
|
|
|
def _userQ(config, user, Q):
|
|
userdir= config.get('GLOBAL', "voice_user_dir")
|
|
# todo: enable this, iff using config.getUser()
|
|
#if not userdir:
|
|
# raise NoOptionError('', 'fax_user_dir')
|
|
return os.path.abspath(os.path.join(userdir, user, Q))
|
|
|
|
|
|
def getAudio(config, user, filename):
|
|
"""
|
|
Search for an audio file first in user_dir, than in audio_dir
|
|
|
|
'config' is the ConfigParser object containing the configuration,
|
|
'user' the name of the user, 'filename' the filename of the wave
|
|
file.
|
|
|
|
Returns the found file with full path
|
|
"""
|
|
|
|
userdir = config.get('GLOBAL', "voice_user_dir")
|
|
userdir = os.path.join(userdir, user)
|
|
if config.has_option('GLOBAL', "user_audio_files") and \
|
|
config.getboolean('GLOBAL', "user_audio_files") and \
|
|
os.access(os.path.join(userdir, filename),os.R_OK):
|
|
return os.path.join(userdir, filename)
|
|
else:
|
|
systemdir = config.get('GLOBAL', "audio_dir")
|
|
return os.path.join(systemdir, filename)
|
|
|
|
|
|
def getNumberFiles(number, gender="-"):
|
|
number = str(number)
|
|
if number == "-" or number == "??":
|
|
# "??" is needed for backward compatibility to versions <= 0.4.1a
|
|
yield 'unbekannt'
|
|
elif gender != "-" and number in ("1", "01"):
|
|
if gender in ("n", "m"):
|
|
yield 'ein'
|
|
else:
|
|
yield 'eine'
|
|
elif len(number) == 2 and number[0] != "0":
|
|
digit10, digit01 = number
|
|
if digit10 == "1" or digit01 == "0":
|
|
# for 10, 11...19, 20, 30, ... we have seperate voice files
|
|
yield number
|
|
else:
|
|
if digit01 == "1":
|
|
yield 'ein'
|
|
else:
|
|
yield digit01
|
|
yield 'und'
|
|
yield '%s0' % digit10
|
|
else:
|
|
for digit in list(number):
|
|
yield digit
|
|
|
|
|
|
# @brief say a german number
|
|
#
|
|
# All numbers from 0 to 99 are said correctly, while all larger ones are
|
|
# split into numbers and only the numbers are said one after another.
|
|
# An input of "-" produces the word "unbekannt" (unknown)
|
|
#
|
|
# @param call reference to the call
|
|
# @param number the number to say
|
|
# @param curr_user the current user named
|
|
# @param config the ConfigParser instance holding the configuration info
|
|
# @param gender if the number is used in connection with a singular noun ("f" --> "eine Nachricht")
|
|
def sayNumber(config, user, call, number, gender="-"):
|
|
for f in getNumberFiles(number, gender=gender):
|
|
say(config, user, call, "%s.la" % f)
|
|
|
|
def say(config, user, call, *files):
|
|
for f in files:
|
|
call.audio_send(getAudio(config, user, f),1)
|
|
|
|
|
|
###---- Queue handling ---###
|
|
|
|
def getInquiryCounter(config, user):
|
|
return fileutils.readCounter(-1,
|
|
_userQ(config, user, "received"),
|
|
"last_inquiry")
|
|
|
|
def setInquiryCounter(config, user, count):
|
|
return fileutils.writeCounter(count,
|
|
_userQ(config, user, "received"),
|
|
"last_inquiry")
|
|
|
|
|
|
def getQueueFiles(config, user):
|
|
"""
|
|
Generated a list of all Queue files, where each entry consists of
|
|
a tuple (job-number, filename).
|
|
|
|
filename is an absolute path
|
|
|
|
"""
|
|
receivedQ = _userQ(config, user, "received")
|
|
for filename in os.listdir(receivedQ):
|
|
m = _job_pattern.match(filename)
|
|
if m:
|
|
yield (m.group(1), # job number
|
|
filename)
|
|
|
|
|
|
def abortJob(controlfile):
|
|
"""
|
|
Abort a fax job defined by it's controlfile.
|
|
|
|
This will remove the job from respective queue and delete both the
|
|
controlfile and the file defined in the controlfile.
|
|
"""
|
|
# todo: security: May this be missused for deleting other users files?
|
|
# todo: security: should ensure controlfile and filename are in the same
|
|
# directory
|
|
lockname = fileutils.lockname(controlfile)
|
|
if not os.access(controlfile, os.W_OK):
|
|
raise InvalidJob(None, controlfile)
|
|
try:
|
|
lock = fileutils._getLock(lockname, blocking=0)
|
|
except IOError, err:
|
|
if err.errno in (errno.EACCES, errno.EAGAIN):
|
|
raise JobLockedError(None, controlfile)
|
|
else:
|
|
raise
|
|
else:
|
|
try:
|
|
control = JobDescription(controlfile)
|
|
filename = control.get('filename')
|
|
os.unlink(filename)
|
|
os.unlink(controlfile)
|
|
finally:
|
|
fileutils._releaseLock(lock)
|
|
|
|
|
|
def createReceivedJob(user, filename, call_from, call_to, causes):
|
|
"""
|
|
Create a file description for a received fax. The description file
|
|
is written to the same directory as the filename.
|
|
"""
|
|
import time
|
|
control = {
|
|
'call_from': call_from,
|
|
'call_to': call_to,
|
|
'date': time.ctime(),
|
|
'cause': "0x%x/0x%x" % causes,
|
|
# we return this dict, thus set the filename here, too
|
|
'filename': filename,
|
|
}
|
|
controlfile = createDescriptionFor(**control)
|
|
fileutils._setProtection(user, 0600, filename, controlfile)
|
|
return control
|