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.
This commit is contained in:
retoor 2026-07-04 23:08:41 +00:00
parent 0f872336b1
commit f1bdefd834
4 changed files with 28 additions and 5 deletions

View File

@ -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']",

View File

@ -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

View File

@ -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"(?<![A-Za-z0-9])__(?=\S)(.*?)(?<=\S)__(?![A-Za-z0-9])", r"\1", text)
text = re.sub(r"(?<![A-Za-z0-9])_(?=\S)(.*?)(?<=\S)_(?![A-Za-z0-9])", r"\1", text)
text = re.sub(r"(?<![A-Za-z0-9])_+(?![A-Za-z0-9])", "", text)
text = text.replace("", "-").replace("", "-")
text = text.replace("", "'").replace("", "'")
text = text.replace("", '"').replace("", '"')

View File

@ -28,7 +28,7 @@ class BotSocialMixin:
try:
await links.nth(i).click(timeout=5000)
await b._idle(1.0, 2.0)
uname = h.split("/profile/")[-1].split("?")[0]
uname = h.split("/profile/")[-1].split("?")[0].split("/")[0]
if uname not in self.state.known_users:
self.state.known_users.append(uname)
self.state.profiles_viewed += 1
@ -446,6 +446,9 @@ class BotSocialMixin:
)
except Exception:
mentioner = ""
if mentioner and mentioner.lower() == self.state.username.lower():
self._log("Mention target resolves to self, skipping")
return False
try:
await comment_loc.scroll_into_view_if_needed(timeout=2000)
await b._idle(0.3, 0.9)
@ -485,7 +488,7 @@ class BotSocialMixin:
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 = [
".comment .reply-form textarea[name='content']",
".reply-form textarea[name='content']",