2026-06-12 01:58:46 +02:00
|
|
|
# retoor <retoor@molodetz.nl>
|
|
|
|
|
|
2026-06-08 17:38:33 +02:00
|
|
|
import json
|
|
|
|
|
import logging
|
|
|
|
|
from dataclasses import dataclass, field, asdict
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
|
|
|
|
class BotState:
|
|
|
|
|
username: str = ""
|
|
|
|
|
email: str = ""
|
|
|
|
|
password: str = ""
|
|
|
|
|
uid: str = ""
|
|
|
|
|
persona: str = ""
|
2026-06-13 13:19:32 +02:00
|
|
|
profile_filled: bool = False
|
|
|
|
|
user_agent: str = ""
|
|
|
|
|
viewport: dict = field(default_factory=dict)
|
2026-06-11 20:52:56 +02:00
|
|
|
identity: dict = field(default_factory=dict)
|
2026-06-08 17:38:33 +02:00
|
|
|
log: list[str] = field(default_factory=list)
|
|
|
|
|
known_users: list[str] = field(default_factory=list)
|
|
|
|
|
known_posts: list[str] = field(default_factory=list)
|
|
|
|
|
created_posts: int = 0
|
|
|
|
|
posted_content_hashes: list[str] = field(default_factory=list)
|
|
|
|
|
comments_posted: int = 0
|
|
|
|
|
quality_rejections: int = 0
|
|
|
|
|
votes_cast: int = 0
|
|
|
|
|
bugs_filed: int = 0
|
|
|
|
|
projects_created: int = 0
|
|
|
|
|
gists_created: int = 0
|
|
|
|
|
profiles_viewed: int = 0
|
|
|
|
|
started_at: str = ""
|
|
|
|
|
own_post_urls: list[str] = field(default_factory=list)
|
|
|
|
|
voted_post_ids: list[str] = field(default_factory=list)
|
|
|
|
|
commented_post_ids: list[str] = field(default_factory=list)
|
|
|
|
|
notifications_seen: list[str] = field(default_factory=list)
|
2026-06-13 14:56:35 +02:00
|
|
|
thread_reply_counts: dict = field(default_factory=dict)
|
2026-06-08 17:38:33 +02:00
|
|
|
mentions_received: int = 0
|
|
|
|
|
mentions_replied: int = 0
|
|
|
|
|
replies_to_own_posts: int = 0
|
|
|
|
|
news_comments: int = 0
|
|
|
|
|
projects_starred: int = 0
|
|
|
|
|
messages_sent: int = 0
|
|
|
|
|
searches_run: int = 0
|
|
|
|
|
poll_votes_cast: int = 0
|
|
|
|
|
reactions_cast: int = 0
|
|
|
|
|
polls_voted: list[str] = field(default_factory=list)
|
|
|
|
|
reacted_targets: list[str] = field(default_factory=list)
|
|
|
|
|
reading_speed_wpm: int = 0
|
|
|
|
|
total_cost: float = 0.0
|
|
|
|
|
total_calls: int = 0
|
|
|
|
|
total_in_tokens: int = 0
|
|
|
|
|
total_out_tokens: int = 0
|
|
|
|
|
|
|
|
|
|
def save(self, path: Path) -> None:
|
|
|
|
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
tmp = path.with_suffix(path.suffix + ".tmp")
|
|
|
|
|
tmp.write_text(json.dumps(asdict(self), indent=2, default=str))
|
2026-06-12 06:30:08 +02:00
|
|
|
tmp.replace(path)
|
2026-06-08 17:38:33 +02:00
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def load(cls, path: Path) -> "BotState":
|
|
|
|
|
if path.exists():
|
|
|
|
|
try:
|
|
|
|
|
data = json.loads(path.read_text())
|
|
|
|
|
return cls(**data)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
logger.warning("Failed to load state: %s", e)
|
|
|
|
|
return cls()
|