Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bbarman4u/d6c337572b476af14a313e40d63a2e84 to your computer and use it in GitHub Desktop.
Save bbarman4u/d6c337572b476af14a313e40d63a2e84 to your computer and use it in GitHub Desktop.
Auto Check-In to Southwest Flights
// ==UserScript==
// @name Auto Check-In to Southwest Flights
// @namespace https://gist.github.com/ryanizzo/058829a5fafe733bd876410db7a1e699
// @version 1.91
// @author Nicholas Buroojy (http://userscripts.org/users/83813)
// @contributor Ryan Izzo (http://www.ryanizzo.com)
// @contributor JR Hehnly (http://www.okstorms.com @stormchasing)
// @contributor Trevor McClellan (github.com/trevormcclellan)
// @contributor Martin Medina (github.com/medinm0)
// @description Automatically check in to Southwest Airline flights at the appropriate time.
// @include https://www.southwest.com/air/check-in/index.html*
// @include https://www.southwest.com/flight/selectCheckinDocDelivery.html*
// @include https://www.southwest.com/air/check-in/review.html*
// @include https://www.southwest.com/air/check-in/confirmation.html*
// @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
/* This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
History
10/2012 v1.2 Ryan Izzo (ryanizzo.com)
Updated to use new Southwest Check In page, added validation
7/2014 v1.3 Ryan Izzo (ryanizzo.com)
Moved script to GitHub since UserScripts appears dead
10/2014 v1.4 JR Hehnly (@stormchasing)
Added phone number entry to auto-text boarding pass docs to mobile device
3/28/2016 v1.5.1 Trevor McClellan (github.com/trevormcclellan)
Fixed phone number entry system
9/2017 v1.6 JR Hehnly (@stormchasing)
Initial changes to handle new Southwest confirmation lookup page
10/03/2017 v1.6.1 JR Hehnly (@stormchasing)
Got the script to a state where check-in works, but no auto-text boarding pass yet.
Have not determined what event listeners need to be triggered in the phone number form field.
Submits sucessfully if last phone number character is manually typed, but fails validation if it's only filled by the script.
12/2017 v1.7 Ryan Izzo (ryanizzo.com)
- Matching visual styles of page.
- Making countdown timer larger.
- Using constants for page values (hopefully this makes it easier to udpdate later when the pages change).
- Aggregating validation errors to avoid cases where you may have additional, unreported errors preventing submission.
- Commenting out confirmation page for texting boarding pass as this doesnt appear to be fully working.
- Using single input for date and time
2/2/2018 v1.8 Shivang Patel (@shivangp)
- Fixed autoPassengerPage function - now does not have check box and only checkin button
- changed default auto set time to :05 seconds - :02 seems to early
- clear displayCountdown interval so does not cause error on other pages
- removed timeout setTime for displayCountdownWrapper and just called directly
2/19/2019 v1.9 Shivang Patel (@shivangp)
- Added interval to continously click submit button after timer is done, in case time is not perfect
7/3/2019 v1.91 Martin Medina (@medinm0)
- Fixed endless looping alert on Check In page
TODO: Use Southwest's server's time instead of the client's clock.
TODO: Test select passenger page.
*/
///////////// CONSTANTS ////////////////
// Check In page
const CHECKIN_FORM_DIV_CLASS = "air-reservation-confirmation-number-search-form";
const CHECKIN_CONF_INPUT_ID = "confirmationNumber";
const CHECKIN_FIRSTNAME_INPUT_ID = "passengerFirstName";
const CHECKIN_LASTNAME_INPUT_ID = "passengerLastName";
const SUBMIT_BUTTON_ID = "form-mixin--submit-button";
const FORM_HEADING_CLASS = 'heading heading_medium retrieve-reservation-form-container--form-title';
const FORM_LABEL_CLASS = 'form-control--label';
const FORM_TEXT_CLASS = 'form-control--label-text';
const REQUIRED_CLASS = 'form--required-indicator';
const BUTTON_DIV_CLASS = 'form-container--search-block';
const BUTTON_CLASS = 'actionable actionable_button actionable_large-button actionable_no-outline actionable_primary button submit-button';
// Review page
const REVIEW_SUBMIT_BUTTON_CLASS = "actionable actionable_button actionable_large-button actionable_no-outline actionable_primary button submit-button air-check-in-review-results--check-in-button";
// Confirmation page
//const CONF_TEXT_ID = "textBoardingPass";
///////////// CHECK IN PAGE ////////////////
var globalDisplayCountdownInterval;
var globalSubmitDate;
//var allDone = false;
var submitNowInterval;
/**
* @brief Submit the check in form on the Southwest Check In Online page.
*/
function submitNow()
{
try{
var submitButton = document.getElementById(SUBMIT_BUTTON_ID);
submitButton.click();
}
catch(e){
//stop attempting to submitNow if there is an error preventing endless alert loop
window.clearInterval(submitNowInterval);
//alert('submitNow: An error has occurred: '+ e.message);
}
}
/**
* @brief Display the countdown.
*
* TODO: Some formatting is wrong eg ("1:0:12" means 1 hour and 12 seconds remain). Make sure everything is represented with 2 digits.
*/
function displayCountdown()
{
try{
var countdown = document.getElementById("countdown");
var timeRemain = globalSubmitDate - new Date();
var days = Math.floor(timeRemain / (1000 * 60 * 60 * 24));
var hours = Math.floor(timeRemain / (1000 * 60 * 60)) % 24;
var minutes = Math.floor(timeRemain / (1000 * 60)) % 60;
//round to the nearest second
var seconds = Math.round(timeRemain / 1000) % 60;
//Don't print negative time.
if (hours < 0 || minutes < 0 || seconds < 0)
{
countdown.nodeValue = "Checking In...";
return;
}
var time = '';
//If 0 days remain, omit them.
if (days !== 0)
time += days + "d ";
//If 0 hours remain, omit them.
if (hours !== 0)
time += hours + "h ";
//Add padding to minute
if (minutes !==0 )
//area.innerHTML += "0";
time += minutes + "m ";
//Add padding to second
//if (seconds < 10)
//area.innerHTML += "0";
time += seconds;
time += "s";
countdown.textContent = time;
}
catch(e){
// has the page changed?
if(/review/.test(document.location.href))
{
/* clear the display countdown interval - otherwise will get error when trying to update */
clearInterval(globalDisplayCountdownInterval);
autoPassengerPage();
return;
}
/*else if(/confirmation/.test(document.location.href))
{
if (allDone === false)
{
autoTextBoardingDocs();
}
return;
}*/
alert('displayCountdown: An error has occurred: ' +e.message);
}
}
/**
* @brief Updates the countdown every second.
*/
function displayCountdownWrapper()
{
try{
globalDisplayCountdownInterval = window.setInterval(displayCountdown, 1000);
}
catch(e){
alert('displayCountdownWrapper:" An error has occurred: ' +e.message);
}
}
/**
* @brief Begins the delay at the next even second.
*/
function beginDelay()
{
try{
var confNumber = document.getElementById(CHECKIN_CONF_INPUT_ID).value;
var firstName = document.getElementById(CHECKIN_FIRSTNAME_INPUT_ID).value;
var lastName = document.getElementById(CHECKIN_LASTNAME_INPUT_ID).value;
var date = document.getElementById("date-input").value;
var time = document.getElementById("time-input").value;
// var phoneArea = document.getElementById("phoneArea").value;
// var phonePrefix = document.getElementById("phonePrefix").value;
// var phoneNumber = document.getElementById("phoneNumber").value;
var errors = "";
if(confNumber === "" || firstName === "" || lastName === "" )
errors += "- Must fill out Confirmation Number, First Name and Last Name.\r";
if(confNumber.length != 6 )
errors += "- Confirmation Number must be 6 characters.\r";
if(!isValidDate(date))
errors += "- Date is not valid.\r";
if(!isValidTime(time))
errors += "- Time is not valid.\r";
if(isValidDate(date) && isValidTime(time)){
//Build the date
var submitDate = new Date(date);
// Parse the date parts to integers
var parts = time.split(":");
var hour = parseInt(parts[0], 10);
var min = parseInt(parts[1], 10);
var sec = parseInt(parts[2], 10);
submitDate.setHours(hour, min, sec, 0);
var now = new Date();
var msRemaining = submitDate - now;
var maxDays = 14;
if(msRemaining < 0)
errors += "- Date/Time must be in the future." + submitDate;
else if(msRemaining > maxDays * 1000 * 60 * 60 * 24)
errors += "- Date/Time cannot be more than " + maxDays + " days in the future." + msRemaining;
}
/*if (phoneArea.search(/\d\d\d/g) == -1 || phonePrefix.search(/\d\d\d/g) == -1 || phoneNumber.search(/\d\d\d\d/g) == -1) {
errors += "- Invalid phone number.\r";
}
else{
//save the text number for later
GM_setValue("phoneArea", phoneArea);
GM_setValue("phonePrefix", phonePrefix);
GM_setValue("phoneNumber", phoneNumber);
}*/
if(errors.length == 0){
//Install the timeout to submit the form.
window.setTimeout(function() {
submitNow();
submitNowInterval = window.setInterval(submitNow, 500);
}, msRemaining);
globalSubmitDate = submitDate;
//Install a short term timeout to call the countdown wrapper at the beginning of the next second.
//window.setTimeout(displayCountdownWrapper, msRemaining % 1000);
displayCountdownWrapper();
}
else{
alert(errors);
}
}
catch(e){
alert('beginDelay: An error has occurred: '+ e.message);
}
}
//Validates that the input string is a valid date formatted as "mm/dd/yyyy"
function isValidDate(dateString)
{
// First check for the pattern
if(!/^\d{1,2}\/\d{1,2}\/\d{4}$/.test(dateString))
return false;
// Parse the date parts to integers
var parts = dateString.split("/");
var day = parseInt(parts[1], 10);
var month = parseInt(parts[0], 10);
var year = parseInt(parts[2], 10);
// Check the ranges of month and year
if(year < 2017 || year > 2100 || month == 0 || month > 12)
return false;
var monthLength = [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ];
// Adjust for leap years
if(year % 400 == 0 || (year % 100 != 0 && year % 4 == 0))
monthLength[1] = 29;
// Check the range of the day
return day > 0 && day <= monthLength[month - 1];
}
//Validates that the input string is a valid date formatted as "mm/dd/yyyy"
function isValidTime(timeString)
{
// First check for the pattern
if(!/^\d{1,2}:\d{1,2}:\d{1,2}$/.test(timeString))
return false;
// Parse the date parts to integers
var parts = timeString.split(":");
var hour = parseInt(parts[0], 10);
var min = parseInt(parts[1], 10);
var sec = parseInt(parts[2], 10);
// Check the ranges of month and year
if(hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || sec > 59)
return false;
return true;
}
/**
* @brief Edits the check in page; Adds Date, time, and Auto Check In button
*
* TODO Dater and Time picker
*/
function checkInPageFormEdit()
{
try{
var delayDiv = document.createElement("div");
delayDiv.setAttribute('id','checkInDelay');
var dateSelect = document.createElement("span");
dateSelect.setAttribute('id','date-select');
//The big label at the top of the menu
var mainLabel = document.createElement("h2");
mainLabel.setAttribute('class',FORM_HEADING_CLASS);
var mainLabelDiv = document.createElement("div");
mainLabelDiv.textContent = 'Set Check In Date and Time';
mainLabel.appendChild(mainLabelDiv);
dateSelect.innerHTML += "<br/>";
dateSelect.appendChild(mainLabel);
//The date portion.
var today = new Date();
var dateLabel = document.createElement("label");
dateLabel.innerHTML = "<span class=\""+FORM_LABEL_CLASS+"\"><span class=\""+FORM_TEXT_CLASS+"\">Date: (mm/dd/yyyy)</span><span class=\"" +REQUIRED_CLASS+"\">*</span></span>";
var dateInput = document.createElement("input");
dateInput.setAttribute('id','date-input');
dateInput.setAttribute('class','input--text');
dateInput.setAttribute('type','text');
dateInput.setAttribute('maxlength','10');
dateInput.setAttribute('value',(today.getMonth()+1)+'/'+today.getDate()+'/'+today.getFullYear());
// dateInput.setAttribute('onfocus','if(this.value==\'mm\') this.value=\'\';');
dateInput.setAttribute('tabindex','7');
dateSelect.appendChild(dateLabel);
dateSelect.appendChild(dateInput);
// The time portion.
var timeLabel = document.createElement("label");
timeLabel.innerHTML = "<span class=\""+FORM_LABEL_CLASS+"\"><span class=\""+FORM_TEXT_CLASS+"\">Time: (24-hour format hh:mm:ss)</span><span class=\"" +REQUIRED_CLASS+"\">*</span></span>";
var timeInput = document.createElement("input");
timeInput.setAttribute('id','time-input');
timeInput.setAttribute('class','input--text');
timeInput.setAttribute('type','text');
timeInput.setAttribute('maxlength','8');
timeInput.setAttribute('value',(today.getHours()+1)+':'+today.getMinutes()+':05');
// timeInput.setAttribute('onfocus','if(this.value==\'mm\') this.value=\'\';');
timeInput.setAttribute('tabindex','8');
dateSelect.appendChild(timeLabel);
dateSelect.appendChild(timeInput);
delayDiv.appendChild(dateSelect);
//auto-text boarding pass section
/*var autoTextArea = document.createElement("div");
var textLabel = document.createElement("label");
textLabel.innerHTML = " Boarding pass text number:";
var phoneArea = document.createElement("input");
phoneArea.setAttribute('id','phoneArea');
phoneArea.setAttribute('type','text');
phoneArea.setAttribute('maxlength','3');
phoneArea.setAttribute('size','3');
phoneArea.setAttribute('value', GM_getValue("phoneArea") !== undefined ? GM_getValue("phoneArea") : '');
phoneArea.setAttribute('tabindex','11');
var phonePrefix = document.createElement("input");
phonePrefix.setAttribute('id','phonePrefix');
phonePrefix.setAttribute('type','text');
phonePrefix.setAttribute('maxlength','3');
phonePrefix.setAttribute('size','3');
phonePrefix.setAttribute('value', GM_getValue("phonePrefix") !== undefined ? GM_getValue("phonePrefix") : '');
phonePrefix.setAttribute('tabindex','12');
var phoneNumber = document.createElement("input");
phoneNumber.setAttribute('id','phoneNumber');
phoneNumber.setAttribute('type','text');
phoneNumber.setAttribute('maxlength','4');
phoneNumber.setAttribute('size','4');
phoneNumber.setAttribute('value', GM_getValue("phoneNumber") !== undefined ? GM_getValue("phoneNumber") : '');
phoneNumber.setAttribute('tabindex','13');
autoTextArea.innerHTML += "<br/>";
autoTextArea.appendChild(textLabel);
autoTextArea.innerHTML += "<br/>";
autoTextArea.innerHTML += "(";
autoTextArea.appendChild(phoneArea);
autoTextArea.innerHTML += ") ";
autoTextArea.appendChild(phonePrefix);
autoTextArea.innerHTML += " - ";
autoTextArea.appendChild(phoneNumber);
delayDiv.appendChild(autoTextArea);
*/
// The area that displays how much time remains before the form is submitted.
delayDiv.innerHTML += "<br/><br />";
var countdownH2 = document.createElement("h2");
countdownH2.setAttribute('class',FORM_HEADING_CLASS);
var countdownDiv = document.createElement("div");
countdownDiv.setAttribute('id','countdown');
countdownDiv.textContent = 'Click to Start ->';
countdownH2.appendChild(countdownDiv);
delayDiv.appendChild(countdownH2);
// Auto Check In button
var delayButtonDiv = document.createElement("div");
delayButtonDiv.setAttribute('class',BUTTON_DIV_CLASS);
var delayButton = document.createElement("button");
delayButton.setAttribute('class',BUTTON_CLASS);
delayButton.setAttribute('type','button');
delayButton.addEventListener("click", beginDelay, true);
delayButton.setAttribute('tabindex','14');
var delayButtonSpan = document.createElement("span");
delayButtonSpan.textContent = 'Auto Check in';
delayButton.appendChild(delayButtonSpan);
delayButtonDiv.appendChild(delayButton);
delayDiv.appendChild(delayButtonDiv);
document.getElementsByClassName(CHECKIN_FORM_DIV_CLASS)[0].appendChild(delayDiv);
}
catch(e){
alert('checkInPageFormEdit: An error has occurred: ' +e.message);
}
}
///////////// SELECT PASSENGER PAGE ////////////////
//automatically select all passengers and submit the form
function autoPassengerPage()
{
try{
//find error notification
if(document.title == "Error")
return;
// Check all the check boxes.
//var node_list = document.getElementsByTagName('input');
//for (var i = 0; i < node_list.length; i++) {
// var node = node_list[i];
// if (node.getAttribute('type') == 'checkbox') {
// node.checked = true;
// }
//}
//Click the print button
var button = document.getElementsByClassName('air-check-in-review-results--check-in-button')[0];
button.click();
}
catch(e){
alert('autoPassengerPage: An error has occurred: '+ e.message);
}
}
///////////// BOARDING DOC DELIVERY PAGE ////////////////
/*function autoTextBoardingDocs()
{
try{
//find error notification
if (document.title == "Error")
return;
//click the Text button
var button = document.getElementsByClassName(REVIEW_SUBMIT_BUTTON_CLASS)[0];
button.click();
window.setTimeout(waitForSendButton, 500);
}
catch(e){
alert('autoTextBoardingDocs: An error has occurred: '+ e.message);
}
}
function waitForTextBoardingPass() {
if(document.getElementById("textBoardingPass") === null) {
window.setTimeout(waitForTextBoardingPass, 100);
} else {
document.getElementById("textBoardingPass").focus();
document.getElementById("textBoardingPass").value = parseInt(GM_getValue("phoneArea") + GM_getValue("phonePrefix") + GM_getValue("phoneNumber"));
waitForSendButton();
}
}
function waitForSendButton() {
if(document.getElementById(SUBMIT_BUTTON_ID) === null || document.getElementById(CONF_TEXT_ID).value != GM_getValue("phoneArea") + GM_getValue("phonePrefix") + GM_getValue("phoneNumber")) {
document.getElementById(CONF_TEXT_ID).focus();
document.getElementById(CONF_TEXT_ID).value = parseInt(GM_getValue("phoneArea") + GM_getValue("phonePrefix") + GM_getValue("phoneNumber"));
window.setTimeout(waitForSendButton, 100);
} else {
//document.getElementById(SUBMIT_BUTTON_ID).focus();
//document.getElementById(SUBMIT_BUTTON_ID).click();
allDone = true;
}
}*/
//case of the select boarding pass page (regex match the url)
if(/check-in/.test(document.location.href))
{
checkInPageFormEdit();
} else {
window.clearInterval(submitNowInterval);
alert("interval cleared");
}
/*else if(/confirmation/.test(document.location.href))
{
autoTextBoardingDocs();
}*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment