fix(gateway): skip topic recovery for non-DM messages
This commit is contained in:
parent
dc91ec931c
commit
d080dc24a8
|
|
@ -5545,11 +5545,16 @@ class BasePlatformAdapter(ABC):
|
|||
|
||||
coerce_plaintext_gateway_command(event)
|
||||
|
||||
# Rewrite ``event.source.thread_id`` via the installed recovery hook
|
||||
# (Telegram DM topic mode) so the session key, guard checks, and
|
||||
# downstream delivery all agree on the same lane.
|
||||
# Offloaded: the sync hook must not block the loop.
|
||||
await asyncio.to_thread(self._apply_topic_recovery, event)
|
||||
# Telegram topic recovery only applies to private DM topic lanes. Do
|
||||
# not submit a no-op check for group/forum/channel traffic to the
|
||||
# shared default executor: a busy pool would delay message dispatch.
|
||||
needs_topic_recovery = (
|
||||
getattr(self, "_topic_recovery_fn", None) is not None
|
||||
and event.source.platform == Platform.TELEGRAM
|
||||
and event.source.chat_type == "dm"
|
||||
)
|
||||
if needs_topic_recovery:
|
||||
await asyncio.to_thread(self._apply_topic_recovery, event)
|
||||
|
||||
session_key = build_session_key(
|
||||
event.source,
|
||||
|
|
|
|||
|
|
@ -115,6 +115,63 @@ def _debounced_event(adapter: BasePlatformAdapter, session_key: str) -> MessageE
|
|||
return adapter._text_debounce[session_key].event
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_non_dm_message_does_not_wait_for_topic_recovery_executor(monkeypatch):
|
||||
"""Group messages must not queue behind the shared thread pool.
|
||||
|
||||
Topic recovery only applies to Telegram DM topic mode. Offloading that
|
||||
no-op check for every group message makes ingress wait behind unrelated
|
||||
blocking jobs when the default executor is saturated.
|
||||
"""
|
||||
adapter = _make_adapter()
|
||||
recovery = MagicMock(return_value=None)
|
||||
adapter.set_topic_recovery_fn(recovery)
|
||||
executor_called = False
|
||||
never_release = asyncio.Event()
|
||||
|
||||
async def _blocked_to_thread(*args, **kwargs):
|
||||
nonlocal executor_called
|
||||
executor_called = True
|
||||
await never_release.wait()
|
||||
|
||||
monkeypatch.setattr(asyncio, "to_thread", _blocked_to_thread)
|
||||
|
||||
await asyncio.wait_for(
|
||||
adapter.handle_message(_make_event("/status", chat_type="group")),
|
||||
timeout=1.0,
|
||||
)
|
||||
await asyncio.sleep(0)
|
||||
|
||||
assert executor_called is False
|
||||
recovery.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_dm_topic_recovery_stays_offloaded(monkeypatch):
|
||||
"""Real Telegram DM topic recovery must still run outside the event loop."""
|
||||
adapter = _make_adapter()
|
||||
recovery = MagicMock(return_value="topic-222")
|
||||
adapter.set_topic_recovery_fn(recovery)
|
||||
offloaded = False
|
||||
|
||||
async def _inline_to_thread(func, *args, **kwargs):
|
||||
nonlocal offloaded
|
||||
offloaded = True
|
||||
return func(*args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(asyncio, "to_thread", _inline_to_thread)
|
||||
event = _make_event("hello", chat_type="dm", thread_id="1")
|
||||
original_source = event.source
|
||||
|
||||
await adapter.handle_message(event)
|
||||
await asyncio.sleep(0)
|
||||
|
||||
assert offloaded is True
|
||||
assert recovery.call_count == 1
|
||||
assert recovery.call_args.args[0] is original_source
|
||||
assert event.source.thread_id == "topic-222"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_rapid_text_followups_accumulate_instead_of_replacing():
|
||||
"""Rapid TEXT follow-ups must all survive in the pending event."""
|
||||
|
|
|
|||
Loading…
Reference in New Issue