forked from retoor/devplacepy
feat: add startup jitter, randomized browser fingerprints, and comment style variants to bot fleet
Introduce `startup_jitter_seconds` config field so each bot delays its first session by a random amount up to the configured maximum, spreading fleet activity on service start. Persist a randomly chosen user-agent and viewport per bot in `BotState` and pass them to `BotBrowser` constructor, replacing the hardcoded defaults with a varied pool defined in `config.py`. Add `HANDLE_SUFFIX_WORDS` list and remove numeric suffixes from handle generation to avoid bot-farm appearance. Implement `pick_comment_style` and `is_short_comment_style` static methods in `LLMClient`, extend `generate_comment` with a `style` parameter that produces one-liner or question variants using `MIN_SHORT_COMMENT_LEN`, and wire session comment caps (`SESSION_COMMENT_CAP_MIN/MAX`) with cooldown intervals (`COMMENT_COOLDOWN_MIN/MAX_SECONDS`) into the bot loop. Update `BotRuntimeConfig`, `BotsService` config field registration, and architecture docs to reflect the new identity and pacing logic.
This commit is contained in:
@@ -256,12 +256,25 @@ class LLMClient:
|
||||
return emoji
|
||||
return ""
|
||||
|
||||
@staticmethod
|
||||
def pick_comment_style() -> str:
|
||||
return random.choices(
|
||||
["standard", "oneliner", "question"],
|
||||
weights=[55, 23, 22],
|
||||
k=1,
|
||||
)[0]
|
||||
|
||||
@staticmethod
|
||||
def is_short_comment_style(style: str) -> bool:
|
||||
return style in ("oneliner", "question")
|
||||
|
||||
def generate_comment(
|
||||
self,
|
||||
post_snippet: str,
|
||||
persona: str = "",
|
||||
mention_target: str = "",
|
||||
parent_context: str = "",
|
||||
style: str = "",
|
||||
) -> str:
|
||||
extras = {
|
||||
"enthusiastic_junior": "Be excited. Use **bold** for agreement. Short replies.",
|
||||
@@ -277,6 +290,20 @@ class LLMClient:
|
||||
else " Be casual. No markdown."
|
||||
)
|
||||
preserve = persona in ("enthusiastic_junior", "mentor", "storyteller")
|
||||
if style == "oneliner":
|
||||
length_rule = (
|
||||
"Write ONE short casual line, under 12 words, like a quick reply "
|
||||
"you would type without thinking too hard. Lowercase is fine, "
|
||||
"contractions are fine, no markdown."
|
||||
)
|
||||
preserve = False
|
||||
elif style == "question":
|
||||
length_rule = (
|
||||
"Write a SINGLE pointed question about a specific detail in the post. "
|
||||
"One sentence. No preamble."
|
||||
)
|
||||
else:
|
||||
length_rule = f"Write 1-3 short sentences.{extra}"
|
||||
mention_rule = ""
|
||||
if mention_target:
|
||||
mention_rule = (
|
||||
@@ -287,7 +314,7 @@ class LLMClient:
|
||||
if parent_context:
|
||||
ctx = f"\n\nReplying to this comment:\n{parent_context[:800]}"
|
||||
system = (
|
||||
f"You are a dev replying to a post. Write 1-3 short sentences.{extra}{mention_rule} "
|
||||
f"You are a dev replying to a post. {length_rule}{mention_rule} "
|
||||
"No em dashes. Reference a specific detail from the post. "
|
||||
"Do not open with or rely on generic filler like 'great point', 'I agree', 'nice', "
|
||||
"'thanks for sharing', or 'interesting'. Add something real: a concrete experience, "
|
||||
@@ -313,17 +340,25 @@ class LLMClient:
|
||||
return hits / len(candidate)
|
||||
|
||||
def quality_check(
|
||||
self, kind: str, text: str, context: str = ""
|
||||
self, kind: str, text: str, context: str = "", style: str = ""
|
||||
) -> tuple[bool, str]:
|
||||
from devplacepy.services.bot.config import (
|
||||
MIN_COMMENT_LEN,
|
||||
MIN_SHORT_COMMENT_LEN,
|
||||
MIN_POST_LEN,
|
||||
RESTATEMENT_OVERLAP_THRESHOLD,
|
||||
GENERIC_COMMENT_PHRASES,
|
||||
)
|
||||
|
||||
stripped = self.clean(text or "").strip()
|
||||
min_len = MIN_COMMENT_LEN if kind == "comment" else MIN_POST_LEN
|
||||
if kind == "comment":
|
||||
min_len = (
|
||||
MIN_SHORT_COMMENT_LEN
|
||||
if self.is_short_comment_style(style)
|
||||
else MIN_COMMENT_LEN
|
||||
)
|
||||
else:
|
||||
min_len = MIN_POST_LEN
|
||||
if len(stripped) < min_len:
|
||||
return False, f"too short ({len(stripped)} < {min_len})"
|
||||
lowered = stripped.lower()
|
||||
@@ -593,10 +628,17 @@ class LLMClient:
|
||||
"rebel": "Be bold about why the usual approach is wrong.",
|
||||
"storyteller": "Open with the motivation behind it.",
|
||||
}.get(persona, "")
|
||||
shape = random.choice(
|
||||
[
|
||||
"Write one terse sentence. Just what it is. No tech stack.",
|
||||
"Write two sentences: what it does and the main tech behind it.",
|
||||
"Write 2 to 3 sentences including the tech stack and a trade-off you made.",
|
||||
"Write a short blurb, under 25 words, plain and understated.",
|
||||
]
|
||||
)
|
||||
return self.clean(
|
||||
self._call(
|
||||
"You are a developer. Write a compelling 2 to 3 sentence project description "
|
||||
f"including the tech stack and purpose. {flavor} No em dashes.",
|
||||
f"You are a developer describing your own project. {shape} {flavor} No em dashes.",
|
||||
f"Project: {title}",
|
||||
)
|
||||
)[:5000]
|
||||
|
||||
Reference in New Issue
Block a user