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)