diff --git a/cmd/tempbot/main.go b/cmd/tempbot/main.go new file mode 100644 index 0000000..a6b6b71 --- /dev/null +++ b/cmd/tempbot/main.go @@ -0,0 +1,99 @@ +package main + +import ( + "context" + "github.com/disgoorg/disgo" + "github.com/disgoorg/disgo/bot" + "github.com/disgoorg/disgo/cache" + "github.com/disgoorg/disgo/gateway" + "github.com/disgoorg/log" + "github.com/disgoorg/snowflake/v2" + "os" + "os/signal" + "syscall" + "time" +) + +var ( + token = os.Getenv("disgo_token") + registerGuildID = snowflake.GetEnv("disgo_guild_id") + channelTempID snowflake.ID +) + +func main() { + log.SetLevel(log.LevelInfo) + log.Info("starting joinbot...") + log.Info("disgo version: ", disgo.Version) + + // intents needed GUILD_MESSAGES + client, err := disgo.New(token, + bot.WithGatewayConfigOpts( + gateway.WithIntents(gateway.IntentsAll), + ), + bot.WithCacheConfigOpts( + cache.WithCacheFlags( + cache.FlagsAll, + ), + ), + ) + if err != nil { + log.Fatal("error while building disgo instance: ", err) + return + } + + defer client.Close(context.TODO()) + + channels, err := client.Rest().GetGuildChannels(registerGuildID) + for _, channel := range channels { + switch channel.Name() { + case "⌛-temp": + channelTempID = channel.ID() + } + } + if channelTempID == 0 { + log.Fatal("couldn't find needed channel") + } + + // delete messages older then x min in channel + + ticker := time.NewTicker(1 * time.Minute) + quit := make(chan struct{}) + go func() { + for { + select { + case <-ticker.C: + messages, err := client.Rest().GetMessages(channelTempID, 0, 0, 0, 100) + if err != nil { + client.Logger().Error("error getting messages: ", err) + } + var messageIDs []snowflake.ID + for _, message := range messages { + if message.CreatedAt.Add(30 * time.Minute).Before(time.Now()) { + messageIDs = append(messageIDs, message.ID) + } + } + client.Logger().Debug("deleting messages: ", len(messageIDs)) + if len(messageIDs) == 1 { + err = client.Rest().DeleteMessage(channelTempID, messageIDs[0]) + if err != nil { + client.Logger().Error("error deleting messages: ", err) + } + } + if len(messageIDs) > 1 { + err = client.Rest().BulkDeleteMessages(channelTempID, messageIDs) + if err != nil { + client.Logger().Error("error deleting messages: ", err) + } + } + case <-quit: + ticker.Stop() + return + } + } + }() + + log.Infof("tempbot is now running. Press CTRL-C to exit.") + s := make(chan os.Signal, 1) + signal.Notify(s, syscall.SIGINT, syscall.SIGTERM, os.Interrupt) + <-s +} diff --git a/cmd/voicechannelbot/main.go b/cmd/voicechannelbot/main.go new file mode 100644 index 0000000..0d33768 --- /dev/null +++ b/cmd/voicechannelbot/main.go @@ -0,0 +1,298 @@ +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" + "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 voicechannelbot...") + log.Info("disgo version: ", disgo.Version) + + err := loadAppleList() + if err != nil { + log.Fatal(err) + return + } + + // intents needed + client, err := disgo.New(token, + bot.WithGatewayConfigOpts( + gateway.WithIntents(gateway.IntentGuildVoiceStates), + ), + bot.WithCacheConfigOpts( + cache.WithCaches( + cache.FlagChannels, + cache.FlagVoiceStates, + cache.FlagMembers, + ), + ), + bot.WithEventListenerFunc(joinEvent), + bot.WithEventListenerFunc(moveEvent), + bot.WithEventListenerFunc(leaveEvent), + ) + if err != nil { + log.Fatal("error while building disgo instance: ", err) + return + } + + defer client.Close(context.TODO()) + + if err = client.OpenGateway(context.TODO()); err != nil { + log.Fatal("error while connecting to gateway: ", err) + } + + channels, err := client.Rest().GetGuildChannels(registerGuildID) + for _, channel := range channels { + switch channel.Name() { + case "🔊-talk": + channelVoiceGroupID = channel.ID() + case "📋-voice": + channelVoiceLogID = channel.ID() + } + } + if channelVoiceGroupID == 0 { + log.Fatal("couldn't find needed channel") + } + + log.Infof("voicechannelbot is now running. Press CTRL-C to exit.") + s := make(chan os.Signal, 1) + 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) + } + +}