110 lines
3.1 KiB
Go
110 lines
3.1 KiB
Go
package commands
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/disgoorg/disgo/bot"
|
|
"github.com/disgoorg/disgo/discord"
|
|
"github.com/disgoorg/disgo/events"
|
|
"github.com/disgoorg/disgolink/v4/lavalink"
|
|
"github.com/disgoorg/snowflake/v2"
|
|
)
|
|
|
|
func guildID(event *events.ApplicationCommandInteractionCreate) (snowflake.ID, bool) {
|
|
id := event.GuildID()
|
|
if id == nil {
|
|
return 0, false
|
|
}
|
|
return *id, true
|
|
}
|
|
|
|
func memberVoiceChannel(discordClient *bot.Client, guild snowflake.ID, userID snowflake.ID) (snowflake.ID, bool) {
|
|
state, ok := discordClient.Caches.VoiceState(guild, userID)
|
|
if !ok || state.ChannelID == nil {
|
|
return 0, false
|
|
}
|
|
return *state.ChannelID, true
|
|
}
|
|
|
|
func ensureCanControlPlayback(discordClient *bot.Client, guild snowflake.ID, userID snowflake.ID) (bool, string) {
|
|
botChannelID, botConnected := memberVoiceChannel(discordClient, guild, discordClient.ApplicationID)
|
|
if !botConnected {
|
|
return true, ""
|
|
}
|
|
|
|
userChannelID, userConnected := memberVoiceChannel(discordClient, guild, userID)
|
|
if !userConnected || userChannelID != botChannelID {
|
|
return false, fmt.Sprintf("I'm currently active in <#%s>. Join that channel to control playback.", botChannelID)
|
|
}
|
|
return true, ""
|
|
}
|
|
|
|
func requirePlaybackAccess(discordClient *bot.Client, event *events.ApplicationCommandInteractionCreate) (snowflake.ID, error) {
|
|
gid, ok := guildID(event)
|
|
if !ok {
|
|
return 0, guildOnlyError(event)
|
|
}
|
|
|
|
if allowed, reason := ensureCanControlPlayback(discordClient, gid, event.User().ID); !allowed {
|
|
return 0, rejectEphemeral(event, reason)
|
|
}
|
|
|
|
return gid, nil
|
|
}
|
|
|
|
func reply(event *events.ApplicationCommandInteractionCreate, content string) error {
|
|
return event.CreateMessage(discord.NewMessageCreate().WithContent(content))
|
|
}
|
|
|
|
func replyEphemeral(event *events.ApplicationCommandInteractionCreate, content string) error {
|
|
return event.CreateMessage(discord.NewMessageCreate().WithContent(content).WithEphemeral(true))
|
|
}
|
|
|
|
func guildOnlyError(event *events.ApplicationCommandInteractionCreate) error {
|
|
return rejectEphemeral(event, "This command only works inside a server.")
|
|
}
|
|
|
|
func rejectEphemeral(event *events.ApplicationCommandInteractionCreate, content string) error {
|
|
if err := replyEphemeral(event, content); err != nil {
|
|
return err
|
|
}
|
|
return errors.New(content)
|
|
}
|
|
|
|
func replyWithError(event *events.ApplicationCommandInteractionCreate, userMessage string, cause error) error {
|
|
if err := replyEphemeral(event, userMessage); err != nil {
|
|
return err
|
|
}
|
|
return fmt.Errorf("%s: %w", userMessage, cause)
|
|
}
|
|
|
|
func intPtr(v int) *int {
|
|
return &v
|
|
}
|
|
|
|
func parsePosition(raw string) (lavalink.Duration, error) {
|
|
parts := strings.Split(raw, ":")
|
|
if len(parts) == 0 || len(parts) > 3 {
|
|
return 0, errors.New("expected seconds, mm:ss or hh:mm:ss")
|
|
}
|
|
|
|
segments := make([]int64, len(parts))
|
|
for i, part := range parts {
|
|
n, err := strconv.ParseInt(strings.TrimSpace(part), 10, 64)
|
|
if err != nil || n < 0 {
|
|
return 0, errors.New("expected seconds, mm:ss or hh:mm:ss")
|
|
}
|
|
segments[i] = n
|
|
}
|
|
|
|
var totalSeconds int64
|
|
for _, segment := range segments {
|
|
totalSeconds = totalSeconds*60 + segment
|
|
}
|
|
|
|
return lavalink.Duration(totalSeconds) * lavalink.Second, nil
|
|
}
|