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
|
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
}
|