mirror of
https://github.com/kaaninchen/Gleiswechsel.git
synced 2026-09-17 16:52:47 +00:00
Compare commits
13
Commits
0a5dcc26c5
..
dbf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
199cedb8b1 | ||
|
|
172d6d5cf1 | ||
|
|
72b4d3185a | ||
|
|
e2e7bf2a14 | ||
|
|
12f65eabc2 | ||
|
|
934ab0479d | ||
|
|
a011308c2f | ||
|
|
453520c8d2 | ||
|
|
e09ad38816 | ||
|
|
8dcac27338 | ||
|
|
c5eac4fcce | ||
|
|
bd7e031972 | ||
|
|
82864167bc |
Binary file not shown.
|
After Width: | Height: | Size: 53 KiB |
@@ -1,3 +1,5 @@
|
|||||||
venv/
|
venv/
|
||||||
__pycache__/
|
__pycache__/
|
||||||
|
src/data/announcements/*
|
||||||
|
!src/data/announcements/.gitkeep
|
||||||
config.json
|
config.json
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
# Gleiswechsel
|
# 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.
|
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.
|
||||||
|
|
||||||

|

|
||||||
@@ -35,8 +39,16 @@ uv pip install -r requirements.txt
|
|||||||
"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.
|
||||||
|
"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
|
"blacklist": [] // Blacklist für bestimmte Zug-Typen
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +70,50 @@ Wenn man nur einen Bahnhof hat ist es stark empfohlen random zu nutzen. Sonst k
|
|||||||
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:
|
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
|
- 3-5 Min vor Umstieg gibt es eine Ankündigung dafür
|
||||||
- Info Embed bei Umstieg
|
- 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:
|
#### 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:
|
||||||
@@ -100,7 +156,7 @@ uv run main.py
|
|||||||
|
|
||||||
## Bekannte Bugs
|
## Bekannte Bugs
|
||||||
#### Stuttgart in Berlin
|
#### 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.
|
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`).
|
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.
|
Falls das einem zu sehr stört kann man S-Bahns auf die Blacklist packen.
|
||||||
|
|
||||||
@@ -113,4 +169,11 @@ Falls das einem zu sehr stört kann man S-Bahns auf die Blacklist packen.
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
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
|
#### 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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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.
|
||||||
+9
-1
@@ -6,7 +6,15 @@
|
|||||||
"vc": ,
|
"vc": ,
|
||||||
"random": true,
|
"random": true,
|
||||||
"emojis": true,
|
"emojis": true,
|
||||||
"announcements": true,
|
|
||||||
"formatting": "┇",
|
"formatting": "┇",
|
||||||
|
"announcements": true,
|
||||||
|
"voice_announcements: [
|
||||||
|
{
|
||||||
|
"enabled": false,
|
||||||
|
"stations": {
|
||||||
|
"general": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
"blacklist": []
|
"blacklist": []
|
||||||
}
|
}
|
||||||
@@ -16,12 +16,18 @@ async def change_status():
|
|||||||
status = random.choice(discord_status)
|
status = random.choice(discord_status)
|
||||||
await bot.change_presence(activity=discord.Game(name=f"{status} • /info"))
|
await bot.change_presence(activity=discord.Game(name=f"{status} • /info"))
|
||||||
|
|
||||||
|
_bot_initialized = False
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_ready():
|
async def on_ready():
|
||||||
|
global _bot_initialized
|
||||||
print(f"{bot.user} ist online")
|
print(f"{bot.user} ist online")
|
||||||
if not change_status.is_running():
|
if not change_status.is_running():
|
||||||
change_status.start()
|
change_status.start()
|
||||||
|
if not _bot_initialized:
|
||||||
|
_bot_initialized = True
|
||||||
await rename_vc(bot)
|
await rename_vc(bot)
|
||||||
|
else:
|
||||||
|
logger("Discord Reconnect, laufende Fahrt bleibt unangetastet")
|
||||||
|
|
||||||
@bot.event
|
@bot.event
|
||||||
async def on_application_command_error(ctx, error):
|
async def on_application_command_error(ctx, error):
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ aiohttp==3.14.1
|
|||||||
aiosignal==1.4.0
|
aiosignal==1.4.0
|
||||||
attrs==26.1.0
|
attrs==26.1.0
|
||||||
certifi==2026.6.17
|
certifi==2026.6.17
|
||||||
|
cffi==2.1.1
|
||||||
charset-normalizer==3.4.9
|
charset-normalizer==3.4.9
|
||||||
|
davey==0.1.6
|
||||||
frozenlist==1.8.0
|
frozenlist==1.8.0
|
||||||
idna==3.18
|
idna==3.18
|
||||||
multidict==6.7.1
|
multidict==6.7.1
|
||||||
@@ -11,6 +13,8 @@ numpy==2.5.1
|
|||||||
pandas==3.0.3
|
pandas==3.0.3
|
||||||
propcache==0.5.2
|
propcache==0.5.2
|
||||||
py-cord==2.8.0
|
py-cord==2.8.0
|
||||||
|
pycparser==3.0
|
||||||
|
pynacl==1.6.2
|
||||||
python-dateutil==2.9.0.post0
|
python-dateutil==2.9.0.post0
|
||||||
requests==2.34.2
|
requests==2.34.2
|
||||||
six==1.17.0
|
six==1.17.0
|
||||||
|
|||||||
+5
-2
@@ -1,10 +1,11 @@
|
|||||||
emoji_list = {
|
emoji_list = {
|
||||||
"Fallback": "🚉",
|
"Fallback": "🚉",
|
||||||
"Nahverkehr": "🚃",
|
"Nahverkehr": "🚃",
|
||||||
"Höchstgeschwindigkeit": "🚄"
|
"Höchstgeschwindigkeit": "🚄",
|
||||||
|
"Bus": "🚎"
|
||||||
}
|
}
|
||||||
|
|
||||||
train_types = { # Unvollständig! Benötigt testing
|
train_types = { # Unvollständig!
|
||||||
"ICE": emoji_list["Höchstgeschwindigkeit"],
|
"ICE": emoji_list["Höchstgeschwindigkeit"],
|
||||||
"TGV": emoji_list["Höchstgeschwindigkeit"],
|
"TGV": emoji_list["Höchstgeschwindigkeit"],
|
||||||
|
|
||||||
@@ -12,4 +13,6 @@ train_types = { # Unvollständig! Benötigt testing
|
|||||||
"RB": emoji_list["Nahverkehr"],
|
"RB": emoji_list["Nahverkehr"],
|
||||||
"ARV": emoji_list["Nahverkehr"],
|
"ARV": emoji_list["Nahverkehr"],
|
||||||
"BRB": emoji_list["Nahverkehr"],
|
"BRB": emoji_list["Nahverkehr"],
|
||||||
|
|
||||||
|
"Bus": emoji_list["Bus"]
|
||||||
}
|
}
|
||||||
@@ -82,6 +82,22 @@ OPERATORS = {
|
|||||||
"European Sleeper": {
|
"European Sleeper": {
|
||||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/European_Sleeper_Logo.svg/960px-European_Sleeper_Logo.svg.png",
|
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/European_Sleeper_Logo.svg/960px-European_Sleeper_Logo.svg.png",
|
||||||
"color": 0xEB4A27
|
"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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+46
-4
@@ -2,6 +2,8 @@ import discord
|
|||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
import asyncio
|
import asyncio
|
||||||
import random
|
import random
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
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
|
from src.embeds import build_announcement_embed, build_info_embed
|
||||||
@@ -14,8 +16,11 @@ async def announcer(bot, announcement):
|
|||||||
if len(voice_channel.members) > 0:
|
if len(voice_channel.members) > 0:
|
||||||
match announcement:
|
match announcement:
|
||||||
case "ende":
|
case "ende":
|
||||||
|
destination = current['destination']
|
||||||
embed = build_announcement_embed(
|
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.')
|
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":
|
case "umstieg":
|
||||||
embed = build_info_embed()
|
embed = build_info_embed()
|
||||||
case _:
|
case _:
|
||||||
@@ -28,8 +33,41 @@ async def announcer(bot, announcement):
|
|||||||
logger(f"Announcement {announcement} wird geskipped, keiner da")
|
logger(f"Announcement {announcement} wird geskipped, keiner da")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
async def voice_announcer(bot: discord.Bot, destination, voice_channel):
|
||||||
|
voice_announcement_config = config["voice_announcements"][0]
|
||||||
|
voice_stations = voice_announcement_config["stations"]
|
||||||
|
|
||||||
async def rename_vc(bot: discord.Bot):
|
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
|
global current, train_name, train_info, train_type, _scheduled_task
|
||||||
|
|
||||||
guild = bot.get_guild(int(config["server"]))
|
guild = bot.get_guild(int(config["server"]))
|
||||||
@@ -42,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")
|
logger(f"Es konnte kein VC mit der ID {config['vc']} auf dem Server gefunden werden", "fatal")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
if not from_scheduler and _scheduled_task and not _scheduled_task.done():
|
||||||
|
_scheduled_task.cancel()
|
||||||
|
|
||||||
attempt = 0
|
attempt = 0
|
||||||
while True:
|
while True:
|
||||||
if attempt == 20:
|
if attempt == 20:
|
||||||
@@ -75,7 +116,6 @@ async def rename_vc(bot: discord.Bot):
|
|||||||
arrival = datetime.fromisoformat((train_info["arrival"]))
|
arrival = datetime.fromisoformat((train_info["arrival"]))
|
||||||
formatting = get_channel_formatting(train_type)
|
formatting = get_channel_formatting(train_type)
|
||||||
|
|
||||||
_scheduled_task = asyncio.create_task(_schedule_next_umstieg(bot, arrival))
|
|
||||||
|
|
||||||
print("-----------------------------------------")
|
print("-----------------------------------------")
|
||||||
logger(f"Umstieg: {train_name}")
|
logger(f"Umstieg: {train_name}")
|
||||||
@@ -89,6 +129,8 @@ async def rename_vc(bot: discord.Bot):
|
|||||||
if config.get("announcements", True):
|
if config.get("announcements", True):
|
||||||
await announcer(bot, "umstieg")
|
await announcer(bot, "umstieg")
|
||||||
|
|
||||||
|
_scheduled_task = asyncio.create_task(_schedule_next_umstieg(bot, arrival))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
async def _schedule_next_umstieg(bot, arrival):
|
async def _schedule_next_umstieg(bot, arrival):
|
||||||
@@ -109,4 +151,4 @@ async def _schedule_next_umstieg(bot, arrival):
|
|||||||
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, from_scheduler=True)
|
||||||
Reference in New Issue
Block a user