0,0 → 1,224 |
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) |