From 21c6e7a748b8dc838b0378ceb5f63233fa0dac0f Mon Sep 17 00:00:00 2001 From: Seraphim Strub Date: Wed, 8 Mar 2023 21:39:42 +0100 Subject: [PATCH] refactor vcbot to packages --- cmd/vcbot/config/apple.go | 40 ++++++ cmd/vcbot/config/config.go | 14 +++ cmd/vcbot/event/event.go | 63 ++++++++++ cmd/vcbot/helper/helper.go | 137 ++++++++++++++++++++ cmd/vcbot/main.go | 248 ++----------------------------------- 5 files changed, 265 insertions(+), 237 deletions(-) create mode 100644 cmd/vcbot/config/apple.go create mode 100644 cmd/vcbot/config/config.go create mode 100644 cmd/vcbot/event/event.go create mode 100644 cmd/vcbot/helper/helper.go diff --git a/cmd/vcbot/config/apple.go b/cmd/vcbot/config/apple.go new file mode 100644 index 0000000..6b2d0c0 --- /dev/null +++ b/cmd/vcbot/config/apple.go @@ -0,0 +1,40 @@ +package config + +import ( + "encoding/json" + "github.com/disgoorg/log" + "io" + "net/http" +) + +var ( + AppleApi = "https://grow.rievo.page/applelist/initial.json" + AppleList []string +) + +type appleApiBody struct { + Status string `json:"status"` + Type string `json:"type"` + Version string `json:"version"` + Body []string `json:"body"` +} + +func LoadAppleList() error { + res, err := http.Get(AppleApi) + if err != nil { + return err + } + body, err := io.ReadAll(res.Body) + if err != nil { + return err + } + + var data appleApiBody + err = json.Unmarshal(body, &data) + if err != nil { + return err + } + AppleList = data.Body + log.Debug("loaded apple list of lenght: ", len(AppleList)) + return nil +} diff --git a/cmd/vcbot/config/config.go b/cmd/vcbot/config/config.go new file mode 100644 index 0000000..79397f5 --- /dev/null +++ b/cmd/vcbot/config/config.go @@ -0,0 +1,14 @@ +package config + +import ( + "github.com/disgoorg/snowflake/v2" + "os" +) + +var ( + Token = os.Getenv("disgo_token") + RegisterGuildID = snowflake.GetEnv("disgo_guild_id") + ChannelVoiceGroupID snowflake.ID + ChannelVoiceLogID snowflake.ID + ChannelVoicePrefix = "🔉 - " +) diff --git a/cmd/vcbot/event/event.go b/cmd/vcbot/event/event.go new file mode 100644 index 0000000..e79f635 --- /dev/null +++ b/cmd/vcbot/event/event.go @@ -0,0 +1,63 @@ +package event + +import ( + "fmt" + "github.com/disgoorg/disgo/events" + "grow.rievo.dev/discordBots/cmd/vcbot/helper" +) + +func JoinEvent(event *events.GuildVoiceJoin) { + event.Client().Logger().Debug("JoinEvent") + channelId := *event.VoiceState.ChannelID + channel, _ := event.Client().Rest().GetChannel(channelId) + channelName := channel.Name() + userName := event.Member.EffectiveName() + userMention := event.Member.Mention() + + message := fmt.Sprintf("**CHANNEL LOG:** %v joined %v", userName, channelName) + messageEdit := fmt.Sprintf("**CHANNEL LOG:** %v joined %v", userMention, channelName) + go helper.SendNoMention(event.Client(), message, messageEdit) + + helper.UpdateVoiceChannels(event.Client(), false) +} + +func MoveEvent(event *events.GuildVoiceMove) { + if event.OldVoiceState.ChannelID.String() == event.VoiceState.ChannelID.String() { + // no that's not a move event + return + } + event.Client().Logger().Debug("MoveEvent") + channelOldId := *event.OldVoiceState.ChannelID + channelOld, _ := event.Client().Rest().GetChannel(channelOldId) + channelOldName := channelOld.Name() + channelId := *event.VoiceState.ChannelID + channel, _ := event.Client().Rest().GetChannel(channelId) + channelName := channel.Name() + userName := event.Member.EffectiveName() + userMention := event.Member.Mention() + + message := fmt.Sprintf("**CHANNEL LOG:** %v moved from %v to %v", userName, channelOldName, channelName) + messageEdit := fmt.Sprintf("**CHANNEL LOG:** %v moved from %v to %v", userMention, channelOldName, channelName) + go helper.SendNoMention(event.Client(), message, messageEdit) + + helper.UpdateVoiceChannels(event.Client(), false) +} + +func LeaveEvent(event *events.GuildVoiceLeave) { + event.Client().Logger().Debug("LeaveEvent") + if event.OldVoiceState.ChannelID == nil { + event.Client().Logger().Error("OldVoiceState.ChannelID missing") + return + } + channelOldId := *event.OldVoiceState.ChannelID + channelOld, _ := event.Client().Rest().GetChannel(channelOldId) + channelOldName := channelOld.Name() + userName := event.Member.EffectiveName() + userMention := event.Member.Mention() + + message := fmt.Sprintf("**CHANNEL LOG:** %v left %v", userName, channelOldName) + messageEdit := fmt.Sprintf("**CHANNEL LOG:** %v left %v", userMention, channelOldName) + go helper.SendNoMention(event.Client(), message, messageEdit) + + helper.UpdateVoiceChannels(event.Client(), true) +} diff --git a/cmd/vcbot/helper/helper.go b/cmd/vcbot/helper/helper.go new file mode 100644 index 0000000..0b2ae52 --- /dev/null +++ b/cmd/vcbot/helper/helper.go @@ -0,0 +1,137 @@ +package helper + +import ( + "errors" + "fmt" + "github.com/disgoorg/disgo/bot" + "github.com/disgoorg/disgo/discord" + "github.com/disgoorg/snowflake/v2" + "grow.rievo.dev/discordBots/cmd/vcbot/config" + "math/rand" +) + +func SendNoMention(client bot.Client, msg, msgEdit string) { + message, err := client.Rest().CreateMessage(config.ChannelVoiceLogID, discord.NewMessageCreateBuilder(). + SetContent(msg).Build()) + if err != nil { + client.Logger().Error("unable to send channel message: ", err) + return + } + _, err = client.Rest().UpdateMessage(config.ChannelVoiceLogID, + message.ID, + discord.NewMessageUpdateBuilder().ClearContent().SetContent(msgEdit).Build()) + if err != nil { + client.Logger().Error("unable to update channel message: ", err) + return + } +} + +func GetVoiceChannels(client bot.Client) (map[snowflake.ID]string, error) { + allChannels, err := client.Rest().GetGuildChannels(config.RegisterGuildID) + if err != nil { + return nil, errors.New(fmt.Sprintf("error getting channels: %v", err.Error())) + } + voiceChannels := make(map[snowflake.ID]string) + channelVoiceGroupIDStr := config.ChannelVoiceGroupID.String() + for _, channel := range allChannels { + if channel.Type() == discord.ChannelTypeGuildCategory { + continue + } + parentIDStr := channel.ParentID().String() + channelType := channel.Type() + if parentIDStr == channelVoiceGroupIDStr && channelType == discord.ChannelTypeGuildVoice { + voiceChannels[channel.ID()] = channel.Name() + } + } + return voiceChannels, nil +} + +func GetName(channels map[snowflake.ID]string) (string, error) { + channelsReverse := make(map[string]snowflake.ID) + for id, name := range channels { + channelsReverse[name] = id + } + + appleListLen := len(config.AppleList) + for i := 0; i < appleListLen; i++ { + randomIndex := rand.Intn(appleListLen) + pick := config.AppleList[randomIndex] + newName := fmt.Sprintf("%v%v", config.ChannelVoicePrefix, pick) + _, exists := channelsReverse[newName] + if exists == false { + return newName, nil + } + } + return "", errors.New("could not find unused name") + +} + +func UpdateVoiceChannels(client bot.Client, deleteChannels bool) { + + voiceChannels, err := GetVoiceChannels(client) + if err != nil { + client.Logger().Error("error in getVoiceChannels: ", err) + } + voiceChannelsWithState := make(map[snowflake.ID]string) + voiceChannelsEmpty := make(map[snowflake.ID]string) + for key, val := range voiceChannels { + voiceChannelsEmpty[key] = val + } + + client.Caches().VoiceStatesForEach(config.RegisterGuildID, func(state discord.VoiceState) { + if state.ChannelID == nil { + return + } + channelId := *state.ChannelID + name, ok := voiceChannels[channelId] + if !ok { + // not channel of group + return + } + voiceChannelsWithState[channelId] = name + delete(voiceChannelsEmpty, channelId) + }) + + if len(voiceChannels) == 2 && len(voiceChannelsWithState) == 0 { + client.Logger().Debug("all channels are empty") + return + } + + if len(voiceChannelsWithState) >= len(voiceChannels) { + // if there are no empty voiceChannels create one + client.Logger().Debug("new channel has to be created") + name, err := GetName(voiceChannels) + if err != nil { + client.Logger().Error("no empty channels") + return + } + _, err = client.Rest().CreateGuildChannel(config.RegisterGuildID, discord.GuildVoiceChannelCreate{ + Name: name, + UserLimit: 77, + ParentID: config.ChannelVoiceGroupID, + }) + if err != nil { + return + } + return + } + + if len(voiceChannels)-len(voiceChannelsWithState) > 1 && deleteChannels { + // if there are more than one empty voiceChannels delete all but 1 + client.Logger().Debug("channel has to be deleted") + client.Logger().Debug("empty channels: ", voiceChannelsEmpty) + + for id, s := range voiceChannelsEmpty { + // get the oldest channel and delete it + client.Logger().Debug("deleting: ", s) + err := client.Rest().DeleteChannel(id) + if err != nil { + client.Logger().Error("not able to delete: ", err) + return + } + break + } + UpdateVoiceChannels(client, true) + } + +} diff --git a/cmd/vcbot/main.go b/cmd/vcbot/main.go index e9f4fac..b04e180 100644 --- a/cmd/vcbot/main.go +++ b/cmd/vcbot/main.go @@ -2,48 +2,24 @@ package main import ( "context" - "encoding/json" - "errors" - "fmt" "github.com/disgoorg/disgo" "github.com/disgoorg/disgo/bot" "github.com/disgoorg/disgo/cache" - "github.com/disgoorg/disgo/discord" - "github.com/disgoorg/disgo/events" "github.com/disgoorg/disgo/gateway" "github.com/disgoorg/log" - "github.com/disgoorg/snowflake/v2" - "io" - "math/rand" - "net/http" + "grow.rievo.dev/discordBots/cmd/vcbot/config" + "grow.rievo.dev/discordBots/cmd/vcbot/event" "os" "os/signal" "syscall" ) -var ( - token = os.Getenv("disgo_token") - registerGuildID = snowflake.GetEnv("disgo_guild_id") - channelVoiceGroupID snowflake.ID - channelVoiceLogID snowflake.ID - channelVoicePrefix = "🔉 - " - appleApi = "https://grow.rievo.page/applelist/initial.json" - appleList []string -) - -type appleApiBody struct { - Status string `json:"status"` - Type string `json:"type"` - Version string `json:"version"` - Body []string `json:"body"` -} - func main() { log.SetLevel(log.LevelInfo) log.Info("starting vcbot...") log.Info("disgo version: ", disgo.Version) - err := loadAppleList() + err := config.LoadAppleList() if err != nil { log.Fatal(err) return @@ -51,7 +27,7 @@ func main() { // permissions: Manage Channels // intents: - client, err := disgo.New(token, + client, err := disgo.New(config.Token, bot.WithGatewayConfigOpts( gateway.WithIntents(gateway.IntentGuildVoiceStates), ), @@ -62,9 +38,9 @@ func main() { cache.FlagMembers, ), ), - bot.WithEventListenerFunc(joinEvent), - bot.WithEventListenerFunc(moveEvent), - bot.WithEventListenerFunc(leaveEvent), + bot.WithEventListenerFunc(event.JoinEvent), + bot.WithEventListenerFunc(event.MoveEvent), + bot.WithEventListenerFunc(event.LeaveEvent), ) if err != nil { log.Fatal("error while building disgo instance: ", err) @@ -77,16 +53,16 @@ func main() { log.Fatal("error while connecting to gateway: ", err) } - channels, err := client.Rest().GetGuildChannels(registerGuildID) + channels, err := client.Rest().GetGuildChannels(config.RegisterGuildID) for _, channel := range channels { switch channel.Name() { case "🔊-talk": - channelVoiceGroupID = channel.ID() + config.ChannelVoiceGroupID = channel.ID() case "📋-voice": - channelVoiceLogID = channel.ID() + config.ChannelVoiceLogID = channel.ID() } } - if channelVoiceGroupID == 0 { + if config.ChannelVoiceGroupID == 0 { log.Fatal("couldn't find needed channel") } @@ -95,205 +71,3 @@ func main() { signal.Notify(s, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) <-s } - -func loadAppleList() error { - res, err := http.Get(appleApi) - if err != nil { - return err - } - body, err := io.ReadAll(res.Body) - if err != nil { - return err - } - - var data appleApiBody - err = json.Unmarshal(body, &data) - if err != nil { - return err - } - appleList = data.Body - log.Debug("loaded apple list of lenght: ", len(appleList)) - return nil -} - -func sendNoMention(client bot.Client, msg, msgEdit string) { - message, err := client.Rest().CreateMessage(channelVoiceLogID, discord.NewMessageCreateBuilder(). - SetContent(msg).Build()) - if err != nil { - client.Logger().Error("unable to send channel message: ", err) - return - } - _, err = client.Rest().UpdateMessage(channelVoiceLogID, - message.ID, - discord.NewMessageUpdateBuilder().ClearContent().SetContent(msgEdit).Build()) - if err != nil { - client.Logger().Error("unable to update channel message: ", err) - return - } -} - -func joinEvent(event *events.GuildVoiceJoin) { - event.Client().Logger().Debug("joinEvent") - channelId := *event.VoiceState.ChannelID - channel, _ := event.Client().Rest().GetChannel(channelId) - channelName := channel.Name() - userName := event.Member.EffectiveName() - userMention := event.Member.Mention() - - message := fmt.Sprintf("**CHANNEL LOG:** %v joined %v", userName, channelName) - messageEdit := fmt.Sprintf("**CHANNEL LOG:** %v joined %v", userMention, channelName) - go sendNoMention(event.Client(), message, messageEdit) - - updateVoiceChannels(event.Client(), false) -} - -func moveEvent(event *events.GuildVoiceMove) { - if event.OldVoiceState.ChannelID.String() == event.VoiceState.ChannelID.String() { - // no that's not a move event - return - } - event.Client().Logger().Debug("moveEvent") - channelOldId := *event.OldVoiceState.ChannelID - channelOld, _ := event.Client().Rest().GetChannel(channelOldId) - channelOldName := channelOld.Name() - channelId := *event.VoiceState.ChannelID - channel, _ := event.Client().Rest().GetChannel(channelId) - channelName := channel.Name() - userName := event.Member.EffectiveName() - userMention := event.Member.Mention() - - message := fmt.Sprintf("**CHANNEL LOG:** %v moved from %v to %v", userName, channelOldName, channelName) - messageEdit := fmt.Sprintf("**CHANNEL LOG:** %v moved from %v to %v", userMention, channelOldName, channelName) - go sendNoMention(event.Client(), message, messageEdit) - - updateVoiceChannels(event.Client(), false) -} - -func leaveEvent(event *events.GuildVoiceLeave) { - event.Client().Logger().Debug("leaveEvent") - if event.OldVoiceState.ChannelID == nil { - event.Client().Logger().Error("OldVoiceState.ChannelID missing") - return - } - channelOldId := *event.OldVoiceState.ChannelID - channelOld, _ := event.Client().Rest().GetChannel(channelOldId) - channelOldName := channelOld.Name() - userName := event.Member.EffectiveName() - userMention := event.Member.Mention() - - message := fmt.Sprintf("**CHANNEL LOG:** %v left %v", userName, channelOldName) - messageEdit := fmt.Sprintf("**CHANNEL LOG:** %v left %v", userMention, channelOldName) - go sendNoMention(event.Client(), message, messageEdit) - - updateVoiceChannels(event.Client(), true) -} - -func getVoiceChannels(client bot.Client) (map[snowflake.ID]string, error) { - allChannels, err := client.Rest().GetGuildChannels(registerGuildID) - if err != nil { - return nil, errors.New(fmt.Sprintf("error getting channels: %v", err.Error())) - } - voiceChannels := make(map[snowflake.ID]string) - channelVoiceGroupIDStr := channelVoiceGroupID.String() - for _, channel := range allChannels { - if channel.Type() == discord.ChannelTypeGuildCategory { - continue - } - parentIDStr := channel.ParentID().String() - channelType := channel.Type() - if parentIDStr == channelVoiceGroupIDStr && channelType == discord.ChannelTypeGuildVoice { - voiceChannels[channel.ID()] = channel.Name() - } - } - return voiceChannels, nil -} - -func getName(channels map[snowflake.ID]string) (string, error) { - channelsReverse := make(map[string]snowflake.ID) - for id, name := range channels { - channelsReverse[name] = id - } - - appleListLen := len(appleList) - for i := 0; i < appleListLen; i++ { - randomIndex := rand.Intn(appleListLen) - pick := appleList[randomIndex] - newName := fmt.Sprintf("%v%v", channelVoicePrefix, pick) - _, exists := channelsReverse[newName] - if exists == false { - return newName, nil - } - } - return "", errors.New("could not find unused name") - -} - -func updateVoiceChannels(client bot.Client, deleteChannels bool) { - - voiceChannels, err := getVoiceChannels(client) - if err != nil { - client.Logger().Error("error in getVoiceChannels: ", err) - } - voiceChannelsWithState := make(map[snowflake.ID]string) - voiceChannelsEmpty := make(map[snowflake.ID]string) - for key, val := range voiceChannels { - voiceChannelsEmpty[key] = val - } - - client.Caches().VoiceStatesForEach(registerGuildID, func(state discord.VoiceState) { - if state.ChannelID == nil { - return - } - channelId := *state.ChannelID - name, ok := voiceChannels[channelId] - if !ok { - // not channel of group - return - } - voiceChannelsWithState[channelId] = name - delete(voiceChannelsEmpty, channelId) - }) - - if len(voiceChannels) == 2 && len(voiceChannelsWithState) == 0 { - client.Logger().Debug("all channels are empty") - return - } - - if len(voiceChannelsWithState) >= len(voiceChannels) { - // if there are no empty voiceChannels create one - client.Logger().Debug("new channel has to be created") - name, err := getName(voiceChannels) - if err != nil { - client.Logger().Error("no empty channels") - return - } - _, err = client.Rest().CreateGuildChannel(registerGuildID, discord.GuildVoiceChannelCreate{ - Name: name, - UserLimit: 77, - ParentID: channelVoiceGroupID, - }) - if err != nil { - return - } - return - } - - if len(voiceChannels)-len(voiceChannelsWithState) > 1 && deleteChannels { - // if there are more than one empty voiceChannels delete all but 1 - client.Logger().Debug("channel has to be deleted") - client.Logger().Debug("empty channels: ", voiceChannelsEmpty) - - for id, s := range voiceChannelsEmpty { - // get the oldest channel and delete it - client.Logger().Debug("deleting: ", s) - err := client.Rest().DeleteChannel(id) - if err != nil { - client.Logger().Error("not able to delete: ", err) - return - } - break - } - updateVoiceChannels(client, true) - } - -}