fix: extract MEDIA: tags from cron delivery before sending (#5598)
The cron scheduler delivery path passed raw text including MEDIA: tags to _send_to_platform(), so media attachments were delivered as literal text instead of actual files. The send function already supports media_files= but the cron path never used it. Now calls BasePlatformAdapter.extract_media() to split media paths from text before sending, matching the gateway's normal message flow. Salvaged from PR #4877 by robert-hoffmann.
This commit is contained in:
@@ -237,6 +237,10 @@ def _deliver_result(job: dict, content: str, adapters=None, loop=None) -> None:
|
||||
else:
|
||||
delivery_content = content
|
||||
|
||||
# Extract MEDIA: tags so attachments are forwarded as files, not raw text
|
||||
from gateway.platforms.base import BasePlatformAdapter
|
||||
media_files, cleaned_delivery_content = BasePlatformAdapter.extract_media(delivery_content)
|
||||
|
||||
# Prefer the live adapter when the gateway is running — this supports E2EE
|
||||
# rooms (e.g. Matrix) where the standalone HTTP path cannot encrypt.
|
||||
runtime_adapter = (adapters or {}).get(platform)
|
||||
@@ -264,7 +268,7 @@ def _deliver_result(job: dict, content: str, adapters=None, loop=None) -> None:
|
||||
)
|
||||
|
||||
# Standalone path: run the async send in a fresh event loop (safe from any thread)
|
||||
coro = _send_to_platform(platform, pconfig, chat_id, delivery_content, thread_id=thread_id)
|
||||
coro = _send_to_platform(platform, pconfig, chat_id, cleaned_delivery_content, thread_id=thread_id, media_files=media_files)
|
||||
try:
|
||||
result = asyncio.run(coro)
|
||||
except RuntimeError:
|
||||
@@ -275,7 +279,7 @@ def _deliver_result(job: dict, content: str, adapters=None, loop=None) -> None:
|
||||
coro.close()
|
||||
import concurrent.futures
|
||||
with concurrent.futures.ThreadPoolExecutor(max_workers=1) as pool:
|
||||
future = pool.submit(asyncio.run, _send_to_platform(platform, pconfig, chat_id, delivery_content, thread_id=thread_id))
|
||||
future = pool.submit(asyncio.run, _send_to_platform(platform, pconfig, chat_id, cleaned_delivery_content, thread_id=thread_id, media_files=media_files))
|
||||
result = future.result(timeout=30)
|
||||
except Exception as e:
|
||||
logger.error("Job '%s': delivery to %s:%s failed: %s", job["id"], platform_name, chat_id, e)
|
||||
|
||||
@@ -250,6 +250,33 @@ class TestDeliverResultWrapping:
|
||||
assert "Cronjob Response" not in sent_content
|
||||
assert "The agent cannot see" not in sent_content
|
||||
|
||||
def test_delivery_extracts_media_tags_before_send(self):
|
||||
"""Cron delivery should pass MEDIA attachments separately to the send helper."""
|
||||
from gateway.config import Platform
|
||||
|
||||
pconfig = MagicMock()
|
||||
pconfig.enabled = True
|
||||
mock_cfg = MagicMock()
|
||||
mock_cfg.platforms = {Platform.TELEGRAM: pconfig}
|
||||
|
||||
with patch("gateway.config.load_gateway_config", return_value=mock_cfg), \
|
||||
patch("tools.send_message_tool._send_to_platform", new=AsyncMock(return_value={"success": True})) as send_mock, \
|
||||
patch("cron.scheduler.load_config", return_value={"cron": {"wrap_response": False}}):
|
||||
job = {
|
||||
"id": "voice-job",
|
||||
"deliver": "origin",
|
||||
"origin": {"platform": "telegram", "chat_id": "123"},
|
||||
}
|
||||
_deliver_result(job, "Title\nMEDIA:/tmp/test-voice.ogg")
|
||||
|
||||
send_mock.assert_called_once()
|
||||
args, kwargs = send_mock.call_args
|
||||
# Text content should have MEDIA: tag stripped
|
||||
assert "MEDIA:" not in args[3]
|
||||
assert "Title" in args[3]
|
||||
# Media files should be forwarded separately
|
||||
assert kwargs["media_files"] == [("/tmp/test-voice.ogg", False)]
|
||||
|
||||
def test_no_mirror_to_session_call(self):
|
||||
"""Cron deliveries should NOT mirror into the gateway session."""
|
||||
from gateway.config import Platform
|
||||
|
||||
Reference in New Issue
Block a user