mirror of
https://github.com/kaaninchen/Gleiswechsel.git
synced 2026-09-17 16:52:47 +00:00
Compare commits
9
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e62355db9 | ||
|
|
e6013a437f | ||
|
|
d46a04af0c | ||
|
|
6006a561b2 | ||
|
|
881d2e865b | ||
|
|
66ec499c27 | ||
|
|
7a3902b7d3 | ||
|
|
bd506ff9da | ||
|
|
59b2ccd64a |
Binary file not shown.
|
Before Width: | Height: | Size: 73 KiB |
@@ -36,55 +36,9 @@ Next, you should configure the bot to your liking
|
||||
## Config
|
||||
Even if it seems a bit tedious, I highly recommend going through the entire config and checking if there's something that you would like to customize.
|
||||
|
||||
I added an [example config](config.json.example):
|
||||
```json
|
||||
{
|
||||
"discord": {
|
||||
"token": "",
|
||||
"server": ,
|
||||
"vc": ,
|
||||
"lang": "de",
|
||||
"formatting": "┇",
|
||||
"emojis": true
|
||||
},
|
||||
"connections": {
|
||||
"stations": [
|
||||
"Berlin Hauptbahnhof",
|
||||
"Amsterdam",
|
||||
"Helsinki"
|
||||
],
|
||||
"IDs": [],
|
||||
"blacklist": [
|
||||
"OTHER",
|
||||
"RIDE_SHARING"
|
||||
],
|
||||
"min_duration": 10,
|
||||
"max_duration": null,
|
||||
"max_wait_time": null,
|
||||
"timezone": "Europe/Berlin"
|
||||
},
|
||||
"announcements": {
|
||||
"enabled": true,
|
||||
"voice": [
|
||||
{
|
||||
"enabled": false,
|
||||
"end_stations": {
|
||||
"general": "general.aac",
|
||||
},
|
||||
"stops": {
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"http": {
|
||||
"user_agent": "Gleiswechsel-Discord-Bot"
|
||||
}
|
||||
}
|
||||
```
|
||||
I added an [example config](config.json.example). Rename `config.json.example` to `config.json` and fill it out.
|
||||
|
||||
You have to rename `config.json.example` to `config.json` and fill out the essential fields so that the bot is usable.
|
||||
|
||||
### Explanations
|
||||
### Config explanations
|
||||
|
||||
#### discord
|
||||
|
||||
@@ -132,9 +86,7 @@ $ python run main.py stations
|
||||
```
|
||||
|
||||
Example output:
|
||||
```sh
|
||||
22:47:56: INFO: Exact match found! London is an assigned station! Bot would use that station directly
|
||||
22:48:33: INFO: {
|
||||
```json
|
||||
"stations": {
|
||||
"Amsterdam": [
|
||||
"Amsterdam Zuid",
|
||||
@@ -157,8 +109,7 @@ Example output:
|
||||
"S Buch (Berlin)",
|
||||
"U Hönow (Berlin)"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The tool will also ask if it should save a .json file with more informations for every similar station. If you're unsure about what which station is, then it can be really helpful! It would give you data like which types of transports arrive at every similar station, in which country they are and also their coordinates.
|
||||
@@ -190,6 +141,8 @@ I highly recommend keeping `"OTHER"` blacklisted, if the API doesn't know what t
|
||||
- `timezone` is timezone in the IANA timezone format. You can [look it up here](https://www.addevent.com/c/documentation/tools/time-zone-lookup)
|
||||
|
||||
##### announcements
|
||||
|
||||
##### `text_announcements:`
|
||||
The bot can send a text announcement in the voice chat at the start/end of a trip.
|
||||
At the end of a trip, it would send this embed:
|
||||
|
||||
@@ -199,37 +152,10 @@ It will also send the `/info` embed at the start of a new connection with inform
|
||||
|
||||
To reduce spam, the bot will only send announcements if someone is in the voice chat
|
||||
|
||||
##### voice
|
||||
⚠️ Requires `announcements` to be set to enabled
|
||||
|
||||
The bot can join the voice chat, play an audio file, and leave at various points of your trip. You have to have [FFmpeg](https://www.ffmpeg.org/) installed for this to work.
|
||||
|
||||
Place the audio file of your desired station in [src/data/announcements](src/data/announcements/). Then, define the stations name with the name of the audio file in either `end_stations` or `stops`. The path will be autocompleted to [src/data/announcements](src/data/announcements/). The station name has to be EXACT, if you're unsure then [get the name through the helper script](#stations)
|
||||
|
||||
`end_stations` is for audio files that should play at the end of your trip, and `stops` is for audio files that should play while the train is passing through your desired station. If you set the name of a station to `general`, then the bot will always play that file before the trip ends/a new stop has been reached.
|
||||
|
||||
I'm hoping that I didn't explain this too complicated. Here's an example to visualize this:
|
||||
|
||||

|
||||
|
||||
```json
|
||||
"announcements": {
|
||||
"enabled": true,
|
||||
"voice": [
|
||||
{
|
||||
"enabled": true,
|
||||
"end_stations": {
|
||||
"general": "general.aac",
|
||||
"Hannover Hbf": "hannover.aac"
|
||||
},
|
||||
"stops": {
|
||||
"Amsterdam, Noorderpark": "noorderpark.aac"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
```
|
||||
##### `voice_announcements:`
|
||||
The bot can join the voice chat, play an audio file, and disconnect from the voice chat, at various points of your trip. You have to have [FFmpeg](https://www.ffmpeg.org/) installed for this to work.
|
||||
|
||||
Place the audio file of your desired station in [src/data/announcements](src/data/announcements/) with the EXACT name of the station. The bot will automatically check if an audio file with the stations name exists, and if it does, play it.
|
||||
#### http
|
||||
|
||||
- `"user_agent"`: The user agent of the bot for the API. If you don't know what that is, then you shouldn't have to change that. Even if you do, you still probably don't have to
|
||||
|
||||
+2
-11
@@ -24,17 +24,8 @@
|
||||
"timezone": "Europe/Berlin"
|
||||
},
|
||||
"announcements": {
|
||||
"enabled": true,
|
||||
"voice": [
|
||||
{
|
||||
"enabled": false,
|
||||
"end_stations": {
|
||||
"general": "general.aac",
|
||||
},
|
||||
"stops": {
|
||||
}
|
||||
}
|
||||
]
|
||||
"text_announcements": true,
|
||||
"voice_announcements": false
|
||||
},
|
||||
"http": {
|
||||
"user_agent": "Gleiswechsel-Discord-Bot"
|
||||
|
||||
@@ -25,8 +25,6 @@ async def on_ready():
|
||||
logger(f"{bot.user} is online")
|
||||
_bot_initialized = True
|
||||
|
||||
await bot.change_presence(activity=discord.Game(name="tschu tschu! • /info"))
|
||||
|
||||
server_id = config.discord.server
|
||||
server_vc_id = config.discord.vc
|
||||
channel = validate_channel(bot=bot, server_id=server_id, channel_id=server_vc_id)
|
||||
|
||||
@@ -21,10 +21,9 @@ headers = {
|
||||
endpoint = "https://api.transitous.org"
|
||||
|
||||
def check_stations():
|
||||
logger(f"Testing {len(station_names)} stations...")
|
||||
all_stations_output = {}
|
||||
minimal_overview = {
|
||||
"stations": {}
|
||||
}
|
||||
minimal_overview = {}
|
||||
|
||||
for station in station_names:
|
||||
req = f"{endpoint}/api/v1/geocode"
|
||||
@@ -54,8 +53,8 @@ def check_stations():
|
||||
"tz": entry.get("tz"),
|
||||
"country": entry.get("country"),
|
||||
"coords": coords,
|
||||
"modes": modes,
|
||||
"id": station_id,
|
||||
"modes": modes,
|
||||
}
|
||||
|
||||
if stop_name:
|
||||
@@ -64,7 +63,6 @@ def check_stations():
|
||||
|
||||
if stop_name == station:
|
||||
if not exact_match:
|
||||
logger(f"Exact match found! {station} is an assigned station! Bot would use that station directly")
|
||||
exact_match = True
|
||||
|
||||
if not stops_dict:
|
||||
@@ -72,10 +70,14 @@ def check_stations():
|
||||
continue
|
||||
|
||||
all_stations_output[station] = {
|
||||
"exact_match": exact_match,
|
||||
"associated": stops_dict
|
||||
}
|
||||
|
||||
minimal_overview["stations"][station] = aliases_list
|
||||
minimal_overview[station] = {
|
||||
"exact_match": exact_match,
|
||||
"names": aliases_list
|
||||
}
|
||||
|
||||
logger(json.dumps(minimal_overview, indent=4, ensure_ascii=False))
|
||||
|
||||
@@ -133,7 +135,6 @@ def get_random_stop_id() -> str | None:
|
||||
|
||||
if stop_name == assigned_station:
|
||||
stop_ids.clear()
|
||||
logger(f"Exact match found! Using {stop_name}")
|
||||
stop_ids[stop_id] = stop_name
|
||||
break
|
||||
|
||||
@@ -146,7 +147,6 @@ def get_random_stop_id() -> str | None:
|
||||
logger(f"No station associated as '{assigned_station}', choosing random from similar named stations")
|
||||
logger(f"Run `python run main.py stations` to get exact station names")
|
||||
chosen_stop_id, chosen_station = random.choice(list(stop_ids.items()))
|
||||
logger(f"Assigned Station: {chosen_station}")
|
||||
return chosen_stop_id
|
||||
|
||||
def get_random_connection(stop_id: str) -> str | None:
|
||||
@@ -241,7 +241,7 @@ def get_trip_details(random_connection: dict | None) -> dict | None:
|
||||
arrival_dt = parse_iso(end_time)
|
||||
departure_dt = parse_iso(start_time)
|
||||
|
||||
train_name = get_train_name(display_name, mode)
|
||||
train_name = get_train_name(display_name, mode) # used by lang
|
||||
if train_from == from_station:
|
||||
long_name = long_name_lang.train_from()
|
||||
else:
|
||||
|
||||
+3
-12
@@ -21,16 +21,10 @@ class ConnectionsConfig:
|
||||
max_wait_time: Optional[int] = None
|
||||
max_duration: Optional[int] = None
|
||||
|
||||
@dataclass
|
||||
class VoiceAnnouncementConfig:
|
||||
enabled: bool
|
||||
end_stations: dict[str, str]
|
||||
stops: dict[str, str]
|
||||
|
||||
@dataclass
|
||||
class AnnouncementConfig:
|
||||
enabled: bool
|
||||
voice: list[VoiceAnnouncementConfig]
|
||||
text_announcements: bool
|
||||
voice_announcements: bool
|
||||
|
||||
@dataclass
|
||||
class HttpConfig:
|
||||
@@ -50,10 +44,7 @@ def _load_config() -> Config:
|
||||
return Config(
|
||||
discord=DiscordConfig(**raw["discord"]),
|
||||
connections=ConnectionsConfig(**raw["connections"]),
|
||||
announcements=AnnouncementConfig(
|
||||
enabled=raw["announcements"]["enabled"],
|
||||
voice=[VoiceAnnouncementConfig(**v) for v in raw["announcements"]["voice"]],
|
||||
),
|
||||
announcements=AnnouncementConfig(**raw["announcements"]),
|
||||
http=HttpConfig(**raw["http"]),
|
||||
)
|
||||
|
||||
|
||||
@@ -14,3 +14,5 @@ embeds:
|
||||
title: "Informationen zu ihrer Fahrt"
|
||||
"end_of_connection":
|
||||
message: "Sehr geehrte Fahrgäste,\nIn wenigen Minuten erreichen wir {destination}. Unsere Reise endet dort\n\nWir wünschen Ihnen eine angenehme Weiterreise.\n\nVielen Dank für ihr Vertrauen und auf Wiedersehen."
|
||||
rich_presence:
|
||||
arrival: "Ankunft um {arrival} • /info"
|
||||
|
||||
@@ -14,3 +14,5 @@ embeds:
|
||||
title: "Information about your trip"
|
||||
"end_of_connection":
|
||||
message: "Dear Passengers,\nwe will be arriving at {destination} in a few minutes. Our journey ends there.\n\nWe wish you a pleasant onward journey.\n\nThank you for your patronage, and goodbye."
|
||||
rich_presence:
|
||||
arrival: "Arrival by {arrival} • /info"
|
||||
|
||||
+98
-95
@@ -1,115 +1,118 @@
|
||||
OPERATORS = {
|
||||
"fallback": {
|
||||
"logo": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQCVeC5E0mNNBKyQftQaFMzxIVkbDvEnSzWv07h_c8PdA&s=10",
|
||||
"color": 0xFFFFFF
|
||||
"Abellio Rail Mitteldeutschland GmbH": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Abellio_logo.svg/1920px-Abellio_logo.svg.png",
|
||||
"color": 0xD7002E
|
||||
},
|
||||
"Agilis": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a3/Agilis_Logo.svg/960px-Agilis_Logo.svg.png",
|
||||
"color": 0xCAE15B
|
||||
},
|
||||
"Arverio Bayern": {
|
||||
"logo": "https://cdn.discordapp.com/attachments/1383843132906537023/1528006572805062776/Arverio_Avi_Bayern_blau_RGB.png?ex=6a5cba83&is=6a5b6903&hm=5781ae6c372c92ab4f52409c6fc91e5014ac34ccf24db665ade052e8135bdde0&animated=true",
|
||||
"color": 0x0083BE
|
||||
},
|
||||
"Berliner Verkehrsbetriebe": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/bf/BVG_Logo_07.2021.svg/960px-BVG_Logo_07.2021.svg.png",
|
||||
"color": 0xEFD13C
|
||||
},
|
||||
"DSB": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/87/DSB_company_logo.svg/960px-DSB_company_logo.svg.png",
|
||||
"color": 0xB22B32
|
||||
},
|
||||
"enno": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/a5/Enno_logo.svg/960px-Enno_logo.svg.png",
|
||||
"color": 0x88216F
|
||||
},
|
||||
"Erfurter Bahn GmbH": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/ac/Erfurter_Bahn_logo.svg/960px-Erfurter_Bahn_logo.svg.png",
|
||||
"color": 0x009133
|
||||
},
|
||||
"eurobahn": {
|
||||
"logo": "https://cdn.discordapp.com/attachments/1383843132906537023/1528405390260179146/Eurpnajm.png?ex=6a5e2df1&is=6a5cdc71&hm=d73fc3458f59f8942a5a0f51311308b12567b35b55b64aab0726bf6be92b3b0e&animated=true",
|
||||
"color": 0x005a9b
|
||||
},
|
||||
"European Sleeper": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/European_Sleeper_Logo.svg/960px-European_Sleeper_Logo.svg.png",
|
||||
"color": 0xEB4A27
|
||||
},
|
||||
"Flixbus": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9e/Flixbus_201x_logo.svg/1280px-Flixbus_201x_logo.svg.png",
|
||||
"color": 0x8CD541
|
||||
},
|
||||
"GoVolta": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5b/GoVolta_logo.svg/1280px-GoVolta_logo.svg.png",
|
||||
"color": 0x0B70F6
|
||||
},
|
||||
"GVB": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/BSicon_LOGO_GVB.svg/960px-BSicon_LOGO_GVB.svg.png",
|
||||
"color": 0x2B62AF
|
||||
},
|
||||
"Nordbahn Eisenbahngesellschaft": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/b/b5/Logo_Nordbahn_NAH.SH_Blau_positiv_final.png",
|
||||
"color": 0x1A2848
|
||||
},
|
||||
"NS": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Logo_NS.svg/960px-Logo_NS.svg.png",
|
||||
"color": 0x00337F,
|
||||
"slogan": ["Goed op weg", "Welkom in de trein van morgen", "Veilig, Vlug, Voordelig", "we haben een serious probleem", "Neuken in de keuken"]
|
||||
},
|
||||
"Ostdeutsche Eisenbahn GmbH": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f3/ODEG-Logo_Neu.svg/960px-ODEG-Logo_Neu.svg.png",
|
||||
"color": 0x00745C
|
||||
},
|
||||
"Regionalverkehre Start Deutschland GmbH (Start Mitteldeutschland)": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/Logo_der_Regionalverkehre_Start_Deutschland.svg/960px-Logo_der_Regionalverkehre_Start_Deutschland.svg.png",
|
||||
"color": 0x61A731
|
||||
},
|
||||
"S-Bahn Hannover (Transdev)": {
|
||||
"logo": "https://cdn.discordapp.com/attachments/1526274436523888732/1528470315234234520/sbahn.png?ex=6a5e6a68&is=6a5d18e8&hm=c9f630c6f0c646eafeccef9b4d591ce8fcf8856b2b45f14e72fbf25e32edb017&animated=true",
|
||||
"color": 0x1A4389
|
||||
},
|
||||
"SBB": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/23/SBB_logo_simplified.svg/960px-SBB_logo_simplified.svg.png",
|
||||
"color": 0xEB0000
|
||||
},
|
||||
"SNCF": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/en/thumb/f/f4/Sncf-logo.svg/960px-Sncf-logo.svg.png",
|
||||
"color": 0x812B6D
|
||||
},
|
||||
"Tallink Grupp AS": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9e/Tallink_logo.svg/330px-Tallink_logo.svg.png",
|
||||
"color": 0x225197
|
||||
},
|
||||
"Vr": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/18/Vr_Logo.png/330px-Vr_Logo.png",
|
||||
"color": 0x00B451
|
||||
},
|
||||
"Westbahn Management GmbH": {
|
||||
"logo": "https://corporate.westbahn.at/uploads/Logos/westbahn2025-logo-signet-small.png",
|
||||
"color": 0x1D4A83
|
||||
},
|
||||
"WestfalenBahn": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/2e/Westfalenbahn_logo.svg/330px-Westfalenbahn_logo.svg.png",
|
||||
"color": 0x48A1DD
|
||||
},
|
||||
"db_allgemein": {
|
||||
"logo": "https://marketingportal.extranet.deutschebahn.com/resource/blob/13602522/c53f806b9df966e144010b276af72dd2/Bild_09-data.png",
|
||||
"color": 0xEC0016,
|
||||
"slogan": ["Senk ju vor träwelling wis Deutsche Bahn.", "Bitte beachten Sie die umgekehrte Wagenreihung.", "Zurückbleiben bitte!", "Alle reden vom Wetter. Wir nicht.", "Die Bahn macht mobil.", "Grün abgefahren"]
|
||||
},
|
||||
"db_bayern": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5b/Bahnland_Bayern_Logo_2021.svg/500px-Bahnland_Bayern_Logo_2021.svg.png",
|
||||
"color": 0x0095DB
|
||||
},
|
||||
"db_bawü": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4f/Bwegt_Logo.svg/960px-Bwegt_Logo.svg.png",
|
||||
"color": 0xFFBF34,
|
||||
"slogan": ["Nett hier", "Aber waren Sie schon mal in Baden-Württemberg?", "Bwegt euch!"]
|
||||
},
|
||||
"Ostdeutsche Eisenbahn GmbH": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f3/ODEG-Logo_Neu.svg/960px-ODEG-Logo_Neu.svg.png",
|
||||
"color": 0x00745C
|
||||
"db_bayern": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5b/Bahnland_Bayern_Logo_2021.svg/500px-Bahnland_Bayern_Logo_2021.svg.png",
|
||||
"color": 0x0095DB
|
||||
},
|
||||
"NS": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/3/3d/Logo_NS.svg/960px-Logo_NS.svg.png",
|
||||
"color": 0X00337F,
|
||||
"slogan": ["Goed op weg", "Welkom in de trein van morgen", "Veilig, Vlug, Voordelig", "we haben een serious probleem", "Neuken in de keuken"]
|
||||
},
|
||||
"eurobahn": {
|
||||
"logo": "https://cdn.discordapp.com/attachments/1383843132906537023/1528405390260179146/Eurpnajm.png?ex=6a5e2df1&is=6a5cdc71&hm=d73fc3458f59f8942a5a0f51311308b12567b35b55b64aab0726bf6be92b3b0e&animated=true",
|
||||
"color": 0x005a9b,
|
||||
},
|
||||
"Arverio Bayern": {
|
||||
"logo": "https://cdn.discordapp.com/attachments/1383843132906537023/1528006572805062776/Arverio_Avi_Bayern_blau_RGB.png?ex=6a5cba83&is=6a5b6903&hm=5781ae6c372c92ab4f52409c6fc91e5014ac34ccf24db665ade052e8135bdde0&animated=true", # alternative nötig!
|
||||
"color": 0x0083BE
|
||||
},
|
||||
"Abellio Rail Mitteldeutschland GmbH": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/Abellio_logo.svg/1920px-Abellio_logo.svg.png",
|
||||
"color": 0xD7002E
|
||||
},
|
||||
"SBB": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/2/23/SBB_logo_simplified.svg/960px-SBB_logo_simplified.svg.png",
|
||||
"color": 0xEB0000
|
||||
},
|
||||
"Nordbahn Eisenbahngesellschaft": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/b/b5/Logo_Nordbahn_NAH.SH_Blau_positiv_final.png",
|
||||
"color": 0x1A2848
|
||||
},
|
||||
"Erfurter Bahn GmbH": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/a/ac/Erfurter_Bahn_logo.svg/960px-Erfurter_Bahn_logo.svg.png",
|
||||
"color": 0x009133
|
||||
},
|
||||
"S-Bahn Hannover (Transdev)": {
|
||||
"logo": "https://cdn.discordapp.com/attachments/1526274436523888732/1528470315234234520/sbahn.png?ex=6a5e6a68&is=6a5d18e8&hm=c9f630c6f0c646eafeccef9b4d591ce8fcf8856b2b45f14e72fbf25e32edb017&animated=true",
|
||||
"color": 0x1A4389
|
||||
},
|
||||
"Regionalverkehre Start Deutschland GmbH (Start Mitteldeutschland)": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/Logo_der_Regionalverkehre_Start_Deutschland.svg/960px-Logo_der_Regionalverkehre_Start_Deutschland.svg.png",
|
||||
"color": 0x61A731
|
||||
},
|
||||
"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
|
||||
},
|
||||
"DSB": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/8/87/DSB_company_logo.svg/960px-DSB_company_logo.svg.png",
|
||||
"color": 0xB22B32
|
||||
},
|
||||
"Vr": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/1/18/Vr_Logo.png/330px-Vr_Logo.png",
|
||||
"color": 0x00B451
|
||||
},
|
||||
"GVB": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/BSicon_LOGO_GVB.svg/960px-BSicon_LOGO_GVB.svg.png",
|
||||
"color": 0x2B62AF
|
||||
},
|
||||
"Berliner Verkehrsbetriebe": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/b/bf/BVG_Logo_07.2021.svg/960px-BVG_Logo_07.2021.svg.png",
|
||||
"color": 0xEFD13C
|
||||
},
|
||||
"Flixbus": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9e/Flixbus_201x_logo.svg/1280px-Flixbus_201x_logo.svg.png",
|
||||
"color": 0x8CD541
|
||||
},
|
||||
"Tallink Grupp AS": {
|
||||
"logo": "https://upload.wikimedia.org/wikipedia/commons/thumb/9/9e/Tallink_logo.svg/330px-Tallink_logo.svg.png",
|
||||
"color": 0x225197
|
||||
"fallback": {
|
||||
"logo": "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQCVeC5E0mNNBKyQftQaFMzxIVkbDvEnSzWv07h_c8PdA&s=10",
|
||||
"color": 0xFFFFFF
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
OPERATOR_ALIASES = {
|
||||
"Arverio Bayern GmbH": OPERATORS["Arverio Bayern"],
|
||||
"DB Regio AG Baden-Württemberg": OPERATORS["db_bawü"],
|
||||
|
||||
+13
-17
@@ -39,30 +39,24 @@ async def rename_vc(bot: discord.Bot, voice_channel, from_scheduler: bool = Fals
|
||||
formatting = channel_formatting(mode)
|
||||
await voice_channel.edit(name=f"{formatting}{long_name}")
|
||||
await voice_channel.set_status(None)
|
||||
start_next_stop_updates(bot, voice_channel)
|
||||
|
||||
start_next_stop_updates(voice_channel)
|
||||
|
||||
logger(f"Updated channel name!")
|
||||
|
||||
await bot.change_presence(activity=discord.Game(name=lang.rich_presence.arrival()))
|
||||
await announcer("transfer", voice_channel)
|
||||
|
||||
|
||||
_scheduled_task = asyncio.create_task(_schedule_next_transfer(bot, trip["arrival_dt"], voice_channel, trip["to"]))
|
||||
|
||||
async def announcer(announcement: str, voice_channel: discord.VoiceChannel, destination = None):
|
||||
from src.dc.embeds import build_info_embed, build_announcement_embed
|
||||
announcements_enabled = config.announcements.enabled
|
||||
voice_announcement_enabled = config.announcements.voice[0].enabled
|
||||
announcements_enabled = config.announcements.text_announcements
|
||||
|
||||
if announcements_enabled:
|
||||
if len(voice_channel.members) > 0:
|
||||
match announcement:
|
||||
case "end_of_connection":
|
||||
if voice_announcement_enabled:
|
||||
announcement_status = await voice_announcer(destination, voice_channel, "end_stations")
|
||||
if announcement_status:
|
||||
return
|
||||
embed = build_announcement_embed(lang.embeds.announcement.end_of_connection.message())
|
||||
embed = build_announcement_embed(lang.embeds.announcement.end_of_connection.message())
|
||||
case "transfer":
|
||||
embed = build_info_embed()
|
||||
case _:
|
||||
@@ -72,8 +66,8 @@ async def announcer(announcement: str, voice_channel: discord.VoiceChannel, dest
|
||||
if embed:
|
||||
await voice_channel.send(embed=embed)
|
||||
|
||||
async def voice_announcer(destination: str, voice_channel: discord.VoiceChannel, type_announcement: str) -> bool:
|
||||
sound_path = get_sound_path(destination=destination, type_announcement=type_announcement)
|
||||
async def voice_announcer(station: str, voice_channel: discord.VoiceChannel) -> bool:
|
||||
sound_path = get_sound_path(station=station)
|
||||
if sound_path is None:
|
||||
return False
|
||||
|
||||
@@ -108,7 +102,7 @@ async def _schedule_next_transfer(bot: discord.Bot, arrival_dt: datetime, voice_
|
||||
|
||||
if wait_seconds > 0:
|
||||
remaining = str(timedelta(seconds=wait_seconds))
|
||||
logger(f"Next transfer in {remaining.split('.')[0]} ({arrival_dt.strftime('%H:%M')} Uhr)")
|
||||
logger(f"Next transfer in {remaining.split('.')[0]} ({arrival_dt.strftime('%H:%M')})")
|
||||
|
||||
if wait_seconds > announcement_countdown:
|
||||
wait_until_end_announcement = wait_seconds - announcement_countdown
|
||||
@@ -121,7 +115,7 @@ async def _schedule_next_transfer(bot: discord.Bot, arrival_dt: datetime, voice_
|
||||
logger("Train arrived, searching for a new connection....")
|
||||
await rename_vc(bot, voice_channel, from_scheduler=True)
|
||||
|
||||
async def _update_next_loop(bot: discord.Bot, voice_channel: discord.VoiceChannel):
|
||||
async def _update_next_loop(voice_channel: discord.VoiceChannel):
|
||||
global trip
|
||||
try:
|
||||
if trip is None:
|
||||
@@ -145,7 +139,9 @@ async def _update_next_loop(bot: discord.Bot, voice_channel: discord.VoiceChanne
|
||||
status_text = f"{lang.embeds.info.next_stop()}: {next_stop_str}"
|
||||
await voice_channel.set_status(status_text, reason="Next stop status")
|
||||
|
||||
await voice_announcer(next_stop_str, voice_channel, type_announcement="stops")
|
||||
if config.announcements.voice_announcements:
|
||||
if len(voice_channel.members) > 0:
|
||||
await voice_announcer(next_stop_str, voice_channel)
|
||||
|
||||
wait_seconds = (next_stop["arrival"] - datetime.now(LOCAL_TZ)).total_seconds()
|
||||
if wait_seconds > 0:
|
||||
@@ -154,10 +150,10 @@ async def _update_next_loop(bot: discord.Bot, voice_channel: discord.VoiceChanne
|
||||
except asyncio.CancelledError:
|
||||
raise
|
||||
|
||||
def start_next_stop_updates(bot: discord.bot, voice_channel: discord.VoiceChannel):
|
||||
def start_next_stop_updates(voice_channel: discord.VoiceChannel):
|
||||
global _next_stop_task
|
||||
|
||||
if _next_stop_task is not None and not _next_stop_task.done():
|
||||
_next_stop_task.cancel()
|
||||
|
||||
_next_stop_task = asyncio.create_task(_update_next_loop(bot, voice_channel))
|
||||
_next_stop_task = asyncio.create_task(_update_next_loop(voice_channel))
|
||||
@@ -1,6 +1,6 @@
|
||||
# generated by datamodel-codegen:
|
||||
# filename: de.yaml
|
||||
# timestamp: 2026-08-17T18:44:37+00:00
|
||||
# filename: en.yaml
|
||||
# timestamp: 2026-08-18T20:08:48+00:00
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -49,7 +49,13 @@ class Embeds:
|
||||
announcement: Announcement
|
||||
|
||||
|
||||
@dataclass
|
||||
class RichPresence:
|
||||
arrival: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class Model:
|
||||
channel: Channel
|
||||
embeds: Embeds
|
||||
rich_presence: RichPresence
|
||||
|
||||
+12
-29
@@ -39,7 +39,7 @@ def validate_connection(start_time: str, end_time: str, departure_time_iso: str)
|
||||
max_wait_time = config.connections.max_wait_time
|
||||
if max_wait_time:
|
||||
if start_dt > now + timedelta(hours=max_wait_time):
|
||||
logger(f"Connection is way too far in the future: {start_dt} (max_wait_time: {max_wait_time}h)", "error")
|
||||
logger(f"Connection is way too far in the future: {start_dt} (max_wait_time: {max_wait_time}h), retrying...", "error")
|
||||
return False
|
||||
|
||||
departure_time_iso_dt = datetime.fromisoformat(departure_time_iso.replace("Z", "+00:00"))
|
||||
@@ -49,14 +49,14 @@ def validate_connection(start_time: str, end_time: str, departure_time_iso: str)
|
||||
min_duration = config.connections.min_duration
|
||||
min_duration_seconds = min_duration * 60
|
||||
if trip_duration < min_duration_seconds:
|
||||
logger(f"Connection is with {trip_duration_minutes} minutes too short (configured to {min_duration} minutes or more)", "error")
|
||||
logger(f"Connection is with {trip_duration_minutes} minutes too short (configured to {min_duration} minutes or more), retrying...", "error")
|
||||
return False
|
||||
|
||||
max_duration = config.connections.max_duration
|
||||
if max_duration:
|
||||
max_duration_seconds = max_duration * 60
|
||||
if max_duration_seconds < trip_duration:
|
||||
logger(f"Connection is with {trip_duration_minutes} too long (configured to {max_duration} minutes at most)", "error")
|
||||
logger(f"Connection is with {trip_duration_minutes} too long (configured to {max_duration} minutes at most), retrying...", "error")
|
||||
return False
|
||||
|
||||
return True
|
||||
@@ -123,31 +123,14 @@ def get_operator_metadata(agency: str, route_color: str, mode: str) -> dict:
|
||||
"slogans": slogans
|
||||
}
|
||||
|
||||
def get_sound_path(destination, type_announcement: str) -> str | None:
|
||||
if type_announcement == "end_stations":
|
||||
voice_stations = config.announcements.voice[0].end_stations
|
||||
elif type_announcement == "stops":
|
||||
voice_stations = config.announcements.voice[0].stops
|
||||
def get_sound_path(station: str) -> str | None:
|
||||
announcement_dir = Path("src/data/announcements")
|
||||
|
||||
if destination in voice_stations:
|
||||
announcement_for = destination
|
||||
else:
|
||||
general_sound_enabled = voice_stations.get("general", "")
|
||||
if not general_sound_enabled:
|
||||
return None
|
||||
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"Couldn't find {sound_path}", "error")
|
||||
return None
|
||||
|
||||
return sound_path
|
||||
for file in announcement_dir.iterdir():
|
||||
if file.is_file():
|
||||
if station.lower() in file.stem.lower():
|
||||
sound_file = file.resolve()
|
||||
return sound_file
|
||||
|
||||
def get_next_station(stops: dict, train_from: str) -> dict | None:
|
||||
now = datetime.now(LOCAL_TZ)
|
||||
@@ -201,10 +184,10 @@ def format_stop_list(stops: dict, next_stop: str | None) -> list[tuple[str, str]
|
||||
for name, info in stops.items():
|
||||
if name == next_stop:
|
||||
stop_arrival = info["arrival"]
|
||||
line = f"• __{name}__ ({stop_arrival.strftime("%H:%M")} Uhr)"
|
||||
line = f"• __{name}__ ({stop_arrival.strftime("%H:%M")})"
|
||||
else:
|
||||
stop_arrival = info["arrival"]
|
||||
line = f"• {name} ({stop_arrival.strftime("%H:%M")} Uhr)"
|
||||
line = f"• {name} ({stop_arrival.strftime("%H:%M")})"
|
||||
|
||||
if field_length + len(line) + 1 > 1024:
|
||||
route_page_name = "Route" if part == 1 else "Route (Fortsetzung)"
|
||||
|
||||
Reference in New Issue
Block a user