# retoor <retoor@molodetz.nl>
import asyncio
import hashlib
import logging
import random
import re
import time
from datetime import datetime
from typing import Any, Optional
from faker import Faker
from devplacepy.services.bot.browser import BotBrowser
from devplacepy.services.bot.config import (
COMMENT_COOLDOWN_MAX_SECONDS,
COMMENT_COOLDOWN_MIN_SECONDS,
FEED_TOPICS,
GIST_LANGUAGES,
MAX_SIBLING_COMMENTS,
MAX_THREAD_REPLIES,
MENTION_REPLIES_PER_SESSION,
NOTIFICATION_CHECK_PROB,
PERSONAS,
PROJECT_STATUSES,
PROJECT_TYPES,
REACT_RATE_DEFAULT,
REACT_RATES,
SEARCH_TERMS,
SESSION_COMMENT_CAP_MAX,
SESSION_COMMENT_CAP_MIN,
SIBLING_SNIPPET_LEN,
USER_AGENTS,
VIEWPORTS,
persona_article_score,
pick_category,
)
from devplacepy.services.bot.handles import make_handle
from devplacepy.services.bot.llm import LLMClient
from devplacepy.services.bot.news_fetcher import NewsFetcher
from devplacepy.services.bot.registry import ArticleRegistry
from devplacepy.services.bot.runtime import BotRuntimeConfig
from devplacepy.services.bot.state import BotState
from devplacepy.services.bot.agent import BotAgentMixin
from devplacepy.services.bot.auth import BotAuthMixin
from devplacepy.services.bot.browse import BotBrowseMixin
from devplacepy.services.bot.engage import BotEngageMixin
from devplacepy.services.bot.helpers import BotHelpersMixin
from devplacepy.services.bot.human import BotHumanMixin
from devplacepy.services.bot.loop import BotLoopMixin
from devplacepy.services.bot.posting import BotPostingMixin
from devplacepy.services.bot.social import BotSocialMixin
logger = logging.getLogger(__name__)
class DevPlaceBot(
BotHelpersMixin,
BotAuthMixin,
BotHumanMixin,
BotAgentMixin,
BotEngageMixin,
BotPostingMixin,
BotSocialMixin,
BotBrowseMixin,
BotLoopMixin,
):
def __init__(self, cfg: BotRuntimeConfig, on_event=None, slot: int = 0):
self.cfg = cfg
self.base_url = cfg.base_url
self.state_path = cfg.state_path
self.on_event = on_event
self._slot = slot
self.state = BotState.load(cfg.state_path)
if not self.state.persona:
self.state.persona = random.choice(PERSONAS)
self._gateway_fallback_key = cfg.api_key
self.llm = LLMClient(
self.state.account_api_key or cfg.api_key,
cfg.api_url,
cfg.model,
cfg.input_cost_per_1m,
cfg.output_cost_per_1m,
gist_min_lines=cfg.gist_min_lines,
)
self.llm.total_cost = self.state.total_cost
self.llm.total_calls = self.state.total_calls
self.llm.total_in_tokens = self.state.total_in_tokens
self.llm.total_out_tokens = self.state.total_out_tokens
self.news = NewsFetcher(cfg.news_api)
self.registry = ArticleRegistry(
cfg.registry_path, cfg.max_per_article, cfg.article_ttl_days
)
self._pause_min = min(cfg.action_pause_min, cfg.action_pause_max)
self._pause_max = max(cfg.action_pause_min, cfg.action_pause_max)
self._break_scale = max(0.1, cfg.break_scale)
self._startup_jitter = max(0, cfg.startup_jitter_seconds)
self._ai_decisions = cfg.ai_decisions
self._decision_temperature = cfg.decision_temperature
self.faker = Faker()
if not self.state.user_agent:
self.state.user_agent = random.choice(USER_AGENTS)
if not self.state.viewport:
self.state.viewport = random.choice(VIEWPORTS)
self.b = BotBrowser(
headless=cfg.headless,
user_agent=self.state.user_agent,
viewport=self.state.viewport,
)
self._session_comments = 0
self._session_comment_cap = 0
self._session_mention_replies = 0
self._profile_verified = False
self._post_cache: list[tuple[str, str, str, str, str]] = []
self._project_cache: list[tuple[str, str]] = []
self._issue_cache: list[tuple[str, str]] = []
self._session_start_cost: float = 0.0
self._session_start_calls: int = 0
self._session_history: list[str] = []
self._session_energy: float = 0.5
self._session_stop_after: int = 0