diff --git a/src/de/steamwar/bungeecore/bot/ChannelManager.java b/src/de/steamwar/bungeecore/bot/ChannelManager.java
new file mode 100644
index 0000000..2871901
--- /dev/null
+++ b/src/de/steamwar/bungeecore/bot/ChannelManager.java
@@ -0,0 +1,189 @@
+/*
+ * This file is a part of the SteamWar software.
+ *
+ * Copyright (C) 2022 SteamWar.de-Serverteam
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+package de.steamwar.bungeecore.bot;
+
+import lombok.Setter;
+import lombok.experimental.Accessors;
+import net.dv8tion.jda.api.JDA;
+import net.dv8tion.jda.api.entities.Guild;
+import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
+import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent;
+import net.dv8tion.jda.api.hooks.ListenerAdapter;
+import net.dv8tion.jda.api.requests.restaction.ChannelAction;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
+import java.util.function.IntFunction;
+
+public class ChannelManager extends ListenerAdapter {
+
+ private static final String[] ROMEN_NUMERALS = {
+ "",
+ "I",
+ "II",
+ "III",
+ "IV",
+ "V",
+ "VI",
+ "VII",
+ "VIII",
+ "IX",
+ "X",
+ "XI",
+ "XII",
+ "XIII",
+ "XIV",
+ "XV",
+ "XVI",
+ "XVII",
+ "XVIII",
+ "XIX",
+ "XX",
+ };
+
+ private ChannelManagerOptions options;
+ private Guild guild;
+
+ private List channelNames = new ArrayList<>();
+ private List channels = new ArrayList<>();
+
+ public ChannelManager(ChannelManagerOptions options) {
+ options.check();
+ this.options = options;
+
+ JDA jda = options.jda;
+ jda.addEventListener(this);
+ guild = jda.getGuildById(options.guildID);
+ if (guild == null) {
+ throw new IllegalArgumentException("Guild with ID " + options.guildID + " not found");
+ }
+
+ for (int i = 1; i <= options.maxChannels; i++) {
+ channelNames.add(options.channelName.apply(i));
+ }
+
+ startUp();
+ }
+
+ private void startUp() {
+ Objects.requireNonNull(guild.getCategoryById(options.categoryID))
+ .getVoiceChannels()
+ .stream()
+ .filter(voiceChannel -> channelNames.contains(voiceChannel.getName()))
+ .forEach(channels::add);
+ channels.sort((o1, o2) -> {
+ int i1 = channelNames.indexOf(o1.getName());
+ int i2 = channelNames.indexOf(o2.getName());
+ return Integer.compare(i1, i2);
+ });
+
+ if (channels.isEmpty()) {
+ ChannelAction channelAction = guild.createVoiceChannel(channelNames.get(0))
+ .setParent(guild.getCategoryById(options.categoryID));
+ if (options.channelCreator != null) {
+ options.channelCreator.accept(channelAction);
+ }
+ VoiceChannel newChannel = channelAction.complete();
+ channels.add(newChannel);
+ return;
+ }
+
+ if (channels.size() == 1) {
+ // TODO: Check if channel is not empty and create new channel
+ }
+ }
+
+ @Override
+ public synchronized void onGuildVoiceUpdate(@NotNull GuildVoiceUpdateEvent event) {
+ if (event.getChannelLeft() instanceof VoiceChannel) {
+ leave((VoiceChannel) event.getChannelLeft());
+ }
+ if (event.getChannelJoined() instanceof VoiceChannel) {
+ join((VoiceChannel) event.getChannelJoined());
+ }
+ }
+
+ private void leave(VoiceChannel voiceChannel) {
+ if (voiceChannel.getGuild().getIdLong() != options.guildID) return;
+ if (voiceChannel.getParentCategoryIdLong() != options.categoryID) return;
+ if (!channelNames.contains(voiceChannel.getName())) return;
+ if (!voiceChannel.getMembers().isEmpty()) return;
+ if (channels.size() <= 1) return;
+
+ int index = channels.indexOf(voiceChannel);
+ boolean needsRecreate = channels.size() == options.maxChannels && index < options.maxChannels - 1 && !channels.get(channels.size() - 1).getMembers().isEmpty();
+ channels.remove(index);
+ voiceChannel.delete().complete();
+ for (int i = index; i < channels.size(); i++) {
+ VoiceChannel channel = channels.get(i);
+ channel.getManager().setName(options.channelName.apply(i + 1)).queue();
+ }
+ if (needsRecreate) {
+ createChannelWithIndex(channels.size());
+ }
+ }
+
+ private void join(VoiceChannel voiceChannel) {
+ if (voiceChannel.getGuild().getIdLong() != options.guildID) return;
+ if (voiceChannel.getParentCategoryIdLong() != options.categoryID) return;
+ if (!channelNames.contains(voiceChannel.getName())) return;
+ int index = channels.indexOf(voiceChannel);
+ if (index < channels.size() - 1) return;
+ if (index >= options.maxChannels - 1) return;
+
+ createChannelWithIndex(index + 1);
+ }
+
+ private void createChannelWithIndex(int index) {
+ ChannelAction channelAction = guild.createVoiceChannel(channelNames.get(index))
+ .setParent(guild.getCategoryById(options.categoryID));
+ if (options.channelCreator != null) {
+ options.channelCreator.accept(channelAction);
+ }
+ VoiceChannel newChannel = channelAction.complete();
+ channels.add(newChannel);
+ }
+
+ @Setter
+ @Accessors(fluent = true)
+ public static final class ChannelManagerOptions {
+ private JDA jda;
+ private long guildID;
+ private long categoryID;
+ private int maxChannels = 20;
+ private IntFunction channelName = USER_LOUNGE;
+ private Consumer> channelCreator = null;
+
+ private void check() {
+ if (jda == null) throw new IllegalStateException("JDA is null");
+ if (guildID == 0) throw new IllegalStateException("GuildID is 0");
+ if (categoryID == 0) throw new IllegalStateException("CategoryID is 0");
+ if (maxChannels < 1) throw new IllegalStateException("MaxChannels is less than 1");
+ if (maxChannels > 20) throw new IllegalStateException("MaxChannels is greater than 20");
+ if (channelName == null) throw new IllegalStateException("ChannelName is null");
+ }
+ }
+
+ public static final IntFunction USER_LOUNGE = i -> "「\uD83D\uDCAC」User Lounge " + ROMEN_NUMERALS[i];
+ public static final IntFunction TEAM_LOUNGE = i -> "「\uD83D\uDCAC」Team Lounge " + ROMEN_NUMERALS[i];
+}
diff --git a/src/de/steamwar/bungeecore/bot/SteamwarDiscordBot.java b/src/de/steamwar/bungeecore/bot/SteamwarDiscordBot.java
index 0900437..19958e4 100644
--- a/src/de/steamwar/bungeecore/bot/SteamwarDiscordBot.java
+++ b/src/de/steamwar/bungeecore/bot/SteamwarDiscordBot.java
@@ -38,11 +38,12 @@ import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.exceptions.ErrorResponseException;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
+import net.dv8tion.jda.api.requests.GatewayIntent;
import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction;
import net.dv8tion.jda.api.utils.MemberCachePolicy;
+import net.dv8tion.jda.api.utils.cache.CacheFlag;
import net.md_5.bungee.api.ProxyServer;
-import javax.security.auth.login.LoginException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -74,8 +75,10 @@ public class SteamwarDiscordBot {
public SteamwarDiscordBot() {
INSTANCE = this;
JDABuilder builder = JDABuilder.createDefault(SteamwarDiscordBotConfig.TOKEN);
+ builder.enableIntents(GatewayIntent.GUILD_VOICE_STATES, GatewayIntent.GUILD_EMOJIS_AND_STICKERS);
builder.setStatus(OnlineStatus.ONLINE);
- builder.setMemberCachePolicy(MemberCachePolicy.ONLINE);
+ builder.enableCache(CacheFlag.VOICE_STATE);
+ builder.setMemberCachePolicy(MemberCachePolicy.ONLINE.or(MemberCachePolicy.VOICE));
jda = builder.build();
ProxyServer.getInstance().getScheduler().runAsync(BungeeCore.get(), () -> {
try {
@@ -88,6 +91,14 @@ public class SteamwarDiscordBot {
} catch (Exception e) {
BungeeCore.get().getLogger().log(Level.SEVERE, "Could not set initial activity to discord", e);
}
+
+ new ChannelManager(new ChannelManager.ChannelManagerOptions()
+ .guildID(869612801499476068L)
+ .categoryID(869612801520435312L)
+ .maxChannels(3)
+ .jda(jda)
+ );
+
EventManager.update();
SchematicsManager.update();
ProxyServer.getInstance().getScheduler().schedule(BungeeCore.get(), () -> {
@@ -113,16 +124,20 @@ public class SteamwarDiscordBot {
new SlashCommandListener();
jda.retrieveCommands().complete().forEach(command -> jda.deleteCommandById(command.getId()).queue());
- Guild guild = jda.getGuildById(SteamwarDiscordBotConfig.GUILD);
- guild.retrieveCommands().complete().forEach(command -> guild.deleteCommandById(command.getId()).complete());
- CommandListUpdateAction commands = jda.getGuildById(SteamwarDiscordBotConfig.GUILD).updateCommands();
- addCommand(commands, new MuteCommand());
- addCommand(commands, new BanCommand());
- addCommand(commands, new WhoisCommand());
- addCommand(commands, new TeamCommand());
- addCommand(commands, new ListCommand());
- addCommand(commands, new UnbanCommand());
- commands.complete();
+ try {
+ Guild guild = jda.getGuildById(SteamwarDiscordBotConfig.GUILD);
+ guild.retrieveCommands().complete().forEach(command -> guild.deleteCommandById(command.getId()).complete());
+ CommandListUpdateAction commands = jda.getGuildById(SteamwarDiscordBotConfig.GUILD).updateCommands();
+ addCommand(commands, new MuteCommand());
+ addCommand(commands, new BanCommand());
+ addCommand(commands, new WhoisCommand());
+ addCommand(commands, new TeamCommand());
+ addCommand(commands, new ListCommand());
+ addCommand(commands, new UnbanCommand());
+ commands.complete();
+ } catch (Exception e) {
+ BungeeCore.get().getLogger().log(Level.SEVERE, "Could not register slash commands", e);
+ }
});
}