Skip to content

Commit

Permalink
feat(Random): added team generator
Browse files Browse the repository at this point in the history
  • Loading branch information
Kesuaheli committed Dec 29, 2024
1 parent b194de9 commit 1aec095
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 8 deletions.
17 changes: 9 additions & 8 deletions config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ event:
name:
#id:
#animated: true
repeat:
name: 🔁
#id:
#animated: true

# Emoji for entering the advent calendar giveaway
adventcalendar: vote.check
random.coin.heads:
Expand All @@ -75,14 +80,10 @@ event:
name: 🪙
#id:
#animated: true
random.coin.reflip:
name: 🔄
#id:
#animated: true
random.dice.reroll:
name: 🔄
#id:
#animated: true
random.coin.reflip: repeat
random.dice.reroll: repeat
random.teams.resplit_size: repeat
random.teams.resplit_amount: repeat
secretsanta: vote.yes
secretsanta.invite.show_match:
name: 🎁
Expand Down
12 changes: 12 additions & 0 deletions data/lang/de.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ discord.command:
no: Nein
msg.self_hidden: Warum ist das unsichtbar?
msg.self_hidden.desc: Da du deinen Geburtstag als nicht sichtbar eingetragen hast, kannst diese Nachricht nur du sehen. Du kannst diese Nachricht nun schließen.
msg.page: Seite %d/%d

birthday:
base: geburtstag
Expand Down Expand Up @@ -126,6 +127,7 @@ discord.command:
random:
base: zufall
base.description: Einige nützliche Zufallsgenerator Befehle
display: Zufallsgenerator

option.coin: münze
option.coin.description: Wirf eine Münze
Expand All @@ -135,8 +137,18 @@ discord.command:
option.dice.option.range.description: "Wie viele Zahlen soll der Würfel haben? (Standard: 6)"
option.teams: gruppen
option.teams.description: Teile Gruppen zu
option.teams.option.members: mitglieder
option.teams.option.members.description: Welche Mitglieder sollen in Gruppen geteilt werden?
option.teams.option.team_size: gruppengröße
option.teams.option.team_size.description: Wie viele Mitglieder sollen in einer Gruppe sein?
option.teams.option.team_amount: gruppenanzahl
option.teams.option.team_amount.description: Wie viele Gruppen sollen erstellt werden?

msg.dice.roll: You rolled a %d
msg.teams.missing_option: "Fehler: Du musst entweder die Gruppengröße oder die Gruppenanzahl angeben!"
msg.teams.multiple_options: "Fehler: Du kannst nicht gleichzeitig die Gruppengröße und die Gruppenanzahl angeben!"
msg.teams.title: Gruppen
msg.teams.team: Gruppe %d

secretsanta:
base: Wichteln
Expand Down
12 changes: 12 additions & 0 deletions data/lang/en.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ discord.command:
no: No
msg.self_hidden: Why is this invisible?
msg.self_hidden.desc: Since you've set your birthday to not be visible, this message is also only visible to you. You can close this message now.
msg.page: Page %d/%d

birthday:
base: birthday
Expand Down Expand Up @@ -126,6 +127,7 @@ discord.command:
random:
base: random
base.description: Some useful Random Number Generator commands
display: Random Generator

option.coin: coin
option.coin.description: Flip a coin
Expand All @@ -135,8 +137,18 @@ discord.command:
option.dice.option.range.description: "How many sides does the dice have? (Default: 6)"
option.teams: teams
option.teams.description: Generate teams
option.teams.option.members: members
option.teams.option.members.description: Which members should be splitted into teams?
option.teams.option.team_size: size
option.teams.option.team_size.description: How many members should be in each team?
option.teams.option.team_amount: amount
option.teams.option.team_amount.description: How many teams should be created?

msg.dice.roll: You rolled a %d
msg.teams.missing_option: "Error: You need to specify either the team size or the team amount!"
msg.teams.multiple_options: "Error: You can't specify both the team size and the team amount at the same time!"
msg.teams.title: Teams
msg.teams.team: Team %d

secretsanta:
base: Secret Santa
Expand Down
205 changes: 205 additions & 0 deletions modules/random/handleSubcommandTeams.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package random
import (
"cake4everybot/data/lang"
"cake4everybot/util"
"fmt"
"math/rand/v2"

"github.com/bwmarrin/discordgo"
)
Expand All @@ -12,6 +14,10 @@ type subcommandTeams struct {
randomBase
*Chat
data *discordgo.ApplicationCommandInteractionDataOption

members *discordgo.ApplicationCommandInteractionDataOption // required
teamSize *discordgo.ApplicationCommandInteractionDataOption // optional
teamAmount *discordgo.ApplicationCommandInteractionDataOption // optional
}

func (rb randomBase) subcommandTeams() subcommandTeams {
Expand All @@ -32,17 +38,216 @@ func (cmd *Chat) subcommandTeams() subcommandTeams {
}

func (cmd subcommandTeams) appCmd() *discordgo.ApplicationCommandOption {
options := []*discordgo.ApplicationCommandOption{
cmd.optionMembers(),
cmd.optionTeamSize(),
cmd.optionTeamAmount(),
}

return &discordgo.ApplicationCommandOption{
Type: discordgo.ApplicationCommandOptionSubCommand,
Name: lang.GetDefault(tp + "option.teams"),
NameLocalizations: *util.TranslateLocalization(tp + "option.teams"),
Description: lang.GetDefault(tp + "option.teams.description"),
DescriptionLocalizations: *util.TranslateLocalization(tp + "option.teams.description"),
Options: options,
}
}

func (cmd subcommandTeams) optionMembers() *discordgo.ApplicationCommandOption {
return &discordgo.ApplicationCommandOption{
Type: discordgo.ApplicationCommandOptionRole,
Name: lang.GetDefault(tp + "option.teams.option.members"),
NameLocalizations: *util.TranslateLocalization(tp + "option.teams.option.members"),
Description: lang.GetDefault(tp + "option.teams.option.members.description"),
DescriptionLocalizations: *util.TranslateLocalization(tp + "option.teams.option.members.description"),
Required: true,
}
}

func (cmd subcommandTeams) optionTeamSize() *discordgo.ApplicationCommandOption {
minValueTwo := float64(2)
return &discordgo.ApplicationCommandOption{
Type: discordgo.ApplicationCommandOptionInteger,
Name: lang.GetDefault(tp + "option.teams.option.team_size"),
NameLocalizations: *util.TranslateLocalization(tp + "option.teams.option.team_size"),
Description: lang.GetDefault(tp + "option.teams.option.team_size.description"),
DescriptionLocalizations: *util.TranslateLocalization(tp + "option.teams.option.team_size.description"),
Required: false,
MinValue: &minValueTwo,
}
}

func (cmd subcommandTeams) optionTeamAmount() *discordgo.ApplicationCommandOption {
minValueOne := float64(1)
return &discordgo.ApplicationCommandOption{
Type: discordgo.ApplicationCommandOptionInteger,
Name: lang.GetDefault(tp + "option.teams.option.team_amount"),
NameLocalizations: *util.TranslateLocalization(tp + "option.teams.option.team_amount"),
Description: lang.GetDefault(tp + "option.teams.option.team_amount.description"),
DescriptionLocalizations: *util.TranslateLocalization(tp + "option.teams.option.team_amount.description"),
Required: false,
MinValue: &minValueOne,
}
}

func (cmd subcommandTeams) handle() {
for _, opt := range cmd.data.Options {
switch opt.Name {
case lang.GetDefault(tp + "option.teams.option.members"):
cmd.members = opt
case lang.GetDefault(tp + "option.teams.option.team_size"):
cmd.teamSize = opt
case lang.GetDefault(tp + "option.teams.option.team_amount"):
cmd.teamAmount = opt
}
}

if cmd.teamSize == nil && cmd.teamAmount == nil {
cmd.ReplyHidden(lang.GetDefault(tp + "msg.teams.missing_option"))
return
} else if cmd.teamSize != nil && cmd.teamAmount != nil {
cmd.ReplyHidden(lang.GetDefault(tp + "msg.teams.multiple_options"))
return
}

var (
memberRole = cmd.members.RoleValue(cmd.Session, cmd.Interaction.GuildID)
teamSize int
teamAmount int
)
if cmd.teamSize != nil {
teamSize = int(cmd.teamSize.IntValue())
} else {
teamAmount = int(cmd.teamAmount.IntValue())
}

members, err := cmd.getMembersWithRole(memberRole.ID)
if err != nil {
log.Printf("ERROR: could not get members with role '%s/%s' (%s): %+v", cmd.Interaction.GuildID, memberRole.ID, memberRole.Name, err)
cmd.ReplyError()
}

data := &discordgo.InteractionResponseData{}
if cmd.teamSize != nil {
data = cmd.splitTeamsSize(members, teamSize)
} else {
data = cmd.splitTeamsN(members, teamAmount)
}

cmd.ReplyComplex(data)
}

func (cmd subcommandTeams) handleComponent(ids []string) {
}

// splitTeamsSize splits the members into teams of a maximum size teamSize.
//
// The last team might be smaller.
func (cmd subcommandTeams) splitTeamsSize(members []*discordgo.Member, teamSize int) (data *discordgo.InteractionResponseData) {
data = &discordgo.InteractionResponseData{}

rand.Shuffle(len(members), func(i, j int) {
members[i], members[j] = members[j], members[i]
})

var teams [][]*discordgo.Member
for i := 0; i < len(members); i += teamSize {
end := i + teamSize
if end > len(members) {
end = len(members)
}
teams = append(teams, members[i:end])
}
data.Embeds = teamsEmbed(cmd.Session, teams)

resplitButton := util.CreateButtonComponent(
fmt.Sprintf("random.teams.resplit_size.%d", teamSize),
"",
discordgo.PrimaryButton,
util.GetConfigComponentEmoji("random.teams.resplit_size"))
data.Components = []discordgo.MessageComponent{discordgo.ActionsRow{Components: []discordgo.MessageComponent{resplitButton}}}

return data
}

// splitTeamsN splits the members into n teams.
func (cmd subcommandTeams) splitTeamsN(members []*discordgo.Member, n int) (data *discordgo.InteractionResponseData) {
data = &discordgo.InteractionResponseData{}

rand.Shuffle(len(members), func(i, j int) {
members[i], members[j] = members[j], members[i]
})

if n > len(members) {
n = len(members)
}
var teams [][]*discordgo.Member = make([][]*discordgo.Member, n)
for i, member := range members {
teams[i%n] = append(teams[i%n], member)
}
data.Embeds = teamsEmbed(cmd.Session, teams)

resplitButton := util.CreateButtonComponent(
"random.teams.resplit_amount",
"",
discordgo.PrimaryButton,
util.GetConfigComponentEmoji("random.teams.resplit_amount"))
data.Components = []discordgo.MessageComponent{discordgo.ActionsRow{Components: []discordgo.MessageComponent{resplitButton}}}

return data
}

func (cmd subcommandTeams) getMembersWithRole(roleID string) ([]*discordgo.Member, error) {
var membersWithRole []*discordgo.Member
var after string

for {
members, err := cmd.Session.GuildMembers(cmd.Interaction.GuildID, after, 1000)
if err != nil {
return nil, err
}
if len(members) == 0 {
break
}

for _, member := range members {
if util.ContainsString(member.Roles, roleID) {
membersWithRole = append(membersWithRole, member)
}
}

after = members[len(members)-1].User.ID
}

return membersWithRole, nil
}

// teamsEmbed returns one or more embeds listing the given teams.
func teamsEmbed(s *discordgo.Session, teams [][]*discordgo.Member) (embeds []*discordgo.MessageEmbed) {
embeds = util.SplitToEmbedFields(s, teams, 0xFFD700, tp+"display", teamEmbed)
embeds[0].Title = lang.GetDefault(tp + "msg.teams.title")

if len(embeds[0].Fields) == 1 {
embeds[0].Description = embeds[0].Fields[0].Value
embeds[0].Fields = nil
}

return embeds
}

// teamEmbed returns the given team as an embed field.
//
// i is the team number (0-indexed) used for the field name.
func teamEmbed(team []*discordgo.Member, i int) *discordgo.MessageEmbedField {
var value string
for i, member := range team {
value += fmt.Sprintf("%d. %s\n", i, member.Mention())
}

return &discordgo.MessageEmbedField{
Name: fmt.Sprintf(lang.GetDefault(tp+"msg.teams.team"), i+1),
Value: value,
Inline: true,
}
}

0 comments on commit 1aec095

Please sign in to comment.