transitous rewrite: some minor info embed improvements

This commit is contained in:
Kaaninchen
2026-08-15 22:13:58 +02:00
parent cf696df5f5
commit 697a0f6218
7 changed files with 152 additions and 25 deletions
+15 -1
View File
@@ -2,8 +2,10 @@ import discord
from src.utils import config, logger from src.utils import config, logger
from src.dc.handlers import rename_vc from src.dc.handlers import rename_vc
from src.dc.helpers import validate_channel from src.dc.helpers import validate_channel
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)
@bot.event @bot.event
async def on_ready(): async def on_ready():
@@ -17,4 +19,16 @@ async def on_ready():
try: try:
bot.run(config["token"]) bot.run(config["token"])
except: except:
logger("Feher peim parsen des tokens", "fatal") logger("Feher peim parsen des tokens", "fatal")
'''
TODO
- Only choose connections in the future
- discord reconnection handling
- Automatic transfer
- discord status
- text announcements
- voice announcements
- improved error handling (retry connection)
- Footer Notice Slogangs
'''
+30 -17
View File
@@ -1,7 +1,8 @@
import requests import requests
import random import random
from datetime import datetime
from src.utils import logger, config, get_train_name from src.utils import logger, config, get_train_name, convert_iso_string
stations = config["stations"] stations = config["stations"]
blacklist = config["blacklist"] blacklist = config["blacklist"]
@@ -14,8 +15,8 @@ headers = {
endpoint = "https://api.transitous.org" endpoint = "https://api.transitous.org"
def get_random_stop_id() -> str: def get_random_stop_id() -> str:
stop = random.choice(stations) assigned_station = random.choice(stations)
req = f"{endpoint}/api/v1/geocode?text={stop}" req = f"{endpoint}/api/v1/geocode?text={assigned_station}"
try: try:
response = requests.get(req, headers=headers) response = requests.get(req, headers=headers)
@@ -26,7 +27,7 @@ def get_random_stop_id() -> str:
return None return None
if response.status_code == 404: if response.status_code == 404:
logger(f"Error finding station '{stop}'") logger(f"Error finding station '{assigned_station}'")
for entry in data: for entry in data:
if entry.get("type") != "STOP": if entry.get("type") != "STOP":
@@ -54,7 +55,8 @@ def get_random_connection(stop_id: str) -> str:
return None return None
trip_ids = [] trip_ids = []
for entry in data.get("stopTimes", []): stop_times = data.get("stopTimes", [])
for entry in stop_times:
trip_id = entry["tripId"] trip_id = entry["tripId"]
if entry["mode"] in blacklist: if entry["mode"] in blacklist:
continue continue
@@ -71,9 +73,14 @@ def get_random_connection(stop_id: str) -> str:
logger("Couldn't find any connection", "fatal") logger("Couldn't find any connection", "fatal")
return None return None
return random.choice(trip_ids) trip_id = random.choice(trip_id)
from_station = stop_times[0]["place"]["name"]
return {
"trip_id": random.choice(trip_ids),
"from_station": from_station
}
def get_trip_details(trip_id: 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}"
try: try:
@@ -88,31 +95,37 @@ def get_trip_details(trip_id: str) -> dict:
display_name = legs["displayName"] display_name = legs["displayName"]
trip_from = legs["tripFrom"]["name"] trip_from = legs["tripFrom"]["name"]
trip_to = legs["tripTo"]["name"] goes_to = legs["tripTo"]["name"]
start_time = legs["startTime"] start_time = legs["startTime"]
end_time = legs["endTime"] end_time = legs["endTime"]
mode = legs["mode"] mode = legs["mode"]
departure = convert_iso_string(start_time)
arrival = convert_iso_string(end_time)
train_name = get_train_name(display_name, mode) train_name = get_train_name(display_name, mode)
trip_details = { trip_details = {
"long_name": f"{train_name} nach {trip_to} von {trip_from}", "long_name": f"{train_name} nach {goes_to} von {from_station}",
"short_name": display_name, "short_name": display_name,
"from": trip_from, "from": from_station,
"to": trip_to, "to": goes_to,
"agency": legs["agencyName"], "agency": legs["agencyName"],
"route_color": legs.get("routeColor"), "route_color": legs.get("routeColor"),
"duration": legs["duration"], "duration": legs["duration"],
"start_time": start_time, "departure": departure,
"end_time": end_time, "arrival": arrival,
"mode": mode, "mode": mode,
"stops": {} "stops": {}
} }
trip_details["stops"][trip_from] = start_time trip_details["stops"][trip_from] = departure
for stop in legs["intermediateStops"]: for stop in legs["intermediateStops"]:
trip_details["stops"][stop["name"]] = stop["arrival"] arrival = convert_iso_string(stop["arrival"])
trip_details["stops"][trip_to] = end_time trip_details["stops"][stop["name"]] = arrival
if stop.get("name") == from_station:
departure_time = stop["departure"]
trip_details["departure"] = convert_iso_string(departure_time)
trip_details["stops"][goes_to] = arrival
return trip_details return trip_details
+7
View File
@@ -0,0 +1,7 @@
import discord
from src.dc.embeds import build_info_embed
def setup_commands(bot: discord.Bot):
@bot.slash_command(description="Informationen über die aktuelle Fahrt")
async def info(ctx):
await ctx.respond(embed=build_info_embed())
+48
View File
@@ -0,0 +1,48 @@
import discord
import random
from src.utils import convert_iso_string, get_operator_metadata
from src.dc.helpers import format_timestamp_to_dc
def build_embed_footer(mode: str, slogans):
footer_notice = f"Data provided by https://transitous.org • Typ: {mode}"
icon = "https://avatars.githubusercontent.com/u/24960008?s=60&v=4"
if slogans is not None:
footer_text = f"{random.choice(slogans)}{footer_notice}"
else:
footer_text = footer_notice
return {
"text": footer_text,
"icon": icon
}
def build_info_embed() -> discord.Embed:
from src.dc.handlers import trip
agency = trip["agency"]
metadata = get_operator_metadata(agency, trip["route_color"])
departure = format_timestamp_to_dc(trip["departure"])
arrival = format_timestamp_to_dc(trip["arrival"])
embed = discord.Embed(
title = trip["long_name"],
description=f"Abfahrt von {trip["from"]} um {departure}. Ankunft um {arrival}",
color = metadata["color"]
)
route_lines = []
for stop_name, stop_arrival in trip["stops"].items():
if stop_name == trip["from"]:
route_lines.append(f"**• {stop_name} ({stop_arrival} Uhr)**")
else:
route_lines.append(f"{stop_name} ({stop_arrival} Uhr)")
embed.add_field(name="Route", value="\n".join(route_lines))
footer = build_embed_footer(trip["mode"], metadata["slogans"])
embed.set_footer(text=footer["text"], icon_url=footer["icon"])
embed.set_author(name=agency)
embed.set_thumbnail(url=metadata["logo"])
return embed
+5 -4
View File
@@ -8,14 +8,15 @@ 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
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() station_id = transitous.get_random_stop_id()
trip_id = transitous.get_random_connection(station_id) connection = transitous.get_random_connection(station_id)
trip = transitous.get_trip_details(trip_id) trip = transitous.get_trip_details(connection["trip_id"], connection["from_station"])
arrival = datetime.fromisoformat(trip["end_time"]) arrival = trip["arrival"]
long_name = trip["long_name"] long_name = trip["long_name"]
print("-----------------") print("-----------------")
@@ -25,7 +26,7 @@ async def rename_vc(bot: discord.Bot, voice_channel, from_scheduler: bool = Fals
formatting = channel_formatting(trip["mode"]) formatting = channel_formatting(trip["mode"])
await voice_channel.edit(name=f"{formatting}{long_name}") await voice_channel.edit(name=f"{formatting}{long_name}")
await voice_channel.set_status(f"Ankunft um {arrival.strftime('%H:%M')}") await voice_channel.set_status(f"Ankunft um {arrival}")
logger(f"Name geändert!") logger(f"Name geändert!")
+18 -2
View File
@@ -1,5 +1,6 @@
import discord import discord
from src.utils import logger from datetime import datetime, timedelta
from src.utils import logger, convert_iso_string
def validate_channel(bot: discord.bot, server_id: int, channel_id: int): def validate_channel(bot: discord.bot, server_id: int, channel_id: int):
guild = bot.get_guild(server_id) guild = bot.get_guild(server_id)
@@ -12,4 +13,19 @@ def validate_channel(bot: discord.bot, server_id: int, channel_id: int):
logger(f"Es konnte kein VC mit der id {channel_id} gefunden werden", "fatal") logger(f"Es konnte kein VC mit der id {channel_id} gefunden werden", "fatal")
return False return False
return channel return channel
def format_timestamp_to_dc(timestr):
parsed_time = datetime.strptime(timestr, "%H:%M")
now = datetime.now()
final_datetime = datetime.now().replace(
hour=parsed_time.hour,
minute=parsed_time.minute,
second=0,
microsecond=0
)
if final_datetime <= now:
final_datetime += timedelta(days=1)
return discord.utils.format_dt(final_datetime, style="t")
+29 -1
View File
@@ -2,8 +2,10 @@ import json
import os import os
from datetime import datetime from datetime import datetime
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)
@@ -14,6 +16,10 @@ def logger(msg, log_type="info") -> str:
if status == "FATAL": if status == "FATAL":
os._exit(1) os._exit(1)
def convert_iso_string(isostring) -> str:
datetime_isostring = datetime.fromisoformat(isostring)
return datetime_isostring.strftime('%H:%M')
def channel_formatting(mode: str) -> str: def channel_formatting(mode: str) -> str:
formatting = config.get("formatting", "") formatting = config.get("formatting", "")
@@ -32,4 +38,26 @@ def get_train_name(train_name: str, mode: str) -> str:
else: else:
train = train_name train = train_name
return train return train
def get_operator_metadata(agency: str, route_color: str) -> dict:
op_data = operators.OPERATOR_ALIASES.get(agency) or operators.OPERATORS.get(agency) or operators.OPERATORS["fallback"]
logo = op_data.get("logo", operators.OPERATORS["fallback"]["logo"])
slogans = op_data.get("slogans")
color = op_data.get("color")
if color is None:
if route_color is not None:
try:
color = int(f"0x{route_color.upper()}")
except ValueError:
color = operators.OPERATORS["fallback"]["color"]
else:
color = operators.OPERATORS["fallback"]["colors"]
return {
"logo": logo,
"color": color,
"slogans": slogans
}