Blame | Last modification | View Log | Download | RSS feed
import urllibimport urllib2import cookielibimport getpassimport timeimport smtplibimport randomimport sysfrom email.mime.text import MIMETextfrom BeautifulSoup import BeautifulSoupurlnit = '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 = 0def log(string):# Open the log file for writingfile = 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 modifiedglobal timeOfLastEmailglobal 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() - timeOfLastEmailif 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 serveremailServer = smtplib.SMTP_SSL(smtpServer, 465)emailServer.login(sourceEmail,pwd)# Create body messagemessage = 'A slot has opened! {0} {1} {2} {3}'.format(CRN, CourseID, CourseName, CourseOpening)log(message)# Send text message to specified phone numberemailServer.sendmail(sourceEmail, destEmail, message)# Create email messageemail = MIMEText(message)email['Subject'] = 'A slot has opened! {0} {1}'.format(CourseID, CourseName)email['From'] = sourceEmailemail['To'] = destEmail# Send email to yourselfemailServer.sendmail(sourceEmail, sourceEmail, email.as_string())emailServer.quit()# make sure we only send one text/email per CRN that opensprocessedCRNs.append(CRN)def sendError(sourceEmail, pwd):# Establish connection to the outbound email serveremailServer = smtplib.SMTP_SSL(smtpServer, 465)emailServer.login(sourceEmail,pwd)# Create body messagemessage = '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 messageemail = MIMEText(message)email['Subject'] = 'An exception has occured in CourseChecker'email['From'] = sourceEmailemail['To'] = sourceEmail# Send email to yourselfemailServer.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 argumentslogFile = 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 themprint "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 argumentslogFile = 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 addressessourceEmail = 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 timerandom.seed()# Create a cookie jar to hold cookies in between requestscookies = 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 scriptblank_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 requestrequest = urllib2.Request(urlnit, blank_data, header)response = opener.open(request)# Get cookies from initial requestcookies.extract_cookies(response, request)# Create second request with login infologin_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 somehowwhile (True):# Poll information for each specified CRNfor CRN in tokens:# Dont bother polling for CRN if an opened slot was already detectedif CRN not in processedCRNs:log("Checking CRN {0}".format(CRN))# Create request for pulling timetable resultspoll_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 statussoupParser = BeautifulSoup(response.replace("\n",""))table = soupParser.find(attrs={'class' : "dataentrytable"})# Check if there are listings for the given CRNif 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 fullif row.contents[5].p.contents[0].string.strip() != "Full":# If class is not full, do somethingCRN = row.contents[0].p.a.b.stringCourseID = row.contents[1].contents[0].stringCourseName = row.contents[2].stringCourseOpening = row.contents[5].p.contents[0].stringprint CRN, CourseID, CourseName, CourseOpeningprocessOpening(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 checkstime.sleep(3)# Sleep for +- 0.5x specified period of timesleepDuration = sleep * (random.random() + 0.5)log("Sleeping for {0} seconds".format(sleepDuration))time.sleep(sleepDuration)sys.stdout.flush()# Catch interrupt from keyboardexcept KeyboardInterrupt:sys.exit(1)# Catch unhandled program exception and send error emailexcept:sendError(sourceEmail, pwd)sys.exit(1)