feat: support multiple radio stations
This commit is contained in:
parent
83843de4ee
commit
e5cf23619d
1 changed files with 87 additions and 26 deletions
113
main.go
113
main.go
|
@ -17,12 +17,30 @@ import (
|
|||
"github.com/lmittmann/tint"
|
||||
)
|
||||
|
||||
// Station represents a radio station
|
||||
type Station struct {
|
||||
Name string // Display name
|
||||
StreamURL string // URL for the audio stream
|
||||
Description string // Optional description
|
||||
}
|
||||
|
||||
var (
|
||||
token string
|
||||
radioURL = "https://atres-live.europafm.com/live/europafm/bitrate_1.m3u8"
|
||||
logger *slog.Logger
|
||||
debug bool
|
||||
guildID string // For testing in a specific guild
|
||||
stations = map[string]Station{
|
||||
"europafm": {
|
||||
Name: "Europa FM",
|
||||
StreamURL: "https://atres-live.europafm.com/live/europafm/bitrate_1.m3u8",
|
||||
Description: "La radio de éxitos musicales",
|
||||
},
|
||||
"cope": {
|
||||
Name: "COPE",
|
||||
StreamURL: "http://flucast31-h-cloud.flumotion.com/cope/net1.mp3",
|
||||
Description: "A tope con la",
|
||||
},
|
||||
}
|
||||
logger *slog.Logger
|
||||
debug bool
|
||||
guildID string // For testing in a specific guild
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -97,8 +115,8 @@ func main() {
|
|||
"guild_id", i.GuildID)
|
||||
|
||||
switch cmdName {
|
||||
case "europafm":
|
||||
handleEuropaFMCommand(s, i)
|
||||
case "radio":
|
||||
handleRadioCommand(s, i)
|
||||
default:
|
||||
logger.Warn("Unknown command received", "command", cmdName)
|
||||
}
|
||||
|
@ -184,18 +202,27 @@ func main() {
|
|||
|
||||
// Define the command
|
||||
cmd := &discordgo.ApplicationCommand{
|
||||
Name: "europafm",
|
||||
Description: "Control EuropaFM radio streaming",
|
||||
Name: "radio",
|
||||
Description: "Controla la transmisión de radio",
|
||||
Options: []*discordgo.ApplicationCommandOption{
|
||||
{
|
||||
Type: discordgo.ApplicationCommandOptionSubCommand,
|
||||
Name: "play",
|
||||
Description: "Start streaming EuropaFM radio in your voice channel",
|
||||
Description: "Comienza a transmitir una radio en tu canal de voz",
|
||||
Options: []*discordgo.ApplicationCommandOption{
|
||||
{
|
||||
Type: discordgo.ApplicationCommandOptionString,
|
||||
Name: "station",
|
||||
Description: "Estación de radio a reproducir",
|
||||
Required: true,
|
||||
Choices: getStationChoices(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: discordgo.ApplicationCommandOptionSubCommand,
|
||||
Name: "stop",
|
||||
Description: "Stop streaming and leave the voice channel",
|
||||
Description: "Detiene la transmisión y abandona el canal de voz",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -237,7 +264,9 @@ func main() {
|
|||
|
||||
// Disconnect from all voice channels
|
||||
for _, vc := range dg.VoiceConnections {
|
||||
vc.Disconnect()
|
||||
if vc.Disconnect() != nil {
|
||||
logger.Error("Error disconnecting from voice channel", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Close the Discord session
|
||||
|
@ -259,7 +288,7 @@ func logRawInteraction(s *discordgo.Session, e *discordgo.Event) {
|
|||
}
|
||||
|
||||
// Detailed logging for interactions
|
||||
func logInteraction(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
func logInteraction(_ *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
if !debug {
|
||||
return
|
||||
}
|
||||
|
@ -275,11 +304,11 @@ func logInteraction(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
|||
"user", i.User != nil)
|
||||
}
|
||||
|
||||
func handleEuropaFMCommand(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
func handleRadioCommand(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
ctx := context.Background()
|
||||
options := i.ApplicationCommandData().Options
|
||||
if len(options) == 0 {
|
||||
logger.Warn("Received europafm command with no subcommand", "interaction_id", i.ID)
|
||||
logger.Warn("Received radio command with no subcommand", "interaction_id", i.ID)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -291,7 +320,7 @@ func handleEuropaFMCommand(s *discordgo.Session, i *discordgo.InteractionCreate)
|
|||
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: "Processing your request...",
|
||||
Content: "Procesando tu solicitud...",
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -302,15 +331,31 @@ func handleEuropaFMCommand(s *discordgo.Session, i *discordgo.InteractionCreate)
|
|||
|
||||
switch subCommand {
|
||||
case "play":
|
||||
// Check if station is specified
|
||||
stationName := "europafm" // Default value
|
||||
if len(options[0].Options) > 0 {
|
||||
stationName = options[0].Options[0].StringValue()
|
||||
}
|
||||
|
||||
// Get station URL
|
||||
station, ok := stations[stationName]
|
||||
if !ok {
|
||||
logger.Warn("Unknown station requested", "station", stationName)
|
||||
followupMsg(s, i, "Estación de radio desconocida.")
|
||||
return
|
||||
}
|
||||
|
||||
logger = logger.With("station", stationName, "station_name", station.Name)
|
||||
|
||||
// Find the guild for that channel
|
||||
guildID := i.GuildID
|
||||
logger := logger.With("guild_id", guildID)
|
||||
logger = logger.With("guild_id", guildID)
|
||||
|
||||
// Find the voice channel the user is in
|
||||
vs, err := findUserVoiceState(ctx, s, guildID, i.Member.User.ID)
|
||||
if err != nil {
|
||||
logger.Warn("User not in voice channel", "error", err)
|
||||
followupMsg(s, i, "You need to be in a voice channel to use this command.")
|
||||
followupMsg(s, i, "Necesitas estar en un canal de voz para usar este comando.")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -327,7 +372,7 @@ func handleEuropaFMCommand(s *discordgo.Session, i *discordgo.InteractionCreate)
|
|||
vc, err := s.ChannelVoiceJoin(guildID, vs.ChannelID, false, true)
|
||||
if err != nil {
|
||||
logger.Error("Error joining voice channel", "error", err)
|
||||
followupMsg(s, i, "Error joining voice channel. Please try again.")
|
||||
followupMsg(s, i, "Error al unirse al canal de voz. Por favor, inténtalo de nuevo.")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -335,7 +380,7 @@ func handleEuropaFMCommand(s *discordgo.Session, i *discordgo.InteractionCreate)
|
|||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Update message
|
||||
followupMsg(s, i, "Now streaming EuropaFM radio in your voice channel!")
|
||||
followupMsg(s, i, fmt.Sprintf("¡Ahora transmitiendo la radio %s en tu canal de voz!", station.Name))
|
||||
|
||||
// Start streaming - this is a blocking call, so we run it in a goroutine
|
||||
logger.Info("Starting audio stream")
|
||||
|
@ -347,11 +392,11 @@ func handleEuropaFMCommand(s *discordgo.Session, i *discordgo.InteractionCreate)
|
|||
defer logger.Info("Audio streaming ended")
|
||||
|
||||
// Start playing audio with enhanced error logging
|
||||
err := playAudioStream(vc, radioURL, stopChan, logger)
|
||||
err := playAudioStream(vc, station.StreamURL, stopChan, logger)
|
||||
|
||||
if err != nil {
|
||||
logger.Error("Audio streaming error", "error", err)
|
||||
followupMsg(s, i, "The audio stream has ended unexpectedly. Please try again later.")
|
||||
followupMsg(s, i, "La transmisión de audio ha terminado inesperadamente. Por favor, inténtalo más tarde.")
|
||||
}
|
||||
}()
|
||||
|
||||
|
@ -360,13 +405,15 @@ func handleEuropaFMCommand(s *discordgo.Session, i *discordgo.InteractionCreate)
|
|||
vc, err := findExistingVoiceConnection(ctx, s, i.GuildID)
|
||||
if err != nil {
|
||||
logger.Info("No active voice connection found", "error", err)
|
||||
followupMsg(s, i, "Not currently streaming in any channel.")
|
||||
followupMsg(s, i, "No hay transmisión activa en ningún canal.")
|
||||
return
|
||||
}
|
||||
|
||||
logger.Info("Disconnecting from voice channel")
|
||||
vc.Disconnect()
|
||||
followupMsg(s, i, "Disconnected from voice channel.")
|
||||
if vc.Disconnect() != nil {
|
||||
logger.Error("Error disconnecting from voice channel", "error", err)
|
||||
}
|
||||
followupMsg(s, i, "Desconectado del canal de voz.")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -380,7 +427,7 @@ func followupMsg(s *discordgo.Session, i *discordgo.InteractionCreate, content s
|
|||
}
|
||||
}
|
||||
|
||||
func findUserVoiceState(ctx context.Context, s *discordgo.Session, guildID, userID string) (*discordgo.VoiceState, error) {
|
||||
func findUserVoiceState(_ context.Context, s *discordgo.Session, guildID, userID string) (*discordgo.VoiceState, error) {
|
||||
guild, err := s.State.Guild(guildID)
|
||||
if err != nil {
|
||||
logger.Error("Error getting guild", "guild_id", guildID, "error", err)
|
||||
|
@ -396,7 +443,7 @@ func findUserVoiceState(ctx context.Context, s *discordgo.Session, guildID, user
|
|||
return nil, fmt.Errorf("user not in any voice channel")
|
||||
}
|
||||
|
||||
func findExistingVoiceConnection(ctx context.Context, s *discordgo.Session, guildID string) (*discordgo.VoiceConnection, error) {
|
||||
func findExistingVoiceConnection(_ context.Context, s *discordgo.Session, guildID string) (*discordgo.VoiceConnection, error) {
|
||||
for _, vc := range s.VoiceConnections {
|
||||
if vc.GuildID == guildID {
|
||||
return vc, nil
|
||||
|
@ -480,3 +527,17 @@ func playAudioStream(vc *discordgo.VoiceConnection, url string, stopChan chan bo
|
|||
|
||||
return fmt.Errorf("audio stream ended unexpectedly")
|
||||
}
|
||||
|
||||
// getStationChoices generates the choices for the command options based on the stations map
|
||||
func getStationChoices() []*discordgo.ApplicationCommandOptionChoice {
|
||||
choices := make([]*discordgo.ApplicationCommandOptionChoice, 0, len(stations))
|
||||
|
||||
for key, station := range stations {
|
||||
choices = append(choices, &discordgo.ApplicationCommandOptionChoice{
|
||||
Name: station.Name,
|
||||
Value: key,
|
||||
})
|
||||
}
|
||||
|
||||
return choices
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue