package config import ( "context" "database/sql" "errors" "fmt" "slices" "strings" "git.anhgelus.world/anhgelus/les-copaings-bot/common" "github.com/nyttikord/gokord/bot" "github.com/nyttikord/gokord/channel" ) type Guild struct { ID uint64 XpRoles []XpRole DisabledChannels string FallbackChannel uint64 DaysXPRemains uint RrMessages []RoleReactMessage } type RoleReactMessage struct { ID uint `gorm:"primarykey"` MessageID uint64 `gorm:"not null;unique"` ChannelID uint64 GuildID uint64 Note string Roles []*RoleReact } type RoleReact struct { ID uint `gorm:"primarykey"` Reaction string RoleID uint64 RoleReactMessageID uint CounterID uint `gorm:"-"` } func GetGuild(ctx context.Context, guildID uint64) *Guild { cfg := Guild{ID: guildID} if err := cfg.Load(ctx); err != nil { panic(err) } return &cfg } func (g *Guild) Load(ctx context.Context) error { db := common.GetDB(ctx) row := db.QueryRowContext( ctx, `SELECT disabled_channels, fallback_channel, days_xp_remains FROM guilds WHERE id = ?`, g.ID, ) g.RrMessages = nil g.XpRoles = nil err := row.Err() if err != nil { if !errors.Is(err, sql.ErrNoRows) { return err } _, err = db.ExecContext( ctx, `INSERT INTO guilds (id) VALUES (?)`, g.ID, ) return err } err = row.Scan(&g.DisabledChannels, &g.FallbackChannel, &g.DaysXPRemains) if err != nil { return err } g.XpRoles, err = getXpRoles(ctx, g.ID) return err } func (g *Guild) Save(ctx context.Context) error { db := common.GetDB(ctx) _, err := db.ExecContext( ctx, `UPDATE guilds SET disabled_channels = ?, fallback_channel = ?, days_xp_remains = ? WHERE id = ?`, g.DisabledChannels, g.FallbackChannel, g.DaysXPRemains, g.ID, ) if err != nil { return err } savedRoles, err := getXpRoles(ctx, g.ID) if err != nil { return err } roleEqual := func(base XpRole) func(XpRole) bool { return func(v XpRole) bool { return v.GuildID == base.GuildID && v.RoleID == base.RoleID && v.XP == base.XP } } // super slow code, but I don't want to implement a data structure to optimize this for _, role := range g.XpRoles { if !slices.ContainsFunc(savedRoles, roleEqual(role)) { err = role.Save(ctx) if err != nil { return err } } } for _, role := range savedRoles { if !slices.ContainsFunc(g.XpRoles, roleEqual(role)) { err = role.Delete(ctx) if err != nil { return err } } } return nil } func (g *Guild) IsDisabled(ctx context.Context, dg bot.Session, channelID uint64) bool { ok := true for channelID != 0 && ok { ok = !strings.Contains(g.DisabledChannels, fmt.Sprintf("%d", channelID)) c, err := dg.ChannelState().GetChannel(channelID) if err != nil { bot.Logger(ctx).Warn("unable to find channel %s in state", "error", err, "channel", c) c, err = channel.Get(channelID).Do(ctx) if err != nil { bot.Logger(ctx).Error("unable to fetch channel", "error", err, "channel", c) return false } } channelID = c.ParentID } return !ok } func (g *Guild) FindXpRole(roleID uint64) (int, *XpRole) { for i, r := range g.XpRoles { if r.RoleID == roleID { return i, &r } } return 0, nil } func getXpRoles(ctx context.Context, gID uint64) ([]XpRole, error) { roles := make([]XpRole, 0) rows, err := common.GetDB(ctx).QueryContext( ctx, `SELECT xp, role FROM xp_roles WHERE guild_id = ?`, gID, ) if err == nil { defer rows.Close() for rows.Next() { var role XpRole err = rows.Scan(&role.XP, &role.RoleID) if err != nil { return roles, err } role.GuildID = gID roles = append(roles, role) } } else { if !errors.Is(err, sql.ErrNoRows) { return roles, err } } return roles, nil }