Announcements

This commit is contained in:
Kaaninchen
2026-07-22 18:53:07 +02:00
parent bb11a5a85f
commit 24edd1fe05
4 changed files with 80 additions and 12 deletions
+7 -1
View File
@@ -34,7 +34,8 @@ uv pip install -r requirements.txt
"server": , // Discord Server ID "server": , // Discord Server ID
"vc": , // Server VC ID "vc": , // Server VC ID
"random": true, // Random Zug aus der Anzeigetafel (true) oder erster Zug, der angezeigt wird (false) "random": true, // Random Zug aus der Anzeigetafel (true) oder erster Zug, der angezeigt wird (false)
"emojis": true, // Emoji Namen beim Channel-Namen (true) oder nicht (false) "emojis": true, // Emoji Namen beim Channel-Namen (true) oder nicht (false),
"announcemenents": true, // Announcements zur aktuellen Zugreise im VC-Kanal ja (true) oder nein (false)
"formatting": "┇", // VC Name. Davor steht der Emoji, danach der Zug. "formatting": "┇", // VC Name. Davor steht der Emoji, danach der Zug.
"blacklist": [] // Blacklist für bestimmte Zug-Typen "blacklist": [] // Blacklist für bestimmte Zug-Typen
} }
@@ -53,6 +54,11 @@ Sollte man `random = false` setzen, würde immer der erste Zug an der Anzeigetaf
Wenn man nur einen Bahnhof hat ist es stark empfohlen random zu nutzen. Sonst könnte der Bot bei unvollständigen Einträgen in einer Schleife immer wieder vergeblich den selben unvollständigen Zug probieren. Wenn man nur einen Bahnhof hat ist es stark empfohlen random zu nutzen. Sonst könnte der Bot bei unvollständigen Einträgen in einer Schleife immer wieder vergeblich den selben unvollständigen Zug probieren.
#### announcements:
Der Bot kann während der Zugreise Ankündigungen in den Textkanal vom Sprachkanal schicken. Das würde er aber auch nur machen wenn sich dort mindestens eine Person aufhält. Aktuelle Announcements:
- 3-5 Min vor Umstieg gibt es eine Ankündigung dafür
- Info Embed bei Umstieg
#### blacklist: #### blacklist:
Die Blacklist ist dafür gedacht, ganze Zugtypen zu ignorieren. Beispielsweise möchte man, dass der Bot keine ICE's, keine NightJets und keine European Sleepers auswählt, da diese sehr lange Strecken fahren und der VC somit lange unverändert bleibt: Die Blacklist ist dafür gedacht, ganze Zugtypen zu ignorieren. Beispielsweise möchte man, dass der Bot keine ICE's, keine NightJets und keine European Sleepers auswählt, da diese sehr lange Strecken fahren und der VC somit lange unverändert bleibt:
```json ```json
+2 -1
View File
@@ -1,11 +1,12 @@
{ {
"token": "", "token": "",
"stations": [ "Berlin Hbf", "Hamburg Hbf", "München Hbf", "Amsterdam Centraal"], "stations": ["Hamburg Hbf", "München Hbf", "Köln Hbf", "Amsterdam Centraal"],
"dbf": "https://dbf.finalrewind.org", "dbf": "https://dbf.finalrewind.org",
"server": , "server": ,
"vc": , "vc": ,
"random": true, "random": true,
"emojis": true, "emojis": true,
"announcements": true,
"formatting": "┇", "formatting": "┇",
"blacklist": [] "blacklist": []
} }
+33 -8
View File
@@ -24,6 +24,20 @@ def format_iso_timestamp(isostr):
parsed_time = discord.utils.parse_time(isostr) parsed_time = discord.utils.parse_time(isostr)
return discord.utils.format_dt(parsed_time, style="t") return discord.utils.format_dt(parsed_time, style="t")
def build_embed_footer(operator_slogans):
dbf = config.get("dbf", "https://dbf.finalrewind.org")
footer_notice = f"Daten großzügig bereitgestellt von {dbf} • Typ: {handlers.train_type}"
footer_text = (
f"{random.choice(operator_slogans)}{footer_notice}"
if operator_slogans else
footer_notice
)
icon = f"{dbf}/static/icons/icon-96x96.png"
return {
"text": footer_text,
"icon": icon
}
def build_info_embed() -> discord.Embed | None: def build_info_embed() -> discord.Embed | None:
conn = handlers.current conn = handlers.current
info = handlers.train_info info = handlers.train_info
@@ -62,16 +76,27 @@ def build_info_embed() -> discord.Embed | None:
embed.add_field(name="Route", value="\n".join(route_lines)) embed.add_field(name="Route", value="\n".join(route_lines))
slogans = operator_infos.get("slogan") footer = build_embed_footer(operator_infos.get("slogan"))
dbf = config.get("dbf", "https://dbf.finalrewind.org") embed.set_footer(text=footer["text"], icon_url=footer["icon"])
footer_notice = f"Daten großzügig bereitgestellt von {dbf} • Typ: {handlers.train_type}"
footer_text = ( return embed
f"{random.choice(slogans)}{footer_notice}"
if slogans else def build_announcement_embed(msg):
footer_notice current_operator = resolve_operator(handlers.train_info["operators"])
operator_infos = operator_metadata(current_operator)
embed = discord.Embed(
title = "Informationen zu Ihrer Fahrt",
description=msg,
color=operator_infos["color"]
) )
embed.set_footer(text=footer_text, icon_url=f"{dbf}/static/icons/icon-96x96.png") embed.set_author(name=current_operator)
embed.set_thumbnail(url=operator_infos["logo"])
footer = build_embed_footer(operator_infos.get("slogan"))
embed.set_footer(text=footer["text"], icon_url=footer["icon"])
return embed return embed
def build_error_embed(errormsg) -> discord.Embed: def build_error_embed(errormsg) -> discord.Embed:
+37 -1
View File
@@ -1,12 +1,34 @@
import discord import discord
from datetime import datetime, timedelta from datetime import datetime, timedelta
import asyncio import asyncio
import random
from src.config import config from src.config import config
from src.utils import random_connection, get_train_info, get_channel_formatting, logger from src.utils import random_connection, get_train_info, get_channel_formatting, logger
from src.embeds import build_announcement_embed, build_info_embed
current = None current = None
_scheduled_task: asyncio.Task | None = None _scheduled_task: asyncio.Task | None = None
async def announcer(bot, announcement):
voice_channel = bot.get_channel(config["vc"])
if len(voice_channel.members) > 0:
match announcement:
case "ende":
embed = build_announcement_embed(
f'Sehr geehrte Fahrgäste,\nIn wenigen Minuten erreichen wir {current['destination']}. Dieser Zug endet dort.\n\nWir wünschen Ihnen eine angenehme Weiterreise.\n\nVielen Dank für Ihr Vertrauen und auf Wiedersehen.')
case "umstieg":
embed = build_info_embed()
case _:
embed = None
if embed is None:
logger(f"Unbekanntes Announcement: {announcement}")
else:
await voice_channel.send(embed=embed)
else:
logger(f"Announcement {announcement} wird geskipped, keiner da")
return
async def rename_vc(bot: discord.Bot): async def rename_vc(bot: discord.Bot):
global current, train_name, train_info, train_type, _scheduled_task global current, train_name, train_info, train_type, _scheduled_task
@@ -64,13 +86,27 @@ async def rename_vc(bot: discord.Bot):
await channel.set_status(f"Ankunft um {arrival.strftime('%H:%M')}") await channel.set_status(f"Ankunft um {arrival.strftime('%H:%M')}")
logger(f"Name geändert!") logger(f"Name geändert!")
if config.get("announcements", True):
await announcer(bot, "umstieg")
return True return True
async def _schedule_next_umstieg(bot, arrival): async def _schedule_next_umstieg(bot, arrival):
announcement = config.get("announcements", True)
announcement_countdown = random.randrange(180, 300) # letzte station announcement ist meistens 3-5min vor ankunft
wait_seconds = (arrival - datetime.now()).total_seconds() wait_seconds = (arrival - datetime.now()).total_seconds()
if wait_seconds > 0: if wait_seconds > 0:
remaining = str(timedelta(seconds=wait_seconds)) remaining = str(timedelta(seconds=wait_seconds))
logger(f"Nächster Umstieg in {remaining.split(".")[0]} ({arrival.strftime('%H:%M:%S')} Uhr)") logger(f"Nächster Umstieg in {remaining.split('.')[0]} ({arrival.strftime('%H:%M:%S')} Uhr)")
if announcement and wait_seconds > announcement_countdown:
wait_until_end_announcement = wait_seconds - announcement_countdown
await asyncio.sleep(wait_until_end_announcement)
await announcer(bot, "ende")
await asyncio.sleep(announcement_countdown)
else:
await asyncio.sleep(wait_seconds) await asyncio.sleep(wait_seconds)
logger("Zug angekommen, wähle neue Verbindung...") logger("Zug angekommen, wähle neue Verbindung...")
await rename_vc(bot) await rename_vc(bot)