|
# retoor <retoor@molodetz.nl>
|
|
|
|
import asyncio
|
|
import logging
|
|
import random
|
|
from datetime import datetime
|
|
|
|
from devplacepy.services.bot.config import SEARCH_TERMS
|
|
from devplacepy.services.bot.handles import make_handle
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class BotAuthMixin:
|
|
def _generate_handle(self) -> str:
|
|
return make_handle(SEARCH_TERMS.get(self.state.persona, []))
|
|
|
|
def _next_handle(self) -> str:
|
|
while self.state.handle_candidates:
|
|
candidate = self.state.handle_candidates.pop(0)
|
|
if candidate:
|
|
return candidate
|
|
return self._generate_handle()
|
|
|
|
async def _ensure_auth(self) -> bool:
|
|
b = self.b
|
|
if not self.state.username:
|
|
if not self.state.handle_candidates:
|
|
self.state.handle_candidates = (
|
|
await self._generate(
|
|
self.llm.generate_handle_candidates, self.state.persona
|
|
)
|
|
or []
|
|
)
|
|
self.state.username = self._next_handle()
|
|
self.state.account_api_key = ""
|
|
self.state.email = self.faker.email()
|
|
self.state.password = self.faker.password(length=random.randint(12, 18))
|
|
self.state.started_at = datetime.now().isoformat()
|
|
self._log(f"Identity: {self.state.username} / {self.state.email}")
|
|
|
|
curl = await b.url()
|
|
if "/feed" in curl:
|
|
return True
|
|
|
|
await b.goto(f"{self.base_url}/auth/login")
|
|
curl = await b.url()
|
|
if "/feed" in curl:
|
|
return True
|
|
|
|
if await b.fill("#email", self.state.email):
|
|
await b._idle(0.2, 0.5)
|
|
await b.fill("#password", self.state.password)
|
|
await b._idle(0.3, 0.8)
|
|
await b.click(".auth-submit")
|
|
await asyncio.sleep(2)
|
|
curl = await b.url()
|
|
if "/feed" in curl:
|
|
self._log("Logged in")
|
|
return True
|
|
|
|
self._log("Login failed, registering")
|
|
for attempt in range(4):
|
|
await b.goto(f"{self.base_url}/auth/signup")
|
|
if not await b.fill("#username", self.state.username):
|
|
return False
|
|
await b._idle(0.2, 0.5)
|
|
await b.fill("#email", self.state.email)
|
|
await b._idle(0.2, 0.5)
|
|
await b.fill("#password", self.state.password)
|
|
await b._idle(0.2, 0.5)
|
|
await b.fill("#confirm_password", self.state.password)
|
|
await b._idle(0.5, 1.0)
|
|
await b.click(".auth-submit")
|
|
await asyncio.sleep(2)
|
|
curl = await b.url()
|
|
if "/feed" in curl:
|
|
self._log(f"Registered as {self.state.username}")
|
|
return True
|
|
if attempt >= 2:
|
|
base = self._next_handle()
|
|
self.state.username = f"{base}{random.randint(10, 9999)}"[:20]
|
|
else:
|
|
self.state.username = self._next_handle()
|
|
self.state.account_api_key = ""
|
|
self.state.email = self.faker.email()
|
|
self._log(f"Signup failed, retrying as {self.state.username}")
|
|
return False
|
|
|
|
async def _fetch_account_api_key(self) -> str:
|
|
if not self.state.username:
|
|
return ""
|
|
try:
|
|
key = await self.b.page.evaluate(
|
|
"""async (username) => {
|
|
const resp = await fetch(`/profile/${username}`, {
|
|
headers: {Accept: 'application/json'},
|
|
});
|
|
if (!resp.ok) return '';
|
|
const data = await resp.json();
|
|
return data.api_key || '';
|
|
}""",
|
|
self.state.username,
|
|
)
|
|
except Exception as e:
|
|
logger.debug("fetch account api key failed: %s", e)
|
|
return ""
|
|
return (key or "").strip()
|
|
|
|
async def _adopt_account_api_key(self) -> bool:
|
|
for attempt in range(5):
|
|
key = await self._fetch_account_api_key()
|
|
if key:
|
|
if key != self.state.account_api_key:
|
|
self.state.account_api_key = key
|
|
self._save()
|
|
self._log("Adopted account API key for gateway calls")
|
|
self.llm.api_key = key
|
|
return True
|
|
if attempt < 4:
|
|
await asyncio.sleep(2)
|
|
self._log("Account API key unavailable after retries")
|
|
return False
|