E-mail to voice call – with Asterisk, Postfix and Cepstral

A few times recently I’ve wanted to be able to turn an e-mail into a voice call. This would be especially handy for emergency server monitoring and notification.

Here is my first attempt. It’s also my first attempt at writing something in Python so you definitely use at your own risk!

There is room for improvement as there is no validation on any of the fields extracted from the e-mail.

It also assumes that these components are already in place –

    • Asterisk (with Astersk Manager Interface)
    • E-mail server (I’m using Postfix)
    • Ceptral text-to-speech (www.cepstral.com) – installed in /opt/swift/bin
    • Python (I’m using v2.4.3)

 

First we need to pipe the incoming e-mail to our Python script. For this I added a line to /etc/aliases –

emailspeak: "|/usr/local/bin/emailspeak.py"

and ran newaliases –

newaliases

Now for the script which is called ‘/usr/local/bin/emailspeak.py’

You will need to change at least the USER, SECRET and TRUNK settings at the top of the script to match you Asterisk setup.

#!/usr/bin/env python
# emailspeak.py by sysadminman - http://sysadminman.net
# v1.0  13/2/10

# Import libs we need
import sys, time, email, email.Message, email.Errors, email.Utils, smtplib, os, socket, random
from datetime import date
from email.Iterators import typed_subpart_iterator
from time import sleep

# Asterisk Manager connection details
HOST = '127.0.0.1'
PORT = 5038
# Asterisk Manager username and password
USER = 'manageruser'
SECRET = 'managerpass'
# Set the name of the SIP trunk to use for outbound calls
TRUNK = 'trunkforcalls'

# Generate a random number as a string. We'll use this for file names later on
callnum = str(random.randint(1, 100000000))

# Taken from here, with thanks - http://ginstrom.com/scribbles/2007/11/19/parsing-multilingual-email-with-python/
def get_charset(message, default="ascii"):
    """Get the message charset"""

    if message.get_content_charset():
        return message.get_content_charset()

    if message.get_charset():
        return message.get_charset()

    return default

# Taken from here, with thanks - http://ginstrom.com/scribbles/2007/11/19/parsing-multilingual-email-with-python/
def get_body(message):
    """Get the body of the email message"""

    if message.is_multipart():
        #get the plain text version only
        text_parts = [part
                      for part in typed_subpart_iterator(message,
                                                         'text',
                                                         'plain')]
        body = []
        for part in text_parts:
            charset = get_charset(part, get_charset(message))
            body.append(unicode(part.get_payload(decode=True),
                                charset,
                                "replace"))

        return u"\n".join(body).strip()

    else: # if it is not multipart, the payload will be a string
          # representing the message body
        body = unicode(message.get_payload(decode=True),
                       get_charset(message),
                       "replace")
        return body.strip()

# Read the e-mail message that has been piped to us by Postfix
raw_msg = sys.stdin.read()
emailmsg = email.message_from_string(raw_msg)

# Extract database Fields from mail
msgfrom = emailmsg['From']
msgto =  emailmsg['To']
msgsubj = emailmsg['Subject']
msgbody = get_body(emailmsg)

# Write a log file in /tmp with a record of the e-mails
currtime = date.today().strftime("%B %d, %Y")
logfile = open('/tmp/email2voice.log', 'a')
logfile.write(currtime + "\n")
logfile.write("Call Number: " + callnum + "\n")
logfile.write("From: " + msgfrom + "\n")
logfile.write("To: " + msgto + "\n")
logfile.write("Subject: " + msgsubj + "\n")
logfile.write("Body: " + msgbody + "\n\n")
logfile.close()

# Convert the body of the text to a wav file
swiftcommand = "/opt/swift/bin/swift -n Millie-8kHz -o /tmp/" + callnum + ".wav '" + msgbody + "'"
os.system(swiftcommand)

# We need to allow Asterisk permission to read the wav file
chmodcommand = "chmod 777 /tmp/" + callnum + ".wav"
os.system(chmodcommand)

# Set the number to be dailed as the subject of the e-mail
OUTBOUND = msgsubj

# Send the call details to the Asteirsk Manager Interface
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT))
sleep(3)
s.send('Action: login\r\n')
s.send('Username: ' + USER + '\r\n')
s.send('Secret: ' + SECRET + '\r\n\r\n')
sleep(3)
s.send('Events: off\r\n\r\n')
sleep(3)
s.send('Action: originate\r\n')
s.send('Channel: Sip/' + TRUNK + '/' + OUTBOUND + '\r\n')
s.send('WaitTime: 30\r\n')
s.send('CallerId: 1234\r\n')
s.send('Application: playback\r\n')
s.send('Data: /tmp/' + callnum + '\r\n')
s.send('Context: from-internal\r\n')
s.send('Async: true\r\n')
s.send('Priority: 1\r\n\r\n')
sleep(3)
s.send('Action: Logoff\r\n\r\n')
s.close()

And that should be it. To test just send an e-mail to emailspeak@yourserver.com with the telephone number you want to call as the subject line, and the text you want to be read in the body.

Don’t forget to write the telephone number in the format that your SIP provider is expecting it.