diff --git a/.gitignore b/.gitignore index 6a8a20f1..ea6c0962 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ duce.py new_enroll_test.py data.json gui-test.py +.venv/ diff --git a/base.py b/base.py index ed14ecb3..33367cc4 100644 --- a/base.py +++ b/base.py @@ -5,7 +5,7 @@ import time import traceback from datetime import datetime, timezone -from decimal import Decimal +from decimal import Decimal, InvalidOperation from urllib.parse import parse_qs, unquote, urlparse, urlsplit, urlunparse import cloudscraper @@ -740,29 +740,43 @@ def get_course_id(self, url): url = re.sub(r"\W+$", "", unquote(url)) try: r = self.client.get(url) + r.raise_for_status() except requests.exceptions.ConnectionError: if self.debug: - print(r.text) + print("Connection error:", url) return "retry", url, False + except requests.exceptions.HTTPError as e: + if self.debug: + print(f"HTTP error {e.response.status_code}: {e}") + return "invalid", url, False soup = bs(r.content, "html5lib") if self.debug: with open("test/soup.html", "w", encoding="utf-8") as f: f.write(str(soup)) - course_id = soup.find("body").get("data-clp-course-id", "invalid") - if course_id == "invalid": - return "invalid", url, False - dma = json.loads(soup.find("body")["data-module-args"]) - if self.debug: - with open("test/dma.json", "w") as f: - json.dump(dma, f, indent=4) + try: + dma = json.loads(soup.find("body")["data-module-args"]) + if self.debug: + with open("test/dma.json", "w") as f: + json.dump(dma, f, indent=4) + course_info = dma.get("serverSideProps", {}).get("course", None) + if not course_info: + if self.debug: + print("Course information is missing in serverSideProps") + return "invalid", url, False + + course_id = soup.find("body").get("data-clp-course-id", "invalid") + is_free = not course_info.get("isPaid", True) + if not self.debug and self.is_course_excluded(dma): + return "excluded", r.url, False - is_free = not dma["serverSideProps"]["course"].get("isPaid", True) - if not self.debug and self.is_course_excluded(dma): - return "excluded", r.url, False + return course_id, r.url, is_free - return course_id, r.url, is_free + except (KeyError, TypeError, json.JSONDecodeError): + if self.debug: + print("Error parsing course data:", traceback.format_exc()) + return "invalid", url, False def is_course_excluded(self, dma): instructors = [ @@ -819,7 +833,13 @@ def check_course(self, course_id, coupon_code=None): status = r["redeem_coupon"]["discount_attempts"][0]["status"] coupon_valid = discount == 100 and status == "applied" - return Decimal(amount), coupon_valid + # can be "retry" or Decimal + try: + amount = Decimal(amount) + except (InvalidOperation, ValueError): + amount = "retry" + + return amount, coupon_valid def start_enrolling(self): self.remove_duplicate_courses()