Email to SMS gateway solution for Google Voice users

I’m a loyal Sprint PCS cell phone subscriber. Not only is their price/feature ratio right, but they offer outstanding integration with Google Voice that is unmatched by any other national cell phone carrier.

With almost all cell phone carriers, they offer an email to SMS gateway solution, wherein anyone can send an email with a short message in it to a specific address and it will be converted by the carrier into a text message and sent to your phone. For Sprint, the form is:

10digitnumber@messaging.sprintpcs.com

This is all fine and dandy, except that if you opt in for the enhanced Google Voice integration, that email to SMS gateway no longer will function for your number. There is a common solution posted on the Internet for this problem:

Have Google Voice forward text messages to your GMail account. From Google Voice, send a text message to yourself and wait for it to show up in your inbox. Look at that email and use the from address there as the address to send email to to send SMS to your phone. The problem with this is that Google generates a unique from address for each email it sends out as a text, which ties that from address to a specific recipient, so that threaded conversation can be maintained in Google Voice. Aka, you can save that specific from address and use it from your local GMail account to email texts to yourself, but if you give that email address to someone else, the emails from them will not go through to your phone.

I required a more robust solution, where I could provide a single email address to multiple outside people/services that would act as a gateway into a text message sent to my phone.  Here is my solution:

Requirements:

  • a machine that continually is up and runs python
  • a dummy GMail account
  • a dummy Google Voice account (optional, may be hard to get if you don’t have an extra phone number to tie it to)
  • pygooglevoice installed on the server machine (either in a virtualenv or system wide), patched as shown on https://code.google.com/p/pygooglevoice/issues/detail?id=58#c24
  • python-daemon installed on the server machine (either in a virtualenv or system wide)

Here is the setup:

Run a python daemon on a machine that monitors the dummy GMail inbox periodically for new mail. When it finds on, it downloads the message, parses out the body text, logs into a Google Voice account, and texts the body of the message to your cell phone. Seem simple enough?

Here is the code for the daemon that I wrote:

'''
Created on Dec 4, 2012

Simple Python daemon that acts as a proxy to forward
emails to a cell phone (for me, my google voice number)

@author: Charlie Meyer <charlie@charliemeyer.net>
'''

import imaplib
import email
from googlevoice import Voice
import logging
import time
from daemon import runner

logger = logging.getLogger("SMSDaemon")
logger.setLevel(logging.INFO)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler = logging.FileHandler("/var/log/SMSDaemon.log")
handler.setFormatter(formatter)
logger.addHandler(handler)

class EmailReceiver:

    def __init__(self):
        logger.debug("Email Init")
        self.username = "yoursmsdummyaccount@gmail.com" #edit this
        self.password = "smsdummyaccountpassword" #edit this
        self.server = "imap.gmail.com"
        self.port = 993
        self.M = None
        self.response = None

    def login(self):
        logger.debug("IMAP Login")
        self.M = imaplib.IMAP4_SSL(self.server, self.port)
        rc, self.response = self.M.login(self.username, self.password)
        logger.info(self.response)
        logger.debug("IMAP Login complete")
        return rc

    def logout(self):
        logger.debug("IMAP logout")
        self.M.logout()
        logger.debug("Logged out")

    def get_messages(self):
        logger.debug("Getting unread mail")
        self.M.select("INBOX")
        logger.debug("Getting unread count")
        status, response = self.M.status('INBOX', "(UNSEEN)")
        logger.debug("rc="+str(status))
        unreadcount = int(response[0].split()[2].strip(').,]'))
        if unreadcount > 0:
            logger.info("There are "+str(unreadcount)+" unread messages")
        logger.debug("Getting unread mail")
        status, email_ids = self.M.search(None, '(UNSEEN)')
        logger.debug("rc="+str(status))
        messages = []
        for e_id in email_ids[0].split():
            logger.info("Fetching message id="+str(e_id))
            rc, data = self.M.FETCH(e_id, '(RFC822)')
            logger.debug("Fetch complete, rc="+str(rc))
            mail = email.message_from_string(data[0][1])
            for part in mail.walk():
                if part.get_content_maintype() == 'multipart':
                    continue
                if part.get_content_subtype() != 'plain':
                    continue
                payload = part.get_payload()
                logger.info("message: "+str(payload))
                messages.append(payload)
        logger.debug("Mail fetch complete")
        return messages

class SMSSender:

    def __init__(self):
        logger.debug("SMS init")
        self.username = "dummygvoice@gmail.com" #edit this to be either your gvoice account or your dummy one if you have one
        self.password = "password" #edit this
        self.to = "your10digitphonenumber" #edit this
        self.voice = None

    def login(self):
        logger.info("SMS Login")
        self.voice = Voice()
        self.voice.login(self.username, self.password)
        logger.info("SMS Login complete")

    def logout(self):
        logger.info("SMS Logout")
        self.voice.logout()
        logger.info("SMS Logout complete")

    def send_sms(self, message):
        logger.info("Sending message: "+str(message))
        self.voice.send_sms(self.to, message)
        logger.info("Message sent")

class SMSDaemon():

    def __init__(self):
        self.stdin_path = '/dev/null'
        self.stdout_path = '/dev/tty'
        self.stderr_path = '/dev/tty'
        self.pidfile_path =  '/var/run/smsdaemon.pid'
        self.pidfile_timeout = 5

    def run(self):
        while True:
            logger.info("Daemon looping")
            g = EmailReceiver()
            g.login()
            messages = g.get_messages()
            if len(messages) > 0:
                sms = SMSSender()
                sms.login()
                for message in messages:
                    sms.send_sms(message)
                sms.logout()
            g.logout()
            logger.debug("Daemon Sleeping")
            time.sleep(60)

daemon = SMSDaemon()
daemon_runner = runner.DaemonRunner(daemon)
daemon_runner.daemon_context.files_preserve=[handler.stream]
daemon_runner.do_action()

I saved that file in /root/smsdaemon/SMSDaemon.py

I then tested it using:

python SMSDaemon.py start

and sent an email to my dummy gmail account and ensured it arrived as a text message on my phone. The program will log to /var/log/SMSDaemon.log, so you can look there to ensure it is working. Once that works, time to write the init script so it loads on boot.

First, ensure the daemon is stopped:

python SMSDaemon.py stop

Then, add the following to /etc/init.d/smsdaemon

#!/bin/bash
#
# /etc/init.d/smsdaemon
#
### BEGIN INIT INFO
# Provides: smsdaemon
# Required-Start:
# Should-Start:
# Required-Stop:
# Should-Stop:
# Default-Start:  3 5
# Default-Stop:   0 1 2 6
# Short-Description: SMSDaemon process
# Description:    Runs up the SMSDaemon process
### END INIT INFO

# modified from http://www.gavinj.net/2012/06/building-python-daemon-process.html

# uncomment and edit the following line if needed to activate the python virtual environment
# . /path_to_virtualenv/activate

case "$1" in
  start)
    echo "Starting"
    python /root/smsdaemon/SMSDaemon.py start
    ;;
  stop)
    echo "Stopping"
    python /root/smsdaemon/SMSDaemon.py stop
    ;;
  restart)
    echo "Restarting"
    python /root/smsdaemon/SMSDaemon.py restart
    ;;
  *)
    echo "Usage: /etc/init.d/smsdaemon {start|stop|restart}"
    exit 1
    ;;
esac

exit 0

Now:

sudo chmod +x /etc/init.d/smsdaemon
sudo update-rc.d smsdaemon defaults
sudo service smsdaemon start

There you go, all done!

Let me know if this works for you or if you have any comments, I might be looking to port this solution to a compute cloud like the Google App Engine or similar in the future.

ymmv

    • Blake
    • June 30th, 2014

    txt.att.net
    mms.att.net
    tmomail.net
    vtext.com
    vmobl.com
    sms.mycricket.com
    mms.mycricket.com

    anyway you get the picture 🙂

    “#1 Rule to life…. don’t make it more difficult than it is!”

      • Qunchuy
      • September 16th, 2014

      So what’s the email domain for Google Voice numbers, Blake?

      • BlakeIsDense
      • October 16th, 2014

      Yeah Blake, what is it for Google Voice? Come on smarty pants. Show us how easy it is.

    • There is no reason to be rude here, but what Blake did not read carefully above is that the email to google voice integration that is listed on the google support site did not work properly for sprint numbers with google voice integration. This post detailed a solution for this specific configuration. For “traditional” cell numbers or pure google voice numbers, Blake is correct.

  1. Come one blake, if its so easy, let us know. Oh sorry you’re not that smart.

    • JC
    • July 17th, 2015

    Hi,

    This is great. Thanks!

    I do have a question though: you said this would need a “dummy” google voice account, why is that? Unless I misunderstood, do you mean to say that you’d need TWO google voice accounts to make this work?

    Unless I have misunderstood, I think you can do it with just a dummy email and your normal google voice account. From your original, albeit limited, solution of establishing an “email thread link” between an email account and your google voice account: you would first establish the email link as you described. Then you would give your friends/contacts the dummy email and your script would take care of re-using the “email thread link”…

  2. This will lessen the weight of products to become moved
    and reduce the cost of move. Industry re-location often comes with the baggage of
    transporting explosives, gases, poisonous substances and fragile stuff.

    “When your ‘Sixth Sense’ will be activated, you will find following ideas and tricks in your head:Research work: You will start conducting a research to learn best moving agency in your city.

  1. No trackbacks yet.