2023-01-25 19:09:58 +00:00
|
|
|
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)
|
2023-01-25 23:33:18 +00:00
|
|
|
log.Info("starting vcbot...")
|
2023-01-25 19:09:58 +00:00
|
|
|
log.Info("disgo version: ", disgo.Version)
|
|
|
|
|
|
|
|
err := loadAppleList()
|
|
|
|
if err != nil {
|
|
|
|
log.Fatal(err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2023-01-25 23:33:18 +00:00
|
|
|
// permissions: Manage Channels
|
|
|
|
// intents:
|
2023-01-25 19:09:58 +00:00
|
|
|
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")
|
|
|
|
}
|
|
|
|
|
2023-01-25 23:33:18 +00:00
|
|
|
log.Infof("vcbot is now running. Press CTRL-C to exit.")
|
2023-01-25 19:09:58 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|