impl drag feature

This commit is contained in:
Lee 2024-07-04 00:54:16 +01:00
parent bd9ac1e138
commit 7083bebef1
7 changed files with 348 additions and 0 deletions

@ -159,6 +159,11 @@
<artifactId>spotify-web-api-java</artifactId> <artifactId>spotify-web-api-java</artifactId>
<version>8.4.0</version> <version>8.4.0</version>
</dependency> </dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.1.8</version>
</dependency>
<!-- Test Dependencies --> <!-- Test Dependencies -->
<dependency> <dependency>

@ -0,0 +1,23 @@
package cc.fascinated.bat.features.drag;
import cc.fascinated.bat.command.Category;
import cc.fascinated.bat.features.Feature;
import cc.fascinated.bat.features.drag.command.DragCommand;
import cc.fascinated.bat.service.CommandService;
import lombok.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
/**
* @author Fascinated (fascinated7)
*/
@Component
public class DragFeature extends Feature {
@Autowired
public DragFeature(@NonNull ApplicationContext context, @NonNull CommandService commandService) {
super("Drag", true,Category.GENERAL);
super.registerCommand(commandService, context.getBean(DragCommand.class));
}
}

@ -0,0 +1,50 @@
package cc.fascinated.bat.features.drag;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
import net.dv8tion.jda.api.interactions.InteractionHook;
import java.util.Date;
/**
* @author Fascinated (fascinated7)
*/
@RequiredArgsConstructor
@Getter @Setter
public class DragRequest {
/**
* The date the request was made
*/
private final Date requestDate = new Date();
/**
* The user that wants to join the voice channel
*/
private final Member member;
/**
* The user that the member wants to join
*/
private final Member target;
/**
* The voice channel the user wants to join
*/
private final VoiceChannel voiceChannel;
/**
* The interaction hook that the request was made from
*/
private final InteractionHook interactionHook;
/**
* The request message sent in the voice channel
*/
private Message requestMessage;
}

@ -0,0 +1,21 @@
package cc.fascinated.bat.features.drag.command;
import cc.fascinated.bat.command.BatCommand;
import cc.fascinated.bat.command.CommandInfo;
import io.sentry.protocol.App;
import lombok.NonNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
/**
* @author Fascinated (fascinated7)
*/
@Component
@CommandInfo(name = "drag", description = "Drag command")
public class DragCommand extends BatCommand {
@Autowired
public DragCommand(@NonNull ApplicationContext context) {
super.addSubCommand(context.getBean(RequestSubCommand.class));
}
}

@ -0,0 +1,152 @@
package cc.fascinated.bat.features.drag.command;
import cc.fascinated.bat.command.BatSubCommand;
import cc.fascinated.bat.command.CommandInfo;
import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.common.TimerUtils;
import cc.fascinated.bat.event.EventListener;
import cc.fascinated.bat.features.drag.DragRequest;
import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.BatUser;
import lombok.NonNull;
import net.dv8tion.jda.api.entities.GuildVoiceState;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.SlashCommandInteraction;
import net.dv8tion.jda.api.interactions.components.ActionRow;
import net.dv8tion.jda.api.interactions.components.buttons.Button;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.HashSet;
import java.util.Set;
/**
* Handles requests to be moved to a voice channel.
* Author: Fascinated (fascinated7)
*/
@Component
@CommandInfo(name = "request", description = "Request to be moved to a voice channel")
public class RequestSubCommand extends BatSubCommand implements EventListener {
/**
* A list of join requests
*/
public static final Set<DragRequest> JOIN_REQUESTS = new HashSet<>();
private final long requestTimeout = Duration.ofMinutes(30).toMillis();
private final long checkInterval = Duration.ofSeconds(10).toMillis();
public RequestSubCommand() {
super.addOption(OptionType.USER, "user", "The user you want to join", true);
TimerUtils.scheduleRepeating(() -> {
Set<DragRequest> toRemove = new HashSet<>();
for (DragRequest joinRequest : JOIN_REQUESTS) {
if (System.currentTimeMillis() - joinRequest.getRequestDate().getTime() < requestTimeout) {
return;
}
// The request has timed out
joinRequest.getInteractionHook().editOriginalEmbeds(EmbedUtils.errorEmbed()
.setDescription("The request to join %s's voice channel has timed out.".formatted(joinRequest.getTarget().getAsMention()))
.build()).queue();
joinRequest.getVoiceChannel().sendMessageEmbeds(EmbedUtils.errorEmbed()
.setDescription("%s's request to join your voice channel has timed out.".formatted(joinRequest.getMember().getAsMention()))
.build()).queue();
joinRequest.getRequestMessage().delete().queue();
toRemove.add(joinRequest);
}
JOIN_REQUESTS.removeAll(toRemove);
}, checkInterval, checkInterval);
}
@Override
public void execute(BatGuild guild, @NonNull BatUser user, @NonNull MessageChannel channel, Member member, @NonNull SlashCommandInteraction event) {
GuildVoiceState voiceState = member.getVoiceState();
// Check if the user is in a voice channel
if (voiceState == null || voiceState.getChannel() == null) {
event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription("You are not in a voice channel.")
.build())
.setEphemeral(true)
.queue();
return;
}
OptionMapping userOption = event.getOption("user");
if (userOption == null) return;
// Check if the user is in a voice channel
Member target = userOption.getAsMember();
if (target == null || target.getId().equals(member.getId())) {
event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription("You cannot request to join your own voice channel.")
.build())
.setEphemeral(true)
.queue();
return;
}
// Check if the target user is in a voice channel
GuildVoiceState targetVoiceState = target.getVoiceState();
if (targetVoiceState == null || targetVoiceState.getChannel() == null) {
event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription("The user %s is not in a voice channel.".formatted(target.getAsMention()))
.build())
.setEphemeral(true)
.queue();
return;
}
VoiceChannel targetChannel = targetVoiceState.getChannel().asVoiceChannel();
// User is already in the target channel
if (voiceState.getChannel().getId().equals(targetChannel.getId())) {
event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription("You are already in the voice channel %s.".formatted(voiceState.getChannel().getAsMention()))
.build())
.setEphemeral(true)
.queue();
return;
}
// Check if the user has already requested to join the target channel
DragRequest existingRequest = JOIN_REQUESTS.stream()
.filter(request -> request.getMember().getId().equals(member.getId()) && request.getVoiceChannel().getId().equals(targetChannel.getId()))
.findFirst()
.orElse(null);
if (existingRequest != null) {
event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription("You have already requested to join %s's voice channel.".formatted(target.getAsMention()))
.build())
.setEphemeral(true)
.queue();
return;
}
// Add the request to the list
JOIN_REQUESTS.add(new DragRequest(member, target, targetChannel, event.getHook()));
// Send the request to the target user
targetChannel.sendMessage(target.getAsMention()).queue();
targetChannel.sendMessageEmbeds(EmbedUtils.successEmbed()
.setDescription("User %s has requested to join your voice channel.".formatted(member.getAsMention()))
.build())
.addComponents(ActionRow.of(
Button.primary("drag-request-accept", "Accept"),
Button.danger("drag-request-decline", "Decline")
))
.queue(message -> {
JOIN_REQUESTS.stream()
.filter(r -> r.getVoiceChannel().getId().equals(targetChannel.getId()))
.findFirst().ifPresent(request -> request.setRequestMessage(message));
});
event.replyEmbeds(EmbedUtils.successEmbed()
.setDescription("Request to join %s's voice channel has been sent.".formatted(target.getAsMention()))
.build())
.setComponents(ActionRow.of(Button.secondary("drag-request-cancel", "Cancel")))
.queue();
}
}

@ -0,0 +1,43 @@
package cc.fascinated.bat.features.drag.listeners.request;
import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.event.EventListener;
import cc.fascinated.bat.features.drag.DragRequest;
import cc.fascinated.bat.features.drag.command.RequestSubCommand;
import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.BatUser;
import lombok.NonNull;
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.interactions.InteractionHook;
import org.springframework.stereotype.Component;
import java.util.Optional;
/**
* @author Fascinated (fascinated7)
*/
@Component
public class RequestListener implements EventListener {
@Override
public void onButtonInteraction(BatGuild guild, @NonNull BatUser user, @NonNull ButtonInteractionEvent event) {
if (!event.getComponentId().equals("drag-request-cancel")) {
return;
}
Optional<DragRequest> optionalDragRequest = RequestSubCommand.JOIN_REQUESTS.stream()
.filter(request -> request.getMember().getId().equals(event.getUser().getId()))
.findFirst();
if (optionalDragRequest.isEmpty()) {
return;
}
DragRequest dragRequest = optionalDragRequest.get();
InteractionHook interactionHook = dragRequest.getInteractionHook();
interactionHook.editOriginalEmbeds(EmbedUtils.errorEmbed()
.setDescription("You have cancelled your request to join %s's voice channel.".formatted(dragRequest.getTarget().getAsMention()))
.build()).queue(message -> message.editMessageComponents().queue());
dragRequest.getVoiceChannel().sendMessageEmbeds(EmbedUtils.errorEmbed()
.setDescription("%s has cancelled their request to join your voice channel.".formatted(dragRequest.getMember().getAsMention()))
.build()).queue();
dragRequest.getRequestMessage().delete().queue();
RequestSubCommand.JOIN_REQUESTS.remove(dragRequest);
}
}

@ -0,0 +1,54 @@
package cc.fascinated.bat.features.drag.listeners.request;
import cc.fascinated.bat.common.EmbedUtils;
import cc.fascinated.bat.event.EventListener;
import cc.fascinated.bat.features.drag.DragRequest;
import cc.fascinated.bat.features.drag.command.RequestSubCommand;
import cc.fascinated.bat.model.BatGuild;
import cc.fascinated.bat.model.BatUser;
import lombok.NonNull;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import org.springframework.stereotype.Component;
/**
* @author Fascinated (fascinated7)
*/
@Component
public class TargetChannelListener implements EventListener {
@Override
public void onButtonInteraction(BatGuild guild, @NonNull BatUser user, @NonNull ButtonInteractionEvent event) {
User buttonUser = event.getUser();
Member member = guild.getDiscordGuild().getMember(buttonUser);
if (member == null) return;
DragRequest joinRequest = RequestSubCommand.JOIN_REQUESTS.stream()
.filter(request -> request.getVoiceChannel().getId().equals(event.getChannel().getId()))
.findFirst()
.orElse(null);
if (joinRequest == null) return;
if (event.getComponentId().equals("drag-request-accept")) {
joinRequest.getVoiceChannel().getGuild().moveVoiceMember(joinRequest.getMember(), joinRequest.getVoiceChannel()).queue();
event.replyEmbeds(EmbedUtils.successEmbed()
.setDescription("You have accepted %s's request to join your voice channel!".formatted(joinRequest.getMember().getAsMention()))
.build())
.queue();
} else if (event.getComponentId().equals("drag-request-decline")) {
event.replyEmbeds(EmbedUtils.errorEmbed()
.setDescription("You have declined %s's request to join your voice channel!".formatted(joinRequest.getMember().getAsMention()))
.build())
.queue();
joinRequest.getInteractionHook().retrieveOriginal().queue(message -> {
message.editMessageEmbeds(EmbedUtils.errorEmbed()
.setDescription("%s has declined your request to join their voice channel.".formatted(joinRequest.getTarget().getAsMention()))
.build()).queue();
message.editMessageComponents().queue();
});
}
RequestSubCommand.JOIN_REQUESTS.remove(joinRequest);
// Remove the buttons from the embed
event.getInteraction().getMessage().editMessageComponents().queue();
}
}