Subversion Repositories Code-Repo

Rev

Rev 99 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
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)