175 lines
5.2 KiB
Python
175 lines
5.2 KiB
Python
|
|
#!/usr/bin/env python3
|
||
|
|
"""
|
||
|
|
Process pending alerts from the queue.
|
||
|
|
|
||
|
|
This script outputs alerts in a format that the OpenClaw agent can parse
|
||
|
|
and send via the message tool.
|
||
|
|
|
||
|
|
Usage:
|
||
|
|
python3 process_alerts.py # Show pending alerts
|
||
|
|
python3 process_alerts.py --json # Output as JSON for agent processing
|
||
|
|
python3 process_alerts.py --mark-sent # Mark all as sent (after agent sends them)
|
||
|
|
"""
|
||
|
|
|
||
|
|
import os
|
||
|
|
import sys
|
||
|
|
import json
|
||
|
|
import argparse
|
||
|
|
from pathlib import Path
|
||
|
|
from datetime import datetime
|
||
|
|
|
||
|
|
sys.path.insert(0, str(Path(__file__).parent))
|
||
|
|
|
||
|
|
from config import get_pending_alerts, mark_alert_sent, clear_old_alerts
|
||
|
|
|
||
|
|
|
||
|
|
def format_alert_message(alert: dict) -> str:
|
||
|
|
"""Format an alert as a nice message."""
|
||
|
|
if alert.get("message"):
|
||
|
|
return alert["message"]
|
||
|
|
|
||
|
|
# Build message from components
|
||
|
|
emoji_map = {"high": "🔥", "medium": "📌", "low": "📝"}
|
||
|
|
emoji = emoji_map.get(alert.get("priority", "medium"), "📌")
|
||
|
|
|
||
|
|
lines = [
|
||
|
|
f"{emoji} **{alert.get('topic_name', 'Alert')}**",
|
||
|
|
"",
|
||
|
|
f"**{alert.get('title', 'Untitled')}**",
|
||
|
|
"",
|
||
|
|
]
|
||
|
|
|
||
|
|
snippet = alert.get("snippet", "")
|
||
|
|
if snippet:
|
||
|
|
if len(snippet) > 300:
|
||
|
|
snippet = snippet[:297] + "..."
|
||
|
|
lines.append(snippet)
|
||
|
|
lines.append("")
|
||
|
|
|
||
|
|
if alert.get("context"):
|
||
|
|
lines.append(f"💡 _Context: {alert['context']}_")
|
||
|
|
lines.append("")
|
||
|
|
|
||
|
|
if alert.get("url"):
|
||
|
|
lines.append(f"🔗 {alert['url']}")
|
||
|
|
lines.append("")
|
||
|
|
|
||
|
|
lines.append(f"📊 _Score: {alert.get('score', 0):.2f} | {alert.get('reason', '')}_")
|
||
|
|
if alert.get("sentiment"):
|
||
|
|
lines.append(f"🙂 _Sentiment: {alert['sentiment']}_")
|
||
|
|
if alert.get("sentiment_shift"):
|
||
|
|
lines.append("🔄 _Sentiment shift detected_")
|
||
|
|
|
||
|
|
return "\n".join(lines)
|
||
|
|
|
||
|
|
|
||
|
|
def show_pending_alerts():
|
||
|
|
"""Show pending alerts in human-readable format."""
|
||
|
|
alerts = get_pending_alerts()
|
||
|
|
|
||
|
|
if not alerts:
|
||
|
|
print("✅ No pending alerts")
|
||
|
|
return
|
||
|
|
|
||
|
|
print(f"\n📬 Pending Alerts: {len(alerts)}\n")
|
||
|
|
|
||
|
|
for i, alert in enumerate(alerts, 1):
|
||
|
|
print(f"{'='*60}")
|
||
|
|
print(f"Alert #{i} - {alert.get('id', 'unknown')}")
|
||
|
|
print(f"{'='*60}")
|
||
|
|
print(f"Priority: {alert.get('priority', 'unknown').upper()}")
|
||
|
|
print(f"Channel: {alert.get('channel', 'telegram')}")
|
||
|
|
print(f"Topic: {alert.get('topic_name', 'unknown')}")
|
||
|
|
print(f"Title: {alert.get('title', 'untitled')[:60]}")
|
||
|
|
print(f"URL: {alert.get('url', '')}")
|
||
|
|
print(f"Score: {alert.get('score', 0):.2f}")
|
||
|
|
print(f"Timestamp: {alert.get('timestamp', '')}")
|
||
|
|
print()
|
||
|
|
print("Message Preview:")
|
||
|
|
print("-" * 40)
|
||
|
|
print(format_alert_message(alert))
|
||
|
|
print()
|
||
|
|
|
||
|
|
|
||
|
|
def output_json_for_agent():
|
||
|
|
"""Output alerts as JSON for agent processing."""
|
||
|
|
alerts = get_pending_alerts()
|
||
|
|
|
||
|
|
if not alerts:
|
||
|
|
print(json.dumps({"alerts": [], "count": 0}))
|
||
|
|
return
|
||
|
|
|
||
|
|
# Format alerts for agent
|
||
|
|
formatted = []
|
||
|
|
for alert in alerts:
|
||
|
|
formatted.append({
|
||
|
|
"id": alert.get("id"),
|
||
|
|
"priority": alert.get("priority", "medium"),
|
||
|
|
"channel": alert.get("channel", "telegram"),
|
||
|
|
"target": os.environ.get("TOPIC_MONITOR_TELEGRAM_ID", ""),
|
||
|
|
"topic_name": alert.get("topic_name"),
|
||
|
|
"title": alert.get("title"),
|
||
|
|
"url": alert.get("url"),
|
||
|
|
"score": alert.get("score", 0),
|
||
|
|
"message": format_alert_message(alert)
|
||
|
|
})
|
||
|
|
|
||
|
|
output = {
|
||
|
|
"alerts": formatted,
|
||
|
|
"count": len(formatted),
|
||
|
|
"timestamp": datetime.now().isoformat()
|
||
|
|
}
|
||
|
|
|
||
|
|
print(json.dumps(output, indent=2))
|
||
|
|
|
||
|
|
|
||
|
|
def mark_all_sent(alert_ids: list = None):
|
||
|
|
"""Mark alerts as sent."""
|
||
|
|
alerts = get_pending_alerts()
|
||
|
|
|
||
|
|
if alert_ids:
|
||
|
|
# Mark specific alerts
|
||
|
|
for aid in alert_ids:
|
||
|
|
mark_alert_sent(aid)
|
||
|
|
print(f"✅ Marked as sent: {aid}")
|
||
|
|
else:
|
||
|
|
# Mark all pending
|
||
|
|
for alert in alerts:
|
||
|
|
mark_alert_sent(alert.get("id"))
|
||
|
|
print(f"✅ Marked as sent: {alert.get('id')}")
|
||
|
|
|
||
|
|
print(f"\n✅ Marked {len(alert_ids) if alert_ids else len(alerts)} alert(s) as sent")
|
||
|
|
|
||
|
|
|
||
|
|
def main():
|
||
|
|
parser = argparse.ArgumentParser(description="Process topic monitor alerts")
|
||
|
|
parser.add_argument("--json", action="store_true", help="Output as JSON for agent")
|
||
|
|
parser.add_argument("--mark-sent", nargs="*", metavar="ID",
|
||
|
|
help="Mark alert(s) as sent (all if no IDs given)")
|
||
|
|
parser.add_argument("--clear-old", type=int, metavar="HOURS",
|
||
|
|
help="Clear alerts older than HOURS (default: 168 = 7 days)")
|
||
|
|
|
||
|
|
args = parser.parse_args()
|
||
|
|
|
||
|
|
if args.clear_old:
|
||
|
|
clear_old_alerts(args.clear_old)
|
||
|
|
print(f"✅ Cleared alerts older than {args.clear_old} hours")
|
||
|
|
return
|
||
|
|
|
||
|
|
if args.mark_sent is not None:
|
||
|
|
# --mark-sent with or without IDs
|
||
|
|
if args.mark_sent:
|
||
|
|
mark_all_sent(args.mark_sent)
|
||
|
|
else:
|
||
|
|
mark_all_sent()
|
||
|
|
return
|
||
|
|
|
||
|
|
if args.json:
|
||
|
|
output_json_for_agent()
|
||
|
|
else:
|
||
|
|
show_pending_alerts()
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|