#!/usr/bin/python3 """Watch https://osmcal.org/ for another Paris OSM hack weekend.""" import email.message import email.utils from urllib.parse import urlparse import smtplib import sys import typing import requests import simplejson.errors MAIL_FROM = "edward@4angle.com" MAIL_TO = "edward@4angle.com" SMTP_HOST = "4angle.com" URL = "https://osmcal.org/api/v2/events/?in=France" SEEN_EVENT_IDS = {4064, 4606, 4641} def get_event_id(event: dict[str, typing.Any]) -> int | None: """Extract the numeric event ID from API data.""" if "id" in event: return typing.cast(int, event["id"]) path_parts = [part for part in urlparse(event["url"]).path.split("/") if part] if len(path_parts) >= 2 and path_parts[-2] == "event": try: return int(path_parts[-1]) except ValueError: return None return None def get_detailed_location(event: dict[str, typing.Any]) -> str: """Return an event's detailed location, or an empty string if absent.""" location = event.get("location") if not isinstance(location, dict): return "" detailed = location.get("detailed") return detailed if isinstance(detailed, str) else "" def send_mail(subject: str, body: str) -> None: """Send an e-mail.""" msg = email.message.EmailMessage() msg["Subject"] = subject msg["To"] = f"Edward Betts <{MAIL_TO}>" msg["From"] = f"OSM Calendar alert <{MAIL_FROM}>" msg["Date"] = email.utils.formatdate() msg["Message-ID"] = email.utils.make_msgid() msg.set_content(body) s = smtplib.SMTP(SMTP_HOST) s.sendmail(MAIL_FROM, [MAIL_TO], msg.as_string()) s.quit() def check_for_paris_events() -> None: """Check for upcoming OSM events in Paris and send an email if found.""" r = requests.get(URL) try: events = r.json() except simplejson.errors.JSONDecodeError: print(r.text) raise all_paris_events = [ event for event in events if "paris" in get_detailed_location(event).lower() ] seen_paris_events = [ event for event in all_paris_events if get_event_id(event) in SEEN_EVENT_IDS ] paris_events = [ event for event in all_paris_events if get_event_id(event) not in SEEN_EVENT_IDS ] if not paris_events: if sys.stdin.isatty(): total_events = len(events) seen_count = len(seen_paris_events) paris_label = "Paris event" if seen_count == 1 else "Paris events" print( f"{total_events} events found in France; " + f"{seen_count} {paris_label} already seen; " + "no new Paris events." ) return subject = "Upcoming Paris OSM Hack Weekend Found!" count = len(paris_events) body = f"Found {count} {'event' if count == 1 else 'events'} in Paris:\n\n" for event in paris_events: body += ( f"{event['name']} - {event['url']}\n" + f"Date: {event['date']['human']}\n" + f"Location: {get_detailed_location(event)}\n\n" ) send_mail(subject, body) if __name__ == "__main__": check_for_paris_events()