discordBots/cmd/voicechannelbot/main.go

299 lines
8.3 KiB
Go
Raw Normal View History

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)
}
}