135 lines
3.4 KiB
Python
Executable File
135 lines
3.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
import json
|
|
import requests
|
|
from pathlib import Path
|
|
from datetime import datetime
|
|
|
|
docstring = """
|
|
This script reads log files generated by various Org automation scripts,
|
|
parses their contents to determine success or failure statuses, and sends
|
|
a summary report as an embed message to a specified Discord webhook URL.
|
|
|
|
It runs on a cron schedule daily at 6 AM.
|
|
"""
|
|
|
|
# -----------------------------
|
|
# CONFIG
|
|
# -----------------------------
|
|
LOG_DIR = Path("/home/zaine/logs")
|
|
|
|
CALIBRE_LOG = LOG_DIR / "org-books-calibre.log"
|
|
ROAM_LOG = LOG_DIR / "org-roam.log"
|
|
WEB_LOG = LOG_DIR / "org-web.log"
|
|
|
|
DISCORD_WEBHOOK_URL = "https://discordapp.com/api/webhooks/1460004143870513464/kRSTsNMXU8AL3Pnj-t1EUKxrLb6cBKROdMVyowX02P46CPmayiB-RHi4UH5g6tCZQf5l"
|
|
|
|
# -----------------------------
|
|
# HELPERS
|
|
# -----------------------------
|
|
def send_discord_embed(embed: dict):
|
|
payload = {
|
|
"embeds": [embed]
|
|
}
|
|
r = requests.post(DISCORD_WEBHOOK_URL, json=payload, timeout=10)
|
|
r.raise_for_status()
|
|
|
|
|
|
def parse_calibre_log(path: Path):
|
|
successes = 0
|
|
failures = 0
|
|
last_ts = None
|
|
|
|
if not path.exists():
|
|
return successes, failures, last_ts
|
|
|
|
for line in path.read_text().splitlines():
|
|
try:
|
|
entry = json.loads(line)
|
|
status = entry.get("status")
|
|
ts = entry.get("timestamp")
|
|
|
|
if status == "success":
|
|
successes += 1
|
|
else:
|
|
failures += 1
|
|
|
|
if ts:
|
|
last_ts = ts
|
|
except json.JSONDecodeError:
|
|
failures += 1
|
|
|
|
return successes, failures, last_ts
|
|
|
|
|
|
def parse_roam_log(path: Path):
|
|
if not path.exists():
|
|
return False
|
|
return "✅ Full rebuild complete" in path.read_text()
|
|
|
|
|
|
def parse_web_log(path: Path):
|
|
if not path.exists():
|
|
return False
|
|
|
|
text = path.read_text()
|
|
return (
|
|
"Successfully installed" in text
|
|
and "JSON saved to" in text
|
|
)
|
|
|
|
|
|
def format_ts(ts):
|
|
if not ts:
|
|
return "Unknown"
|
|
try:
|
|
return datetime.fromisoformat(ts).strftime("%Y-%m-%d %H:%M")
|
|
except ValueError:
|
|
return ts
|
|
|
|
|
|
# -----------------------------
|
|
# MAIN
|
|
# -----------------------------
|
|
def main():
|
|
cal_ok, cal_fail, cal_last = parse_calibre_log(CALIBRE_LOG)
|
|
roam_ok = parse_roam_log(ROAM_LOG)
|
|
web_ok = parse_web_log(WEB_LOG)
|
|
|
|
all_ok = (cal_fail == 0) and roam_ok and web_ok
|
|
|
|
embed = {
|
|
"title": "Org Automation Status",
|
|
"color": 0x2ECC71 if all_ok else 0xE74C3C,
|
|
"fields": [
|
|
{
|
|
"name": "Calibre → Org",
|
|
"value": (
|
|
f"✅ Successes: **{cal_ok}**\n"
|
|
f"❌ Failures: **{cal_fail}**\n"
|
|
f"⏱ Last run: `{format_ts(cal_last)}`"
|
|
),
|
|
"inline": False
|
|
},
|
|
{
|
|
"name": "Org-Roam",
|
|
"value": "✅ Successful rebuild" if roam_ok else "❌ No success marker found",
|
|
"inline": True
|
|
},
|
|
{
|
|
"name": "Org-Web",
|
|
"value": "✅ Build + index complete" if web_ok else "❌ Build or index incomplete",
|
|
"inline": True
|
|
}
|
|
],
|
|
"footer": {
|
|
"text": "Generated automatically from ~/logs"
|
|
},
|
|
"timestamp": datetime.utcnow().isoformat()
|
|
}
|
|
|
|
send_discord_embed(embed)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|