/
main.go
92 lines (79 loc) · 2.52 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
package dgc
import (
"fmt"
"github.com/bwmarrin/discordgo"
"github.com/rs/zerolog/log"
)
type (
// ErrorHandler represents an error handler function
ErrorHandler func(error, *discordgo.Session, *discordgo.InteractionCreate) error
// Commander represents Discord slash commands
Commander struct {
errorHandler ErrorHandler
commands map[string]*Command
}
)
// NewCommander returns a new command collection
func NewCommander() *Commander {
return &Commander{
errorHandler: DefaultErrorHandler,
commands: map[string]*Command{},
}
}
// OnError sets the error handler when an error is returned from a command
func (m *Commander) OnError(h ErrorHandler) {
m.errorHandler = h
}
// AddCommand adds a new slash command
func (m *Commander) AddCommand(c *Command) {
m.commands[c.Name] = c
}
// Handler handles interaction create events from Discord
func (m *Commander) Handler(s *discordgo.Session, i *discordgo.InteractionCreate) {
name := i.ApplicationCommandData().Name
l := log.With().
Str("interaction", i.ID).
Str("command", name).
Str("guild", i.GuildID).
Str("channel", i.ChannelID).
Str("user", GetUser(i).String()).
Logger()
l.Debug().Msg("Interaction created")
if c, ok := m.commands[name]; ok {
if err := c.Run(s, i); err != nil {
l.Error().Err(err).Msg("")
if err := m.errorHandler(err, s, i); err != nil {
l.Error().Err(err).Msg("Could not respond to interaction")
}
}
}
}
// DefaultErrorHandler is the default command error handler
func DefaultErrorHandler(err error, s *discordgo.Session, i *discordgo.InteractionCreate) error {
msg := fmt.Sprintf("An error occurred: %v", err)
if Responded(s, i) {
_, err = s.InteractionResponseEdit(s.State.User.ID, i.Interaction, &discordgo.WebhookEdit{
Content: msg,
})
return err
}
return s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Content: msg,
Flags: InteractionResponseEphemeral,
},
})
}
// Responded checks if the current interaction has been responded to
func Responded(s *discordgo.Session, i *discordgo.InteractionCreate) bool {
msg, err := s.InteractionResponse(s.State.User.ID, i.Interaction)
if err != nil {
// a 404 is an expected error, so ignore those, but log everything else
if r, ok := err.(*discordgo.RESTError); !ok || r.Response.StatusCode != 404 {
log.Error().Err(err).Str("interaction", i.ID).Msg("Could not fetch interaction response")
}
return false
}
return msg.ID != ""
}