Re-write the libs with discordgo
This commit is contained in:
@ -1,28 +1,27 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"net"
|
||||
"os"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/diamondburned/arikawa/v3/api"
|
||||
"github.com/diamondburned/arikawa/v3/discord"
|
||||
"github.com/diamondburned/arikawa/v3/utils/json/option"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
func ErrorResponse(err error) *api.InteractionResponseData {
|
||||
var content string
|
||||
switch e := err.(type) {
|
||||
case *net.OpError:
|
||||
content = "**Network Error:** " + e.Error()
|
||||
case *os.PathError:
|
||||
content = "**File Error:** " + e.Error()
|
||||
default:
|
||||
content = "**Error:** " + err.Error()
|
||||
}
|
||||
|
||||
return &api.InteractionResponseData{
|
||||
Content: option.NewNullableString(content),
|
||||
Flags: discord.EphemeralMessage,
|
||||
AllowedMentions: &api.AllowedMentions{},
|
||||
// respondWithError sends an error message as a response to the interaction
|
||||
func RespondWithError(s *discordgo.Session, i *discordgo.InteractionCreate, message string) {
|
||||
log.Printf("Responding with error: %s", message)
|
||||
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: message,
|
||||
Flags: discordgo.MessageFlagsEphemeral,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Error sending error response: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func ThrowWithError(command, message string) error {
|
||||
return fmt.Errorf("error in command '%s': %s", command, message)
|
||||
}
|
||||
|
@ -1,89 +0,0 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/diamondburned/arikawa/v3/discord"
|
||||
)
|
||||
|
||||
var manager = NewTimerManager()
|
||||
|
||||
// Userish is an interface that captures the common methods you may want to call
|
||||
// on either a discord.Member or discord.User, including a display name.
|
||||
type Userish interface {
|
||||
ID() discord.UserID
|
||||
Username() string
|
||||
DisplayName() string
|
||||
}
|
||||
|
||||
// memberUser adapts a discord.Member to the Userish interface.
|
||||
type memberUser struct {
|
||||
*discord.Member
|
||||
}
|
||||
|
||||
func (mu memberUser) ID() discord.UserID {
|
||||
return mu.User.ID
|
||||
}
|
||||
|
||||
func (mu memberUser) Username() string {
|
||||
return mu.User.Username
|
||||
}
|
||||
|
||||
func (mu memberUser) DisplayName() string {
|
||||
// If Nick is set, return it as the display name, otherwise return Username
|
||||
if mu.Member.Nick != "" {
|
||||
return mu.Member.Nick
|
||||
}
|
||||
return mu.User.Username
|
||||
}
|
||||
|
||||
// directUser adapts a discord.User to the Userish interface.
|
||||
type directUser struct {
|
||||
*discord.User
|
||||
}
|
||||
|
||||
func (du directUser) ID() discord.UserID {
|
||||
return du.User.ID
|
||||
}
|
||||
|
||||
func (du directUser) Username() string {
|
||||
return du.User.Username
|
||||
}
|
||||
|
||||
func (du directUser) DisplayName() string {
|
||||
// For a direct user, the display name is just the username since no nickname is available.
|
||||
return du.User.Username
|
||||
}
|
||||
|
||||
// GetUserObject takes an interaction event and returns a Userish, which may be
|
||||
// either a discord.Member or a discord.User, but exposes it through a consistent interface.
|
||||
func GetUserObject(event discord.InteractionEvent) Userish {
|
||||
if event.Member != nil {
|
||||
return memberUser{event.Member}
|
||||
} else {
|
||||
return directUser{event.User}
|
||||
}
|
||||
}
|
||||
|
||||
func CooldownHandler(event discord.InteractionEvent, key string, duration time.Duration) (bool, string) {
|
||||
user := GetUserObject(event)
|
||||
allowList := strings.Split(os.Getenv("COOLDOWN_ALLOW_LIST"), ",")
|
||||
|
||||
// Check if the user ID is in the allowList
|
||||
for _, id := range allowList {
|
||||
if id == user.ID().String() {
|
||||
return true, ""
|
||||
}
|
||||
}
|
||||
|
||||
isOnCooldown, remaining := manager.TimerRunning(user.ID().String(), key)
|
||||
if isOnCooldown {
|
||||
return false, fmt.Sprintf("You are on cooldown. Please wait for %v", remaining)
|
||||
}
|
||||
|
||||
manager.StartTimer(user.ID().String(), key, duration)
|
||||
return true, ""
|
||||
}
|
47
lib/member.go
Normal file
47
lib/member.go
Normal file
@ -0,0 +1,47 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
// InteractionUser represents a user from an interaction, abstracting away the differences
|
||||
// between guild members and DM users.
|
||||
type InteractionUser struct {
|
||||
ID string
|
||||
Username string
|
||||
Bot bool
|
||||
}
|
||||
|
||||
// GetUser extracts user information from an interaction, handling both guild and DM cases.
|
||||
func GetUser(i *discordgo.InteractionCreate) (*InteractionUser, error) {
|
||||
if i.Member != nil && i.Member.User != nil {
|
||||
// Guild interaction
|
||||
return &InteractionUser{
|
||||
ID: i.Member.User.ID,
|
||||
Username: i.Member.User.Username,
|
||||
Bot: i.Member.User.Bot,
|
||||
}, nil
|
||||
} else if i.User != nil {
|
||||
// DM interaction
|
||||
return &InteractionUser{
|
||||
ID: i.User.ID,
|
||||
Username: i.User.Username,
|
||||
Bot: i.User.Bot,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return nil, ThrowWithError("GetUser", "Unable to extract user information from interaction")
|
||||
}
|
||||
|
||||
// IsInGuild checks if the interaction occurred in a guild.
|
||||
func IsInGuild(i *discordgo.InteractionCreate) bool {
|
||||
return i.Member != nil
|
||||
}
|
||||
|
||||
// GetGuildID safely retrieves the guild ID if the interaction is from a guild.
|
||||
func GetGuildID(i *discordgo.InteractionCreate) string {
|
||||
if i.GuildID != "" {
|
||||
return i.GuildID
|
||||
}
|
||||
return ""
|
||||
}
|
98
lib/timer.go
98
lib/timer.go
@ -1,72 +1,76 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
var (
|
||||
mu sync.Mutex
|
||||
instance *TimerManager
|
||||
once sync.Once
|
||||
instance *CooldownManager
|
||||
)
|
||||
|
||||
type TimerManager struct {
|
||||
timers map[string]time.Time
|
||||
mu sync.Mutex
|
||||
type CooldownManager struct {
|
||||
cooldowns map[string]time.Time
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
func NewTimerManager() *TimerManager {
|
||||
return &TimerManager{
|
||||
timers: make(map[string]time.Time),
|
||||
}
|
||||
}
|
||||
|
||||
func GetInstance() *TimerManager {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if instance == nil {
|
||||
instance = &TimerManager{
|
||||
timers: make(map[string]time.Time),
|
||||
func GetCooldownManager() *CooldownManager {
|
||||
once.Do(func() {
|
||||
instance = &CooldownManager{
|
||||
cooldowns: make(map[string]time.Time),
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
return instance
|
||||
}
|
||||
|
||||
func (m *TimerManager) StartTimer(userID string, key string, duration time.Duration) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
m.timers[userID+":"+key] = time.Now().Add(duration)
|
||||
func (cm *CooldownManager) SetCooldown(userID, commandName string, duration time.Duration) {
|
||||
cm.mu.Lock()
|
||||
defer cm.mu.Unlock()
|
||||
cm.cooldowns[userID+":"+commandName] = time.Now().Add(duration)
|
||||
}
|
||||
|
||||
func (m *TimerManager) TimerRunning(userID string, key string) (bool, time.Duration) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
func (cm *CooldownManager) CheckCooldown(userID, commandName string) (bool, time.Duration) {
|
||||
cm.mu.Lock()
|
||||
defer cm.mu.Unlock()
|
||||
|
||||
timerEnd, exists := m.timers[userID+":"+key]
|
||||
if !exists {
|
||||
return false, 0
|
||||
key := userID + ":" + commandName
|
||||
if cooldownEnd, exists := cm.cooldowns[key]; exists {
|
||||
if time.Now().Before(cooldownEnd) {
|
||||
return false, time.Until(cooldownEnd)
|
||||
}
|
||||
delete(cm.cooldowns, key)
|
||||
}
|
||||
|
||||
if time.Now().After(timerEnd) {
|
||||
delete(m.timers, userID+":"+key)
|
||||
return false, 0
|
||||
}
|
||||
|
||||
return true, time.Until(timerEnd)
|
||||
return true, 0
|
||||
}
|
||||
|
||||
func CancelTimer(userID string, key string) {
|
||||
manager := GetInstance()
|
||||
func CheckAndApplyCooldown(s *discordgo.Session, i *discordgo.InteractionCreate, commandName string, duration time.Duration) bool {
|
||||
cooldownManager := GetCooldownManager()
|
||||
user, err := GetUser(i)
|
||||
if err != nil {
|
||||
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: "Error processing command: " + err.Error(),
|
||||
},
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
// Handle non-existent keys gracefully
|
||||
if _, exists := manager.timers[userID+":"+key]; !exists {
|
||||
return
|
||||
}
|
||||
canUse, remaining := cooldownManager.CheckCooldown(user.ID, commandName)
|
||||
if !canUse {
|
||||
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: fmt.Sprintf("You can use this command again in %v", remaining.Round(time.Second)),
|
||||
},
|
||||
})
|
||||
return false
|
||||
}
|
||||
|
||||
manager.mu.Lock()
|
||||
defer manager.mu.Unlock()
|
||||
delete(manager.timers, userID+":"+key)
|
||||
cooldownManager.SetCooldown(user.ID, commandName, duration)
|
||||
return true
|
||||
}
|
||||
|
Reference in New Issue
Block a user