File size: 4,752 Bytes
afd56bc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#!/usr/bin/env python3
import sys
import os
import json
import logging
import asyncio
import hashlib
import requests
from datetime import datetime

# Dodanie 艣cie偶ki projektu do PYTHONPATH
sys.path.append(os.path.join(os.path.dirname(__file__), "../.."))

from backend.core.search.grant_search_service import grant_search_service

logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
logger = logging.getLogger(__name__)

CACHE_FILE = os.path.join(os.path.dirname(__file__), ".monitoring_cache.json")

def load_cache():
    if os.path.exists(CACHE_FILE):
        try:
            with open(CACHE_FILE, "r", encoding="utf-8") as f:
                return json.load(f)
        except Exception as e:
            logger.error(f"B艂膮d odczytu cache: {e}")
    return {}

def save_cache(cache_data):
    try:
        with open(CACHE_FILE, "w", encoding="utf-8") as f:
            json.dump(cache_data, f, ensure_ascii=False, indent=4)
    except Exception as e:
        logger.error(f"B艂膮d zapisu cache: {e}")

async def check_content_hash(url: str) -> str:
    """Pobiera zawarto艣膰 strony i zwraca hash SHA-256."""
    try:
        response = await asyncio.to_thread(requests.get, url, timeout=10, allow_redirects=True)
        if response.status_code == 200:
            return hashlib.sha256(response.text.encode('utf-8')).hexdigest()
    except Exception as e:
        logger.warning(f"B艂膮d podczas pobierania tre艣ci {url}: {e}")
    return None

async def monitor_grants():
    logger.info("Rozpoczynam sprawdzanie zmian w regulaminach i terminach...")
    cache = load_cache()
    alerts = []

    for source in grant_search_service.sources:
        if hasattr(source, "_get_verified_fallback"):
            fallback_list = source._get_verified_fallback()
            for grant in fallback_list:
                program_id = grant.get("id") or grant.get("name")
                url = grant.get("url", "")
                name = grant.get("name", "Brak nazwy")
                current_deadline = grant.get("deadline", "")
                
                if not program_id or not url.startswith("http"):
                    continue

                logger.info(f"Monitorowanie: {name}")
                current_hash = await check_content_hash(url)
                
                if program_id in cache:
                    prev_data = cache[program_id]
                    prev_deadline = prev_data.get("deadline", "")
                    prev_hash = prev_data.get("content_hash", "")
                    
                    changes = []
                    if current_deadline and current_deadline != prev_deadline:
                        changes.append(f"Zmieniono termin z {prev_deadline} na {current_deadline}")
                        
                    if current_hash and prev_hash and current_hash != prev_hash:
                        changes.append("Wykryto zmian臋 w tre艣ci strony (regulamin/og艂oszenie)")
                        
                    if changes:
                        alerts.append(f"鈿狅笍 {name}:\n- " + "\n- ".join(changes) + f"\nLink: {url}")
                
                # Aktualizacja cache
                cache[program_id] = {
                    "deadline": current_deadline,
                    "content_hash": current_hash or cache.get(program_id, {}).get("content_hash", ""),
                    "last_checked": datetime.now().isoformat()
                }

    save_cache(cache)

    # Wysy艂anie powiadomie艅
    if alerts:
        logger.info(f"Wykryto {len(alerts)} zmian. Przygotowuj臋 powiadomienia administracyjne.")
        alert_body = "\n\n".join(alerts)
        
        try:
            from backend.gsd.email_notifier import send_hitl_email
            # U偶ywamy istniej膮cego powiadomiacza dla administrator贸w (DEFAULT_TARGET)
            # Wys艂anie jednej wiadomo艣ci ze wszystkimi alertami
            admin_email = os.environ.get("ADMIN_EMAIL", "bogmaz1@gmail.com")
            subject = "Dotacje AI: Zmiany w regulaminach lub terminach nabor贸w"
            
            # W send_hitl_email parametr to hitl_question, ale mo偶emy to lekko obej艣膰 buduj膮c tre艣膰
            # Dla Fazy 1 wy艣lemy po prostu log / mail.
            logger.warning(f"[EMAIL DO {admin_email}]\nTemat: {subject}\n{alert_body}")
            
            send_hitl_email(f"ALERTY MONITORINGU:\n\n{alert_body}", "SYSTEM_MONITOR")
            logger.info("Wys艂ano e-mail do administrator贸w.")
        except Exception as e:
            logger.error(f"Nie uda艂o si臋 wys艂a膰 powiadomienia email: {e}")
    else:
        logger.info("Brak zmian w regulaminach i terminach.")

if __name__ == "__main__":
    asyncio.run(monitor_grants())