fix(zulip): add _strip_html for slash command matching

Zulip delivers message content as HTML (<p>/approve</p>).
The gateway slash command parser expects plain text, so HTML
tags prevent command matching. This helper strips HTML tags
and decodes common entities (&amp;, &lt;, etc.).

Per contract: zulip-approval-fix.prose.md

Applied to: Mumuni (CT114), Tanko (CT112), Koby (CT111)
This commit is contained in:
2026-07-13 06:49:43 +00:00
committed by Abiba (pi)
parent bfc6e1877d
commit 5d9e36f657
+19
View File
@@ -99,6 +99,23 @@ def _build_auth_header(email: str, api_key: str) -> str:
return f"Basic {token}"
def _strip_html(text: str) -> str:
"""Strip HTML tags and decode HTML entities from Zulip message content.
Zulip delivers message content as rendered HTML (e.g. <p>/approve</p>).
The gateway slash command parser expects plain text, so HTML tags
prevent command matching. This helper strips tags and decodes entities.
"""
if not text:
return text
# Strip HTML tags
text = re.sub(r"<[^>]+>", "", text)
# Decode common HTML entities
text = text.replace("&amp;", "&").replace("&lt;", "<").replace("&gt;", ">")
text = text.replace("&quot;", '"').replace("&#39;", "'").replace("&nbsp;", " ")
return text.strip()
def _truncate(text: str, limit: int = MAX_ZULIP_MESSAGE) -> str:
"""Truncate to Zulip's message limit with notice."""
if len(text) <= limit:
@@ -784,6 +801,8 @@ class ZulipAdapter(BasePlatformAdapter):
sender_name = msg.get("sender_full_name", "Unknown")
sender_id = msg.get("sender_id")
content = msg.get("content", "")
# Strip Zulip HTML for slash command matching (zulip-approval-fix contract)
content = _strip_html(content)
# Echo-loop prevention: skip own messages
if sender_email == self._bot_email: