Radio: den Beitritt in drei Schritte zerlegen statt "signalling" zu melden
"signalling" sagt nur, dass etwas fehlt, nicht was. Ein Beitritt zum Sprachkanal ist ein Dreischritt: 1. Der Bot bittet Discord ueber die bestehende Verbindung um den Beitritt. 2. Discord antwortet mit dem eigenen Sprachstatus (darin die Sitzungs-ID). 3. Discord nennt den Sprachserver. Der Adapter zwischen Verbindung und Sprachbibliothek haelt jetzt fest, welcher davon stattgefunden hat. Aus den drei Haekchen wird ein Satz, der sagt, was zu tun ist — fehlende Berechtigung, fehlender Intent, oder ein Netz, das UDP nicht rauslaesst. Fuenf Faelle, fuenf verschiedene Antworten; der Test besteht darauf, dass keine zwei gleich lauten. Dazu eine Pruefung vorweg, die genau dasselbe Bild erzeugt haette: ein voller Sprachkanal. Discord nimmt die Anfrage entgegen und antwortet dann einfach nicht mehr — das sieht aus wie ein Rechteproblem. Wer Mitglieder verschieben darf, kommt am Limit vorbei, sonst nicht. Nachgesehen und ausgeschlossen: discord.js 14.27 verdrahtet beide Sprachereignisse (VOICE_STATE_UPDATE ueber die Action, VOICE_SERVER_UPDATE ueber den Handler), und GuildVoiceStates steht in den Intents. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+72
-7
@@ -136,6 +136,70 @@ function tonquelle(url) {
|
||||
return ff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Der Adapter zwischen Discord-Verbindung und Sprachbibliothek — mit Protokoll.
|
||||
*
|
||||
* Ein Beitritt zum Sprachkanal ist ein Dreischritt, und `signalling` heisst
|
||||
* nur „einer davon fehlt", ohne zu sagen welcher:
|
||||
*
|
||||
* 1. Der Bot bittet Discord über die bestehende Verbindung um den Beitritt.
|
||||
* 2. Discord antwortet mit dem eigenen Sprachstatus (darin die Sitzungs-ID).
|
||||
* 3. Discord nennt den Sprachserver (Adresse und Zugangswort).
|
||||
*
|
||||
* Fehlt 1, ist die Verbindung zu Discord nicht bereit. Fehlt 2, kommen die
|
||||
* Sprachstatus-Meldungen nicht an — dann fehlt der Intent oder der eigene
|
||||
* Mitgliedseintrag. Fehlt 3, hat Discord den Beitritt abgelehnt, praktisch
|
||||
* immer wegen fehlender Rechte im Kanal.
|
||||
*
|
||||
* Ohne diese drei Häkchen bleibt nur Raten, und Raten hat hier schon zweimal
|
||||
* nicht gereicht.
|
||||
*/
|
||||
function beobachteterAdapter(guild, schritte) {
|
||||
const echt = guild.voiceAdapterCreator;
|
||||
return (methods) => {
|
||||
const gepatcht = {
|
||||
...methods,
|
||||
onVoiceStateUpdate: (daten) => {
|
||||
schritte.eigenerStatus = true;
|
||||
return methods.onVoiceStateUpdate(daten);
|
||||
},
|
||||
onVoiceServerUpdate: (daten) => {
|
||||
schritte.sprachserver = true;
|
||||
return methods.onVoiceServerUpdate(daten);
|
||||
},
|
||||
};
|
||||
const adapter = echt(gepatcht);
|
||||
return {
|
||||
...adapter,
|
||||
sendPayload: (daten) => {
|
||||
const ok = adapter.sendPayload(daten);
|
||||
schritte.angefragt = ok;
|
||||
return ok;
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/** Aus den drei Häkchen wird ein Satz, der sagt, was zu tun ist */
|
||||
export function beitrittsDiagnose(schritte) {
|
||||
if (schritte.angefragt === false) {
|
||||
return 'Die Verbindung zu Discord war nicht bereit — der Beitritt ging gar nicht erst raus.';
|
||||
}
|
||||
if (!schritte.eigenerStatus && !schritte.sprachserver) {
|
||||
return 'Discord hat auf den Beitritt nicht geantwortet. Das ist fast immer eine '
|
||||
+ 'fehlende Berechtigung **Verbinden** im Sprachkanal selbst.';
|
||||
}
|
||||
if (!schritte.sprachserver) {
|
||||
return 'Discord kennt den Beitritt, nennt aber keinen Sprachserver — '
|
||||
+ 'darf der Bot in diesem Kanal sprechen?';
|
||||
}
|
||||
if (!schritte.eigenerStatus) {
|
||||
return 'Der Sprachserver steht, aber der eigene Sprachstatus kam nie an — '
|
||||
+ 'dem Bot fehlt der Intent **GuildVoiceStates**.';
|
||||
}
|
||||
return 'Der Austausch mit dem Sprachserver kam nicht zu Ende — ausgehendes UDP dicht?';
|
||||
}
|
||||
|
||||
/** Läuft gerade etwas auf diesem Server? */
|
||||
export const laeuft = (guildId) => laufend.get(guildId) ?? null;
|
||||
|
||||
@@ -161,13 +225,16 @@ export async function spielen(kanal, station) {
|
||||
const guildId = kanal.guild.id;
|
||||
const vorher = laufend.get(guildId);
|
||||
|
||||
// Wird nur gefüllt, wenn wirklich neu verbunden wird
|
||||
const schritte = { angefragt: null, eigenerStatus: false, sprachserver: false };
|
||||
|
||||
let verbindung = getVoiceConnection(guildId);
|
||||
if (!verbindung || verbindung.joinConfig.channelId !== kanal.id) {
|
||||
verbindung?.destroy();
|
||||
verbindung = joinVoiceChannel({
|
||||
channelId: kanal.id,
|
||||
guildId,
|
||||
adapterCreator: kanal.guild.voiceAdapterCreator,
|
||||
adapterCreator: beobachteterAdapter(kanal.guild, schritte),
|
||||
selfDeaf: true, // der Bot muss nicht hören, nur senden
|
||||
});
|
||||
// Discord verschiebt Sprachserver im Betrieb; ohne dieses Zurückholen
|
||||
@@ -213,12 +280,10 @@ export async function spielen(kanal, station) {
|
||||
const steckt = verbindung.state.status;
|
||||
verbindung.destroy();
|
||||
laufend.delete(guildId);
|
||||
const grund = steckt === VoiceConnectionStatus.Connecting
|
||||
? 'Der Sprachserver antwortet nicht auf UDP — ausgehendes UDP ist vermutlich gesperrt.'
|
||||
: steckt === VoiceConnectionStatus.Signalling
|
||||
? 'Discord nennt keinen Sprachserver — fehlen dem Bot Rechte im Kanal?'
|
||||
: `Die Verbindung blieb bei „${steckt}" stehen.`;
|
||||
console.error(`[radio] Verbindung steckt bei "${steckt}": ${grund}`);
|
||||
const grund = beitrittsDiagnose(schritte);
|
||||
console.error(`[radio] Beitritt steckt bei "${steckt}" — `
|
||||
+ `angefragt: ${schritte.angefragt}, eigener Status: ${schritte.eigenerStatus}, `
|
||||
+ `Sprachserver: ${schritte.sprachserver}`);
|
||||
throw new Error(`Sprachverbindung kam nicht zustande. ${grund}`);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user