diff --git a/.gitignore b/.gitignore index 90253eb..8c74cc6 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ cake4everybot *env.yaml # runtime config files (as described in config.yml) +modules/secretsanta/backlist.json modules/secretsanta/players.json twitch/prizes.json twitch/times.json diff --git a/config.yaml b/config.yaml index a387eac..20748be 100644 --- a/config.yaml +++ b/config.yaml @@ -37,6 +37,9 @@ event: secretsanta: # the filepath for the players players: modules/secretsanta/players.json + # the filepath for the blacklist + # the blacklist maps a user ID to a list of user IDs that should not be matched + blacklist: modules/secretsanta/backlist.json twitch_giveaway: # The amount of points a single giveaway ticket costs. diff --git a/data/lang/de.yaml b/data/lang/de.yaml index 6f48b83..9304531 100644 --- a/data/lang/de.yaml +++ b/data/lang/de.yaml @@ -132,7 +132,9 @@ discord.command: msg.setup.no_reactions: Diese Nachricht hat keine Reaktionen. Nur Leute, die mit %s reagiert haben, werden eingeschlossen. msg.setup.not_enough_reactions: Nicht genug Reaktionen um zu starten. Es werden mindestens %d Reaktionen benötigt. msg.setup.users: Teilnehmer + msg.setup.match_error: Fehler beim Auslosen msg.setup.invite: Einladen + msg.setup.invite.error: "Einladung(en) konnte(n) nicht verschickt werden an:%s" msg.setup.error: "%d Einladungen konnten nicht verschickt werden." msg.setup.success: Einladungen wurden verschickt! diff --git a/data/lang/en.yaml b/data/lang/en.yaml index 07adc80..e2e4540 100644 --- a/data/lang/en.yaml +++ b/data/lang/en.yaml @@ -132,7 +132,9 @@ discord.command: msg.setup.no_reactions: This message doesn't have any vote reactions. Only members who reated with %s are included. msg.setup.not_enough_reactions: Not enough votes to start a game. At least %d votes are required. msg.setup.users: Members + msg.setup.match_error: Failed to match msg.setup.invite: Invite + msg.setup.invite.error: "Failed to send invite(s) to:%s" msg.setup.error: Failed to send %d invites. msg.setup.success: Invites sent! diff --git a/modules/secretsanta/handleComponentSetup.go b/modules/secretsanta/handleComponentSetup.go index b2d8b79..b037c81 100644 --- a/modules/secretsanta/handleComponentSetup.go +++ b/modules/secretsanta/handleComponentSetup.go @@ -26,7 +26,12 @@ func (c Component) handleSetupInvite() { return } c.ReplyDeferedHidden() - players = derangementMatch(players) + players, err = derangementMatch(players) + if err != nil { + log.Printf("ERROR: could not match players: %+v", err) + c.ReplySimpleEmbed(0xFF0000, lang.GetDefault(tp+"msg.setup.match_error")) + return + } inviteMessage := &discordgo.MessageSend{ Embeds: make([]*discordgo.MessageEmbed, 1), @@ -67,11 +72,10 @@ func (c Component) handleSetupInvite() { continue } player.MessageID = msg.ID - log.Printf("Sent invite to user %s in channel %s", player.User.ID, DMChannel.ID) } if failedToSend != "" { - c.ReplyHiddenf("Failed to send invites to:%s", failedToSend) + c.ReplyHiddenSimpleEmbedf(0xFF0000, lang.GetDefault(tp+"msg.setup.invite.error"), failedToSend) return } @@ -82,5 +86,5 @@ func (c Component) handleSetupInvite() { return } - c.ReplyHidden(lang.GetDefault(tp + "msg.setup.success")) + c.ReplyHiddenSimpleEmbed(0x690042, lang.GetDefault(tp+"msg.setup.success")) } diff --git a/modules/secretsanta/secretsantabase.go b/modules/secretsanta/secretsantabase.go index badcaf8..1687a3b 100644 --- a/modules/secretsanta/secretsantabase.go +++ b/modules/secretsanta/secretsantabase.go @@ -123,6 +123,7 @@ func (player *player) InviteEmbed(s *discordgo.Session) (e *discordgo.MessageEmb e = &discordgo.MessageEmbed{ Title: lang.GetDefault(tp + "msg.invite.title"), Description: lang.GetDefault(tp + "msg.invite.description"), + Color: 0x690042, Fields: []*discordgo.MessageEmbedField{ { Name: lang.GetDefault(tp + "msg.invite.set_address.match"), @@ -205,7 +206,7 @@ func (allPlayersUnresolved AllPlayersUnresolved) Resolve(s *discordgo.Session) ( } // derangementMatch matches the players in a way that no one gets matched to themselves. -func derangementMatch(players map[string]*player) map[string]*player { +func derangementMatch(players map[string]*player) (map[string]*player, error) { n := len(players) playersSlice := make([]*player, 0, n) for _, p := range players { @@ -218,5 +219,29 @@ func derangementMatch(players map[string]*player) map[string]*player { playersSlice[i].Match, playersSlice[j].Match = playersSlice[j].Match, playersSlice[i].Match } - return players + blacklistPath := viper.GetString("event.secretsanta.blacklist") + blacklistData, err := os.ReadFile(blacklistPath) + if err != nil { + if !os.IsNotExist(err) { + return nil, fmt.Errorf("read blacklist file: %w", err) + } + err = os.WriteFile(blacklistPath, []byte("{}"), 0644) + if err != nil { + return nil, fmt.Errorf("write blacklist file: %w", err) + } + return players, nil + } + + blacklist := make(map[string][]string) + err = json.Unmarshal(blacklistData, &blacklist) + if err != nil { + return nil, fmt.Errorf("parse blacklist file: %w", err) + } + for id, blacklisted := range blacklist { + if player, ok := players[id]; ok && util.ContainsString(blacklisted, player.Match.User.ID) { + return nil, fmt.Errorf("%s has a blacklisted match", player.DisplayName()) + } + } + + return players, nil }