diff --git a/.gitignore b/.gitignore index 8c09524..d2a73a6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ *.pyc -*.swp \ No newline at end of file +*.swp +client_secret.json \ No newline at end of file diff --git a/checkin.py b/checkin.py index 6f412fe..f39cd0a 100755 --- a/checkin.py +++ b/checkin.py @@ -12,63 +12,63 @@ from math import trunc # Pulled from proxying the Southwest iOS App headers = {'Host': 'mobile.southwest.com', 'Content-Type': 'application/vnd.swacorp.com.mobile.reservations-v1.0+json', 'X-API-Key': 'l7xxab4b3533b8d54bad9df230deb96a6f90', 'Accept': '*/*'} -reservation_number = sys.argv[1] -first_name = sys.argv[2] -last_name = sys.argv[3] - -# Find our existing record -url = "https://mobile.southwest.com/api/extensions/v1/mobile/reservations/record-locator/{}?first-name={}&last-name={}".format(reservation_number, first_name, last_name) - -r = requests.get(url, headers=headers) -body = r.json() - -# Confirm this reservation is found -if 'httpStatusCode' in body and body['httpStatusCode'] == 'NOT_FOUND': - print(body['message']) -else: - now = datetime.now(pytz.utc).astimezone(get_localzone()) - tomorrow = now + timedelta(days=1) - date = now - - airport = "" - - # Get the correct flight information - for leg in body['itinerary']['originationDestinations']: - departure_time = leg['segments'][0]['departureDateTime'] - airport = leg['segments'][0]['originationAirportCode'] - date = parse(departure_time) - # Stop when we reach a future flight - if date > now: - break - - print("Flight information found, departing {} at {}".format(airport, date.strftime('%b %d %I:%M%p'))) - - # Wait until checkin time - if date > tomorrow: - delta = (date-tomorrow).total_seconds() - m, s = divmod(delta, 60) - h, m = divmod(m, 60) - print("Too early to check in. Waiting {} hours, {} minutes, {} seconds".format(trunc(h), trunc(m), s)) - time.sleep(delta) - - print("Attempting check-in...") - - # Get our passengers to get boarding passes for - passengers = [] - for passenger in body['passengers']: - passengers.append({'firstName': passenger['secureFlightName']['firstName'], 'lastName': passenger['secureFlightName']['lastName']}) - - # Check in - headers['Content-Type'] = 'application/vnd.swacorp.com.mobile.boarding-passes-v1.0+json' - url = "https://mobile.southwest.com/api/extensions/v1/mobile/reservations/record-locator/{}/boarding-passes".format(reservation_number) - r = requests.post(url, headers=headers, json={'names': passengers}) +def checkin(first_name, last_name, reservation_number): + # Find our existing record + url = "https://mobile.southwest.com/api/extensions/v1/mobile/reservations/record-locator/{}?first-name={}&last-name={}".format(reservation_number, first_name, last_name) + r = requests.get(url, headers=headers) body = r.json() - if 'httpStatusCode' in body and body['httpStatusCode'] == 'FORBIDDEN': + # Confirm this reservation is found + if 'httpStatusCode' in body and body['httpStatusCode'] == 'NOT_FOUND': print(body['message']) else: - # Spit out info about boarding number - for checkinDocument in body['passengerCheckInDocuments']: - for doc in checkinDocument['checkinDocuments']: - print("You got {}{}!".format(doc['boardingGroup'], doc['boardingGroupNumber'])) + now = datetime.now(pytz.utc).astimezone(get_localzone()) + tomorrow = now + timedelta(days=1) + date = now + + airport = "" + + # Get the correct flight information + for leg in body['itinerary']['originationDestinations']: + departure_time = leg['segments'][0]['departureDateTime'] + airport = leg['segments'][0]['originationAirportCode'] + date = parse(departure_time) + # Stop when we reach a future flight + if date > now: + break + + print("Flight information found, departing {} at {}".format(airport, date.strftime('%b %d %I:%M%p'))) + + # Wait until checkin time + if date > tomorrow: + delta = (date-tomorrow).total_seconds() + m, s = divmod(delta, 60) + h, m = divmod(m, 60) + print("Too early to check in. Waiting {} hours, {} minutes, {} seconds".format(trunc(h), trunc(m), s)) + time.sleep(delta) + + print("Attempting check-in...") + + # Get our passengers to get boarding passes for + passengers = [] + for passenger in body['passengers']: + passengers.append({'firstName': passenger['secureFlightName']['firstName'], 'lastName': passenger['secureFlightName']['lastName']}) + + # Check in + headers['Content-Type'] = 'application/vnd.swacorp.com.mobile.boarding-passes-v1.0+json' + url = "https://mobile.southwest.com/api/extensions/v1/mobile/reservations/record-locator/{}/boarding-passes".format(reservation_number) + r = requests.post(url, headers=headers, json={'names': passengers}) + + body = r.json() + + if 'httpStatusCode' in body and body['httpStatusCode'] == 'FORBIDDEN': + print(body['message']) + else: + # Spit out info about boarding number + for checkinDocument in body['passengerCheckInDocuments']: + for doc in checkinDocument['checkinDocuments']: + print("You got {}{}!".format(doc['boardingGroup'], doc['boardingGroupNumber'])) + +if __name__ == '__main__': + main(sys.argv[1], sys.argv[2], sys.argv[3]) diff --git a/gmail.py b/gmail.py new file mode 100644 index 0000000..96ff4dd --- /dev/null +++ b/gmail.py @@ -0,0 +1,79 @@ +from __future__ import print_function +import base64 +import httplib2 +import os + +from apiclient import discovery +from oauth2client import client +from oauth2client import tools +from oauth2client.file import Storage + +from scrapy.selector import Selector +from scrapy.http import HtmlResponse + +import checkin + +try: + import argparse + flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args() +except ImportError: + flags = None + +SCOPES = 'https://www.googleapis.com/auth/gmail.readonly' +CLIENT_SECRET_FILE = 'client_secret.json' +APPLICATION_NAME = 'SW Checking App' + +def get_credentials(): + """Gets valid user credentials from storage. + + If nothing has been stored, or if the stored credentials are invalid, + the OAuth2 flow is completed to obtain the new credentials. + + Returns: + Credentials, the obtained credential. + """ + home_dir = os.path.expanduser('~') + credential_dir = os.path.join(home_dir, '.credentials') + if not os.path.exists(credential_dir): + os.makedirs(credential_dir) + credential_path = os.path.join(credential_dir, + 'sw-checkin.json') + + store = Storage(credential_path) + credentials = store.get() + if not credentials or credentials.invalid: + flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES) + flow.user_agent = APPLICATION_NAME + if flags: + credentials = tools.run_flow(flow, store, flags) + else: # Needed only for compatibility with Python 2.6 + credentials = tools.run(flow, store) + print('Storing credentials to ' + credential_path) + return credentials + +def main(): + """ Pulling Southwest emails from GMail """ + credentials = get_credentials() + http = credentials.authorize(httplib2.Http()) + service = discovery.build('gmail', 'v1', http=http) + + results = service.users().messages().list(userId='me').execute() + messages = results.get('messages', []) + + if not messages: + print('No emails found.') + else: + for message in messages: + r = service.users().messages().get(userId='me',id=message['id'],format='full').execute() + if "Southwest" in r['snippet'] and r['payload']['body'].has_key('data'): + body = base64.urlsafe_b64decode(r['payload']['body']['data'].encode('UTF-8')) + selector = Selector(text=body) + divs = selector.css('span[style*="23972a"]::text').extract() + if len(divs) > 0: + conf = divs[0].replace("\r", "").replace("\n", "").strip() + name = selector.css('div[style*="13px"]::text').extract()[13] + last_name, first_name = name.strip().split('/') + checkin.checkin(first_name, last_name, conf) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 9b544fd..7f590f1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,5 @@ python-dateutil pytz requests tzlocal +google-api-python-client +scrapy