From f1bdefd834d6f4f29c916af60331592dd2ccbb9e Mon Sep 17 00:00:00 2001 From: retoor Date: Sat, 4 Jul 2026 23:08:41 +0000 Subject: [PATCH] feat: add _sanitize_mentions helper and apply to comment/reply truncation Introduce a new `_sanitize_mentions` method in `BotHelpersMixin` that deduplicates and removes self-mentions from text, using a compiled regex for `@handle` patterns. Apply this sanitizer before the 2000-character truncation in both `engage.py` comment posting and `social.py` reply posting to prevent duplicate or self-referential mentions from being cut off mid-handle. Additionally, fix profile URL parsing in `social.py` to strip trailing path segments, and refine `LLMClient.clean` to handle asterisks and underscores more precisely without breaking adjacent alphanumeric characters. --- devplacepy/services/bot/engage.py | 4 ++-- devplacepy/services/bot/helpers.py | 17 +++++++++++++++++ devplacepy/services/bot/llm.py | 5 ++++- devplacepy/services/bot/social.py | 7 +++++-- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/devplacepy/services/bot/engage.py b/devplacepy/services/bot/engage.py index 92804395..3b81d687 100644 --- a/devplacepy/services/bot/engage.py +++ b/devplacepy/services/bot/engage.py @@ -305,7 +305,7 @@ class BotEngageMixin: else: comment = f"@{mention_target} {comment}" - comment = comment[:2000] + comment = self._sanitize_mentions(comment)[:2000] mention_log = f" @{mention_target}" if mention_target else "" textarea_sels = [ @@ -437,7 +437,7 @@ class BotEngageMixin: return False if mentioner and f"@{mentioner.lower()}" not in reply.lower(): reply = f"@{mentioner} {reply}" - reply = reply[:2000] + reply = self._sanitize_mentions(reply)[:2000] textarea_sels = [ ".reply-form textarea[name='content']", ".comment-form textarea[name='content']", diff --git a/devplacepy/services/bot/helpers.py b/devplacepy/services/bot/helpers.py index fb5bc067..9f5a7aa3 100644 --- a/devplacepy/services/bot/helpers.py +++ b/devplacepy/services/bot/helpers.py @@ -11,6 +11,8 @@ from devplacepy.services.bot.config import persona_article_score, pick_category logger = logging.getLogger(__name__) +_MENTION_RE = re.compile(r"@([A-Za-z0-9_-]+)") + class BotHelpersMixin: def _identity(self) -> str: @@ -31,6 +33,21 @@ class BotHelpersMixin: m = re.search(r"/(?:posts|gists|news|projects)/([^/?#]+)", url) return m.group(1) if m else "" + def _sanitize_mentions(self, text: str) -> str: + own = (self.state.username or "").lower() + seen: set[str] = set() + + def keep(match: "re.Match[str]") -> str: + handle = match.group(1) + lowered = handle.lower() + if lowered == own or lowered in seen: + return "" + seen.add(lowered) + return match.group(0) + + cleaned = _MENTION_RE.sub(keep, text) + return re.sub(r"\s{2,}", " ", cleaned).strip() + def _sync_cost(self) -> None: self.state.total_cost = self.llm.total_cost self.state.total_calls = self.llm.total_calls diff --git a/devplacepy/services/bot/llm.py b/devplacepy/services/bot/llm.py index d6b728a2..67688b16 100644 --- a/devplacepy/services/bot/llm.py +++ b/devplacepy/services/bot/llm.py @@ -115,7 +115,10 @@ class LLMClient: @staticmethod def clean(text: str, preserve_md: bool = False) -> str: if not preserve_md: - text = re.sub(r"\*\*|__|\*|_", "", text) + text = re.sub(r"\*+", "", text) + text = re.sub(r"(?