Compare commits

..
27 Commits
Author SHA1 Message Date
Kaaninchen 199cedb8b1 commit so that the new branch uploads 2026-08-18 14:33:22 +02:00
Kaaninchen 172d6d5cf1 add README notice about maintenance status 2026-08-18 14:31:47 +02:00
Kaaninchen 72b4d3185a emojis.py: Bus emoji 2026-08-12 19:00:46 +02:00
Kaaninchen e2e7bf2a14 README: grammar 2026-08-12 14:02:25 +02:00
Kaaninchen 12f65eabc2 fix: .gitkeep damit announcements ordner gepushed wird 2026-08-12 14:01:40 +02:00
Kaaninchen 934ab0479d README: verbessert 2026-08-12 13:57:47 +02:00
Kaaninchen a011308c2f operators.py: Agilis und enno 2026-08-12 13:51:57 +02:00
Kaaninchen 453520c8d2 feat: voice_announcement 2026-08-12 13:47:50 +02:00
Kaaninchen e09ad38816 operators.py: SNCF 2026-08-11 23:23:21 +02:00
Kaaninchen 8dcac27338 operators.py: GoVolta 2026-08-10 12:34:38 +02:00
Kaaninchen c5eac4fcce Fix: Discord reconnects abfangen 2026-08-09 12:42:13 +02:00
Kaaninchen bd7e031972 README: Nahreisezug Bug 2026-08-09 12:38:41 +02:00
Kaaninchen 82864167bc fix: asyncio aufrufe abbrechen, damit nicht mehrere tasks gleichzeitig laufen 2026-08-09 12:23:37 +02:00
Kaaninchen 0a5dcc26c5 Fix: print mit logger ersetzt in main.py 2026-07-27 19:47:09 +02:00
Kaaninchen ab95047e59 Fix: vergessen error handling embed kommentar zu entfernen 2026-07-24 16:30:40 +02:00
Kaaninchen 24edd1fe05 Announcements 2026-07-22 18:53:07 +02:00
Kaaninchen bb11a5a85f README: Fix blacklist beispiele 2026-07-21 13:42:55 +02:00
Kaaninchen 4c032a7f0f README 2026-07-21 13:41:20 +02:00
Kaaninchen bf48cd984b Config: Emojis deaktivieren 2026-07-21 13:40:31 +02:00
Kaaninchen d3e6b8ac6c Config: Eigene DBF Instanz 2026-07-21 13:19:48 +02:00
Kaaninchen e0dfc1bada Update README 2026-07-20 22:39:55 +02:00
Kaaninchen 7e4c0ee14a update status 2026-07-20 22:11:07 +02:00
Kaaninchen e289cdb7cf README: .github bilder geupdated 2026-07-20 21:51:26 +02:00
Kaaninchen 463a6d283b operators.py: European Sleeper 2026-07-20 21:31:54 +02:00
Kaaninchen 585bee7a63 leerzeichen zwischen emoji und formattierung entfernt 2026-07-20 21:28:42 +02:00
Kaaninchen e86d2f6a6a TGV zu Emojis hinzugefügt 2026-07-20 21:25:15 +02:00
Kaaninchen 068eaa7b85 Dynamische Emojis je nach Zugart 2026-07-20 21:22:09 +02:00
16 changed files with 363 additions and 43 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 110 KiB

+2
View File
@@ -1,3 +1,5 @@
venv/
__pycache__/
src/data/announcements/*
!src/data/announcements/.gitkeep
config.json
+148 -14
View File
@@ -1,45 +1,179 @@
# Gleiswechsel
## THIS BRANCH IS NO LONGER MAINTAINED
## This is the code for the legacy Gleiswechsel bot, based on the much simpler dbf API.
Gleiswechel ist ein Discord Bot, welcher einen Sprachkanal zu einer real-existierenden, aktuell befahrenden Zugverbindung umbenennt. Diesen Namen behält der Kanal so lange, wie die Verbindung in echt dauert.
![Beispiel Kanal](.github/preview_kanal.png)
Der Bot stellt außerdem den `/info` Befehl dar, welche einem weitere Informationen zur Verbindung zurückgibt
Der Bot stellt außerdem den `/info` Befehl dar, welcher einem weitere Informationen zur Verbindung zurückgibt
![Beispiel info](.github/preview_info.png)
## Setup
```sh
git clone https://github.com/kaaninchen/Gleiswechsel.git
# Mit Python
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
# Mit uv
uv venv venv
source venv/bin/activate
uv pip install -r requirements.txt
```
### Config
`$ mv config.json.example`
```
`$ cp config.json.example config.json`
```jsonc
{
"token": "", // Discord Token
"stations": [ "Berlin Hbf", "Hamburg Hbf", "München Hbf", "Amsterdam Centraal"], // Stationen deiner Wahl. Im Zweifel auf https://dbf.finalrewind.org/ Namen raussuchen
"token": "", // Token des Bots
"stations": [ "Berlin Hbf", "Hamburg Hbf", "München Hbf", "Amsterdam Centraal"], // Bahnhöfe, von denen die Anzeigetafelns gelesen werden. Namen kann man auf https://dbf.finalrewind.org/ nachschlagen.
"dbf": "https://dbf.finalrewind.org", // Die DBF Instanz. Normalerweise müsste man hier nichts ändern
"server": , // Discord Server ID
"vc": , // Discord VC ID
"formatting": "🚅┇", // Name des Kanals
"random": false // Random Züge aussuchen (true) oder immer die erste Station der Anzeigetafel (false)
"vc": , // Server VC ID
"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),
"formatting": "┇", // VC Name. Davor steht der Emoji, danach der Zug.
"announcemenents": true, // Announcements zur aktuellen Zugreise im VC-Kanal ja (true) oder nein (false)
"voice_announcements": [ // siehe weitere config erklärungen in der README, benötigt extra setup
{
"enabled": false,
"stations": {
"general": ""
}
}
],
"blacklist": [] // Blacklist für bestimmte Zug-Typen
}
```
#### Erklärung bezüglich random:
Bei kleineren Bahnhöfen stehen an den Anzeigetafeln die Namen Züge öfters Stunden vor Abfahrt, da das Gleis sonst frei ist. Dadurch wird auch der Name des VC sehr lange gleich bleiben. Sollte man random ausmachen, würde immer der erste Zug an der Anzeigetafel genommen werden, welcher auch der erst der am frühesten losfährt. Wenn man aber wenige Bahnhöfen zur Auswahl hat könnte es repetitiv werden, da die Züge ja oft hin und her fahren.
<details>
<summary>weitere Config Erklärungen</summary>
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 vergeblichg den selben Zug probieren.
#### dbf:
Falls, aus irgendwelchem Gründen, man nicht die [offizielle DBF Instanz](https://dbf.finalrewind.org) nutzen möchte, hat man die Möglichkeiten seine eigene zu hosten. Instruction dazu gibts auf dem [zuständigen Repo](https://github.com/derf/db-fakedisplay). Dafür kann man das Feld in der config mit der eigenen URL austauschen.
## Metadaten
#### random:
Bei kleineren Bahnhöfen stehen an den Anzeigetafeln öfters die Züge lange vor Abfahrt da, weil sonst der Bahnhof leer steht. Dadurch wird auch der Name des VC sehr lange gleich bleiben.
Sollte man `random = false` setzen, würde immer der erste Zug an der Anzeigetafel genommen werden, welcher auch der wäre welcher am frühesten losfährt. Wenn man viele Bahnhöfe hat besteht darin keine Gefahr.
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
- voice_announcements
#### voice_announcements:
⚠️ Announcements muss aktiviert sein
Der Bot kann 3-5 Min vor Umstieg den VC joinen und eine Audiodatei abspielen. Dafür braucht der Bot ffmpeg.
```sh
$ sudo apt install ffmpeg # Debian/Ubuntu
$ brew install ffmpeg # macOS (brew)
```
##### Voice_announcements config:
```json
"voice_announcements": [
{
"enabled": true,
"stations": {
"general": "general.aac",
"Hannover": "hannover.aac"
}
}
],
```
Audiodateien werden in [src/data/announcements](src/data/announcements) platziert. In der Config wird der Name der Station (z.B. Hannover) zu dem Namen der Audiodatei (z.B. hannover.aac) zugewiesen. Die Audiodatei braucht keinen Path.
Der Eintrag `general` meint die allgemeine Audiodatei, welche bei jeder Endstation (mit Ausnahme der zugewiesenen) spielt. Sollte man nur Audios bei zugewiesenen Endstationen abspielen wollen kann man `general` leerlassen, der bot skipped das ganze dann:
```json
"stations": {
"general": ""
}
```
Es gibt außerdem die Möglichkeit, mehrere Audiodateien zu einer Endstation durch Listen zuzuweisen. Der Bot sucht sich dann jedes mal wenn er den vc joined eine davon aus.
```json
"stations": {
"general": ["general_1.aac", "general_2.aac"]
}
```
#### 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:
```json
{
"blacklist": [
"ICE",
"NJ",
"ES"
]
}
```
Die Namen der einzelnen Zugtypen kann im Footer von `/info` oder im Terminal log erfahren.
#### src/data
Es kann vorkommen, dass während dem `/info` Befehl das Logo und die Farbe des Bahnuntermehns fehlt.
![Beispiel für fehlende Daten](.github/info_fehlende_daten.png)
Die zugehörigen Daten lassen sich innerhalb [operators.py](src/data/operators.py) ergänzen. Der Aufbau dabei sollte selbsterklärend sein, dennoch habe ich eine kleine Beschreibung in die Datei hinzugefügt. Bei Änderungen sind PR's willkommen, ebenso wie Issues falls man das nicht selber ändern will da das ja eigentlich mein Job sein sollte.
Die zugehörigen Daten lassen sich innerhalb [src/data/operators.py](src/data/operators.py) ergänzen. Der Aufbau dabei sollte selbsterklärend sein, dennoch habe ich eine kleine Beschreibung in die Datei hinzugefügt. Bei Änderungen sind PR's willkommen.
Die Statuseinträge, von welchen der Bot alle 5 Minuten einen aussucht, kann mn in [status.py](src/data/operators.py) anpassen
Emojis für die Formattierung werden dynamisch anhand des Zugtypens gepulled. Dabei wird zwischen Nahverkehr und Fernverkehr unterschieden. Bei einem Zugtyp, welcher zu keiner der Kategorie assigned ist, wird ein Fallback Emoji eingesetzt. Sollte man einen Zugtypen hinzufügen wollen oder die Emojis ändern/deaktivieren wollen ist dies in [src/data/emojis.py](src/data/emojis.py) möglich. Die Namen der einzelnen Zugtypen kann im Footer von `/info` oder im Terminal log erfahren.
Den Status, den sich der Bot alle 5 Minuten random auswählt, kann man in [src/data/status.py](src/data/status.py) anpassen.
</details>
### Running
```sh
# Python
python main.py
# ODER
python3 main.py
# uv
uv run main.py
```
## Bekannte Bugs
#### Stuttgart in Berlin
Ich weiß nicht ganz wieso, aber die API vertauscht manchmal die S-Bahn von Berlin mit der S-Bahn von Stuttgart. Es scheint eher ein API-Issue zu sein, weswegen ich da leider mit dem Bot nicht viel ändern kann.
Der Bug führt dazu, dass bei manchen S-Bahn Verbindungen `DB Regio AG S-Bahn Stuttgart` als Betreiber der Berlin S-Bahn angezeigt wird. Außerdem gibt die API dem Bot die Ankunftszeiten einer S-Bahn Verbindung von Stuttgart wieder, während die Route von der aus Berlin stammt (Die Route und die Ankunftszeiten werden von zwei verschiedenen Endpoints gepulled: Route: `{dbf}/Berlin%20Hbf.json`, Ankunftszeit: `{dbf}/z/S%20{ID}/Berlin Hbf.json`).
Falls das einem zu sehr stört kann man S-Bahns auf die Blacklist packen.
```json
{
"blacklist": [
"S "
]
}
```
#### Nahreisezug
Der Bot empfängt durch die dbf API ein Operator Field, wo der Betreiber des Züges angezeigt wird. Dadurch kann im `/info` Embed das Logo durch [src/data/operators.py](src/data/operators.py) zugewiesen werden. Manchmal schmeißt die API aber als Operator "Nahreisezug" aus.
![Beispiel für Nahreisezug](.github/info_nahreisezug.png)
Wenn mehrere Betreiber angezeigt werden (bspw `["SBB", "Nahreisezug"]`) versucht der Bot immer, den Embed den bekannten Betreiber (in dem Fall SBB) zuzuweisen. Sollte allerdings nur Nahreisezug angezeigt werden, kann damit nicht gearbeitet werden, wodurch die Fallback Metadaten genutzt werden.
Die Fallback Metadaten können in [src/data/operators.py](src/data/operators.py) angepasst werden.
+14 -5
View File
@@ -1,11 +1,20 @@
{
"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",
"server": ,
"vc": ,
"formatting": "🚅┇",
"random": false
"blacklist": [
"random": true,
"emojis": true,
"formatting": "┇",
"announcements": true,
"voice_announcements: [
{
"enabled": false,
"stations": {
"general": ""
}
}
]
"blacklist": []
}
+9 -2
View File
@@ -5,6 +5,7 @@ from src.config import config
from src.handlers import rename_vc
from src.commands import setup_commands
from src.embeds import build_error_embed
from src.utils import logger
from src.data.status import discord_status
bot = discord.Bot(intents=discord.Intents.all())
@@ -13,14 +14,20 @@ setup_commands(bot)
@tasks.loop(minutes=5)
async def change_status():
status = random.choice(discord_status)
await bot.change_presence(activity=discord.Game(name=status))
await bot.change_presence(activity=discord.Game(name=f"{status} • /info"))
_bot_initialized = False
@bot.event
async def on_ready():
global _bot_initialized
print(f"{bot.user} ist online")
if not change_status.is_running():
change_status.start()
if not _bot_initialized:
_bot_initialized = True
await rename_vc(bot)
else:
logger("Discord Reconnect, laufende Fahrt bleibt unangetastet")
@bot.event
async def on_application_command_error(ctx, error):
@@ -30,4 +37,4 @@ async def on_application_command_error(ctx, error):
try:
bot.run(config['token'])
except:
print("An error occured while parsing the token (check the config!)", "fatal")
logger("Fehler beim parsen des token", "fatal")
+4
View File
@@ -3,7 +3,9 @@ aiohttp==3.14.1
aiosignal==1.4.0
attrs==26.1.0
certifi==2026.6.17
cffi==2.1.1
charset-normalizer==3.4.9
davey==0.1.6
frozenlist==1.8.0
idna==3.18
multidict==6.7.1
@@ -11,6 +13,8 @@ numpy==2.5.1
pandas==3.0.3
propcache==0.5.2
py-cord==2.8.0
pycparser==3.0
pynacl==1.6.2
python-dateutil==2.9.0.post0
requests==2.34.2
six==1.17.0
View File
+18
View File
@@ -0,0 +1,18 @@
emoji_list = {
"Fallback": "🚉",
"Nahverkehr": "🚃",
"Höchstgeschwindigkeit": "🚄",
"Bus": "🚎"
}
train_types = { # Unvollständig!
"ICE": emoji_list["Höchstgeschwindigkeit"],
"TGV": emoji_list["Höchstgeschwindigkeit"],
"RE": emoji_list["Nahverkehr"],
"RB": emoji_list["Nahverkehr"],
"ARV": emoji_list["Nahverkehr"],
"BRB": emoji_list["Nahverkehr"],
"Bus": emoji_list["Bus"]
}
+20
View File
@@ -78,6 +78,26 @@ OPERATORS = {
"Westbahn Management GmbH": {
"logo": "https://corporate.westbahn.at/uploads/Logos/westbahn2025-logo-signet-small.png",
"color": 0x1D4A83
},
"European Sleeper": {
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/European_Sleeper_Logo.svg/960px-European_Sleeper_Logo.svg.png",
"color": 0xEB4A27
},
"GoVolta": {
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5b/GoVolta_logo.svg/1280px-GoVolta_logo.svg.png",
"color": 0x0B70F6
},
"SNCF": {
"logo": "https://upload.wikimedia.org/wikipedia/en/thumb/f/f4/Sncf-logo.svg/960px-Sncf-logo.svg.png",
"color": 0x812B6D
},
"Agilis": {
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a3/Agilis_Logo.svg/960px-Agilis_Logo.svg.png",
"color": 0xCAE15B
},
"enno": {
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Enno_logo.svg/960px-Enno_logo.svg.png",
"color": 0x88216F
}
}
+7 -2
View File
@@ -5,7 +5,12 @@ discord_status = [
"Störung an der Weiche",
"Heute ohne Bordbistro",
"SEV ist eingerichtet",
"BahnBonus Status",
"Gleiswechsel!",
"Zug entgleist :("
"Zug entgleist :(",
"Die Bundespolizei ist informiert",
"Dieser Zug endet hier",
"Außerplanmäßiger Halt in Oberhausen Hbf",
"Verspätung aus vorheriger Fahrt",
"Der Zug hat keine Bremsen",
"Heute ohne Klimatisierung",
]
+34 -6
View File
@@ -2,6 +2,7 @@ import random
import discord
from datetime import datetime, timedelta
import src.handlers as handlers
from src.config import config
from src.utils import operator_metadata, format_via_list, resolve_operator, logger
def format_timestamp(timestr):
@@ -23,6 +24,20 @@ def format_iso_timestamp(isostr):
parsed_time = discord.utils.parse_time(isostr)
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:
conn = handlers.current
info = handlers.train_info
@@ -61,14 +76,27 @@ def build_info_embed() -> discord.Embed | None:
embed.add_field(name="Route", value="\n".join(route_lines))
slogans = operator_infos.get("slogan")
footer_text = (
f"{random.choice(slogans)} • Daten großzügig bereitgestellt von dbf.finalrewind.org"
if slogans else
"Daten großzügig bereitgestellt von dbf.finalrewind.org"
footer = build_embed_footer(operator_infos.get("slogan"))
embed.set_footer(text=footer["text"], icon_url=footer["icon"])
return embed
def build_announcement_embed(msg):
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="https://dbf.finalrewind.org/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
def build_error_embed(errormsg) -> discord.Embed:
+88 -8
View File
@@ -1,14 +1,74 @@
import discord
from datetime import datetime, timedelta
import asyncio
import random
from pathlib import Path
from src.config import config
from src.utils import random_connection, get_train_info, 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
_scheduled_task: asyncio.Task | None = None
async def rename_vc(bot: discord.Bot):
global current, train_name, train_info, _scheduled_task
async def announcer(bot, announcement):
voice_channel = bot.get_channel(config["vc"])
if len(voice_channel.members) > 0:
match announcement:
case "ende":
destination = current['destination']
embed = build_announcement_embed(
f'Sehr geehrte Fahrgäste,\nIn wenigen Minuten erreichen wir {destination}. Dieser Zug endet dort.\n\nWir wünschen Ihnen eine angenehme Weiterreise.\n\nVielen Dank für Ihr Vertrauen und auf Wiedersehen.')
if config["voice_announcements"][0]["enabled"]:
await voice_announcer(bot, destination, voice_channel)
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 voice_announcer(bot: discord.Bot, destination, voice_channel):
voice_announcement_config = config["voice_announcements"][0]
voice_stations = voice_announcement_config["stations"]
if destination in voice_stations:
announcement_for = destination
else:
if voice_stations.get("general", "") == "":
return
announcement_for = "general"
if voice_stations.values() == list:
sound_file = random.choice(voice_stations.get(announcement_for))
else:
sound_file = voice_stations.get(announcement_for)
sound_path = f"src/data/announcements/{sound_file}"
if Path(sound_path).is_file() is False:
logger(f"Konnte Datei {sound_path} nicht finden", "error")
return
logger(f"VC wird betreten, spiele {sound_path}")
vc = await voice_channel.connect(timeout=15, reconnect=True)
audio_source = discord.FFmpegPCMAudio(sound_path)
if not vc.is_playing():
def after_playing(error):
if error:
logger(f"Player error: {error}", "error")
bot.loop.create_task(vc.disconnect())
logger("VC wird verlassen")
vc.play(audio_source, after=after_playing)
async def rename_vc(bot: discord.Bot, from_scheduler: bool = False):
global current, train_name, train_info, train_type, _scheduled_task
guild = bot.get_guild(int(config["server"]))
if guild is None:
@@ -20,6 +80,9 @@ async def rename_vc(bot: discord.Bot):
logger(f"Es konnte kein VC mit der ID {config['vc']} auf dem Server gefunden werden", "fatal")
return False
if not from_scheduler and _scheduled_task and not _scheduled_task.done():
_scheduled_task.cancel()
attempt = 0
while True:
if attempt == 20:
@@ -51,24 +114,41 @@ async def rename_vc(bot: discord.Bot):
train_name = f"{train} nach {current['destination']} von {current['station']}"
logger(f"Vorbereitung auf {train_name} (typ: {train_type})")
arrival = datetime.fromisoformat((train_info["arrival"]))
formatting = get_channel_formatting(train_type)
_scheduled_task = asyncio.create_task(_schedule_next_umstieg(bot, arrival))
print("-----------------------------------------")
logger(f"Umstieg: {train_name}")
logger(f"Betreiber: {train_info['operators']}")
logger(f"Betreiber: {''.join(train_info['operators'])}")
logger(f"Train-Type: {train_type}")
logger(f"Wenn der Name nicht geändert wird bin ich im Cooldown")
await channel.edit(name=f"{config['formatting']}{train_name}")
await channel.edit(name=f"{formatting}{train_name}")
await channel.set_status(f"Ankunft um {arrival.strftime('%H:%M')}")
logger(f"Name geändert!")
if config.get("announcements", True):
await announcer(bot, "umstieg")
_scheduled_task = asyncio.create_task(_schedule_next_umstieg(bot, arrival))
return True
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()
if wait_seconds > 0:
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)
logger("Zug angekommen, wähle neue Verbindung...")
await rename_vc(bot)
await rename_vc(bot, from_scheduler=True)
+16 -3
View File
@@ -5,8 +5,10 @@ import importlib
from datetime import datetime
from src.config import config
import src.data.operators as operators_module
import src.data.emojis as emojis
_operators_mtime = None
dbf = config.get("dbf", "https://dbf.finalrewind.org")
def _reload_operators_if_changed():
global _operators_mtime
@@ -27,10 +29,10 @@ def random_connection():
available_stations = config["stations"].copy()
while True:
if not available_stations:
logger("Keine validen Bahnhöfe. Schlag den richtigen Bahnhofsnamen auf https://dbf.finalrewind.org/ nach", "fatal")
logger("Keine validen Bahnhöfe. Schlag den richtigen Bahnhofsnamen auf {dbf} nach", "fatal")
station = random.choice(available_stations)
url = f"https://dbf.finalrewind.org/{station}.json"
url = f"{dbf}/{station}.json"
blacklist = config.get("blacklist", [])
try:
@@ -71,7 +73,7 @@ def random_connection():
}
def get_train_info(station, train_ID, train_type):
url = f"https://dbf.finalrewind.org/z/{train_type}%20{train_ID}/{station}.json"
url = f"{dbf}/z/{train_type}%20{train_ID}/{station}.json"
logger(f"Fetche {url}")
try:
response = requests.get(url)
@@ -128,6 +130,17 @@ def operator_metadata(operator):
return operators_module.OPERATORS.get(operator, operators_module.OPERATORS["fallback"])
def get_channel_formatting(train_type):
formatting = config.get("formatting", "")
train_emoji = ""
if config.get("emojis", True):
train_emoji = emojis.train_types.get(train_type)
if train_emoji is None:
train_emoji = emojis.emoji_list.get("Fallback", "")
return f"{train_emoji}{formatting}"
def logger(msg, log_type="info"):
status = log_type.upper()
current_time = datetime.now().strftime('%X')