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:
63
cron/brave_search.py
Normal file
63
cron/brave_search.py
Normal file
@@ -0,0 +1,63 @@
|
||||
"""Brave Search executor for cron jobs."""
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
import httpx
|
||||
|
||||
from .formatter import format_search_results
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
BRAVE_API_KEY = os.environ.get("BRAVE_API_KEY", "")
|
||||
|
||||
|
||||
async def execute_brave_search(job: dict, send_text, **_kwargs) -> dict:
|
||||
"""Run a Brave Search query, dedup against known keys, post new results to Matrix."""
|
||||
if not BRAVE_API_KEY:
|
||||
return {"status": "error", "error": "BRAVE_API_KEY not configured"}
|
||||
|
||||
config = job.get("config", {})
|
||||
query = config.get("query", "")
|
||||
max_results = config.get("maxResults", 10)
|
||||
target_room = job["targetRoom"]
|
||||
dedup_keys = set(job.get("dedupKeys", []))
|
||||
|
||||
if not query:
|
||||
return {"status": "error", "error": "No search query configured"}
|
||||
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=15.0) as client:
|
||||
resp = await client.get(
|
||||
"https://api.search.brave.com/res/v1/web/search",
|
||||
headers={
|
||||
"Accept": "application/json",
|
||||
"X-Subscription-Token": BRAVE_API_KEY,
|
||||
},
|
||||
params={"q": query, "count": max_results, "text_decorations": False},
|
||||
)
|
||||
resp.raise_for_status()
|
||||
data = resp.json()
|
||||
|
||||
results = data.get("web", {}).get("results", [])
|
||||
if not results:
|
||||
return {"status": "no_results"}
|
||||
|
||||
# Dedup by URL
|
||||
new_results = [r for r in results if r.get("url") not in dedup_keys]
|
||||
|
||||
if not new_results:
|
||||
return {"status": "no_results"}
|
||||
|
||||
msg = format_search_results(job["name"], new_results)
|
||||
await send_text(target_room, msg)
|
||||
|
||||
new_keys = [r["url"] for r in new_results if r.get("url")]
|
||||
return {
|
||||
"status": "success",
|
||||
"newDedupKeys": new_keys,
|
||||
}
|
||||
|
||||
except Exception as exc:
|
||||
logger.error("Brave search cron failed: %s", exc, exc_info=True)
|
||||
return {"status": "error", "error": str(exc)}
|
||||
Reference in New Issue
Block a user