"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>
113 lines
4.5 KiB
JavaScript
113 lines
4.5 KiB
JavaScript
// /radio — Steuertafel im Kanal öffnen.
|
|
//
|
|
// Der Befehl nimmt bewusst keinen Sendernamen entgegen: getippt werden müsste
|
|
// er auf dem Handy erst recht, und die Auswahl steht ohnehin im Menü. Ein
|
|
// Aufruf, eine Tafel, umschalten per Klick.
|
|
import { SlashCommandBuilder, MessageFlags } from 'discord.js';
|
|
import { moduleEnabled } from '../../modules.js';
|
|
import { listStations, getStation, setRadioState } from '../../db.js';
|
|
import { spielen, laeuft, tafelEmbed, tafelKomponenten, titelHolen, ffmpegVorhanden } from '../radio.js';
|
|
|
|
export const data = new SlashCommandBuilder()
|
|
.setName('radio')
|
|
.setDescription('Radio im Sprachkanal — Sender wählen und umschalten');
|
|
|
|
export async function execute(interaction) {
|
|
if (!moduleEnabled('radio')) {
|
|
await interaction.reply({
|
|
content: '❌ Radio ist ausgeschaltet — im Panel unter **Community → Radio** einschalten.',
|
|
flags: MessageFlags.Ephemeral,
|
|
});
|
|
return;
|
|
}
|
|
|
|
const stationen = listStations();
|
|
if (stationen.length === 0) {
|
|
await interaction.reply({
|
|
content: '❌ Es sind noch keine Sender eingetragen — im Panel unter **Community → Radio**.',
|
|
flags: MessageFlags.Ephemeral,
|
|
});
|
|
return;
|
|
}
|
|
|
|
const kanal = interaction.member?.voice?.channel;
|
|
const schonAn = laeuft(interaction.guild.id);
|
|
if (!kanal && !schonAn) {
|
|
await interaction.reply({
|
|
content: '❌ Geh erst in einen Sprachkanal.',
|
|
flags: MessageFlags.Ephemeral,
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Darf der Bot da überhaupt rein? Sonst hängt der Verbindungsaufbau, bis
|
|
// die Interaktion abgelaufen ist, und der Nutzer sieht nur „Fehler".
|
|
if (kanal) {
|
|
const rechte = kanal.permissionsFor(interaction.guild.members.me);
|
|
if (!rechte?.has('Connect') || !rechte?.has('Speak')) {
|
|
await interaction.reply({
|
|
content: `❌ Ich darf in <#${kanal.id}> nicht rein oder nicht sprechen.`,
|
|
flags: MessageFlags.Ephemeral,
|
|
});
|
|
return;
|
|
}
|
|
// Ein voller Kanal sieht aus wie ein Rechteproblem: Discord nimmt die
|
|
// Beitrittsanfrage entgegen und antwortet einfach nicht mehr. Nur wer
|
|
// Mitglieder verschieben darf, kommt am Limit vorbei.
|
|
if (kanal.userLimit > 0 && kanal.members.size >= kanal.userLimit && !rechte.has('MoveMembers')) {
|
|
await interaction.reply({
|
|
content: `❌ <#${kanal.id}> ist voll (${kanal.members.size}/${kanal.userLimit}). `
|
|
+ 'Entweder Platz machen oder mir das Recht **Mitglieder verschieben** geben.',
|
|
flags: MessageFlags.Ephemeral,
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Ohne ffmpeg wuerde der Bot sich hinsetzen und schweigen. Lieber gleich
|
|
// sagen, woran es liegt, als jemanden im stillen Kanal warten lassen.
|
|
if (ffmpegVorhanden() === false) {
|
|
await interaction.reply({
|
|
content: '❌ Auf dem Server fehlt **ffmpeg** — ohne das kann der Bot keinen Ton '
|
|
+ 'umwandeln. Das Image muss neu gebaut werden.',
|
|
flags: MessageFlags.Ephemeral,
|
|
});
|
|
return;
|
|
}
|
|
|
|
// Verbinden dauert länger als die drei Sekunden, die Discord gibt
|
|
await interaction.deferReply();
|
|
|
|
let station = schonAn ? getStation(schonAn.stationId) : null;
|
|
let titel = schonAn?.titel ?? null;
|
|
if (!schonAn) {
|
|
station = stationen[0];
|
|
try {
|
|
await spielen(kanal, station);
|
|
} catch (error) {
|
|
console.error('[radio]', error);
|
|
await interaction.editReply(`❌ ${error.message || 'Verbindung in den Sprachkanal fehlgeschlagen.'}`);
|
|
return;
|
|
}
|
|
({ titel } = await titelHolen(station.url));
|
|
const s = laeuft(interaction.guild.id);
|
|
if (s) s.titel = titel;
|
|
}
|
|
|
|
const jetzt = laeuft(interaction.guild.id);
|
|
const nachricht = await interaction.editReply({
|
|
embeds: [tafelEmbed(interaction.client, station, titel, kanal?.id ?? schonAn?.channelId,
|
|
{ tonLaeuft: jetzt?.tonLaeuft, ffmpeg: ffmpegVorhanden() })],
|
|
components: tafelKomponenten(stationen, station?.id),
|
|
});
|
|
|
|
// Die Tafel merken, damit der Titel-Melder sie später bearbeiten kann
|
|
setRadioState({
|
|
guildId: interaction.guild.id,
|
|
channelId: kanal?.id ?? schonAn.channelId,
|
|
stationId: station?.id ?? null,
|
|
panelChannelId: nachricht.channelId ?? interaction.channelId,
|
|
panelMessageId: nachricht.id,
|
|
});
|
|
}
|