Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions cron/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,62 @@ def _resolve_delivery_target(job: dict) -> Optional[dict]:
_IMAGE_EXTS = frozenset({'.jpg', '.jpeg', '.png', '.webp', '.gif'})


def _describe_media_for_delivery_mirror(media_files: list) -> str:
"""Return a compact transcript summary when a cron delivery only sent media."""
if not media_files:
return ""
if len(media_files) == 1:
media_path, _is_voice = media_files[0]
ext = Path(media_path).suffix.lower()
if ext in _IMAGE_EXTS:
return "[Cronjob sent image attachment]"
if ext in _VIDEO_EXTS:
return "[Cronjob sent video attachment]"
if ext in _AUDIO_EXTS:
return "[Cronjob sent audio attachment]"
return "[Cronjob sent document attachment]"
return f"[Cronjob sent {len(media_files)} media attachments]"


def _mirror_delivered_result_to_session(
job: dict,
target: dict,
content: str,
media_files: list,
) -> None:
"""Record a successfully delivered cron response in the target chat session."""
mirror_text = (content or "").strip() or _describe_media_for_delivery_mirror(media_files)
if not mirror_text:
return

try:
from gateway.mirror import mirror_to_session

mirrored = mirror_to_session(
str(target["platform"]),
str(target["chat_id"]),
mirror_text,
source_label="cron",
thread_id=target.get("thread_id"),
)
if mirrored:
logger.info(
"Job '%s': mirrored delivered cron response to %s:%s",
job.get("id", "?"),
target.get("platform"),
target.get("chat_id"),
)
else:
logger.debug(
"Job '%s': no matching gateway session for cron delivery mirror to %s:%s",
job.get("id", "?"),
target.get("platform"),
target.get("chat_id"),
)
except Exception as exc:
logger.debug("Job '%s': cron delivery mirror failed: %s", job.get("id", "?"), exc)


def _send_media_via_adapter(
adapter,
chat_id: str,
Expand Down Expand Up @@ -575,6 +631,7 @@ def _deliver_result(job: dict, content: str, adapters=None, loop=None) -> Option
if adapter_ok:
logger.info("Job '%s': delivered to %s:%s via live adapter", job["id"], platform_name, chat_id)
delivered = True
_mirror_delivered_result_to_session(job, target, cleaned_delivery_content, media_files)
except Exception as e:
logger.warning(
"Job '%s': live adapter delivery to %s:%s failed (%s), falling back to standalone",
Expand Down Expand Up @@ -608,6 +665,7 @@ def _deliver_result(job: dict, content: str, adapters=None, loop=None) -> Option
continue

logger.info("Job '%s': delivered to %s:%s", job["id"], platform_name, chat_id)
_mirror_delivered_result_to_session(job, target, cleaned_delivery_content, media_files)

if delivery_errors:
return "; ".join(delivery_errors)
Expand Down
33 changes: 31 additions & 2 deletions tests/cron/test_scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,8 +637,8 @@ def fake_run_coro(coro, _loop):
assert "MEDIA:" not in text_sent
assert "Report" in text_sent

def test_no_mirror_to_session_call(self):
"""Cron deliveries should NOT mirror into the gateway session."""
def test_successful_delivery_mirrors_to_target_session(self):
"""Cron deliveries should be remembered by the chat that received them."""
from gateway.config import Platform

pconfig = MagicMock()
Expand All @@ -656,6 +656,35 @@ def test_no_mirror_to_session_call(self):
}
_deliver_result(job, "Hello!")

mirror_mock.assert_called_once()
args, kwargs = mirror_mock.call_args
platform_name, chat_id, mirrored_text = args[:3]
assert platform_name == "telegram"
assert chat_id == "123"
assert "Cronjob Response: test-job" in mirrored_text
assert "Hello!" in mirrored_text
assert kwargs == {"source_label": "cron", "thread_id": None}

def test_failed_delivery_does_not_mirror_to_target_session(self):
"""Failed cron deliveries must not create fake chat history."""
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={"error": "boom"})), \
patch("gateway.mirror.mirror_to_session") as mirror_mock:
job = {
"id": "test-job",
"deliver": "origin",
"origin": {"platform": "telegram", "chat_id": "123"},
}
err = _deliver_result(job, "Hello!")

assert "delivery error" in err
mirror_mock.assert_not_called()

def test_origin_delivery_preserves_thread_id(self):
Expand Down