diff --git a/main.py b/main.py index 2de3078..0121fc8 100644 --- a/main.py +++ b/main.py @@ -6,15 +6,22 @@ from src.dc.commands import setup_commands bot = discord.Bot(intents=discord.Intents.all()) setup_commands(bot=bot) +_bot_initialized = False @bot.event async def on_ready(): - server_id = config["server"] - server_vc_id = config["vc"] - channel = validate_channel(bot=bot, server_id=server_id, channel_id=server_vc_id) + global _bot_initialized logger(f"{bot.user} ist online") - await rename_vc(bot=bot, voice_channel=channel) + + if not _bot_initialized: + _bot_initialized = True + server_id = config["server"] + server_vc_id = config["vc"] + channel = validate_channel(bot=bot, server_id=server_id, channel_id=server_vc_id) + await rename_vc(bot, voice_channel=channel) + else: + logger("Reconnected to discord gateway, this wont disturb your current ride") try: bot.run(config["token"]) @@ -23,12 +30,12 @@ except: ''' TODO -- Only choose connections in the future -- discord reconnection handling +- 1024 embed limit - Automatic transfer - discord status - text announcements - voice announcements - improved error handling (retry connection) - multi language support +- random = False ''' \ No newline at end of file diff --git a/src/api/transitous.py b/src/api/transitous.py index 6abe4a0..b9f2567 100644 --- a/src/api/transitous.py +++ b/src/api/transitous.py @@ -1,6 +1,7 @@ import requests import random import json +from datetime import datetime, timezone from src.utils import logger, config, get_train_name, convert_iso_string @@ -35,55 +36,65 @@ def get_random_stop_id() -> str: return entry["id"] def get_random_connection(stop_id: str) -> str: - max_pages = 5 + now = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ") + print(f"Aktuelle Zeit für Query: {now}") cursor = None - count = 20 + max_pages = 5 + min_results = 5 + trip_ids = [] + all_stop_times = [] for _ in range(max_pages): - params = f"stopId={stop_id}&n={count}" + params = { + "stopId": stop_id, + "n": 20, + "time": now, + } if cursor: - params += f"&pageCursor={cursor}" + params["pageCursor"] = cursor - req = f"{endpoint}/api/v1/stoptimes?{params}" - - try: - response = requests.get(req, headers=headers) - response.raise_for_status() - data = response.json() - except requests.RequestException as e: - print(e) - return None - - trip_ids = [] - stop_times = data.get("stopTimes", []) - for entry in stop_times: - trip_id = entry["tripId"] - if entry["mode"] in blacklist: - continue - trip_ids.append(trip_id) - - if len(trip_ids) >= 5: + try: + response = requests.get(f"{endpoint}/api/v5/stoptimes", params=params, headers=headers) + response.raise_for_status() + data = response.json() + except requests.RequestException as e: + print(e) break + + stop_times = data.get("stopTimes", []) + all_stop_times.extend(stop_times) + for entry in stop_times: + dep = entry.get("place", {}).get("departure") or entry.get("place", {}).get("arrival") + print(f"{entry.get('mode')}: {convert_iso_string(dep)}") + trip_id = entry["tripId"] + if entry["mode"] in blacklist: + continue + trip_ids.append(entry["tripId"]) + + if len(trip_ids) >= min_results: + break + cursor = data.get("nextPageCursor") if not cursor: break - if not trip_ids: + if not trip_ids: logger("Couldn't find any connection", "fatal") return None - + trip_id = random.choice(trip_ids) - for trip in stop_times: - if trip.get("tripId") == trip_id: - from_station = trip.get("place").get("name") + + from_station = None + for entry in all_stop_times: + if entry.get("tripId") == trip_id: + from_station = entry.get("place", {}).get("name") break - print(from_station) return { "trip_id": trip_id, "from_station": from_station - } + } def get_trip_details(trip_id: str, from_station: str) -> dict: req = f"{endpoint}/api/v2/trip?tripId={trip_id}" @@ -108,7 +119,7 @@ def get_trip_details(trip_id: str, from_station: str) -> dict: departure = convert_iso_string(start_time) arrival = convert_iso_string(end_time) train_name = get_train_name(display_name, mode) - + trip_details = { "long_name": f"{train_name} nach {goes_to} von {from_station}", "short_name": display_name, diff --git a/src/data/operators.py b/src/data/operators.py index 2b9ccfa..e301893 100644 --- a/src/data/operators.py +++ b/src/data/operators.py @@ -38,7 +38,7 @@ OPERATORS = { "logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f3/ODEG-Logo_Neu.svg/960px-ODEG-Logo_Neu.svg.png", "color": 0x00745C }, - "Nederlandse Spoorwegen": { + "NS": { "logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Logo_NS.svg/960px-Logo_NS.svg.png", "color": 0X00337F, "slogan": ["Goed op weg", "Welkom in de trein van morgen", "Veilig, Vlug, Voordelig", "we haben een serious probleem", "Neuken in de keuken"] diff --git a/src/dc/handlers.py b/src/dc/handlers.py index 6599691..c7f0b53 100644 --- a/src/dc/handlers.py +++ b/src/dc/handlers.py @@ -1,26 +1,22 @@ import discord import asyncio -from datetime import datetime +from datetime import datetime, timedelta, date -from src.api import transitous -from src.utils import logger, channel_formatting +from src.utils import logger, channel_formatting, choose_connection _scheduled_task: asyncio.Task | None = None async def rename_vc(bot: discord.Bot, voice_channel, from_scheduler: bool = False): - global trip + global trip, _scheduled_task if not from_scheduler and _scheduled_task and not _scheduled_task.done(): _scheduled_task.cancel() - station_id = transitous.get_random_stop_id() - connection = transitous.get_random_connection(station_id) - trip = transitous.get_trip_details(connection["trip_id"], connection["from_station"]) - + trip = choose_connection() arrival = trip["arrival"] long_name = trip["long_name"] print("-----------------") - logger(f"Umstieg: {long_name}, Ankunft: {arrival}") + logger(f"Umstieg: {long_name}, Ankunft: {arrival} Uhr") logger(f"Betreiber: {trip["agency"]}, Typ: {trip["mode"]}") logger(f"Versuche Namen zu ändern, wenn nichts passiert bin ich im cooldown... (warte bis zu 10min!)") @@ -30,3 +26,23 @@ async def rename_vc(bot: discord.Bot, voice_channel, from_scheduler: bool = Fals logger(f"Name geändert!") + _scheduled_task = asyncio.create_task(_schedule_next_transfer(bot, arrival)) + +async def _schedule_next_transfer(bot, arrival): + now = datetime.now() + parsed_time = datetime.strptime(arrival, "%H:%M").time() + arrival_dt = datetime.combine(date.today(), parsed_time) + + if arrival_dt < now: + arrival_dt += timedelta(days=1) + + wait_seconds = (arrival_dt - now).total_seconds() + if wait_seconds > 0: + remaining = str(timedelta(seconds=wait_seconds)) + logger(f"Nächster Umstieg in {remaining.split('.')[0]} ({arrival} Uhr)") + + await asyncio.sleep(wait_seconds) + + logger("Zug angekommen, wähle neue Verbindung") + await rename_vc(bot, from_scheduler=True) + diff --git a/src/utils.py b/src/utils.py index 8c82ed4..3c00290 100644 --- a/src/utils.py +++ b/src/utils.py @@ -1,11 +1,11 @@ import json import os from datetime import datetime, timedelta +from zoneinfo import ZoneInfo import src.data.operators as operators from src.data.emojis import emoji_list - with open("config.json", "r") as file: config = json.load(file) @@ -16,8 +16,19 @@ def logger(msg, log_type="info") -> str: if status == "FATAL": os._exit(1) +def choose_connection() -> dict: + from src.api import transitous + station_id = transitous.get_random_stop_id() + connection = transitous.get_random_connection(station_id) + trip = transitous.get_trip_details(connection["trip_id"], connection["from_station"]) + + return trip + def convert_iso_string(isostring) -> str: + timezone = config.get("timezone", "Europe/Berlin") dt = datetime.fromisoformat(isostring.replace('Z', '+00:00')) + dt = dt.astimezone(ZoneInfo(timezone)) + if dt.second >= 30: dt += timedelta(minutes=1)