diff --git a/application/src/main/java/org/togetherjava/tjbot/features/VoiceReceiver.java b/application/src/main/java/org/togetherjava/tjbot/features/VoiceReceiver.java new file mode 100644 index 0000000000..170dabd4fe --- /dev/null +++ b/application/src/main/java/org/togetherjava/tjbot/features/VoiceReceiver.java @@ -0,0 +1,69 @@ +package org.togetherjava.tjbot.features; + +import net.dv8tion.jda.api.events.guild.voice.GuildVoiceDeafenEvent; +import net.dv8tion.jda.api.events.guild.voice.GuildVoiceMuteEvent; +import net.dv8tion.jda.api.events.guild.voice.GuildVoiceStreamEvent; +import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent; +import net.dv8tion.jda.api.events.guild.voice.GuildVoiceVideoEvent; + +import java.util.regex.Pattern; + +/** + * Receives incoming Discord guild events from voice channels matching a given pattern. + *
+ * All voice receivers have to implement this interface. For convenience, there is a + * {@link VoiceReceiverAdapter} available that implemented most methods already. A new receiver can + * then be registered by adding it to {@link Features}. + *
+ *
+ * After registration, the system will notify a receiver whenever a new event was sent or an + * existing event was updated in any channel matching the {@link #getChannelNamePattern()} the bot + * is added to. + */ +public interface VoiceReceiver extends Feature { + /** + * Retrieves the pattern matching the names of channels of which this receiver is interested in + * receiving events from. Called by the core system once during the startup in order to register + * the receiver accordingly. + *
+ * Changes on the pattern returned by this method afterwards will not be picked up.
+ *
+ * @return the pattern matching the names of relevant channels
+ */
+ Pattern getChannelNamePattern();
+
+ /**
+ * Triggered by the core system whenever a member joined, left or moved voice channels.
+ *
+ * @param event the event that triggered this
+ */
+ void onVoiceUpdate(GuildVoiceUpdateEvent event);
+
+ /**
+ * Triggered by the core system whenever a member toggled their camera in a voice channel.
+ *
+ * @param event the event that triggered this
+ */
+ void onVideoToggle(GuildVoiceVideoEvent event);
+
+ /**
+ * Triggered by the core system whenever a member started or stopped a stream.
+ *
+ * @param event the event that triggered this
+ */
+ void onStreamToggle(GuildVoiceStreamEvent event);
+
+ /**
+ * Triggered by the core system whenever a member toggled their mute status.
+ *
+ * @param event the event that triggered this
+ */
+ void onMuteToggle(GuildVoiceMuteEvent event);
+
+ /**
+ * Triggered by the core system whenever a member toggled their deafened status.
+ *
+ * @param event the event that triggered this
+ */
+ void onDeafenToggle(GuildVoiceDeafenEvent event);
+}
diff --git a/application/src/main/java/org/togetherjava/tjbot/features/VoiceReceiverAdapter.java b/application/src/main/java/org/togetherjava/tjbot/features/VoiceReceiverAdapter.java
new file mode 100644
index 0000000000..c92fbb339a
--- /dev/null
+++ b/application/src/main/java/org/togetherjava/tjbot/features/VoiceReceiverAdapter.java
@@ -0,0 +1,52 @@
+package org.togetherjava.tjbot.features;
+
+import net.dv8tion.jda.api.events.guild.voice.GuildVoiceDeafenEvent;
+import net.dv8tion.jda.api.events.guild.voice.GuildVoiceMuteEvent;
+import net.dv8tion.jda.api.events.guild.voice.GuildVoiceStreamEvent;
+import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent;
+import net.dv8tion.jda.api.events.guild.voice.GuildVoiceVideoEvent;
+
+import java.util.regex.Pattern;
+
+public class VoiceReceiverAdapter implements VoiceReceiver {
+
+ private final Pattern channelNamePattern;
+
+ protected VoiceReceiverAdapter() {
+ this(Pattern.compile(".*"));
+ }
+
+ protected VoiceReceiverAdapter(Pattern channelNamePattern) {
+ this.channelNamePattern = channelNamePattern;
+ }
+
+ @Override
+ public Pattern getChannelNamePattern() {
+ return channelNamePattern;
+ }
+
+ @Override
+ public void onVoiceUpdate(GuildVoiceUpdateEvent event) {
+ // Adapter does not react by default, subclasses may change this behavior
+ }
+
+ @Override
+ public void onVideoToggle(GuildVoiceVideoEvent event) {
+ // Adapter does not react by default, subclasses may change this behavior
+ }
+
+ @Override
+ public void onStreamToggle(GuildVoiceStreamEvent event) {
+ // Adapter does not react by default, subclasses may change this behavior
+ }
+
+ @Override
+ public void onMuteToggle(GuildVoiceMuteEvent event) {
+ // Adapter does not react by default, subclasses may change this behavior
+ }
+
+ @Override
+ public void onDeafenToggle(GuildVoiceDeafenEvent event) {
+ // Adapter does not react by default, subclasses may change this behavior
+ }
+}
diff --git a/application/src/main/java/org/togetherjava/tjbot/features/system/BotCore.java b/application/src/main/java/org/togetherjava/tjbot/features/system/BotCore.java
index 869e978a17..7c337e2efb 100644
--- a/application/src/main/java/org/togetherjava/tjbot/features/system/BotCore.java
+++ b/application/src/main/java/org/togetherjava/tjbot/features/system/BotCore.java
@@ -2,6 +2,12 @@
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.channel.Channel;
+import net.dv8tion.jda.api.entities.channel.unions.AudioChannelUnion;
+import net.dv8tion.jda.api.events.guild.voice.GuildVoiceDeafenEvent;
+import net.dv8tion.jda.api.events.guild.voice.GuildVoiceMuteEvent;
+import net.dv8tion.jda.api.events.guild.voice.GuildVoiceStreamEvent;
+import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent;
+import net.dv8tion.jda.api.events.guild.voice.GuildVoiceVideoEvent;
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent;
@@ -16,6 +22,8 @@
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import net.dv8tion.jda.api.interactions.callbacks.IReplyCallback;
import net.dv8tion.jda.api.interactions.components.ComponentInteraction;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Unmodifiable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -32,6 +40,7 @@
import org.togetherjava.tjbot.features.UserContextCommand;
import org.togetherjava.tjbot.features.UserInteractionType;
import org.togetherjava.tjbot.features.UserInteractor;
+import org.togetherjava.tjbot.features.VoiceReceiver;
import org.togetherjava.tjbot.features.componentids.ComponentId;
import org.togetherjava.tjbot.features.componentids.ComponentIdParser;
import org.togetherjava.tjbot.features.componentids.ComponentIdStore;
@@ -75,6 +84,7 @@ public final class BotCore extends ListenerAdapter implements CommandProvider {
private final ComponentIdParser componentIdParser;
private final ComponentIdStore componentIdStore;
private final Map
+ * If there is a
+ * This is an essential method due to the need of updating both channel categories that a member
+ * utilizes. For example, take the scenario of a user browsing through voice channels:
+ *
+ *
+ * This way, we make sure that all relevant voice channels are updated.
+ *
+ * @param channelJoined the channel that the member has connected to, if any
+ * @param channelLeft the channel that the member left from, if any
+ * @return the join channel if not null, otherwise the leave channel, otherwise an empty
+ * optional
+ */
+ private OptionalchannelJoined and a channelLeft, then the
+ * channelJoined is prioritized and returned. Otherwise, it returns
+ * channelLeft.
+ *
+ *
+ * - User joins General -> channelJoined = General | channelLeft = null
+ * - User switches to Gaming -> channelJoined = Gaming | channelLeft = General
+ * - User leaves Discord -> channelJoined = null | channelLeft = Gaming
+ *
+ *
+ *