mirror of
https://github.com/pyro2927/SouthwestCheckin.git
synced 2025-12-06 01:13:19 +00:00
91 lines
3.5 KiB
Python
91 lines
3.5 KiB
Python
from time import sleep
|
|
import requests
|
|
import json
|
|
import sys
|
|
import uuid
|
|
|
|
BASE_URL = 'https://mobile.southwest.com/api/'
|
|
CHECKIN_INTERVAL_SECONDS = 0.25
|
|
MAX_ATTEMPTS = 40
|
|
|
|
|
|
class Reservation():
|
|
|
|
def __init__(self, number, first, last, verbose=False):
|
|
self.number = number
|
|
self.first = first
|
|
self.last = last
|
|
self.verbose = verbose
|
|
|
|
@staticmethod
|
|
def generate_headers():
|
|
config_js = requests.get('https://mobile.southwest.com/js/config.js')
|
|
if config_js.status_code == requests.codes.ok:
|
|
modded = config_js.text[config_js.text.index("API_KEY"):]
|
|
API_KEY = modded[modded.index(':') + 1:modded.index(',')].strip('"')
|
|
else:
|
|
print("Couldn't get API_KEY")
|
|
sys.exit(1)
|
|
|
|
USER_EXPERIENCE_KEY = str(uuid.uuid1()).upper()
|
|
# Pulled from proxying the Southwest iOS App
|
|
return {'Host': 'mobile.southwest.com', 'Content-Type': 'application/json', 'X-API-Key': API_KEY, 'X-User-Experience-Id': USER_EXPERIENCE_KEY, 'Accept': '*/*', 'X-Channel-ID': 'MWEB'}
|
|
|
|
# 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(self, url, body=None):
|
|
try:
|
|
attempts = 0
|
|
headers = Reservation.generate_headers()
|
|
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
|
|
if not self.verbose:
|
|
print(data['message'])
|
|
else:
|
|
print(r.headers)
|
|
print(json.dumps(data, indent=2))
|
|
if attempts > MAX_ATTEMPTS:
|
|
sys.exit("Unable to get data, killing self")
|
|
sleep(CHECKIN_INTERVAL_SECONDS)
|
|
continue
|
|
if self.verbose:
|
|
print(r.headers)
|
|
print(json.dumps(data, indent=2))
|
|
return data
|
|
except ValueError:
|
|
# Ignore responses with no json data in body
|
|
pass
|
|
|
|
def load_json_page(self, url, body=None):
|
|
data = self.safe_request(url, body)
|
|
if not data:
|
|
return
|
|
for k, v in list(data.items()):
|
|
if k.endswith("Page"):
|
|
return v
|
|
|
|
def with_suffix(self, uri):
|
|
return "{}{}{}?first-name={}&last-name={}".format(BASE_URL, uri, self.number, self.first, self.last)
|
|
|
|
def lookup_existing_reservation(self):
|
|
# Find our existing record
|
|
return self.load_json_page(self.with_suffix("mobile-air-booking/v1/mobile-air-booking/page/view-reservation/"))
|
|
|
|
def get_checkin_data(self):
|
|
return self.load_json_page(self.with_suffix("mobile-air-operations/v1/mobile-air-operations/page/check-in/"))
|
|
|
|
def checkin(self):
|
|
data = self.get_checkin_data()
|
|
info_needed = data['_links']['checkIn']
|
|
url = "{}mobile-air-operations{}".format(BASE_URL, info_needed['href'])
|
|
print("Attempting check-in...")
|
|
confirmation = self.load_json_page(url, info_needed['body'])
|
|
return confirmation
|