transitous rewrite: make sure to only use connections in the future

This commit is contained in:
Kaaninchen
2026-08-16 10:43:38 +02:00
parent 53358c08b1
commit f3d9a25f95
5 changed files with 93 additions and 48 deletions
+13 -6
View File
@@ -6,15 +6,22 @@ from src.dc.commands import setup_commands
bot = discord.Bot(intents=discord.Intents.all()) bot = discord.Bot(intents=discord.Intents.all())
setup_commands(bot=bot) setup_commands(bot=bot)
_bot_initialized = False
@bot.event @bot.event
async def on_ready(): async def on_ready():
server_id = config["server"] global _bot_initialized
server_vc_id = config["vc"]
channel = validate_channel(bot=bot, server_id=server_id, channel_id=server_vc_id)
logger(f"{bot.user} ist online") 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: try:
bot.run(config["token"]) bot.run(config["token"])
@@ -23,12 +30,12 @@ except:
''' '''
TODO TODO
- Only choose connections in the future - 1024 embed limit
- discord reconnection handling
- Automatic transfer - Automatic transfer
- discord status - discord status
- text announcements - text announcements
- voice announcements - voice announcements
- improved error handling (retry connection) - improved error handling (retry connection)
- multi language support - multi language support
- random = False
''' '''
+37 -26
View File
@@ -1,6 +1,7 @@
import requests import requests
import random import random
import json import json
from datetime import datetime, timezone
from src.utils import logger, config, get_train_name, convert_iso_string 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"] return entry["id"]
def get_random_connection(stop_id: str) -> str: 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 cursor = None
count = 20 max_pages = 5
min_results = 5
trip_ids = []
all_stop_times = []
for _ in range(max_pages): for _ in range(max_pages):
params = f"stopId={stop_id}&n={count}" params = {
"stopId": stop_id,
"n": 20,
"time": now,
}
if cursor: if cursor:
params += f"&pageCursor={cursor}" params["pageCursor"] = cursor
req = f"{endpoint}/api/v1/stoptimes?{params}" 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
try: stop_times = data.get("stopTimes", [])
response = requests.get(req, headers=headers) all_stop_times.extend(stop_times)
response.raise_for_status()
data = response.json()
except requests.RequestException as e:
print(e)
return None
trip_ids = [] for entry in stop_times:
stop_times = data.get("stopTimes", []) dep = entry.get("place", {}).get("departure") or entry.get("place", {}).get("arrival")
for entry in stop_times: print(f"{entry.get('mode')}: {convert_iso_string(dep)}")
trip_id = entry["tripId"] trip_id = entry["tripId"]
if entry["mode"] in blacklist: if entry["mode"] in blacklist:
continue continue
trip_ids.append(trip_id) trip_ids.append(entry["tripId"])
if len(trip_ids) >= 5: if len(trip_ids) >= min_results:
break break
cursor = data.get("nextPageCursor") cursor = data.get("nextPageCursor")
if not cursor: if not cursor:
break break
if not trip_ids: if not trip_ids:
logger("Couldn't find any connection", "fatal") logger("Couldn't find any connection", "fatal")
return None return None
trip_id = random.choice(trip_ids) trip_id = random.choice(trip_ids)
for trip in stop_times:
if trip.get("tripId") == trip_id: from_station = None
from_station = trip.get("place").get("name") for entry in all_stop_times:
if entry.get("tripId") == trip_id:
from_station = entry.get("place", {}).get("name")
break break
print(from_station)
return { return {
"trip_id": trip_id, "trip_id": trip_id,
"from_station": from_station "from_station": from_station
} }
def get_trip_details(trip_id: str, from_station: str) -> dict: def get_trip_details(trip_id: str, from_station: str) -> dict:
req = f"{endpoint}/api/v2/trip?tripId={trip_id}" req = f"{endpoint}/api/v2/trip?tripId={trip_id}"
+1 -1
View File
@@ -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", "logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f3/ODEG-Logo_Neu.svg/960px-ODEG-Logo_Neu.svg.png",
"color": 0x00745C "color": 0x00745C
}, },
"Nederlandse Spoorwegen": { "NS": {
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Logo_NS.svg/960px-Logo_NS.svg.png", "logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Logo_NS.svg/960px-Logo_NS.svg.png",
"color": 0X00337F, "color": 0X00337F,
"slogan": ["Goed op weg", "Welkom in de trein van morgen", "Veilig, Vlug, Voordelig", "we haben een serious probleem", "Neuken in de keuken"] "slogan": ["Goed op weg", "Welkom in de trein van morgen", "Veilig, Vlug, Voordelig", "we haben een serious probleem", "Neuken in de keuken"]
+25 -9
View File
@@ -1,26 +1,22 @@
import discord import discord
import asyncio import asyncio
from datetime import datetime from datetime import datetime, timedelta, date
from src.api import transitous from src.utils import logger, channel_formatting, choose_connection
from src.utils import logger, channel_formatting
_scheduled_task: asyncio.Task | None = None _scheduled_task: asyncio.Task | None = None
async def rename_vc(bot: discord.Bot, voice_channel, from_scheduler: bool = False): 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(): if not from_scheduler and _scheduled_task and not _scheduled_task.done():
_scheduled_task.cancel() _scheduled_task.cancel()
station_id = transitous.get_random_stop_id() trip = choose_connection()
connection = transitous.get_random_connection(station_id)
trip = transitous.get_trip_details(connection["trip_id"], connection["from_station"])
arrival = trip["arrival"] arrival = trip["arrival"]
long_name = trip["long_name"] long_name = trip["long_name"]
print("-----------------") 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"Betreiber: {trip["agency"]}, Typ: {trip["mode"]}")
logger(f"Versuche Namen zu ändern, wenn nichts passiert bin ich im cooldown... (warte bis zu 10min!)") 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!") 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)
+12 -1
View File
@@ -1,11 +1,11 @@
import json import json
import os import os
from datetime import datetime, timedelta from datetime import datetime, timedelta
from zoneinfo import ZoneInfo
import src.data.operators as operators import src.data.operators as operators
from src.data.emojis import emoji_list from src.data.emojis import emoji_list
with open("config.json", "r") as file: with open("config.json", "r") as file:
config = json.load(file) config = json.load(file)
@@ -16,8 +16,19 @@ def logger(msg, log_type="info") -> str:
if status == "FATAL": if status == "FATAL":
os._exit(1) 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: def convert_iso_string(isostring) -> str:
timezone = config.get("timezone", "Europe/Berlin")
dt = datetime.fromisoformat(isostring.replace('Z', '+00:00')) dt = datetime.fromisoformat(isostring.replace('Z', '+00:00'))
dt = dt.astimezone(ZoneInfo(timezone))
if dt.second >= 30: if dt.second >= 30:
dt += timedelta(minutes=1) dt += timedelta(minutes=1)