mirror of
https://github.com/j93es/browser-use-oauth.git
synced 2026-06-05 00:11:28 +09:00
[Enhancement] async_playwright 추가 및 OAuth 로그인 흐름 개선, Planner 제외 (오히려 문제가 일어남)
This commit is contained in:
parent
1474399f5c
commit
9b797a8d21
2 changed files with 49 additions and 12 deletions
|
|
@ -3,7 +3,7 @@ ANONYMIZED_TELEMETRY=false
|
||||||
GOOGLE_API_KEY=
|
GOOGLE_API_KEY=
|
||||||
# 권장 (다른 모델로 교체 가능) [다른 모델로 교체시 성능 보장 불가]
|
# 권장 (다른 모델로 교체 가능) [다른 모델로 교체시 성능 보장 불가]
|
||||||
GOOGLE_MODEL=gemini-2.5-flash-preview-05-20
|
GOOGLE_MODEL=gemini-2.5-flash-preview-05-20
|
||||||
GOOGLE_PLANNER_MODEL=gemini-2.5-flash-preview-05-20
|
#GOOGLE_PLANNER_MODEL=gemini-2.5-flash-preview-05-20
|
||||||
|
|
||||||
# min(INITIAL_BACKOFF * (2 ** try_cnt), MAX_BACKOFF)만큼 API가 실패시 대기합니다.
|
# min(INITIAL_BACKOFF * (2 ** try_cnt), MAX_BACKOFF)만큼 API가 실패시 대기합니다.
|
||||||
INITIAL_BACKOFF=60
|
INITIAL_BACKOFF=60
|
||||||
|
|
@ -17,6 +17,9 @@ BACKEND_URL=http://localhost:11081
|
||||||
# 브라우저 언어 설정
|
# 브라우저 언어 설정
|
||||||
LANG=en_US
|
LANG=en_US
|
||||||
|
|
||||||
|
# 필수 뒤에 있는 이메일 주소는 Google 계정의 로그인 힌트로 사용됩니다.
|
||||||
|
# 이메일의 전체를 입력해주세요
|
||||||
|
GOOGLE_ID=bot.imnya.ng@gmail.com
|
||||||
|
|
||||||
# provider 계정 (본인이 사용하지 않는 계정 권장) (Github, apple, kakao등 다른 계정 추가 가능)
|
# provider 계정 (본인이 사용하지 않는 계정 권장) (Github, apple, kakao등 다른 계정 추가 가능)
|
||||||
# PROVIDOR_CREDENTIALS_IN_LLM는 True로 설정시, 아래 계정 정보가 LLM에 포함되어 사용됩니다.
|
# PROVIDOR_CREDENTIALS_IN_LLM는 True로 설정시, 아래 계정 정보가 LLM에 포함되어 사용됩니다.
|
||||||
|
|
|
||||||
56
main.py
56
main.py
|
|
@ -17,6 +17,7 @@ from browser_use import (
|
||||||
BrowserProfile,
|
BrowserProfile,
|
||||||
Controller,
|
Controller,
|
||||||
)
|
)
|
||||||
|
from patchright.async_api import async_playwright as async_patchright
|
||||||
from lib.is_html import is_html_url
|
from lib.is_html import is_html_url
|
||||||
from lib.read_txt import read_lines_between
|
from lib.read_txt import read_lines_between
|
||||||
from lib.prompt import extend_planner_system_message
|
from lib.prompt import extend_planner_system_message
|
||||||
|
|
@ -39,11 +40,12 @@ backend_url = os.getenv("BACKEND_URL", "http://localhost:11081")
|
||||||
|
|
||||||
print("🔧 환경 설정:")
|
print("🔧 환경 설정:")
|
||||||
print(f"🔗 Backend URL: {backend_url}")
|
print(f"🔗 Backend URL: {backend_url}")
|
||||||
api_key = os.getenv('GOOGLE_API_KEY')
|
api_key = os.getenv("GOOGLE_API_KEY")
|
||||||
print(f"🔑 Google API Key: {api_key[-4:] if api_key else None}")
|
print(f"🔑 Google API Key: {api_key[-4:] if api_key else None}")
|
||||||
print(f"🌐 Google Model: {os.getenv('GOOGLE_MODEL')}")
|
print(f"🌐 Google Model: {os.getenv('GOOGLE_MODEL')}")
|
||||||
print(f"🌐 Google Planner Model: {os.getenv('GOOGLE_PLANNER_MODEL')}")
|
print(f"🌐 Google Planner Model: {os.getenv('GOOGLE_PLANNER_MODEL')}")
|
||||||
|
|
||||||
|
|
||||||
# API 쿼터 처리를 위한 콜백 핸들러
|
# API 쿼터 처리를 위한 콜백 핸들러
|
||||||
class QuotaExhaustedHandler(BaseCallbackHandler):
|
class QuotaExhaustedHandler(BaseCallbackHandler):
|
||||||
def on_llm_error(self, error, **kwargs):
|
def on_llm_error(self, error, **kwargs):
|
||||||
|
|
@ -147,8 +149,8 @@ async def scan_one_url(url: str, skip_html_check: bool = False):
|
||||||
print("🔗 No proxy configured, using direct connection.")
|
print("🔗 No proxy configured, using direct connection.")
|
||||||
|
|
||||||
# user_data_dir 설정
|
# user_data_dir 설정
|
||||||
#user_data_path = Path("./data/user_data").resolve()
|
# user_data_path = Path("./data/user_data").resolve()
|
||||||
#user_data_path.mkdir(parents=True, exist_ok=True)
|
# user_data_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
storage_state_path = Path("./data/storage_state.json").resolve()
|
storage_state_path = Path("./data/storage_state.json").resolve()
|
||||||
storage_state_temp_path = Path("./data/storage_state_temp.json").resolve()
|
storage_state_temp_path = Path("./data/storage_state_temp.json").resolve()
|
||||||
|
|
@ -156,7 +158,9 @@ async def scan_one_url(url: str, skip_html_check: bool = False):
|
||||||
if storage_state_path.exists():
|
if storage_state_path.exists():
|
||||||
if storage_state_temp_path.exists():
|
if storage_state_temp_path.exists():
|
||||||
storage_state_temp_path.unlink()
|
storage_state_temp_path.unlink()
|
||||||
storage_state_temp_path.write_text(storage_state_path.read_text(), encoding="utf-8")
|
storage_state_temp_path.write_text(
|
||||||
|
storage_state_path.read_text(), encoding="utf-8"
|
||||||
|
)
|
||||||
print(f"🔄 Using existing storage state: {storage_state_temp_path}")
|
print(f"🔄 Using existing storage state: {storage_state_temp_path}")
|
||||||
else:
|
else:
|
||||||
storage_state_temp_path = None
|
storage_state_temp_path = None
|
||||||
|
|
@ -164,11 +168,17 @@ async def scan_one_url(url: str, skip_html_check: bool = False):
|
||||||
# BrowserProfile에 모든 설정 포함
|
# BrowserProfile에 모든 설정 포함
|
||||||
profile = BrowserProfile(
|
profile = BrowserProfile(
|
||||||
disable_security=True,
|
disable_security=True,
|
||||||
|
deterministic_rendering=True,
|
||||||
stealth=True,
|
stealth=True,
|
||||||
headless=False,
|
headless=False,
|
||||||
#user_data_dir=str(user_data_path),
|
# user_data_dir=str(user_data_path),
|
||||||
user_data_dir=None,
|
user_data_dir=None,
|
||||||
storage_state=str(storage_state_temp_path) if storage_state_temp_path and storage_state_temp_path.exists() else None,
|
executable_path="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome",
|
||||||
|
storage_state=(
|
||||||
|
str(storage_state_temp_path)
|
||||||
|
if storage_state_temp_path and storage_state_temp_path.exists()
|
||||||
|
else None
|
||||||
|
),
|
||||||
viewport={"width": 1600, "height": 900},
|
viewport={"width": 1600, "height": 900},
|
||||||
# 프록시 설정
|
# 프록시 설정
|
||||||
proxy={"server": proxy_url} if proxy_url else None,
|
proxy={"server": proxy_url} if proxy_url else None,
|
||||||
|
|
@ -184,11 +194,23 @@ async def scan_one_url(url: str, skip_html_check: bool = False):
|
||||||
"--ignore-certificate-errors",
|
"--ignore-certificate-errors",
|
||||||
"--ignore-ssl-errors",
|
"--ignore-ssl-errors",
|
||||||
"--allow-running-insecure-content",
|
"--allow-running-insecure-content",
|
||||||
|
"--disable-web-security",
|
||||||
|
"--disable-features=VizDisplayCompositor",
|
||||||
|
"--disable-blink-features=AutomationControlled",
|
||||||
|
"--no-first-run",
|
||||||
|
"--no-service-autorun",
|
||||||
|
"--password-store=basic",
|
||||||
|
"--use-mock-keychain",
|
||||||
|
"--no-default-browser-check",
|
||||||
|
"--disable-extensions-file-access-check",
|
||||||
|
"--disable-extensions-http-throttling",
|
||||||
|
"--disable-component-extensions-with-background-pages",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
# BrowserSession에 profile 전달
|
# BrowserSession에 profile 전달
|
||||||
session = BrowserSession(
|
session = BrowserSession(
|
||||||
|
playwright=(await async_patchright().start()),
|
||||||
browser_profile=profile,
|
browser_profile=profile,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -200,9 +222,19 @@ async def scan_one_url(url: str, skip_html_check: bool = False):
|
||||||
agent = Agent(
|
agent = Agent(
|
||||||
browser_session=session,
|
browser_session=session,
|
||||||
initial_actions=initial_actions,
|
initial_actions=initial_actions,
|
||||||
task="Navigate to the login page, and collect the OAuth provider buttons and their login URLs. Ignore Passkey.",
|
task=(
|
||||||
llm=CreateChatGoogleGenerativeAI(os.getenv("GOOGLE_MODEL") or "fallback"),
|
"Navigate to the login page, identify all OAuth provider buttons (excluding Passkey), "
|
||||||
planner_llm=CreateChatGoogleGenerativeAI(os.getenv("GOOGLE_PLANNER_MODEL") or "fallback"),
|
"and for each one: click the button, follow the full OAuth login flow as far as possible "
|
||||||
|
"with a real user account (without using a fake or non-existent account), and capture the "
|
||||||
|
"final redirect URL after login. Do not stop at just collecting the initial authorization URL—"
|
||||||
|
"actually perform the login step like a real user would. "
|
||||||
|
"If the OAuth buttons do not appear immediately, wait briefly to allow the page to load completely before proceeding. "
|
||||||
|
"Always log out before starting the login process, and make sure to attempt the login again from a clean state."
|
||||||
|
),
|
||||||
|
llm=CreateChatGoogleGenerativeAI(
|
||||||
|
os.getenv("GOOGLE_MODEL") or "fallback"
|
||||||
|
),
|
||||||
|
# planner_llm=CreateChatGoogleGenerativeAI(os.getenv("GOOGLE_PLANNER_MODEL") or "fallback"),
|
||||||
controller=controller,
|
controller=controller,
|
||||||
extend_planner_system_message=extend_planner_system_message(),
|
extend_planner_system_message=extend_planner_system_message(),
|
||||||
)
|
)
|
||||||
|
|
@ -214,7 +246,7 @@ async def scan_one_url(url: str, skip_html_check: bool = False):
|
||||||
await clean_resources(agent, session)
|
await clean_resources(agent, session)
|
||||||
# API 쿼터 문제인지 확인
|
# API 쿼터 문제인지 확인
|
||||||
if "ResourceExhausted" in str(e) or "429" in str(e):
|
if "ResourceExhausted" in str(e) or "429" in str(e):
|
||||||
wait = min(INITIAL_BACKOFF * (2 ** try_cnt), MAX_BACKOFF)
|
wait = min(INITIAL_BACKOFF * (2**try_cnt), MAX_BACKOFF)
|
||||||
print(f"⚠️ API 쿼터 에러: {e}. {wait}초 대기 후 재시도합니다...")
|
print(f"⚠️ API 쿼터 에러: {e}. {wait}초 대기 후 재시도합니다...")
|
||||||
await asyncio.sleep(wait)
|
await asyncio.sleep(wait)
|
||||||
try_cnt += 1
|
try_cnt += 1
|
||||||
|
|
@ -245,7 +277,9 @@ async def scan_one_url(url: str, skip_html_check: bool = False):
|
||||||
print("🔐 Detected OAuth Providers and URLs:")
|
print("🔐 Detected OAuth Providers and URLs:")
|
||||||
for entry in oauth_entries:
|
for entry in oauth_entries:
|
||||||
if "<" in entry.oauth_uri or "..." in entry.oauth_uri:
|
if "<" in entry.oauth_uri or "..." in entry.oauth_uri:
|
||||||
print(f"⚠️ WARNING: {entry.provider} URL may be masked or incomplete:\n{entry.oauth_uri}\n")
|
print(
|
||||||
|
f"⚠️ WARNING: {entry.provider} URL may be masked or incomplete:\n{entry.oauth_uri}\n"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
print(f"- {entry.provider}: {entry.oauth_uri}")
|
print(f"- {entry.provider}: {entry.oauth_uri}")
|
||||||
print("-" * 50)
|
print("-" * 50)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue