# voice-bridge Verbindet Gruppen in Simple Voice Chat mit Voice-Channels in TeamSpeak und Discord („Crosstalk“). Jede Seite hört die beiden anderen: Discord und TeamSpeak bekommen je einen gemischten Stream, im Spiel kommt jeder externe Sprecher einzeln an (Lautstärke-Kategorie „Discord/TeamSpeak“). | Modul | Läuft als | Inhalt | |---|---|---| | `plugin` | `plugins/voice-bridge.jar` auf Paper | legt die Gruppen an, schickt/spielt Audio, Hinweis beim Beitritt | | `bridge` | eigener Dienst (`voice-bridge.jar`) | Discord-Bots (JDA + JDave), TeamSpeak-Clients (ts3j), Mischer (Concentus) | | `protocol` | in beiden | UDP-Nachrichten Plugin ↔ Bridge (Standard: `127.0.0.1:24470`) | `third_party/ts3j` ist ein Git-Submodul (`git submodule update --init`), JitPack hat keinen aktuellen Build. ## Bauen ``` ./gradlew build ``` - Plugin: `plugin/build/libs/voice-bridge.jar` - Bridge: `bridge/build/libs/voice-bridge.jar` (Fat-Jar), starten mit `java --enable-native-access=ALL-UNNAMED -jar voice-bridge.jar config.yml` ## Verhalten - Ein Raum ist aktiv, sobald auf mindestens zwei Seiten jemand ist. - Bots verbinden sich nur, wenn der Raum aktiv ist und auf ihrer Seite jemand sitzt; danach bleiben sie `lingerSeconds` (Standard 15) verbunden. - TeamSpeak: eine ServerQuery über SSH (Port 10022) sieht alle Clients und Channels und belegt keinen Slot. Raum-Clients verbinden sich nur bei Bedarf. Fehlende Channels legt die Query unter `parentChannel` an. - Rechte: Servergruppe „Bridge“ (Kopie von „Normal“) plus `b_channel_create_permanent`, `b_channel_create_child`, `b_channel_create_modify_with_codec_opusvoice`, `b_client_ignore_antiflood`, `i_client_talk_power` 75. Darin: die Identität des Query-Logins und alle Raum-Identitäten. Kein Serveradmin nötig. - Discord: ein Bot pro Raum (ein Bot kann pro Server nur in einem Voice-Channel sein). Fehlende Channels legt der Bot in `category` an. Einladen mit `https://discord.com/oauth2/authorize?client_id=APP_ID&scope=bot&permissions=70255632` (Kanäle sehen, verbinden, sprechen, Kanäle verwalten, Nickname ändern). ## Config Vorlage: `bridge/config.example.yml`. Die echte `config.yml` enthält Bot-Tokens und gehört nicht ins Repo. `teamspeakChannel` und `discordChannel` pro Raum dürfen ein Name oder eine ID sein. Einen Namen löst die Bridge beim Start auf (oder legt den Channel an) und schreibt die ID in die `config.yml` zurück, nur diese Zeile, mit dem alten Namen als Kommentar. Die Datei muss dafür für den Dienst beschreibbar sein. ## Stolpersteine - **glibc ≥ 2.38**: Die `libdave.so` aus JDave (und Discords eigene Builds) brauchen glibc 2.38 und GLIBCXX_3.4.32. Auf Debian 13 kein Problem, auf Ubuntu 22.04 lädt sie nicht („Cannot open library … dave….so“). Lokal hilft ein Debian-13-Loader: glibc und libstdc++ aus den trixie-Paketen entpacken und `ld-linux-x86-64.so.2 --library-path :$JAVA_HOME/lib:$JAVA_HOME/lib/server $JAVA_HOME/bin/java …` starten. - **TeamSpeak 6 (mhsl.eu)**: verlangt Security-Level 15, Nicknames höchstens 30 Zeichen. Schnelle Reconnects lösen den Flood-Schutz aus (IP-Bann), deshalb Backoff beim Verbinden. Den Bot-Identitäten am besten `b_client_ignore_antiflood` geben. - **TeamSpeak-Query-Flood**: Die Query sperrt die IP schon nach wenigen schnellen Befehlen (Instanz-Bann, nicht in der Bann-Liste des Servers, läuft nach einigen Minuten ab). Die Bridge hält mindestens 1 s Abstand; sauberer ist es, ihre IP in `query_ip_allowlist.txt` des TS-Servers einzutragen. - **UIDs**: ts3j rechnet die UID einer Identität anders als der TS6-Server. Eigene Clients erkennt die Bridge deshalb an ihrer Client-ID, nicht an der UID. - **Discord**: Die ersten Pakete nach dem Beitritt schlagen beim Entschlüsseln fehl („Decrypt failed“), bis DAVE die Schlüssel ausgehandelt hat. Das ist normal. - Mit `-Dvoicebridge.ts3debug=true` loggt ts3j das TeamSpeak-Protokoll (Fehlercodes vom Server).