diff --git a/Essentials/src/main/java/com/earth2me/essentials/AlternativeCommandsHandler.java b/Essentials/src/main/java/com/earth2me/essentials/AlternativeCommandsHandler.java index 2be729a4d..b5d860182 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/AlternativeCommandsHandler.java +++ b/Essentials/src/main/java/com/earth2me/essentials/AlternativeCommandsHandler.java @@ -29,7 +29,7 @@ public class AlternativeCommandsHandler { } public final void addPlugin(final Plugin plugin) { - if (plugin.getDescription().getMain().contains("com.earth2me.essentials")) { + if (plugin.getDescription().getMain().contains("com.earth2me.essentials") || plugin.getDescription().getMain().contains("net.essentialsx")) { return; } for (final Map.Entry entry : getPluginCommands(plugin).entrySet()) { diff --git a/Essentials/src/main/java/com/earth2me/essentials/utils/NumberUtil.java b/Essentials/src/main/java/com/earth2me/essentials/utils/NumberUtil.java index a4d91e5a8..c9d824914 100644 --- a/Essentials/src/main/java/com/earth2me/essentials/utils/NumberUtil.java +++ b/Essentials/src/main/java/com/earth2me/essentials/utils/NumberUtil.java @@ -104,6 +104,15 @@ public final class NumberUtil { return true; } + public static boolean isLong(final String sLong) { + try { + Long.parseLong(sLong); + } catch (final NumberFormatException e) { + return false; + } + return true; + } + public static boolean isPositiveInt(final String sInt) { if (!isInt(sInt)) { return false; diff --git a/Essentials/src/main/resources/messages.properties b/Essentials/src/main/resources/messages.properties index 6a083cd7a..a140e3036 100644 --- a/Essentials/src/main/resources/messages.properties +++ b/Essentials/src/main/resources/messages.properties @@ -233,6 +233,13 @@ destinationNotSet=Destination not set\! disabled=disabled disabledToSpawnMob=\u00a74Spawning this mob was disabled in the config file. disableUnlimited=\u00a76Disabled unlimited placing of\u00a7c {0} \u00a76for\u00a7c {1}\u00a76. +discordbroadcastCommandDescription=Broadcasts a message to the specified Discord channel. +discordbroadcastCommandUsage=/ +discordbroadcastCommandUsage1=/ +discordbroadcastCommandUsage1Description=Sends the given message to the specified Discord channel +discordbroadcastInvalidChannel=\u00a74Discord channel \u00a7c{0}\u00a74 does not exist. +discordbroadcastPermission=\u00a74You do not have permission to send messages to the \u00a7c{0}\u00a74 channel. +discordbroadcastSent=\u00a76Message sent to \u00a7c{0}\u00a76! discordCommandExecuteDescription=Executes a console command on the Minecraft server. discordCommandExecuteArgumentCommand=The command to be executed discordCommandExecuteReply=Executing command: "/{0}" diff --git a/EssentialsDiscord/src/main/java/net/essentialsx/discord/DiscordSettings.java b/EssentialsDiscord/src/main/java/net/essentialsx/discord/DiscordSettings.java index cb31be06a..e06d5dbc1 100644 --- a/EssentialsDiscord/src/main/java/net/essentialsx/discord/DiscordSettings.java +++ b/EssentialsDiscord/src/main/java/net/essentialsx/discord/DiscordSettings.java @@ -69,6 +69,10 @@ public class DiscordSettings implements IConf { } } + public List getChannelNames() { + return new ArrayList<>(nameToChannelIdMap.keySet()); + } + public List getKeysFromChannelId(long channelId) { return channelIdToNamesMap.get(channelId); } diff --git a/EssentialsDiscord/src/main/java/net/essentialsx/discord/EssentialsDiscord.java b/EssentialsDiscord/src/main/java/net/essentialsx/discord/EssentialsDiscord.java index 1760129c3..438f8455a 100644 --- a/EssentialsDiscord/src/main/java/net/essentialsx/discord/EssentialsDiscord.java +++ b/EssentialsDiscord/src/main/java/net/essentialsx/discord/EssentialsDiscord.java @@ -4,8 +4,13 @@ import com.earth2me.essentials.IEssentials; import com.earth2me.essentials.IEssentialsModule; import com.earth2me.essentials.metrics.MetricsWrapper; import net.essentialsx.discord.interactions.InteractionControllerImpl; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; import org.bukkit.plugin.java.JavaPlugin; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -86,4 +91,17 @@ public class EssentialsDiscord extends JavaPlugin implements IEssentialsModule { jda.shutdown(); } } + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + return ess.onCommandEssentials(sender, command, label, args, EssentialsDiscord.class.getClassLoader(), "net.essentialsx.discord.commands.Command", + "essentials.", jda); + } + + @Nullable + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String alias, @NotNull String[] args) { + return ess.onTabCompleteEssentials(sender, command, alias, args, EssentialsDiscord.class.getClassLoader(), + "net.essentialsx.discord.commands.Command", "essentials.", jda); + } } diff --git a/EssentialsDiscord/src/main/java/net/essentialsx/discord/JDADiscordService.java b/EssentialsDiscord/src/main/java/net/essentialsx/discord/JDADiscordService.java index 7e02352b0..025daf51c 100644 --- a/EssentialsDiscord/src/main/java/net/essentialsx/discord/JDADiscordService.java +++ b/EssentialsDiscord/src/main/java/net/essentialsx/discord/JDADiscordService.java @@ -4,17 +4,21 @@ import club.minnced.discord.webhook.WebhookClient; import club.minnced.discord.webhook.WebhookClientBuilder; import club.minnced.discord.webhook.send.WebhookMessage; import club.minnced.discord.webhook.send.WebhookMessageBuilder; +import com.earth2me.essentials.IEssentialsModule; import com.earth2me.essentials.User; import com.earth2me.essentials.utils.FormatUtil; +import com.earth2me.essentials.utils.NumberUtil; import com.earth2me.essentials.utils.VersionUtil; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; +import net.dv8tion.jda.api.entities.Emote; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.Webhook; import net.dv8tion.jda.api.events.ShutdownEvent; import net.dv8tion.jda.api.hooks.EventListener; import net.dv8tion.jda.api.hooks.ListenerAdapter; +import net.dv8tion.jda.api.utils.cache.CacheFlag; import net.ess3.nms.refl.providers.AchievementListenerProvider; import net.ess3.nms.refl.providers.AdvancementListenerProvider; import net.essentialsx.api.v2.events.discord.DiscordMessageEvent; @@ -51,10 +55,11 @@ import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; +import java.util.regex.Pattern; import static com.earth2me.essentials.I18n.tl; -public class JDADiscordService implements DiscordService { +public class JDADiscordService implements DiscordService, IEssentialsModule { private final static Logger logger = Logger.getLogger("EssentialsDiscord"); private final EssentialsDiscord plugin; private final Unsafe unsafe = this::getJda; @@ -80,12 +85,14 @@ public class JDADiscordService implements DiscordService { } public TextChannel getChannel(String key, boolean primaryFallback) { - long resolvedId; - try { - resolvedId = Long.parseLong(key); - } catch (NumberFormatException ignored) { - resolvedId = getSettings().getChannelId(getSettings().getMessageChannel(key)); + if (NumberUtil.isLong(key)) { + return getDefinedChannel(key, primaryFallback); } + return getDefinedChannel(getSettings().getMessageChannel(key), primaryFallback); + } + + public TextChannel getDefinedChannel(String key, boolean primaryFallback) { + final long resolvedId = getSettings().getChannelId(key); if (isDebug()) { logger.log(Level.INFO, "Channel definition " + key + " resolved as " + resolvedId); @@ -149,8 +156,9 @@ public class JDADiscordService implements DiscordService { jda = JDABuilder.createDefault(plugin.getSettings().getBotToken()) .addEventListeners(new DiscordListener(this)) + .enableCache(CacheFlag.EMOTE) + .disableCache(CacheFlag.MEMBER_OVERRIDES, CacheFlag.VOICE_STATE) .setContextEnabled(false) - .setRawEventsEnabled(true) .build() .awaitReady(); updatePresence(); @@ -182,6 +190,9 @@ public class JDADiscordService implements DiscordService { } catch (InteractionException ignored) { } + // Load emotes into cache, JDA will handle updates from here on out. + guild.retrieveEmotes().queue(); + updatePrimaryChannel(); updateConsoleRelay(); @@ -274,6 +285,13 @@ public class JDADiscordService implements DiscordService { primaryChannel = channel; } + public String parseMessageEmotes(String message) { + for (final Emote emote : guild.getEmoteCache()) { + message = message.replaceAll(":" + Pattern.quote(emote.getName()) + ":", emote.getAsMention()); + } + return message; + } + public void updatePresence() { jda.getPresence().setPresence(plugin.getSettings().getStatus(), plugin.getSettings().getStatusActivity()); } diff --git a/EssentialsDiscord/src/main/java/net/essentialsx/discord/commands/Commanddiscordbroadcast.java b/EssentialsDiscord/src/main/java/net/essentialsx/discord/commands/Commanddiscordbroadcast.java new file mode 100644 index 000000000..d94fa9274 --- /dev/null +++ b/EssentialsDiscord/src/main/java/net/essentialsx/discord/commands/Commanddiscordbroadcast.java @@ -0,0 +1,81 @@ +package net.essentialsx.discord.commands; + +import com.earth2me.essentials.CommandSource; +import com.earth2me.essentials.commands.EssentialsCommand; +import com.earth2me.essentials.commands.NotEnoughArgumentsException; +import com.vdurmont.emoji.EmojiParser; +import net.dv8tion.jda.api.entities.Emote; +import net.dv8tion.jda.api.entities.TextChannel; +import net.essentialsx.discord.JDADiscordService; +import net.essentialsx.discord.util.DiscordUtil; +import net.essentialsx.discord.util.MessageUtil; +import org.bukkit.Server; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static com.earth2me.essentials.I18n.tl; + +public class Commanddiscordbroadcast extends EssentialsCommand { + public Commanddiscordbroadcast() { + super("discordbroadcast"); + } + + @Override + protected void run(Server server, CommandSource sender, String commandLabel, String[] args) throws Exception { + if (args.length < 2) { + throw new NotEnoughArgumentsException(); + } + + if (!sender.isAuthorized("essentials.discordbroadcast." + args[0], ess)) { + throw new Exception(tl("discordbroadcastPermission", args[0])); + } + + String message = getFinalArg(args, 1); + if (!sender.isAuthorized("essentials.discordbroadcast.markdown", ess)) { + message = MessageUtil.sanitizeDiscordMarkdown(message); + } + + final JDADiscordService jda = (JDADiscordService) module; + final TextChannel channel = jda.getDefinedChannel(args[0], false); + if (channel == null) { + throw new Exception(tl("discordbroadcastInvalidChannel", args[0])); + } + + if (!channel.canTalk()) { + throw new Exception(tl("discordNoSendPermission", channel.getName())); + } + + channel.sendMessage(jda.parseMessageEmotes(message)) + .allowedMentions(sender.isAuthorized("essentials.discordbroadcast.ping", ess) ? null : DiscordUtil.NO_GROUP_MENTIONS) + .queue(); + + sender.sendMessage(tl("discordbroadcastSent", "#" + EmojiParser.parseToAliases(channel.getName()))); + } + + @Override + protected List getTabCompleteOptions(Server server, CommandSource sender, String commandLabel, String[] args) { + if (args.length == 1) { + final JDADiscordService jda = (JDADiscordService) module; + final List channels = jda.getSettings().getChannelNames(); + channels.removeIf(s -> !sender.isAuthorized("essentials.discordbroadcast." + s, ess)); + return channels; + } else { + final String curArg = args[args.length - 1]; + if (!curArg.isEmpty() && curArg.charAt(0) == ':' && (curArg.length() == 1 || curArg.charAt(curArg.length() - 1) != ':')) { + final JDADiscordService jda = (JDADiscordService) module; + if (jda.getGuild().getEmoteCache().isEmpty()) { + return Collections.emptyList(); + } + + final List completions = new ArrayList<>(); + for (final Emote emote : jda.getGuild().getEmoteCache()) { + completions.add(":" + emote.getName() + ":"); + } + return completions; + } + } + return Collections.emptyList(); + } +} diff --git a/EssentialsDiscord/src/main/resources/plugin.yml b/EssentialsDiscord/src/main/resources/plugin.yml index 32679b7db..5f9ed3019 100644 --- a/EssentialsDiscord/src/main/resources/plugin.yml +++ b/EssentialsDiscord/src/main/resources/plugin.yml @@ -8,3 +8,8 @@ authors: [mdcfe, JRoy, pop4959, Glare] depend: [Essentials] softdepend: [EssentialsChat, PlaceholderAPI] api-version: 1.13 +commands: + discordbroadcast: + description: Broadcasts a message to the specified Discord channel. + usage: / + aliases: [dbroadcast, dbc, dbcast, ediscordbroadcast, edbroadcast, edbc, edbcast] \ No newline at end of file