From 63588e4f27b24a9338277a5ead553de75db7fb62 Mon Sep 17 00:00:00 2001 From: zayac Date: Fri, 31 May 2024 18:02:44 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E4=BA=86=E4=BC=91=E7=9C=A0?= =?UTF-8?q?=E6=97=B6=E9=97=B4,=E9=98=B2=E6=AD=A2=E6=B5=8F=E8=A7=88?= =?UTF-8?q?=E5=99=A8=E8=BF=87=E6=97=A9=E5=85=B3=E9=97=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/login.py | 225 +++++++++++++++++++--------------------------- 1 file changed, 92 insertions(+), 133 deletions(-) diff --git a/src/core/login.py b/src/core/login.py index 752e0b1..8502bd7 100644 --- a/src/core/login.py +++ b/src/core/login.py @@ -1,88 +1,69 @@ import base64 +import time import re -import asyncio -from playwright.async_api import async_playwright, TimeoutError +from playwright.sync_api import sync_playwright, TimeoutError from loguru import logger -from src.core.message_client import send_message from src.entity.account import Account, AccountType -import aiohttp +import requests from src.entity.database import db +from src.ui import global_signals -# 提取重复的请求和响应处理逻辑 -async def handle_event(page, event_type, callback, *args): - page.on(event_type, lambda event: asyncio.create_task(callback(event, *args))) - - -async def handle_request(request, result_container, request_event): - if 'perInfo' in request.url: +def handle_request(request, result_container, response_container): + if 'loginFlow' in request.url: + response = request.response() + if response: + data = response.json() + response_container['data'] = data + if 'banner' in request.url: headers = request.headers result_container['headers'] = headers logger.info(f'Successfully got headers: {headers}') - request_event.set() -async def handle_response(response, response_container, response_event): - if 'loginFlow' in response.url: - data = await response.json() - response_container['data'] = data - response_event.set() - - -async def playwright_login(account: Account) -> dict: +def playwright_login(account: Account) -> dict: result_container = {'headers': {}} response_container = {'data': {}} logger.info(f'Starting login for username: {account.username}') try: - async with async_playwright() as playwright: - await perform_login(playwright, account, result_container, response_container) + with sync_playwright() as playwright: + perform_login(playwright, account, result_container, response_container) + return result_container['headers'] except Exception as e: logger.error(f'Error during login for account {account.username}: {e}', exc_info=True) - await handle_login_failure(account) - return result_container['headers'] + handle_login_failure(account) -async def perform_login(playwright, account: Account, request_container: dict, - response_container: dict): - browser = await playwright.chromium.launch(headless=False) - context = await browser.new_context() - page = await context.new_page() +def perform_login(playwright, account: Account, request_container: dict, response_container: dict): + browser = playwright.chromium.launch(headless=False) + context = browser.new_context() + page = context.new_page() - request_event = asyncio.Event() - response_event = asyncio.Event() + page.on("request", lambda request: handle_request(request, request_container, response_container)) - await page.goto(account.url) + page.goto(account.url, timeout=60000) - await handle_event(page, 'request', handle_request, request_container, request_event) - await handle_event(page, 'response', handle_response, response_container, response_event) # 填充基本的元素 - await fill_form_common(page, account) - # 如果是九游 验证码滑动,并且滑动之后自动登录成功 + fill_form_common(page, account) + if account.type == AccountType.jy: - await handle_jy_captcha(page, account, 0) + handle_jy_captcha(page, account) logger.info("验证码处理成功") - try: - # 等待请求发送 获取header返回值 - await request_event.wait() - except TimeoutError: - # 超时 即验证码处理失败 - logger.warning("登录处理失败") + time.sleep(3) else: - # 验证码输入成功 max_retries = 3 for attempt in range(max_retries): - if await handle_captcha(page): - response_event.clear() # 重置事件 - await page.locator("div").filter(has_text=re.compile(r"^登录$")).get_by_role("button").click() + if handle_captcha(page): + page.locator("div").filter(has_text=re.compile(r"^登录$")).get_by_role("button").click() + time.sleep(2) try: - await response_event.wait() - if response_container['data'].get('status_code') != 6000: + if response_container['data'] and response_container['data'].get('status_code') != 6000: logger.warning(f"登录发生错误, {response_container['data'].get('message', '未知错误')}") close_button = page.locator("button.ant-modal-close") - await close_button.wait_for() - await close_button.click() + close_button.wait_for() + close_button.click() else: break except TimeoutError: @@ -91,97 +72,79 @@ async def perform_login(playwright, account: Account, request_container: dict, logger.warning(f"验证码输入失败,重试 {attempt + 1}/{max_retries}") else: logger.warning("登录失败,达到最大重试次数") - try: - # 等待请求发送 获取header返回值 - await request_event.wait() - except TimeoutError: - # 超时 即验证码处理失败 - logger.warning("登录处理失败") - await close_resources(page, context, browser) + persistence(account, response_container['data']) + # 通知app数据更新了 + global_signals.user_data_updated.emit() + page.wait_for_url(f'{account.url}/app/home?showWelcome=false') + close_resources(page, context, browser) -async def fill_form_common(page, account: Account): +def fill_form_common(page, account: Account): username_input = page.get_by_placeholder('用户名') password_input = page.get_by_placeholder('密码') - await username_input.click() - await username_input.fill(account.username) - await password_input.click() - await password_input.fill(account.password) + username_input.click() + username_input.fill(account.username) + password_input.click() + password_input.fill(account.password) -async def handle_jy_captcha(page, account, retry_count=0): - await page.locator("div").filter(has_text=re.compile(r"^登录$")).get_by_role("button").click() - validate_code = page.locator('.geetest_box') - await validate_code.wait_for(state='visible') - await asyncio.sleep(1) - validate_code_buffer = await validate_code.screenshot() - img = base64.b64encode(validate_code_buffer).decode('utf-8') - res = await base64_api(img=img, typeid=33) - drag_btn = validate_code.locator(".geetest_btn") - drag_btn_box = await drag_btn.bounding_box() - try: - distance = int(res) + 3 - target_x = drag_btn_box['x'] + distance - await drag_btn.hover() - await page.mouse.down() - await page.mouse.move(target_x, drag_btn_box["y"], steps=25) - await page.mouse.up() - await validate_code.wait_for(state='hidden') - return True - except (ValueError, TimeoutError): - retry_count += 1 - logger.error(f"验证码处理失败,重试: {res}") - return await handle_jy_captcha(page, account, retry_count) +def handle_jy_captcha(page, account, retry_count=0): + if retry_count < 3: + page.locator("div").filter(has_text=re.compile(r"^登录$")).get_by_role("button").click() + validate_code = page.locator('.geetest_box') + validate_code.wait_for(state='visible') + time.sleep(1) + validate_code_buffer = validate_code.screenshot() + img = base64.b64encode(validate_code_buffer).decode('utf-8') + res = base64_api(img=img, typeid=33) + drag_btn = validate_code.locator(".geetest_btn") + drag_btn_box = drag_btn.bounding_box() + try: + distance = int(res) + 3 + target_x = drag_btn_box['x'] + distance + drag_btn.hover() + page.mouse.down() + page.mouse.move(target_x, drag_btn_box["y"], steps=25) + page.mouse.up() + validate_code.wait_for(state='hidden') + return True + except (ValueError, TimeoutError): + retry_count += 1 + logger.error(f"验证码处理失败,重试: {retry_count}") + return handle_jy_captcha(page, account, retry_count) + else: + logger.error("验证码处理失败") -async def handle_captcha(page): +def handle_captcha(page): validate_code_input = page.get_by_placeholder('请输入验证码') validate_code_img = page.locator('#boxModle') - await validate_code_img.wait_for() - validate_code_buffer = await validate_code_img.screenshot() + validate_code_img.wait_for() + validate_code_buffer = validate_code_img.screenshot() img_base64 = base64.b64encode(validate_code_buffer).decode('utf-8') - res = await base64_api(img=img_base64, typeid=1003) - await validate_code_input.click() - await validate_code_input.fill(res) + res = base64_api(img=img_base64, typeid=1003) + validate_code_input.click() + validate_code_input.fill(res) return True -async def handle_standard_captcha(page, validate_code, img): - res = await base64_api(img=img) - if '|' in res: - await click_captcha_positions(validate_code, res) - await validate_code.locator('.geetest_submit').click() - await validate_code.wait_for(state='hidden') - logger.debug('验证码点击成功') - return True - else: - logger.error(res) - return False - - -async def click_captcha_positions(validate_code, positions_str): - for part in positions_str.split('|'): - x, y = part.split(',') - await validate_code.click(position={"x": int(x), "y": int(y)}) - await asyncio.sleep(.5) - - -async def close_resources(page, context, browser): +def close_resources(page, context, browser): try: - await page.close() - await context.close() - await browser.close() + if page and not page.is_closed(): + page.close() + if context: + context.close() + if browser: + browser.close() except Exception as e: logger.error(f'Error closing browser resources: {e}', exc_info=True) -async def handle_login_failure(account: Account): - send_message(account.user.bot_token, account.user.group_id, - f'{account.url}:加载超时,请检查是否后台更换了链接') - return +def handle_login_failure(account: Account): + pass -async def base64_api(uname='luffy230505', pwd='qwer12345', img='', typeid=20): +def base64_api(uname='luffy230505', pwd='qwer12345', img='', typeid=20): logger.info('Calling third part interfaces') data = { "username": uname, @@ -190,25 +153,21 @@ async def base64_api(uname='luffy230505', pwd='qwer12345', img='', typeid=20): "image": img, 'softid': '8d13df0efe074035b54ee9c2bef85106' } - async with aiohttp.ClientSession() as session: - async with session.post("http://api.ttshitu.com/predict", json=data) as response: - result = await response.json() - if result['success']: - return result["data"]["result"] - else: - return result["message"] + response = requests.post("http://api.ttshitu.com/predict", json=data) + result = response.json() + if result['success']: + return result["data"]["result"] + else: + return result["message"] def login(account: Account): - headers = asyncio.run(playwright_login(account)) + headers = playwright_login(account) if headers: account.headers = headers - persistence(account, headers) return account else: - send_message(account.user.bot_token, account.user.group_id, - f'{account.url}:加载超时,请检查是否后台更换了链接') - return + pass def persistence(account: Account, headers: dict):