-
Notifications
You must be signed in to change notification settings - Fork 0
/
guild.go
249 lines (225 loc) · 7.69 KB
/
guild.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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
package fuse
import (
"fmt"
"github.com/bwmarrin/discordgo"
log "github.com/inconshreveable/log15"
"github.com/sylvrs/fuse/command"
"github.com/sylvrs/fuse/component"
"github.com/sylvrs/fuse/modal"
"github.com/sylvrs/fuse/utils"
"gorm.io/gorm"
)
// GuildConfiguration is the structure that holds the configuration for a single guild
type GuildConfiguration struct {
// GuildID is the ID of the guild and is used as the primary key
GuildID string `gorm:"primarykey"`
}
// GuildManager is the structure that manages all of the services for a single guild
// It holds the guild's ID, its configuration, the database connection, and the Discord session
type GuildManager struct {
logger log.Logger
config *GuildConfiguration
manager *Manager
connection *gorm.DB
session *discordgo.Session
guild *discordgo.Guild
commandHandler *command.CommandHandler
modalHandler *modal.ModalHandler
listenedComponents map[string]component.ComponentHandlerFunc
services []Service
}
func CreateGuildManager(manager *Manager, config *GuildConfiguration) (*GuildManager, error) {
guild, err := manager.session.State.Guild(config.GuildID)
if err != nil {
return nil, fmt.Errorf("failed to get guild by id %s", config.GuildID)
}
commandHandler, err := command.NewCommandHandler(manager.session, guild)
if err != nil {
return nil, err
}
guildManager := &GuildManager{
logger: log.New("guild", config.GuildID),
config: config,
manager: manager,
connection: manager.connection,
session: manager.session,
guild: guild,
commandHandler: commandHandler,
modalHandler: modal.NewModalHandler(manager.session, guild),
listenedComponents: make(map[string]component.ComponentHandlerFunc),
services: make([]Service, 0),
}
// register services to guild manager
services, err := manager.CreateServices(guildManager)
if err != nil {
return nil, err
}
guildManager.services = services
return guildManager, nil
}
// Start starts all of the services for the guild and registers all handlers, both component and command
func (mng *GuildManager) Start() error {
for _, service := range mng.services {
if err := service.Start(mng); err != nil {
return err
}
}
err := mng.commandHandler.Init()
if err != nil {
return err
}
mng.AddHandler(mng.handleListenedComponents)
return nil
}
// Stop stops all of the services for the guild and deinitializes the command handler
func (mng *GuildManager) Stop() error {
for _, service := range mng.services {
if err := service.Stop(mng); err != nil {
return err
}
}
mng.commandHandler.Deinit()
return nil
}
// FetchServiceConfig fetches the service configuration from the database
// If the configuration does not exist, it will be created for the guild
// An example of this in action would be like so:
//
// var config MyServiceConfig
// err := mng.FetchServiceConfig(&config)
// ...
func (mng *GuildManager) FetchServiceConfig(config interface{}, defaults ...interface{}) error {
mng.connection.AutoMigrate(config)
// Ensure that our guild ID is set in the service configuration
defaults = append(defaults, ServiceConfiguration{GuildId: mng.guild.ID})
return mng.Connection().Where("guild_id = ?", mng.guild.ID).Attrs(defaults...).FirstOrCreate(config).Error
}
// SaveServiceConfig saves the service configuration to the database
func (mng *GuildManager) SaveServiceConfig(config interface{}) error {
return mng.Connection().Save(config).Error
}
// Save saves the built-in guild configuration to the database
func (mng *GuildManager) Save() error {
return mng.Connection().Save(mng.config).Error
}
// Logger returns the logger for the guild
func (mng *GuildManager) Logger() log.Logger {
return mng.logger
}
// GlobalManager returns Fuse's global manager instance
func (mng *GuildManager) GlobalManager() *Manager {
return mng.manager
}
// Connection returns the database connection for the guild
func (mng *GuildManager) Connection() *gorm.DB {
return mng.connection
}
// Session returns the Discord session for the guild
func (mng *GuildManager) Session() *discordgo.Session {
return mng.session
}
// BotUser returns the bot user for the guild
func (mng *GuildManager) BotUser() *discordgo.User {
return mng.GlobalManager().BotUser()
}
// Guild returns the instance of the guild
func (mng *GuildManager) Guild() *discordgo.Guild {
return mng.guild
}
// CommandHandler returns the command handler for the guild
func (mng *GuildManager) CommandHandler() *command.CommandHandler {
return mng.commandHandler
}
// ModalHandler returns the modal handler for the guild
func (mng *GuildManager) ModalHandler() *modal.ModalHandler {
return mng.modalHandler
}
// AddHandler is a wrapper for the session handler but limits the handler to only the guild
// This may seem excessive but it is a good practice to prevent accidental checking of the wrong guild
func (mng *GuildManager) AddHandler(handler interface{}) {
switch handler := handler.(type) {
case func(s *discordgo.Session, i *discordgo.MessageCreate):
mng.session.AddHandler(func(s *discordgo.Session, i *discordgo.MessageCreate) {
if i.GuildID != mng.guild.ID {
return
}
handler(s, i)
})
case func(s *discordgo.Session, i *discordgo.InteractionCreate):
mng.session.AddHandler(func(s *discordgo.Session, i *discordgo.InteractionCreate) {
if i.GuildID != mng.guild.ID {
return
}
handler(s, i)
})
case func(s *discordgo.Session, i *discordgo.GuildCreate):
mng.session.AddHandler(func(s *discordgo.Session, i *discordgo.GuildCreate) {
if i.Guild.ID != mng.guild.ID {
return
}
handler(s, i)
})
case func(s *discordgo.Session, i *discordgo.GuildDelete):
mng.session.AddHandler(func(s *discordgo.Session, i *discordgo.GuildDelete) {
if i.Guild.ID != mng.guild.ID {
return
}
handler(s, i)
})
case func(s *discordgo.Session, i *discordgo.GuildRoleUpdate):
mng.session.AddHandler(func(s *discordgo.Session, i *discordgo.GuildRoleUpdate) {
if i.GuildID != mng.guild.ID {
return
}
handler(s, i)
})
case func(s *discordgo.Session, i *discordgo.ChannelDelete):
mng.session.AddHandler(func(s *discordgo.Session, i *discordgo.ChannelDelete) {
if i.GuildID != mng.guild.ID {
return
}
handler(s, i)
})
case func(s *discordgo.Session, i *discordgo.GuildMemberAdd):
mng.session.AddHandler(func(s *discordgo.Session, i *discordgo.GuildMemberAdd) {
if i.GuildID != mng.guild.ID {
return
}
handler(s, i)
})
default:
mng.logger.Warn("guild handler will not check for guild id", "handler", handler)
mng.session.AddHandler(handler)
}
}
func (mng *GuildManager) handleListenedComponents(s *discordgo.Session, i *discordgo.InteractionCreate) {
if i.Type != discordgo.InteractionMessageComponent {
return
}
data := i.MessageComponentData()
handler, ok := mng.listenedComponents[data.CustomID]
if !ok {
return
}
resp, err := handler(i, &data)
if err != nil {
mng.logger.Error("failed to handle component", "error", err)
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
Type: discordgo.InteractionResponseChannelMessageWithSource,
Data: &discordgo.InteractionResponseData{
Flags: discordgo.MessageFlagsEphemeral,
Embeds: []*discordgo.MessageEmbed{utils.ErrorAsEmbed(err.Error())},
},
})
return
}
if resp != nil {
if err := s.InteractionRespond(i.Interaction, resp); err != nil {
mng.logger.Error("failed to respond to interaction", "error", err)
}
}
}
// ListenForComponent registers a handler for a specific component
func (mng *GuildManager) ListenForComponent(customId string, handler component.ComponentHandlerFunc) {
mng.listenedComponents[customId] = handler
}