fix(gateway): preserve case-sensitive chat IDs in DeliveryTarget.parse

Fixes NousResearch/hermes-agent#11768

Root cause: target.strip().lower() was lowercasing the entire target string,
corrupting case-sensitive chat IDs like Slack C123ABC and Matrix !RoomABC.

Fix: Only lowercase the platform prefix for case-insensitive matching;
preserve the original case for chat_id and thread_id values.
This commit is contained in:
nightq 2026-04-18 09:55:21 +08:00 committed by kshitij
parent 2b3923ff13
commit 5cdc39e29a
2 changed files with 67 additions and 7 deletions

View File

@ -53,9 +53,10 @@ class DeliveryTarget:
- "telegram" Telegram home channel
- "telegram:123456" specific Telegram chat
"""
target = target.strip().lower()
target_stripped = target.strip()
target_lower = target_stripped.lower()
if target == "origin":
if target_lower == "origin":
if origin:
return cls(
platform=origin.platform,
@ -67,13 +68,14 @@ class DeliveryTarget:
# Fallback to local if no origin
return cls(platform=Platform.LOCAL, is_origin=True)
if target == "local":
if target_lower == "local":
return cls(platform=Platform.LOCAL)
# Check for platform:chat_id or platform:chat_id:thread_id format
if ":" in target:
parts = target.split(":", 2)
platform_str = parts[0]
# Use the original case for chat_id/thread_id to preserve case-sensitive IDs
if ":" in target_stripped:
parts = target_stripped.split(":", 2)
platform_str = parts[0].lower() # Platform names are case-insensitive
chat_id = parts[1] if len(parts) > 1 else None
thread_id = parts[2] if len(parts) > 2 else None
try:
@ -85,7 +87,7 @@ class DeliveryTarget:
# Just a platform name (use home channel)
try:
platform = Platform(target)
platform = Platform(target_lower)
return cls(platform=platform)
except ValueError:
# Unknown platform, treat as local

View File

@ -65,4 +65,62 @@ class TestTargetToStringRoundtrip:
assert reparsed.chat_id == "999"
class TestCaseSensitiveChatIdParsing:
"""Test that chat IDs preserve their original case (issue #11768)."""
def test_slack_uppercase_chat_id_preserved(self):
"""Slack channel IDs like C123ABC should preserve case."""
target = DeliveryTarget.parse("slack:C123ABC")
assert target.platform == Platform.SLACK
assert target.chat_id == "C123ABC" # Should NOT be lowercased to c123abc
assert target.is_explicit is True
def test_slack_chat_id_with_thread_preserved(self):
"""Slack channel:thread IDs should preserve case."""
target = DeliveryTarget.parse("slack:C123ABC:thread123")
assert target.platform == Platform.SLACK
assert target.chat_id == "C123ABC"
assert target.thread_id == "thread123"
def test_matrix_room_id_preserved(self):
"""Matrix room IDs like !RoomABC:example.org should preserve case.
Note: Matrix room IDs contain colons (e.g., !RoomABC:example.org).
Due to the platform:chat_id:thread_id format, these are parsed as
chat_id=!RoomABC and thread_id=example.org. This is a known limitation
of the current format. The fix preserves case but doesn't change the
parsing structure.
"""
target = DeliveryTarget.parse("matrix:!RoomABC:example.org")
assert target.platform == Platform.MATRIX
# The room ID is split at the first colon after the platform prefix
# This is a format limitation - the case is preserved but the structure is split
assert target.chat_id == "!RoomABC"
assert target.thread_id == "example.org"
def test_mixed_case_chat_id_roundtrip(self):
"""Mixed-case chat IDs should survive parse-to_string roundtrip."""
original = "telegram:ChatId123ABC"
target = DeliveryTarget.parse(original)
s = target.to_string()
reparsed = DeliveryTarget.parse(s)
assert reparsed.chat_id == "ChatId123ABC"
class TestPlatformNameCaseInsensitivity:
"""Test that platform names are case-insensitive."""
def test_uppercase_platform_name(self):
"""Platform names should be case-insensitive."""
target = DeliveryTarget.parse("TELEGRAM:12345")
assert target.platform == Platform.TELEGRAM
assert target.chat_id == "12345"
def test_mixed_case_platform_name(self):
"""Mixed-case platform names should work."""
target = DeliveryTarget.parse("TeleGram:12345")
assert target.platform == Platform.TELEGRAM
assert target.chat_id == "12345"