SMS and Email Responder

From Unallocated Space
Revision as of 13:25, 25 October 2011 by C-P (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

The Unallocated Space SMS and Email Responder is a way for people to interact with the space and access certain functions and information over SMS or Email. Commands are currently as follows:

  • status returns the current open/closed status of the space.
  • rollcall returns a list of people who have checked into the space using the beta member check-in system.
  • tweet returns the contents of the latest @Unallocated twitter post.
  • site returns the latest blog post from the space website.
  • sign returns the current contents of the prolite LED_Sign, and updates the content if used with any further text. (sign this is the new sign text.
  • weather returns the current weather conditions in Severn MD.
  • address returns the address of the space.


The number to SMS is +1 (512) 943-2827, this number can also be called to check the open/closed status of the space as well as speak to someone in the space. Those functions and more are detailed here: Asterisk


  • Version 1.0 was written by Textile, and is no longer in use.
  • Version 2.0 was written by C-P and actually uses botfunc (respfunc is a symlink to botfunc) of the IRC_Bot for its commands code, and has an upgraded polling and response setup using IMAP and SMTP.



Version 2.0


#!/usr/bin/env python
import email,imaplib,smtplib,os,respfunc

def send_response(toaddrs,msg):
	print "Sending response to "+toaddrs
	print msg
	server=smtplib.SMTP('smtp.gmail.com:587')  
	server.starttls()  
	server.login('','')  
	server.sendmail('',toaddrs,"\r\n"+msg+"\r\n")  
	server.quit() 

def get_emails(email_ids):
        data=[]
        for e_id in email_ids:
                _,response=imap_server.fetch(e_id,'(RFC822)')		
		mail=email.message_from_string(response[0][1])
        	msg=''
        	for part in mail.walk():
                	if str(part.get_content_type())=='text/plain':
                        	msg=msg+str(part.get_payload())
        	msg=msg.split('--')[0].strip()
        	sender=email.utils.parseaddr(mail['from'])[1]
		data.append([sender.replace("'",''),msg.replace("'",'')])
	return data

imap_server=imaplib.IMAP4_SSL("imap.gmail.com",993)
imap_server.login('','')
imap_server.select('INBOX')
email_ids=str(imap_server.search(None,'(UNSEEN)')[1])[2:-2].split()

for em in get_emails(email_ids):
	if "postmaster.twitter.com" in em[0]: #Twitter mention
		print "unused"
		#em[1]=em[1].replace("=\r\n",'')
		#em[1]=em[1].replace("'",'')
        	#em[1]=em[1].replace('"','')
		#em[1]=em[1].split(" you:\r\n\r\n")[1].split("\r\n\r\n\r\nReply")[0].split("\r\n\r\nIn reply to")[0].strip()
		#print em[1]
		#os.system("echo 'New Twitter Mention: "+em[1]+"' > /uas/irc/irc")
	
	elif "unallocated@unallocatedspace.org" == em[0]: #wiki alert
		data=em[1].split('<{0}>')[1].split()
		os.system("echo 'Wiki Update: Page %s by %s: %s' > /uas/irc/irc" % (data[0], data[1], data[2]))
	else:
		print "From: "+em[0]
		print "Message: "+em[1]
		print
		em[1]=em[1].partition(" ")
		command=em[1][0].replace('!','')
		command=command.lower()
        	if command in respfunc.responder_commands:
        	        send_response(em[0],respfunc.responder_commands[command](em[1][2]))

imap_server.close()
imap_server.logout()





Version 1 is depreciated

Here is the code for the sensor responder using gmail and google voice -Textile

#!/usr/bin/python2.6
import imaplib,smtplib,re,urllib,urllib2,time,socket
from email.mime.text import MIMEText
import pdb,os
class actions:
    def __init__(self):
        pass

    def status(self,X=None):
        f = open('/tmp/status','r')
        stat = f.read()
        f.close()
        return stat

    def address(self,X=None):
        return "512 Shaw Court suite 105\nSevern, MD 21122"
    
    def sign(self,data):
	data=data.replace('"','')
	data=data.replace("'",'')
	try:
		if data=="":
			message='The last sign update read as: '+open('/tmp/sign','r').read()
                else:	
                        if '<FO>' in data:
                                message="<FO> is not allowed"
                        else:
                                s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
                                s.connect(('127.0.0.1',9001))
                                s.sendall(data)
                                message=s.recv(1024)
                                s.close()
				if "Updating sign to " in message:
					os.system("echo 'Mobile update to sign: "+data+"' > /uas/irc/irc")
        except socket.error:
                message="Failed to update sign"
        return message
        
class emailResponder:
        def __init__(self):
            cmd = actions()
            self.trigger = {'status':cmd.status,'address':cmd.address,'sign':cmd.sign}
            self.msg = None

        def parse(self):
            fromaddy = None
            subject = None
            self.body.reverse()
            for field in self.body:
                if field[:5] == 'From:':
                    fromaddy = field.split('<')[-1][:-1]
                elif field[:8] == 'Subject:':
                    subject = field.split(' ')[1].strip().lower()
                    self.msg = ' '.join(field.split(' ')[1:])
                    if subject not in self.trigger.keys():
                        subject = None
            if fromaddy and subject != None:
                return (fromaddy,subject)
            else: return False
        
        def getMessage(self,msg):
            if msg in self.trigger.keys():
                return self.trigger[msg](self.msg) #call function
            else:
                return "Invalid Command"

        def getUnread(self):
            """
            get unread messages and return subject and from address
            """
            self.messages = {}
            mbox = imaplib.IMAP4_SSL('imap.gmail.com', 993)
            mbox.login(######################,################)
            status, count = mbox.select('EmailResponder')
            #print count #number of total messages
            typ, data = mbox.search(None, 'UNSEEN')
            for num in data[0].split():
                typ, data = mbox.fetch(num, '(RFC822)')
                self.body = data[0][1].split('\r\n')
                data = self.parse()
                if data is not False:
                    self.messages[data[0]] = {"subject":data[1],"from":data[0]}
            mbox.close()
            mbox.logout()

        def sendRespond(self,responce,toaddy):
            server = smtplib.SMTP("smtp.gmail.com",587)
            server.ehlo('x')
            server.starttls()
            server.ehlo('x')
            server.login(######################,################)
            server.sendmail('',toaddy,responce)

        def status(self,toaddy):
            """
            format the responce from the sensor into an email message and return it
            """
            stat = self.getMessage(self.messages[toaddy]['subject'])
            msg = MIMEText(stat+"\n\nTeach, Learn, Party")
            msg['Subject'] = stat
            msg['From'] = ""
            msg['To'] = toaddy
            return str(msg)
        
        def respond(self):
            self.getUnread()
            if len(self.messages) == 0:
                return
            else:
                for key in self.messages.keys():
                    toaddy = self.messages[key]['from']
                    self.sendRespond(
                        self.status(toaddy),toaddy)

class textResponder:
        def __init__(self):
            cmd = actions()
            self.trigger = {'status':cmd.status,'address':cmd.address,'sign':cmd.sign}
            self.msg = None
            self.fromaddy = None

        def getUnread(self):
            """
            get unread messages and return subject and from address
            """
            self.messages = {}
            mbox = imaplib.IMAP4_SSL('imap.gmail.com', 993)
            mbox.login(######################,################)
            status, count = mbox.select('TXTMESSAGE')
            #print count #number of total messages
            typ, data = mbox.search(None, 'UNSEEN')
            for num in data[0].split():
                typ, data = mbox.fetch(num, '(RFC822)')
                self.body = data[0][1].split('\r\n')
                data = self.parse()
                if data is not False:
                    #we do this so multiple text messages are not sent to the same user based on email history
                    #its a hack but it works
                    self.messages[data[0]] = {"from":data[0],"msg":data[1]}
            mbox.close()
            mbox.logout()

        def parse(self):
            fromaddy = None
            msg = None
            self.body.reverse()
            for field in self.body:
                if field[:5] == 'From:':
                    fromaddy = field.split('<')[-1][:-1].split('.')[1]
                    self.fromaddy = fromaddy
                elif field.lower().split(' ')[0] in self.trigger.keys():
                    msg = field.lower().split(' ')[0]
                    self.msg = ' '.join(field.lower().split(' ')[1:])
            if fromaddy and msg != None:
                return (fromaddy,msg)
            else: return False
        
        def getMessage(self,msg):
            if msg in self.trigger.keys():
		return self.trigger[msg](self.msg) #call function
            else:
                return "Invalid Command"

        def respond(self):
            self.getUnread()
            #check to see if there are any messages to be sent and return
            if len(self.messages) == 0:
                return 
            gv = GoogleVoiceLogin(######################,################)
            if not gv.logged_in:
                print "voice login failed"
                return
            for number in self.messages.keys():
                text_sender = TextSend(gv.opener, gv.key)
                text_sender.text = self.getMessage(self.messages[number]['msg'])
                text_sender.send_text(number)
                #following 5 lines are used for debug
                #if text_sender.responce:
                #   print text_sender.responce
                #   print "success"
                #else:
                #   print "failed"
                #sleep so as not to have google think we are trying to txt message spam
                time.sleep(3)
                
class TextSend():
    """
    text send coded taken from here
    http://everydayscripting.blogspot.com/2009/08/python-google-voice-mass-sms-and-mass.html
    """
    def __init__(self, opener, key):
        self.opener = opener
        self.key = key
        self.sms_url = "https://www.google.com/voice/sms/send/"
        self.text = ''

    def send_text(self, phone_number):
        sms_params = urllib.urlencode({
        '_rnr_se': self.key,
        'phoneNumber': phone_number,
        'text': self.text
        })
        #send the text
        self.responce = self.opener.open(self.sms_url, sms_params).read()

class GoogleVoiceLogin:
    """
    gvoice code from here
    http://everydayscripting.blogspot.com/2009/08/python-google-voice-mass-sms-and-mass.html
    """
    def __init__(self, email, password):
        # Set up our opener
        self.opener = urllib2.build_opener(urllib2.HTTPCookieProcessor())
        urllib2.install_opener(self.opener)
                        
        # Define URLs
        self.loing_page_url = 'https://www.google.com/accounts/ServiceLogin'
        self.authenticate_url = 'https://www.google.com/accounts/ServiceLoginAuth'
        self.gv_home_page_url = 'https://www.google.com/voice/#inbox'
                                                    
        # Load sign in page
        login_page_contents = self.opener.open(self.loing_page_url).read()

        # Find GALX value
        galx_match_obj = re.search(r'name="GALX"\s*value="([^"]+)"', login_page_contents, re.IGNORECASE)
                                                                                
        galx_value = galx_match_obj.group(1) if galx_match_obj.group(1) is not None else ''
        # Set up login credentials  
        login_params = urllib.urlencode( {
            'Email' : email,
            'Passwd' : password,
            'continue' : 'https://www.google.com/voice/account/signin',
            'GALX': galx_value
            })
        # Login
        self.opener.open(self.authenticate_url, login_params)

        # Open GV home page
        gv_home_page_contents = self.opener.open(self.gv_home_page_url).read()
        # Fine _rnr_se value
        key = re.search('name="_rnr_se".*?value="(.*?)"', gv_home_page_contents)
        if not key:
            self.logged_in = False
        else:
            self.logged_in = True
            self.key = key.group(1)

if __name__ == "__main__":
    emailResponder().respond()
    textResponder().respond()