diff options
| author | Anhgelus Morhtuuzh <william@herges.fr> | 2026-03-08 18:51:05 +0100 |
|---|---|---|
| committer | Anhgelus Morhtuuzh <william@herges.fr> | 2026-03-08 18:51:05 +0100 |
| commit | 6e92feaba23a4992e0ec4b529660921a6bcb492a (patch) | |
| tree | 05024b52d4c8ede041facdc48ac65af5ba0259da /config | |
| parent | 3bcf74c47d1597ba650dc5a55868f83f5f547ad7 (diff) | |
feat(config): leave gorm
Diffstat (limited to 'config')
| -rw-r--r-- | config/channel.go | 4 | ||||
| -rw-r--r-- | config/guild.go | 136 | ||||
| -rw-r--r-- | config/xp_reduce.go | 4 | ||||
| -rw-r--r-- | config/xp_role.go | 368 | ||||
| -rw-r--r-- | config/xp_role_events.go | 358 |
5 files changed, 485 insertions, 385 deletions
diff --git a/config/channel.go b/config/channel.go index 5cc88aa..a015aef 100644 --- a/config/channel.go +++ b/config/channel.go @@ -21,7 +21,7 @@ const ( ) func HandleModifyFallbackChannel(ctx context.Context, dg bot.Session, i *interaction.MessageComponent) bool { - cfg := GetGuildConfig(ctx, i.GuildID) + cfg := GetGuild(ctx, i.GuildID) var channelID uint64 if len(i.Data.Values) > 0 { var err error @@ -40,7 +40,7 @@ func HandleModifyFallbackChannel(ctx context.Context, dg bot.Session, i *interac } func HandleModifyDisChannel(ctx context.Context, dg bot.Session, i *interaction.MessageComponent) bool { - cfg := GetGuildConfig(ctx, i.GuildID) + cfg := GetGuild(ctx, i.GuildID) cfg.DisabledChannels = strings.Join(i.Data.Values, ";") err := cfg.Save(ctx) if err != nil { diff --git a/config/guild.go b/config/guild.go index dc8251f..aec78ed 100644 --- a/config/guild.go +++ b/config/guild.go @@ -2,31 +2,33 @@ 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 uint `gorm:"primarykey"` - GuildID uint64 `gorm:"not null;unique"` - XpRoles []XpRole `gorm:"foreignKey:GuildConfigID"` + ID uint64 + XpRoles []XpRole DisabledChannels string FallbackChannel uint64 - DaysXPRemains uint `gorm:"default:90"` // 30 * 3 = 90 (three months) + 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 - GuildConfigID uint + ID uint `gorm:"primarykey"` + MessageID uint64 `gorm:"not null;unique"` + ChannelID uint64 + GuildID uint64 + Note string + Roles []*RoleReact } type RoleReact struct { @@ -37,45 +39,102 @@ type RoleReact struct { CounterID uint `gorm:"-"` } -func GetGuildConfig(ctx context.Context, guildID uint64) *Guild { - cfg := Guild{GuildID: guildID} +func GetGuild(ctx context.Context, guildID uint64) *Guild { + cfg := Guild{ID: guildID} if err := cfg.Load(ctx); err != nil { panic(err) } return &cfg } -func (cfg *Guild) Load(ctx context.Context) error { - return nil //common.GetDB(ctx).Where("guild_id = ?", cfg.GuildID).Preload("XpRoles").FirstOrCreate(cfg).Error +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 (cfg *Guild) Save(ctx context.Context) error { - return nil //common.GetDB(ctx).Save(cfg).Error +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 (cfg *Guild) IsDisabled(ctx context.Context, dg bot.Session, channelID uint64) bool { +func (g *Guild) IsDisabled(ctx context.Context, dg bot.Session, channelID uint64) bool { ok := true for channelID != 0 && ok { - ok = !strings.Contains(cfg.DisabledChannels, fmt.Sprintf("%d", channelID)) + ok = !strings.Contains(g.DisabledChannels, fmt.Sprintf("%d", channelID)) c, err := dg.ChannelState().GetChannel(channelID) if err != nil { - bot.Logger(ctx).Error("unable to find channel %s in state", "error", err, "channel", c) + 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 } } - if err != nil { - return false - } channelID = c.ParentID } return !ok } -func (cfg *Guild) FindXpRole(roleID uint64) (int, *XpRole) { - for i, r := range cfg.XpRoles { +func (g *Guild) FindXpRole(roleID uint64) (int, *XpRole) { + for i, r := range g.XpRoles { if r.RoleID == roleID { return i, &r } @@ -83,11 +142,28 @@ func (cfg *Guild) FindXpRole(roleID uint64) (int, *XpRole) { return 0, nil } -func (cfg *Guild) FindXpRoleID(ID uint) (int, *XpRole) { - for i, r := range cfg.XpRoles { - if r.ID == ID { - return i, &r +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 -1, nil + return roles, nil } diff --git a/config/xp_reduce.go b/config/xp_reduce.go index 36e64ed..baa4586 100644 --- a/config/xp_reduce.go +++ b/config/xp_reduce.go @@ -16,7 +16,7 @@ const ( ) func HandleModifyPeriodicReduceCommand(ctx context.Context, dg bot.Session, i *interaction.MessageComponent) { - cfg := GetGuildConfig(ctx, i.GuildID) + cfg := GetGuild(ctx, i.GuildID) resp := interaction.NewModalResponse(). CustomID(TimeReduceSet). Title("Modifier la durée de l'expérience"). @@ -66,7 +66,7 @@ func HandleTimeReduceSet(ctx context.Context, dg bot.Session, i *interaction.Mod } return false } - cfg := GetGuildConfig(ctx, i.GuildID) + cfg := GetGuild(ctx, i.GuildID) cfg.DaysXPRemains = uint(days) err = cfg.Save(ctx) if err != nil { diff --git a/config/xp_role.go b/config/xp_role.go index 81082e8..f0d57c8 100644 --- a/config/xp_role.go +++ b/config/xp_role.go @@ -2,364 +2,30 @@ package config import ( "context" - "fmt" - "slices" - "strconv" - "git.anhgelus.world/anhgelus/les-copaings-bot/dynamicid" - "git.anhgelus.world/anhgelus/les-copaings-bot/exp" - "github.com/nyttikord/gokord/bot" - "github.com/nyttikord/gokord/channel" - "github.com/nyttikord/gokord/component" - "github.com/nyttikord/gokord/discord/types" - "github.com/nyttikord/gokord/interaction" + "git.anhgelus.world/anhgelus/les-copaings-bot/common" ) type XpRole struct { - ID uint `gorm:"primarykey"` - XP uint - RoleID uint64 - GuildConfigID uint + XP uint + RoleID uint64 + GuildID uint64 } -type XpRoleId struct { - ID uint -} - -const ( - ModifyXpRole = "xp_role" - XpRoleNew = "xp_role_add" - XpRoleAdd = "xp_role_add_level" - XpRoleEdit = `xp_role_edit` - XpRoleEditLevel = `xp_role_edit_level` - XpRoleEditLevelStart = `xp_role_edit_level_start` - XpRoleEditRole = `xp_role_edit_role` - XpRoleDel = `xp_role_del` -) - -func HandleXpRole(ctx context.Context, dg bot.Session, i *interaction.Interaction) { - cfg := GetGuildConfig(ctx, i.GuildID) - container := component.Container{ - Components: []component.Message{ - &component.TextDisplay{Content: "## Configuration / Rôles de niveaux"}, - &component.TextDisplay{Content: "Ces rôles seront donnés et retirés en fonction du niveau de chacun"}, - &component.Separator{}, - }, - } - slices.SortFunc(cfg.XpRoles, func(xp1, xp2 XpRole) int { - return int(xp2.XP) - int(xp1.XP) - }) - for _, r := range cfg.XpRoles { - container.Components = append(container.Components, &component.Section{ - Components: []component.Message{ - &component.TextDisplay{ - Content: fmt.Sprintf("<@&%s> - Niveau %d", r.RoleID, exp.Level(r.XP)), - }, - }, - Accessory: &component.Button{ - CustomID: dynamicid.FormatCustomID(XpRoleEdit, XpRoleId{ID: r.ID}), - Style: component.ButtonStyleSecondary, - Label: "Modifier", - }, - }) - } - container.Components = append(container.Components, - &component.ActionsRow{ - Components: []component.Message{ - &component.Button{ - CustomID: XpRoleNew, - Style: component.ButtonStylePrimary, - Label: "Nouveau rôle", - }, - }, - }, - &component.Separator{}, - &component.ActionsRow{ - Components: []component.Message{ - &component.Button{CustomID: "config", Style: component.ButtonStyleSecondary, Label: "Retour"}, - }, - }, +func (xp XpRole) Save(ctx context.Context) error { + _, err := common.GetDB(ctx).ExecContext( + ctx, + `INSERT INTO xp_roles (xp, role, guild_id) VALUES (?, ?, ?)`, + xp.XP, xp.RoleID, xp.GuildID, ) - - response := &interaction.Response{ - Type: types.InteractionResponseUpdateMessage, - Data: &interaction.ResponseData{ - Components: []component.Component{&container}, - Flags: channel.MessageFlagsIsComponentsV2, - }, - } - err := interaction.Respond(i, response).Do(ctx) - if err != nil { - bot.Logger(ctx).Error("sending config", "error", err) - } -} - -func HandleXpRoleNew(ctx context.Context, dg bot.Session, i *interaction.MessageComponent) { - one := 1 - resp := interaction.NewModalResponse(). - Title("Nouveau rôle de niveau"). - CustomID(XpRoleAdd). - AddComponent(&component.Label{ - Label: "Niveau", - Component: &component.TextInput{ - CustomID: "level", - Style: component.TextInputShort, - Placeholder: "5", - MinLength: 1, - MaxLength: 5, - Required: true, - }, - }). - AddComponent(&component.Label{ - Label: "Rôle", - Component: &component.SelectMenu{ - MenuType: types.SelectMenuRole, - CustomID: "role", - MinValues: &one, - MaxValues: one, - }, - }). - Response() - err := interaction.Respond(i.Interaction, resp).Do(ctx) - if err != nil { - bot.Logger(ctx).Error("sending modal to add", "error", err) - } -} - -func HandleXpRoleEdit(ctx context.Context, dg bot.Session, i *interaction.Interaction, params *XpRoleId) { - config := GetGuildConfig(ctx, i.GuildID) - id := params.ID - _, role := config.FindXpRoleID(id) - if role == nil { - HandleXpRole(ctx, dg, i) - return - } - - roleSelect := &component.SelectMenu{ - MenuType: types.SelectMenuRole, - CustomID: dynamicid.FormatCustomID(XpRoleEditRole, XpRoleId{ID: id}), - DefaultValues: []component.SelectMenuDefaultValue{ - {ID: role.RoleID, Type: types.SelectMenuDefaultValueRole}, - }, - } - - container := &component.Container{ - Components: []component.Message{ - &component.TextDisplay{Content: "## Configuration / Rôles de niveaux"}, - &component.Separator{}, - &component.Section{ - Components: []component.Message{ - &component.TextDisplay{Content: fmt.Sprintf("Niveau **%d**", exp.Level(role.XP))}, - }, - Accessory: &component.Button{ - CustomID: dynamicid.FormatCustomID(XpRoleEditLevelStart, XpRoleId{ID: id}), - Style: component.ButtonStyleSecondary, - Label: "Modifier", - }, - }, - &component.ActionsRow{Components: []component.Message{roleSelect}}, - &component.ActionsRow{Components: []component.Message{ - &component.Button{ - CustomID: dynamicid.FormatCustomID(XpRoleDel, XpRoleId{ID: id}), - Style: component.ButtonStyleDanger, - Label: "Supprimer", - }, - }}, - &component.Separator{}, - &component.ActionsRow{Components: []component.Message{ - &component.Button{Label: "Retour", CustomID: ModifyXpRole, Style: component.ButtonStyleSecondary}, - }}, - }, - } - - response := &interaction.Response{ - Type: types.InteractionResponseUpdateMessage, - Data: &interaction.ResponseData{ - Components: []component.Component{container}, - Flags: channel.MessageFlagsIsComponentsV2, - }, - } - - err := interaction.Respond(i, response).Do(ctx) - if err != nil { - bot.Logger(ctx).Error("sending xp_role config", "error", err) - } -} - -func HandleXpRoleEditRole(ctx context.Context, dg bot.Session, i *interaction.MessageComponent, params *XpRoleId) { - id := params.ID - role, err := strconv.ParseUint(i.Data.Values[0], 10, 64) - if err != nil { - // panic because must ensure that the role is valid before - panic(err) - } - cfg := GetGuildConfig(ctx, i.GuildID) - _, xpRole := cfg.FindXpRoleID(id) - if xpRole == nil { - err := interaction.Respond(i.Interaction, &interaction.Response{ - Type: types.InteractionResponseChannelMessageWithSource, - Data: &interaction.ResponseData{ - Flags: channel.MessageFlagsEphemeral, - Content: "Impossible de modifier le rôle. Peut-être a-t-il été supprimé ?", - }, - }).Do(ctx) - if err != nil { - bot.Logger(ctx).Error("sending unable to get role message", "error", err) - } - return - } - xpRole.RoleID = role - err = nil // common.GetDB(ctx).Save(xpRole).Error - if err != nil { - bot.Logger(ctx).Error("saving config", "error", err, "guild", i.GuildID, "id", id, "type", "add") - } - HandleXpRoleEdit(ctx, dg, i.Interaction, params) + return err } -func HandleXpRoleEditLevelStart(ctx context.Context, dg bot.Session, i *interaction.MessageComponent, params *XpRoleId) { - id := params.ID - cfg := GetGuildConfig(ctx, i.GuildID) - _, xpRole := cfg.FindXpRoleID(id) - if xpRole == nil { - err := interaction.Respond(i.Interaction, &interaction.Response{ - Type: types.InteractionResponseChannelMessageWithSource, - Data: &interaction.ResponseData{ - Flags: channel.MessageFlagsEphemeral, - Content: "Impossible de trouver le rôle. Peut-être a-t-il été supprimé ?", - }, - }).Do(ctx) - if err != nil { - bot.Logger(ctx).Error("sending unable to get role message", "error", err) - } - return - } - response := &interaction.Response{ - Type: types.InteractionResponseModal, - Data: &interaction.ResponseData{ - Title: "Modification du niveau lié au rôle", - CustomID: dynamicid.FormatCustomID(XpRoleEditLevel, XpRoleId{ID: id}), - Components: []component.Component{ - &component.Label{ - Label: "Nouveau niveau", - Component: &component.TextInput{ - Style: component.TextInputShort, - Required: true, - CustomID: "level", - MinLength: 1, - MaxLength: 5, - Placeholder: "5", - Value: strconv.FormatUint(uint64(exp.Level(xpRole.XP)), 10), - }, - }, - }, - }, - } - err := interaction.Respond(i.Interaction, response).Do(ctx) - if err != nil { - bot.Logger(ctx).Error("sending edit level modal", "error", err) - } -} - -func HandleXpRoleEditLevel(ctx context.Context, dg bot.Session, i *interaction.ModalSubmit, params *XpRoleId) { - id := params.ID - - levelInput := i.Data.Components[0].(*component.Label).Component.(*component.TextInput) - level, err := strconv.Atoi(levelInput.Value) - if err != nil || level < 0 { - resp := interaction.NewMessageResponse(). - IsEphemeral(). - Message(fmt.Sprintf("Le niveau doit être un nombre entier positif.\n-# Trouvé : %s", levelInput.Value)). - Response() - err = interaction.Respond(i.Interaction, resp).Do(ctx) - if err != nil { - bot.Logger(ctx).Error("sending bad number warning message", "error", err) - } - return - } - xp := exp.LevelXP(uint(level)) - - cfg := GetGuildConfig(ctx, i.GuildID) - _, xpRole := cfg.FindXpRoleID(id) - if xpRole == nil { - err = interaction.Respond(i.Interaction, &interaction.Response{ - Type: types.InteractionResponseChannelMessageWithSource, - Data: &interaction.ResponseData{ - Flags: channel.MessageFlagsEphemeral, - Content: "Impossible de modifier le rôle. Peut-être a-t-il été supprimé ?", - }, - }).Do(ctx) - if err != nil { - bot.Logger(ctx).Error("sending unable to modify role message", "error", err) - } - return - } - xpRole.XP = xp - err = nil //common.GetDB(ctx).Save(xpRole).Error - if err != nil { - bot.Logger(ctx).Error("saving config", "guild", i.GuildID, "id", id, "type", "edit") - } - HandleXpRoleEdit(ctx, dg, i.Interaction, params) -} - -func HandleXpRoleDel(ctx context.Context, dg bot.Session, i *interaction.MessageComponent, parameters *XpRoleId) { - id := parameters.ID - cfg := GetGuildConfig(ctx, i.GuildID) - _, role := cfg.FindXpRoleID(id) - if role == nil { - err := interaction.Respond(i.Interaction, &interaction.Response{ - Type: types.InteractionResponseChannelMessageWithSource, - Data: &interaction.ResponseData{ - Content: "Rôle introuvable. Peut-être a-t-il déjà été supprimé ?", - Flags: channel.MessageFlagsEphemeral, - }, - }).Do(ctx) - if err != nil { - bot.Logger(ctx).Error("sending role not found message", "error", err) - } - return - } - var err error = nil //common.GetDB(ctx).Delete(role).Error - if err != nil { - bot.Logger(ctx).Error("deleting entry", "error", err, "guild", i.GuildID, "id", id, "type", "del") - } - - HandleXpRole(ctx, dg, i.Interaction) -} - -func HandleXpRoleAdd(ctx context.Context, dg bot.Session, i *interaction.ModalSubmit) { - levelInput := i.Data.Components[0].(*component.Label).Component.(*component.TextInput) - - in, err := strconv.Atoi(levelInput.Value) - if err != nil || in < 0 { - resp := interaction.NewMessageResponse(). - IsEphemeral(). - Message(fmt.Sprintf("Le niveau doit être un nombre entier positif.\n-# Trouvé : %s", levelInput.Value)). - Response() - err = interaction.Respond(i.Interaction, resp).Do(ctx) - if err != nil { - bot.Logger(ctx).Error("sending bad number warning message", "error", err) - } - return - } - xp := exp.LevelXP(uint(in)) - - rawRoleId := i.Data.Components[1].(*component.Label).Component.(*component.SelectMenu).Values[0] - roleId, err := strconv.ParseUint(rawRoleId, 10, 64) - if err != nil { - // panic because select menu must ensure that the value is valid - panic(err) - } - - cfg := GetGuildConfig(ctx, i.GuildID) - cfg.XpRoles = append(cfg.XpRoles, XpRole{ - XP: xp, - RoleID: roleId, - }) - err = cfg.Save(ctx) - if err != nil { - bot.Logger(ctx).Error("saving config", "error", err, "role", roleId, "guild", i.GuildID) - return - } - - HandleXpRole(ctx, dg, i.Interaction) +func (xp XpRole) Delete(ctx context.Context) error { + _, err := common.GetDB(ctx).ExecContext( + ctx, + `DELETE FROM xp_roles WHERE xp = ? AND role = ? AND guild_id = ?`, + xp.XP, xp.RoleID, xp.GuildID, + ) + return err } diff --git a/config/xp_role_events.go b/config/xp_role_events.go new file mode 100644 index 0000000..cda90d5 --- /dev/null +++ b/config/xp_role_events.go @@ -0,0 +1,358 @@ +package config + +import ( + "context" + "fmt" + "slices" + "strconv" + + "git.anhgelus.world/anhgelus/les-copaings-bot/dynamicid" + "git.anhgelus.world/anhgelus/les-copaings-bot/exp" + "github.com/nyttikord/gokord/bot" + "github.com/nyttikord/gokord/channel" + "github.com/nyttikord/gokord/component" + "github.com/nyttikord/gokord/discord/types" + "github.com/nyttikord/gokord/interaction" +) + +type XpRoleId struct { + ID uint64 +} + +const ( + ModifyXpRole = "xp_role" + XpRoleNew = "xp_role_add" + XpRoleAdd = "xp_role_add_level" + XpRoleEdit = `xp_role_edit` + XpRoleEditLevel = `xp_role_edit_level` + XpRoleEditLevelStart = `xp_role_edit_level_start` + XpRoleEditRole = `xp_role_edit_role` + XpRoleDel = `xp_role_del` +) + +func HandleXpRole(ctx context.Context, dg bot.Session, i *interaction.Interaction) { + cfg := GetGuild(ctx, i.GuildID) + container := component.Container{ + Components: []component.Message{ + &component.TextDisplay{Content: "## Configuration / Rôles de niveaux"}, + &component.TextDisplay{Content: "Ces rôles seront donnés et retirés en fonction du niveau de chacun"}, + &component.Separator{}, + }, + } + slices.SortFunc(cfg.XpRoles, func(xp1, xp2 XpRole) int { + return int(xp2.XP) - int(xp1.XP) + }) + for _, r := range cfg.XpRoles { + container.Components = append(container.Components, &component.Section{ + Components: []component.Message{ + &component.TextDisplay{ + Content: fmt.Sprintf("<@&%d> - Niveau %d", r.RoleID, exp.Level(r.XP)), + }, + }, + Accessory: &component.Button{ + CustomID: dynamicid.FormatCustomID(XpRoleEdit, XpRoleId{ID: r.RoleID}), + Style: component.ButtonStyleSecondary, + Label: "Modifier", + }, + }) + } + container.Components = append(container.Components, + &component.ActionsRow{ + Components: []component.Message{ + &component.Button{ + CustomID: XpRoleNew, + Style: component.ButtonStylePrimary, + Label: "Nouveau rôle", + }, + }, + }, + &component.Separator{}, + &component.ActionsRow{ + Components: []component.Message{ + &component.Button{CustomID: "config", Style: component.ButtonStyleSecondary, Label: "Retour"}, + }, + }, + ) + + response := &interaction.Response{ + Type: types.InteractionResponseUpdateMessage, + Data: &interaction.ResponseData{ + Components: []component.Component{&container}, + Flags: channel.MessageFlagsIsComponentsV2, + }, + } + err := interaction.Respond(i, response).Do(ctx) + if err != nil { + bot.Logger(ctx).Error("sending config", "error", err) + } +} + +func HandleXpRoleNew(ctx context.Context, dg bot.Session, i *interaction.MessageComponent) { + one := 1 + resp := interaction.NewModalResponse(). + Title("Nouveau rôle de niveau"). + CustomID(XpRoleAdd). + AddComponent(&component.Label{ + Label: "Niveau", + Component: &component.TextInput{ + CustomID: "level", + Style: component.TextInputShort, + Placeholder: "5", + MinLength: 1, + MaxLength: 5, + Required: true, + }, + }). + AddComponent(&component.Label{ + Label: "Rôle", + Component: &component.SelectMenu{ + MenuType: types.SelectMenuRole, + CustomID: "role", + MinValues: &one, + MaxValues: one, + }, + }). + Response() + err := interaction.Respond(i.Interaction, resp).Do(ctx) + if err != nil { + bot.Logger(ctx).Error("sending modal to add", "error", err) + } +} + +func HandleXpRoleEdit(ctx context.Context, dg bot.Session, i *interaction.Interaction, params *XpRoleId) { + config := GetGuild(ctx, i.GuildID) + id := params.ID + _, role := config.FindXpRole(id) + if role == nil { + HandleXpRole(ctx, dg, i) + return + } + + roleSelect := &component.SelectMenu{ + MenuType: types.SelectMenuRole, + CustomID: dynamicid.FormatCustomID(XpRoleEditRole, XpRoleId{ID: id}), + DefaultValues: []component.SelectMenuDefaultValue{ + {ID: role.RoleID, Type: types.SelectMenuDefaultValueRole}, + }, + } + + container := &component.Container{ + Components: []component.Message{ + &component.TextDisplay{Content: "## Configuration / Rôles de niveaux"}, + &component.Separator{}, + &component.Section{ + Components: []component.Message{ + &component.TextDisplay{Content: fmt.Sprintf("Niveau **%d**", exp.Level(role.XP))}, + }, + Accessory: &component.Button{ + CustomID: dynamicid.FormatCustomID(XpRoleEditLevelStart, XpRoleId{ID: id}), + Style: component.ButtonStyleSecondary, + Label: "Modifier", + }, + }, + &component.ActionsRow{Components: []component.Message{roleSelect}}, + &component.ActionsRow{Components: []component.Message{ + &component.Button{ + CustomID: dynamicid.FormatCustomID(XpRoleDel, XpRoleId{ID: id}), + Style: component.ButtonStyleDanger, + Label: "Supprimer", + }, + }}, + &component.Separator{}, + &component.ActionsRow{Components: []component.Message{ + &component.Button{Label: "Retour", CustomID: ModifyXpRole, Style: component.ButtonStyleSecondary}, + }}, + }, + } + + response := &interaction.Response{ + Type: types.InteractionResponseUpdateMessage, + Data: &interaction.ResponseData{ + Components: []component.Component{container}, + Flags: channel.MessageFlagsIsComponentsV2, + }, + } + + err := interaction.Respond(i, response).Do(ctx) + if err != nil { + bot.Logger(ctx).Error("sending xp_role config", "error", err) + } +} + +func HandleXpRoleEditRole(ctx context.Context, dg bot.Session, i *interaction.MessageComponent, params *XpRoleId) { + id := params.ID + role, err := strconv.ParseUint(i.Data.Values[0], 10, 64) + if err != nil { + // panic because must ensure that the role is valid before + panic(err) + } + cfg := GetGuild(ctx, i.GuildID) + _, xpRole := cfg.FindXpRole(id) + if xpRole == nil { + err := interaction.Respond(i.Interaction, &interaction.Response{ + Type: types.InteractionResponseChannelMessageWithSource, + Data: &interaction.ResponseData{ + Flags: channel.MessageFlagsEphemeral, + Content: "Impossible de modifier le rôle. Peut-être a-t-il été supprimé ?", + }, + }).Do(ctx) + if err != nil { + bot.Logger(ctx).Error("sending unable to get role message", "error", err) + } + return + } + xpRole.RoleID = role + err = xpRole.Save(ctx) + if err != nil { + bot.Logger(ctx).Error("saving config", "error", err, "guild", i.GuildID, "id", id, "type", "add") + } + HandleXpRoleEdit(ctx, dg, i.Interaction, params) +} + +func HandleXpRoleEditLevelStart(ctx context.Context, dg bot.Session, i *interaction.MessageComponent, params *XpRoleId) { + id := params.ID + cfg := GetGuild(ctx, i.GuildID) + _, xpRole := cfg.FindXpRole(id) + if xpRole == nil { + err := interaction.Respond(i.Interaction, &interaction.Response{ + Type: types.InteractionResponseChannelMessageWithSource, + Data: &interaction.ResponseData{ + Flags: channel.MessageFlagsEphemeral, + Content: "Impossible de trouver le rôle. Peut-être a-t-il été supprimé ?", + }, + }).Do(ctx) + if err != nil { + bot.Logger(ctx).Error("sending unable to get role message", "error", err) + } + return + } + response := &interaction.Response{ + Type: types.InteractionResponseModal, + Data: &interaction.ResponseData{ + Title: "Modification du niveau lié au rôle", + CustomID: dynamicid.FormatCustomID(XpRoleEditLevel, XpRoleId{ID: id}), + Components: []component.Component{ + &component.Label{ + Label: "Nouveau niveau", + Component: &component.TextInput{ + Style: component.TextInputShort, + Required: true, + CustomID: "level", + MinLength: 1, + MaxLength: 5, + Placeholder: "5", + Value: strconv.FormatUint(uint64(exp.Level(xpRole.XP)), 10), + }, + }, + }, + }, + } + err := interaction.Respond(i.Interaction, response).Do(ctx) + if err != nil { + bot.Logger(ctx).Error("sending edit level modal", "error", err) + } +} + +func HandleXpRoleEditLevel(ctx context.Context, dg bot.Session, i *interaction.ModalSubmit, params *XpRoleId) { + id := params.ID + + levelInput := i.Data.Components[0].(*component.Label).Component.(*component.TextInput) + level, err := strconv.Atoi(levelInput.Value) + if err != nil || level < 0 { + resp := interaction.NewMessageResponse(). + IsEphemeral(). + Message(fmt.Sprintf("Le niveau doit être un nombre entier positif.\n-# Trouvé : %s", levelInput.Value)). + Response() + err = interaction.Respond(i.Interaction, resp).Do(ctx) + if err != nil { + bot.Logger(ctx).Error("sending bad number warning message", "error", err) + } + return + } + xp := exp.LevelXP(uint(level)) + + cfg := GetGuild(ctx, i.GuildID) + _, xpRole := cfg.FindXpRole(id) + if xpRole == nil { + err = interaction.Respond(i.Interaction, &interaction.Response{ + Type: types.InteractionResponseChannelMessageWithSource, + Data: &interaction.ResponseData{ + Flags: channel.MessageFlagsEphemeral, + Content: "Impossible de modifier le rôle. Peut-être a-t-il été supprimé ?", + }, + }).Do(ctx) + if err != nil { + bot.Logger(ctx).Error("sending unable to modify role message", "error", err) + } + return + } + xpRole.XP = xp + err = xpRole.Save(ctx) + if err != nil { + bot.Logger(ctx).Error("saving config", "guild", i.GuildID, "id", id, "type", "edit") + } + HandleXpRoleEdit(ctx, dg, i.Interaction, params) +} + +func HandleXpRoleDel(ctx context.Context, dg bot.Session, i *interaction.MessageComponent, parameters *XpRoleId) { + id := parameters.ID + cfg := GetGuild(ctx, i.GuildID) + _, role := cfg.FindXpRole(id) + if role == nil { + err := interaction.Respond(i.Interaction, &interaction.Response{ + Type: types.InteractionResponseChannelMessageWithSource, + Data: &interaction.ResponseData{ + Content: "Rôle introuvable. Peut-être a-t-il déjà été supprimé ?", + Flags: channel.MessageFlagsEphemeral, + }, + }).Do(ctx) + if err != nil { + bot.Logger(ctx).Error("sending role not found message", "error", err) + } + return + } + err := role.Delete(ctx) + if err != nil { + bot.Logger(ctx).Error("deleting entry", "error", err, "guild", i.GuildID, "id", id, "type", "del") + } + + HandleXpRole(ctx, dg, i.Interaction) +} + +func HandleXpRoleAdd(ctx context.Context, dg bot.Session, i *interaction.ModalSubmit) { + levelInput := i.Data.Components[0].(*component.Label).Component.(*component.TextInput) + + in, err := strconv.Atoi(levelInput.Value) + if err != nil || in < 0 { + resp := interaction.NewMessageResponse(). + IsEphemeral(). + Message(fmt.Sprintf("Le niveau doit être un nombre entier positif.\n-# Trouvé : %s", levelInput.Value)). + Response() + err = interaction.Respond(i.Interaction, resp).Do(ctx) + if err != nil { + bot.Logger(ctx).Error("sending bad number warning message", "error", err) + } + return + } + xp := exp.LevelXP(uint(in)) + + rawRoleId := i.Data.Components[1].(*component.Label).Component.(*component.SelectMenu).Values[0] + roleId, err := strconv.ParseUint(rawRoleId, 10, 64) + if err != nil { + // panic because select menu must ensure that the value is valid + panic(err) + } + + cfg := GetGuild(ctx, i.GuildID) + cfg.XpRoles = append(cfg.XpRoles, XpRole{ + XP: xp, + RoleID: roleId, + }) + err = cfg.Save(ctx) + if err != nil { + bot.Logger(ctx).Error("saving config", "error", err, "role", roleId, "guild", i.GuildID) + return + } + + HandleXpRole(ctx, dg, i.Interaction) +} |
