Compare commits

...
4 Commits
Author SHA1 Message Date
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
5 changed files with 87 additions and 34 deletions
+69 -24
View File
@@ -3,63 +3,108 @@ Gleiswechel ist ein Discord Bot, welcher einen Sprachkanal zu einer real-existie
![Beispiel Kanal](.github/preview_kanal.png) ![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) ![Beispiel info](.github/preview_info.png)
## Setup ## Setup
```sh ```sh
git clone https://github.com/kaaninchen/Gleiswechsel.git git clone https://github.com/kaaninchen/Gleiswechsel.git
# Mit Python
python -m venv venv python -m venv venv
source venv/bin/activate source venv/bin/activate
pip install -r requirements.txt pip install -r requirements.txt
# Mit uv
uv venv venv
source venv/bin/activate
uv pip install -r requirements.txt
``` ```
### Config ### Config
`$ mv config.json.example` `$ cp config.json.example config.json`
``` ```jsonc
{ {
"token": "", # Token des Bots "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. "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.
"server": , # Discord Server ID "dbf": "https://dbf.finalrewind.org", // Die DBF Instanz. Normalerweise müsste man hier nichts ändern
"vc": , # Server VC ID "server": , // Discord Server ID
"random": true, # Random Zug aus der Anzeigetafel (true) oder erster Zug, der angezeigt wird (false) "vc": , // Server VC ID
"formatting": "┇", # VC Name. Davor steht der Emoji, danach der Zug "random": true, // Random Zug aus der Anzeigetafel (true) oder erster Zug, der angezeigt wird (false)
"blacklist": [] # Blacklist für bestimmte Zug-Typen "emojis": true, // Emoji Namen beim Channel-Namen (true) oder nicht (false)
"formatting": "┇", // VC Name. Davor steht der Emoji, danach der Zug.
"blacklist": [] // Blacklist für bestimmte Zug-Typen
} }
``` ```
#### random: <details>
Bei kleineren Bahnhöfen stehen an den Anzeigetafeln die Namen Züge öfters Stunden, bevor der Zug eigentlich abfährt, da dort sonst keiner fährt. Dadurch wird auch der Name des VC sehr lange gleich bleiben. <summary>weitere Config Erklärungen</summary>
Sollte man `random = false` setzen, würde immer der erste Zug an der Anzeigetafel genommen werden, welcher auch der erst der 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 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.
#### 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.
#### blacklist: #### blacklist:
Die Blacklist ist dafür gedacht, ganze Zugtypen zu ignorieren. So würde man es beispielsweise bevorzugen, dass der Bot keine ICE's, keine NightJets und keine European Sleepers auswählt, da diese sehr lange Strecken haben 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
{ {
...
"blacklist": [ "blacklist": [
"ICE", "ICE",
"NJ", "NJ",
"ES "ES"
] ]
} }
``` ```
Die Namen der einzelnen Zugtypen kann mit im Footer von `/info` oder im Terminal log erfahren. Die Namen der einzelnen Zugtypen kann im Footer von `/info` oder im Terminal log erfahren.
## Data #### src/data
Es kann vorkommen, dass während dem `/info` Befehl das Logo und die Farbe des Bahnuntermehns fehlt. 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) ![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. 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 Datei wird leider nie vollständig sein da die verschiedenen Fahrtaufträge in Deutschland sehr fragmentiert sind.
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 ist. Sollte man einen Zugtypen hinzufügen wollen oder die Emojis ändern/deaktivieren wollen ist dies in [emojis.py](src/data/emojis.py) möglich. Die Namen der einzelnen Zugtypen kann mit im Footer von `/info` oder im Terminal log erfahren. 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.
Die Statuseinträge, von welchen der Bot alle 5 Minuten einen aussucht, kann mn in [status.py](src/data/status.py) anpassen. 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 Upstream-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 "
]
}
```
Allgemein scheint der Berlin HBF eine sehr komische Station zu sein. Die meisten Bugs beim Testing kommt von dieser. Falls jemand mehr weiß sind PR's wie immer willkommen
+2
View File
@@ -1,9 +1,11 @@
{ {
"token": "", "token": "",
"stations": [ "Berlin Hbf", "Hamburg Hbf", "München Hbf", "Amsterdam Centraal"], "stations": [ "Berlin Hbf", "Hamburg Hbf", "München Hbf", "Amsterdam Centraal"],
"dbf": "https://dbf.finalrewind.org",
"server": , "server": ,
"vc": , "vc": ,
"random": true, "random": true,
"emojis": true,
"formatting": "┇", "formatting": "┇",
"blacklist": [] "blacklist": []
} }
+2 -2
View File
@@ -21,12 +21,12 @@ async def on_ready():
if not change_status.is_running(): if not change_status.is_running():
change_status.start() change_status.start()
await rename_vc(bot) await rename_vc(bot)
'''
@bot.event @bot.event
async def on_application_command_error(ctx, error): async def on_application_command_error(ctx, error):
embed = build_error_embed(f"Ein Fehler ist aufgetreten: {error}") embed = build_error_embed(f"Ein Fehler ist aufgetreten: {error}")
await ctx.respond(embed=embed) await ctx.respond(embed=embed)
'''
try: try:
bot.run(config['token']) bot.run(config['token'])
except: except:
+4 -2
View File
@@ -2,6 +2,7 @@ import random
import discord import discord
from datetime import datetime, timedelta from datetime import datetime, timedelta
import src.handlers as handlers import src.handlers as handlers
from src.config import config
from src.utils import operator_metadata, format_via_list, resolve_operator, logger from src.utils import operator_metadata, format_via_list, resolve_operator, logger
def format_timestamp(timestr): def format_timestamp(timestr):
@@ -62,14 +63,15 @@ 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") slogans = operator_infos.get("slogan")
footer_notice = f"Daten großzügig bereitgestellt von dbf.finalrewind.org • Typ: {handlers.train_type}" dbf = config.get("dbf", "https://dbf.finalrewind.org")
footer_notice = f"Daten großzügig bereitgestellt von {dbf} • Typ: {handlers.train_type}"
footer_text = ( footer_text = (
f"{random.choice(slogans)}{footer_notice}" f"{random.choice(slogans)}{footer_notice}"
if slogans else if slogans else
footer_notice footer_notice
) )
embed.set_footer(text=footer_text, icon_url="https://dbf.finalrewind.org/static/icons/icon-96x96.png") embed.set_footer(text=footer_text, icon_url=f"{dbf}/static/icons/icon-96x96.png")
return embed return embed
def build_error_embed(errormsg) -> discord.Embed: def build_error_embed(errormsg) -> discord.Embed:
+7 -3
View File
@@ -8,6 +8,7 @@ import src.data.operators as operators_module
import src.data.emojis as emojis import src.data.emojis as emojis
_operators_mtime = None _operators_mtime = None
dbf = config.get("dbf", "https://dbf.finalrewind.org")
def _reload_operators_if_changed(): def _reload_operators_if_changed():
global _operators_mtime global _operators_mtime
@@ -28,10 +29,10 @@ def random_connection():
available_stations = config["stations"].copy() available_stations = config["stations"].copy()
while True: while True:
if not available_stations: 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) station = random.choice(available_stations)
url = f"https://dbf.finalrewind.org/{station}.json" url = f"{dbf}/{station}.json"
blacklist = config.get("blacklist", []) blacklist = config.get("blacklist", [])
try: try:
@@ -72,7 +73,7 @@ def random_connection():
} }
def get_train_info(station, train_ID, train_type): 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}") logger(f"Fetche {url}")
try: try:
response = requests.get(url) response = requests.get(url)
@@ -131,10 +132,13 @@ def operator_metadata(operator):
def get_channel_formatting(train_type): def get_channel_formatting(train_type):
formatting = config.get("formatting", "") formatting = config.get("formatting", "")
train_emoji = ""
if config.get("emojis", True):
train_emoji = emojis.train_types.get(train_type) train_emoji = emojis.train_types.get(train_type)
if train_emoji is None: if train_emoji is None:
train_emoji = emojis.emoji_list.get("Fallback", "") train_emoji = emojis.emoji_list.get("Fallback", "")
return f"{train_emoji}{formatting}" return f"{train_emoji}{formatting}"
def logger(msg, log_type="info"): def logger(msg, log_type="info"):