Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Companion Pass Check #78

Merged
merged 22 commits into from
Apr 22, 2023
Merged

Conversation

fusionneo
Copy link
Contributor

When a companion is added to a ticket, this causes fare_checker.py to silently crash without reporting an error. When a companion is added, there is no changeFlightPage link as the only way to make a change to the flight is to first cancel the companion's ticket, then make changes, and finally readd your companion.

The reason the script crashes silently is due to flight_retriever.py only catching a RequestError in the _check_flight_fares method. I did notice that fare_checker.py can also raise a KeyError in the _get_matching_fare method, which would also currently be uncaught in flight_retriever.py.

The Good:

  • This is a relatively easy fix. We can check if a companion has been added after retrieving the reservation by checking the response after viewing the reservation. I have added a method to check the response for a companion, and to raise a ValueError if a companion is present (there might be a better error we can use here, or we can even add a custom error in utils) I also changed flight_retriever to catch all exceptions in the _check_flight_fares method, although we can just add the exceptions we know about to the flight_retriever method.

The Bad:

  • Properly adding companion pass support would require a lot of changes to be made to fare_checker. In my testing, the only way to compare fares is by pretending to make a new booking on the same route and comparing the current cost against the price you paid.
  • Making matters worse, as far as I can tell there is no way to view the price you paid for a flight without access to the changeFlightPage. The view reservation page does not indicate the price a user paid for their flight. The only way around this would be to have the user manually input the price they paid - and the currency, whether it's USD or points - so we can properly compare flight prices.

I am content to work on a method to compare flight prices for flights with a companion added. I already have a branch in my fork that checks for the same flight and makes sure at least 8 tickets are left (this way, if they're not, I know I need to rebook my companion ASAP) as a band aid solution. It would be easy to change that to check the flight price instead of the passenger availability. However, I'm unsure of the best way to proceed in terms of getting the cost of a flight with a companion added from the user and then storing that for later use.

fusionneo and others added 16 commits April 19, 2023 23:40
I also removed BASE_URL because it is unused
Sometimes, the Southwest API could return part of a URL that has a
leading slash. When creating the full URL, a double slash would appear,
making the request fail.

Here the double slash is first replaced and then a leading slashed is
removed. This will ensure that the URL will not have any double slashes
(except for the https:// protocol).
@jdholtz
Copy link
Owner

jdholtz commented Apr 21, 2023

Thanks for submitting this and going into a lot of detail on the issue.

First, I don't want to catch all exceptions. My reason is that I want to know what other issues the fare checker could have (or things that weren't thought of before). If an unexpected error occurs, the user can report it and the necessary steps can be taken to handle it. For example, we wouldn't necessarily know the companion pass was an issue if all exceptions were caught, as it would log a general message and continue. Also, I left the KeyError uncaught on purpose because I want to know if it is possible for that to be reached.

Instead, another except block can be to catch the ValueError (and it would be nice to create a custom CompanionError so we don't catch all ValueErrors)

this causes fare_checker.py to silently crash without reporting an error.

What do you mean by silently crash? Does it give any output or does it just terminate?

I'm not familiar with how Companion Passes work (besides what I have read on Southwest's website). Does the Change button not appear on the view reservation page? Also, will greyBoxMessage only ever be None when a companion is added (although, maybe there isn't a better way to find this information out).

@jdholtz
Copy link
Owner

jdholtz commented Apr 21, 2023

I now understand what you mean by the exception not producing any output. Multiprocessing seems to get halted by the exception and the entire execution pauses.

I regress on my statement about not adding a global Exception catcher. My new plan is to individually handle the errors we expect (Request Error and Companion Error) and print the traceback of unexpected errors, but still continue.

I can handle the above in a later commit (the global Exception catcher).

@fusionneo
Copy link
Contributor Author

fusionneo commented Apr 21, 2023

What do you mean by silently crash? Does it give any output or does it just terminate?

Right now, when an uncaught exception occurs in fare_checker, there is no output. The terminal just seems to freeze. You can test this yourself pretty easily: in the first line of the _get_flight_price method of fare_checker, raise an error that is currently uncaught (say, ValueError). Instead of the program crashing or an error message being output to the terminal, this is what I get:

2023-04-21 16:39:50 DEBUG Process-1[fare_checker]: Checking current price for flight
2023-04-21 16:39:50 DEBUG Process-1:2[checkin_handler]: Sleeping until ten minutes before check-in...

The program doesn't crash or output an error, so the user doesn't know anything is wrong. This is why when I first commented on the repo I thought the smart_sleep method was causing issues and it took further debugging to identify the companion I had on my ticket was the root cause.

I'm not familiar with how Companion Passes work (besides what I have read on Southwest's website). Does the Change button not appear on the view reservation page?

Correct, the change button is replaced by a gray box.

companion_pass

Here's a screenshot attached so you can see what it looks like.

I can mess around with the API a little bit more to check if I can specifically grab if there's a companion associated, as I have a feeling that greyBoxMessage can appear for more than just a companion.

EDIT: I see you replied as I was making my post and my code changes, so a lot of this is redundant. Keeping it for posterity's sake.

@fusionneo
Copy link
Contributor Author

fusionneo commented Apr 21, 2023

It seems that on the response after viewing the reservation, there's a key that looks like this:

response["viewReservationViewPage"]["companion"]

It is set to null if there's not a companion added, and a confirmation number if there is a companion added. If you don't have access to companion pass, can you check if that key exists for you - and if it is set to null? If so, that's a much better way of checking.

EDIT: It seems that the response I get when proxying Bluestacks vs the response the script is getting is different. Companion returns a null value for me even on a flight with a booked companion via the script (whereas BlueStacks I get a confirmation number), but the greyBoxMessage correctly sees that there is a companion. Strange.

@jdholtz
Copy link
Owner

jdholtz commented Apr 21, 2023

My suggestion is to not support companion tickets (if my suggestion below does not work). For one, Southwest says the companion needs to be canceled before changing the flight, so it really doesn't even seem possible to do a fare check on their website (without canceling the companion which should not be done with this script at all).

Another reason is that I also looked if it is possible to get the price you paid for your flight. I found this forum from the Southwest community (it was written in 2017, but it seems to still be correct). The only ways suggested were to use the change flight page. Last, it isn't reasonable to have the user input the price because this script is meant to be run fully automatically (scheduling of flights, check-ins, fare checks). This would require manual intervention which takes away part of the purpose of the script.

It seems that on the response after viewing the reservation, there's a key that looks like this

That is a perfect way to do it. Without a companion, this shows up as null for me. If you can't get that to work, you can also test if greyBoxMessage both is available and includes the companion word. That would basically ensure there is a companion.

Potential Way To The Change Flight Page

Although the change flight page is not available, the contactInformation key might still be available (maybe earlyBird is still available too). For this, the passengerSearchToken is the same as the change flight page. Could you test hardcoding the URL to /v1/mobile-air-booking/page/flights/change/current/{confirmation_number}? The query would also need to be built using the passenger-search-token (The entire query can be grabbed if earlyBird is available and the following can be skipped):

{
    "first-name": self.flight_retriever.first_name,
    "last-name": self.flight_retriever.last_name,
    "passenger-search-token": passenger_search_token
}

@fusionneo
Copy link
Contributor Author

Tested out your potential solution and sadly it didn't work. Loved the out of the box thinking though. Even though the request is valid, Southwest returns an error saying a companion is associated with PNR and so the change reservation page cannot be loaded.

Completely agree that automating canceling/booking anything would be undesirable for a number of reasons, Since it would require an entirely new flow to track fares on a reservation with a ticketed companion, I think it's best to just not support it. The only other way I can think of would be to accept the flight price as a command line argument or in the config file if users want those fares to be tracked, but even then it gets messy as users would have to update that manually every single time they change the flight. Companion pass users like myself are probably in the minority anyway.

I have updated the code to check for a companion ticket in the greyBoxMessage and if a companion is detected, to raise a CompanionError exception which I added to the utils file.

jdholtz and others added 3 commits April 22, 2023 09:31
I also decided to have the 'unsupported' message in Flight Retriever to
log as a debug message instead of an error message because it will
constantly be printed every time fares are checked.
@jdholtz
Copy link
Owner

jdholtz commented Apr 22, 2023

Thanks for taking the time to debug this issue and explore different possibilities for fixing it.

I decided to log the companion error in the flight retriever as a debug message. I think it would be annoying if this is printed every single time fares are checked. I also added a note to the CONFIGURATION.md to tell users that companion passes are not supported.

@jdholtz jdholtz merged commit c54ed66 into jdholtz:fare_checker Apr 22, 2023
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants