| 99 |
Kevin |
1 |
import urllib
|
|
|
2 |
import urllib2
|
|
|
3 |
import cookielib
|
|
|
4 |
import getpass
|
|
|
5 |
import time
|
|
|
6 |
import smtplib
|
|
|
7 |
import random
|
|
|
8 |
import sys
|
|
|
9 |
from email.mime.text import MIMEText
|
|
|
10 |
from BeautifulSoup import BeautifulSoup
|
|
|
11 |
|
|
|
12 |
urlnit = 'https://banweb.banner.vt.edu/ssb/prod/twbkwbis.P_WWWLogin'
|
|
|
13 |
urlAuth = 'https://banweb.banner.vt.edu/ssb/prod/twbkwbis.P_ValLogin'
|
|
|
14 |
urlRequest = 'https://banweb.banner.vt.edu/ssb/prod/HZSKVTSC.P_ProcRequest'
|
|
|
15 |
smtpServer = 'auth.smtp.vt.edu'
|
|
|
16 |
logFile = ""
|
|
|
17 |
processedCRNs = []
|
|
|
18 |
timeOfLastEmail = 0
|
|
|
19 |
|
|
|
20 |
def log(string):
|
|
|
21 |
# Open the log file for writing
|
|
|
22 |
file = open(logFile, 'a')
|
|
|
23 |
file.write(string + '\n')
|
|
|
24 |
file.close()
|
|
|
25 |
|
|
|
26 |
def processOpening(sourceEmail, destEmail, pwd, CRN, CourseID, CourseName, CourseOpening):
|
|
|
27 |
# Explicitly declare global variables so they can be modified
|
|
|
28 |
global timeOfLastEmail
|
|
|
29 |
global processedCRNs
|
|
|
30 |
|
|
|
31 |
# Make sure the script waits for at least 20 seconds inbetween sending emails (to avoid getting banned from SMTP)
|
|
|
32 |
if timeOfLastEmail == 0:
|
|
|
33 |
timeOfLastEmail = time.time()
|
|
|
34 |
else:
|
|
|
35 |
elapsedTime = time.time() - timeOfLastEmail
|
|
|
36 |
if elapsedTime < 20:
|
|
|
37 |
log("Waiting {0} seconds before sending more emails".format(20-elapsedTime))
|
|
|
38 |
time.sleep(20 - elapsedTime)
|
|
|
39 |
timeOfLastEmail = time.time()
|
|
|
40 |
|
|
|
41 |
# Establish connection to the outbound email server
|
|
|
42 |
emailServer = smtplib.SMTP_SSL(smtpServer, 465)
|
|
|
43 |
emailServer.login(sourceEmail,pwd)
|
|
|
44 |
|
|
|
45 |
# Create body message
|
|
|
46 |
message = 'A slot has opened! {0} {1} {2} {3}'.format(CRN, CourseID, CourseName, CourseOpening)
|
|
|
47 |
log(message)
|
|
|
48 |
# Send text message to specified phone number
|
|
|
49 |
emailServer.sendmail(sourceEmail, destEmail, message)
|
|
|
50 |
|
|
|
51 |
# Create email message
|
|
|
52 |
email = MIMEText(message)
|
|
|
53 |
email['Subject'] = 'A slot has opened! {0} {1}'.format(CourseID, CourseName)
|
|
|
54 |
email['From'] = sourceEmail
|
|
|
55 |
email['To'] = destEmail
|
|
|
56 |
|
|
|
57 |
# Send email to yourself
|
|
|
58 |
emailServer.sendmail(sourceEmail, sourceEmail, email.as_string())
|
|
|
59 |
emailServer.quit()
|
|
|
60 |
|
|
|
61 |
# make sure we only send one text/email per CRN that opens
|
|
|
62 |
processedCRNs.append(CRN)
|
|
|
63 |
|
|
|
64 |
def sendError(sourceEmail, pwd):
|
|
|
65 |
# Establish connection to the outbound email server
|
|
|
66 |
emailServer = smtplib.SMTP_SSL(smtpServer, 465)
|
|
|
67 |
emailServer.login(sourceEmail,pwd)
|
|
|
68 |
|
|
|
69 |
# Create body message
|
|
|
70 |
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])
|
|
|
71 |
log (message)
|
|
|
72 |
|
|
|
73 |
# Create email message
|
|
|
74 |
email = MIMEText(message)
|
|
|
75 |
email['Subject'] = 'An exception has occured in CourseChecker'
|
|
|
76 |
email['From'] = sourceEmail
|
|
|
77 |
email['To'] = sourceEmail
|
|
|
78 |
|
|
|
79 |
# Send email to yourself
|
|
|
80 |
emailServer.sendmail(sourceEmail, sourceEmail, email.as_string())
|
|
|
81 |
emailServer.quit()
|
|
|
82 |
|
|
|
83 |
if __name__ == '__main__':
|
|
|
84 |
try:
|
|
|
85 |
if len(sys.argv) == 1:
|
|
|
86 |
# If no passed in parameter, prompt for input. Otherwise use passed in arguments
|
|
|
87 |
logFile = raw_input("Enter name of log file: ")
|
|
|
88 |
user = raw_input("Input Username (PID): ")
|
|
|
89 |
pwd = getpass.getpass("Input Password: ")
|
|
|
90 |
phone = raw_input("Enter Phone Number: ")
|
|
|
91 |
while len(phone) != 10:
|
|
|
92 |
print "Enter in phone number without spaces or dashes"
|
|
|
93 |
phone = raw_input("Enter Phone Number: ")
|
|
|
94 |
carrier = str.lower(raw_input("Enter Carrier (Verizon/Sprint/AT&T/T-Mobile): "))
|
|
|
95 |
while (carrier != 'verizon' and carrier != 'sprint' and carrier != 'at&t' and carrier != 't-mobile'):
|
|
|
96 |
print "Invalid service provider"
|
|
|
97 |
carrier = str.lower(raw_input("Enter Carrier (Verizon/Sprint/AT&T/T-Mobile): "))
|
|
|
98 |
sleep = int(raw_input("Average time to sleep between checks (seconds): "))
|
|
|
99 |
CRNs = raw_input("Enter CRNs to monitor (seperated by ' '): ")
|
|
|
100 |
tokens = CRNs.split()
|
|
|
101 |
elif len(sys.argv) < 8:
|
|
|
102 |
# If argument are passed in, make sure that theres a correct number of them
|
|
|
103 |
print "Arguments should be in the format 'logfile' 'username' 'password' 'wait-period' 'phone #' [Verizon/Sprint/AT&T/T-Mobile] ['CRN' ...]"
|
|
|
104 |
sys.exit(1)
|
|
|
105 |
else:
|
|
|
106 |
# Otherwise get arguments
|
|
|
107 |
logFile = sys.argv[1]
|
|
|
108 |
user = sys.argv[2]
|
|
|
109 |
pwd = sys.argv[3]
|
|
|
110 |
sleep = int(sys.argv[4])
|
|
|
111 |
phone = sys.argv[5]
|
|
|
112 |
if len(phone) != 10:
|
|
|
113 |
print "Invalid Phone Number"
|
|
|
114 |
sys.exit(1)
|
|
|
115 |
carrier = str.lower(sys.argv[6])
|
|
|
116 |
if (carrier != 'verizon' and carrier != 'sprint' and carrier != 'at&t' and carrier != 't-mobile'):
|
|
|
117 |
print "Invalid Carrier"
|
|
|
118 |
sys.exit(1)
|
|
|
119 |
tokens = sys.argv[7:]
|
|
|
120 |
|
|
|
121 |
# Create source and destination email addresses
|
|
|
122 |
sourceEmail = user+'@vt.edu'
|
|
|
123 |
if carrier == 'verizon':
|
|
|
124 |
destEmail = phone+'@vtext.com'
|
|
|
125 |
elif carrier == 'sprint':
|
|
|
126 |
destEmail = phone+'@messaging.sprintpcs.com'
|
|
|
127 |
elif carrier == 'at&t':
|
|
|
128 |
destEmail = phone+'@txt.att.net'
|
|
|
129 |
elif carrier == 't-mobile':
|
|
|
130 |
destEmail = phone+'@tmomail.net'
|
|
|
131 |
|
|
|
132 |
# Seed the random generator with the current time
|
|
|
133 |
random.seed()
|
|
|
134 |
|
|
|
135 |
# Create a cookie jar to hold cookies in between requests
|
|
|
136 |
cookies = cookielib.CookieJar()
|
|
|
137 |
cookie_handler = urllib2.HTTPCookieProcessor(cookies)
|
|
|
138 |
redirect_handler = urllib2.HTTPRedirectHandler()
|
|
|
139 |
opener = urllib2.build_opener(redirect_handler, cookie_handler)
|
|
|
140 |
|
|
|
141 |
# 'Fake' headers so they theoretically wont know that its a script
|
|
|
142 |
blank_data = urllib.urlencode({})
|
|
|
143 |
header = {
|
|
|
144 |
"Accept" : "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
|
|
145 |
"Accept-Charset" : "ISO-8859-1,utf-8;q=0.7,*;q=0.3",
|
|
|
146 |
"Accept-Encoding" : "gzip,deflate,sdch",
|
|
|
147 |
"Accept-Language" : "en-US,en;q=0.8",
|
|
|
148 |
"Cache-Control" : "max-age=0",
|
|
|
149 |
"Connection" : "keep-alive",
|
|
|
150 |
"Content-Type" : "application/x-www-form-urlencoded",
|
|
|
151 |
"Host" : "banweb.banner.vt.edu",
|
|
|
152 |
"Referer:https" : "//banweb.banner.vt.edu/ssb/prod/HZSKVTSC.P_ProcRequest",
|
|
|
153 |
"User-Agent" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2"
|
|
|
154 |
}
|
|
|
155 |
|
|
|
156 |
# Create the initial request
|
|
|
157 |
request = urllib2.Request(urlnit, blank_data, header)
|
|
|
158 |
response = opener.open(request)
|
|
|
159 |
# Get cookies from initial request
|
|
|
160 |
cookies.extract_cookies(response, request)
|
|
|
161 |
|
|
|
162 |
# Create second request with login info
|
|
|
163 |
login_cred = urllib.urlencode({
|
|
|
164 |
'pid' : user,
|
|
|
165 |
'password' : pwd
|
|
|
166 |
})
|
|
|
167 |
request = urllib2.Request(urlAuth, login_cred, header)
|
|
|
168 |
response = opener.open(request)
|
|
|
169 |
|
|
|
170 |
# Perpetual loop. Should change it so that it can be interrupted somehow
|
|
|
171 |
while (True):
|
|
|
172 |
# Poll information for each specified CRN
|
|
|
173 |
for CRN in tokens:
|
|
|
174 |
# Dont bother polling for CRN if an opened slot was already detected
|
|
|
175 |
if CRN not in processedCRNs:
|
|
|
176 |
log("Checking CRN {0}".format(CRN))
|
|
|
177 |
# Create request for pulling timetable results
|
|
|
178 |
poll_data = urllib.urlencode({
|
|
|
179 |
"CAMPUS" : "0",
|
|
|
180 |
"TERMYEAR" : "201201",
|
|
|
181 |
"CORE_CODE" : "AR%",
|
|
|
182 |
"crn" : CRN
|
|
|
183 |
})
|
|
|
184 |
request = urllib2.Request(urlRequest, poll_data, header)
|
|
|
185 |
response = opener.open(request).read()
|
|
|
186 |
|
|
|
187 |
try:
|
|
|
188 |
# Parse results for CRN, course ID, course title, and seat status
|
|
|
189 |
soupParser = BeautifulSoup(response.replace("\n",""))
|
|
|
190 |
table = soupParser.find(attrs={'class' : "dataentrytable"})
|
|
|
191 |
# Check if there are listings for the given CRN
|
|
|
192 |
if table == None:
|
|
|
193 |
log("No listing found for CRN {0}".format(CRN))
|
|
|
194 |
continue
|
|
|
195 |
# Check if the row value is blank (some classes occupy two rows)
|
|
|
196 |
for row in table.contents[1:]:
|
|
|
197 |
if row.p != None:
|
|
|
198 |
# Check if class status is full
|
|
|
199 |
if row.contents[5].p.contents[0].string.strip() != "Full":
|
|
|
200 |
# If class is not full, do something
|
|
|
201 |
CRN = row.contents[0].p.a.b.string
|
|
|
202 |
CourseID = row.contents[1].contents[0].string
|
|
|
203 |
CourseName = row.contents[2].string
|
|
|
204 |
CourseOpening = row.contents[5].p.contents[0].string
|
|
|
205 |
print CRN, CourseID, CourseName, CourseOpening
|
|
|
206 |
processOpening(sourceEmail, destEmail, pwd, CRN, CourseID, CourseName, CourseOpening)
|
|
|
207 |
except AttributeError:
|
|
|
208 |
log("Attribute error thrown by parser. Skipping CRN")
|
|
|
209 |
sendError(sourceEmail, pwd)
|
|
|
210 |
time.sleep(1)
|
|
|
211 |
# Sleep for 3 seconds in between checks
|
|
|
212 |
time.sleep(3)
|
|
|
213 |
# Sleep for +- 0.5x specified period of time
|
|
|
214 |
sleepDuration = sleep * (random.random() + 0.5)
|
|
|
215 |
log("Sleeping for {0} seconds".format(sleepDuration))
|
|
|
216 |
time.sleep(sleepDuration)
|
|
|
217 |
sys.stdout.flush()
|
|
|
218 |
# Catch interrupt from keyboard
|
|
|
219 |
except KeyboardInterrupt:
|
|
|
220 |
sys.exit(1)
|
|
|
221 |
# Catch unhandled program exception and send error email
|
|
|
222 |
except:
|
|
|
223 |
sendError(sourceEmail, pwd)
|
|
|
224 |
sys.exit(1)
|