Add Discord client, command router and voice channel handling
This commit is contained in:
@@ -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),
|
||||
),
|
||||
)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
Reference in New Issue
Block a user