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

Better avoidance of 403/429 #283

Draft
wants to merge 64 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 57 commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
c759b7b
Modified make_request
dmytrokoren Jul 10, 2024
58e6178
Merge branch 'develop' into develop
dmytrokoren Jul 10, 2024
e3bc403
Merge branch 'jdholtz:develop' into develop
dmytrokoren Jul 11, 2024
e71fa02
Minor changes
dmytrokoren Jul 11, 2024
1a78b9c
Reverted changes for webdriver and requirements
dmytrokoren Jul 11, 2024
5a54b32
Better avoidance of 403/429
dmytrokoren Jul 11, 2024
138c6bb
Reverted changes done for check-in
dmytrokoren Jul 11, 2024
c151290
Minor changes for webdriver.py
dmytrokoren Jul 17, 2024
2c92c8b
Using xvfb with seleniumbase in docker
dmytrokoren Jul 23, 2024
d3c4122
Updated seleniumbase version
dmytrokoren Jul 23, 2024
7ec4500
Updated driver_options
dmytrokoren Jul 23, 2024
609db2d
Updated get_driver
dmytrokoren Jul 24, 2024
f08f8aa
- Modified webdriver.py with latest enhacement to 429 error
dmytrokoren Jul 24, 2024
8f7532c
Enhancements
dmytrokoren Aug 15, 2024
5e2aa42
Removed unused variable
dmytrokoren Aug 15, 2024
64277bd
Reverted change
dmytrokoren Aug 15, 2024
3ebd9c9
Changes finalized
dmytrokoren Aug 15, 2024
59a30fc
Latest changes to Dockerfile and some adds/minus to webdriver
dmytrokoren Aug 16, 2024
6afafde
Minor changes
dmytrokoren Jul 11, 2024
8ff273c
Finalized and working with docker
dmytrokoren Aug 19, 2024
b96815c
Fixed SyntaxError - mybad
dmytrokoren Aug 19, 2024
e0a35de
Merge branch 'develop' into modified_webdriver_and_updated_requirements
dmytrokoren Aug 19, 2024
abf9664
Enhancement changes for Docker driver
dmytrokoren Aug 21, 2024
1c00edb
Modified functions as per request
dmytrokoren Aug 21, 2024
99f2ce1
refactored _get_driver and make new changes as requested
dmytrokoren Aug 22, 2024
a4980b3
Minor change
dmytrokoren Aug 22, 2024
742d5ba
Added tempfile.TemporaryDirectory() for automatic cleanup.
dmytrokoren Aug 22, 2024
bcaf144
Refactored approach of deleting temp folder for chrome profile
dmytrokoren Aug 22, 2024
ea62ec0
Few changes
dmytrokoren Aug 23, 2024
6da4471
Merge pull request #2 from dmytrokoren/dev_ws_test
dmytrokoren Aug 26, 2024
d3ef535
Done updated as per request
dmytrokoren Aug 26, 2024
919ad13
Refactor _get_driver: Improve temp directory handling and enhance bot…
dmytrokoren Aug 27, 2024
fd94064
Revert "Refactor _get_driver: Improve temp directory handling and enh…
dmytrokoren Aug 27, 2024
565553b
Updated profile directory handling for cross-platform compatibility
dmytrokoren Aug 27, 2024
56c5767
Updated with try block to always cleanup even if exception occurs or …
dmytrokoren Aug 29, 2024
4041cb9
Final minor enhancements
dmytrokoren Aug 30, 2024
4ed9cc5
Added is_mobile
dmytrokoren Aug 31, 2024
b39b425
Major changes to avoid 429 error
dmytrokoren Sep 2, 2024
68f075f
`feat: Improve header caching logic`
dmytrokoren Sep 4, 2024
351c00b
Fixed and optimized headers caching
dmytrokoren Sep 6, 2024
e48d10d
requirements.txt upgrade version
dmytrokoren Sep 6, 2024
0b7c111
Merge pull request #3 from jdholtz/develop
dmytrokoren Sep 8, 2024
89d2df9
Major updates:
dmytrokoren Sep 10, 2024
5b7e866
- Docker file updated with latest python rc and Env Path added to sup…
dmytrokoren Sep 12, 2024
741e43e
Removed logger for User-Agent
dmytrokoren Sep 12, 2024
d85ea53
next line added
dmytrokoren Sep 12, 2024
70530cc
Major update that helped fix 429 error for users that encoutered it t…
dmytrokoren Sep 15, 2024
0da258c
Removed --verbose from docker file
dmytrokoren Sep 15, 2024
f7eb251
Minor adjustment
dmytrokoren Sep 16, 2024
63c467d
Modified changes to use uc_click and wait for CDP to capture properly
dmytrokoren Sep 17, 2024
2e28f51
Removed unused
dmytrokoren Sep 17, 2024
35f4611
Included refresh driver when fetching headers
dmytrokoren Sep 18, 2024
194fd02
Added user-agent generator
dmytrokoren Sep 19, 2024
f3eead4
Fixed tests
dmytrokoren Sep 19, 2024
4d7728d
Updates to webdriver
dmytrokoren Sep 20, 2024
f774706
Updated webdriver
dmytrokoren Sep 21, 2024
fbbb859
Merge pull request #4 from dmytrokoren/develop
dmytrokoren Sep 21, 2024
b3daf75
feat: Implement wait time between accounts to reduce 429 errors
dmytrokoren Sep 26, 2024
957bb4a
Tweaked
dmytrokoren Sep 27, 2024
797abc2
- Updated dockerfile
dmytrokoren Sep 30, 2024
4c8a431
Merge remote-tracking branch 'external-repo/develop' into modified_we…
dmytrokoren Oct 14, 2024
d9a905c
Reverted to default dockerfile
dmytrokoren Oct 14, 2024
d6bb171
Updated headers url and removed user_agent
dmytrokoren Oct 31, 2024
b7915b8
Added mobile version to the driver, to be less detectable
dmytrokoren Nov 1, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ pyproject.toml
setup.cfg
tests/
venv
cached_headers.json
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ venv/

# From SeleniumBase
downloaded_files/
cached_headers.json
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ repos:
- repo: https://github.com/pycqa/flake8
rev: 7.1.1
hooks:
- id: flake8
- id: flake8
- repo: https://github.com/PyCQA/isort
rev: 5.13.2
hooks:
Expand Down
8 changes: 5 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
FROM python:3.12-rc-alpine
FROM python:3.13.0rc2-alpine3.19

dmytrokoren marked this conversation as resolved.
Show resolved Hide resolved
WORKDIR /app

# Define so the script knows not to download a new driver version, as
# this Docker image already downloads a compatible chromedriver
ENV AUTO_SOUTHWEST_CHECK_IN_DOCKER=1
ENV PATH="/app/.local/bin:${PATH}"

RUN apk add --update --no-cache chromium chromium-chromedriver
RUN apk add --update --no-cache chromium chromium-chromedriver xvfb xauth

RUN adduser -D auto-southwest-check-in -h /app
RUN chown -R auto-southwest-check-in:auto-southwest-check-in /app
USER auto-southwest-check-in

COPY requirements.txt requirements.txt
COPY requirements.txt .
RUN pip3 install --upgrade pip && pip3 install --no-cache-dir -r requirements.txt
dmytrokoren marked this conversation as resolved.
Show resolved Hide resolved

COPY . .
Expand Down
69 changes: 40 additions & 29 deletions lib/reservation_monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,42 +187,53 @@ def _check(self) -> bool:
# this scope
return False

def _get_reservations(self) -> Tuple[List[Dict[str, Any]], bool]:
def _get_reservations(self, max_retries: int = 2) -> Tuple[List[Dict[str, Any]], bool]:
"""
Returns a list of reservations and a boolean indicating if reservation
scheduling should be skipped.
Attempts to retrieve a list of reservations and returns a tuple containing the list
of reservations and a boolean indicating whether reservation scheduling should be skipped.

Reservation scheduling will be skipped if a Too Many Requests error or timeout occurs
because new headers might not be valid and a list of reservations could not be retrieved.
The method will retry fetching reservations once in case of a timeout
or a Too Many Requests error. If the retry fails, reservation scheduling will be
skipped until the next scheduled attempt.
"""
logger.debug("Retrieving reservations for account")
webdriver = WebDriver(self.checkin_scheduler)

try:
reservations = webdriver.get_reservations(self)
except DriverTimeoutError:
logger.debug(
"Timeout while retrieving reservations during login. Skipping reservation retrieval"
)
self.notification_handler.timeout_during_retrieval("account")
return [], True
except LoginError as err:
if err.status_code == TOO_MANY_REQUESTS_CODE:
# Don't exit when a Too Many Requests error happens. Instead, just skip the
# retrieval until the next time.
for attempt in range(max_retries + 1):
dmytrokoren marked this conversation as resolved.
Show resolved Hide resolved
webdriver = WebDriver(self.checkin_scheduler)

try:
reservations = webdriver.get_reservations(self)
logger.debug(
"Encountered a Too Many Requests error while logging in. Skipping reservation "
"retrieval"
"Successfully retrieved %d reservations after %d attempts",
len(reservations),
attempt + 1,
)
self.notification_handler.too_many_requests_during_login()
return [], True

logger.debug("Error logging in. %s. Exiting", err)
self.notification_handler.failed_login(err)
sys.exit(1)

logger.debug("Successfully retrieved %d reservations", len(reservations))
return reservations, False
return reservations, False

except DriverTimeoutError:
if attempt < max_retries:
logger.debug("Timeout while retrieving reservations during login. Retrying")
else:
logger.debug("Timeout persisted after retries. Skipping reservation retrieval")
self.notification_handler.timeout_during_retrieval("account")

except LoginError as err:
if err.status_code == TOO_MANY_REQUESTS_CODE:
if attempt < max_retries:
logger.debug(
"Encountered a Too Many Requests error while logging in. Retrying"
)
else:
logger.debug(
"Too Many Requests error persists. Skipping reservation retrieval"
)
self.notification_handler.too_many_requests_during_login()
else:
logger.debug("Error logging in. %s. Exiting", err)
self.notification_handler.failed_login(err)
sys.exit(1)

return [], True

def _stop_monitoring(self) -> None:
print(f"\nStopping monitoring for account with username {self.username}")
Expand Down
27 changes: 14 additions & 13 deletions lib/utils.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import random
import re
import socket
import time
from datetime import datetime, timezone
Expand Down Expand Up @@ -54,27 +55,27 @@ def make_request(
server is not in sync with our NTP server or local computer).
"""
# Ensure the URL is not malformed
site = site.replace("//", "/").lstrip("/")
site = re.sub(r"(?<!:)//+", "/", site).lstrip("/")
dmytrokoren marked this conversation as resolved.
Show resolved Hide resolved
url = BASE_URL + site

attempts = 0
while attempts < max_attempts:
attempts += 1
if method == "POST":
response = requests.post(url, headers=headers, json=info)
else:
response = requests.get(url, headers=headers, params=info)
try:
dmytrokoren marked this conversation as resolved.
Show resolved Hide resolved
if method.upper() == "POST":
response = requests.post(url, headers=headers, json=info)
else:
response = requests.get(url, headers=headers, params=info)

if response.status_code == 200:
logger.debug("Successfully made request after %d attempts", attempts)
return response.json()
if response.status_code == 200:
logger.debug("Successfully made request after %d attempts", attempts)
return response.json()

# Request did not succeed
response_body = response.content.decode()
error_msg = response.reason + " " + str(response.status_code)
error = RequestError(error_msg, response_body)
# Handle unsuccessful responses
response_body = response.content.decode()
error_msg = f"{response.reason} ({response.status_code})"
error = RequestError(error_msg, response_body)

try:
_handle_southwest_error_code(error)
except (RequestError, AirportCheckInError) as err:
# Stop requesting after one attempt for special codes, as the requests won't succeed
Expand Down
Loading
Loading