aboutsummaryrefslogtreecommitdiff
path: root/vitrasa_gtfs_ntfy/main.py
blob: cd2efdbbe31f032f2119516024873283793d7f9f (plain)
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
117
118
119
120
121
122
123
124
125
126
# /// script
# requires-python = ">=3.13"
# dependencies = [
#     "requests",
# ]
# ///


import json
import requests
import os
from datetime import datetime, timezone
from email.utils import parsedate_to_datetime


class Config:
    gtfs_url: str = ""
    ntfy_topic: str = ""
    lastfeed: str = ""
    file_to_monitor: str = ""

    def __init__(
        self, gtfs_url: str, ntfy_topic: str, lastfeed: str, file_to_monitor: str
    ) -> None:
        self.gtfs_url = gtfs_url
        self.ntfy_topic = ntfy_topic
        self.lastfeed = lastfeed
        self.file_to_monitor = file_to_monitor


def load_config(config_path: str) -> Config:
    """Load the lastfeed data from a local source."""
    with open(config_path, "r", encoding="utf-8") as f:
        data = json.load(f)

        return Config(
            data["gtfs_url"],
            data["ntfy_topic"],
            data["lastfeed"],
            data.get("file_to_monitor", ""),
        )


def save_config(config_path: str, config: Config) -> None:
    """Save the lastfeed data to a local source."""
    with open(config_path, "w", encoding="utf-8") as f:
        json.dump(
            {
                "gtfs_url": config.gtfs_url,
                "ntfy_topic": config.ntfy_topic,
                "lastfeed": config.lastfeed,
                "file_to_monitor": config.file_to_monitor,
            },
            f,
            ensure_ascii=False,
            indent=4,
        )


def load_gtfs_last_modified(url: str) -> datetime | None:
    """Perform a HEAD request to the GTFS service to get last modification date."""
    response = requests.head(url, timeout=10)
    response.raise_for_status()
    last_modified = response.headers.get("Last-Modified")
    if last_modified:
        return parsedate_to_datetime(last_modified)
    return None


def push_ntfy(topic: str, message: str, title: str, priority: str = "default", tags: str = "") -> None:
    """Push notification using ntfy service."""
    requests.post(
        f"https://ntfy.sh/{topic}",
        data=message.encode("utf-8"),
        headers={
            "Priority": priority,
            "Tags": tags,
            "Title": title
        },
        timeout=10
    )


if __name__ == "__main__":
    # Get the directory where the script is located
    script_dir = os.path.dirname(os.path.abspath(__file__))
    config_path = os.path.join(script_dir, "config.json")

    conf = load_config(config_path)

    current_last_modified = load_gtfs_last_modified(conf.gtfs_url)
    if not current_last_modified:
        print("Could not get last modification date.")
    else:
        # datetime.fromisoformat supports 'Z' in Python 3.11+
        stored_last_modified = datetime.fromisoformat(conf.lastfeed)

        # 1. Check if server has a NEWER feed than what we LAST NOTIFIED about
        if current_last_modified > stored_last_modified:
            print(f"New GTFS found: {current_last_modified.isoformat()}")
            msg = f"Nuevo GTFS de Vitrasa con fecha {current_last_modified.isoformat()}"
            push_ntfy(conf.ntfy_topic, msg, "Nuevo GTFS listo", priority="high", tags="rotating_light")

            # Update config with new date
            conf.lastfeed = current_last_modified.isoformat().replace("+00:00", "Z")
            save_config(config_path, conf)
        else:
            print("GTFS server date has not changed since last notification.")

        # 2. Check if the LOCAL file is currently outdated compared to the server
        if conf.file_to_monitor:
            # Resolve relative path from script location if necessary
            full_path = os.path.normpath(os.path.join(script_dir, conf.file_to_monitor))
            if os.path.exists(full_path):
                file_mtime = datetime.fromtimestamp(os.path.getmtime(full_path), tz=timezone.utc)
                if file_mtime < current_last_modified:
                    print(f"Warning: Local file {conf.file_to_monitor} is outdated.")
                    msg = (
                        f"El archivo local {os.path.basename(conf.file_to_monitor)} "
                        f"es anterior al feed disponible ({current_last_modified.isoformat()})"
                    )
                    push_ntfy(conf.ntfy_topic, msg, "Archivo GTFS desactualizado", priority="default", tags="warning")
                else:
                    print(f"Local file {conf.file_to_monitor} is up to date.")
            else:
                print(f"File to monitor not found: {full_path}")