# 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