Skip to content

Commit

Permalink
Slash commands for moderation (#306)
Browse files Browse the repository at this point in the history
* WIP slash commands for moderation

* Run commands on own thread

Implement list for blacklist

* Clear black list

* Adding to the blacklist

* Remove blacklisted word

* Finish blacklist command

* Adding and listing vc autoroles

* Add vcautorole command to slash support

* Lint

* Move dehoist command to slash support

* Convert slowmode command

* Convert warn command

* Lint code

* Convert warnings command

* convert announce command

* Implemeht slash method in all new mod commands
  • Loading branch information
duncte123 authored Sep 2, 2024
1 parent 33bd681 commit 683de21
Show file tree
Hide file tree
Showing 58 changed files with 1,381 additions and 206 deletions.
54 changes: 37 additions & 17 deletions bot/src/main/java/me/duncte123/skybot/CommandManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import me.duncte123.skybot.commands.utils.EnlargeCommand;
import me.duncte123.skybot.commands.utils.RoleInfoCommand;
import me.duncte123.skybot.commands.weeb.*;
import me.duncte123.skybot.entities.jda.DunctebotGuild;
import me.duncte123.skybot.objects.SlashSupport;
import me.duncte123.skybot.objects.command.*;
import me.duncte123.skybot.objects.pairs.LongLongPair;
Expand Down Expand Up @@ -745,29 +746,48 @@ public List<SlashCommandData> getAllSlashCommands() {
}

public void executeSlashCommand(SlashCommandInteractionEvent event) {
final String fullCommandName = event.getFullCommandName();
this.commandThread.submit(() -> {
try {
MDC.put("command.invoke", event.getFullCommandName());
MDC.put("user.tag", event.getUser().getAsTag());
MDC.put("user.name", event.getUser().getEffectiveName());
MDC.put("user.id", event.getUser().getId());
MDC.put("guild", event.isFromGuild() ? event.getGuild().toString() : "(not in guild)");
setJDAContext(event.getJDA());

if (fullCommandName.startsWith("music")) {
final String musicName = fullCommandName.replace("music", "").trim();
final MusicCommand command = (MusicCommand) this.getCommand(musicName);
final var guild = new DunctebotGuild(event.getGuild(), variables);

if (command != null) {
command.handleEvent(event, variables);
}
final String fullCommandName = event.getFullCommandName();

return;
}
if (fullCommandName.startsWith("music")) {
final String musicName = fullCommandName.replace("music", "").trim();
final MusicCommand command = (MusicCommand) this.getCommand(musicName);

try {
final SlashSupport command = (SlashSupport) this.getCommand(event.getName());
if (command != null) {
command.handleEvent(event, guild, variables);
}

return;
}

if (command != null) {
command.executeEventWithChecks(event, variables);
final SlashSupport command = (SlashSupport) this.getCommand(event.getName());

if (command != null) {
command.executeEventWithChecks(event, guild, variables);
}
}
}
catch (Exception e) {
e.printStackTrace();
}
catch (Exception e) {
event.reply(
"Failed to run command: %s\nPlease join [my discord server](https://duncte.bot/discord) if this happens a lot.".formatted(
e.getMessage()
)
).queue();
LOGGER.error(
"Error in '%s'".formatted(event.getCommandString()),
e
);
}
});
}

private static long calcTimeRemaining(long startTime) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,19 +20,23 @@

import me.duncte123.botcommons.messaging.EmbedUtils;
import me.duncte123.botcommons.messaging.MessageConfig;
import me.duncte123.skybot.extensions.StringKt;
import me.duncte123.skybot.Variables;
import me.duncte123.skybot.entities.jda.DunctebotGuild;
import me.duncte123.skybot.objects.command.CommandCategory;
import me.duncte123.skybot.objects.command.CommandContext;
import me.duncte123.skybot.objects.command.Flag;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
import org.jetbrains.annotations.NotNull;

import javax.annotation.Nonnull;
import java.util.List;

import static me.duncte123.botcommons.messaging.MessageUtils.*;
import static me.duncte123.botcommons.messaging.MessageUtils.sendMsg;

public class AnnounceCommand extends ModBaseCommand {

Expand All @@ -59,66 +63,110 @@ public AnnounceCommand() {
}

@Override
public void execute(@Nonnull CommandContext ctx) {
final List<GuildMessageChannel> mentioned = ctx.getMessage().getMentions().getChannels(GuildMessageChannel.class);
protected void configureSlashSupport(@NotNull SlashCommandData baseData) {
baseData.addOptions(
new OptionData(
OptionType.CHANNEL,
"channel",
"Channel to send the message to",
true
),
new OptionData(
OptionType.STRING,
"message",
"The message to send",
true
),
new OptionData(
OptionType.ATTACHMENT,
"image",
"An image to send in the embed",
false
),
new OptionData(
OptionType.BOOLEAN,
"embed",
"Use an embed instead of plain text (defaults to true)",
false
),
new OptionData(
OptionType.BOOLEAN,
"thumbnail",
"Set the image as a thumbnail instead of a large image",
false
)
);
}

if (mentioned.isEmpty()) {
sendMsg(ctx, "You did not specify a channel, usage: " + this.getUsageInstructions(ctx));
@Override
public void handleEvent(@NotNull SlashCommandInteractionEvent event, @NotNull DunctebotGuild guild, @NotNull Variables variables) {
final var channel = event.getOption("channel").getAsChannel();

if (channel.getType() != ChannelType.TEXT) {
event.reply("That channel is not a text channel!").queue();
return;
}

final GuildMessageChannel targetChannel = mentioned.get(0);
final var txtChan = channel.asGuildMessageChannel();

if (!targetChannel.canTalk()) {
sendErrorWithMessage(ctx.getMessage(), "I can not talk in " + targetChannel.getAsMention());
if (!txtChan.canTalk()) {
event.reply("I can't talk in that channel").queue();
return;
}

final var flags = ctx.getParsedFlags(this);
final List<String> text = flags.get("undefined");
final var message = event.getOption("message").getAsString();

if (text.isEmpty()) {
this.sendUsageInstructions(ctx);
return;
}
final var useEmbedOption = event.getOption("embed");
final var useEmbed = useEmbedOption == null || useEmbedOption.getAsBoolean();

final String msg = StringKt.stripFlags(
ctx.getArgsRaw()
.replace(targetChannel.getAsMention(), ""),
this
);
if (!useEmbed) {
event.reply("Sending message.....").queue();

if (flags.containsKey("noembed")) {
sendMsg(
new MessageConfig.Builder()
.setChannel(targetChannel)
.setMessage(msg)
.setChannel(txtChan)
.setMessage(message)
.setSuccessAction((msg) -> {
event.getHook()
.editOriginal("Message sent: " + msg.getJumpUrl())
.queue();
})
);
sendSuccess(ctx.getMessage());

return;
}

final EmbedBuilder embed = EmbedUtils.getDefaultEmbed()
.setDescription(message)
.setFooter(null, "");

final EmbedBuilder embed = EmbedUtils.getDefaultEmbed().setDescription(msg).setFooter(null, "");
final List<Message.Attachment> attachments = ctx.getMessage().getAttachments();
final var attachmentOption = event.getOption("image");
final var useThumbOption = event.getOption("thumbnail");

if (!attachments.isEmpty()) {
attachments.stream().filter(Message.Attachment::isImage).findFirst().ifPresent((attachment) -> {
if (flags.containsKey("thumbnail")) {
embed.setThumbnail(attachment.getUrl());
} else {
embed.setImage(attachment.getUrl());
}
});
// TODO: download the attachment??
if (attachmentOption != null) {
if (useThumbOption != null && useThumbOption.getAsBoolean()) {
embed.setThumbnail(attachmentOption.getAsAttachment().getUrl());
} else {
embed.setImage(attachmentOption.getAsAttachment().getUrl());
}
}

event.reply("Sending message.....").queue();

sendMsg(new MessageConfig.Builder()
.setChannel(targetChannel)
.setChannel(txtChan)
.addEmbed(embed)
.setSuccessAction((msg) -> {
event.getHook()
.editOriginal("Message sent: " + msg.getJumpUrl())
.queue();
})
.build());

sendSuccess(ctx.getMessage());
}

@Override
public void execute(@Nonnull CommandContext ctx) {
sendMsg(ctx, "Guess what! Even this is a slash command now!");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@

package me.duncte123.skybot.commands.guild.mod;

import me.duncte123.skybot.Variables;
import me.duncte123.skybot.database.AbstractDatabase;
import me.duncte123.skybot.entities.jda.DunctebotGuild;
import me.duncte123.skybot.objects.command.CommandContext;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
import net.dv8tion.jda.api.utils.MiscUtil;
import org.jetbrains.annotations.NotNull;

Expand All @@ -33,6 +39,49 @@ public AutoBanBypassCommand() {
this.usage = "<user id>";
}

@Override
protected void configureSlashSupport(@NotNull SlashCommandData baseData) {
baseData.addOptions(
new OptionData(
OptionType.USER,
"user_id",
"The id of the user that you want to create the bypass for.",
true
)
);
}

@Override
public void handleEvent(@NotNull SlashCommandInteractionEvent event, @NotNull DunctebotGuild guild, @NotNull Variables variables) {
final var checkId = event.getOption("user_id").getAsLong();

final var database = variables.getDatabase();
final var guildId = event.getGuild().getIdLong();

event.deferReply().queue();

database.getBanBypass(guildId, checkId).thenAccept((byPass) -> {
if (byPass == null) {
database.createBanBypass(guildId, checkId);
event.getHook()
.editOriginal("Single use bypass created, please note that this bypass will expire after a week if unused." +
"\nPlease keep in mind that this has not unbanned any user, meaning that you will have to unban the user yourself if they are banned")
.queue();
return;
}

event.getHook()
.editOriginal("A bypass already exists for this user")
.queue();
}).exceptionally((thr) -> {
event.getHook()
.editOriginal("Something went wrong: " + thr.getMessage())
.queue();

return null;
});
}

@Override
public void execute(@NotNull CommandContext ctx) {
final long checkId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,23 @@

package me.duncte123.skybot.commands.guild.mod;

import me.duncte123.skybot.Variables;
import me.duncte123.skybot.entities.jda.DunctebotGuild;
import me.duncte123.skybot.objects.command.CommandContext;
import me.duncte123.skybot.objects.command.Flag;
import net.dv8tion.jda.api.Permission;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
import org.jetbrains.annotations.NotNull;

import javax.annotation.Nonnull;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import static me.duncte123.botcommons.messaging.MessageUtils.sendMsg;
Expand Down Expand Up @@ -60,6 +69,59 @@ public BanCommand() {
};
}

@Override
protected void configureSlashSupport(@NotNull SlashCommandData baseData) {
baseData.addOptions(
new OptionData(
OptionType.USER,
"user",
"The user that you want to ban.",
true
),
new OptionData(
OptionType.STRING,
"reason",
"The reason for this ban",
false
),
new OptionData(
OptionType.BOOLEAN,
"nodel",
"Prevents the deletion of any messages",
false
)
);
}

@Override
public void handleEvent(@NotNull SlashCommandInteractionEvent event, @NotNull DunctebotGuild guild, @NotNull Variables variables) {
final var toBanMember = event.getOption("user").getAsMember();
final var moderator = event.getMember();

if (!canInteract(moderator, toBanMember, "ban", event.getChannel())) {
return;
}

final var reason = Optional.ofNullable(event.getOption("reason"))
.map(OptionMapping::getAsString)
.orElse("No reason given");
final var nodel = Optional.ofNullable(event.getOption("nodel"))
.map(OptionMapping::getAsBoolean)
.orElse(false);

final int delDays = nodel ? 0 : 1;
final var modUser = moderator.getUser();

guild.ban(toBanMember, delDays, TimeUnit.DAYS)
.reason(String.format("%#s: %s", modUser, reason))
.queue(
(m) -> {
modLog(modUser, toBanMember.getUser(), "banned", reason, null, guild);
event.reply("User has been banned").queue();
}
);
}

@Override
public void execute(@Nonnull CommandContext ctx) {
final List<String> args = ctx.getArgs();
Expand Down
Loading

0 comments on commit 683de21

Please sign in to comment.