feat(MAT-174): Add cron job scheduler and executors

Cron package that syncs jobs from matrixhost portal API, schedules execution
with timezone-aware timing, and posts results to Matrix rooms. Includes
Brave Search, reminder, and browser scrape (placeholder) executors with
formatter. 31 pytest tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Christian Gick
2026-03-16 09:31:19 +02:00
parent 21b8a4efb1
commit 4d8ea44b3d
15 changed files with 1009 additions and 0 deletions

55
cron/formatter.py Normal file
View File

@@ -0,0 +1,55 @@
"""Format cron job results as Matrix messages (markdown)."""
def format_search_results(job_name: str, results: list[dict]) -> str:
"""Format Brave Search results as a markdown message for Matrix."""
count = len(results)
lines = [f"**{job_name}** \u2014 {count} new result{'s' if count != 1 else ''}:\n"]
for i, r in enumerate(results, 1):
title = r.get("title", "Untitled")
url = r.get("url", "")
desc = r.get("description", "")
lines.append(f"{i}. **[{title}]({url})**")
if desc:
lines.append(f" {desc}")
lines.append("")
lines.append(
"_Manage automations: https://matrixhost.eu/settings/automations_"
)
return "\n".join(lines)
def format_listings(job_name: str, listings: list[dict]) -> str:
"""Format browser-scraped listings as a markdown message for Matrix."""
count = len(listings)
lines = [f"**{job_name}** \u2014 {count} new listing{'s' if count != 1 else ''}:\n"]
for i, item in enumerate(listings, 1):
title = item.get("title", "Unknown")
price = item.get("price", "")
location = item.get("location", "")
url = item.get("url", "")
age = item.get("age", "")
line = f"{i}. **{title}**"
if price:
line += f" \u2014 {price}"
lines.append(line)
details = []
if location:
details.append(f"\U0001f4cd {location}")
if age:
details.append(f"\U0001f4c5 {age}")
if url:
details.append(f"[View listing]({url})")
if details:
lines.append(f" {' \u00b7 '.join(details)}")
lines.append("")
lines.append(
"_Manage automations: https://matrixhost.eu/settings/automations_"
)
return "\n".join(lines)