diff --git a/lotto-runner/lotto_auto_buy.py b/lotto-runner/lotto_auto_buy.py index 120e683..a0eddd9 100644 --- a/lotto-runner/lotto_auto_buy.py +++ b/lotto-runner/lotto_auto_buy.py @@ -228,9 +228,55 @@ def buy_lotto(driver, game_count=5): except Exception: return False + def ensure_no_layer_alert(max_rounds: int = 3) -> None: + """popupLayerAlert가 떠 있으면 닫히는 것까지 보장(짧게 재시도).""" + for _ in range(max_rounds): + closed = close_layer_alert_if_present(timeout_s=0.6) + try: + WebDriverWait(driver, 1.2).until( + EC.invisibility_of_element_located((By.ID, "popupLayerAlert")) + ) + return + except Exception: + if not closed: + return + time.sleep(0.2) + + def safe_click(locator, timeout_s: float = 10, retries: int = 2) -> None: + """클릭이 팝업에 가로채이면 popupLayerAlert 닫고 재시도.""" + from selenium.common.exceptions import ElementClickInterceptedException + + last_exc = None + for _ in range(retries + 1): + ensure_no_layer_alert() + el = WebDriverWait(driver, timeout_s).until(EC.element_to_be_clickable(locator)) + try: + el.click() + return + except ElementClickInterceptedException as e: + last_exc = e + # 팝업이 클릭을 가로채는 경우가 대부분이라 닫고 재시도 + close_layer_alert_if_present(timeout_s=0.8) + time.sleep(0.3) + except Exception as e: + last_exc = e + break + + # 마지막 fallback: JS click + try: + ensure_no_layer_alert() + el = WebDriverWait(driver, timeout_s).until(EC.presence_of_element_located(locator)) + driver.execute_script("arguments[0].click();", el) + return + except Exception: + if last_exc: + raise last_exc + raise + # 첫 상호작용 전에 레이어 알림이 떠 있으면 먼저 닫기 if close_layer_alert_if_present(): logger.info("초기 popupLayerAlert 감지 — 닫기 처리 완료") + ensure_no_layer_alert() # 모든 레이어 팝업을 JS로 직접 숨기기 (z-index 오버레이 완전 제거) driver.execute_script(""" @@ -242,16 +288,14 @@ def buy_lotto(driver, game_count=5): time.sleep(0.5) logger.info("팝업 레이어 숨김 처리 완료") - # "자동" 탭 선택 — onclick 함수(selectWayTab)를 직접 호출 - # (간헐적으로 popupLayerAlert가 클릭을 가로채므로 전/후로 닫기 처리 + 1회 재시도) - for attempt in range(2): - close_layer_alert_if_present(timeout_s=0.5) + # "자동" 탭 선택 (id=num2). 팝업이 클릭을 가로채는 케이스 방지. + try: + safe_click((By.ID, "num2"), timeout_s=10, retries=2) + except Exception: + # fallback: 함수 직접 호출 + ensure_no_layer_alert() driver.execute_script("selectWayTab(1);") - time.sleep(1) - if not close_layer_alert_if_present(timeout_s=0.5): - break - logger.info("자동 탭 선택 중 popupLayerAlert 재발 — 닫기 후 재시도") - time.sleep(0.5) + time.sleep(1) # 게임 수 설정 (드롭다운에서 선택) from selenium.webdriver.support.ui import Select @@ -263,14 +307,12 @@ def buy_lotto(driver, game_count=5): except Exception: # 드롭다운이 없는 경우 자동번호 버튼을 game_count번 클릭 for i in range(game_count): - auto_select_btn = driver.find_element(By.ID, "btnSelectNum") - auto_select_btn.click() + safe_click((By.ID, "btnSelectNum"), timeout_s=10, retries=2) time.sleep(0.5) # "자동번호 선택" 버튼 클릭 try: - select_num_btn = driver.find_element(By.ID, "btnSelectNum") - select_num_btn.click() + safe_click((By.ID, "btnSelectNum"), timeout_s=10, retries=2) time.sleep(1) except: pass @@ -283,14 +325,20 @@ def buy_lotto(driver, game_count=5): buy_btn = wait.until( EC.element_to_be_clickable((By.ID, "btnBuy")) ) - buy_btn.click() + try: + buy_btn.click() + except Exception: + safe_click((By.ID, "btnBuy"), timeout_s=10, retries=2) time.sleep(2) # 구매 확인 팝업 "확인" 클릭 confirm_btn = wait.until( EC.element_to_be_clickable((By.CSS_SELECTOR, "#popupLayerConfirm input[value='확인']")) ) - confirm_btn.click() + try: + confirm_btn.click() + except Exception: + safe_click((By.CSS_SELECTOR, "#popupLayerConfirm input[value='확인']"), timeout_s=10, retries=2) time.sleep(3) logger.info("구매 요청 완료! 구매 결과를 확인합니다...")