Subversion Repositories Code-Repo

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

import urllib
import urllib2
import cookielib
import getpass
import time
import smtplib
import random
import sys
from email.mime.text import MIMEText
from BeautifulSoup import BeautifulSoup

urlnit = 'https://banweb.banner.vt.edu/ssb/prod/twbkwbis.P_WWWLogin'
urlAuth = 'https://banweb.banner.vt.edu/ssb/prod/twbkwbis.P_ValLogin'
urlRequest = 'https://banweb.banner.vt.edu/ssb/prod/HZSKVTSC.P_ProcRequest'
smtpServer = 'auth.smtp.vt.edu'
logFile = ""
processedCRNs = []
timeOfLastEmail = 0

def log(string):
        # Open the log file for writing
        file = open(logFile, 'a')
        file.write(string + '\n')
        file.close()

def processOpening(sourceEmail, destEmail, pwd, CRN, CourseID, CourseName, CourseOpening):
        # Explicitly declare global variables so they can be modified
        global timeOfLastEmail
        global processedCRNs

        # Make sure the script waits for at least 20 seconds inbetween sending emails (to avoid getting banned from SMTP)
        if timeOfLastEmail == 0:
                timeOfLastEmail = time.time()
        else:
                elapsedTime = time.time() - timeOfLastEmail
                if elapsedTime < 20:
                        log("Waiting {0} seconds before sending more emails".format(20-elapsedTime))
                        time.sleep(20 - elapsedTime)
                timeOfLastEmail = time.time()

        # Establish connection to the outbound email server
        emailServer = smtplib.SMTP_SSL(smtpServer, 465)
        emailServer.login(sourceEmail,pwd)

        # Create body message 
        message = 'A slot has opened! {0}  {1}  {2}  {3}'.format(CRN, CourseID, CourseName, CourseOpening)
        log(message)
        # Send text message to specified phone number
        emailServer.sendmail(sourceEmail, destEmail, message)

        # Create email message
        email = MIMEText(message)
        email['Subject'] = 'A slot has opened! {0}  {1}'.format(CourseID, CourseName)
        email['From'] = sourceEmail
        email['To'] = destEmail

        # Send email to yourself
        emailServer.sendmail(sourceEmail, sourceEmail, email.as_string())
        emailServer.quit()

        # make sure we only send one text/email per CRN that opens
        processedCRNs.append(CRN)

def sendError(sourceEmail, pwd):
        # Establish connection to the outbound email server
        emailServer = smtplib.SMTP_SSL(smtpServer, 465)
        emailServer.login(sourceEmail,pwd)

        # Create body message 
        message = 'An exception has occured. Please restart the CourseChecker script.\n{0}\n{1}\n{2}'.format(sys.exc_info()[0],sys.exc_info()[1],sys.exc_info()[2])
        log (message)

        # Create email message
        email = MIMEText(message)
        email['Subject'] = 'An exception has occured in CourseChecker'
        email['From'] = sourceEmail
        email['To'] = sourceEmail

        # Send email to yourself
        emailServer.sendmail(sourceEmail, sourceEmail, email.as_string())
        emailServer.quit()

if __name__ == '__main__':
        try:
                if len(sys.argv) == 1:
                        # If no passed in parameter, prompt for input. Otherwise use passed in arguments
                        logFile = raw_input("Enter name of log file: ")
                        user = raw_input("Input Username (PID): ")
                        pwd = getpass.getpass("Input Password: ")
                        phone = raw_input("Enter Phone Number: ")
                        while len(phone) != 10:
                                print "Enter in phone number without spaces or dashes"
                                phone = raw_input("Enter Phone Number: ")
                        carrier = str.lower(raw_input("Enter Carrier (Verizon/Sprint/AT&T/T-Mobile): "))
                        while (carrier != 'verizon' and carrier != 'sprint' and carrier != 'at&t' and carrier != 't-mobile'):
                                print "Invalid service provider"
                                carrier = str.lower(raw_input("Enter Carrier (Verizon/Sprint/AT&T/T-Mobile): "))
                        sleep = int(raw_input("Average time to sleep between checks (seconds): "))
                        CRNs = raw_input("Enter CRNs to monitor (seperated by ' '): ")
                        tokens = CRNs.split()
                elif len(sys.argv) < 8:
                        # If argument are passed in, make sure that theres a correct number of them
                        print "Arguments should be in the format 'logfile' 'username' 'password' 'wait-period' 'phone #' [Verizon/Sprint/AT&T/T-Mobile] ['CRN' ...]"
                        sys.exit(1)
                else:
                        # Otherwise get arguments
                        logFile = sys.argv[1]
                        user = sys.argv[2]
                        pwd = sys.argv[3]
                        sleep = int(sys.argv[4])
                        phone = sys.argv[5]
                        if len(phone) != 10:
                                print "Invalid Phone Number"
                                sys.exit(1)
                        carrier = str.lower(sys.argv[6])
                        if (carrier != 'verizon' and carrier != 'sprint' and carrier != 'at&t' and carrier != 't-mobile'):
                                print "Invalid Carrier"
                                sys.exit(1)
                        tokens = sys.argv[7:]

                # Create source and destination email addresses
                sourceEmail = user+'@vt.edu'
                if carrier == 'verizon':
                        destEmail = phone+'@vtext.com'
                elif carrier == 'sprint':
                        destEmail = phone+'@messaging.sprintpcs.com'
                elif carrier == 'at&t':
                        destEmail = phone+'@txt.att.net'
                elif carrier == 't-mobile':
                        destEmail = phone+'@tmomail.net'

                # Seed the random generator with the current time
                random.seed()

                # Create a cookie jar to hold cookies in between requests
                cookies = cookielib.CookieJar()
                cookie_handler = urllib2.HTTPCookieProcessor(cookies)
                redirect_handler = urllib2.HTTPRedirectHandler()
                opener = urllib2.build_opener(redirect_handler, cookie_handler)

                # 'Fake' headers so they theoretically wont know that its a script
                blank_data = urllib.urlencode({})
                header = {
                        "Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
                        "Accept-Charset" : "ISO-8859-1,utf-8;q=0.7,*;q=0.3",
                        "Accept-Encoding" : "gzip,deflate,sdch",
                        "Accept-Language" : "en-US,en;q=0.8",
                        "Cache-Control" : "max-age=0",
                        "Connection" : "keep-alive",
                        "Content-Type" : "application/x-www-form-urlencoded",
                        "Host" : "banweb.banner.vt.edu",
                        "Referer:https" : "//banweb.banner.vt.edu/ssb/prod/HZSKVTSC.P_ProcRequest",
                        "User-Agent" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2"
                        }

                # Create the initial request
                request = urllib2.Request(urlnit, blank_data, header)
                response = opener.open(request)
                # Get cookies from initial request
                cookies.extract_cookies(response, request)

                # Create second request with login info
                login_cred = urllib.urlencode({
                        'pid' : user,
                        'password' : pwd
                        })
                request = urllib2.Request(urlAuth, login_cred, header) 
                response = opener.open(request)
        
                # Perpetual loop. Should change it so that it can be interrupted somehow
                while (True):
                        # Poll information for each specified CRN
                        for CRN in tokens:
                                # Dont bother polling for CRN if an opened slot was already detected
                                if CRN not in processedCRNs:
                                        log("Checking CRN {0}".format(CRN))
                                        # Create request for pulling timetable results
                                        poll_data = urllib.urlencode({
                                                "CAMPUS" : "0", 
                                                "TERMYEAR" : "201201", 
                                                "CORE_CODE" : "AR%", 
                                                "crn" : CRN
                                                })
                                        request = urllib2.Request(urlRequest, poll_data, header)
                                        response = opener.open(request).read()
                                        
                                        try:
                                                # Parse results for CRN, course ID, course title, and seat status
                                                soupParser = BeautifulSoup(response.replace("\n",""))
                                                table = soupParser.find(attrs={'class' : "dataentrytable"})
                                                # Check if there are listings for the given CRN
                                                if table == None:
                                                        log("No listing found for CRN {0}".format(CRN))
                                                        continue
                                                # Check if the row value is blank (some classes occupy two rows)
                                                for row in table.contents[1:]:
                                                        if row.p != None:
                                                                # Check if class status is full
                                                                if row.contents[5].p.contents[0].string.strip() != "Full":
                                                                        # If class is not full, do something
                                                                        CRN = row.contents[0].p.a.b.string
                                                                        CourseID = row.contents[1].contents[0].string
                                                                        CourseName =  row.contents[2].string
                                                                        CourseOpening = row.contents[5].p.contents[0].string
                                                                        print CRN, CourseID, CourseName, CourseOpening
                                                                        processOpening(sourceEmail, destEmail, pwd, CRN, CourseID, CourseName, CourseOpening)
                                        except AttributeError:
                                                log("Attribute error thrown by parser. Skipping CRN")
                                                sendError(sourceEmail, pwd)
                                                time.sleep(1)
                                        # Sleep for 3 seconds in between checks
                                        time.sleep(3)
                        # Sleep for +- 0.5x specified period of time
                        sleepDuration = sleep * (random.random() + 0.5)
                        log("Sleeping for {0} seconds".format(sleepDuration))
                        time.sleep(sleepDuration) 
                        sys.stdout.flush()
        # Catch interrupt from keyboard
        except KeyboardInterrupt:
                sys.exit(1)
        # Catch unhandled program exception and send error email
        except:
                sendError(sourceEmail, pwd)
                sys.exit(1)