From 891684d29ac8c11a7f79e2206f2612da69659dcb Mon Sep 17 00:00:00 2001 From: QIN2DIM <62018067+QIN2DIM@users.noreply.github.com> Date: Tue, 21 Nov 2023 21:13:44 +0800 Subject: [PATCH] feat(tenacity): Stable Message Queue (#238) --- src/claim.py | 27 +++++++++---------------- src/epic_games/agent.py | 44 ++++++++++++++++++++++++++++++----------- src/utils/solver.py | 37 ++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+), 30 deletions(-) diff --git a/src/claim.py b/src/claim.py index 9cd4b2f..bfbc8f9 100644 --- a/src/claim.py +++ b/src/claim.py @@ -11,9 +11,11 @@ import sys from dataclasses import dataclass, field from typing import List -import hcaptcha_challenger as solver +import importlib_metadata +from hcaptcha_challenger import install +from hcaptcha_challenger.agents import Malenia from loguru import logger -from playwright.async_api import BrowserContext, async_playwright, TimeoutError +from playwright.async_api import BrowserContext, async_playwright from epic_games import ( EpicPlayer, @@ -23,7 +25,6 @@ from epic_games import ( get_promotions, get_order_history, ) -import importlib_metadata self_supervised = True @@ -118,19 +119,9 @@ class ISurrender: single_promotions.append(p) if single_promotions: - for _ in range(3): - try: - if await epic.claim_weekly_games(page, single_promotions): - break - except TimeoutError: - continue + await epic.claim_weekly_games(page, single_promotions) if bundle_promotions: - for _ in range(3): - try: - if await epic.claim_bundle_games(page, bundle_promotions): - break - except TimeoutError: - continue + await epic.claim_bundle_games(page, bundle_promotions) @logger.catch async def stash(self): @@ -139,11 +130,10 @@ class ISurrender: logger.info( "run", - image="20231105", + image="20231121", version=importlib_metadata.version("hcaptcha-challenger"), role="EpicPlayer", headless=self.headless, - self_supervised=self_supervised, ) async with async_playwright() as p: @@ -155,8 +145,9 @@ class ISurrender: locale=self.locale, args=["--hide-crash-restore-bubble"], ) + await Malenia.apply_stealth(context) if not await self.prelude_with_context(context): - solver.install(upgrade=True, clip=self_supervised) + install(upgrade=True, clip=True) await self.claim_epic_games(context) await context.close() diff --git a/src/epic_games/agent.py b/src/epic_games/agent.py index 1ce23d5..cbc67d9 100644 --- a/src/epic_games/agent.py +++ b/src/epic_games/agent.py @@ -15,6 +15,7 @@ from typing import List, Dict, Literal import httpx from loguru import logger from playwright.async_api import BrowserContext, expect, TimeoutError, Page, FrameLocator, Locator +from tenacity import * from epic_games.player import EpicPlayer from utils import from_dict_to_model, AgentG @@ -87,6 +88,12 @@ class CommonHandler: return True @staticmethod + @retry( + retry=retry_if_exception_type(TimeoutError), + wait=wait_fixed(0.5), + stop=stop_after_attempt(15), + reraise=True, + ) async def insert_challenge( solver: AgentG, page: Page, @@ -95,18 +102,19 @@ class CommonHandler: recur_url: str, is_uk: bool, ): - for _ in range(15): - # {{< if fall in challenge >}} - match await solver(window="free", recur_url=recur_url): - case solver.status.CHALLENGE_BACKCALL | solver.status.CHALLENGE_RETRY: - await wpc.locator("//a[@class='talon_close_button']").click() - await page.wait_for_timeout(1000) - if is_uk: - await CommonHandler.uk_confirm_order(wpc) - await payment_btn.click(delay=200) - case solver.status.CHALLENGE_SUCCESS: - await page.wait_for_url(recur_url) - break + response = await solver.execute(window="free") + logger.debug("task done", sattus=f"{solver.status.CHALLENGE_SUCCESS}") + + match response: + case solver.status.CHALLENGE_BACKCALL | solver.status.CHALLENGE_RETRY: + await wpc.locator("//a[@class='talon_close_button']").click() + await page.wait_for_timeout(1000) + if is_uk: + await CommonHandler.uk_confirm_order(wpc) + await payment_btn.click(delay=200) + case solver.status.CHALLENGE_SUCCESS: + await page.wait_for_url(recur_url) + return @staticmethod async def empty_cart(page: Page, wait_rerender: int = 30) -> bool | None: @@ -272,6 +280,12 @@ class EpicGames: logger.success("flush_token", path=self.player.ctx_cookie_path) return cookies + @retry( + retry=retry_if_exception_type(TimeoutError), + wait=wait_fixed(0.5), + stop=(stop_after_delay(360) | stop_after_attempt(3)), + reraise=True, + ) async def claim_weekly_games(self, page: Page, promotions: List[Game]): in_cart_nums = 0 @@ -326,6 +340,12 @@ class EpicGames: return True + @retry( + retry=retry_if_exception_type(TimeoutError), + wait=wait_fixed(0.5), + stop=(stop_after_delay(360) | stop_after_attempt(3)), + reraise=True, + ) async def claim_bundle_games(self, page: Page, promotions: List[Game]): for promotion in promotions: logger.info("claim_bundle_games", action="go to store", url=promotion.url) diff --git a/src/utils/solver.py b/src/utils/solver.py index be65f5d..0b49a75 100644 --- a/src/utils/solver.py +++ b/src/utils/solver.py @@ -3,10 +3,12 @@ # Author : QIN2DIM # GitHub : https://github.com/QIN2DIM # Description: +import asyncio from dataclasses import dataclass from hcaptcha_challenger.agents import AgentT from playwright.async_api import Page +from tenacity import * @dataclass @@ -29,3 +31,38 @@ class AgentG(AgentT): frame_challenge = frame_purchase.frame_locator(self.HOOK_CHALLENGE) return frame_challenge + + @retry( + retry=retry_if_exception_type(asyncio.QueueEmpty), + wait=wait_fixed(0.5), + stop=(stop_after_delay(30) | stop_after_attempt(60)), + reraise=True, + ) + async def _reset_state(self) -> bool | None: + self.cr = None + self.qr = self.qr_queue.get_nowait() + + if not self.qr_queue.empty(): + for _ in range(self.qr_queue.qsize()): + self.qr = self.qr_queue.get_nowait() + + return True + + @retry( + retry=retry_if_exception_type(asyncio.QueueEmpty), + wait=wait_fixed(0.5), + stop=(stop_after_delay(30) | stop_after_attempt(60)), + reraise=True, + ) + async def _is_success(self): + self.cr = self.cr_queue.get_nowait() + + if not self.cr_queue.empty(): + for _ in range(self.cr_queue.qsize()): + self.cr = self.cr_queue.get_nowait() + + # Match: Timeout / Loss + if not self.cr or not self.cr.is_pass: + return self.status.CHALLENGE_RETRY + if self.cr.is_pass: + return self.status.CHALLENGE_SUCCESS