mirror of
https://github.com/kaaninchen/Gleiswechsel.git
synced 2026-09-17 16:52:47 +00:00
feat: map of route in /info embed
This commit is contained in:
+15
-7
@@ -249,7 +249,7 @@ def get_trip_details(random_connection: dict | None) -> dict | None:
|
||||
|
||||
trip_details = {
|
||||
"long_name": long_name,
|
||||
"short_name": display_name,
|
||||
"short_name": train_name,
|
||||
"station": from_station,
|
||||
"from": train_from,
|
||||
"to": goes_to,
|
||||
@@ -266,7 +266,9 @@ def get_trip_details(random_connection: dict | None) -> dict | None:
|
||||
train_from_importance = legs["from"]["importance"]
|
||||
trip_details["stops"][train_from] = {
|
||||
"arrival": departure_dt,
|
||||
"importance": train_from_importance
|
||||
"importance": train_from_importance,
|
||||
"lat": legs["from"]["lat"],
|
||||
"lon": legs["from"]["lon"]
|
||||
}
|
||||
|
||||
departure_time_iso = start_time
|
||||
@@ -276,22 +278,28 @@ def get_trip_details(random_connection: dict | None) -> dict | None:
|
||||
stop_importance = stop["importance"]
|
||||
stop_details = {
|
||||
"arrival": stop_arrival_dt,
|
||||
"importance": stop_importance
|
||||
"importance": stop_importance,
|
||||
"lat": stop["lat"],
|
||||
"lon": stop["lon"]
|
||||
}
|
||||
trip_details["stops"][stop["name"]] = stop_details
|
||||
|
||||
if stop.get("name") == from_station:
|
||||
departure_time_iso = stop["departure"]
|
||||
trip_details["departure"] = parse_iso(departure_time_iso).strftime("%H:%M")
|
||||
departure_time = parse_iso(stop["departure"])
|
||||
trip_details["departure_dt"] = departure_time
|
||||
trip_details["departure"] = departure_time.strftime("%H:%M")
|
||||
|
||||
train_to_importance = legs["to"]["importance"]
|
||||
trip_details["stops"][goes_to] = {
|
||||
"arrival": arrival_dt,
|
||||
"importance": train_to_importance
|
||||
"importance": train_to_importance,
|
||||
"lat": legs["to"]["lat"],
|
||||
"lon": legs["to"]["lon"]
|
||||
}
|
||||
|
||||
valid = validate_connection(start_time, end_time, departure_time_iso)
|
||||
if not valid:
|
||||
return None
|
||||
|
||||
|
||||
return trip_details
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ channel:
|
||||
train_via: "{train_name} nach {goes_to} über {from_station}"
|
||||
embeds:
|
||||
footer:
|
||||
notice: "Daten großzügig bereitgestellt von https://transitous.org"
|
||||
notice: "Daten bereitgestellt von https://transitous.org • Maps (C) CARTO (C) OpenStreetMap.org contributors"
|
||||
info:
|
||||
description: "Abfahrt von {station} um {departure}. Ankunft um {arrival}"
|
||||
via: "Über"
|
||||
|
||||
@@ -4,7 +4,7 @@ channel:
|
||||
train_via: "{train_name} to {goes_to} via {from_station}"
|
||||
embeds:
|
||||
footer:
|
||||
notice: "Data provided by https://transitous.org"
|
||||
notice: "Data provided by https://transitous.org • Maps (C) CARTO (C) OpenStreetMap.org contributors"
|
||||
info:
|
||||
description: "Departure from {station} at {departure}. Arrival by {arrival}"
|
||||
via: "via"
|
||||
|
||||
@@ -92,6 +92,10 @@ OPERATORS = {
|
||||
"color": 0x812B6D,
|
||||
"slogan": ["Pour nous tous", "Donner au train des idées d'avance"]
|
||||
},
|
||||
"Stadtverkehr Tübingen GmbH": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Stadtwerke_T%C3%BCbingen_logo.svg/330px-Stadtwerke_T%C3%BCbingen_logo.svg.png",
|
||||
"color": 0x439669
|
||||
},
|
||||
"Tallink Grupp AS": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9e/Tallink_logo.svg/330px-Tallink_logo.svg.png",
|
||||
"color": 0x225197
|
||||
|
||||
+3
-1
@@ -4,4 +4,6 @@ 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())
|
||||
await ctx.defer()
|
||||
image = discord.File("src/data/assets/current_map.png", filename="ride.png")
|
||||
await ctx.respond(file=image, embed=build_info_embed())
|
||||
@@ -61,6 +61,7 @@ def build_info_embed() -> discord.Embed:
|
||||
embed.set_author(name=agency)
|
||||
embed.set_thumbnail(url=metadata["logo"])
|
||||
|
||||
embed.set_image(url="attachment://ride.png")
|
||||
return embed
|
||||
|
||||
def build_announcement_embed(msg):
|
||||
|
||||
+17
-6
@@ -3,7 +3,7 @@ import asyncio
|
||||
import random
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from src.utils import logger, channel_formatting, choose_connection, get_sound_path, LOCAL_TZ, get_next_station
|
||||
from src.utils import logger, channel_formatting, choose_connection, get_sound_path, LOCAL_TZ, get_next_station, generate_static_map
|
||||
from src.config import config
|
||||
from src.lang.locales import lang
|
||||
|
||||
@@ -26,18 +26,25 @@ async def rename_vc(bot: discord.Bot, voice_channel, from_scheduler: bool = Fals
|
||||
if trip is None:
|
||||
logger(f"Failed to select route after {max_attempt} attempts", "fatal")
|
||||
return False
|
||||
|
||||
generate_static_map(trip["stops"], trip["mode"], trip["agency"], trip["route_color"])
|
||||
|
||||
arrival = trip["arrival"]
|
||||
long_name = trip["long_name"]
|
||||
|
||||
if len(trip["long_name"]) >= 100:
|
||||
channel_name = trip["short_name"]
|
||||
else:
|
||||
channel_name = trip["long_name"]
|
||||
|
||||
mode = trip["mode"]
|
||||
|
||||
print("-----------------")
|
||||
logger(f"Transfer: {long_name}; Arrival: {arrival}")
|
||||
logger(f"Transfer: {channel_name}; Arrival: {arrival}")
|
||||
logger(f"Agency: {trip["agency"]}, mode: {mode}")
|
||||
logger(f"Trying to change the channels name. If nothing happens, then the bot is in cooldown... (automatically resolves after up to 10min)")
|
||||
|
||||
formatting = channel_formatting(mode)
|
||||
await voice_channel.edit(name=f"{formatting}{long_name}")
|
||||
await voice_channel.edit(name=f"{formatting}{channel_name}")
|
||||
await voice_channel.set_status(None)
|
||||
start_next_stop_updates(voice_channel)
|
||||
|
||||
@@ -59,12 +66,16 @@ async def announcer(announcement: str, voice_channel: discord.VoiceChannel, dest
|
||||
embed = build_announcement_embed(lang.embeds.announcement.end_of_connection.message())
|
||||
case "transfer":
|
||||
embed = build_info_embed()
|
||||
image = discord.File("src/data/assets/current_map.png", filename="ride.png")
|
||||
case _:
|
||||
logger(f"Unknown announcement: {announcement}")
|
||||
embed = None
|
||||
|
||||
if embed:
|
||||
await voice_channel.send(embed=embed)
|
||||
if image:
|
||||
await voice_channel.send(file=image, embed=embed)
|
||||
else:
|
||||
await voice_channel.send(embed=embed)
|
||||
|
||||
async def voice_announcer(station: str, voice_channel: discord.VoiceChannel) -> bool:
|
||||
sound_path = get_sound_path(station=station)
|
||||
@@ -145,7 +156,7 @@ async def _update_next_loop(voice_channel: discord.VoiceChannel):
|
||||
|
||||
wait_seconds = (next_stop["arrival"] - datetime.now(LOCAL_TZ)).total_seconds()
|
||||
if wait_seconds > 0:
|
||||
await asyncio.sleep(wait_seconds)
|
||||
await (wait_seconds)
|
||||
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
|
||||
+127
-1
@@ -1,8 +1,8 @@
|
||||
import os
|
||||
import importlib
|
||||
import random
|
||||
from pathlib import Path
|
||||
from datetime import datetime, timedelta, timezone
|
||||
import staticmaps
|
||||
from src.config import config
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
@@ -202,3 +202,129 @@ def format_stop_list(stops: dict, next_stop: str | None) -> list[tuple[str, str]
|
||||
fields.append((route_page_name, "\n".join(field_lines)))
|
||||
|
||||
return fields
|
||||
|
||||
def generate_static_map(stops: dict, mode: str, operator: str, route_color: str):
|
||||
logger("Generating a new route map..")
|
||||
operator_metadata = get_operator_metadata(operator, route_color, mode)
|
||||
operator_color = operator_metadata["color"]
|
||||
if operator_color == 0xFFFFFF:
|
||||
stop_color = staticmaps.Color(105, 105, 105, 255)
|
||||
else:
|
||||
stop_color = staticmaps.parse_color(f"#{operator_color:06x}")
|
||||
|
||||
white = staticmaps.Color(255, 255, 255, 255)
|
||||
|
||||
context = staticmaps.Context()
|
||||
|
||||
context.set_tile_provider(
|
||||
staticmaps.TileProvider(
|
||||
"carto-voyager",
|
||||
url_pattern=(
|
||||
"https://$s.basemaps.cartocdn.com/"
|
||||
"rastertiles/voyager/$z/$x/$y.png"
|
||||
),
|
||||
shards=["a", "b", "c", "d"],
|
||||
attribution="",
|
||||
)
|
||||
# It is against the law (and against your morals...) to not give
|
||||
# OSM and carto credits for their great work
|
||||
# I only removed the attribution text because I really dislike the
|
||||
# ugly white box that py-staticmaps adds. Credits are still visible in the
|
||||
# embeds footer
|
||||
)
|
||||
|
||||
stop_coords = [
|
||||
staticmaps.create_latlng(
|
||||
stop["lat"],
|
||||
stop["lon"],
|
||||
)
|
||||
for stop in stops.values()
|
||||
]
|
||||
|
||||
context.add_object(
|
||||
staticmaps.Line(
|
||||
stop_coords,
|
||||
color=white,
|
||||
width=20,
|
||||
)
|
||||
)
|
||||
|
||||
context.add_object(
|
||||
staticmaps.Line(
|
||||
stop_coords,
|
||||
color=stop_color,
|
||||
width=8,
|
||||
)
|
||||
)
|
||||
|
||||
last_index = len(stop_coords) - 1
|
||||
for i, point in enumerate(stop_coords):
|
||||
|
||||
if i == 0 or i == last_index:
|
||||
context.add_object(
|
||||
staticmaps.Circle(
|
||||
point,
|
||||
radius_km=0.1,
|
||||
fill_color=white,
|
||||
color=stop_color,
|
||||
width=12
|
||||
)
|
||||
)
|
||||
if i == last_index:
|
||||
context.add_object(
|
||||
staticmaps.Marker(
|
||||
point,
|
||||
color=stop_color,
|
||||
size=12
|
||||
)
|
||||
)
|
||||
elif mode not in ["TRAM", "BUS", "SUBWAY", "SUBURBAN", "METRO"]: # wayyy too many stops...
|
||||
context.add_object(
|
||||
staticmaps.Circle(
|
||||
center=point,
|
||||
fill_color=white,
|
||||
radius_km=0.1,
|
||||
color=stop_color,
|
||||
width=10,
|
||||
)
|
||||
)
|
||||
|
||||
lats = [c.lat().degrees for c in stop_coords]
|
||||
lons = [c.lng().degrees for c in stop_coords]
|
||||
lat_span = max(lats) - min(lats)
|
||||
lon_span = max(lons) - min(lons)
|
||||
|
||||
MIN_SPAN = 0.01
|
||||
|
||||
if lat_span < MIN_SPAN or lon_span < MIN_SPAN:
|
||||
center_lat = (max(lats) + min(lats)) / 2
|
||||
center_lon = (max(lons) + min(lons)) / 2
|
||||
pad = MIN_SPAN / 2
|
||||
|
||||
context.add_object(
|
||||
staticmaps.Circle(
|
||||
staticmaps.create_latlng(center_lat + pad, center_lon + pad),
|
||||
radius_km=0.01,
|
||||
fill_color=staticmaps.TRANSPARENT,
|
||||
color=staticmaps.TRANSPARENT,
|
||||
width=0,
|
||||
)
|
||||
)
|
||||
context.add_object(
|
||||
staticmaps.Circle(
|
||||
staticmaps.create_latlng(center_lat - pad, center_lon - pad),
|
||||
radius_km=0.01,
|
||||
fill_color=staticmaps.TRANSPARENT,
|
||||
color=staticmaps.TRANSPARENT,
|
||||
width=0,
|
||||
)
|
||||
)
|
||||
|
||||
try:
|
||||
image = context.render_cairo(400, 300)
|
||||
image.write_to_png("src/data/assets/current_map.png")
|
||||
except RuntimeError:
|
||||
logger(f"You don't have cairo installed! Because of that I can only give you a low-res png of the route...")
|
||||
image = context.render_pillow(400, 300)
|
||||
image.save("src/data/assets/current_map.png")
|
||||
logger("Map generated!")
|
||||
|
||||
Reference in New Issue
Block a user