fix(cron): accept list-form deliver values so deliver=['telegram'] works (#17456)

The cron schema contracts deliver as a string ("local", "origin",
"telegram", "telegram:chat_id[:thread_id]", or comma-separated combos),
but MCP clients and scripts sometimes pass an array like ['telegram'].

Before this change, the list was written to jobs.json verbatim, and
the scheduler's str(deliver).split(',') then tried to resolve the
literal string "['telegram']" as a platform — returning None and
logging 'no delivery target resolved for deliver=[\'telegram\']'.

Fix on both ends:
- tools/cronjob_tools.py: normalize deliver at the API boundary on
  create and update, so storage is always a string.
- cron/scheduler.py: normalize deliver in _resolve_delivery_targets,
  so existing jobs.json entries with list-form deliver are handled
  gracefully without requiring users to edit the file.

Closes #17139
This commit is contained in:
Teknium 2026-04-29 06:35:34 -07:00 committed by GitHub
parent 7141cda967
commit 398945e7b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 140 additions and 4 deletions

View file

@ -233,12 +233,32 @@ def _resolve_single_delivery_target(job: dict, deliver_value: str) -> Optional[d
}
def _normalize_deliver_value(deliver) -> str:
"""Normalize a stored/submitted ``deliver`` value to its canonical string form.
The contract is that ``deliver`` is a string (``"local"``, ``"origin"``,
``"telegram"``, ``"telegram:-1001:17"``, or comma-separated combinations).
Historically some callers MCP clients passing an array, direct edits of
``jobs.json``, or stale code paths have stored a list/tuple like
``["telegram"]``. ``str(["telegram"])`` would serialize to the literal
string ``"['telegram']"``, which is not a known platform and fails
resolution silently. Flatten lists/tuples into a comma-separated string
so both forms work. Returns ``"local"`` for anything falsy.
"""
if deliver is None or deliver == "":
return "local"
if isinstance(deliver, (list, tuple)):
parts = [str(p).strip() for p in deliver if str(p).strip()]
return ",".join(parts) if parts else "local"
return str(deliver)
def _resolve_delivery_targets(job: dict) -> List[dict]:
"""Resolve all concrete auto-delivery targets for a cron job (supports comma-separated deliver)."""
deliver = job.get("deliver", "local")
deliver = _normalize_deliver_value(job.get("deliver", "local"))
if deliver == "local":
return []
parts = [p.strip() for p in str(deliver).split(",") if p.strip()]
parts = [p.strip() for p in deliver.split(",") if p.strip()]
seen = set()
targets = []
for part in parts: