1
0
mirror of https://github.com/pyro2927/SouthwestCheckin.git synced 2025-12-06 01:13:19 +00:00

Refactored, added in pycodestyle for linting

This commit is contained in:
pyro2927
2019-02-16 14:17:43 -08:00
parent f2c4019c2f
commit 7a43ff00c8
6 changed files with 104 additions and 90 deletions

View File

@@ -4,4 +4,5 @@ python:
- "2.7.15" - "2.7.15"
- "3.7.1" - "3.7.1"
script: script:
- pytest - pytest
- pycodestyle checkin.py --show-source --ignore=E501

View File

@@ -19,84 +19,20 @@ from datetime import timedelta
from dateutil.parser import parse from dateutil.parser import parse
from docopt import docopt from docopt import docopt
from math import trunc from math import trunc
from pytz import utc
from threading import Thread from threading import Thread
from tzlocal import get_localzone from tzlocal import get_localzone
import json import openflights
import pytz import southwest
import requests
import sys import sys
import time import time
API_KEY = 'l7xxb3dcccc4a5674bada48fc6fcf0946bc8'
USER_EXPERIENCE_KEY = 'AAAA3198-4545-46F4-9A05-BB3E868BEFF5'
BASE_URL = 'https://mobile.southwest.com/api/'
CHECKIN_EARLY_SECONDS = 5 CHECKIN_EARLY_SECONDS = 5
CHECKIN_INTERVAL_SECONDS = 0.25
MAX_ATTEMPTS = 40
# Pulled from proxying the Southwest iOS App
headers = {'Host': 'mobile.southwest.com', 'Content-Type': 'application/json', 'X-API-Key': API_KEY, 'X-User-Experience-Id': USER_EXPERIENCE_KEY, 'Accept': '*/*'}
# You might ask yourself, "Why the hell does this exist?"
# Basically, there sometimes appears a "hiccup" in Southwest where things
# aren't exactly available 24-hours before, so we try a few times
def safe_request(url, body=None):
try:
attempts = 0
while True:
if body is not None:
r = requests.post(url, headers=headers, json=body)
else:
r = requests.get(url, headers=headers)
data = r.json()
if 'httpStatusCode' in data and data['httpStatusCode'] in ['NOT_FOUND', 'BAD_REQUEST', 'FORBIDDEN']:
attempts += 1
print(data['message'])
if attempts > MAX_ATTEMPTS:
sys.exit("Unable to get data, killing self")
time.sleep(CHECKIN_INTERVAL_SECONDS)
continue
return data
except ValueError:
# Ignore responses with no json data in body
pass
def lookup_existing_reservation(number, first, last):
# Find our existing record
url = "{}mobile-misc/v1/mobile-misc/page/view-reservation/{}?first-name={}&last-name={}".format(BASE_URL, number, first, last)
data = safe_request(url)
return data['viewReservationViewPage']
def get_checkin_data(number, first, last):
url = "{}mobile-air-operations/v1/mobile-air-operations/page/check-in/{}?first-name={}&last-name={}".format(BASE_URL, number, first, last)
data = safe_request(url)
return data['checkInViewReservationPage']
def checkin(number, first, last):
data = get_checkin_data(number, first, last)
info_needed = data['_links']['checkIn']
url = "{}mobile-air-operations{}".format(BASE_URL, info_needed['href'])
print("Attempting check-in...")
return safe_request(url, info_needed['body'])['checkInConfirmationPage']
def send_notification(checkindata, emailaddr=None, mobilenum=None):
info_needed = checkindata['_links']['boardingPasses']
url = "{}mobile-air-operations{}".format(BASE_URL, info_needed['href'])
mbpdata = safe_request(url, info_needed['body'])
info_needed = mbpdata['checkInViewBoardingPassPage']['_links']
url = "{}mobile-air-operations{}".format(BASE_URL, info_needed['href'])
if emailaddr:
info_needed['body']['mediaType'] = 'EMAIL'
info_needed['body']['emailAddress'] = emailaddr
if mobilenum:
info_needed['body']['mediaType'] = 'SMS'
info_needed['body']['phoneNumber'] = mobilenum
print("Attempting to send boarding pass...")
safe_request(url, info_needed['body'])
def schedule_checkin(flight_time, number, first, last, email, mobile): def schedule_checkin(flight_time, number, first, last, email, mobile):
checkin_time = flight_time - timedelta(days=1) checkin_time = flight_time - timedelta(days=1)
current_time = datetime.now(pytz.utc).astimezone(get_localzone()) current_time = datetime.now(utc).astimezone(get_localzone())
# check to see if we need to sleep until 24 hours before flight # check to see if we need to sleep until 24 hours before flight
if checkin_time > current_time: if checkin_time > current_time:
# calculate duration to sleep # calculate duration to sleep
@@ -106,21 +42,21 @@ def schedule_checkin(flight_time, number, first, last, email, mobile):
h, m = divmod(m, 60) h, m = divmod(m, 60)
print("Too early to check in. Waiting {} hours, {} minutes, {} seconds".format(trunc(h), trunc(m), s)) print("Too early to check in. Waiting {} hours, {} minutes, {} seconds".format(trunc(h), trunc(m), s))
time.sleep(delta) time.sleep(delta)
data = checkin(number, first, last) data = southwest.checkin(number, first, last)
for flight in data['flights']: for flight in data['flights']:
for doc in flight['passengers']: for doc in flight['passengers']:
print("{} got {}{}!".format(doc['name'], doc['boardingGroup'], doc['boardingPosition'])) print("{} got {}{}!".format(doc['name'], doc['boardingGroup'], doc['boardingPosition']))
if email: if email:
send_notification(data, emailaddr=email) southwest.send_notification(data, emailaddr=email)
elif mobile: elif mobile:
send_notification(data, mobilenum=mobile) southwest.send_notification(data, mobilenum=mobile)
def auto_checkin(reservation_number, first_name, last_name, email=None, mobile=None): def auto_checkin(reservation_number, first_name, last_name, email=None, mobile=None):
body = lookup_existing_reservation(reservation_number, first_name, last_name) body = southwest.lookup_existing_reservation(reservation_number, first_name, last_name)
# Get our local current time # Get our local current time
now = datetime.now(pytz.utc).astimezone(get_localzone()) now = datetime.now(utc).astimezone(get_localzone())
tomorrow = now + timedelta(days=1) tomorrow = now + timedelta(days=1)
threads = [] threads = []
@@ -130,14 +66,7 @@ def auto_checkin(reservation_number, first_name, last_name, email=None, mobile=N
# calculate departure for this leg # calculate departure for this leg
airport = "{}, {}".format(leg['departureAirport']['name'], leg['departureAirport']['state']) airport = "{}, {}".format(leg['departureAirport']['name'], leg['departureAirport']['state'])
takeoff = "{} {}".format(leg['departureDate'], leg['departureTime']) takeoff = "{} {}".format(leg['departureDate'], leg['departureTime'])
tzrequest = {'iata': leg['departureAirport']['code'], airport_tz = openflights.timezone_for_airport(leg['departureAirport']['code'])
'country': 'ALL',
'db': 'airports',
'iatafilter': 'true',
'action': 'SEARCH',
'offset': '0'}
tzresult = requests.post("https://openflights.org/php/apsearch.php", tzrequest)
airport_tz = pytz.timezone(json.loads(tzresult.text)['airports'][0]['tz_id'])
date = airport_tz.localize(datetime.strptime(takeoff, '%Y-%m-%d %H:%M')) date = airport_tz.localize(datetime.strptime(takeoff, '%Y-%m-%d %H:%M'))
if date > now: if date > now:
# found a flight for checkin! # found a flight for checkin!
@@ -158,10 +87,10 @@ def auto_checkin(reservation_number, first_name, last_name, email=None, mobile=N
threads.remove(t) threads.remove(t)
break break
if __name__ == '__main__':
arguments = docopt(__doc__, version='Southwest Checkin 0.2')
# work work if __name__ == '__main__':
arguments = docopt(__doc__, version='Southwest Checkin 0.2')
reservation_number = arguments['CONFIRMATION_NUMBER'] reservation_number = arguments['CONFIRMATION_NUMBER']
first_name = arguments['FIRST_NAME'] first_name = arguments['FIRST_NAME']
last_name = arguments['LAST_NAME'] last_name = arguments['LAST_NAME']

View File

@@ -2,6 +2,7 @@ import checkin
import json import json
import pytest import pytest
import requests import requests
import southwest
from datetime import datetime, timedelta from datetime import datetime, timedelta
from pytz import timezone, utc from pytz import timezone, utc
from tzlocal import get_localzone from tzlocal import get_localzone
@@ -45,16 +46,16 @@ def test_notifications(requests_mock, mocker):
requests_mock.register_uri('POST', '/api/mobile-air-operations/v1/mobile-air-operations/page/check-in', text=data) requests_mock.register_uri('POST', '/api/mobile-air-operations/v1/mobile-air-operations/page/check-in', text=data)
data = json.loads(data) data = json.loads(data)
requests_mock.register_uri('POST', '/php/apsearch.php', text=template_time('fixtures/openflights.json')) requests_mock.register_uri('POST', '/php/apsearch.php', text=template_time('fixtures/openflights.json'))
mocked_checkin = mocker.patch('checkin.send_notification') mocked_checkin = mocker.patch('southwest.send_notification')
t = datetime.now(utc).astimezone(get_localzone()) + timedelta(minutes=5) t = datetime.now(utc).astimezone(get_localzone()) + timedelta(minutes=5)
try: try:
checkin.schedule_checkin(t, 'XXXX', 'John', 'Smith', None, None) checkin.schedule_checkin(t, 'XXXX', 'John', 'Smith', None, None)
checkin.send_notification.assert_not_called() southwest.send_notification.assert_not_called()
checkin.schedule_checkin(t, 'XXXX', 'John', 'Smith', 'test@example.com', None) checkin.schedule_checkin(t, 'XXXX', 'John', 'Smith', 'test@example.com', None)
checkin.send_notification.assert_called_once_with(data['checkInConfirmationPage'], emailaddr='test@example.com') southwest.send_notification.assert_called_once_with(data['checkInConfirmationPage'], emailaddr='test@example.com')
checkin.send_notification.reset_mock() southwest.send_notification.reset_mock()
checkin.schedule_checkin(t, 'XXXX', 'John', 'Smith', None, '1234567890') checkin.schedule_checkin(t, 'XXXX', 'John', 'Smith', None, '1234567890')
checkin.send_notification.assert_called_once_with(data['checkInConfirmationPage'], mobilenum='1234567890') southwest.send_notification.assert_called_once_with(data['checkInConfirmationPage'], mobilenum='1234567890')
except: except:
pytest.fail("Error checking in") pytest.fail("Error checking in")

14
openflights.py Normal file
View File

@@ -0,0 +1,14 @@
import requests
import json
import pytz
def timezone_for_airport(airport_code):
tzrequest = {'iata': airport_code,
'country': 'ALL',
'db': 'airports',
'iatafilter': 'true',
'action': 'SEARCH',
'offset': '0'}
tzresult = requests.post("https://openflights.org/php/apsearch.php", tzrequest)
airport_tz = pytz.timezone(json.loads(tzresult.text)['airports'][0]['tz_id'])
return airport_tz

View File

@@ -1,5 +1,6 @@
datetime datetime
docopts docopts
pycodestyle
pytest pytest
pytest-mock pytest-mock
python-dateutil python-dateutil

68
southwest.py Normal file
View File

@@ -0,0 +1,68 @@
import requests
import sys
API_KEY = 'l7xxb3dcccc4a5674bada48fc6fcf0946bc8'
USER_EXPERIENCE_KEY = 'AAAA3198-4545-46F4-9A05-BB3E868BEFF5'
BASE_URL = 'https://mobile.southwest.com/api/'
CHECKIN_INTERVAL_SECONDS = 0.25
MAX_ATTEMPTS = 40
# Pulled from proxying the Southwest iOS App
headers = {'Host': 'mobile.southwest.com', 'Content-Type': 'application/json', 'X-API-Key': API_KEY, 'X-User-Experience-Id': USER_EXPERIENCE_KEY, 'Accept': '*/*'}
# You might ask yourself, "Why the hell does this exist?"
# Basically, there sometimes appears a "hiccup" in Southwest where things
# aren't exactly available 24-hours before, so we try a few times
def safe_request(url, body=None):
try:
attempts = 0
while True:
if body is not None:
r = requests.post(url, headers=headers, json=body)
else:
r = requests.get(url, headers=headers)
data = r.json()
if 'httpStatusCode' in data and data['httpStatusCode'] in ['NOT_FOUND', 'BAD_REQUEST', 'FORBIDDEN']:
attempts += 1
print(data['message'])
if attempts > MAX_ATTEMPTS:
sys.exit("Unable to get data, killing self")
time.sleep(CHECKIN_INTERVAL_SECONDS)
continue
return data
except ValueError:
# Ignore responses with no json data in body
pass
def lookup_existing_reservation(number, first, last):
# Find our existing record
url = "{}mobile-misc/v1/mobile-misc/page/view-reservation/{}?first-name={}&last-name={}".format(BASE_URL, number, first, last)
data = safe_request(url)
return data['viewReservationViewPage']
def get_checkin_data(number, first, last):
url = "{}mobile-air-operations/v1/mobile-air-operations/page/check-in/{}?first-name={}&last-name={}".format(BASE_URL, number, first, last)
data = safe_request(url)
return data['checkInViewReservationPage']
def checkin(number, first, last):
data = get_checkin_data(number, first, last)
info_needed = data['_links']['checkIn']
url = "{}mobile-air-operations{}".format(BASE_URL, info_needed['href'])
print("Attempting check-in...")
return safe_request(url, info_needed['body'])['checkInConfirmationPage']
def send_notification(checkindata, emailaddr=None, mobilenum=None):
info_needed = checkindata['_links']['boardingPasses']
url = "{}mobile-air-operations{}".format(BASE_URL, info_needed['href'])
mbpdata = safe_request(url, info_needed['body'])
info_needed = mbpdata['checkInViewBoardingPassPage']['_links']
url = "{}mobile-air-operations{}".format(BASE_URL, info_needed['href'])
if emailaddr:
info_needed['body']['mediaType'] = 'EMAIL'
info_needed['body']['emailAddress'] = emailaddr
if mobilenum:
info_needed['body']['mediaType'] = 'SMS'
info_needed['body']['phoneNumber'] = mobilenum
print("Attempting to send boarding pass...")
safe_request(url, info_needed['body'])