Add Discord client, command router and voice channel handling

This commit is contained in:
2026-09-21 18:48:11 +02:00
parent c4757933e1
commit 347faf6b94
4 changed files with 155 additions and 0 deletions
+25
View File
@@ -0,0 +1,25 @@
package discord
import (
"log/slog"
"github.com/disgoorg/disgo"
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/cache"
"github.com/disgoorg/disgo/gateway"
)
func NewClient(token string, logger *slog.Logger) (*bot.Client, error) {
return disgo.New(token,
bot.WithLogger(logger),
bot.WithGatewayConfigOpts(
gateway.WithIntents(
gateway.IntentGuilds,
gateway.IntentGuildVoiceStates,
),
),
bot.WithCacheConfigOpts(
cache.WithCaches(cache.FlagVoiceStates),
),
)
}
+15
View File
@@ -0,0 +1,15 @@
package discord
import (
"fmt"
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord"
)
func RegisterCommands(client *bot.Client, definitions []discord.ApplicationCommandCreate) error {
if _, err := client.Rest.SetGlobalCommands(client.ApplicationID, definitions); err != nil {
return fmt.Errorf("registering commands: %w", err)
}
return nil
}
+36
View File
@@ -0,0 +1,36 @@
package discord
import (
"context"
"log/slog"
"github.com/disgoorg/disgo/events"
"discord-music-bot/internal/commands"
)
type Router struct {
registry *commands.Registry
logger *slog.Logger
}
func NewRouter(registry *commands.Registry, logger *slog.Logger) *Router {
return &Router{
registry: registry,
logger: logger,
}
}
func (r *Router) OnApplicationCommand(event *events.ApplicationCommandInteractionCreate) {
name := event.SlashCommandInteractionData().CommandName()
cmd, ok := r.registry.Get(name)
if !ok {
r.logger.Warn("received unknown command", slog.String("command", name))
return
}
if err := cmd.Execute(context.Background(), event); err != nil {
r.logger.Error("command execution failed", slog.String("command", name), slog.Any("err", err))
}
}
+79
View File
@@ -0,0 +1,79 @@
package discord
import (
"context"
"log/slog"
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/disgolink/v4/disgolink"
"discord-music-bot/internal/music"
)
type VoiceForwarder struct {
client *bot.Client
lavalink *disgolink.Client
music *music.Manager
logger *slog.Logger
}
func NewVoiceForwarder(client *bot.Client, lavalinkClient *disgolink.Client, musicManager *music.Manager, logger *slog.Logger) *VoiceForwarder {
return &VoiceForwarder{
client: client,
lavalink: lavalinkClient,
music: musicManager,
logger: logger,
}
}
func (f *VoiceForwarder) OnVoiceStateUpdate(event *events.GuildVoiceStateUpdate) {
if event.VoiceState.UserID == f.client.ApplicationID {
f.lavalink.OnVoiceStateUpdate(context.Background(), event.VoiceState.GuildID, event.VoiceState.ChannelID, event.VoiceState.SessionID)
return
}
f.leaveIfNowAlone(event)
}
func (f *VoiceForwarder) leaveIfNowAlone(event *events.GuildVoiceStateUpdate) {
oldChannelID := event.OldVoiceState.ChannelID
if oldChannelID == nil {
return
}
if newChannelID := event.VoiceState.ChannelID; newChannelID != nil && *newChannelID == *oldChannelID {
return
}
guildID := event.VoiceState.GuildID
botState, ok := f.client.Caches.VoiceState(guildID, f.client.ApplicationID)
if !ok || botState.ChannelID == nil || *botState.ChannelID != *oldChannelID {
return
}
for state := range f.client.Caches.VoiceStates(guildID) {
if state.UserID != f.client.ApplicationID && state.ChannelID != nil && *state.ChannelID == *oldChannelID {
return
}
}
ctx := context.Background()
if err := f.music.Guild(guildID).Stop(ctx); err != nil {
f.logger.ErrorContext(ctx, "failed to stop playback after being left alone",
slog.String("guild_id", guildID.String()), slog.Any("err", err))
}
if err := f.client.UpdateVoiceState(ctx, guildID, nil, false, false); err != nil {
f.logger.ErrorContext(ctx, "failed to leave voice channel after being left alone",
slog.String("guild_id", guildID.String()), slog.Any("err", err))
}
}
func (f *VoiceForwarder) OnVoiceServerUpdate(event *events.VoiceServerUpdate) {
if event.Endpoint == nil {
f.logger.Warn("received voice server update without an endpoint", slog.String("guild_id", event.GuildID.String()))
return
}
f.lavalink.OnVoiceServerUpdate(context.Background(), event.GuildID, event.Token, *event.Endpoint)
}