diff --git a/pom.xml b/pom.xml
index 4fb93cb..f0c321a 100755
--- a/pom.xml
+++ b/pom.xml
@@ -1,208 +1,225 @@
- 4.0.0
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ 4.0.0
- com.github.TBMCPlugins
- DiscordPlugin
- master-SNAPSHOT
- jar
+ com.github.TBMCPlugins
+ DiscordPlugin
+ master-SNAPSHOT
+ jar
- DiscordPlugin
- http://maven.apache.org
+ DiscordPlugin
+ http://maven.apache.org
-
-
- src/main/java
-
-
- src
-
- **/*.java
-
-
-
- src/main/resources
-
- *.properties
- *.yml
- *.csv
- *.txt
-
- true
-
-
- DiscordPlugin
-
-
- maven-compiler-plugin
- 3.6.2
-
- 1.8
- 1.8
-
-
-
- org.apache.maven.plugins
- maven-shade-plugin
- 2.4.2
-
-
- package
-
- shade
-
-
-
-
- org.spigotmc:spigot-api
- com.github.TBMCPlugins.ButtonCore:ButtonCore
- net.ess3:Essentials
+
+
+ src/main/java
+
+
+ src
+
+ **/*.java
+
+
+
+ src/main/resources
+
+ *.properties
+ *.yml
+ *.csv
+ *.txt
+
+ true
+
+
+ DiscordPlugin
+
+
+ maven-compiler-plugin
+ 3.6.2
+
+ 1.8
+ 1.8
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.4.2
+
+
+ package
+
+ shade
+
+
+
+
+ org.spigotmc:spigot-api
+ com.github.TBMCPlugins.ButtonCore:ButtonCore
+ net.ess3:Essentials
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-resources-plugin
- 3.0.1
-
-
- copy
- compile
-
- copy-resources
-
-
- target
-
-
- resources
-
-
-
-
-
-
-
-
- maven-surefire-plugin
-
- false
-
-
-
-
-
+
+ true
+
+
+ io.netty
+ btndvtm.dp.io.netty
+
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ 3.0.1
+
+
+ copy
+ compile
+
+ copy-resources
+
+
+ target
+
+
+ resources
+
+
+
+
+
+
+
+
+ maven-surefire-plugin
+ 2.4.2
+
+ false
+
+
+
+
+
-
- UTF-8
+
+ UTF-8
master
-
+
-
-
- spigot-repo
- https://hub.spigotmc.org/nexus/content/repositories/snapshots/
-
-
- jcenter
- http://jcenter.bintray.com
-
-
- jitpack.io
- https://jitpack.io
-
+
+
+ spigot-repo
+ https://hub.spigotmc.org/nexus/content/repositories/snapshots/
+
+
+ jcenter
+ http://jcenter.bintray.com
+
+
+ jitpack.io
+ https://jitpack.io
+
-
- Essentials
- http://repo.ess3.net/content/repositories/essrel/
-
-
- projectlombok.org
- http://projectlombok.org/mavenrepo
-
+
+ Essentials
+ http://repo.ess3.net/content/repositories/essrel/
+
+
+ projectlombok.org
+ http://projectlombok.org/mavenrepo
+
-
+
+
-
-
- junit
- junit
- 3.8.1
- test
-
-
- org.spigotmc
- spigot-api
- 1.12-R0.1-SNAPSHOT
- provided
-
-
- org.spigotmc
- spigot
- 1.12.2-R0.1-SNAPSHOT
- provided
-
-
+
+
+ junit
+ junit
+ 3.8.1
+ test
+
+
+ org.spigotmc
+ spigot-api
+ 1.12-R0.1-SNAPSHOT
+ provided
+
+
+ org.spigotmc
+ spigot
+ 1.12.2-R0.1-SNAPSHOT
+ provided
+
+
com.discord4j
- Discord4J
- 2.10.1
+ discord4j-core
+ 3.0.6
-
-
- org.slf4j
- slf4j-jdk14
- 1.7.21
-
-
- com.github.TBMCPlugins.ButtonCore
- ButtonCore
- ${branch}-SNAPSHOT
- provided
-
-
- com.github.milkbowl
- VaultAPI
- master-SNAPSHOT
- provided
-
-
- net.ess3
- Essentials
- 2.13.1
+
+
+ org.slf4j
+ slf4j-jdk14
+ 1.7.21
+
+
+ com.github.TBMCPlugins.ButtonCore
+ ButtonCore
+ ${branch}-SNAPSHOT
provided
-
-
- com.github.xaanit
- D4J-OAuth
- master-SNAPSHOT
-
-
-
- org.projectlombok
- lombok
- 1.16.16
- provided
-
+
+
+ com.github.milkbowl
+ VaultAPI
+ master-SNAPSHOT
+ provided
+
+
+ net.ess3
+ Essentials
+ 2.13.1
+ provided
+
+
+
+ org.projectlombok
+ lombok
+ 1.16.16
+ provided
+
-
-
- org.objenesis
- objenesis
- 2.6
- test
-
-
- com.vdurmont
- emoji-java
- 4.0.0
-
-
+
+
+ org.objenesis
+ objenesis
+ 2.6
+ test
+
+
+ com.vdurmont
+ emoji-java
+ 4.0.0
+
+
+
+
+ com.github.lucko
+ LuckPerms
+ v4.4
+ provided
+
+
-
-
- ci
-
-
- env.TRAVIS_BRANCH
-
-
-
-
- ${env.TRAVIS_BRANCH}
-
-
-
+
+
+ ci
+
+
+ env.TRAVIS_BRANCH
+
+
+
+
+ ${env.TRAVIS_BRANCH}
+
+
+
diff --git a/src/main/java/buttondevteam/discordplugin/AsyncDiscordEvent.java b/src/main/java/buttondevteam/discordplugin/AsyncDiscordEvent.java
deleted file mode 100644
index c4479f3..0000000
--- a/src/main/java/buttondevteam/discordplugin/AsyncDiscordEvent.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package buttondevteam.discordplugin;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import lombok.Setter;
-import org.bukkit.event.Cancellable;
-import org.bukkit.event.Event;
-import org.bukkit.event.HandlerList;
-
-@RequiredArgsConstructor
-public class AsyncDiscordEvent extends Event implements Cancellable {
- private final @Getter T event;
- @Getter
- @Setter
- private boolean cancelled;
-
- private static final HandlerList handlers = new HandlerList();
-
- @Override
- public HandlerList getHandlers() {
- return handlers;
- }
-
- public static HandlerList getHandlerList() {
- return handlers;
- }
-}
diff --git a/src/main/java/buttondevteam/discordplugin/ChromaBot.java b/src/main/java/buttondevteam/discordplugin/ChromaBot.java
index 56a6fb9..6f74671 100755
--- a/src/main/java/buttondevteam/discordplugin/ChromaBot.java
+++ b/src/main/java/buttondevteam/discordplugin/ChromaBot.java
@@ -1,15 +1,14 @@
package buttondevteam.discordplugin;
import buttondevteam.discordplugin.mcchat.MCChatUtils;
+import discord4j.core.object.entity.Message;
+import discord4j.core.object.entity.MessageChannel;
import lombok.Getter;
-import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitScheduler;
-import sx.blah.discord.api.internal.json.objects.EmbedObject;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.util.EmbedBuilder;
+import reactor.core.publisher.Mono;
import javax.annotation.Nullable;
-import java.awt.*;
+import java.util.function.Function;
public class ChromaBot {
/**
@@ -33,113 +32,26 @@ public class ChromaBot {
instance = null;
}
- /**
- * Send a message to the chat channel and private chats.
- *
- * @param message
- * The message to send, duh
- */
- public void sendMessage(String message) {
- MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message));
- }
-
/**
* Send a message to the chat channels and private chats.
*
* @param message
- * The message to send, duh
- * @param embed
- * Custom fancy stuff, use {@link EmbedBuilder} to create one
+ * The message to send, duh (use {@link MessageChannel#createMessage(String)})
*/
- public void sendMessage(String message, EmbedObject embed) {
- MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, embed));
+ public void sendMessage(Function, Mono> message) {
+ MCChatUtils.forAllMCChat(ch -> message.apply(ch).subscribe());
}
/**
* Send a message to the chat channels, private chats and custom chats.
*
* @param message The message to send, duh
- * @param embed Custom fancy stuff, use {@link EmbedBuilder} to create one
* @param toggle The toggle type for channelcon
*/
- public void sendMessageCustomAsWell(String message, EmbedObject embed, @Nullable ChannelconBroadcast toggle) {
- MCChatUtils.forCustomAndAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, embed), toggle, false);
+ public void sendMessageCustomAsWell(Function, Mono> message, @Nullable ChannelconBroadcast toggle) {
+ MCChatUtils.forCustomAndAllMCChat(ch -> message.apply(ch).subscribe(), toggle, false);
}
- /**
- * Send a message to an arbitrary channel. This will not send it to the private chats.
- *
- * @param channel
- * The channel to send to, use the channel variables in {@link DiscordPlugin}
- * @param message
- * The message to send, duh
- * @param embed
- * Custom fancy stuff, use {@link EmbedBuilder} to create one
- */
- public void sendMessage(IChannel channel, String message, EmbedObject embed) {
- DiscordPlugin.sendMessageToChannel(channel, message, embed);
- }
-
- /**
- * Send a fancy message to the chat channels. This will show a bold text with a colored line.
- *
- * @param message
- * The message to send, duh
- * @param color
- * The color of the line before the text
- */
- public void sendMessage(String message, Color color) {
- MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message,
- new EmbedBuilder().withTitle(message).withColor(color).build()));
- }
-
- /**
- * Send a fancy message to the chat channels. This will show a bold text with a colored line.
- *
- * @param message
- * The message to send, duh
- * @param color
- * The color of the line before the text
- * @param mcauthor
- * The name of the Minecraft player who is the author of this message
- */
- public void sendMessage(String message, Color color, String mcauthor) {
- MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message,
- DPUtils.embedWithHead(new EmbedBuilder().withTitle(message).withColor(color), mcauthor).build()));
- }
-
- /**
- * Send a fancy message to the chat channels. This will show a bold text with a colored line.
- *
- * @param message
- * The message to send, duh
- * @param color
- * The color of the line before the text
- * @param authorname
- * The name of the author of this message
- * @param authorimg
- * The URL of the avatar image for this message's author
- */
- public void sendMessage(String message, Color color, String authorname, String authorimg) {
- MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, new EmbedBuilder()
- .withTitle(message).withColor(color).withAuthorName(authorname).withAuthorIcon(authorimg).build()));
- }
-
- /**
- * Send a message to the chat channels. This will show a bold text with a colored line.
- *
- * @param message
- * The message to send, duh
- * @param color
- * The color of the line before the text
- * @param sender
- * The player who sends this message
- */
- public void sendMessage(String message, Color color, Player sender) {
- MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, DPUtils
- .embedWithHead(new EmbedBuilder().withTitle(message).withColor(color), sender.getName()).build()));
- }
-
public void updatePlayerList() {
MCChatUtils.updatePlayerList();
}
diff --git a/src/main/java/buttondevteam/discordplugin/DPUtils.java b/src/main/java/buttondevteam/discordplugin/DPUtils.java
index c21cceb..616b750 100755
--- a/src/main/java/buttondevteam/discordplugin/DPUtils.java
+++ b/src/main/java/buttondevteam/discordplugin/DPUtils.java
@@ -4,128 +4,82 @@ import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.architecture.IHaveConfig;
+import buttondevteam.lib.architecture.ReadOnlyConfigData;
+import discord4j.core.object.entity.Guild;
+import discord4j.core.object.entity.Message;
+import discord4j.core.object.entity.MessageChannel;
+import discord4j.core.object.entity.Role;
+import discord4j.core.object.util.Snowflake;
+import discord4j.core.spec.EmbedCreateSpec;
import lombok.val;
-import org.bukkit.Bukkit;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IGuild;
-import sx.blah.discord.handle.obj.IIDLinkedObject;
-import sx.blah.discord.handle.obj.IRole;
-import sx.blah.discord.util.EmbedBuilder;
-import sx.blah.discord.util.RequestBuffer;
-import sx.blah.discord.util.RequestBuffer.IRequest;
-import sx.blah.discord.util.RequestBuffer.IVoidRequest;
+import reactor.core.publisher.Mono;
import javax.annotation.Nullable;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import java.util.regex.Matcher;
public final class DPUtils {
- public static EmbedBuilder embedWithHead(EmbedBuilder builder, String playername) {
- return builder.withAuthorIcon("https://minotar.net/avatar/" + playername + "/32.png");
+ public static EmbedCreateSpec embedWithHead(EmbedCreateSpec ecs, String displayname, String playername, String profileUrl) {
+ return ecs.setAuthor(displayname, profileUrl, "https://minotar.net/avatar/" + playername + "/32.png");
}
- /**
- * Removes §[char] colour codes from strings & escapes them for Discord
- * Ensure that this method only gets called once (escaping)
- */
- public static String sanitizeString(String string) {
- return escape(sanitizeStringNoEscape(string));
- }
-
- /**
- * Removes §[char] colour codes from strings
- */
- public static String sanitizeStringNoEscape(String string) {
- String sanitizedString = "";
- boolean random = false;
- for (int i = 0; i < string.length(); i++) {
- if (string.charAt(i) == '§') {
- i++;// Skips the data value, the 4 in "§4Alisolarflare"
- random = string.charAt(i) == 'k';
- } else {
- if (!random) // Skip random/obfuscated characters
- sanitizedString += string.charAt(i);
- }
- }
- return sanitizedString;
- }
-
/**
- * Performs Discord actions, retrying when ratelimited. May return null if action fails too many times or in safe mode.
+ * Removes §[char] colour codes from strings & escapes them for Discord
+ * Ensure that this method only gets called once (escaping)
*/
- @Nullable
- public static T perform(IRequest action, long timeout, TimeUnit unit) throws TimeoutException, InterruptedException {
- if (DiscordPlugin.SafeMode)
- return null;
- if (Bukkit.isPrimaryThread()) // TODO: Ignore shutdown message <--
- // throw new RuntimeException("Tried to wait for a Discord request on the main thread. This could cause lag.");
- getLogger().warning("Waiting for a Discord request on the main thread!");
- return RequestBuffer.request(action).get(timeout, unit); // Let the pros handle this
- }
-
- /**
- * Performs Discord actions, retrying when ratelimited. May return null if action fails too many times or in safe mode.
- */
- @Nullable
- public static T perform(IRequest action) {
- if (DiscordPlugin.SafeMode)
- return null;
- if (Bukkit.isPrimaryThread()) // TODO: Ignore shutdown message <--
- // throw new RuntimeException("Tried to wait for a Discord request on the main thread. This could cause lag.");
- getLogger().warning("Waiting for a Discord request on the main thread!");
- return RequestBuffer.request(action).get(); // Let the pros handle this
- }
+ public static String sanitizeString(String string) {
+ return escape(sanitizeStringNoEscape(string));
+ }
/**
- * Performs Discord actions, retrying when ratelimited.
+ * Removes §[char] colour codes from strings
*/
- public static Void perform(IVoidRequest action) {
- if (DiscordPlugin.SafeMode)
- return null;
- if (Bukkit.isPrimaryThread())
- throw new RuntimeException("Tried to wait for a Discord request on the main thread. This could cause lag.");
- return RequestBuffer.request(action).get(); // Let the pros handle this
+ public static String sanitizeStringNoEscape(String string) {
+ StringBuilder sanitizedString = new StringBuilder();
+ boolean random = false;
+ for (int i = 0; i < string.length(); i++) {
+ if (string.charAt(i) == '§') {
+ i++;// Skips the data value, the 4 in "§4Alisolarflare"
+ random = string.charAt(i) == 'k';
+ } else {
+ if (!random) // Skip random/obfuscated characters
+ sanitizedString.append(string.charAt(i));
+ }
+ }
+ return sanitizedString.toString();
}
- public static void performNoWait(IVoidRequest action) {
- if (DiscordPlugin.SafeMode)
- return;
- RequestBuffer.request(action);
+ private static String escape(String message) {
+ return message.replaceAll("([*_~])", Matcher.quoteReplacement("\\") + "$1");
}
- public static void performNoWait(IRequest action) {
- if (DiscordPlugin.SafeMode)
- return;
- RequestBuffer.request(action);
- }
-
- public static String escape(String message) {
- return message.replaceAll("([*_~])", Matcher.quoteReplacement("\\")+"$1");
- }
-
public static Logger getLogger() {
if (DiscordPlugin.plugin == null || DiscordPlugin.plugin.getLogger() == null)
return Logger.getLogger("DiscordPlugin");
return DiscordPlugin.plugin.getLogger();
}
- public static ConfigData channelData(IHaveConfig config, String key, long defID) {
- return config.getDataPrimDef(key, defID, id -> DiscordPlugin.dc.getChannelByID((long) id), IIDLinkedObject::getLongID); //We can afford to search for the channel in the cache once (instead of using mainServer)
+ public static ReadOnlyConfigData> channelData(IHaveConfig config, String key, long defID) {
+ return config.getReadOnlyDataPrimDef(key, defID, id -> getMessageChannel(key, Snowflake.of((Long) id)), ch -> defID); //We can afford to search for the channel in the cache once (instead of using mainServer)
}
- public static ConfigData roleData(IHaveConfig config, String key, String defName) {
- return roleData(config, key, defName, DiscordPlugin.mainServer);
+ public static ReadOnlyConfigData> roleData(IHaveConfig config, String key, String defName) {
+ return roleData(config, key, defName, Mono.just(DiscordPlugin.mainServer));
}
- public static ConfigData roleData(IHaveConfig config, String key, String defName, IGuild guild) {
- return config.getDataPrimDef(key, defName, name -> {
- if (!(name instanceof String)) return null;
- val roles = guild.getRolesByName((String) name);
- return roles.size() > 0 ? roles.get(0) : null;
- }, IIDLinkedObject::getLongID);
+ /**
+ * Needs to be a {@link ConfigData} for checking if it's set
+ */
+ public static ReadOnlyConfigData> roleData(IHaveConfig config, String key, String defName, Mono guild) {
+ return config.getReadOnlyDataPrimDef(key, defName, name -> {
+ if (!(name instanceof String)) return Mono.empty();
+ return guild.flatMapMany(Guild::getRoles).filter(r -> r.getName().equals(name)).next();
+ }, r -> defName);
+ }
+
+ public static ConfigData snowflakeData(IHaveConfig config, String key, long defID) {
+ return config.getDataPrimDef(key, defID, id -> Snowflake.of((long) id), Snowflake::asLong);
}
/**
@@ -134,10 +88,8 @@ public final class DPUtils {
* @return The string for mentioning the channel
*/
public static String botmention() {
- IChannel channel;
- if (DiscordPlugin.plugin == null
- || (channel = DiscordPlugin.plugin.CommandChannel().get()) == null) return "#bot";
- return channel.mention();
+ if (DiscordPlugin.plugin == null) return "#bot";
+ return channelMention(DiscordPlugin.plugin.commandChannel().get());
}
/**
@@ -149,23 +101,66 @@ public final class DPUtils {
*/
public static boolean disableIfConfigError(@Nullable Component component, ConfigData>... configs) {
for (val config : configs) {
- if (config.get() == null) {
- String path = null;
- try {
- if (component != null)
- Component.setComponentEnabled(component, false);
- val f = ConfigData.class.getDeclaredField("path");
- f.setAccessible(true); //Hacking my own plugin
- path = (String) f.get(config);
- } catch (Exception e) {
- TBMCCoreAPI.SendException("Failed to disable component after config error!", e);
- }
- getLogger().warning("The config value " + path + " isn't set correctly " + (component == null ? "in global settings!" : "for component " + component.getClass().getSimpleName() + "!"));
- getLogger().warning("Set the correct ID in the config" + (component == null ? "" : " or disable this component") + " to remove this message.");
+ Object v = config.get();
+ if (disableIfConfigErrorRes(component, config, v))
return true;
- }
}
return false;
}
+ /**
+ * Disables the component if one of the given configs return null. Useful for channel/role configs.
+ *
+ * @param component The component to disable if needed
+ * @param config The (snowflake) config to check for null
+ * @param result The result of getting the value
+ * @return Whether the component got disabled and a warning logged
+ */
+ public static boolean disableIfConfigErrorRes(@Nullable Component component, ConfigData> config, Object result) {
+ //noinspection ConstantConditions
+ if (result == null || (result instanceof Mono> && !((Mono>) result).hasElement().block())) {
+ String path = null;
+ try {
+ if (component != null)
+ Component.setComponentEnabled(component, false);
+ path = config.getPath();
+ } catch (Exception e) {
+ TBMCCoreAPI.SendException("Failed to disable component after config error!", e);
+ }
+ getLogger().warning("The config value " + path + " isn't set correctly " + (component == null ? "in global settings!" : "for component " + component.getClass().getSimpleName() + "!"));
+ getLogger().warning("Set the correct ID in the config" + (component == null ? "" : " or disable this component") + " to remove this message.");
+ return true;
+ }
+ return false;
+ }
+
+ public static Mono reply(Message original, @Nullable MessageChannel channel, String message) {
+ Mono ch;
+ if (channel == null)
+ ch = original.getChannel();
+ else
+ ch = Mono.just(channel);
+ return ch.flatMap(chan -> chan.createMessage((original.getAuthor().isPresent()
+ ? original.getAuthor().get().getMention() + ", " : "") + message));
+ }
+
+ public static String nickMention(Snowflake userId) {
+ return "<@!" + userId.asString() + ">";
+ }
+
+ public static String channelMention(Snowflake channelId) {
+ return "<#" + channelId.asString() + ">";
+ }
+
+ public static Mono getMessageChannel(String key, Snowflake id) {
+ return DiscordPlugin.dc.getChannelById(id).onErrorResume(e -> {
+ getLogger().warning("Failed to get channel data for " + key + "=" + id + " - " + e.getMessage());
+ return Mono.empty();
+ }).filter(ch -> ch instanceof MessageChannel).cast(MessageChannel.class);
+ }
+
+ public static Mono getMessageChannel(ConfigData config) {
+ return getMessageChannel(config.getPath(), config.get());
+ }
+
}
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java
index f5b779f..6e30a53 100755
--- a/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java
+++ b/src/main/java/buttondevteam/discordplugin/DiscordConnectedPlayer.java
@@ -1,19 +1,24 @@
package buttondevteam.discordplugin;
+import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
import buttondevteam.discordplugin.playerfaker.DiscordFakePlayer;
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
+import discord4j.core.object.entity.MessageChannel;
+import discord4j.core.object.entity.User;
import lombok.Getter;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IUser;
+import lombok.Setter;
import java.util.UUID;
public class DiscordConnectedPlayer extends DiscordFakePlayer implements IMCPlayer {
private static int nextEntityId = 10000;
private @Getter VanillaCommandListener vanillaCmdListener;
+ @Getter
+ @Setter
+ private boolean loggedIn = false;
- public DiscordConnectedPlayer(IUser user, IChannel channel, UUID uuid, String mcname) {
- super(user, channel, nextEntityId++, uuid, mcname);
+ public DiscordConnectedPlayer(User user, MessageChannel channel, UUID uuid, String mcname, MinecraftChatModule module) {
+ super(user, channel, nextEntityId++, uuid, mcname, module);
vanillaCmdListener = new VanillaCommandListener<>(this);
}
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlayer.java b/src/main/java/buttondevteam/discordplugin/DiscordPlayer.java
index 0055792..d748433 100755
--- a/src/main/java/buttondevteam/discordplugin/DiscordPlayer.java
+++ b/src/main/java/buttondevteam/discordplugin/DiscordPlayer.java
@@ -1,28 +1,28 @@
-package buttondevteam.discordplugin;
-
-import buttondevteam.discordplugin.mcchat.MCChatPrivate;
-import buttondevteam.lib.player.ChromaGamerBase;
-import buttondevteam.lib.player.UserClass;
-
-@UserClass(foldername = "discord")
-public class DiscordPlayer extends ChromaGamerBase {
- private String did;
- // private @Getter @Setter boolean minecraftChatEnabled;
-
- public DiscordPlayer() {
- }
-
- public String getDiscordID() {
- if (did == null)
- did = plugindata.getString(getFolder() + "_id");
- return did;
- }
-
- /**
- * Returns true if player has the private Minecraft chat enabled. For setting the value, see
- * {@link MCChatPrivate#privateMCChat(sx.blah.discord.handle.obj.IChannel, boolean, sx.blah.discord.handle.obj.IUser, DiscordPlayer)}
- */
- public boolean isMinecraftChatEnabled() {
- return MCChatPrivate.isMinecraftChatEnabled(this);
- }
-}
+package buttondevteam.discordplugin;
+
+import buttondevteam.discordplugin.mcchat.MCChatPrivate;
+import buttondevteam.lib.player.ChromaGamerBase;
+import buttondevteam.lib.player.UserClass;
+
+@UserClass(foldername = "discord")
+public class DiscordPlayer extends ChromaGamerBase {
+ private String did;
+ // private @Getter @Setter boolean minecraftChatEnabled;
+
+ public DiscordPlayer() {
+ }
+
+ public String getDiscordID() {
+ if (did == null)
+ did = plugindata.getString(getFolder() + "_id");
+ return did;
+ }
+
+ /**
+ * Returns true if player has the private Minecraft chat enabled. For setting the value, see
+ * {@link MCChatPrivate#privateMCChat(sx.blah.discord.handle.obj.MessageChannel, boolean, sx.blah.discord.handle.obj.User, DiscordPlayer)}
+ */
+ public boolean isMinecraftChatEnabled() {
+ return MCChatPrivate.isMinecraftChatEnabled(this);
+ }
+}
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java b/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java
index 2c10314..807abd4 100755
--- a/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java
+++ b/src/main/java/buttondevteam/discordplugin/DiscordPlayerSender.java
@@ -1,6 +1,8 @@
package buttondevteam.discordplugin;
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
+import discord4j.core.object.entity.MessageChannel;
+import discord4j.core.object.entity.User;
import lombok.Getter;
import org.bukkit.*;
import org.bukkit.advancement.Advancement;
@@ -26,8 +28,6 @@ import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.util.Vector;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IUser;
import java.net.InetSocketAddress;
import java.util.*;
@@ -38,7 +38,7 @@ public class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer<
protected Player player;
private @Getter VanillaCommandListener vanillaCmdListener;
- public DiscordPlayerSender(IUser user, IChannel channel, Player player) {
+ public DiscordPlayerSender(User user, MessageChannel channel, Player player) {
super(user, channel);
this.player = player;
vanillaCmdListener = new VanillaCommandListener(this);
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java
index 6d71070..b5a0efc 100755
--- a/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java
+++ b/src/main/java/buttondevteam/discordplugin/DiscordPlugin.java
@@ -10,16 +10,27 @@ import buttondevteam.discordplugin.listeners.MCListener;
import buttondevteam.discordplugin.mcchat.MCChatPrivate;
import buttondevteam.discordplugin.mcchat.MCChatUtils;
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
-import buttondevteam.discordplugin.mccommands.DiscordMCCommandBase;
-import buttondevteam.discordplugin.mccommands.ResetMCCommand;
+import buttondevteam.discordplugin.mccommands.DiscordMCCommand;
import buttondevteam.discordplugin.role.GameRoleModule;
+import buttondevteam.discordplugin.util.Timings;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.ButtonPlugin;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
-import buttondevteam.lib.chat.TBMCChatAPI;
+import buttondevteam.lib.architecture.IHaveConfig;
import buttondevteam.lib.player.ChromaGamerBase;
import com.google.common.io.Files;
+import discord4j.core.DiscordClient;
+import discord4j.core.DiscordClientBuilder;
+import discord4j.core.event.domain.guild.GuildCreateEvent;
+import discord4j.core.event.domain.lifecycle.ReadyEvent;
+import discord4j.core.object.entity.Guild;
+import discord4j.core.object.entity.Role;
+import discord4j.core.object.presence.Activity;
+import discord4j.core.object.presence.Presence;
+import discord4j.core.object.reaction.ReactionEmoji;
+import discord4j.core.object.util.Snowflake;
+import discord4j.store.jdk.JdkStoreService;
import lombok.Getter;
import lombok.val;
import net.milkbowl.vault.permission.Permission;
@@ -27,310 +38,254 @@ import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.plugin.RegisteredServiceProvider;
-import org.bukkit.scheduler.BukkitTask;
-import sx.blah.discord.api.ClientBuilder;
-import sx.blah.discord.api.IDiscordClient;
-import sx.blah.discord.api.events.IListener;
-import sx.blah.discord.api.internal.json.objects.EmbedObject;
-import sx.blah.discord.handle.impl.events.ReadyEvent;
-import sx.blah.discord.handle.impl.obj.ReactionEmoji;
-import sx.blah.discord.handle.obj.*;
-import sx.blah.discord.util.EmbedBuilder;
-import sx.blah.discord.util.RequestBuffer;
+import reactor.core.publisher.Mono;
import java.awt.*;
import java.io.File;
import java.nio.charset.StandardCharsets;
+import java.util.List;
+import java.util.Objects;
import java.util.Optional;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
-public class DiscordPlugin extends ButtonPlugin implements IListener {
- public static IDiscordClient dc;
- public static DiscordPlugin plugin;
- public static boolean SafeMode = true;
+@ButtonPlugin.ConfigOpts(disableConfigGen = true)
+public class DiscordPlugin extends ButtonPlugin {
+ public static DiscordClient dc;
+ public static DiscordPlugin plugin;
+ public static boolean SafeMode = true;
@Getter
private Command2DC manager;
- public ConfigData Prefix() {
- return getIConfig().getData("prefix", '/', str -> ((String) str).charAt(0), Object::toString);
- }
-
- public static char getPrefix() {
- if (plugin == null) return '/';
- return plugin.Prefix().get();
- }
-
- public ConfigData MainServer() {
- return getIConfig().getDataPrimDef("mainServer", 219529124321034241L, id -> dc.getGuildByID((long) id), IIDLinkedObject::getLongID);
+ private ConfigData prefix() {
+ return getIConfig().getData("prefix", '/', str -> ((String) str).charAt(0), Object::toString);
}
- public ConfigData CommandChannel() {
- return DPUtils.channelData(getIConfig(), "commandChannel", 239519012529111040L);
+ public static char getPrefix() {
+ if (plugin == null) return '/';
+ return plugin.prefix().get();
}
- public ConfigData ModRole() {
+ private ConfigData> mainServer() {
+ return getIConfig().getDataPrimDef("mainServer", 0L,
+ id -> {
+ //It attempts to get the default as well
+ if ((long) id == 0L)
+ return Optional.empty(); //Hack?
+ return dc.getGuildById(Snowflake.of((long) id))
+ .onErrorResume(t -> Mono.fromRunnable(() -> getLogger().warning("Failed to get guild: " + t.getMessage()))).blockOptional();
+ },
+ g -> g.map(gg -> gg.getId().asLong()).orElse(0L));
+ }
+
+ public ConfigData commandChannel() {
+ return DPUtils.snowflakeData(getIConfig(), "commandChannel", 239519012529111040L);
+ }
+
+ /**
+ * If the role doesn't exist, then it will only allow for the owner.
+ */
+ public ConfigData> modRole() {
return DPUtils.roleData(getIConfig(), "modRole", "Moderator");
}
- @Override
- public void pluginEnable() {
- try {
- getLogger().info("Initializing...");
- plugin = this;
- manager = new Command2DC();
- ClientBuilder cb = new ClientBuilder();
- File tokenFile = new File("TBMC", "Token.txt");
- if (tokenFile.exists()) //Legacy support
- //noinspection UnstableApiUsage
- cb.withToken(Files.readFirstLine(tokenFile, StandardCharsets.UTF_8));
- else {
- File privateFile = new File(getDataFolder(), "private.yml");
- val conf = YamlConfiguration.loadConfiguration(privateFile);
- String token = conf.getString("token");
- if (token == null) {
- conf.set("token", "Token goes here");
- conf.save(privateFile);
+ /**
+ * The invite link to show by /discord invite. If empty, it defaults to the first invite if the bot has access.
+ */
+ public ConfigData inviteLink() {
+ return getIConfig().getData("inviteLink", "");
+ }
- getLogger().severe("Token not found! Set it in private.yml");
- Bukkit.getPluginManager().disablePlugin(this);
- return;
- } else
- cb.withToken(token);
- }
- dc = cb.login();
- dc.getDispatcher().registerListener(this);
- } catch (Exception e) {
- e.printStackTrace();
- Bukkit.getPluginManager().disablePlugin(this);
- }
- }
+ @Override
+ public void pluginEnable() {
+ try {
+ getLogger().info("Initializing...");
+ plugin = this;
+ manager = new Command2DC();
+ String token;
+ File tokenFile = new File("TBMC", "Token.txt");
+ if (tokenFile.exists()) //Legacy support
+ //noinspection UnstableApiUsage
+ token = Files.readFirstLine(tokenFile, StandardCharsets.UTF_8);
+ else {
+ File privateFile = new File(getDataFolder(), "private.yml");
+ val conf = YamlConfiguration.loadConfiguration(privateFile);
+ token = conf.getString("token");
+ if (token == null || token.equalsIgnoreCase("Token goes here")) {
+ conf.set("token", "Token goes here");
+ conf.save(privateFile);
- public static IGuild mainServer;
+ getLogger().severe("Token not found! Set it in private.yml");
+ Bukkit.getPluginManager().disablePlugin(this);
+ return;
+ }
+ }
+ val cb = new DiscordClientBuilder(token);
+ cb.setInitialPresence(Presence.doNotDisturb(Activity.playing("booting")));
+ cb.setStoreService(new JdkStoreService()); //The default doesn't work for some reason - it's waaay faster now
+ dc = cb.build();
+ dc.getEventDispatcher().on(ReadyEvent.class) // Listen for ReadyEvent(s)
+ .map(event -> event.getGuilds().size()) // Get how many guilds the bot is in
+ .flatMap(size -> dc.getEventDispatcher()
+ .on(GuildCreateEvent.class) // Listen for GuildCreateEvent(s)
+ .take(size) // Take only the first `size` GuildCreateEvent(s) to be received
+ .collectList()) // Take all received GuildCreateEvents and make it a List
+ .subscribe(this::handleReady); /* All guilds have been received, client is fully connected */
+ dc.login().subscribe();
+ } catch (Exception e) {
+ e.printStackTrace();
+ Bukkit.getPluginManager().disablePlugin(this);
+ }
+ }
- private static volatile BukkitTask task;
- private static volatile boolean sent = false;
+ public static Guild mainServer;
- @Override
- public void handle(ReadyEvent event) {
- try {
- dc.changePresence(StatusType.DND, ActivityType.PLAYING, "booting");
- val tries = new AtomicInteger();
- task = Bukkit.getScheduler().runTaskTimerAsynchronously(this, () -> {
- tries.incrementAndGet();
- if (tries.get() > 10) { //5 seconds
- task.cancel();
- getLogger().severe("Main server not found! Invite the bot and do /discord reset");
- //getIConfig().getConfig().set("mainServer", 219529124321034241L); //Needed because it won't save as long as it's null - made it save
- saveConfig(); //Put default there
- return;
- }
- mainServer = MainServer().get(); //Shouldn't change afterwards
- if (mainServer == null) {
- val guilds = dc.getGuilds();
- if (guilds.size() == 0)
- return; //If there are no guilds in cache, retry
- mainServer = guilds.get(0);
- getLogger().warning("Main server set to first one: " + mainServer.getName());
- MainServer().set(mainServer); //Save in config
- }
- if (!TBMCCoreAPI.IsTestServer()) { //Don't change conditions here, see mainServer=devServer=null in onDisable()
- dc.changePresence(StatusType.ONLINE, ActivityType.PLAYING, "Minecraft");
- } else {
- dc.changePresence(StatusType.ONLINE, ActivityType.PLAYING, "testing");
- }
- SafeMode = false;
- if (task != null)
- task.cancel();
- if (!sent) {
- DPUtils.disableIfConfigError(null, CommandChannel(), ModRole()); //Won't disable, just prints the warning here
+ private void handleReady(List event) {
+ try {
+ mainServer = mainServer().get().orElse(null); //Shouldn't change afterwards
+ if (mainServer == null) {
+ if (event.size() == 0) {
+ getLogger().severe("Main server not found! Invite the bot and do /discord reset");
+ saveConfig(); //Put default there
+ return; //We should have all guilds by now, no need to retry
+ }
+ mainServer = event.get(0).getGuild();
+ getLogger().warning("Main server set to first one: " + mainServer.getName());
+ mainServer().set(Optional.of(mainServer)); //Save in config
+ }
+ SafeMode = false;
+ DPUtils.disableIfConfigErrorRes(null, commandChannel(), DPUtils.getMessageChannel(commandChannel()));
+ DPUtils.disableIfConfigError(null, modRole()); //Won't disable, just prints the warning here
- Component.registerComponent(this, new GeneralEventBroadcasterModule());
- Component.registerComponent(this, new MinecraftChatModule());
- Component.registerComponent(this, new ExceptionListenerModule());
- Component.registerComponent(this, new GameRoleModule()); //Needs the mainServer to be set
- Component.registerComponent(this, new AnnouncerModule());
- Component.registerComponent(this, new FunModule());
- new ChromaBot(this).updatePlayerList(); //Initialize ChromaBot - The MCCHatModule is tested to be enabled
+ Component.registerComponent(this, new GeneralEventBroadcasterModule());
+ Component.registerComponent(this, new MinecraftChatModule());
+ Component.registerComponent(this, new ExceptionListenerModule());
+ Component.registerComponent(this, new GameRoleModule()); //Needs the mainServer to be set
+ Component.registerComponent(this, new AnnouncerModule());
+ Component.registerComponent(this, new FunModule());
+ new ChromaBot(this).updatePlayerList(); //Initialize ChromaBot - The MCCHatModule is tested to be enabled
- getManager().registerCommand(new VersionCommand());
- getManager().registerCommand(new UserinfoCommand());
- getManager().registerCommand(new HelpCommand());
- getManager().registerCommand(new DebugCommand());
- getManager().registerCommand(new ConnectCommand());
- if (ResetMCCommand.resetting) //These will only execute if the chat is enabled
- ChromaBot.getInstance().sendMessageCustomAsWell("", new EmbedBuilder().withColor(Color.CYAN)
- .withTitle("Discord plugin restarted - chat connected.").build(), ChannelconBroadcast.RESTART); //Really important to note the chat, hmm
- else if (getConfig().getBoolean("serverup", false)) {
- ChromaBot.getInstance().sendMessageCustomAsWell("", new EmbedBuilder().withColor(Color.YELLOW)
- .withTitle("Server recovered from a crash - chat connected.").build(), ChannelconBroadcast.RESTART);
- val thr = new Throwable(
- "The server shut down unexpectedly. See the log of the previous run for more details.");
- thr.setStackTrace(new StackTraceElement[0]);
- TBMCCoreAPI.SendException("The server crashed!", thr);
- } else
- ChromaBot.getInstance().sendMessageCustomAsWell("", new EmbedBuilder().withColor(Color.GREEN)
- .withTitle("Server started - chat connected.").build(), ChannelconBroadcast.RESTART);
+ getManager().registerCommand(new VersionCommand());
+ getManager().registerCommand(new UserinfoCommand());
+ getManager().registerCommand(new HelpCommand());
+ getManager().registerCommand(new DebugCommand());
+ getManager().registerCommand(new ConnectCommand());
+ if (DiscordMCCommand.resetting) //These will only execute if the chat is enabled
+ ChromaBot.getInstance().sendMessageCustomAsWell(chan -> chan.flatMap(ch -> ch.createEmbed(ecs -> ecs.setColor(Color.CYAN)
+ .setTitle("Discord plugin restarted - chat connected."))), ChannelconBroadcast.RESTART); //Really important to note the chat, hmm
+ else if (getConfig().getBoolean("serverup", false)) {
+ ChromaBot.getInstance().sendMessageCustomAsWell(chan -> chan.flatMap(ch -> ch.createEmbed(ecs -> ecs.setColor(Color.YELLOW)
+ .setTitle("Server recovered from a crash - chat connected."))), ChannelconBroadcast.RESTART);
+ val thr = new Throwable(
+ "The server shut down unexpectedly. See the log of the previous run for more details.");
+ thr.setStackTrace(new StackTraceElement[0]);
+ TBMCCoreAPI.SendException("The server crashed!", thr);
+ } else
+ ChromaBot.getInstance().sendMessageCustomAsWell(chan -> chan.flatMap(ch -> ch.createEmbed(ecs -> ecs.setColor(Color.GREEN)
+ .setTitle("Server started - chat connected."))), ChannelconBroadcast.RESTART);
- ResetMCCommand.resetting = false; //This is the last event handling this flag
+ DiscordMCCommand.resetting = false; //This is the last event handling this flag
- getConfig().set("serverup", true);
- saveConfig();
- sent = true;
- if (TBMCCoreAPI.IsTestServer() && !dc.getOurUser().getName().toLowerCase().contains("test")) {
- TBMCCoreAPI.SendException(
- "Won't load because we're in testing mode and not using a separate account.",
- new Exception(
- "The plugin refuses to load until you change the token to a testing account. (The account needs to have \"test\" in it's name.)"));
- Bukkit.getPluginManager().disablePlugin(this);
- }
- TBMCCoreAPI.SendUnsentExceptions();
- TBMCCoreAPI.SendUnsentDebugMessages();
- }
- }, 0, 10);
- for (IListener> listener : CommonListeners.getListeners())
- dc.getDispatcher().registerListener(listener);
- TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), this);
- TBMCChatAPI.AddCommands(this, DiscordMCCommandBase.class);
- TBMCCoreAPI.RegisterUserClass(DiscordPlayer.class);
- ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof DiscordSenderBase
- ? ((DiscordSenderBase) sender).getChromaUser() : null));
- setupProviders();
- } catch (Exception e) {
- TBMCCoreAPI.SendException("An error occured while enabling DiscordPlugin!", e);
- }
- }
+ getConfig().set("serverup", true);
+ saveConfig();
+ if (TBMCCoreAPI.IsTestServer() && !Objects.requireNonNull(dc.getSelf().block()).getUsername().toLowerCase().contains("test")) {
+ TBMCCoreAPI.SendException(
+ "Won't load because we're in testing mode and not using a separate account.",
+ new Exception(
+ "The plugin refuses to load until you change the token to a testing account. (The account needs to have \"test\" in its name.)"
+ + "\nYou can disable test mode in ThorpeCore config."));
+ Bukkit.getPluginManager().disablePlugin(this);
+ }
+ TBMCCoreAPI.SendUnsentExceptions();
+ TBMCCoreAPI.SendUnsentDebugMessages();
+
+ CommonListeners.register(dc.getEventDispatcher());
+ TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), this);
+ getCommand2MC().registerCommand(new DiscordMCCommand());
+ TBMCCoreAPI.RegisterUserClass(DiscordPlayer.class);
+ ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof DiscordSenderBase
+ ? ((DiscordSenderBase) sender).getChromaUser() : null));
+ setupProviders();
+
+ IHaveConfig.pregenConfig(this, null);
+ if (!TBMCCoreAPI.IsTestServer()) {
+ dc.updatePresence(Presence.online(Activity.playing("Minecraft"))).subscribe();
+ } else {
+ dc.updatePresence(Presence.online(Activity.playing("testing"))).subscribe();
+ }
+ } catch (Exception e) {
+ TBMCCoreAPI.SendException("An error occurred while enabling DiscordPlugin!", e);
+ }
+ }
/**
- * Always true, except when running "stop" from console
- */
- public static boolean Restart;
+ * Always true, except when running "stop" from console
+ */
+ public static boolean Restart;
@Override
public void pluginPreDisable() {
if (ChromaBot.getInstance() == null) return; //Failed to load
- EmbedObject embed;
- if (ResetMCCommand.resetting)
- embed = new EmbedBuilder().withColor(Color.ORANGE).withTitle("Discord plugin restarting").build();
- else
- embed = new EmbedBuilder().withColor(Restart ? Color.ORANGE : Color.RED)
- .withTitle(Restart ? "Server restarting" : "Server stopping")
- .withDescription(
- Bukkit.getOnlinePlayers().size() > 0
- ? (DPUtils
- .sanitizeString(Bukkit.getOnlinePlayers().stream()
- .map(Player::getDisplayName).collect(Collectors.joining(", ")))
- + (Bukkit.getOnlinePlayers().size() == 1 ? " was " : " were ")
- + "kicked the hell out.") //TODO: Make configurable
- : "") //If 'restart' is disabled then this isn't shown even if joinleave is enabled
- .build();
- MCChatUtils.forCustomAndAllMCChat(ch -> {
- try {
- DiscordPlugin.sendMessageToChannelWait(ch, "",
- embed, 5, TimeUnit.SECONDS);
- } catch (TimeoutException | InterruptedException e) {
- e.printStackTrace();
- }
- }, ChannelconBroadcast.RESTART, false);
+ Timings timings = new Timings();
+ timings.printElapsed("Disable start");
+ MCChatUtils.forCustomAndAllMCChat(chan -> chan.flatMap(ch -> ch.createEmbed(ecs -> {
+ timings.printElapsed("Sending message to " + ch.getMention());
+ if (DiscordMCCommand.resetting)
+ ecs.setColor(Color.ORANGE).setTitle("Discord plugin restarting");
+ else
+ ecs.setColor(Restart ? Color.ORANGE : Color.RED)
+ .setTitle(Restart ? "Server restarting" : "Server stopping")
+ .setDescription(
+ Bukkit.getOnlinePlayers().size() > 0
+ ? (DPUtils
+ .sanitizeString(Bukkit.getOnlinePlayers().stream()
+ .map(Player::getDisplayName).collect(Collectors.joining(", ")))
+ + (Bukkit.getOnlinePlayers().size() == 1 ? " was " : " were ")
+ + "kicked the hell out.") //TODO: Make configurable
+ : ""); //If 'restart' is disabled then this isn't shown even if joinleave is enabled
+ })).subscribe(), ChannelconBroadcast.RESTART, false);
+ timings.printElapsed("Updating player list");
ChromaBot.getInstance().updatePlayerList();
+ timings.printElapsed("Done");
}
@Override
public void pluginDisable() {
+ Timings timings = new Timings();
+ timings.printElapsed("Actual disable start (logout)");
MCChatPrivate.logoutAll();
+ timings.printElapsed("Config setup");
getConfig().set("serverup", false);
if (ChromaBot.getInstance() == null) return; //Failed to load
saveConfig();
- try {
- SafeMode = true; // Stop interacting with Discord
- ChromaBot.delete();
- dc.changePresence(StatusType.IDLE, ActivityType.PLAYING, "Chromacraft"); //No longer using the same account for testing
- dc.logout();
- //Configs are emptied so channels and servers are fetched again
- sent = false;
- } catch (Exception e) {
- TBMCCoreAPI.SendException("An error occured while disabling DiscordPlugin!", e);
- }
- }
+ try {
+ SafeMode = true; // Stop interacting with Discord
+ ChromaBot.delete();
+ timings.printElapsed("Updating presence...");
+ dc.updatePresence(Presence.idle(Activity.playing("Chromacraft"))).block(); //No longer using the same account for testing
+ timings.printElapsed("Logging out...");
+ dc.logout().block();
+ //Configs are emptied so channels and servers are fetched again
+ } catch (Exception e) {
+ TBMCCoreAPI.SendException("An error occured while disabling DiscordPlugin!", e);
+ }
+ }
- public static final ReactionEmoji DELIVERED_REACTION = ReactionEmoji.of("✅");
+ public static final ReactionEmoji DELIVERED_REACTION = ReactionEmoji.unicode("✅");
- public static void sendMessageToChannel(IChannel channel, String message) {
- sendMessageToChannel(channel, message, null);
- }
+ public static Permission perms;
- public static void sendMessageToChannel(IChannel channel, String message, EmbedObject embed) {
- try {
- sendMessageToChannel(channel, message, embed, false);
- } catch (TimeoutException | InterruptedException e) {
- e.printStackTrace(); //Shouldn't happen, as we're not waiting on the result
- }
- }
+ private boolean setupProviders() {
+ try {
+ Class.forName("net.milkbowl.vault.permission.Permission");
+ Class.forName("net.milkbowl.vault.chat.Chat");
+ } catch (ClassNotFoundException e) {
+ return false;
+ }
- public static IMessage sendMessageToChannelWait(IChannel channel, String message) throws TimeoutException, InterruptedException {
- return sendMessageToChannelWait(channel, message, null);
- }
-
- public static IMessage sendMessageToChannelWait(IChannel channel, String message, EmbedObject embed) throws TimeoutException, InterruptedException {
- return sendMessageToChannel(channel, message, embed, true);
- }
-
- public static IMessage sendMessageToChannelWait(IChannel channel, String message, EmbedObject embed, long timeout, TimeUnit unit) throws TimeoutException, InterruptedException {
- return sendMessageToChannel(channel, message, embed, true, timeout, unit);
- }
-
- private static IMessage sendMessageToChannel(IChannel channel, String message, EmbedObject embed, boolean wait) throws TimeoutException, InterruptedException {
- return sendMessageToChannel(channel, message, embed, wait, -1, null);
- }
-
- private static IMessage sendMessageToChannel(IChannel channel, String message, EmbedObject embed, boolean wait, long timeout, TimeUnit unit) throws TimeoutException, InterruptedException {
- if (message.length() > 1980) {
- message = message.substring(0, 1980);
- DPUtils.getLogger()
- .warning("Message was too long to send to discord and got truncated. In " + channel.getName());
- }
- try {
- MCChatUtils.resetLastMessage(channel); // If this is a chat message, it'll be set again
- final String content = message;
- RequestBuffer.IRequest r = () -> embed == null ? channel.sendMessage(content)
- : channel.sendMessage(content, embed, false);
- if (wait) {
- if (unit != null)
- return DPUtils.perform(r, timeout, unit);
- else
- return DPUtils.perform(r);
- } else {
- if (unit != null)
- plugin.getLogger().warning("Tried to set timeout for non-waiting call.");
- else
- DPUtils.performNoWait(r);
- return null;
- }
- } catch (TimeoutException | InterruptedException e) {
- throw e;
- } catch (Exception e) {
- DPUtils.getLogger().warning(
- "Failed to deliver message to Discord! Channel: " + channel.getName() + " Message: " + message);
- throw new RuntimeException(e);
- }
- }
-
- public static Permission perms;
-
- public boolean setupProviders() {
- try {
- Class.forName("net.milkbowl.vault.permission.Permission");
- Class.forName("net.milkbowl.vault.chat.Chat");
- } catch (ClassNotFoundException e) {
- return false;
- }
-
- RegisteredServiceProvider permsProvider = Bukkit.getServer().getServicesManager()
- .getRegistration(Permission.class);
- perms = permsProvider.getProvider();
- return perms != null;
- }
+ RegisteredServiceProvider permsProvider = Bukkit.getServer().getServicesManager()
+ .getRegistration(Permission.class);
+ perms = permsProvider.getProvider();
+ return perms != null;
+ }
}
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordRunnable.java b/src/main/java/buttondevteam/discordplugin/DiscordRunnable.java
deleted file mode 100755
index fb27234..0000000
--- a/src/main/java/buttondevteam/discordplugin/DiscordRunnable.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package buttondevteam.discordplugin;
-
-import sx.blah.discord.util.DiscordException;
-import sx.blah.discord.util.MissingPermissionsException;
-import sx.blah.discord.util.RateLimitException;
-
-@FunctionalInterface
-public interface DiscordRunnable {
- public abstract void run() throws DiscordException, RateLimitException, MissingPermissionsException;
-}
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordSender.java b/src/main/java/buttondevteam/discordplugin/DiscordSender.java
index 8ad7445..9eda278 100755
--- a/src/main/java/buttondevteam/discordplugin/DiscordSender.java
+++ b/src/main/java/buttondevteam/discordplugin/DiscordSender.java
@@ -1,5 +1,9 @@
package buttondevteam.discordplugin;
+import discord4j.core.object.entity.Member;
+import discord4j.core.object.entity.MessageChannel;
+import discord4j.core.object.entity.User;
+import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
@@ -8,8 +12,6 @@ import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IUser;
import java.util.Set;
@@ -18,12 +20,13 @@ public class DiscordSender extends DiscordSenderBase implements CommandSender {
private String name;
- public DiscordSender(IUser user, IChannel channel) {
+ public DiscordSender(User user, MessageChannel channel) {
super(user, channel);
- name = user == null ? "Discord user" : user.getDisplayName(DiscordPlugin.mainServer);
+ val def = "Discord user";
+ name = user == null ? def : user.asMember(DiscordPlugin.mainServer.getId()).blockOptional().map(Member::getDisplayName).orElse(def);
}
- public DiscordSender(IUser user, IChannel channel, String name) {
+ public DiscordSender(User user, MessageChannel channel, String name) {
super(user, channel);
this.name = name;
}
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.java b/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.java
index 6d4098b..103a350 100755
--- a/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.java
+++ b/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.java
@@ -1,20 +1,20 @@
package buttondevteam.discordplugin;
import buttondevteam.lib.TBMCCoreAPI;
+import discord4j.core.object.entity.MessageChannel;
+import discord4j.core.object.entity.User;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.scheduler.BukkitTask;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IUser;
public abstract class DiscordSenderBase implements CommandSender {
/**
* May be null.
*/
- protected IUser user;
- protected IChannel channel;
+ protected User user;
+ protected MessageChannel channel;
- protected DiscordSenderBase(IUser user, IChannel channel) {
+ protected DiscordSenderBase(User user, MessageChannel channel) {
this.user = user;
this.channel = channel;
}
@@ -27,11 +27,11 @@ public abstract class DiscordSenderBase implements CommandSender {
*
* @return The user or null.
*/
- public IUser getUser() {
+ public User getUser() {
return user;
}
- public IChannel getChannel() {
+ public MessageChannel getChannel() {
return channel;
}
@@ -43,7 +43,7 @@ public abstract class DiscordSenderBase implements CommandSender {
* @return A Chroma user of Discord or a Discord user of Chroma
*/
public DiscordPlayer getChromaUser() {
- if (chromaUser == null) chromaUser = DiscordPlayer.getUser(user.getStringID(), DiscordPlayer.class);
+ if (chromaUser == null) chromaUser = DiscordPlayer.getUser(user.getId().asString(), DiscordPlayer.class);
return chromaUser;
}
@@ -58,8 +58,7 @@ public abstract class DiscordSenderBase implements CommandSender {
msgtosend += "\n" + sendmsg;
if (sendtask == null)
sendtask = Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> {
- DiscordPlugin.sendMessageToChannel(channel,
- (!broadcast && user != null ? user.mention() + "\n" : "") + msgtosend.trim());
+ channel.createMessage((!broadcast && user != null ? user.getMention() + "\n" : "") + msgtosend.trim()).subscribe();
sendtask = null;
msgtosend = "";
}, 4); // Waits a 0.2 second to gather all/most of the different messages
diff --git a/src/main/java/buttondevteam/discordplugin/DiscordSupplier.java b/src/main/java/buttondevteam/discordplugin/DiscordSupplier.java
deleted file mode 100755
index e2fb570..0000000
--- a/src/main/java/buttondevteam/discordplugin/DiscordSupplier.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package buttondevteam.discordplugin;
-
-import sx.blah.discord.handle.obj.IDiscordObject;
-import sx.blah.discord.util.DiscordException;
-import sx.blah.discord.util.MissingPermissionsException;
-import sx.blah.discord.util.RateLimitException;
-
-@FunctionalInterface
-public interface DiscordSupplier> {
- public abstract T get() throws DiscordException, RateLimitException, MissingPermissionsException;
-}
diff --git a/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.java b/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.java
index c434dce..11250a8 100644
--- a/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.java
+++ b/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.java
@@ -6,40 +6,48 @@ import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
+import buttondevteam.lib.architecture.ReadOnlyConfigData;
import buttondevteam.lib.player.ChromaGamerBase;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
+import discord4j.core.object.entity.Message;
+import discord4j.core.object.entity.MessageChannel;
import lombok.val;
import org.bukkit.configuration.file.YamlConfiguration;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IMessage;
+import reactor.core.publisher.Flux;
+import reactor.core.publisher.Mono;
import java.io.File;
-import java.util.List;
public class AnnouncerModule extends Component {
- public ConfigData channel() {
+ /**
+ * Channel to post new posts.
+ */
+ public ReadOnlyConfigData> channel() {
return DPUtils.channelData(getConfig(), "channel", 239519012529111040L);
}
- public ConfigData modChannel() {
+ /**
+ * Channel where distinguished (moderator) posts go.
+ */
+ public ReadOnlyConfigData> modChannel() {
return DPUtils.channelData(getConfig(), "modChannel", 239519012529111040L);
}
/**
- * Set to 0 or >50 to disable
+ * Automatically unpins all messages except the last few. Set to 0 or >50 to disable
*/
public ConfigData keepPinned() {
return getConfig().getData("keepPinned", (short) 40);
}
- private ConfigData lastannouncementtime() {
+ private ConfigData lastAnnouncementTime() {
return getConfig().getData("lastAnnouncementTime", 0L);
}
- private ConfigData lastseentime() {
+ private ConfigData lastSeenTime() {
return getConfig().getData("lastSeenTime", 0L);
}
@@ -50,24 +58,15 @@ public class AnnouncerModule extends Component {
protected void enable() {
if (DPUtils.disableIfConfigError(this, channel(), modChannel())) return;
stop = false; //If not the first time
- DPUtils.performNoWait(() -> {
- try {
- val keepPinned = keepPinned().get();
- if (keepPinned == 0) return;
- val channel = channel().get();
- List msgs = channel.getPinnedMessages();
- for (int i = msgs.size() - 1; i >= keepPinned; i--) { // Unpin all pinned messages except the newest 10
- channel.unpin(msgs.get(i));
- Thread.sleep(10);
- }
- } catch (InterruptedException ignore) {
- }
- });
+ val keepPinned = keepPinned().get();
+ if (keepPinned == 0) return;
+ Flux msgs = channel().get().flatMapMany(MessageChannel::getPinnedMessages);
+ msgs.subscribe(Message::unpin);
val yc = YamlConfiguration.loadConfiguration(new File("plugins/DiscordPlugin", "config.yml")); //Name change
- if (lastannouncementtime().get() == 0) //Load old data
- lastannouncementtime().set(yc.getLong("lastannouncementtime"));
- if (lastseentime().get() == 0)
- lastseentime().set(yc.getLong("lastseentime"));
+ if (lastAnnouncementTime().get() == 0) //Load old data
+ lastAnnouncementTime().set(yc.getLong("lastannouncementtime"));
+ if (lastSeenTime().get() == 0)
+ lastSeenTime().set(yc.getLong("lastseentime"));
new Thread(this::AnnouncementGetterThreadMethod).start();
}
@@ -88,7 +87,7 @@ public class AnnouncerModule extends Component {
.get("children").getAsJsonArray();
StringBuilder msgsb = new StringBuilder();
StringBuilder modmsgsb = new StringBuilder();
- long lastanntime = lastannouncementtime().get();
+ long lastanntime = lastAnnouncementTime().get();
for (int i = json.size() - 1; i >= 0; i--) {
JsonObject item = json.get(i).getAsJsonObject();
final JsonObject data = item.get("data").getAsJsonObject();
@@ -101,9 +100,9 @@ public class AnnouncerModule extends Component {
distinguished = distinguishedjson.getAsString();
String permalink = "https://www.reddit.com" + data.get("permalink").getAsString();
long date = data.get("created_utc").getAsLong();
- if (date > lastseentime().get())
- lastseentime().set(date);
- else if (date > lastannouncementtime().get()) {
+ if (date > lastSeenTime().get())
+ lastSeenTime().set(date);
+ else if (date > lastAnnouncementTime().get()) {
do {
val reddituserclass = ChromaGamerBase.getTypeForFolder("reddit");
if (reddituserclass == null)
@@ -122,13 +121,13 @@ public class AnnouncerModule extends Component {
}
}
if (msgsb.length() > 0)
- channel().get().pin(DiscordPlugin.sendMessageToChannelWait(channel().get(), msgsb.toString()));
+ channel().get().flatMap(ch -> ch.createMessage(msgsb.toString()))
+ .flatMap(Message::pin).subscribe();
if (modmsgsb.length() > 0)
- DiscordPlugin.sendMessageToChannel(modChannel().get(), modmsgsb.toString());
- if (lastannouncementtime().get() != lastanntime) {
- lastannouncementtime().set(lastanntime); // If sending succeeded
- getPlugin().saveConfig(); //TODO: Won't be needed if I implement auto-saving
- }
+ modChannel().get().flatMap(ch -> ch.createMessage(modmsgsb.toString()))
+ .flatMap(Message::pin).subscribe();
+ if (lastAnnouncementTime().get() != lastanntime)
+ lastAnnouncementTime().set(lastanntime); // If sending succeeded
} catch (Exception e) {
e.printStackTrace();
}
diff --git a/src/main/java/buttondevteam/discordplugin/commands/Command2DC.java b/src/main/java/buttondevteam/discordplugin/commands/Command2DC.java
index a0b8fda..ab56eb8 100644
--- a/src/main/java/buttondevteam/discordplugin/commands/Command2DC.java
+++ b/src/main/java/buttondevteam/discordplugin/commands/Command2DC.java
@@ -3,6 +3,8 @@ package buttondevteam.discordplugin.commands;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.chat.Command2;
+import java.lang.reflect.Method;
+
public class Command2DC extends Command2 {
@Override
public void registerCommand(ICommand2DC command) {
@@ -10,8 +12,8 @@ public class Command2DC extends Command2 {
}
@Override
- public boolean hasPermission(Command2DCSender sender, ICommand2DC command) {
- //return !command.isModOnly() || sender.getMessage().getAuthor().hasRole(DiscordPlugin.plugin.ModRole().get()); //TODO: ModRole may be null; more customisable way?
+ public boolean hasPermission(Command2DCSender sender, ICommand2DC command, Method method) {
+ //return !command.isModOnly() || sender.getMessage().getAuthor().hasRole(DiscordPlugin.plugin.modRole().get()); //TODO: modRole may be null; more customisable way?
return true;
}
}
diff --git a/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.java b/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.java
index bdff52d..8b705fd 100644
--- a/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.java
+++ b/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.java
@@ -2,20 +2,28 @@ package buttondevteam.discordplugin.commands;
import buttondevteam.discordplugin.DPUtils;
import buttondevteam.lib.chat.Command2Sender;
+import discord4j.core.object.entity.Message;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
-import sx.blah.discord.handle.obj.IMessage;
+import lombok.val;
@RequiredArgsConstructor
public class Command2DCSender implements Command2Sender {
- private final @Getter IMessage message;
+ private final @Getter
+ Message message;
@Override
public void sendMessage(String message) {
if (message.length() == 0) return;
message = DPUtils.sanitizeString(message);
message = Character.toLowerCase(message.charAt(0)) + message.substring(1);
- this.message.reply(message);
+ val msg = message;
+ /*this.message.getAuthorAsMember().flatMap(author ->
+ this.message.getChannel().flatMap(ch ->
+ ch.createMessage(author.getNicknameMention() + ", " + msg))).subscribe();*/
+ this.message.getChannel().flatMap(ch ->
+ ch.createMessage(this.message.getAuthor().map(u -> DPUtils.nickMention(u.getId()) + ", ").orElse("")
+ + msg)).subscribe();
}
@Override
diff --git a/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.java b/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.java
index 4e37e35..afa4cfb 100755
--- a/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.java
+++ b/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.java
@@ -1,7 +1,6 @@
package buttondevteam.discordplugin.commands;
import buttondevteam.discordplugin.DiscordPlayer;
-import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
@@ -28,34 +27,37 @@ public class ConnectCommand extends ICommand2DC {
@Command2.Subcommand
public boolean def(Command2DCSender sender, String Minecraftname) {
val message = sender.getMessage();
- if (WaitingToConnect.inverse().containsKey(message.getAuthor().getStringID())) {
- DiscordPlugin.sendMessageToChannel(message.getChannel(),
- "Replacing " + WaitingToConnect.inverse().get(message.getAuthor().getStringID()) + " with " + Minecraftname);
- WaitingToConnect.inverse().remove(message.getAuthor().getStringID());
+ val channel = message.getChannel().block();
+ val author = message.getAuthor().orElse(null);
+ if (author == null || channel == null) return true;
+ if (WaitingToConnect.inverse().containsKey(author.getId().asString())) {
+ channel.createMessage(
+ "Replacing " + WaitingToConnect.inverse().get(author.getId().asString()) + " with " + Minecraftname).subscribe();
+ WaitingToConnect.inverse().remove(author.getId().asString());
}
@SuppressWarnings("deprecation")
OfflinePlayer p = Bukkit.getOfflinePlayer(Minecraftname);
if (p == null) {
- DiscordPlugin.sendMessageToChannel(message.getChannel(), "The specified Minecraft player cannot be found");
+ channel.createMessage("The specified Minecraft player cannot be found").subscribe();
return true;
}
try (TBMCPlayer pl = TBMCPlayerBase.getPlayer(p.getUniqueId(), TBMCPlayer.class)) {
DiscordPlayer dp = pl.getAs(DiscordPlayer.class);
- if (dp != null && message.getAuthor().getStringID().equals(dp.getDiscordID())) {
- DiscordPlugin.sendMessageToChannel(message.getChannel(), "You already have this account connected.");
+ if (dp != null && author.getId().asString().equals(dp.getDiscordID())) {
+ channel.createMessage("You already have this account connected.").subscribe();
return true;
}
} catch (Exception e) {
TBMCCoreAPI.SendException("An error occured while connecting a Discord account!", e);
- DiscordPlugin.sendMessageToChannel(message.getChannel(), "An internal error occured!\n" + e);
+ channel.createMessage("An internal error occured!\n" + e).subscribe();
}
- WaitingToConnect.put(p.getName(), message.getAuthor().getStringID());
- DiscordPlugin.sendMessageToChannel(message.getChannel(),
+ WaitingToConnect.put(p.getName(), author.getId().asString());
+ channel.createMessage(
"Alright! Now accept the connection in Minecraft from the account " + Minecraftname
- + " before the next server restart. You can also adjust the Minecraft name you want to connect to with the same command.");
+ + " before the next server restart. You can also adjust the Minecraft name you want to connect to with the same command.").subscribe();
if (p.isOnline())
- ((Player) p).sendMessage("§bTo connect with the Discord account " + message.getAuthor().getName() + "#"
- + message.getAuthor().getDiscriminator() + " do /discord accept");
+ ((Player) p).sendMessage("§bTo connect with the Discord account " + author.getUsername() + "#"
+ + author.getDiscriminator() + " do /discord accept");
return true;
}
diff --git a/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.java b/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.java
index 2cf87fb..e1c0686 100644
--- a/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.java
+++ b/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.java
@@ -4,17 +4,27 @@ import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.listeners.CommonListeners;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
+import reactor.core.publisher.Mono;
@CommandClass(helpText = {
"Switches debug mode."
})
public class DebugCommand extends ICommand2DC {
@Command2.Subcommand
- public boolean def(Command2DCSender sender, String args) {
- if (sender.getMessage().getAuthor().hasRole(DiscordPlugin.plugin.ModRole().get()))
- sender.sendMessage("debug " + (CommonListeners.debug() ? "enabled" : "disabled"));
- else
- sender.sendMessage("you need to be a moderator to use this command.");
- return true;
- }
+ public boolean def(Command2DCSender sender) {
+ sender.getMessage().getAuthorAsMember()
+ .switchIfEmpty(sender.getMessage().getAuthor() //Support DMs
+ .map(u -> u.asMember(DiscordPlugin.mainServer.getId()))
+ .orElse(Mono.empty()))
+ .flatMap(m -> DiscordPlugin.plugin.modRole().get()
+ .map(mr -> m.getRoleIds().stream().anyMatch(r -> r.equals(mr.getId())))
+ .switchIfEmpty(Mono.fromSupplier(() -> DiscordPlugin.mainServer.getOwnerId().asLong() == m.getId().asLong()))) //Role not found
+ .subscribe(success -> {
+ if (success)
+ sender.sendMessage("debug " + (CommonListeners.debug() ? "enabled" : "disabled"));
+ else
+ sender.sendMessage("you need to be a moderator to use this command.");
+ });
+ return true;
+ }
}
diff --git a/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.java b/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.java
index 0194dab..546d4ee 100755
--- a/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.java
+++ b/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.java
@@ -1,5 +1,6 @@
package buttondevteam.discordplugin.commands;
+import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
@CommandClass(helpText = {
@@ -7,12 +8,17 @@ import buttondevteam.lib.chat.CommandClass;
"Shows some info about a command or lists the available commands.", //
})
public class HelpCommand extends ICommand2DC {
- @Override
- public boolean def(Command2DCSender sender, String args) {
- if (args.length() == 0)
+ @Command2.Subcommand
+ public boolean def(Command2DCSender sender, @Command2.TextArg @Command2.OptionalArg String args) {
+ if (args == null || args.length() == 0)
sender.sendMessage(getManager().getCommandsText());
- else
- sender.sendMessage("Soon:tm:"); //TODO
- return true;
+ else {
+ String[] ht = getManager().getHelpText(args);
+ if (ht == null)
+ sender.sendMessage("Command not found: " + args);
+ else
+ sender.sendMessage(ht);
+ }
+ return true;
}
}
diff --git a/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.java b/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.java
index 0433c81..40f98cb 100755
--- a/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.java
+++ b/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.java
@@ -7,13 +7,11 @@ import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.ChromaGamerBase.InfoTarget;
+import discord4j.core.object.entity.Message;
+import discord4j.core.object.entity.User;
import lombok.val;
-import sx.blah.discord.handle.obj.IMessage;
-import sx.blah.discord.handle.obj.IUser;
import java.util.List;
-import java.util.Optional;
-import java.util.stream.Collectors;
@CommandClass(helpText = {
"User information", //
@@ -24,67 +22,71 @@ public class UserinfoCommand extends ICommand2DC {
@Command2.Subcommand
public boolean def(Command2DCSender sender, @Command2.OptionalArg @Command2.TextArg String user) {
val message = sender.getMessage();
- IUser target = null;
+ User target = null;
+ val channel = message.getChannel().block();
+ assert channel != null;
if (user == null || user.length() == 0)
- target = message.getAuthor();
+ target = message.getAuthor().orElse(null);
else {
- final Optional firstmention = message.getMentions().stream()
- .filter(m -> !m.getStringID().equals(DiscordPlugin.dc.getOurUser().getStringID())).findFirst();
- if (firstmention.isPresent())
- target = firstmention.get();
+ @SuppressWarnings("OptionalGetWithoutIsPresent") final User firstmention = message.getUserMentions()
+ .filter(m -> !m.getId().asString().equals(DiscordPlugin.dc.getSelfId().get().asString())).blockFirst();
+ if (firstmention != null)
+ target = firstmention;
else if (user.contains("#")) {
String[] targettag = user.split("#");
- final List targets = getUsers(message, targettag[0]);
+ final List targets = getUsers(message, targettag[0]);
if (targets.size() == 0) {
- DiscordPlugin.sendMessageToChannel(message.getChannel(),
- "The user cannot be found (by name): " + user);
- return true;
+ channel.createMessage("The user cannot be found (by name): " + user).subscribe();
+ return true;
}
- for (IUser ptarget : targets) {
+ for (User ptarget : targets) {
if (ptarget.getDiscriminator().equalsIgnoreCase(targettag[1])) {
target = ptarget;
break;
}
}
if (target == null) {
- DiscordPlugin.sendMessageToChannel(message.getChannel(),
- "The user cannot be found (by discriminator): " + user + "(Found " + targets.size()
- + " users with the name.)");
- return true;
+ channel.createMessage("The user cannot be found (by discriminator): " + user + "(Found " + targets.size()
+ + " users with the name.)").subscribe();
+ return true;
}
} else {
- final List targets = getUsers(message, user);
+ final List targets = getUsers(message, user);
if (targets.size() == 0) {
- DiscordPlugin.sendMessageToChannel(message.getChannel(),
- "The user cannot be found on Discord: " + user);
- return true;
+ channel.createMessage("The user cannot be found on Discord: " + user).subscribe();
+ return true;
}
if (targets.size() > 1) {
- DiscordPlugin.sendMessageToChannel(message.getChannel(),
- "Multiple users found with that (nick)name. Please specify the whole tag, like ChromaBot#6338 or use a ping.");
- return true;
+ channel.createMessage("Multiple users found with that (nick)name. Please specify the whole tag, like ChromaBot#6338 or use a ping.").subscribe();
+ return true;
}
target = targets.get(0);
}
}
- try (DiscordPlayer dp = ChromaGamerBase.getUser(target.getStringID(), DiscordPlayer.class)) {
- StringBuilder uinfo = new StringBuilder("User info for ").append(target.getName()).append(":\n");
- uinfo.append(dp.getInfo(InfoTarget.Discord));
- DiscordPlugin.sendMessageToChannel(message.getChannel(), uinfo.toString());
- } catch (Exception e) {
- DiscordPlugin.sendMessageToChannel(message.getChannel(), "An error occured while getting the user!");
- TBMCCoreAPI.SendException("Error while getting info about " + target.getName() + "!", e);
+ if (target == null) {
+ sender.sendMessage("An error occurred.");
+ return true;
}
- return true;
+ try (DiscordPlayer dp = ChromaGamerBase.getUser(target.getId().asString(), DiscordPlayer.class)) {
+ StringBuilder uinfo = new StringBuilder("User info for ").append(target.getUsername()).append(":\n");
+ uinfo.append(dp.getInfo(InfoTarget.Discord));
+ channel.createMessage(uinfo.toString()).subscribe();
+ } catch (Exception e) {
+ channel.createMessage("An error occured while getting the user!").subscribe();
+ TBMCCoreAPI.SendException("Error while getting info about " + target.getUsername() + "!", e);
+ }
+ return true;
}
- private List getUsers(IMessage message, String args) {
- final List targets;
- if (message.getChannel().isPrivate())
- targets = DiscordPlugin.dc.getUsers().stream().filter(u -> u.getName().equalsIgnoreCase(args))
- .collect(Collectors.toList());
+ private List getUsers(Message message, String args) {
+ final List targets;
+ val guild = message.getGuild().block();
+ if (guild == null) //Private channel
+ targets = DiscordPlugin.dc.getUsers().filter(u -> u.getUsername().equalsIgnoreCase(args))
+ .collectList().block();
else
- targets = message.getGuild().getUsersByName(args, true);
+ targets = guild.getMembers().filter(m -> m.getUsername().equalsIgnoreCase(args))
+ .map(m -> (User) m).collectList().block();
return targets;
}
diff --git a/src/main/java/buttondevteam/discordplugin/exceptions/DebugMessageListener.java b/src/main/java/buttondevteam/discordplugin/exceptions/DebugMessageListener.java
index 95c3cdb..0f05934 100755
--- a/src/main/java/buttondevteam/discordplugin/exceptions/DebugMessageListener.java
+++ b/src/main/java/buttondevteam/discordplugin/exceptions/DebugMessageListener.java
@@ -3,10 +3,12 @@ package buttondevteam.discordplugin.exceptions;
import buttondevteam.core.ComponentManager;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.TBMCDebugMessageEvent;
+import discord4j.core.object.entity.MessageChannel;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
+import reactor.core.publisher.Mono;
-public class DebugMessageListener implements Listener{
+public class DebugMessageListener implements Listener {
@EventHandler
public void onDebugMessage(TBMCDebugMessageEvent e) {
SendMessage(e.getDebugMessage());
@@ -17,13 +19,15 @@ public class DebugMessageListener implements Listener{
if (DiscordPlugin.SafeMode || !ComponentManager.isEnabled(ExceptionListenerModule.class))
return;
try {
+ Mono mc = ExceptionListenerModule.getChannel();
+ if (mc == null) return;
StringBuilder sb = new StringBuilder();
sb.append("```").append("\n");
if (message.length() > 2000)
message = message.substring(0, 2000);
sb.append(message).append("\n");
sb.append("```");
- DiscordPlugin.sendMessageToChannel(ExceptionListenerModule.getChannel(), sb.toString());
+ mc.flatMap(ch -> ch.createMessage(sb.toString())).subscribe();
} catch (Exception ex) {
ex.printStackTrace();
}
diff --git a/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java
index 5d21bb1..d57c313 100755
--- a/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java
+++ b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java
@@ -7,13 +7,16 @@ import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.TBMCExceptionEvent;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
+import buttondevteam.lib.architecture.ReadOnlyConfigData;
+import discord4j.core.object.entity.Guild;
+import discord4j.core.object.entity.GuildChannel;
+import discord4j.core.object.entity.MessageChannel;
+import discord4j.core.object.entity.Role;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IGuild;
-import sx.blah.discord.handle.obj.IRole;
+import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.Arrays;
@@ -21,64 +24,71 @@ import java.util.List;
import java.util.stream.Collectors;
public class ExceptionListenerModule extends Component implements Listener {
- private List lastthrown = new ArrayList<>();
- private List lastsourcemsg = new ArrayList<>();
+ private List lastthrown = new ArrayList<>();
+ private List lastsourcemsg = new ArrayList<>();
- @EventHandler
- public void onException(TBMCExceptionEvent e) {
- if (DiscordPlugin.SafeMode || !ComponentManager.isEnabled(getClass()))
- return;
- if (lastthrown.stream()
- .anyMatch(ex -> Arrays.equals(e.getException().getStackTrace(), ex.getStackTrace())
- && (e.getException().getMessage() == null ? ex.getMessage() == null
- : e.getException().getMessage().equals(ex.getMessage()))) // e.Exception.Message==ex.Message
- && lastsourcemsg.contains(e.getSourceMessage()))
- return;
- SendException(e.getException(), e.getSourceMessage());
- if (lastthrown.size() >= 10)
- lastthrown.remove(0);
- if (lastsourcemsg.size() >= 10)
- lastsourcemsg.remove(0);
- lastthrown.add(e.getException());
- lastsourcemsg.add(e.getSourceMessage());
- e.setHandled();
- }
+ @EventHandler
+ public void onException(TBMCExceptionEvent e) {
+ if (DiscordPlugin.SafeMode || !ComponentManager.isEnabled(getClass()))
+ return;
+ if (lastthrown.stream()
+ .anyMatch(ex -> Arrays.equals(e.getException().getStackTrace(), ex.getStackTrace())
+ && (e.getException().getMessage() == null ? ex.getMessage() == null
+ : e.getException().getMessage().equals(ex.getMessage()))) // e.Exception.Message==ex.Message
+ && lastsourcemsg.contains(e.getSourceMessage()))
+ return;
+ SendException(e.getException(), e.getSourceMessage());
+ if (lastthrown.size() >= 10)
+ lastthrown.remove(0);
+ if (lastsourcemsg.size() >= 10)
+ lastsourcemsg.remove(0);
+ lastthrown.add(e.getException());
+ lastsourcemsg.add(e.getSourceMessage());
+ e.setHandled();
+ }
- private static void SendException(Throwable e, String sourcemessage) {
+ private static void SendException(Throwable e, String sourcemessage) {
if (instance == null) return;
- try {
- IChannel channel = getChannel();
- assert channel != null;
- IRole coderRole = instance.pingRole(channel.getGuild()).get();
- StringBuilder sb = TBMCCoreAPI.IsTestServer() ? new StringBuilder()
- : new StringBuilder(coderRole == null ? "" : coderRole.mention()).append("\n");
- sb.append(sourcemessage).append("\n");
- sb.append("```").append("\n");
- String stackTrace = Arrays.stream(ExceptionUtils.getStackTrace(e).split("\\n"))
- .filter(s -> !s.contains("\tat ") || s.contains("\tat buttondevteam."))
- .collect(Collectors.joining("\n"));
- if (stackTrace.length() > 1800)
- stackTrace = stackTrace.substring(0, 1800);
- sb.append(stackTrace).append("\n");
- sb.append("```");
- DiscordPlugin.sendMessageToChannel(channel, sb.toString()); //Instance isn't null here
- } catch (Exception ex) {
- ex.printStackTrace();
- }
- }
+ try {
+ Mono channel = getChannel();
+ assert channel != null;
+ Mono coderRole;
+ if (channel instanceof GuildChannel)
+ coderRole = instance.pingRole(((GuildChannel) channel).getGuild()).get();
+ else
+ coderRole = Mono.empty();
+ coderRole.map(role -> TBMCCoreAPI.IsTestServer() ? new StringBuilder()
+ : new StringBuilder(role.getMention()).append("\n"))
+ .defaultIfEmpty(new StringBuilder())
+ .flatMap(sb -> {
+ sb.append(sourcemessage).append("\n");
+ sb.append("```").append("\n");
+ String stackTrace = Arrays.stream(ExceptionUtils.getStackTrace(e).split("\\n"))
+ .filter(s -> !s.contains("\tat ") || s.contains("\tat buttondevteam."))
+ .collect(Collectors.joining("\n"));
+ if (sb.length() + stackTrace.length() >= 1980)
+ stackTrace = stackTrace.substring(0, 1980 - sb.length());
+ sb.append(stackTrace).append("\n");
+ sb.append("```");
+ return channel.flatMap(ch -> ch.createMessage(sb.toString()));
+ }).subscribe();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
private static ExceptionListenerModule instance;
- public static IChannel getChannel() {
+ public static Mono getChannel() {
if (instance != null) return instance.channel().get();
- return null;
+ return Mono.empty();
}
- private ConfigData channel() {
+ private ReadOnlyConfigData> channel() {
return DPUtils.channelData(getConfig(), "channel", 239519012529111040L);
}
- private ConfigData pingRole(IGuild guild) {
+ private ConfigData> pingRole(Mono guild) {
return DPUtils.roleData(getConfig(), "pingRole", "Coder", guild);
}
diff --git a/src/main/java/buttondevteam/discordplugin/fun/FunModule.java b/src/main/java/buttondevteam/discordplugin/fun/FunModule.java
index f5adf0d..fa5e7d3 100644
--- a/src/main/java/buttondevteam/discordplugin/fun/FunModule.java
+++ b/src/main/java/buttondevteam/discordplugin/fun/FunModule.java
@@ -6,15 +6,17 @@ import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
+import buttondevteam.lib.architecture.ReadOnlyConfigData;
import com.google.common.collect.Lists;
+import discord4j.core.event.domain.PresenceUpdateEvent;
+import discord4j.core.object.entity.*;
+import discord4j.core.object.presence.Status;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
-import sx.blah.discord.handle.impl.events.user.PresenceUpdateEvent;
-import sx.blah.discord.handle.obj.*;
-import sx.blah.discord.util.EmbedBuilder;
+import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.Arrays;
@@ -23,36 +25,43 @@ import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
+/**
+ * The YEEHAW event uses an emoji named :YEEHAW: if available
+ */
public class FunModule extends Component implements Listener {
private static final String[] serverReadyStrings = new String[]{"In one week from now", // Ali
- "Between now and the heat-death of the universe.", // Ghostise
- "Soon™", "Ask again this time next month", // Ghostise
- "In about 3 seconds", // Nicolai
- "After we finish 8 plugins", // Ali
- "Tomorrow.", // Ali
- "After one tiiiny feature", // Ali
- "Next commit", // Ali
- "After we finish strangling Towny", // Ali
- "When we kill every *fucking* bug", // Ali
- "Once the server stops screaming.", // Ali
- "After HL3 comes out", // Ali
- "Next time you ask", // Ali
- "When will *you* be open?" // Ali
+ "Between now and the heat-death of the universe.", // Ghostise
+ "Soon™", "Ask again this time next month", // Ghostise
+ "In about 3 seconds", // Nicolai
+ "After we finish 8 plugins", // Ali
+ "Tomorrow.", // Ali
+ "After one tiiiny feature", // Ali
+ "Next commit", // Ali
+ "After we finish strangling Towny", // Ali
+ "When we kill every *fucking* bug", // Ali
+ "Once the server stops screaming.", // Ali
+ "After HL3 comes out", // Ali
+ "Next time you ask", // Ali
+ "When will *you* be open?" // Ali
};
- private ConfigData serverReady() {
- return getConfig().getData("serverReady", true);
+ /**
+ * Questions that the bot will choose a random answer to give to.
+ */
+ private ConfigData serverReady() {
+ return getConfig().getData("serverReady", () -> new String[]{"when will the server be open",
+ "when will the server be ready", "when will the server be done", "when will the server be complete",
+ "when will the server be finished", "when's the server ready", "when's the server open",
+ "Vhen vill ze server be open?"});
}
+ /**
+ * Answers for a recognized question. Selected randomly.
+ */
private ConfigData> serverReadyAnswers() {
return getConfig().getData("serverReadyAnswers", () -> Lists.newArrayList(serverReadyStrings)); //TODO: Test
}
- private static final String[] serverReadyQuestions = new String[]{"when will the server be open",
- "when will the server be ready", "when will the server be done", "when will the server be complete",
- "when will the server be finished", "when's the server ready", "when's the server open",
- "Vhen vill ze server be open?"};
-
private static final Random serverReadyRandom = new Random();
private static final ArrayList usableServerReadyStrings = new ArrayList<>(0);
@@ -76,10 +85,10 @@ public class FunModule extends Component implements Listener {
private static short ListC = 0;
- public static boolean executeMemes(IMessage message) {
+ public static boolean executeMemes(Message message) {
val fm = ComponentManager.getIfEnabled(FunModule.class);
if (fm == null) return false;
- String msglowercased = message.getContent().toLowerCase();
+ String msglowercased = message.getContent().orElse("").toLowerCase();
lastlist++;
if (lastlist > 5) {
ListC = 0;
@@ -87,22 +96,20 @@ public class FunModule extends Component implements Listener {
}
if (msglowercased.equals("list") && Bukkit.getOnlinePlayers().size() == lastlistp && ListC++ > 2) // Lowered already
{
- message.reply("Stop it. You know the answer.");
+ DPUtils.reply(message, null, "Stop it. You know the answer.").subscribe();
lastlist = 0;
lastlistp = (short) Bukkit.getOnlinePlayers().size();
return true; //Handled
}
lastlistp = (short) Bukkit.getOnlinePlayers().size(); //Didn't handle
- if (fm.serverReady().get()) {
- if (!TBMCCoreAPI.IsTestServer()
- && Arrays.stream(serverReadyQuestions).anyMatch(msglowercased::contains)) {
- int next;
- if (usableServerReadyStrings.size() == 0)
- fm.createUsableServerReadyStrings();
- next = usableServerReadyStrings.remove(serverReadyRandom.nextInt(usableServerReadyStrings.size()));
- DiscordPlugin.sendMessageToChannel(message.getChannel(), serverReadyStrings[next]);
- return false; //Still process it as a command/mcchat if needed
- }
+ if (!TBMCCoreAPI.IsTestServer()
+ && Arrays.stream(fm.serverReady().get()).anyMatch(msglowercased::contains)) {
+ int next;
+ if (usableServerReadyStrings.size() == 0)
+ fm.createUsableServerReadyStrings();
+ next = usableServerReadyStrings.remove(serverReadyRandom.nextInt(usableServerReadyStrings.size()));
+ DPUtils.reply(message, null, fm.serverReadyAnswers().get().get(next)).subscribe();
+ return false; //Still process it as a command/mcchat if needed
}
return false;
}
@@ -112,12 +119,12 @@ public class FunModule extends Component implements Listener {
ListC = 0;
}
- private ConfigData fullHouseDevRole(IGuild guild) {
+ private ConfigData> fullHouseDevRole(Mono guild) {
return DPUtils.roleData(getConfig(), "fullHouseDevRole", "Developer", guild);
}
- private ConfigData fullHouseChannel() {
+ private ReadOnlyConfigData> fullHouseChannel() {
return DPUtils.channelData(getConfig(), "fullHouseChannel", 219626707458457603L);
}
@@ -126,24 +133,24 @@ public class FunModule extends Component implements Listener {
public static void handleFullHouse(PresenceUpdateEvent event) {
val fm = ComponentManager.getIfEnabled(FunModule.class);
if (fm == null) return;
- val channel = fm.fullHouseChannel().get();
- if (channel == null) return;
- val devrole = fm.fullHouseDevRole(channel.getGuild()).get();
- if (devrole == null) return;
- if (event.getOldPresence().getStatus().equals(StatusType.OFFLINE)
- && !event.getNewPresence().getStatus().equals(StatusType.OFFLINE)
- && event.getUser().getRolesForGuild(channel.getGuild()).stream()
- .anyMatch(r -> r.getLongID() == devrole.getLongID())
- && channel.getGuild().getUsersByRole(devrole).stream()
- .noneMatch(u -> u.getPresence().getStatus().equals(StatusType.OFFLINE))
- && lasttime + 10 < TimeUnit.NANOSECONDS.toHours(System.nanoTime())
- && Calendar.getInstance().get(Calendar.DAY_OF_MONTH) % 5 == 0) {
- DiscordPlugin.sendMessageToChannel(channel, "Full house!",
- new EmbedBuilder()
- .withImage(
- "https://cdn.discordapp.com/attachments/249295547263877121/249687682618359808/poker-hand-full-house-aces-kings-playing-cards-15553791.png")
- .build());
- lasttime = TimeUnit.NANOSECONDS.toHours(System.nanoTime());
- }
+ if (Calendar.getInstance().get(Calendar.DAY_OF_MONTH) % 5 != 0) return;
+ fm.fullHouseChannel().get()
+ .filter(ch -> ch instanceof GuildChannel)
+ .flatMap(channel -> fm.fullHouseDevRole(((GuildChannel) channel).getGuild()).get()
+ .filter(role -> event.getOld().map(p -> p.getStatus().equals(Status.OFFLINE)).orElse(false))
+ .filter(role -> !event.getCurrent().getStatus().equals(Status.OFFLINE))
+ .filterWhen(devrole -> event.getMember().flatMap(m -> m.getRoles()
+ .any(r -> r.getId().asLong() == devrole.getId().asLong())))
+ .filterWhen(devrole ->
+ event.getGuild().flatMapMany(g -> g.getMembers().filter(m -> m.getRoleIds().stream().anyMatch(s -> s.equals(devrole.getId()))))
+ .flatMap(Member::getPresence).all(pr -> !pr.getStatus().equals(Status.OFFLINE)))
+ .filter(devrole -> lasttime + 10 < TimeUnit.NANOSECONDS.toHours(System.nanoTime())) //This should stay so it checks this last
+ .flatMap(devrole -> {
+ lasttime = TimeUnit.NANOSECONDS.toHours(System.nanoTime());
+ return channel.createMessage(mcs -> mcs.setContent("Full house!").setEmbed(ecs ->
+ ecs.setImage(
+ "https://cdn.discordapp.com/attachments/249295547263877121/249687682618359808/poker-hand-full-house-aces-kings-playing-cards-15553791.png")
+ ));
+ })).subscribe();
}
}
diff --git a/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.java b/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.java
index 4f99d35..45aa261 100644
--- a/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.java
+++ b/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.java
@@ -1,11 +1,18 @@
package buttondevteam.discordplugin.listeners;
+import buttondevteam.discordplugin.DPUtils;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.commands.Command2DCSender;
+import buttondevteam.discordplugin.util.Timings;
import buttondevteam.lib.TBMCCoreAPI;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IMessage;
-import sx.blah.discord.handle.obj.IRole;
+import discord4j.core.object.entity.Message;
+import discord4j.core.object.entity.MessageChannel;
+import discord4j.core.object.entity.PrivateChannel;
+import discord4j.core.object.entity.Role;
+import lombok.val;
+import reactor.core.publisher.Mono;
+
+import java.util.concurrent.atomic.AtomicBoolean;
public class CommandListener {
/**
@@ -13,44 +20,61 @@ public class CommandListener {
*
* @param message The Discord message
* @param mentionedonly Only run the command if ChromaBot is mentioned at the start of the message
- * @return Whether it ran the command
+ * @return Whether it did not run the command
*/
- public static boolean runCommand(IMessage message, boolean mentionedonly) {
- if (message.getContent().length() == 0)
- return false; //Pin messages and such, let the mcchat listener deal with it
- final IChannel channel = message.getChannel();
- if (!mentionedonly) { //mentionedonly conditions are in CommonListeners
- if (!message.getChannel().isPrivate()
- && !(message.getContent().charAt(0) == DiscordPlugin.getPrefix()
- && channel.getStringID().equals(DiscordPlugin.plugin.CommandChannel().get().getStringID()))) //
- return false;
- message.getChannel().setTypingStatus(true); // Fun
- }
- final StringBuilder cmdwithargs = new StringBuilder(message.getContent());
- final String mention = DiscordPlugin.dc.getOurUser().mention(false);
- final String mentionNick = DiscordPlugin.dc.getOurUser().mention(true);
- boolean gotmention = checkanddeletemention(cmdwithargs, mention, message);
- gotmention = checkanddeletemention(cmdwithargs, mentionNick, message) || gotmention;
- for (String mentionRole : (Iterable) message.getRoleMentions().stream().filter(r -> DiscordPlugin.dc.getOurUser().hasRole(r)).map(IRole::mention)::iterator)
- gotmention = checkanddeletemention(cmdwithargs, mentionRole, message) || gotmention; // Delete all mentions
- if (mentionedonly && !gotmention) {
- message.getChannel().setTypingStatus(false);
- return false;
- }
- message.getChannel().setTypingStatus(true);
- String cmdwithargsString = cmdwithargs.toString();
- try {
- if (!DiscordPlugin.plugin.getManager().handleCommand(new Command2DCSender(message), cmdwithargsString))
- message.reply("Unknown command. Do " + DiscordPlugin.getPrefix() + "help for help.\n" + cmdwithargsString);
- } catch (Exception e) {
- TBMCCoreAPI.SendException("Failed to process Discord command: " + cmdwithargsString, e);
- }
- message.getChannel().setTypingStatus(false);
- return true;
+ public static Mono runCommand(Message message, MessageChannel commandChannel, boolean mentionedonly) {
+ Timings timings = CommonListeners.timings;
+ Mono ret = Mono.just(true);
+ if (!message.getContent().isPresent())
+ return ret; //Pin messages and such, let the mcchat listener deal with it
+ val content = message.getContent().get();
+ timings.printElapsed("A");
+ return message.getChannel().flatMap(channel -> {
+ Mono> tmp = ret;
+ if (!mentionedonly) { //mentionedonly conditions are in CommonListeners
+ timings.printElapsed("B");
+ if (!(channel instanceof PrivateChannel)
+ && !(content.charAt(0) == DiscordPlugin.getPrefix()
+ && channel.getId().asLong() == commandChannel.getId().asLong())) //
+ return ret;
+ timings.printElapsed("C");
+ tmp = ret.then(channel.type()).thenReturn(true); // Fun (this true is ignored - x)
+ }
+ final StringBuilder cmdwithargs = new StringBuilder(content);
+ val gotmention = new AtomicBoolean();
+ timings.printElapsed("Before self");
+ return tmp.flatMapMany(x ->
+ DiscordPlugin.dc.getSelf().flatMap(self -> self.asMember(DiscordPlugin.mainServer.getId()))
+ .flatMapMany(self -> {
+ timings.printElapsed("D");
+ gotmention.set(checkanddeletemention(cmdwithargs, self.getMention(), message));
+ gotmention.set(checkanddeletemention(cmdwithargs, self.getNicknameMention(), message) || gotmention.get());
+ val mentions = message.getRoleMentions();
+ return self.getRoles().filterWhen(r -> mentions.any(rr -> rr.getName().equals(r.getName())))
+ .map(Role::getMention);
+ }).map(mentionRole -> {
+ timings.printElapsed("E");
+ gotmention.set(checkanddeletemention(cmdwithargs, mentionRole, message) || gotmention.get()); // Delete all mentions
+ return !mentionedonly || gotmention.get(); //Stops here if false
+ }).switchIfEmpty(Mono.fromSupplier(() -> !mentionedonly || gotmention.get())))
+ .filter(b -> b).last(false).filter(b -> b).doOnNext(b -> channel.type().subscribe()).flatMap(b -> {
+ String cmdwithargsString = cmdwithargs.toString();
+ try {
+ timings.printElapsed("F");
+ if (!DiscordPlugin.plugin.getManager().handleCommand(new Command2DCSender(message), cmdwithargsString))
+ return DPUtils.reply(message, channel, "Unknown command. Do " + DiscordPlugin.getPrefix() + "help for help.\n" + cmdwithargsString)
+ .map(m -> false);
+ } catch (Exception e) {
+ TBMCCoreAPI.SendException("Failed to process Discord command: " + cmdwithargsString, e);
+ }
+ return Mono.just(false); //If the command succeeded or there was an error, return false
+ }).defaultIfEmpty(true);
+ });
}
- private static boolean checkanddeletemention(StringBuilder cmdwithargs, String mention, IMessage message) {
- if (message.getContent().startsWith(mention)) // TODO: Resolve mentions: Compound arguments, either a mention or text
+ private static boolean checkanddeletemention(StringBuilder cmdwithargs, String mention, Message message) {
+ final char prefix = DiscordPlugin.getPrefix();
+ if (message.getContent().orElse("").startsWith(mention)) // TODO: Resolve mentions: Compound arguments, either a mention or text
if (cmdwithargs.length() > mention.length() + 1) {
int i = cmdwithargs.indexOf(" ", mention.length());
if (i == -1)
@@ -60,14 +84,16 @@ public class CommandListener {
for (; i < cmdwithargs.length() && cmdwithargs.charAt(i) == ' '; i++)
; //Removes any space before the command
cmdwithargs.delete(0, i);
- cmdwithargs.insert(0, DiscordPlugin.getPrefix()); //Always use the prefix for processing
+ cmdwithargs.insert(0, prefix); //Always use the prefix for processing
} else
- cmdwithargs.replace(0, cmdwithargs.length(), DiscordPlugin.getPrefix() + "help");
+ cmdwithargs.replace(0, cmdwithargs.length(), prefix + "help");
else {
+ if (cmdwithargs.length() == 0)
+ cmdwithargs.replace(0, cmdwithargs.length(), prefix + "help");
+ else if (cmdwithargs.charAt(0) != prefix)
+ cmdwithargs.insert(0, prefix);
return false; //Don't treat / as mention, mentions can be used in public mcchat
}
- if (cmdwithargs.length() == 0)
- cmdwithargs.replace(0, cmdwithargs.length(), DiscordPlugin.getPrefix() + "help");
return true;
}
}
diff --git a/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.java b/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.java
index 5350b32..510e71a 100755
--- a/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.java
+++ b/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.java
@@ -5,18 +5,23 @@ import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.fun.FunModule;
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
import buttondevteam.discordplugin.role.GameRoleModule;
+import buttondevteam.discordplugin.util.Timings;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.Component;
+import discord4j.core.event.EventDispatcher;
+import discord4j.core.event.domain.PresenceUpdateEvent;
+import discord4j.core.event.domain.message.MessageCreateEvent;
+import discord4j.core.event.domain.role.RoleCreateEvent;
+import discord4j.core.event.domain.role.RoleDeleteEvent;
+import discord4j.core.event.domain.role.RoleUpdateEvent;
+import discord4j.core.object.entity.PrivateChannel;
import lombok.val;
-import sx.blah.discord.api.events.IListener;
-import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent;
-import sx.blah.discord.handle.impl.events.guild.role.RoleCreateEvent;
-import sx.blah.discord.handle.impl.events.guild.role.RoleDeleteEvent;
-import sx.blah.discord.handle.impl.events.guild.role.RoleUpdateEvent;
-import sx.blah.discord.handle.impl.events.user.PresenceUpdateEvent;
+import reactor.core.publisher.Mono;
public class CommonListeners {
+ public static final Timings timings = new Timings();
+
/*
MentionEvent:
- CommandListener (starts with mention, only 'channelcon' and not in #bot)
@@ -26,52 +31,59 @@ public class CommonListeners {
- Minecraft chat (is enabled in the channel and message isn't [/]mcchat)
- CommandListener (with the correct prefix in #bot, or in private)
*/
- public static IListener>[] getListeners() {
- return new IListener[]{new IListener() {
- @Override
- public void handle(MessageReceivedEvent event) {
- if (DiscordPlugin.SafeMode)
- return;
- if (event.getMessage().getAuthor().isBot())
- return;
- if (FunModule.executeMemes(event.getMessage()))
- return;
- try {
- boolean handled = false;
- val commandChannel = DiscordPlugin.plugin.CommandChannel().get();
- if ((commandChannel != null && event.getChannel().getLongID() == commandChannel.getLongID()) //If mentioned, that's higher than chat
- || event.getMessage().getContent().contains("channelcon")) //Only 'channelcon' is allowed in other channels
- handled = CommandListener.runCommand(event.getMessage(), true); //#bot is handled here
- if (handled) return;
- val mcchat = Component.getComponents().get(MinecraftChatModule.class);
- if (mcchat != null && mcchat.isEnabled()) //ComponentManager.isEnabled() searches the component again
- handled = ((MinecraftChatModule) mcchat).getListener().handleDiscord(event); //Also runs Discord commands in chat channels
- if (!handled)
- handled = CommandListener.runCommand(event.getMessage(), false);
- } catch (Exception e) {
- TBMCCoreAPI.SendException("An error occured while handling a message!", e);
- }
- }
- }, new IListener() {
- @Override
- public void handle(PresenceUpdateEvent event) {
- if (DiscordPlugin.SafeMode)
- return;
- FunModule.handleFullHouse(event);
- }
- }, (IListener) GameRoleModule::handleRoleEvent, //
- (IListener) GameRoleModule::handleRoleEvent, //
- (IListener) GameRoleModule::handleRoleEvent};
+ public static void register(EventDispatcher dispatcher) {
+ dispatcher.on(MessageCreateEvent.class).flatMap(event -> {
+ timings.printElapsed("Message received");
+ val def = Mono.empty();
+ if (DiscordPlugin.SafeMode)
+ return def;
+ val author = event.getMessage().getAuthor();
+ if (!author.isPresent() || author.get().isBot())
+ return def;
+ if (FunModule.executeMemes(event.getMessage()))
+ return def;
+ val commandChannel = DiscordPlugin.plugin.commandChannel().get();
+ val commandCh = DPUtils.getMessageChannel(DiscordPlugin.plugin.commandChannel());
+ return commandCh.filterWhen(ch -> event.getMessage().getChannel().map(mch ->
+ (commandChannel != null && mch.getId().asLong() == commandChannel.asLong()) //If mentioned, that's higher than chat
+ || mch instanceof PrivateChannel
+ || event.getMessage().getContent().orElse("").contains("channelcon")) //Only 'channelcon' is allowed in other channels
+ .flatMap(shouldRun -> { //Only continue if this doesn't handle the event
+ if (!shouldRun)
+ return Mono.just(true); //The condition is only for the first command execution, not mcchat
+ timings.printElapsed("Run command 1");
+ return CommandListener.runCommand(event.getMessage(), ch, true); //#bot is handled here
+ })).filterWhen(ch -> {
+ timings.printElapsed("mcchat");
+ val mcchat = Component.getComponents().get(MinecraftChatModule.class);
+ if (mcchat != null && mcchat.isEnabled()) //ComponentManager.isEnabled() searches the component again
+ return ((MinecraftChatModule) mcchat).getListener().handleDiscord(event); //Also runs Discord commands in chat channels
+ return Mono.empty(); //Wasn't handled, continue
+ }).filterWhen(ch -> {
+ timings.printElapsed("Run command 2");
+ return CommandListener.runCommand(event.getMessage(), ch, false);
+ });
+ }).onErrorContinue((err, obj) -> TBMCCoreAPI.SendException("An error occured while handling a message!", err))
+ .subscribe();
+ dispatcher.on(PresenceUpdateEvent.class).subscribe(event -> {
+ if (DiscordPlugin.SafeMode)
+ return;
+ FunModule.handleFullHouse(event);
+ });
+ dispatcher.on(RoleCreateEvent.class).subscribe(GameRoleModule::handleRoleEvent);
+ dispatcher.on(RoleDeleteEvent.class).subscribe(GameRoleModule::handleRoleEvent);
+ dispatcher.on(RoleUpdateEvent.class).subscribe(GameRoleModule::handleRoleEvent);
+
}
- private static boolean debug = false;
+ private static boolean debug = false;
- public static void debug(String debug) {
- if (CommonListeners.debug) //Debug
- DPUtils.getLogger().info(debug);
- }
+ public static void debug(String debug) {
+ if (CommonListeners.debug) //Debug
+ DPUtils.getLogger().info(debug);
+ }
- public static boolean debug() {
- return debug = !debug;
- }
+ public static boolean debug() {
+ return debug = !debug;
+ }
}
diff --git a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java
index c23b8fa..6062711 100755
--- a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java
+++ b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java
@@ -5,39 +5,50 @@ import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.commands.ConnectCommand;
import buttondevteam.lib.player.TBMCPlayerGetInfoEvent;
import buttondevteam.lib.player.TBMCPlayerJoinEvent;
+import discord4j.core.object.entity.Member;
+import discord4j.core.object.entity.User;
+import discord4j.core.object.util.Snowflake;
+import lombok.val;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.server.ServerCommandEvent;
-import sx.blah.discord.handle.obj.IUser;
public class MCListener implements Listener {
@EventHandler
public void onPlayerJoin(TBMCPlayerJoinEvent e) {
if (ConnectCommand.WaitingToConnect.containsKey(e.GetPlayer().PlayerName().get())) {
- @SuppressWarnings("ConstantConditions") IUser user = DiscordPlugin.dc
- .getUserByID(Long.parseLong(ConnectCommand.WaitingToConnect.get(e.GetPlayer().PlayerName().get())));
- e.getPlayer().sendMessage("§bTo connect with the Discord account @" + user.getName() + "#" + user.getDiscriminator()
+ @SuppressWarnings("ConstantConditions") User user = DiscordPlugin.dc
+ .getUserById(Snowflake.of(ConnectCommand.WaitingToConnect.get(e.GetPlayer().PlayerName().get()))).block();
+ if (user == null) return;
+ e.getPlayer().sendMessage("§bTo connect with the Discord account @" + user.getUsername() + "#" + user.getDiscriminator()
+ " do /discord accept");
e.getPlayer().sendMessage("§bIf it wasn't you, do /discord decline");
}
}
- @EventHandler
- public void onGetInfo(TBMCPlayerGetInfoEvent e) {
- if (DiscordPlugin.SafeMode)
- return;
- DiscordPlayer dp = e.getPlayer().getAs(DiscordPlayer.class);
- if (dp == null || dp.getDiscordID() == null || dp.getDiscordID().equals(""))
- return;
- IUser user = DiscordPlugin.dc.getUserByID(Long.parseLong(dp.getDiscordID()));
- e.addInfo("Discord tag: " + user.getName() + "#" + user.getDiscriminator());
- e.addInfo(user.getPresence().getStatus().toString());
- if (user.getPresence().getActivity().isPresent() && user.getPresence().getText().isPresent())
- e.addInfo(user.getPresence().getActivity().get() + ": " + user.getPresence().getText().get());
- }
+ @EventHandler
+ public void onGetInfo(TBMCPlayerGetInfoEvent e) {
+ if (DiscordPlugin.SafeMode)
+ return;
+ DiscordPlayer dp = e.getPlayer().getAs(DiscordPlayer.class);
+ if (dp == null || dp.getDiscordID() == null || dp.getDiscordID().equals(""))
+ return;
+ User user = DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID())).block();
+ if (user == null) return;
+ e.addInfo("Discord tag: " + user.getUsername() + "#" + user.getDiscriminator());
+ Member member = user.asMember(DiscordPlugin.mainServer.getId()).block();
+ if (member == null) return;
+ val pr = member.getPresence().block();
+ if (pr == null) return;
+ e.addInfo(pr.getStatus().toString());
+ if (pr.getActivity().isPresent()) {
+ val activity = pr.getActivity().get();
+ e.addInfo(activity.getType() + ": " + activity.getName());
+ }
+ }
- @EventHandler
- public void onServerCommand(ServerCommandEvent e) {
- DiscordPlugin.Restart = !e.getCommand().equalsIgnoreCase("stop"); // The variable is always true except if stopped
- }
+ @EventHandler
+ public void onServerCommand(ServerCommandEvent e) {
+ DiscordPlugin.Restart = !e.getCommand().equalsIgnoreCase("stop"); // The variable is always true except if stopped
+ }
}
diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java b/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java
index 2bd2bb6..41599b7 100644
--- a/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java
+++ b/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java
@@ -9,15 +9,16 @@ import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.player.TBMCPlayer;
+import discord4j.core.object.entity.Message;
+import discord4j.core.object.util.Permission;
+import lombok.RequiredArgsConstructor;
import lombok.val;
import org.bukkit.Bukkit;
-import sx.blah.discord.handle.obj.IMessage;
-import sx.blah.discord.handle.obj.Permissions;
-import sx.blah.discord.util.PermissionUtils;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
+import java.util.Objects;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -32,15 +33,17 @@ import java.util.stream.Collectors;
"Mentioning the bot is needed in this case because the / prefix only works in #bot.", //
"Invite link: " //
})
+@RequiredArgsConstructor
public class ChannelconCommand extends ICommand2DC {
+ private final MinecraftChatModule module;
@Command2.Subcommand
public boolean remove(Command2DCSender sender) {
val message = sender.getMessage();
if (checkPerms(message)) return true;
- if (MCChatCustom.removeCustomChat(message.getChannel()))
- message.reply("channel connection removed.");
+ if (MCChatCustom.removeCustomChat(message.getChannelId()))
+ DPUtils.reply(message, null, "channel connection removed.").subscribe();
else
- message.reply("this channel isn't connected.");
+ DPUtils.reply(message, null, "this channel isn't connected.").subscribe();
return true;
}
@@ -48,13 +51,13 @@ public class ChannelconCommand extends ICommand2DC {
public boolean toggle(Command2DCSender sender, @Command2.OptionalArg String toggle) {
val message = sender.getMessage();
if (checkPerms(message)) return true;
- val cc = MCChatCustom.getCustomChat(message.getChannel());
+ val cc = MCChatCustom.getCustomChat(message.getChannelId());
if (cc == null)
return respond(sender, "this channel isn't connected.");
Supplier togglesString = () -> Arrays.stream(ChannelconBroadcast.values()).map(t -> t.toString().toLowerCase() + ": " + ((cc.toggles & t.flag) == 0 ? "disabled" : "enabled")).collect(Collectors.joining("\n"))
+ "\n\n" + TBMCSystemChatEvent.BroadcastTarget.stream().map(target -> target.getName() + ": " + (cc.brtoggles.contains(target) ? "enabled" : "disabled")).collect(Collectors.joining("\n"));
if (toggle == null) {
- message.reply("toggles:\n" + togglesString.get());
+ DPUtils.reply(message, null, "toggles:\n" + togglesString.get()).subscribe();
return true;
}
String arg = toggle.toUpperCase();
@@ -62,7 +65,7 @@ public class ChannelconCommand extends ICommand2DC {
if (!b.isPresent()) {
val bt = TBMCSystemChatEvent.BroadcastTarget.get(arg);
if (bt == null) {
- message.reply("cannot find toggle. Toggles:\n" + togglesString.get());
+ DPUtils.reply(message, null, "cannot find toggle. Toggles:\n" + togglesString.get()).subscribe();
return true;
}
final boolean add;
@@ -80,7 +83,7 @@ public class ChannelconCommand extends ICommand2DC {
//1 1 | 0
// XOR
cc.toggles ^= b.get().flag;
- message.reply("'" + b.get().toString().toLowerCase() + "' " + ((cc.toggles & b.get().flag) == 0 ? "disabled" : "enabled"));
+ DPUtils.reply(message, null, "'" + b.get().toString().toLowerCase() + "' " + ((cc.toggles & b.get().flag) == 0 ? "disabled" : "enabled")).subscribe();
return true;
}
@@ -88,45 +91,49 @@ public class ChannelconCommand extends ICommand2DC {
public boolean def(Command2DCSender sender, String channelID) {
val message = sender.getMessage();
if (checkPerms(message)) return true;
- if (MCChatCustom.hasCustomChat(message.getChannel()))
+ if (MCChatCustom.hasCustomChat(message.getChannelId()))
return respond(sender, "this channel is already connected to a Minecraft channel. Use `@ChromaBot channelcon remove` to remove it.");
val chan = Channel.getChannels().filter(ch -> ch.ID.equalsIgnoreCase(channelID) || (Arrays.stream(ch.IDs().get()).anyMatch(cid -> cid.equalsIgnoreCase(channelID)))).findAny();
if (!chan.isPresent()) { //TODO: Red embed that disappears over time (kinda like the highlight messages in OW)
- message.reply("MC channel with ID '" + channelID + "' not found! The ID is the command for it without the /.");
+ DPUtils.reply(message, null, "MC channel with ID '" + channelID + "' not found! The ID is the command for it without the /.").subscribe();
return true;
}
- val dp = DiscordPlayer.getUser(message.getAuthor().getStringID(), DiscordPlayer.class);
+ if (!message.getAuthor().isPresent()) return true;
+ val author = message.getAuthor().get();
+ val dp = DiscordPlayer.getUser(author.getId().asString(), DiscordPlayer.class);
val chp = dp.getAs(TBMCPlayer.class);
if (chp == null) {
- message.reply("you need to connect your Minecraft account. On our server in " + DPUtils.botmention() + " do " + DiscordPlugin.getPrefix() + "connect ");
+ DPUtils.reply(message, null, "you need to connect your Minecraft account. On our server in " + DPUtils.botmention() + " do " + DiscordPlugin.getPrefix() + "connect ").subscribe();
return true;
}
- DiscordConnectedPlayer dcp = new DiscordConnectedPlayer(message.getAuthor(), message.getChannel(), chp.getUUID(), Bukkit.getOfflinePlayer(chp.getUUID()).getName());
+ val channel = message.getChannel().block();
+ DiscordConnectedPlayer dcp = new DiscordConnectedPlayer(message.getAuthor().get(), channel, chp.getUUID(), Bukkit.getOfflinePlayer(chp.getUUID()).getName(), module);
//Using a fake player with no login/logout, should be fine for this event
String groupid = chan.get().getGroupID(dcp);
if (groupid == null && !(chan.get() instanceof ChatRoom)) { //ChatRooms don't allow it unless the user joins, which happens later
- message.reply("sorry, you cannot use that Minecraft channel.");
+ DPUtils.reply(message, null, "sorry, you cannot use that Minecraft channel.").subscribe();
return true;
}
if (chan.get() instanceof ChatRoom) { //ChatRooms don't work well
- message.reply("chat rooms are not supported yet.");
+ DPUtils.reply(message, null, "chat rooms are not supported yet.").subscribe();
return true;
}
/*if (MCChatListener.getCustomChats().stream().anyMatch(cc -> cc.groupID.equals(groupid) && cc.mcchannel.ID.equals(chan.get().ID))) {
- message.reply("sorry, this MC chat is already connected to a different channel, multiple channels are not supported atm.");
+ DPUtils.reply(message, null, "sorry, this MC chat is already connected to a different channel, multiple channels are not supported atm.");
return true;
}*/ //TODO: "Channel admins" that can connect channels?
- MCChatCustom.addCustomChat(message.getChannel(), groupid, chan.get(), message.getAuthor(), dcp, 0, new HashSet<>());
+ MCChatCustom.addCustomChat(channel, groupid, chan.get(), author, dcp, 0, new HashSet<>());
if (chan.get() instanceof ChatRoom)
- message.reply("alright, connection made to the room!");
+ DPUtils.reply(message, null, "alright, connection made to the room!").subscribe();
else
- message.reply("alright, connection made to group `" + groupid + "`!");
+ DPUtils.reply(message, null, "alright, connection made to group `" + groupid + "`!").subscribe();
return true;
}
- private boolean checkPerms(IMessage message) {
- if (!PermissionUtils.hasPermissions(message.getChannel(), message.getAuthor(), Permissions.MANAGE_CHANNEL)) {
- message.reply("you need to have manage permissions for this channel!");
+ @SuppressWarnings("ConstantConditions")
+ private boolean checkPerms(Message message) {
+ if (!message.getAuthorAsMember().block().getBasePermissions().block().contains(Permission.MANAGE_CHANNELS)) {
+ DPUtils.reply(message, null, "you need to have manage permissions for this channel!").subscribe();
return true;
}
return false;
@@ -140,7 +147,7 @@ public class ChannelconCommand extends ICommand2DC {
"You need to have access to the MC channel and have manage permissions on the Discord channel.", //
"You also need to have your Minecraft account connected. In " + DPUtils.botmention() + " use " + DiscordPlugin.getPrefix() + "connect .", //
"Call this command from the channel you want to use.", //
- "Usage: @" + DiscordPlugin.dc.getOurUser().getName() + " channelcon ", //
+ "Usage: " + Objects.requireNonNull(DiscordPlugin.dc.getSelf().block()).getMention() + " channelcon ", //
"Use the ID (command) of the channel, for example `g` for the global chat.", //
"To remove a connection use @ChromaBot channelcon remove in the channel.", //
"Mentioning the bot is needed in this case because the " + DiscordPlugin.getPrefix() + " prefix only works in " + DPUtils.botmention() + ".", //
diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.java
index 23af46f..63b2fb5 100755
--- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.java
+++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.java
@@ -1,5 +1,6 @@
package buttondevteam.discordplugin.mcchat;
+import buttondevteam.discordplugin.DPUtils;
import buttondevteam.discordplugin.DiscordPlayer;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.commands.Command2DCSender;
@@ -7,6 +8,7 @@ import buttondevteam.discordplugin.commands.ICommand2DC;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
+import discord4j.core.object.entity.PrivateChannel;
import lombok.val;
@CommandClass(helpText = {
@@ -20,18 +22,20 @@ public class MCChatCommand extends ICommand2DC {
@Command2.Subcommand
public boolean def(Command2DCSender sender) {
val message = sender.getMessage();
- if (!message.getChannel().isPrivate()) {
- message.reply("this command can only be issued in a direct message with the bot.");
+ val channel = message.getChannel().block();
+ @SuppressWarnings("OptionalGetWithoutIsPresent") val author = message.getAuthor().get();
+ if (!(channel instanceof PrivateChannel)) {
+ DPUtils.reply(message, null, "this command can only be issued in a direct message with the bot.").subscribe();
return true;
}
- try (final DiscordPlayer user = DiscordPlayer.getUser(message.getAuthor().getStringID(), DiscordPlayer.class)) {
+ try (final DiscordPlayer user = DiscordPlayer.getUser(author.getId().asString(), DiscordPlayer.class)) {
boolean mcchat = !user.isMinecraftChatEnabled();
- MCChatPrivate.privateMCChat(message.getChannel(), mcchat, message.getAuthor(), user);
- message.reply("Minecraft chat " + (mcchat //
+ MCChatPrivate.privateMCChat(channel, mcchat, author, user);
+ DPUtils.reply(message, null, "Minecraft chat " + (mcchat //
? "enabled. Use '" + DiscordPlugin.getPrefix() + "mcchat' again to turn it off." //
- : "disabled."));
+ : "disabled.")).subscribe();
} catch (Exception e) {
- TBMCCoreAPI.SendException("Error while setting mcchat for user" + message.getAuthor().getName(), e);
+ TBMCCoreAPI.SendException("Error while setting mcchat for user " + author.getUsername() + "#" + author.getDiscriminator(), e);
}
return true;
} // TODO: Pin channel switching to indicate the current channel
diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.java
index 3d1b52f..c9edaca 100644
--- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.java
+++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.java
@@ -4,10 +4,11 @@ import buttondevteam.core.component.channel.Channel;
import buttondevteam.core.component.channel.ChatRoom;
import buttondevteam.discordplugin.DiscordConnectedPlayer;
import buttondevteam.lib.TBMCSystemChatEvent;
+import discord4j.core.object.entity.MessageChannel;
+import discord4j.core.object.entity.User;
+import discord4j.core.object.util.Snowflake;
import lombok.NonNull;
import lombok.val;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IUser;
import javax.annotation.Nullable;
import java.util.ArrayList;
@@ -21,7 +22,7 @@ public class MCChatCustom {
*/
static ArrayList lastmsgCustom = new ArrayList<>();
- public static void addCustomChat(IChannel channel, String groupid, Channel mcchannel, IUser user, DiscordConnectedPlayer dcp, int toggles, Set brtoggles) {
+ public static void addCustomChat(MessageChannel channel, String groupid, Channel mcchannel, User user, DiscordConnectedPlayer dcp, int toggles, Set brtoggles) {
if (mcchannel instanceof ChatRoom) {
((ChatRoom) mcchannel).joinRoom(dcp);
if (groupid == null) groupid = mcchannel.getGroupID(dcp);
@@ -30,19 +31,19 @@ public class MCChatCustom {
lastmsgCustom.add(lmd);
}
- public static boolean hasCustomChat(IChannel channel) {
- return lastmsgCustom.stream().anyMatch(lmd -> lmd.channel.getLongID() == channel.getLongID());
+ public static boolean hasCustomChat(Snowflake channel) {
+ return lastmsgCustom.stream().anyMatch(lmd -> lmd.channel.getId().asLong() == channel.asLong());
}
@Nullable
- public static CustomLMD getCustomChat(IChannel channel) {
- return lastmsgCustom.stream().filter(lmd -> lmd.channel.getLongID() == channel.getLongID()).findAny().orElse(null);
+ public static CustomLMD getCustomChat(Snowflake channel) {
+ return lastmsgCustom.stream().filter(lmd -> lmd.channel.getId().asLong() == channel.asLong()).findAny().orElse(null);
}
- public static boolean removeCustomChat(IChannel channel) {
- MCChatUtils.lastmsgfromd.remove(channel.getLongID());
+ public static boolean removeCustomChat(Snowflake channel) {
+ MCChatUtils.lastmsgfromd.remove(channel.asLong());
return lastmsgCustom.removeIf(lmd -> {
- if (lmd.channel.getLongID() != channel.getLongID())
+ if (lmd.channel.getId().asLong() != channel.asLong())
return false;
if (lmd.mcchannel instanceof ChatRoom)
((ChatRoom) lmd.mcchannel).leaveRoom(lmd.dcp);
@@ -61,8 +62,8 @@ public class MCChatCustom {
public int toggles;
public Set brtoggles;
- private CustomLMD(@NonNull IChannel channel, @NonNull IUser user,
- @NonNull String groupid, @NonNull Channel mcchannel, @NonNull DiscordConnectedPlayer dcp, int toggles, Set brtoggles) {
+ private CustomLMD(@NonNull MessageChannel channel, @NonNull User user,
+ @NonNull String groupid, @NonNull Channel mcchannel, @NonNull DiscordConnectedPlayer dcp, int toggles, Set brtoggles) {
super(channel, user);
groupID = groupid;
this.mcchannel = mcchannel;
diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java
index 5113f3c..aa7e278 100755
--- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java
+++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java
@@ -8,26 +8,26 @@ import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.DiscordSender;
import buttondevteam.discordplugin.DiscordSenderBase;
import buttondevteam.discordplugin.listeners.CommandListener;
+import buttondevteam.discordplugin.listeners.CommonListeners;
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
+import buttondevteam.discordplugin.util.Timings;
import buttondevteam.lib.*;
import buttondevteam.lib.chat.ChatMessage;
import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.player.TBMCPlayer;
import com.vdurmont.emoji.EmojiParser;
+import discord4j.core.event.domain.message.MessageCreateEvent;
+import discord4j.core.object.Embed;
+import discord4j.core.object.entity.*;
+import discord4j.core.object.util.Snowflake;
+import discord4j.core.spec.EmbedCreateSpec;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.scheduler.BukkitTask;
-import sx.blah.discord.api.internal.json.objects.EmbedObject;
-import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IMessage;
-import sx.blah.discord.handle.obj.IUser;
-import sx.blah.discord.util.DiscordException;
-import sx.blah.discord.util.EmbedBuilder;
-import sx.blah.discord.util.MissingPermissionsException;
+import reactor.core.publisher.Mono;
import java.awt.*;
import java.time.Instant;
@@ -36,15 +36,16 @@ import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class MCChatListener implements Listener {
- private BukkitTask sendtask;
- private LinkedBlockingQueue> sendevents = new LinkedBlockingQueue<>();
- private Runnable sendrunnable;
- private static Thread sendthread;
+ private BukkitTask sendtask;
+ private LinkedBlockingQueue> sendevents = new LinkedBlockingQueue<>();
+ private Runnable sendrunnable;
+ private static Thread sendthread;
private final MinecraftChatModule module;
public MCChatListener(MinecraftChatModule minecraftChatModule) {
@@ -52,359 +53,363 @@ public class MCChatListener implements Listener {
}
@EventHandler // Minecraft
- public void onMCChat(TBMCChatEvent ev) {
- if (!ComponentManager.isEnabled(MinecraftChatModule.class) || ev.isCancelled()) //SafeMode: Needed so it doesn't restart after server shutdown
- return;
- sendevents.add(new AbstractMap.SimpleEntry<>(ev, Instant.now()));
- if (sendtask != null)
- return;
- sendrunnable = () -> {
- sendthread = Thread.currentThread();
- processMCToDiscord();
- if (DiscordPlugin.plugin.isEnabled()) //Don't run again if shutting down
- sendtask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, sendrunnable);
- };
- sendtask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, sendrunnable);
- }
+ public void onMCChat(TBMCChatEvent ev) {
+ if (!ComponentManager.isEnabled(MinecraftChatModule.class) || ev.isCancelled()) //SafeMode: Needed so it doesn't restart after server shutdown
+ return;
+ sendevents.add(new AbstractMap.SimpleEntry<>(ev, Instant.now()));
+ if (sendtask != null)
+ return;
+ sendrunnable = () -> {
+ sendthread = Thread.currentThread();
+ processMCToDiscord();
+ if (DiscordPlugin.plugin.isEnabled()) //Don't run again if shutting down
+ sendtask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, sendrunnable);
+ };
+ sendtask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, sendrunnable);
+ }
- private void processMCToDiscord() {
- try {
- TBMCChatEvent e;
- Instant time;
- val se = sendevents.take(); // Wait until an element is available
- e = se.getKey();
- time = se.getValue();
+ private void processMCToDiscord() {
+ try {
+ TBMCChatEvent e;
+ Instant time;
+ val se = sendevents.take(); // Wait until an element is available
+ e = se.getKey();
+ time = se.getValue();
- final String authorPlayer = "[" + DPUtils.sanitizeStringNoEscape(e.getChannel().DisplayName().get()) + "] " //
- + ("Minecraft".equals(e.getOrigin()) ? "" : "[" + e.getOrigin().substring(0, 1) + "]") //
- + (DPUtils.sanitizeStringNoEscape(e.getSender() instanceof Player //
- ? ((Player) e.getSender()).getDisplayName() //
- : e.getSender().getName()));
- val color = e.getChannel().Color().get();
- final EmbedBuilder embed = new EmbedBuilder().withAuthorName(authorPlayer)
- .withDescription(e.getMessage()).withColor(new Color(color.getRed(),
- color.getGreen(), color.getBlue()));
- // embed.appendField("Channel", ((e.getSender() instanceof DiscordSenderBase ? "d|" : "")
- // + DiscordPlugin.sanitizeString(e.getChannel().DisplayName)), false);
- if (e.getSender() instanceof Player)
- DPUtils.embedWithHead(
- embed.withAuthorUrl("https://tbmcplugins.github.io/profile.html?type=minecraft&id="
- + ((Player) e.getSender()).getUniqueId()),
- e.getSender().getName());
- else if (e.getSender() instanceof DiscordSenderBase)
- embed.withAuthorIcon(((DiscordSenderBase) e.getSender()).getUser().getAvatarURL())
- .withAuthorUrl("https://tbmcplugins.github.io/profile.html?type=discord&id="
- + ((DiscordSenderBase) e.getSender()).getUser().getStringID()); // TODO: Constant/method to get URLs like this
- // embed.withFooterText(e.getChannel().DisplayName);
- embed.withTimestamp(time);
- final long nanoTime = System.nanoTime();
- InterruptibleConsumer doit = lastmsgdata -> {
- final EmbedObject embedObject = embed.build();
- if (lastmsgdata.message == null || lastmsgdata.message.isDeleted()
- || !authorPlayer.equals(lastmsgdata.message.getEmbeds().get(0).getAuthor().getName())
- || lastmsgdata.time / 1000000000f < nanoTime / 1000000000f - 120
- || !lastmsgdata.mcchannel.ID.equals(e.getChannel().ID)) {
- lastmsgdata.message = DiscordPlugin.sendMessageToChannelWait(lastmsgdata.channel, "",
- embedObject); // TODO Use ChromaBot API
- lastmsgdata.time = nanoTime;
- lastmsgdata.mcchannel = e.getChannel();
- lastmsgdata.content = embedObject.description;
- } else
- try {
- lastmsgdata.content = embedObject.description = lastmsgdata.content + "\n"
- + embedObject.description;// The message object doesn't get updated
- final MCChatUtils.LastMsgData _lastmsgdata = lastmsgdata;
- DPUtils.perform(() -> _lastmsgdata.message.edit("", embedObject));
- } catch (MissingPermissionsException | DiscordException e1) {
- TBMCCoreAPI.SendException("An error occurred while editing chat message!", e1);
- }
- };
- // Checks if the given channel is different than where the message was sent from
- // Or if it was from MC
- Predicate isdifferentchannel = ch -> !(e.getSender() instanceof DiscordSenderBase)
- || ((DiscordSenderBase) e.getSender()).getChannel().getLongID() != ch.getLongID();
+ final String authorPlayer = "[" + DPUtils.sanitizeStringNoEscape(e.getChannel().DisplayName().get()) + "] " //
+ + ("Minecraft".equals(e.getOrigin()) ? "" : "[" + e.getOrigin().substring(0, 1) + "]") //
+ + (DPUtils.sanitizeStringNoEscape(ThorpeUtils.getDisplayName(e.getSender())));
+ val color = e.getChannel().Color().get();
+ final Consumer embed = ecs -> {
+ ecs.setDescription(e.getMessage()).setColor(new Color(color.getRed(),
+ color.getGreen(), color.getBlue()));
+ if (e.getSender() instanceof Player)
+ DPUtils.embedWithHead(ecs, authorPlayer, e.getSender().getName(),
+ "https://tbmcplugins.github.io/profile.html?type=minecraft&id="
+ + ((Player) e.getSender()).getUniqueId());
+ else if (e.getSender() instanceof DiscordSenderBase)
+ ecs.setAuthor(authorPlayer, "https://tbmcplugins.github.io/profile.html?type=discord&id=" // TODO: Constant/method to get URLs like this
+ + ((DiscordSenderBase) e.getSender()).getUser().getId().asString(),
+ ((DiscordSenderBase) e.getSender()).getUser().getAvatarUrl());
+ else
+ DPUtils.embedWithHead(ecs, authorPlayer, e.getSender().getName(), null);
+ ecs.setTimestamp(time);
+ };
+ final long nanoTime = System.nanoTime();
+ InterruptibleConsumer doit = lastmsgdata -> {
+ if (lastmsgdata.message == null
+ || !authorPlayer.equals(lastmsgdata.message.getEmbeds().get(0).getAuthor().map(Embed.Author::getName).orElse(null))
+ || lastmsgdata.time / 1000000000f < nanoTime / 1000000000f - 120
+ || !lastmsgdata.mcchannel.ID.equals(e.getChannel().ID)) {
+ lastmsgdata.message = lastmsgdata.channel.createEmbed(embed).block();
+ lastmsgdata.time = nanoTime;
+ lastmsgdata.mcchannel = e.getChannel();
+ lastmsgdata.content = e.getMessage();
+ } else {
+ lastmsgdata.content = lastmsgdata.content + "\n"
+ + e.getMessage(); // The message object doesn't get updated
+ lastmsgdata.message.edit(mes -> mes.setEmbed(embed.andThen(ecs ->
+ ecs.setDescription(lastmsgdata.content)))).block();
+ }
+ };
+ // Checks if the given channel is different than where the message was sent from
+ // Or if it was from MC
+ Predicate isdifferentchannel = id -> !(e.getSender() instanceof DiscordSenderBase)
+ || ((DiscordSenderBase) e.getSender()).getChannel().getId().asLong() != id.asLong();
- if (e.getChannel().isGlobal()
- && (e.isFromCommand() || isdifferentchannel.test(module.chatChannel().get())))
- doit.accept(MCChatUtils.lastmsgdata == null
- ? MCChatUtils.lastmsgdata = new MCChatUtils.LastMsgData(module.chatChannel().get(), null)
- : MCChatUtils.lastmsgdata);
+ if (e.getChannel().isGlobal()
+ && (e.isFromCommand() || isdifferentchannel.test(module.chatChannel().get())))
+ doit.accept(MCChatUtils.lastmsgdata == null
+ ? MCChatUtils.lastmsgdata = new MCChatUtils.LastMsgData(module.chatChannelMono().block(), null)
+ : MCChatUtils.lastmsgdata);
- for (MCChatUtils.LastMsgData data : MCChatPrivate.lastmsgPerUser) {
- if ((e.isFromCommand() || isdifferentchannel.test(data.channel))
- && e.shouldSendTo(MCChatUtils.getSender(data.channel, data.user)))
- doit.accept(data);
- }
+ for (MCChatUtils.LastMsgData data : MCChatPrivate.lastmsgPerUser) {
+ if ((e.isFromCommand() || isdifferentchannel.test(data.channel.getId()))
+ && e.shouldSendTo(MCChatUtils.getSender(data.channel.getId(), data.user)))
+ doit.accept(data);
+ }
- val iterator = MCChatCustom.lastmsgCustom.iterator();
- while (iterator.hasNext()) {
- val lmd = iterator.next();
- if ((e.isFromCommand() || isdifferentchannel.test(lmd.channel)) //Test if msg is from Discord
- && e.getChannel().ID.equals(lmd.mcchannel.ID) //If it's from a command, the command msg has been deleted, so we need to send it
- && e.getGroupID().equals(lmd.groupID)) { //Check if this is the group we want to test - #58
- if (e.shouldSendTo(lmd.dcp)) //Check original user's permissions
- doit.accept(lmd);
- else {
- iterator.remove(); //If the user no longer has permission, remove the connection
- DiscordPlugin.sendMessageToChannel(lmd.channel, "The user no longer has permission to view the channel, connection removed.");
- }
- }
- }
- } catch (InterruptedException ex) { //Stop if interrupted anywhere
- sendtask.cancel();
- sendtask = null;
- } catch (Exception ex) {
- TBMCCoreAPI.SendException("Error while sending message to Discord!", ex);
- }
- }
+ val iterator = MCChatCustom.lastmsgCustom.iterator();
+ while (iterator.hasNext()) {
+ val lmd = iterator.next();
+ if ((e.isFromCommand() || isdifferentchannel.test(lmd.channel.getId())) //Test if msg is from Discord
+ && e.getChannel().ID.equals(lmd.mcchannel.ID) //If it's from a command, the command msg has been deleted, so we need to send it
+ && e.getGroupID().equals(lmd.groupID)) { //Check if this is the group we want to test - #58
+ if (e.shouldSendTo(lmd.dcp)) //Check original user's permissions
+ doit.accept(lmd);
+ else {
+ iterator.remove(); //If the user no longer has permission, remove the connection
+ lmd.channel.createMessage("The user no longer has permission to view the channel, connection removed.").subscribe();
+ }
+ }
+ }
+ } catch (InterruptedException ex) { //Stop if interrupted anywhere
+ sendtask.cancel();
+ sendtask = null;
+ } catch (Exception ex) {
+ TBMCCoreAPI.SendException("Error while sending message to Discord!", ex);
+ }
+ }
- @EventHandler
- public void onChatPreprocess(TBMCChatPreprocessEvent event) {
- int start = -1;
- while ((start = event.getMessage().indexOf('@', start + 1)) != -1) {
- int mid = event.getMessage().indexOf('#', start + 1);
- if (mid == -1)
- return;
- int end_ = event.getMessage().indexOf(' ', mid + 1);
- if (end_ == -1)
- end_ = event.getMessage().length();
- final int end = end_;
- final int startF = start;
- DiscordPlugin.dc.getUsersByName(event.getMessage().substring(start + 1, mid)).stream()
- .filter(u -> u.getDiscriminator().equals(event.getMessage().substring(mid + 1, end))).findAny()
- .ifPresent(user -> event.setMessage(event.getMessage().substring(0, startF) + "@" + user.getName()
- + (event.getMessage().length() > end ? event.getMessage().substring(end) : ""))); // TODO: Add formatting
- start = end; // Skip any @s inside the mention
- }
- }
+ @EventHandler
+ public void onChatPreprocess(TBMCChatPreprocessEvent event) {
+ int start = -1;
+ while ((start = event.getMessage().indexOf('@', start + 1)) != -1) {
+ int mid = event.getMessage().indexOf('#', start + 1);
+ if (mid == -1)
+ return;
+ int end_ = event.getMessage().indexOf(' ', mid + 1);
+ if (end_ == -1)
+ end_ = event.getMessage().length();
+ final int end = end_;
+ final int startF = start;
+ val user = DiscordPlugin.dc.getUsers().filter(u -> u.getUsername().equals(event.getMessage().substring(startF + 1, mid)))
+ .filter(u -> u.getDiscriminator().equals(event.getMessage().substring(mid + 1, end))).blockFirst();
+ if (user != null) //TODO: Nicknames
+ event.setMessage(event.getMessage().substring(0, startF) + "@" + user.getUsername()
+ + (event.getMessage().length() > end ? event.getMessage().substring(end) : "")); // TODO: Add formatting
+ start = end; // Skip any @s inside the mention
+ }
+ }
- // ......................DiscordSender....DiscordConnectedPlayer.DiscordPlayerSender
- // Offline public chat......x............................................
- // Online public chat.......x...........................................x
- // Offline private chat.....x.......................x....................
- // Online private chat......x.......................x...................x
- // If online and enabling private chat, don't login
- // If leaving the server and private chat is enabled (has ConnectedPlayer), call login in a task on lowest priority
- // If private chat is enabled and joining the server, logout the fake player on highest priority
- // If online and disabling private chat, don't logout
- // The maps may not contain the senders for UnconnectedSenders
+ // ......................DiscordSender....DiscordConnectedPlayer.DiscordPlayerSender
+ // Offline public chat......x............................................
+ // Online public chat.......x...........................................x
+ // Offline private chat.....x.......................x....................
+ // Online private chat......x.......................x...................x
+ // If online and enabling private chat, don't login
+ // If leaving the server and private chat is enabled (has ConnectedPlayer), call login in a task on lowest priority
+ // If private chat is enabled and joining the server, logout the fake player on highest priority
+ // If online and disabling private chat, don't logout
+ // The maps may not contain the senders for UnconnectedSenders
- /**
- * Stop the listener. Any calls to onMCChat will restart it as long as we're not in safe mode.
- *
- * @param wait Wait 5 seconds for the threads to stop
- */
- public static void stop(boolean wait) {
- if (sendthread != null) sendthread.interrupt();
- if (recthread != null) recthread.interrupt();
- try {
- if (sendthread != null) {
- sendthread.interrupt();
- if (wait)
- sendthread.join(5000);
- }
- if (recthread != null) {
- recthread.interrupt();
- if (wait)
- recthread.join(5000);
- }
- MCChatUtils.lastmsgdata = null;
- MCChatPrivate.lastmsgPerUser.clear();
- MCChatCustom.lastmsgCustom.clear();
- MCChatUtils.lastmsgfromd.clear();
- MCChatUtils.ConnectedSenders.clear();
- MCChatUtils.UnconnectedSenders.clear();
- recthread = sendthread = null;
- } catch (InterruptedException e) {
- e.printStackTrace(); //This thread shouldn't be interrupted
- }
- }
+ /**
+ * Stop the listener. Any calls to onMCChat will restart it as long as we're not in safe mode.
+ *
+ * @param wait Wait 5 seconds for the threads to stop
+ */
+ public static void stop(boolean wait) {
+ if (sendthread != null) sendthread.interrupt();
+ if (recthread != null) recthread.interrupt();
+ try {
+ if (sendthread != null) {
+ sendthread.interrupt();
+ if (wait)
+ sendthread.join(5000);
+ }
+ if (recthread != null) {
+ recthread.interrupt();
+ if (wait)
+ recthread.join(5000);
+ }
+ MCChatUtils.lastmsgdata = null;
+ MCChatPrivate.lastmsgPerUser.clear();
+ MCChatCustom.lastmsgCustom.clear();
+ MCChatUtils.lastmsgfromd.clear();
+ MCChatUtils.ConnectedSenders.clear();
+ MCChatUtils.UnconnectedSenders.clear();
+ recthread = sendthread = null;
+ } catch (InterruptedException e) {
+ e.printStackTrace(); //This thread shouldn't be interrupted
+ }
+ }
- private BukkitTask rectask;
- private LinkedBlockingQueue recevents = new LinkedBlockingQueue<>();
- private Runnable recrun;
- private static Thread recthread;
+ private BukkitTask rectask;
+ private LinkedBlockingQueue recevents = new LinkedBlockingQueue<>();
+ private Runnable recrun;
+ private static Thread recthread;
// Discord
- public boolean handleDiscord(MessageReceivedEvent ev) {
- if (!ComponentManager.isEnabled(MinecraftChatModule.class))
- return false;
- val author = ev.getMessage().getAuthor();
- final boolean hasCustomChat = MCChatCustom.hasCustomChat(ev.getChannel());
- if (ev.getMessage().getChannel().getLongID() != module.chatChannel().get().getLongID()
- && !(ev.getMessage().getChannel().isPrivate() && MCChatPrivate.isMinecraftChatEnabled(author.getStringID()))
- && !hasCustomChat)
- return false; //Chat isn't enabled on this channel
- if (ev.getMessage().getChannel().isPrivate() //Only in private chat
- && ev.getMessage().getContent().length() < "/mcchat<>".length()
- && ev.getMessage().getContent().replace("/", "")
- .equalsIgnoreCase("mcchat")) //Either mcchat or /mcchat
- return false; //Allow disabling the chat if needed
- if (CommandListener.runCommand(ev.getMessage(), true))
- return true; //Allow running commands in chat channels
- MCChatUtils.resetLastMessage(ev.getChannel());
- recevents.add(ev);
- if (rectask != null)
- return true;
- recrun = () -> { //Don't return in a while loop next time
- recthread = Thread.currentThread();
- processDiscordToMC();
- if (DiscordPlugin.plugin.isEnabled()) //Don't run again if shutting down
- rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Continue message processing
- };
- rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Start message processing
- return true;
- }
+ public Mono handleDiscord(MessageCreateEvent ev) {
+ val ret = Mono.just(true);
+ if (!ComponentManager.isEnabled(MinecraftChatModule.class))
+ return ret;
+ Timings timings = CommonListeners.timings;
+ timings.printElapsed("Chat event");
+ val author = ev.getMessage().getAuthor();
+ final boolean hasCustomChat = MCChatCustom.hasCustomChat(ev.getMessage().getChannelId());
+ return ev.getMessage().getChannel().filter(channel -> {
+ timings.printElapsed("Filter 1");
+ return !(ev.getMessage().getChannelId().asLong() != module.chatChannel().get().asLong()
+ && !(channel instanceof PrivateChannel
+ && author.map(u -> MCChatPrivate.isMinecraftChatEnabled(u.getId().asString())).orElse(false)
+ && !hasCustomChat)); //Chat isn't enabled on this channel
+ }).filter(channel -> {
+ timings.printElapsed("Filter 2");
+ return !(channel instanceof PrivateChannel //Only in private chat
+ && ev.getMessage().getContent().isPresent()
+ && ev.getMessage().getContent().get().length() < "/mcchat<>".length()
+ && ev.getMessage().getContent().get().replace("/", "")
+ .equalsIgnoreCase("mcchat")); //Either mcchat or /mcchat
+ //Allow disabling the chat if needed
+ }).filterWhen(channel -> CommandListener.runCommand(ev.getMessage(), channel, true))
+ //Allow running commands in chat channels
+ .filter(channel -> {
+ MCChatUtils.resetLastMessage(channel);
+ recevents.add(ev);
+ timings.printElapsed("Message event added");
+ if (rectask != null)
+ return true;
+ recrun = () -> { //Don't return in a while loop next time
+ recthread = Thread.currentThread();
+ processDiscordToMC();
+ if (DiscordPlugin.plugin.isEnabled()) //Don't run again if shutting down
+ rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Continue message processing
+ };
+ rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Start message processing
+ return true;
+ }).map(b -> false).defaultIfEmpty(true);
+ }
- private void processDiscordToMC() {
- @val
- sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent event;
- try {
- event = recevents.take();
- } catch (InterruptedException e1) {
- rectask.cancel();
- return;
- }
- val sender = event.getMessage().getAuthor();
- String dmessage = event.getMessage().getContent();
- try {
- final DiscordSenderBase dsender = MCChatUtils.getSender(event.getMessage().getChannel(), sender);
- val user = dsender.getChromaUser();
+ private void processDiscordToMC() {
+ MessageCreateEvent event;
+ try {
+ event = recevents.take();
+ } catch (InterruptedException e1) {
+ rectask.cancel();
+ return;
+ }
+ val sender = event.getMessage().getAuthor().orElse(null);
+ String dmessage = event.getMessage().getContent().orElse("");
+ try {
+ final DiscordSenderBase dsender = MCChatUtils.getSender(event.getMessage().getChannelId(), sender);
+ val user = dsender.getChromaUser();
- for (IUser u : event.getMessage().getMentions()) {
- dmessage = dmessage.replace(u.mention(false), "@" + u.getName()); // TODO: IG Formatting
- final String nick = u.getNicknameForGuild(DiscordPlugin.mainServer);
- dmessage = dmessage.replace(u.mention(true), "@" + (nick != null ? nick : u.getName()));
- }
- for (IChannel ch : event.getMessage().getChannelMentions()) {
- dmessage = dmessage.replace(ch.mention(), "#" + ch.getName()); // TODO: IG Formatting
- }
+ for (User u : event.getMessage().getUserMentions().toIterable()) { //TODO: Role mentions
+ dmessage = dmessage.replace(u.getMention(), "@" + u.getUsername()); // TODO: IG Formatting
+ val m = u.asMember(DiscordPlugin.mainServer.getId()).block();
+ if (m != null) {
+ final String nick = m.getDisplayName();
+ dmessage = dmessage.replace(m.getNicknameMention(), "@" + nick);
+ }
+ }
+ for (GuildChannel ch : event.getGuild().flux().flatMap(Guild::getChannels).toIterable()) {
+ dmessage = dmessage.replace(ch.getMention(), "#" + ch.getName()); // TODO: IG Formatting
+ }
- dmessage = EmojiParser.parseToAliases(dmessage, EmojiParser.FitzpatrickAction.PARSE); //Converts emoji to text- TODO: Add option to disable (resource pack?)
- dmessage = dmessage.replaceAll(":(\\S+)\\|type_(?:(\\d)|(1)_2):", ":$1::skin-tone-$2:"); //Convert to Discord's format so it still shows up
+ dmessage = EmojiParser.parseToAliases(dmessage, EmojiParser.FitzpatrickAction.PARSE); //Converts emoji to text- TODO: Add option to disable (resource pack?)
+ dmessage = dmessage.replaceAll(":(\\S+)\\|type_(?:(\\d)|(1)_2):", ":$1::skin-tone-$2:"); //Convert to Discord's format so it still shows up
- Function getChatMessage = msg -> //
- msg + (event.getMessage().getAttachments().size() > 0 ? "\n" + event.getMessage()
- .getAttachments().stream().map(IMessage.Attachment::getUrl).collect(Collectors.joining("\n"))
- : "");
+ Function getChatMessage = msg -> //
+ msg + (event.getMessage().getAttachments().size() > 0 ? "\n" + event.getMessage()
+ .getAttachments().stream().map(Attachment::getUrl).collect(Collectors.joining("\n"))
+ : "");
- MCChatCustom.CustomLMD clmd = MCChatCustom.getCustomChat(event.getChannel());
+ MCChatCustom.CustomLMD clmd = MCChatCustom.getCustomChat(event.getMessage().getChannelId());
- boolean react = false;
+ boolean react = false;
- if (dmessage.startsWith("/")) { // Ingame command
- DPUtils.perform(() -> {
- if (!event.getMessage().isDeleted() && !event.getChannel().isPrivate())
- event.getMessage().delete();
- });
- final String cmd = dmessage.substring(1);
- final String cmdlowercased = cmd.toLowerCase();
- if (dsender instanceof DiscordSender && module.whitelistedCommands().get().stream()
- .noneMatch(s -> cmdlowercased.equals(s) || cmdlowercased.startsWith(s + " "))) {
- // Command not whitelisted
- dsender.sendMessage("Sorry, you can only access these commands:\n"
- + module.whitelistedCommands().get().stream().map(uc -> "/" + uc)
- .collect(Collectors.joining(", "))
- + (user.getConnectedID(TBMCPlayer.class) == null
- ? "\nTo access your commands, first please connect your accounts, using /connect in "
- + DPUtils.botmention()
- + "\nThen y"
- : "\nY")
- + "ou can access all of your regular commands (even offline) in private chat: DM me `mcchat`!");
- return;
- }
- val ev = new TBMCCommandPreprocessEvent(dsender, dmessage);
- Bukkit.getPluginManager().callEvent(ev);
- if (ev.isCancelled())
- return;
- int spi = cmdlowercased.indexOf(' ');
- final String topcmd = spi == -1 ? cmdlowercased : cmdlowercased.substring(0, spi);
- Optional ch = Channel.getChannels()
- .filter(c -> c.ID.equalsIgnoreCase(topcmd)
- || (c.IDs().get().length > 0
- && Arrays.stream(c.IDs().get()).anyMatch(id -> id.equalsIgnoreCase(topcmd)))).findAny();
- if (!ch.isPresent()) //TODO: What if talking in the public chat while we have it on a different one
- Bukkit.getScheduler().runTask(DiscordPlugin.plugin, //Commands need to be run sync
- () -> { //TODO: Better handling...
- val channel = user.channel();
- val chtmp = channel.get();
- if (clmd != null) {
- channel.set(clmd.mcchannel); //Hack to send command in the channel
- } //TODO: Permcheck isn't implemented for commands
- VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd);
- Bukkit.getLogger().info(dsender.getName() + " issued command from Discord: /" + cmdlowercased);
- if (clmd != null)
- channel.set(chtmp);
- });
- else {
- Channel chc = ch.get();
- if (!chc.isGlobal() && !event.getMessage().getChannel().isPrivate())
- dsender.sendMessage(
- "You can only talk in a public chat here. DM `mcchat` to enable private chat to talk in the other channels.");
- else {
- if (spi == -1) // Switch channels
- {
- val channel = dsender.getChromaUser().channel();
- val oldch = channel.get();
- if (oldch instanceof ChatRoom)
- ((ChatRoom) oldch).leaveRoom(dsender);
- if (!oldch.ID.equals(chc.ID)) {
- channel.set(chc);
- if (chc instanceof ChatRoom)
- ((ChatRoom) chc).joinRoom(dsender);
- } else
- channel.set(Channel.GlobalChat);
- dsender.sendMessage("You're now talking in: "
- + DPUtils.sanitizeString(channel.get().DisplayName().get()));
- } else { // Send single message
- final String msg = cmd.substring(spi + 1);
- val cmb = ChatMessage.builder(dsender, user, getChatMessage.apply(msg)).fromCommand(true);
- if (clmd == null)
- TBMCChatAPI.SendChatMessage(cmb.build(), chc);
- else
- TBMCChatAPI.SendChatMessage(cmb.permCheck(clmd.dcp).build(), chc);
- react = true;
- }
- }
- }
- } else {// Not a command
- if (dmessage.length() == 0 && event.getMessage().getAttachments().size() == 0
- && !event.getChannel().isPrivate() && event.getMessage().isSystemMessage()) {
- val rtr = clmd != null ? clmd.mcchannel.getRTR(clmd.dcp)
- : dsender.getChromaUser().channel().get().getRTR(dsender);
- TBMCChatAPI.SendSystemMessage(clmd != null ? clmd.mcchannel : dsender.getChromaUser().channel().get(), rtr,
- (dsender instanceof Player ? ((Player) dsender).getDisplayName()
- : dsender.getName()) + " pinned a message on Discord.", TBMCSystemChatEvent.BroadcastTarget.ALL);
- }
- else {
- val cmb = ChatMessage.builder(dsender, user, getChatMessage.apply(dmessage)).fromCommand(false);
- if (clmd != null)
- TBMCChatAPI.SendChatMessage(cmb.permCheck(clmd.dcp).build(), clmd.mcchannel);
- else
- TBMCChatAPI.SendChatMessage(cmb.build());
- react = true;
- }
- }
- if (react) {
- try {
- val lmfd = MCChatUtils.lastmsgfromd.get(event.getChannel().getLongID());
- if (lmfd != null) {
- DPUtils.perform(() -> lmfd.removeReaction(DiscordPlugin.dc.getOurUser(),
- DiscordPlugin.DELIVERED_REACTION)); // Remove it no matter what, we know it's there 99.99% of the time
- }
- } catch (Exception e) {
- TBMCCoreAPI.SendException("An error occured while removing reactions from chat!", e);
- }
- MCChatUtils.lastmsgfromd.put(event.getChannel().getLongID(), event.getMessage());
- DPUtils.perform(() -> event.getMessage().addReaction(DiscordPlugin.DELIVERED_REACTION));
- }
- } catch (Exception e) {
- TBMCCoreAPI.SendException("An error occured while handling message \"" + dmessage + "\"!", e);
- }
- }
+ val sendChannel = event.getMessage().getChannel().block();
+ boolean isPrivate = sendChannel instanceof PrivateChannel;
+ if (dmessage.startsWith("/")) { // Ingame command
+ if (!isPrivate)
+ event.getMessage().delete().subscribe();
+ final String cmd = dmessage.substring(1);
+ final String cmdlowercased = cmd.toLowerCase();
+ if (dsender instanceof DiscordSender && module.whitelistedCommands().get().stream()
+ .noneMatch(s -> cmdlowercased.equals(s) || cmdlowercased.startsWith(s + " "))) {
+ // Command not whitelisted
+ dsender.sendMessage("Sorry, you can only access these commands:\n"
+ + module.whitelistedCommands().get().stream().map(uc -> "/" + uc)
+ .collect(Collectors.joining(", "))
+ + (user.getConnectedID(TBMCPlayer.class) == null
+ ? "\nTo access your commands, first please connect your accounts, using /connect in "
+ + DPUtils.botmention()
+ + "\nThen y"
+ : "\nY")
+ + "ou can access all of your regular commands (even offline) in private chat: DM me `mcchat`!");
+ return;
+ }
+ val ev = new TBMCCommandPreprocessEvent(dsender, dmessage);
+ Bukkit.getPluginManager().callEvent(ev);
+ if (ev.isCancelled())
+ return;
+ int spi = cmdlowercased.indexOf(' ');
+ final String topcmd = spi == -1 ? cmdlowercased : cmdlowercased.substring(0, spi);
+ Optional ch = Channel.getChannels()
+ .filter(c -> c.ID.equalsIgnoreCase(topcmd)
+ || (c.IDs().get().length > 0
+ && Arrays.stream(c.IDs().get()).anyMatch(id -> id.equalsIgnoreCase(topcmd)))).findAny();
+ if (!ch.isPresent()) //TODO: What if talking in the public chat while we have it on a different one
+ Bukkit.getScheduler().runTask(DiscordPlugin.plugin, //Commands need to be run sync
+ () -> { //TODO: Better handling...
+ val channel = user.channel();
+ val chtmp = channel.get();
+ if (clmd != null) {
+ channel.set(clmd.mcchannel); //Hack to send command in the channel
+ } //TODO: Permcheck isn't implemented for commands
+ VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd);
+ Bukkit.getLogger().info(dsender.getName() + " issued command from Discord: /" + cmdlowercased);
+ if (clmd != null)
+ channel.set(chtmp);
+ });
+ else {
+ Channel chc = ch.get();
+ if (!chc.isGlobal() && !isPrivate)
+ dsender.sendMessage(
+ "You can only talk in a public chat here. DM `mcchat` to enable private chat to talk in the other channels.");
+ else {
+ if (spi == -1) // Switch channels
+ {
+ val channel = dsender.getChromaUser().channel();
+ val oldch = channel.get();
+ if (oldch instanceof ChatRoom)
+ ((ChatRoom) oldch).leaveRoom(dsender);
+ if (!oldch.ID.equals(chc.ID)) {
+ channel.set(chc);
+ if (chc instanceof ChatRoom)
+ ((ChatRoom) chc).joinRoom(dsender);
+ } else
+ channel.set(Channel.GlobalChat);
+ dsender.sendMessage("You're now talking in: "
+ + DPUtils.sanitizeString(channel.get().DisplayName().get()));
+ } else { // Send single message
+ final String msg = cmd.substring(spi + 1);
+ val cmb = ChatMessage.builder(dsender, user, getChatMessage.apply(msg)).fromCommand(true);
+ if (clmd == null)
+ TBMCChatAPI.SendChatMessage(cmb.build(), chc);
+ else
+ TBMCChatAPI.SendChatMessage(cmb.permCheck(clmd.dcp).build(), chc);
+ react = true;
+ }
+ }
+ }
+ } else {// Not a command
+ if (dmessage.length() == 0 && event.getMessage().getAttachments().size() == 0
+ && !isPrivate && event.getMessage().getType() == Message.Type.CHANNEL_PINNED_MESSAGE) {
+ val rtr = clmd != null ? clmd.mcchannel.getRTR(clmd.dcp)
+ : dsender.getChromaUser().channel().get().getRTR(dsender);
+ TBMCChatAPI.SendSystemMessage(clmd != null ? clmd.mcchannel : dsender.getChromaUser().channel().get(), rtr,
+ (dsender instanceof Player ? ((Player) dsender).getDisplayName()
+ : dsender.getName()) + " pinned a message on Discord.", TBMCSystemChatEvent.BroadcastTarget.ALL);
+ } else {
+ val cmb = ChatMessage.builder(dsender, user, getChatMessage.apply(dmessage)).fromCommand(false);
+ if (clmd != null)
+ TBMCChatAPI.SendChatMessage(cmb.permCheck(clmd.dcp).build(), clmd.mcchannel);
+ else
+ TBMCChatAPI.SendChatMessage(cmb.build());
+ react = true;
+ }
+ }
+ if (react) {
+ try {
+ val lmfd = MCChatUtils.lastmsgfromd.get(event.getMessage().getChannelId().asLong());
+ if (lmfd != null) {
+ lmfd.removeSelfReaction(DiscordPlugin.DELIVERED_REACTION).subscribe(); // Remove it no matter what, we know it's there 99.99% of the time
+ }
+ } catch (Exception e) {
+ TBMCCoreAPI.SendException("An error occured while removing reactions from chat!", e);
+ }
+ MCChatUtils.lastmsgfromd.put(event.getMessage().getChannelId().asLong(), event.getMessage());
+ event.getMessage().addReaction(DiscordPlugin.DELIVERED_REACTION).subscribe();
+ }
+ } catch (Exception e) {
+ TBMCCoreAPI.SendException("An error occured while handling message \"" + dmessage + "\"!", e);
+ }
+ }
- @FunctionalInterface
- private interface InterruptibleConsumer {
- void accept(T value) throws TimeoutException, InterruptedException;
- }
+ @FunctionalInterface
+ private interface InterruptibleConsumer {
+ void accept(T value) throws TimeoutException, InterruptedException;
+ }
}
diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.java
index 7d87376..344ecf5 100644
--- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.java
+++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatPrivate.java
@@ -1,17 +1,14 @@
package buttondevteam.discordplugin.mcchat;
+import buttondevteam.core.ComponentManager;
import buttondevteam.discordplugin.DiscordConnectedPlayer;
import buttondevteam.discordplugin.DiscordPlayer;
-import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.player.TBMCPlayer;
+import discord4j.core.object.entity.MessageChannel;
+import discord4j.core.object.entity.PrivateChannel;
+import discord4j.core.object.entity.User;
import lombok.val;
import org.bukkit.Bukkit;
-import org.bukkit.event.Event;
-import org.bukkit.event.player.PlayerJoinEvent;
-import org.bukkit.event.player.PlayerQuitEvent;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IPrivateChannel;
-import sx.blah.discord.handle.obj.IUser;
import java.util.ArrayList;
@@ -22,27 +19,31 @@ public class MCChatPrivate {
*/
static ArrayList lastmsgPerUser = new ArrayList<>();
- public static boolean privateMCChat(IChannel channel, boolean start, IUser user, DiscordPlayer dp) {
+ public static boolean privateMCChat(MessageChannel channel, boolean start, User user, DiscordPlayer dp) {
TBMCPlayer mcp = dp.getAs(TBMCPlayer.class);
if (mcp != null) { // If the accounts aren't connected, can't make a connected sender
val p = Bukkit.getPlayer(mcp.getUUID());
val op = Bukkit.getOfflinePlayer(mcp.getUUID());
+ val mcm = ComponentManager.getIfEnabled(MinecraftChatModule.class);
if (start) {
- val sender = new DiscordConnectedPlayer(user, channel, mcp.getUUID(), op.getName());
+ val sender = new DiscordConnectedPlayer(user, channel, mcp.getUUID(), op.getName(), mcm);
MCChatUtils.addSender(MCChatUtils.ConnectedSenders, user, sender);
if (p == null)// Player is offline - If the player is online, that takes precedence
- callEventSync(new PlayerJoinEvent(sender, ""));
+ MCChatUtils.callLoginEvents(sender);
} else {
- val sender = MCChatUtils.removeSender(MCChatUtils.ConnectedSenders, channel, user);
- if (p == null)// Player is offline - If the player is online, that takes precedence
- callEventSync(new PlayerQuitEvent(sender, ""));
+ val sender = MCChatUtils.removeSender(MCChatUtils.ConnectedSenders, channel.getId(), user);
+ assert sender != null;
+ if (p == null // Player is offline - If the player is online, that takes precedence
+ && sender.isLoggedIn()) //Don't call the quit event if login failed
+ MCChatUtils.callLogoutEvent(sender, true);
+ sender.setLoggedIn(false);
}
} // ---- PermissionsEx warning is normal on logout ----
if (!start)
- MCChatUtils.lastmsgfromd.remove(channel.getLongID());
+ MCChatUtils.lastmsgfromd.remove(channel.getId().asLong());
return start //
- ? lastmsgPerUser.add(new MCChatUtils.LastMsgData(channel, user)) // Doesn't support group DMs
- : lastmsgPerUser.removeIf(lmd -> lmd.channel.getLongID() == channel.getLongID());
+ ? lastmsgPerUser.add(new MCChatUtils.LastMsgData(channel, user)) // Doesn't support group DMs
+ : lastmsgPerUser.removeIf(lmd -> lmd.channel.getId().asLong() == channel.getId().asLong());
}
public static boolean isMinecraftChatEnabled(DiscordPlayer dp) {
@@ -51,18 +52,16 @@ public class MCChatPrivate {
public static boolean isMinecraftChatEnabled(String did) { // Don't load the player data just for this
return lastmsgPerUser.stream()
- .anyMatch(lmd -> ((IPrivateChannel) lmd.channel).getRecipient().getStringID().equals(did));
+ .anyMatch(lmd -> ((PrivateChannel) lmd.channel)
+ .getRecipientIds().stream().anyMatch(u -> u.asString().equals(did)));
}
public static void logoutAll() {
for (val entry : MCChatUtils.ConnectedSenders.entrySet())
for (val valueEntry : entry.getValue().entrySet())
if (MCChatUtils.getSender(MCChatUtils.OnlineSenders, valueEntry.getKey(), valueEntry.getValue().getUser()) == null) //If the player is online then the fake player was already logged out
- MCChatUtils.callEventExcludingSome(new PlayerQuitEvent(valueEntry.getValue(), "")); //This is sync
+ MCChatUtils.callLogoutEvent(valueEntry.getValue(), false); //This is sync
MCChatUtils.ConnectedSenders.clear();
}
- private static void callEventSync(Event event) {
- Bukkit.getScheduler().runTask(DiscordPlugin.plugin, () -> MCChatUtils.callEventExcludingSome(event));
- }
}
diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java
index 3f04e56..92af87d 100644
--- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java
+++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java
@@ -1,10 +1,13 @@
package buttondevteam.discordplugin.mcchat;
import buttondevteam.core.ComponentManager;
-import buttondevteam.core.component.channel.Channel;
import buttondevteam.discordplugin.*;
import buttondevteam.discordplugin.broadcaster.GeneralEventBroadcasterModule;
+import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.TBMCSystemChatEvent;
+import com.google.common.collect.Sets;
+import discord4j.core.object.entity.*;
+import discord4j.core.object.util.Snowflake;
import io.netty.util.collection.LongObjectHashMap;
import lombok.RequiredArgsConstructor;
import lombok.experimental.var;
@@ -13,16 +16,20 @@ import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
+import org.bukkit.event.player.AsyncPlayerPreLoginEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerLoginEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.AuthorNagException;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredListener;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IMessage;
-import sx.blah.discord.handle.obj.IUser;
+import reactor.core.publisher.Mono;
import javax.annotation.Nullable;
+import java.net.InetAddress;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -34,23 +41,22 @@ public class MCChatUtils {
/**
* May contain P<DiscordID> as key for public chat
*/
- public static final HashMap> UnconnectedSenders = new HashMap<>();
- public static final HashMap> ConnectedSenders = new HashMap<>();
+ public static final HashMap> UnconnectedSenders = new HashMap<>();
+ public static final HashMap> ConnectedSenders = new HashMap<>();
/**
* May contain P<DiscordID> as key for public chat
*/
- public static final HashMap> OnlineSenders = new HashMap<>();
+ public static final HashMap> OnlineSenders = new HashMap<>();
static @Nullable LastMsgData lastmsgdata;
- static LongObjectHashMap lastmsgfromd = new LongObjectHashMap<>(); // Last message sent by a Discord user, used for clearing checkmarks
+ static LongObjectHashMap lastmsgfromd = new LongObjectHashMap<>(); // Last message sent by a Discord user, used for clearing checkmarks
private static MinecraftChatModule module;
+ private static HashMap, HashSet> staticExcludedPlugins = new HashMap<>();
public static void updatePlayerList() {
if (notEnabled()) return;
- DPUtils.performNoWait(() -> {
- if (lastmsgdata != null)
- updatePL(lastmsgdata);
- MCChatCustom.lastmsgCustom.forEach(MCChatUtils::updatePL);
- });
+ if (lastmsgdata != null)
+ updatePL(lastmsgdata);
+ MCChatCustom.lastmsgCustom.forEach(MCChatUtils::updatePL);
}
private static boolean notEnabled() {
@@ -64,55 +70,60 @@ public class MCChatUtils {
}
private static void updatePL(LastMsgData lmd) {
- String topic = lmd.channel.getTopic();
- if (topic == null || topic.length() == 0)
+ if (!(lmd.channel instanceof TextChannel)) {
+ TBMCCoreAPI.SendException("Failed to update player list for channel " + lmd.channel.getId(),
+ new Exception("The channel isn't a (guild) text channel."));
+ return;
+ }
+ String topic = ((TextChannel) lmd.channel).getTopic().orElse("");
+ if (topic.length() == 0)
topic = ".\n----\nMinecraft chat\n----\n.";
String[] s = topic.split("\\n----\\n");
if (s.length < 3)
return;
s[0] = Bukkit.getOnlinePlayers().size() + " player" + (Bukkit.getOnlinePlayers().size() != 1 ? "s" : "")
- + " online";
+ + " online";
s[s.length - 1] = "Players: " + Bukkit.getOnlinePlayers().stream()
- .map(p -> DPUtils.sanitizeString(p.getDisplayName())).collect(Collectors.joining(", "));
- lmd.channel.changeTopic(String.join("\n----\n", s));
+ .map(p -> DPUtils.sanitizeString(p.getDisplayName())).collect(Collectors.joining(", "));
+ ((TextChannel) lmd.channel).edit(tce -> tce.setTopic(String.join("\n----\n", s)).setReason("Player list update")).subscribe(); //Don't wait
}
- public static T addSender(HashMap> senders,
- IUser user, T sender) {
- return addSender(senders, user.getStringID(), sender);
+ public static T addSender(HashMap> senders,
+ User user, T sender) {
+ return addSender(senders, user.getId().asString(), sender);
}
- public static T addSender(HashMap> senders,
+ public static T addSender(HashMap> senders,
String did, T sender) {
var map = senders.get(did);
if (map == null)
map = new HashMap<>();
- map.put(sender.getChannel(), sender);
+ map.put(sender.getChannel().getId(), sender);
senders.put(did, map);
return sender;
}
- public static T getSender(HashMap> senders,
- IChannel channel, IUser user) {
- var map = senders.get(user.getStringID());
+ public static T getSender(HashMap> senders,
+ Snowflake channel, User user) {
+ var map = senders.get(user.getId().asString());
if (map != null)
return map.get(channel);
return null;
}
- public static T removeSender(HashMap> senders,
- IChannel channel, IUser user) {
- var map = senders.get(user.getStringID());
+ public static T removeSender(HashMap> senders,
+ Snowflake channel, User user) {
+ var map = senders.get(user.getId().asString());
if (map != null)
return map.remove(channel);
return null;
}
- public static void forAllMCChat(Consumer action) {
+ public static void forAllMCChat(Consumer> action) {
if (notEnabled()) return;
- action.accept(module.chatChannel().get());
+ action.accept(module.chatChannelMono());
for (LastMsgData data : MCChatPrivate.lastmsgPerUser)
- action.accept(data.channel);
+ action.accept(Mono.just(data.channel));
// lastmsgCustom.forEach(cc -> action.accept(cc.channel)); - Only send relevant messages to custom chat
}
@@ -123,11 +134,11 @@ public class MCChatUtils {
* @param toggle The toggle to check
* @param hookmsg Whether the message is also sent from the hook
*/
- public static void forCustomAndAllMCChat(Consumer action, @Nullable ChannelconBroadcast toggle, boolean hookmsg) {
+ public static void forCustomAndAllMCChat(Consumer> action, @Nullable ChannelconBroadcast toggle, boolean hookmsg) {
if (notEnabled()) return;
if (!GeneralEventBroadcasterModule.isHooked() || !hookmsg)
forAllMCChat(action);
- final Consumer customLMDConsumer = cc -> action.accept(cc.channel);
+ final Consumer customLMDConsumer = cc -> action.accept(Mono.just(cc.channel));
if (toggle == null)
MCChatCustom.lastmsgCustom.forEach(customLMDConsumer);
else
@@ -141,7 +152,7 @@ public class MCChatUtils {
* @param sender The sender to check perms of or null to send to all that has it toggled
* @param toggle The toggle to check or null to send to all allowed
*/
- public static void forAllowedCustomMCChat(Consumer action, @Nullable CommandSender sender, @Nullable ChannelconBroadcast toggle) {
+ public static void forAllowedCustomMCChat(Consumer> action, @Nullable CommandSender sender, @Nullable ChannelconBroadcast toggle) {
if (notEnabled()) return;
MCChatCustom.lastmsgCustom.stream().filter(clmd -> {
//new TBMCChannelConnectFakeEvent(sender, clmd.mcchannel).shouldSendTo(clmd.dcp) - Thought it was this simple hehe - Wait, it *should* be this simple
@@ -150,7 +161,7 @@ public class MCChatUtils {
if (sender == null)
return true;
return clmd.groupID.equals(clmd.mcchannel.getGroupID(sender));
- }).forEach(cc -> action.accept(cc.channel)); //TODO: Send error messages on channel connect
+ }).forEach(cc -> action.accept(Mono.just(cc.channel))); //TODO: Send error messages on channel connect
}
/**
@@ -161,42 +172,42 @@ public class MCChatUtils {
* @param toggle The toggle to check or null to send to all allowed
* @param hookmsg Whether the message is also sent from the hook
*/
- public static void forAllowedCustomAndAllMCChat(Consumer action, @Nullable CommandSender sender, @Nullable ChannelconBroadcast toggle, boolean hookmsg) {
+ public static void forAllowedCustomAndAllMCChat(Consumer> action, @Nullable CommandSender sender, @Nullable ChannelconBroadcast toggle, boolean hookmsg) {
if (notEnabled()) return;
if (!GeneralEventBroadcasterModule.isHooked() || !hookmsg)
forAllMCChat(action);
forAllowedCustomMCChat(action, sender, toggle);
}
- public static Consumer send(String message) {
- return ch -> DiscordPlugin.sendMessageToChannel(ch, DPUtils.sanitizeString(message));
+ public static Consumer> send(String message) {
+ return ch -> ch.flatMap(mc -> mc.createMessage(DPUtils.sanitizeString(message))).subscribe();
}
- public static void forAllowedMCChat(Consumer action, TBMCSystemChatEvent event) {
+ public static void forAllowedMCChat(Consumer> action, TBMCSystemChatEvent event) {
if (notEnabled()) return;
if (event.getChannel().isGlobal())
- action.accept(module.chatChannel().get());
+ action.accept(module.chatChannelMono());
for (LastMsgData data : MCChatPrivate.lastmsgPerUser)
- if (event.shouldSendTo(getSender(data.channel, data.user)))
- action.accept(data.channel);
+ if (event.shouldSendTo(getSender(data.channel.getId(), data.user)))
+ action.accept(Mono.just(data.channel)); //TODO: Only store ID?
MCChatCustom.lastmsgCustom.stream().filter(clmd -> {
if (!clmd.brtoggles.contains(event.getTarget()))
return false;
return event.shouldSendTo(clmd.dcp);
- }).map(clmd -> clmd.channel).forEach(action);
+ }).map(clmd -> Mono.just(clmd.channel)).forEach(action);
}
/**
* This method will find the best sender to use: if the player is online, use that, if not but connected then use that etc.
*/
- static DiscordSenderBase getSender(IChannel channel, final IUser author) {
+ static DiscordSenderBase getSender(Snowflake channel, final User author) {
//noinspection OptionalGetWithoutIsPresent
return Stream.>>of( // https://stackoverflow.com/a/28833677/2703239
- () -> Optional.ofNullable(getSender(OnlineSenders, channel, author)), // Find first non-null
- () -> Optional.ofNullable(getSender(ConnectedSenders, channel, author)), // This doesn't support the public chat, but it'll always return null for it
- () -> Optional.ofNullable(getSender(UnconnectedSenders, channel, author)), //
- () -> Optional.of(addSender(UnconnectedSenders, author,
- new DiscordSender(author, channel)))).map(Supplier::get).filter(Optional::isPresent).map(Optional::get).findFirst().get();
+ () -> Optional.ofNullable(getSender(OnlineSenders, channel, author)), // Find first non-null
+ () -> Optional.ofNullable(getSender(ConnectedSenders, channel, author)), // This doesn't support the public chat, but it'll always return null for it
+ () -> Optional.ofNullable(getSender(UnconnectedSenders, channel, author)), //
+ () -> Optional.of(addSender(UnconnectedSenders, author,
+ new DiscordSender(author, (MessageChannel) DiscordPlugin.dc.getChannelById(channel).block())))).map(Supplier::get).filter(Optional::isPresent).map(Optional::get).findFirst().get();
}
/**
@@ -205,15 +216,15 @@ public class MCChatUtils {
*
* @param channel The channel to reset in - the process is slightly different for the public, private and custom chats
*/
- public static void resetLastMessage(IChannel channel) {
+ public static void resetLastMessage(Channel channel) {
if (notEnabled()) return;
- if (channel.getLongID() == module.chatChannel().get().getLongID()) {
- (lastmsgdata == null ? lastmsgdata = new LastMsgData(module.chatChannel().get(), null)
- : lastmsgdata).message = null;
+ if (channel.getId().asLong() == module.chatChannel().get().asLong()) {
+ (lastmsgdata == null ? lastmsgdata = new LastMsgData(module.chatChannelMono().block(), null)
+ : lastmsgdata).message = null;
return;
} // Don't set the whole object to null, the player and channel information should be preserved
- for (LastMsgData data : channel.isPrivate() ? MCChatPrivate.lastmsgPerUser : MCChatCustom.lastmsgCustom) {
- if (data.channel.getLongID() == channel.getLongID()) {
+ for (LastMsgData data : channel instanceof PrivateChannel ? MCChatPrivate.lastmsgPerUser : MCChatCustom.lastmsgCustom) {
+ if (data.channel.getId().asLong() == channel.getId().asLong()) {
data.message = null;
return;
}
@@ -221,9 +232,23 @@ public class MCChatUtils {
//If it gets here, it's sending a message to a non-chat channel
}
+ public static void addStaticExcludedPlugin(Class extends Event> event, String plugin) {
+ staticExcludedPlugins.compute(event, (e, hs) -> hs == null
+ ? Sets.newHashSet(plugin)
+ : (hs.add(plugin) ? hs : hs));
+ }
+
public static void callEventExcludingSome(Event event) {
if (notEnabled()) return;
- callEventExcluding(event, false, module.excludedPlugins().get());
+ val second = staticExcludedPlugins.get(event.getClass());
+ String[] first = module.excludedPlugins().get();
+ String[] both = second == null ? first
+ : Arrays.copyOf(first, first.length + second.size());
+ int i = first.length;
+ if (second != null)
+ for (String plugin : second)
+ both[i++] = plugin;
+ callEventExcluding(event, false, both);
}
/**
@@ -284,13 +309,59 @@ public class MCChatUtils {
}
}
+ /**
+ * Call it from an async thread.
+ */
+ public static void callLoginEvents(DiscordConnectedPlayer dcp) {
+ Consumer> loginFail = kickMsg -> {
+ dcp.sendMessage("Minecraft chat disabled, as the login failed: " + kickMsg.get());
+ MCChatPrivate.privateMCChat(dcp.getChannel(), false, dcp.getUser(), dcp.getChromaUser());
+ }; //Probably also happens if the user is banned or so
+ val event = new AsyncPlayerPreLoginEvent(dcp.getName(), InetAddress.getLoopbackAddress(), dcp.getUniqueId());
+ callEventExcludingSome(event);
+ if (event.getLoginResult() != AsyncPlayerPreLoginEvent.Result.ALLOWED) {
+ loginFail.accept(event::getKickMessage);
+ return;
+ }
+ Bukkit.getScheduler().runTask(DiscordPlugin.plugin, () -> {
+ val ev = new PlayerLoginEvent(dcp, "localhost", InetAddress.getLoopbackAddress());
+ callEventExcludingSome(ev);
+ if (ev.getResult() != PlayerLoginEvent.Result.ALLOWED) {
+ loginFail.accept(ev::getKickMessage);
+ return;
+ }
+ callEventExcludingSome(new PlayerJoinEvent(dcp, ""));
+ dcp.setLoggedIn(true);
+ DPUtils.getLogger().info(dcp.getName() + " (" + dcp.getUniqueId() + ") logged in from Discord");
+ });
+ }
+
+ /**
+ * Only calls the events if the player is actually logged in
+ *
+ * @param dcp The player
+ * @param needsSync Whether we're in an async thread
+ */
+ public static void callLogoutEvent(DiscordConnectedPlayer dcp, boolean needsSync) {
+ if (!dcp.isLoggedIn()) return;
+ val event = new PlayerQuitEvent(dcp, "");
+ if (needsSync) callEventSync(event);
+ else callEventExcludingSome(event);
+ dcp.setLoggedIn(false);
+ DPUtils.getLogger().info(dcp.getName() + " (" + dcp.getUniqueId() + ") logged out from Discord");
+ }
+
+ static void callEventSync(Event event) {
+ Bukkit.getScheduler().runTask(DiscordPlugin.plugin, () -> callEventExcludingSome(event));
+ }
+
@RequiredArgsConstructor
public static class LastMsgData {
- public IMessage message;
+ public Message message;
public long time;
public String content;
- public final IChannel channel;
- public Channel mcchannel;
- public final IUser user;
+ public final MessageChannel channel;
+ public buttondevteam.core.component.channel.Channel mcchannel;
+ public final User user;
}
}
diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java
index 3ed7bcf..7c44627 100644
--- a/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java
+++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCListener.java
@@ -1,11 +1,12 @@
package buttondevteam.discordplugin.mcchat;
import buttondevteam.discordplugin.*;
-import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.player.*;
import com.earth2me.essentials.CommandSource;
+import discord4j.core.object.entity.Role;
+import discord4j.core.object.util.Snowflake;
import lombok.RequiredArgsConstructor;
import lombok.val;
import net.ess3.api.events.AfkStatusChangeEvent;
@@ -17,16 +18,14 @@ import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
-import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
-import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.server.BroadcastMessageEvent;
-import sx.blah.discord.handle.obj.IRole;
-import sx.blah.discord.handle.obj.IUser;
-import sx.blah.discord.util.DiscordException;
-import sx.blah.discord.util.MissingPermissionsException;
+import reactor.core.publisher.Mono;
+
+import java.util.Objects;
+import java.util.Optional;
@RequiredArgsConstructor
class MCListener implements Listener {
@@ -36,9 +35,11 @@ class MCListener implements Listener {
public void onPlayerLogin(PlayerLoginEvent e) {
if (e.getResult() != Result.ALLOWED)
return;
+ if (e.getPlayer() instanceof DiscordConnectedPlayer)
+ return;
MCChatUtils.ConnectedSenders.values().stream().flatMap(v -> v.values().stream()) //Only private mcchat should be in ConnectedSenders
.filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny()
- .ifPresent(dcp -> MCChatUtils.callEventExcludingSome(new PlayerQuitEvent(dcp, "")));
+ .ifPresent(dcp -> MCChatUtils.callLogoutEvent(dcp, false));
}
@EventHandler(priority = EventPriority.LOWEST)
@@ -49,11 +50,11 @@ class MCListener implements Listener {
final Player p = e.getPlayer();
DiscordPlayer dp = e.GetPlayer().getAs(DiscordPlayer.class);
if (dp != null) {
- val user = DiscordPlugin.dc.getUserByID(Long.parseLong(dp.getDiscordID()));
+ val user = DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID())).block();
MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(),
- new DiscordPlayerSender(user, user.getOrCreatePMChannel(), p));
+ new DiscordPlayerSender(user, Objects.requireNonNull(user).getPrivateChannel().block(), p)); //TODO: Don't block
MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(),
- new DiscordPlayerSender(user, module.chatChannel().get(), p)); //Stored per-channel
+ new DiscordPlayerSender(user, module.chatChannelMono().block(), p)); //Stored per-channel
}
final String message = e.GetPlayer().PlayerName().get() + " joined the game";
MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(message), e.getPlayer(), ChannelconBroadcast.JOINLEAVE, true);
@@ -67,10 +68,10 @@ class MCListener implements Listener {
return; // Only care about real users
MCChatUtils.OnlineSenders.entrySet()
.removeIf(entry -> entry.getValue().entrySet().stream().anyMatch(p -> p.getValue().getUniqueId().equals(e.getPlayer().getUniqueId())));
- Bukkit.getScheduler().runTask(DiscordPlugin.plugin,
+ Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin,
() -> MCChatUtils.ConnectedSenders.values().stream().flatMap(v -> v.values().stream())
.filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny()
- .ifPresent(dcp -> MCChatUtils.callEventExcludingSome(new PlayerJoinEvent(dcp, ""))));
+ .ifPresent(MCChatUtils::callLoginEvents));
Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin,
ChromaBot.getInstance()::updatePlayerList, 5);
final String message = e.GetPlayer().PlayerName().get() + " left the game";
@@ -99,38 +100,34 @@ class MCListener implements Listener {
MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(msg), base, ChannelconBroadcast.AFK, false);
}
- private ConfigData muteRole() {
+ private ConfigData> muteRole() {
return DPUtils.roleData(module.getConfig(), "muteRole", "Muted");
}
@EventHandler
public void onPlayerMute(MuteStatusChangeEvent e) {
- try {
- DPUtils.performNoWait(() -> {
- final IRole role = muteRole().get();
- if (role == null) return;
- final CommandSource source = e.getAffected().getSource();
- if (!source.isPlayer())
- return;
- final DiscordPlayer p = TBMCPlayerBase.getPlayer(source.getPlayer().getUniqueId(), TBMCPlayer.class)
- .getAs(DiscordPlayer.class);
- if (p == null) return;
- final IUser user = DiscordPlugin.dc.getUserByID(
- Long.parseLong(p.getDiscordID()));
+ final Mono role = muteRole().get();
+ if (role == null) return;
+ final CommandSource source = e.getAffected().getSource();
+ if (!source.isPlayer())
+ return;
+ final DiscordPlayer p = TBMCPlayerBase.getPlayer(source.getPlayer().getUniqueId(), TBMCPlayer.class)
+ .getAs(DiscordPlayer.class);
+ if (p == null) return;
+ DiscordPlugin.dc.getUserById(Snowflake.of(p.getDiscordID()))
+ .flatMap(user -> user.asMember(DiscordPlugin.mainServer.getId()))
+ .flatMap(user -> role.flatMap(r -> {
if (e.getValue())
- user.addRole(role);
+ user.addRole(r.getId());
else
- user.removeRole(role);
+ user.removeRole(r.getId());
val modlog = module.modlogChannel().get();
- String msg = (e.getValue() ? "M" : "Unm") + "uted user: " + user.getName();
- if (modlog != null)
- DiscordPlugin.sendMessageToChannel(modlog, msg);
+ String msg = (e.getValue() ? "M" : "Unm") + "uted user: " + user.getUsername() + "#" + user.getDiscriminator();
DPUtils.getLogger().info(msg);
- });
- } catch (DiscordException | MissingPermissionsException ex) {
- TBMCCoreAPI.SendException("Failed to give/take Muted role to player " + e.getAffected().getName() + "!",
- ex);
- }
+ if (modlog != null)
+ return modlog.flatMap(ch -> ch.createMessage(msg));
+ return Mono.empty();
+ })).subscribe();
}
@EventHandler
@@ -148,8 +145,9 @@ class MCListener implements Listener {
String name = event.getSender() instanceof Player ? ((Player) event.getSender()).getDisplayName()
: event.getSender().getName();
//Channel channel = ChromaGamerBase.getFromSender(event.getSender()).channel().get(); - TODO
- val yeehaw = DiscordPlugin.mainServer.getEmojiByName("YEEHAW");
- MCChatUtils.forAllMCChat(MCChatUtils.send(name + (yeehaw != null ? " <:YEEHAW:" + yeehaw.getStringID() + ">s" : " YEEHAWs")));
+ DiscordPlugin.mainServer.getEmojis().filter(e -> "YEEHAW".equals(e.getName()))
+ .take(1).singleOrEmpty().map(Optional::of).defaultIfEmpty(Optional.empty()).subscribe(yeehaw ->
+ MCChatUtils.forAllMCChat(MCChatUtils.send(name + (yeehaw.map(guildEmoji -> " <:YEEHAW:" + guildEmoji.getId().asString() + ">s").orElse(" YEEHAWs")))));
}
@EventHandler
diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java
index 509ea66..28e1e72 100644
--- a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java
+++ b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.java
@@ -1,18 +1,23 @@
package buttondevteam.discordplugin.mcchat;
+import buttondevteam.core.MainPlugin;
import buttondevteam.core.component.channel.Channel;
import buttondevteam.discordplugin.DPUtils;
import buttondevteam.discordplugin.DiscordConnectedPlayer;
import buttondevteam.discordplugin.DiscordPlugin;
+import buttondevteam.discordplugin.playerfaker.perm.LPInjector;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
+import buttondevteam.lib.architecture.ReadOnlyConfigData;
import com.google.common.collect.Lists;
+import discord4j.core.object.entity.MessageChannel;
+import discord4j.core.object.util.Snowflake;
import lombok.Getter;
import lombok.val;
import org.bukkit.Bukkit;
-import sx.blah.discord.handle.obj.IChannel;
+import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.Objects;
@@ -23,11 +28,12 @@ import java.util.stream.Collectors;
* Provides Minecraft chat connection to Discord. Commands may be used either in a public chat (limited) or in a DM.
*/
public class MinecraftChatModule extends Component {
- private @Getter MCChatListener listener;
+ private @Getter
+ MCChatListener listener;
- public MCChatListener getListener() { //It doesn't want to generate
- return listener;
- }
+ /*public MCChatListener getListener() { //It doesn't want to generate
+ return listener; - And now ButtonProcessor didn't look beyond this - return instead of continue...
+ }*/
/**
* A list of commands that can be used in public chats - Warning: Some plugins will treat players as OPs, always test before allowing a command!
@@ -40,33 +46,46 @@ public class MinecraftChatModule extends Component {
/**
* The channel to use as the public Minecraft chat - everything public gets broadcasted here
*/
- public ConfigData chatChannel() {
- return DPUtils.channelData(getConfig(), "chatChannel", 239519012529111040L);
+ public ConfigData chatChannel() {
+ return DPUtils.snowflakeData(getConfig(), "chatChannel", 239519012529111040L);
+ }
+
+ public Mono chatChannelMono() {
+ return DPUtils.getMessageChannel(chatChannel().getPath(), chatChannel().get());
}
/**
* The channel where the plugin can log when it mutes a player on Discord because of a Minecraft mute
*/
- public ConfigData modlogChannel() {
+ public ReadOnlyConfigData> modlogChannel() {
return DPUtils.channelData(getConfig(), "modlogChannel", 283840717275791360L);
}
/**
- * 0 * The plugins to exclude from fake player events used for the 'mcchat' command - some plugins may crash, add them here
+ * The plugins to exclude from fake player events used for the 'mcchat' command - some plugins may crash, add them here
*/
public ConfigData excludedPlugins() {
return getConfig().getData("excludedPlugins", new String[]{"ProtocolLib", "LibsDisguises", "JourneyMapServer"});
}
+ /**
+ * If this setting is on then players logged in through the 'mcchat' command will be able to teleport using plugin commands.
+ * They can then use commands like /tpahere to teleport others to that place.
+ * If this is off, then teleporting will have no effect.
+ */
+ public ConfigData allowFakePlayerTeleports() {
+ return getConfig().getData("allowFakePlayerTeleports", false);
+ }
+
@Override
protected void enable() {
- if (DPUtils.disableIfConfigError(this, chatChannel())) return;
+ if (DPUtils.disableIfConfigErrorRes(this, chatChannel(), chatChannelMono()))
+ return;
listener = new MCChatListener(this);
- DiscordPlugin.dc.getDispatcher().registerListener(listener);
TBMCCoreAPI.RegisterEventsForExceptions(listener, getPlugin());
TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(this), getPlugin());//These get undone if restarting/resetting - it will ignore events if disabled
getPlugin().getManager().registerCommand(new MCChatCommand());
- getPlugin().getManager().registerCommand(new ChannelconCommand());
+ getPlugin().getManager().registerCommand(new ChannelconCommand(this));
val chcons = getConfig().getConfig().getConfigurationSection("chcons");
if (chcons == null) //Fallback to old place
@@ -76,20 +95,28 @@ public class MinecraftChatModule extends Component {
for (val chconkey : chconkeys) {
val chcon = chcons.getConfigurationSection(chconkey);
val mcch = Channel.getChannels().filter(ch -> ch.ID.equals(chcon.getString("mcchid"))).findAny();
- val ch = DiscordPlugin.dc.getChannelByID(chcon.getLong("chid"));
+ val ch = DiscordPlugin.dc.getChannelById(Snowflake.of(chcon.getLong("chid"))).block();
val did = chcon.getLong("did");
- val user = DiscordPlugin.dc.fetchUser(did);
+ val user = DiscordPlugin.dc.getUserById(Snowflake.of(did)).block();
val groupid = chcon.getString("groupid");
val toggles = chcon.getInt("toggles");
val brtoggles = chcon.getStringList("brtoggles");
if (!mcch.isPresent() || ch == null || user == null || groupid == null)
continue;
Bukkit.getScheduler().runTask(getPlugin(), () -> { //<-- Needed because of occasional ConcurrentModificationExceptions when creating the player (PermissibleBase)
- val dcp = new DiscordConnectedPlayer(user, ch, UUID.fromString(chcon.getString("mcuid")), chcon.getString("mcname"));
- MCChatCustom.addCustomChat(ch, groupid, mcch.get(), user, dcp, toggles, brtoggles.stream().map(TBMCSystemChatEvent.BroadcastTarget::get).filter(Objects::nonNull).collect(Collectors.toSet()));
+ val dcp = new DiscordConnectedPlayer(user, (MessageChannel) ch, UUID.fromString(chcon.getString("mcuid")), chcon.getString("mcname"), this);
+ MCChatCustom.addCustomChat((MessageChannel) ch, groupid, mcch.get(), user, dcp, toggles, brtoggles.stream().map(TBMCSystemChatEvent.BroadcastTarget::get).filter(Objects::nonNull).collect(Collectors.toSet()));
});
}
}
+
+ try {
+ new LPInjector(MainPlugin.Instance);
+ } catch (Exception e) {
+ TBMCCoreAPI.SendException("Failed to init LuckPerms injector", e);
+ } catch (NoClassDefFoundError e) {
+ getPlugin().getLogger().info("No LuckPerms, not injecting");
+ }
}
@Override
@@ -97,10 +124,10 @@ public class MinecraftChatModule extends Component {
val chcons = MCChatCustom.getCustomChats();
val chconsc = getConfig().getConfig().createSection("chcons");
for (val chcon : chcons) {
- val chconc = chconsc.createSection(chcon.channel.getStringID());
+ val chconc = chconsc.createSection(chcon.channel.getId().asString());
chconc.set("mcchid", chcon.mcchannel.ID);
- chconc.set("chid", chcon.channel.getLongID());
- chconc.set("did", chcon.user.getLongID());
+ chconc.set("chid", chcon.channel.getId().asLong());
+ chconc.set("did", chcon.user.getId().asLong());
chconc.set("mcuid", chcon.dcp.getUniqueId().toString());
chconc.set("mcname", chcon.dcp.getName());
chconc.set("groupid", chcon.groupID);
diff --git a/src/main/java/buttondevteam/discordplugin/mccommands/AcceptMCCommand.java b/src/main/java/buttondevteam/discordplugin/mccommands/AcceptMCCommand.java
deleted file mode 100755
index 29ed176..0000000
--- a/src/main/java/buttondevteam/discordplugin/mccommands/AcceptMCCommand.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package buttondevteam.discordplugin.mccommands;
-
-import buttondevteam.discordplugin.DPUtils;
-import buttondevteam.discordplugin.DiscordPlayer;
-import buttondevteam.discordplugin.commands.ConnectCommand;
-import buttondevteam.discordplugin.mcchat.MCChatUtils;
-import buttondevteam.lib.chat.CommandClass;
-import buttondevteam.lib.player.ChromaGamerBase;
-import buttondevteam.lib.player.TBMCPlayer;
-import buttondevteam.lib.player.TBMCPlayerBase;
-import org.bukkit.entity.Player;
-
-@CommandClass(modOnly = false, path = "accept")
-public class AcceptMCCommand extends DiscordMCCommandBase {
-
- @Override
- public String[] GetHelpText(String alias) {
- return new String[] { //
- "§6---- Accept Discord connection ----", //
- "Accept a pending connection between your Discord and Minecraft account.", //
- "To start the connection process, do §b/connect §r in the " + DPUtils.botmention() + " channel on Discord", //
- "Usage: /" + alias + " accept" //
- };
- }
-
- @Override
- public boolean OnCommand(Player player, String alias, String[] args) {
- String did = ConnectCommand.WaitingToConnect.get(player.getName());
- if (did == null) {
- player.sendMessage("§cYou don't have a pending connection to Discord.");
- return true;
- }
- DiscordPlayer dp = ChromaGamerBase.getUser(did, DiscordPlayer.class);
- TBMCPlayer mcp = TBMCPlayerBase.getPlayer(player.getUniqueId(), TBMCPlayer.class);
- dp.connectWith(mcp);
- dp.save();
- mcp.save();
- ConnectCommand.WaitingToConnect.remove(player.getName());
- MCChatUtils.UnconnectedSenders.remove(did); //Remove all unconnected, will be recreated where needed
- player.sendMessage("§bAccounts connected.");
- return true;
- }
-
-}
diff --git a/src/main/java/buttondevteam/discordplugin/mccommands/DeclineMCCommand.java b/src/main/java/buttondevteam/discordplugin/mccommands/DeclineMCCommand.java
deleted file mode 100755
index 813d6b9..0000000
--- a/src/main/java/buttondevteam/discordplugin/mccommands/DeclineMCCommand.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package buttondevteam.discordplugin.mccommands;
-
-import buttondevteam.discordplugin.DPUtils;
-import buttondevteam.discordplugin.commands.ConnectCommand;
-import buttondevteam.lib.chat.CommandClass;
-import org.bukkit.entity.Player;
-
-@CommandClass(modOnly = false, path = "decline")
-public class DeclineMCCommand extends DiscordMCCommandBase {
-
- @Override
- public String[] GetHelpText(String alias) {
- return new String[] { //
- "§6---- Decline Discord connection ----", //
- "Decline a pending connection between your Discord and Minecraft account.", //
- "To start the connection process, do §b/connect §r in the " + DPUtils.botmention() + " channel on Discord", //
- "Usage: /" + alias + " decline" //
- };
- }
-
- @Override
- public boolean OnCommand(Player player, String alias, String[] args) {
- String did = ConnectCommand.WaitingToConnect.remove(player.getName());
- if (did == null) {
- player.sendMessage("§cYou don't have a pending connection to Discord.");
- return true;
- }
- player.sendMessage("§bPending connection declined.");
- return true;
- }
-
-}
diff --git a/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java b/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java
new file mode 100644
index 0000000..29eb5ce
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java
@@ -0,0 +1,131 @@
+package buttondevteam.discordplugin.mccommands;
+
+import buttondevteam.discordplugin.DPUtils;
+import buttondevteam.discordplugin.DiscordPlayer;
+import buttondevteam.discordplugin.DiscordPlugin;
+import buttondevteam.discordplugin.DiscordSenderBase;
+import buttondevteam.discordplugin.commands.ConnectCommand;
+import buttondevteam.discordplugin.commands.VersionCommand;
+import buttondevteam.discordplugin.mcchat.MCChatUtils;
+import buttondevteam.lib.chat.Command2;
+import buttondevteam.lib.chat.CommandClass;
+import buttondevteam.lib.chat.ICommand2MC;
+import buttondevteam.lib.player.ChromaGamerBase;
+import buttondevteam.lib.player.TBMCPlayer;
+import buttondevteam.lib.player.TBMCPlayerBase;
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import reactor.core.publisher.Mono;
+
+import java.lang.reflect.Method;
+
+@CommandClass(path = "discord", helpText = {
+ "Discord",
+ "This command allows performing Discord-related actions."
+})
+public class DiscordMCCommand extends ICommand2MC {
+ @Command2.Subcommand
+ public boolean accept(Player player) {
+ String did = ConnectCommand.WaitingToConnect.get(player.getName());
+ if (did == null) {
+ player.sendMessage("§cYou don't have a pending connection to Discord.");
+ return true;
+ }
+ DiscordPlayer dp = ChromaGamerBase.getUser(did, DiscordPlayer.class);
+ TBMCPlayer mcp = TBMCPlayerBase.getPlayer(player.getUniqueId(), TBMCPlayer.class);
+ dp.connectWith(mcp);
+ dp.save();
+ mcp.save();
+ ConnectCommand.WaitingToConnect.remove(player.getName());
+ MCChatUtils.UnconnectedSenders.remove(did); //Remove all unconnected, will be recreated where needed
+ player.sendMessage("§bAccounts connected.");
+ return true;
+ }
+
+ @Command2.Subcommand
+ public boolean decline(Player player) {
+ String did = ConnectCommand.WaitingToConnect.remove(player.getName());
+ if (did == null) {
+ player.sendMessage("§cYou don't have a pending connection to Discord.");
+ return true;
+ }
+ player.sendMessage("§bPending connection declined.");
+ return true;
+ }
+
+ @Command2.Subcommand(permGroup = Command2.Subcommand.MOD_GROUP, helpText = {
+ "Reload Discord plugin",
+ "Reloads the config. To apply some changes, you may need to also run /discord reset."
+ })
+ public void reload(CommandSender sender) {
+ if (DiscordPlugin.plugin.tryReloadConfig())
+ sender.sendMessage("§bConfig reloaded.");
+ else
+ sender.sendMessage("§cFailed to reload config.");
+ }
+
+ public static boolean resetting = false;
+
+ @Command2.Subcommand(permGroup = Command2.Subcommand.MOD_GROUP, helpText = {
+ "Reset ChromaBot", //
+ "This command disables and then enables the plugin." //
+ })
+ public void reset(CommandSender sender) {
+ Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, () -> {
+ resetting = true; //Turned off after sending enable message (ReadyEvent)
+ sender.sendMessage("§bDisabling DiscordPlugin...");
+ Bukkit.getPluginManager().disablePlugin(DiscordPlugin.plugin);
+ if (!(sender instanceof DiscordSenderBase)) //Sending to Discord errors
+ sender.sendMessage("§bEnabling DiscordPlugin...");
+ Bukkit.getPluginManager().enablePlugin(DiscordPlugin.plugin);
+ if (!(sender instanceof DiscordSenderBase)) //Sending to Discord errors
+ sender.sendMessage("§bReset finished!");
+ });
+ }
+
+ @Command2.Subcommand(helpText = {
+ "Version command",
+ "Prints the plugin version"
+ })
+ public void version(CommandSender sender) {
+ sender.sendMessage(VersionCommand.getVersion());
+ }
+
+ @Command2.Subcommand(helpText = {
+ "Invite",
+ "Shows an invite link to the server"
+ })
+ public void invite(CommandSender sender) {
+ String invi = DiscordPlugin.plugin.inviteLink().get();
+ if (invi.length() > 0) {
+ sender.sendMessage("§bInvite link: " + invi);
+ return;
+ }
+ DiscordPlugin.mainServer.getInvites().limitRequest(1)
+ .switchIfEmpty(Mono.fromRunnable(() -> sender.sendMessage("§cNo invites found for the server.")))
+ .subscribe(inv -> {
+ sender.sendMessage("§bInvite link: https://discord.gg/" + inv.getCode());
+ }, e -> sender.sendMessage("§cThe invite link is not set and the bot has no permission to get it."));
+ }
+
+ @Override
+ public String[] getHelpText(Method method, Command2.Subcommand ann) {
+ switch (method.getName()) {
+ case "accept":
+ return new String[]{ //
+ "Accept Discord connection", //
+ "Accept a pending connection between your Discord and Minecraft account.", //
+ "To start the connection process, do §b/connect §r in the " + DPUtils.botmention() + " channel on Discord", //
+ };
+ case "decline":
+ return new String[]{ //
+ "Decline Discord connection", //
+ "Decline a pending connection between your Discord and Minecraft account.", //
+ "To start the connection process, do §b/connect §r in the " + DPUtils.botmention() + " channel on Discord", //
+ };
+ default:
+ return super.getHelpText(method, ann);
+ }
+ }
+}
diff --git a/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommandBase.java b/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommandBase.java
deleted file mode 100755
index 5edbafe..0000000
--- a/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommandBase.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package buttondevteam.discordplugin.mccommands;
-
-import buttondevteam.lib.chat.CommandClass;
-import buttondevteam.lib.chat.PlayerCommandBase;
-
-@CommandClass(modOnly = false, path = "discord")
-public abstract class DiscordMCCommandBase extends PlayerCommandBase {
-
-}
diff --git a/src/main/java/buttondevteam/discordplugin/mccommands/ReloadMCCommand.java b/src/main/java/buttondevteam/discordplugin/mccommands/ReloadMCCommand.java
deleted file mode 100644
index 01433c9..0000000
--- a/src/main/java/buttondevteam/discordplugin/mccommands/ReloadMCCommand.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package buttondevteam.discordplugin.mccommands;
-
-import buttondevteam.discordplugin.DiscordPlugin;
-import buttondevteam.lib.chat.CommandClass;
-import buttondevteam.lib.chat.TBMCCommandBase;
-import org.bukkit.command.CommandSender;
-
-@CommandClass(path = "discord reload")
-public class ReloadMCCommand extends TBMCCommandBase {
- @Override
- public boolean OnCommand(CommandSender sender, String alias, String[] args) {
- if (DiscordPlugin.plugin.tryReloadConfig())
- sender.sendMessage("§bConfig reloaded."); //TODO: Convert to new command system
- else
- sender.sendMessage("§cFailed to reload config.");
- return true;
- }
-
- @Override
- public String[] GetHelpText(String alias) {
- return new String[]{
- "Reload",
- "Reloads the config. To apply some changes, you may need to also run /discord reset."
- };
- }
-}
diff --git a/src/main/java/buttondevteam/discordplugin/mccommands/ResetMCCommand.java b/src/main/java/buttondevteam/discordplugin/mccommands/ResetMCCommand.java
deleted file mode 100644
index 1f4855f..0000000
--- a/src/main/java/buttondevteam/discordplugin/mccommands/ResetMCCommand.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package buttondevteam.discordplugin.mccommands;
-
-import buttondevteam.discordplugin.DiscordPlugin;
-import buttondevteam.discordplugin.DiscordSenderBase;
-import buttondevteam.lib.chat.CommandClass;
-import buttondevteam.lib.chat.TBMCCommandBase;
-import org.bukkit.Bukkit;
-import org.bukkit.command.CommandSender;
-
-@CommandClass(path = "discord reset", modOnly = true)
-public class ResetMCCommand extends TBMCCommandBase { //Not player-only, so not using DiscordMCCommandBase
- public static boolean resetting = false;
- @Override
- public boolean OnCommand(CommandSender sender, String s, String[] strings) {
- Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, () -> {
- resetting = true; //Turned off after sending enable message (ReadyEvent)
- sender.sendMessage("§bDisabling DiscordPlugin...");
- Bukkit.getPluginManager().disablePlugin(DiscordPlugin.plugin);
- if (!(sender instanceof DiscordSenderBase)) //Sending to Discord errors
- sender.sendMessage("§bEnabling DiscordPlugin...");
- Bukkit.getPluginManager().enablePlugin(DiscordPlugin.plugin);
- if (!(sender instanceof DiscordSenderBase)) //Sending to Discord errors
- sender.sendMessage("§bReset finished!");
- });
- return true;
- }
-
- @Override
- public String[] GetHelpText(String s) {
- return new String[]{ //
- "§6---- Reset ChromaBot ----", //
- "This command disables and then enables the plugin." //
- };
- }
-}
diff --git a/src/main/java/buttondevteam/discordplugin/mccommands/VersionMCCommand.java b/src/main/java/buttondevteam/discordplugin/mccommands/VersionMCCommand.java
deleted file mode 100644
index a2cacba..0000000
--- a/src/main/java/buttondevteam/discordplugin/mccommands/VersionMCCommand.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package buttondevteam.discordplugin.mccommands;
-
-import buttondevteam.discordplugin.commands.VersionCommand;
-import buttondevteam.lib.chat.CommandClass;
-import buttondevteam.lib.chat.TBMCCommandBase;
-import org.bukkit.command.CommandSender;
-
-@CommandClass(path = "discord version")
-public class VersionMCCommand extends TBMCCommandBase {
- @Override
- public boolean OnCommand(CommandSender commandSender, String s, String[] strings) {
- commandSender.sendMessage(VersionCommand.getVersion());
- return true;
- }
-
- @Override
- public String[] GetHelpText(String s) {
- return VersionCommand.getVersion(); //Heh
- }
-}
diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordEntity.java b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordEntity.java
index 6ce85f8..9a75717 100755
--- a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordEntity.java
+++ b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordEntity.java
@@ -1,6 +1,10 @@
package buttondevteam.discordplugin.playerfaker;
+import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.DiscordSenderBase;
+import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
+import discord4j.core.object.entity.MessageChannel;
+import discord4j.core.object.entity.User;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.*;
@@ -11,8 +15,6 @@ import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.plugin.Plugin;
import org.bukkit.util.Vector;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IUser;
import java.util.*;
@@ -20,10 +22,11 @@ import java.util.*;
@Setter
@SuppressWarnings("deprecated")
public abstract class DiscordEntity extends DiscordSenderBase implements Entity {
- protected DiscordEntity(IUser user, IChannel channel, int entityId, UUID uuid) {
+ protected DiscordEntity(User user, MessageChannel channel, int entityId, UUID uuid, MinecraftChatModule module) {
super(user, channel);
this.entityId = entityId;
uniqueId = uuid;
+ this.module = module;
}
private HashMap metadata = new HashMap();
@@ -34,6 +37,7 @@ public abstract class DiscordEntity extends DiscordSenderBase implements Entity
private EntityDamageEvent lastDamageCause;
private final Set scoreboardTags = new HashSet();
private final UUID uniqueId;
+ private final MinecraftChatModule module;
@Override
public void setMetadata(String metadataKey, MetadataValue newMetadataValue) {
@@ -42,7 +46,7 @@ public abstract class DiscordEntity extends DiscordSenderBase implements Entity
@Override
public List getMetadata(String metadataKey) {
- return Arrays.asList(metadata.get(metadataKey)); // Who needs multiple data anyways
+ return Collections.singletonList(metadata.get(metadataKey)); // Who needs multiple data anyways
}
@Override
@@ -91,31 +95,35 @@ public abstract class DiscordEntity extends DiscordSenderBase implements Entity
@Override
public boolean teleport(Location location) {
- this.location = location;
+ if (module.allowFakePlayerTeleports().get())
+ this.location = location;
return true;
}
@Override
public boolean teleport(Location location, TeleportCause cause) {
- this.location = location;
+ if (module.allowFakePlayerTeleports().get())
+ this.location = location;
return true;
}
@Override
public boolean teleport(Entity destination) {
- this.location = destination.getLocation();
+ if (module.allowFakePlayerTeleports().get())
+ this.location = destination.getLocation();
return true;
}
@Override
public boolean teleport(Entity destination, TeleportCause cause) {
- this.location = destination.getLocation();
+ if (module.allowFakePlayerTeleports().get())
+ this.location = destination.getLocation();
return true;
}
@Override
public List getNearbyEntities(double x, double y, double z) {
- return Arrays.asList();
+ return Collections.emptyList();
}
@Override
@@ -163,7 +171,7 @@ public abstract class DiscordEntity extends DiscordSenderBase implements Entity
@Override
public List getPassengers() {
- return Arrays.asList();
+ return Collections.emptyList();
}
@Override
diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java
index a2b4a13..6879155 100755
--- a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java
+++ b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordFakePlayer.java
@@ -1,7 +1,11 @@
package buttondevteam.discordplugin.playerfaker;
import buttondevteam.discordplugin.DiscordPlugin;
+import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
+import discord4j.core.object.entity.MessageChannel;
+import discord4j.core.object.entity.User;
import lombok.Getter;
+import lombok.Setter;
import lombok.experimental.Delegate;
import org.bukkit.*;
import org.bukkit.advancement.Advancement;
@@ -14,704 +18,716 @@ import org.bukkit.entity.Player;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.map.MapView;
import org.bukkit.permissions.PermissibleBase;
+import org.bukkit.permissions.ServerOperator;
import org.bukkit.plugin.Plugin;
import org.bukkit.scoreboard.Scoreboard;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IUser;
import java.net.InetSocketAddress;
import java.util.*;
@SuppressWarnings("deprecation")
public class DiscordFakePlayer extends DiscordHumanEntity implements Player {
- protected DiscordFakePlayer(IUser user, IChannel channel, int entityId, UUID uuid, String mcname) {
- super(user, channel, entityId, uuid);
- perm = new PermissibleBase(Bukkit.getOfflinePlayer(uuid));
- name = mcname;
- }
-
- @Delegate
- private PermissibleBase perm;
-
- private @Getter String name;
-
- @Override
- public EntityType getType() {
- return EntityType.PLAYER;
- }
-
- @Override
- public String getCustomName() {
- return user.getName();
- }
-
- @Override
- public void setCustomName(String name) {
- }
-
- @Override
- public boolean isConversing() {
-
- return false;
- }
-
- @Override
- public void acceptConversationInput(String input) {
- }
-
- @Override
- public boolean beginConversation(Conversation conversation) {
- return false;
- }
-
- @Override
- public void abandonConversation(Conversation conversation) {
- }
-
- @Override
- public void abandonConversation(Conversation conversation, ConversationAbandonedEvent details) {
- }
-
- @Override
- public boolean isOnline() {
- return true;// Let's pretend
- }
-
- @Override
- public boolean isBanned() {
- return false;
- }
-
- @Override
- public boolean isWhitelisted() {
- return true;
- }
-
- @Override
- public void setWhitelisted(boolean value) {
- }
-
- @Override
- public Player getPlayer() {
- return this;
- }
-
- @Override
- public long getFirstPlayed() {
- return 0;
- }
-
- @Override
- public long getLastPlayed() {
- return 0;
- }
-
- @Override
- public boolean hasPlayedBefore() {
- return false;
- }
-
- @Override
- public Map serialize() {
- return new HashMap<>();
- }
-
- @Override
- public void sendPluginMessage(Plugin source, String channel, byte[] message) {
- }
-
- @Override
- public Set getListeningPluginChannels() {
- return Collections.emptySet();
- }
-
- @Override
- public String getDisplayName() {
- return user.getDisplayName(DiscordPlugin.mainServer);
- }
-
- @Override
- public void setDisplayName(String name) {
- }
-
- @Override
- public String getPlayerListName() {
- return getName();
- }
-
- @Override
- public void setPlayerListName(String name) {
- }
-
- @Override
- public void setCompassTarget(Location loc) {
- }
-
- @Override
- public Location getCompassTarget() {
- return new Location(Bukkit.getWorlds().get(0), 0, 0, 0);
- }
-
- @Override
- public InetSocketAddress getAddress() {
- return null;
- }
-
- @Override
- public void sendRawMessage(String message) {
- sendMessage(message);
- }
-
- @Override
- public void kickPlayer(String message) {
- }
-
- @Override
- public void chat(String msg) {
- Bukkit.getPluginManager()
- .callEvent(new AsyncPlayerChatEvent(true, this, msg, new HashSet<>(Bukkit.getOnlinePlayers())));
- }
-
- @Override
- public boolean performCommand(String command) {
- return Bukkit.getServer().dispatchCommand(this, command);
- }
-
- @Override
- public boolean isSneaking() {
- return false;
- }
-
- @Override
- public void setSneaking(boolean sneak) {
- }
-
- @Override
- public boolean isSprinting() {
- return false;
- }
-
- @Override
- public void setSprinting(boolean sprinting) {
- }
-
- @Override
- public void saveData() {
- }
-
- @Override
- public void loadData() {
- }
-
- @Override
- public void setSleepingIgnored(boolean isSleeping) {
- }
-
- @Override
- public boolean isSleepingIgnored() {
- return false;
- }
-
- @Override
- public void playNote(Location loc, byte instrument, byte note) {
- }
-
- @Override
- public void playNote(Location loc, Instrument instrument, Note note) {
- }
-
- @Override
- public void playSound(Location location, Sound sound, float volume, float pitch) {
- }
+ protected DiscordFakePlayer(User user, MessageChannel channel, int entityId, UUID uuid, String mcname, MinecraftChatModule module) {
+ super(user, channel, entityId, uuid, module);
+ origPerm = perm = new PermissibleBase(basePlayer = Bukkit.getOfflinePlayer(uuid));
+ name = mcname;
+ }
+
+ @Delegate(excludes = ServerOperator.class)
+ private PermissibleBase origPerm;
+
+ private @Getter String name;
+
+ private @Getter OfflinePlayer basePlayer;
+
+ @Getter
+ @Setter
+ private PermissibleBase perm;
+
+ public void setOp(boolean value) { //CraftPlayer-compatible implementation
+ this.origPerm.setOp(value);
+ this.perm.recalculatePermissions();
+ }
+
+ public boolean isOp() { return this.origPerm.isOp(); }
+
+ @Override
+ public EntityType getType() {
+ return EntityType.PLAYER;
+ }
+
+ @Override
+ public String getCustomName() {
+ return user.getUsername();
+ }
+
+ @Override
+ public void setCustomName(String name) {
+ }
+
+ @Override
+ public boolean isConversing() {
+
+ return false;
+ }
+
+ @Override
+ public void acceptConversationInput(String input) {
+ }
+
+ @Override
+ public boolean beginConversation(Conversation conversation) {
+ return false;
+ }
+
+ @Override
+ public void abandonConversation(Conversation conversation) {
+ }
+
+ @Override
+ public void abandonConversation(Conversation conversation, ConversationAbandonedEvent details) {
+ }
+
+ @Override
+ public boolean isOnline() {
+ return true;// Let's pretend
+ }
+
+ @Override
+ public boolean isBanned() {
+ return false;
+ }
+
+ @Override
+ public boolean isWhitelisted() {
+ return true;
+ }
+
+ @Override
+ public void setWhitelisted(boolean value) {
+ }
+
+ @Override
+ public Player getPlayer() {
+ return this;
+ }
+
+ @Override
+ public long getFirstPlayed() {
+ return 0;
+ }
+
+ @Override
+ public long getLastPlayed() {
+ return 0;
+ }
+
+ @Override
+ public boolean hasPlayedBefore() {
+ return false;
+ }
+
+ @Override
+ public Map serialize() {
+ return new HashMap<>();
+ }
+
+ @Override
+ public void sendPluginMessage(Plugin source, String channel, byte[] message) {
+ }
+
+ @Override
+ public Set getListeningPluginChannels() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public String getDisplayName() {
+ return Objects.requireNonNull(user.asMember(DiscordPlugin.mainServer.getId()).block()).getDisplayName();
+ }
+
+ @Override
+ public void setDisplayName(String name) {
+ }
+
+ @Override
+ public String getPlayerListName() {
+ return getName();
+ }
+
+ @Override
+ public void setPlayerListName(String name) {
+ }
+
+ @Override
+ public void setCompassTarget(Location loc) {
+ }
+
+ @Override
+ public Location getCompassTarget() {
+ return new Location(Bukkit.getWorlds().get(0), 0, 0, 0);
+ }
+
+ @Override
+ public InetSocketAddress getAddress() {
+ return null;
+ }
+
+ @Override
+ public void sendRawMessage(String message) {
+ sendMessage(message);
+ }
+
+ @Override
+ public void kickPlayer(String message) {
+ }
+
+ @Override
+ public void chat(String msg) {
+ Bukkit.getPluginManager()
+ .callEvent(new AsyncPlayerChatEvent(true, this, msg, new HashSet<>(Bukkit.getOnlinePlayers())));
+ }
+
+ @Override
+ public boolean performCommand(String command) {
+ return Bukkit.getServer().dispatchCommand(this, command);
+ }
+
+ @Override
+ public boolean isSneaking() {
+ return false;
+ }
+
+ @Override
+ public void setSneaking(boolean sneak) {
+ }
+
+ @Override
+ public boolean isSprinting() {
+ return false;
+ }
+
+ @Override
+ public void setSprinting(boolean sprinting) {
+ }
+
+ @Override
+ public void saveData() {
+ }
+
+ @Override
+ public void loadData() {
+ }
+
+ @Override
+ public void setSleepingIgnored(boolean isSleeping) {
+ }
+
+ @Override
+ public boolean isSleepingIgnored() {
+ return false;
+ }
+
+ @Override
+ public void playNote(Location loc, byte instrument, byte note) {
+ }
+
+ @Override
+ public void playNote(Location loc, Instrument instrument, Note note) {
+ }
+
+ @Override
+ public void playSound(Location location, Sound sound, float volume, float pitch) {
+ }
- @Override
- public void playSound(Location location, String sound, float volume, float pitch) {
- }
+ @Override
+ public void playSound(Location location, String sound, float volume, float pitch) {
+ }
- @Override
- public void playSound(Location location, Sound sound, SoundCategory category, float volume, float pitch) {
- }
+ @Override
+ public void playSound(Location location, Sound sound, SoundCategory category, float volume, float pitch) {
+ }
- @Override
- public void playSound(Location location, String sound, SoundCategory category, float volume, float pitch) {
- }
+ @Override
+ public void playSound(Location location, String sound, SoundCategory category, float volume, float pitch) {
+ }
- @Override
- public void stopSound(Sound sound) {
- }
+ @Override
+ public void stopSound(Sound sound) {
+ }
- @Override
- public void stopSound(String sound) {
- }
+ @Override
+ public void stopSound(String sound) {
+ }
- @Override
- public void stopSound(Sound sound, SoundCategory category) {
- }
+ @Override
+ public void stopSound(Sound sound, SoundCategory category) {
+ }
- @Override
- public void stopSound(String sound, SoundCategory category) {
- }
+ @Override
+ public void stopSound(String sound, SoundCategory category) {
+ }
- @Override
- public void playEffect(Location loc, Effect effect, int data) {
- }
+ @Override
+ public void playEffect(Location loc, Effect effect, int data) {
+ }
- @Override
- public void playEffect(Location loc, Effect effect, T data) {
- }
+ @Override
+ public void playEffect(Location loc, Effect effect, T data) {
+ }
- @Override
- public void sendBlockChange(Location loc, Material material, byte data) {
- }
+ @Override
+ public void sendBlockChange(Location loc, Material material, byte data) {
+ }
- @Override
- public boolean sendChunkChange(Location loc, int sx, int sy, int sz, byte[] data) {
- return false;
- }
+ @Override
+ public boolean sendChunkChange(Location loc, int sx, int sy, int sz, byte[] data) {
+ return false;
+ }
- @Override
- public void sendBlockChange(Location loc, int material, byte data) {
- }
+ @Override
+ public void sendBlockChange(Location loc, int material, byte data) {
+ }
- @Override
- public void sendSignChange(Location loc, String[] lines) throws IllegalArgumentException {
- }
+ @Override
+ public void sendSignChange(Location loc, String[] lines) throws IllegalArgumentException {
+ }
- @Override
- public void sendMap(MapView map) {
- }
+ @Override
+ public void sendMap(MapView map) {
+ }
- @Override
- public void updateInventory() {
- }
+ @Override
+ public void updateInventory() {
+ }
- @Override
- public void awardAchievement(@SuppressWarnings("deprecation") Achievement achievement) {
- }
+ @Override
+ public void awardAchievement(@SuppressWarnings("deprecation") Achievement achievement) {
+ }
- @Override
- public void removeAchievement(@SuppressWarnings("deprecation") Achievement achievement) {
- }
+ @Override
+ public void removeAchievement(@SuppressWarnings("deprecation") Achievement achievement) {
+ }
- @Override
- public boolean hasAchievement(@SuppressWarnings("deprecation") Achievement achievement) {
- return false;
- }
+ @Override
+ public boolean hasAchievement(@SuppressWarnings("deprecation") Achievement achievement) {
+ return false;
+ }
- @Override
- public void incrementStatistic(Statistic statistic) throws IllegalArgumentException {
- }
+ @Override
+ public void incrementStatistic(Statistic statistic) throws IllegalArgumentException {
+ }
- @Override
- public void decrementStatistic(Statistic statistic) throws IllegalArgumentException {
- }
+ @Override
+ public void decrementStatistic(Statistic statistic) throws IllegalArgumentException {
+ }
- @Override
- public void incrementStatistic(Statistic statistic, int amount) throws IllegalArgumentException {
+ @Override
+ public void incrementStatistic(Statistic statistic, int amount) throws IllegalArgumentException {
- }
+ }
- @Override
- public void decrementStatistic(Statistic statistic, int amount) throws IllegalArgumentException {
+ @Override
+ public void decrementStatistic(Statistic statistic, int amount) throws IllegalArgumentException {
- }
+ }
- @Override
- public void setStatistic(Statistic statistic, int newValue) throws IllegalArgumentException {
+ @Override
+ public void setStatistic(Statistic statistic, int newValue) throws IllegalArgumentException {
- }
+ }
- @Override
- public int getStatistic(Statistic statistic) throws IllegalArgumentException {
+ @Override
+ public int getStatistic(Statistic statistic) throws IllegalArgumentException {
- return 0;
- }
+ return 0;
+ }
- @Override
- public void incrementStatistic(Statistic statistic, Material material) throws IllegalArgumentException {
+ @Override
+ public void incrementStatistic(Statistic statistic, Material material) throws IllegalArgumentException {
- }
+ }
- @Override
- public void decrementStatistic(Statistic statistic, Material material) throws IllegalArgumentException {
+ @Override
+ public void decrementStatistic(Statistic statistic, Material material) throws IllegalArgumentException {
- }
+ }
- @Override
- public int getStatistic(Statistic statistic, Material material) throws IllegalArgumentException {
+ @Override
+ public int getStatistic(Statistic statistic, Material material) throws IllegalArgumentException {
- return 0;
- }
+ return 0;
+ }
- @Override
- public void incrementStatistic(Statistic statistic, Material material, int amount) throws IllegalArgumentException {
+ @Override
+ public void incrementStatistic(Statistic statistic, Material material, int amount) throws IllegalArgumentException {
- }
+ }
- @Override
- public void decrementStatistic(Statistic statistic, Material material, int amount) throws IllegalArgumentException {
+ @Override
+ public void decrementStatistic(Statistic statistic, Material material, int amount) throws IllegalArgumentException {
- }
+ }
- @Override
- public void setStatistic(Statistic statistic, Material material, int newValue) throws IllegalArgumentException {
+ @Override
+ public void setStatistic(Statistic statistic, Material material, int newValue) throws IllegalArgumentException {
- }
+ }
- @Override
- public void incrementStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException {
+ @Override
+ public void incrementStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException {
- }
+ }
- @Override
- public void decrementStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException {
+ @Override
+ public void decrementStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException {
- }
+ }
- @Override
- public int getStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException {
+ @Override
+ public int getStatistic(Statistic statistic, EntityType entityType) throws IllegalArgumentException {
- return 0;
- }
+ return 0;
+ }
- @Override
- public void incrementStatistic(Statistic statistic, EntityType entityType, int amount)
- throws IllegalArgumentException {
+ @Override
+ public void incrementStatistic(Statistic statistic, EntityType entityType, int amount)
+ throws IllegalArgumentException {
- }
+ }
- @Override
- public void decrementStatistic(Statistic statistic, EntityType entityType, int amount) {
+ @Override
+ public void decrementStatistic(Statistic statistic, EntityType entityType, int amount) {
- }
+ }
- @Override
- public void setStatistic(Statistic statistic, EntityType entityType, int newValue) {
+ @Override
+ public void setStatistic(Statistic statistic, EntityType entityType, int newValue) {
- }
+ }
- @Override
- public void setPlayerTime(long time, boolean relative) {
+ @Override
+ public void setPlayerTime(long time, boolean relative) {
- }
+ }
- @Override
- public long getPlayerTime() {
+ @Override
+ public long getPlayerTime() {
- return 0;
- }
+ return 0;
+ }
- @Override
- public long getPlayerTimeOffset() {
+ @Override
+ public long getPlayerTimeOffset() {
- return 0;
- }
+ return 0;
+ }
- @Override
- public boolean isPlayerTimeRelative() {
+ @Override
+ public boolean isPlayerTimeRelative() {
- return false;
- }
+ return false;
+ }
- @Override
- public void resetPlayerTime() {
+ @Override
+ public void resetPlayerTime() {
- }
+ }
- @Override
- public void setPlayerWeather(WeatherType type) {
+ @Override
+ public void setPlayerWeather(WeatherType type) {
- }
+ }
- @Override
- public WeatherType getPlayerWeather() {
+ @Override
+ public WeatherType getPlayerWeather() {
- return null;
- }
+ return null;
+ }
- @Override
- public void resetPlayerWeather() {
+ @Override
+ public void resetPlayerWeather() {
- }
+ }
- @Override
- public void giveExp(int amount) {
+ @Override
+ public void giveExp(int amount) {
- }
+ }
- @Override
- public void giveExpLevels(int amount) {
+ @Override
+ public void giveExpLevels(int amount) {
- }
+ }
- @Override
- public float getExp() {
+ @Override
+ public float getExp() {
- return 0;
- }
+ return 0;
+ }
- @Override
- public void setExp(float exp) {
+ @Override
+ public void setExp(float exp) {
- }
+ }
- @Override
- public int getLevel() {
+ @Override
+ public int getLevel() {
- return 0;
- }
+ return 0;
+ }
- @Override
- public void setLevel(int level) {
+ @Override
+ public void setLevel(int level) {
- }
+ }
- @Override
- public int getTotalExperience() {
+ @Override
+ public int getTotalExperience() {
- return 0;
- }
+ return 0;
+ }
- @Override
- public void setTotalExperience(int exp) {
+ @Override
+ public void setTotalExperience(int exp) {
- }
+ }
- @Override
- public float getExhaustion() {
+ @Override
+ public float getExhaustion() {
- return 0;
- }
+ return 0;
+ }
- @Override
- public void setExhaustion(float value) {
+ @Override
+ public void setExhaustion(float value) {
- }
+ }
- @Override
- public float getSaturation() {
+ @Override
+ public float getSaturation() {
- return 0;
- }
+ return 0;
+ }
- @Override
- public void setSaturation(float value) {
+ @Override
+ public void setSaturation(float value) {
- }
+ }
- @Override
- public int getFoodLevel() {
+ @Override
+ public int getFoodLevel() {
- return 0;
- }
+ return 0;
+ }
- @Override
- public void setFoodLevel(int value) {
+ @Override
+ public void setFoodLevel(int value) {
- }
+ }
- @Override
- public Location getBedSpawnLocation() {
- return null;
- }
+ @Override
+ public Location getBedSpawnLocation() {
+ return null;
+ }
- @Override
- public void setBedSpawnLocation(Location location) {
- }
+ @Override
+ public void setBedSpawnLocation(Location location) {
+ }
- @Override
- public void setBedSpawnLocation(Location location, boolean force) {
- }
+ @Override
+ public void setBedSpawnLocation(Location location, boolean force) {
+ }
- @Override
- public boolean getAllowFlight() {
- return false;
- }
+ @Override
+ public boolean getAllowFlight() {
+ return false;
+ }
- @Override
- public void setAllowFlight(boolean flight) {
- }
+ @Override
+ public void setAllowFlight(boolean flight) {
+ }
- @Override
- public void hidePlayer(Player player) {
- }
+ @Override
+ public void hidePlayer(Player player) {
+ }
- @Override
- public void showPlayer(Player player) {
- }
+ @Override
+ public void showPlayer(Player player) {
+ }
- @Override
- public boolean canSee(Player player) { // Nobody can see them
- return false;
- }
+ @Override
+ public boolean canSee(Player player) { // Nobody can see them
+ return false;
+ }
- @Override
- public boolean isFlying() {
- return false;
- }
+ @Override
+ public boolean isFlying() {
+ return false;
+ }
- @Override
- public void setFlying(boolean value) {
- }
+ @Override
+ public void setFlying(boolean value) {
+ }
- @Override
- public void setFlySpeed(float value) throws IllegalArgumentException {
- }
+ @Override
+ public void setFlySpeed(float value) throws IllegalArgumentException {
+ }
- @Override
- public void setWalkSpeed(float value) throws IllegalArgumentException {
- }
+ @Override
+ public void setWalkSpeed(float value) throws IllegalArgumentException {
+ }
- @Override
- public float getFlySpeed() {
- return 0;
- }
+ @Override
+ public float getFlySpeed() {
+ return 0;
+ }
- @Override
- public float getWalkSpeed() {
- return 0;
- }
+ @Override
+ public float getWalkSpeed() {
+ return 0;
+ }
- @Override
- public void setTexturePack(String url) {
- }
+ @Override
+ public void setTexturePack(String url) {
+ }
- @Override
- public void setResourcePack(String url) {
- }
+ @Override
+ public void setResourcePack(String url) {
+ }
- @Override
- public void setResourcePack(String url, byte[] hash) {
- }
+ @Override
+ public void setResourcePack(String url, byte[] hash) {
+ }
- @Override
- public Scoreboard getScoreboard() {
- return null;
- }
+ @Override
+ public Scoreboard getScoreboard() {
+ return null;
+ }
- @Override
- public void setScoreboard(Scoreboard scoreboard) throws IllegalArgumentException, IllegalStateException {
- }
+ @Override
+ public void setScoreboard(Scoreboard scoreboard) throws IllegalArgumentException, IllegalStateException {
+ }
- @Override
- public boolean isHealthScaled() {
- return false;
- }
+ @Override
+ public boolean isHealthScaled() {
+ return false;
+ }
- @Override
- public void setHealthScaled(boolean scale) {
- }
+ @Override
+ public void setHealthScaled(boolean scale) {
+ }
- @Override
- public void setHealthScale(double scale) throws IllegalArgumentException {
- }
+ @Override
+ public void setHealthScale(double scale) throws IllegalArgumentException {
+ }
- @Override
- public double getHealthScale() {
- return 1;
- }
+ @Override
+ public double getHealthScale() {
+ return 1;
+ }
- @Override
- public Entity getSpectatorTarget() {
- return null;
- }
+ @Override
+ public Entity getSpectatorTarget() {
+ return null;
+ }
- @Override
- public void setSpectatorTarget(Entity entity) {
- }
+ @Override
+ public void setSpectatorTarget(Entity entity) {
+ }
- @Override
- public void sendTitle(String title, String subtitle) {
- }
+ @Override
+ public void sendTitle(String title, String subtitle) {
+ }
- @Override
- public void sendTitle(String title, String subtitle, int fadeIn, int stay, int fadeOut) {
- }
+ @Override
+ public void sendTitle(String title, String subtitle, int fadeIn, int stay, int fadeOut) {
+ }
- @Override
- public void resetTitle() {
- }
+ @Override
+ public void resetTitle() {
+ }
- @Override
- public void spawnParticle(Particle particle, Location location, int count) {
- }
+ @Override
+ public void spawnParticle(Particle particle, Location location, int count) {
+ }
- @Override
- public void spawnParticle(Particle particle, double x, double y, double z, int count) {
+ @Override
+ public void spawnParticle(Particle particle, double x, double y, double z, int count) {
- }
+ }
- @Override
- public void spawnParticle(Particle particle, Location location, int count, T data) {
+ @Override
+ public void spawnParticle(Particle particle, Location location, int count, T data) {
- }
+ }
- @Override
- public void spawnParticle(Particle particle, double x, double y, double z, int count, T data) {
+ @Override
+ public void spawnParticle(Particle particle, double x, double y, double z, int count, T data) {
- }
+ }
- @Override
- public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY,
- double offsetZ) {
+ @Override
+ public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY,
+ double offsetZ) {
- }
+ }
- @Override
- public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX,
- double offsetY, double offsetZ) {
+ @Override
+ public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX,
+ double offsetY, double offsetZ) {
- }
+ }
- @Override
- public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY,
- double offsetZ, T data) {
+ @Override
+ public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY,
+ double offsetZ, T data) {
- }
+ }
- @Override
- public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX,
- double offsetY, double offsetZ, T data) {
+ @Override
+ public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX,
+ double offsetY, double offsetZ, T data) {
- }
+ }
- @Override
- public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY,
- double offsetZ, double extra) {
+ @Override
+ public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY,
+ double offsetZ, double extra) {
- }
+ }
- @Override
- public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX,
- double offsetY, double offsetZ, double extra) {
+ @Override
+ public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX,
+ double offsetY, double offsetZ, double extra) {
- }
+ }
- @Override
- public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY,
- double offsetZ, double extra, T data) {
+ @Override
+ public void spawnParticle(Particle particle, Location location, int count, double offsetX, double offsetY,
+ double offsetZ, double extra, T data) {
- }
+ }
- @Override
- public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX,
- double offsetY, double offsetZ, double extra, T data) {
+ @Override
+ public void spawnParticle(Particle particle, double x, double y, double z, int count, double offsetX,
+ double offsetY, double offsetZ, double extra, T data) {
- }
+ }
- @Override
- public AdvancementProgress getAdvancementProgress(Advancement advancement) { // TODO: Test
- return null;
- }
+ @Override
+ public AdvancementProgress getAdvancementProgress(Advancement advancement) { // TODO: Test
+ return null;
+ }
- @Override
- public String getLocale() {
+ @Override
+ public String getLocale() {
- return null;
- }
+ return null;
+ }
- @Override
- public Player.Spigot spigot() {
- return new Player.Spigot();
- }
+ @Override
+ public Player.Spigot spigot() {
+ return new Player.Spigot();
+ }
}
diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordHumanEntity.java b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordHumanEntity.java
index c1522f1..4b8b32d 100755
--- a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordHumanEntity.java
+++ b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordHumanEntity.java
@@ -1,5 +1,8 @@
package buttondevteam.discordplugin.playerfaker;
+import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
+import discord4j.core.object.entity.MessageChannel;
+import discord4j.core.object.entity.User;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
@@ -8,14 +11,12 @@ import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Villager;
import org.bukkit.inventory.*;
import org.bukkit.inventory.InventoryView.Property;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IUser;
import java.util.UUID;
public abstract class DiscordHumanEntity extends DiscordLivingEntity implements HumanEntity {
- protected DiscordHumanEntity(IUser user, IChannel channel, int entityId, UUID uuid) {
- super(user, channel, entityId, uuid);
+ protected DiscordHumanEntity(User user, MessageChannel channel, int entityId, UUID uuid, MinecraftChatModule module) {
+ super(user, channel, entityId, uuid, module);
}
private PlayerInventory inv = new DiscordPlayerInventory(this);
diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordLivingEntity.java b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordLivingEntity.java
index f261de4..c561fbf 100755
--- a/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordLivingEntity.java
+++ b/src/main/java/buttondevteam/discordplugin/playerfaker/DiscordLivingEntity.java
@@ -1,5 +1,8 @@
package buttondevteam.discordplugin.playerfaker;
+import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
+import discord4j.core.object.entity.MessageChannel;
+import discord4j.core.object.entity.User;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Location;
@@ -16,15 +19,13 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IUser;
import java.util.*;
public abstract class DiscordLivingEntity extends DiscordEntity implements LivingEntity {
- protected DiscordLivingEntity(IUser user, IChannel channel, int entityId, UUID uuid) {
- super(user, channel, entityId, uuid);
+ protected DiscordLivingEntity(User user, MessageChannel channel, int entityId, UUID uuid, MinecraftChatModule module) {
+ super(user, channel, entityId, uuid, module);
}
private @Getter EntityEquipment equipment = new DiscordEntityEquipment(this);
diff --git a/src/main/java/buttondevteam/discordplugin/playerfaker/perm/LPInjector.java b/src/main/java/buttondevteam/discordplugin/playerfaker/perm/LPInjector.java
new file mode 100644
index 0000000..de5a84b
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/playerfaker/perm/LPInjector.java
@@ -0,0 +1,240 @@
+package buttondevteam.discordplugin.playerfaker.perm;
+
+import buttondevteam.core.MainPlugin;
+import buttondevteam.discordplugin.mcchat.MCChatUtils;
+import buttondevteam.discordplugin.playerfaker.DiscordFakePlayer;
+import buttondevteam.lib.TBMCCoreAPI;
+import me.lucko.luckperms.bukkit.LPBukkitBootstrap;
+import me.lucko.luckperms.bukkit.LPBukkitPlugin;
+import me.lucko.luckperms.bukkit.inject.dummy.DummyPermissibleBase;
+import me.lucko.luckperms.bukkit.inject.permissible.LPPermissible;
+import me.lucko.luckperms.bukkit.listeners.BukkitConnectionListener;
+import me.lucko.luckperms.common.config.ConfigKeys;
+import me.lucko.luckperms.common.locale.message.Message;
+import me.lucko.luckperms.common.model.User;
+import org.bukkit.Bukkit;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerLoginEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.permissions.PermissibleBase;
+import org.bukkit.permissions.PermissionAttachment;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public final class LPInjector implements Listener { //Disable login event for LuckPerms
+ private LPBukkitPlugin plugin;
+ private BukkitConnectionListener connectionListener;
+ private Set deniedLogin;
+ private Field detectedCraftBukkitOfflineMode;
+ private Method printCraftBukkitOfflineModeError;
+ private Field PERMISSIBLE_BASE_ATTACHMENTS_FIELD;
+ private Method convertAndAddAttachments;
+ private Method getActive;
+ private Method setOldPermissible;
+ private Method getOldPermissible;
+
+ public LPInjector(MainPlugin mp) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException {
+ LPBukkitBootstrap bs = (LPBukkitBootstrap) Bukkit.getPluginManager().getPlugin("LuckPerms");
+ Field field = LPBukkitBootstrap.class.getDeclaredField("plugin");
+ field.setAccessible(true);
+ plugin = (LPBukkitPlugin) field.get(bs);
+ MCChatUtils.addStaticExcludedPlugin(PlayerLoginEvent.class, "LuckPerms");
+ MCChatUtils.addStaticExcludedPlugin(PlayerQuitEvent.class, "LuckPerms");
+
+ field = LPBukkitPlugin.class.getDeclaredField("connectionListener");
+ field.setAccessible(true);
+ connectionListener = (BukkitConnectionListener) field.get(plugin);
+ field = connectionListener.getClass().getDeclaredField("deniedLogin");
+ field.setAccessible(true);
+ //noinspection unchecked
+ deniedLogin = (Set) field.get(connectionListener);
+ field = connectionListener.getClass().getDeclaredField("detectedCraftBukkitOfflineMode");
+ field.setAccessible(true);
+ detectedCraftBukkitOfflineMode = field;
+ printCraftBukkitOfflineModeError = connectionListener.getClass().getDeclaredMethod("printCraftBukkitOfflineModeError");
+ printCraftBukkitOfflineModeError.setAccessible(true);
+
+ //PERMISSIBLE_FIELD = DiscordFakePlayer.class.getDeclaredField("perm");
+ //PERMISSIBLE_FIELD.setAccessible(true); //Hacking my own plugin, while we're at it
+ PERMISSIBLE_BASE_ATTACHMENTS_FIELD = PermissibleBase.class.getDeclaredField("attachments");
+ PERMISSIBLE_BASE_ATTACHMENTS_FIELD.setAccessible(true);
+
+ convertAndAddAttachments = LPPermissible.class.getDeclaredMethod("convertAndAddAttachments", Collection.class);
+ convertAndAddAttachments.setAccessible(true);
+ getActive = LPPermissible.class.getDeclaredMethod("getActive");
+ getActive.setAccessible(true);
+ setOldPermissible = LPPermissible.class.getDeclaredMethod("setOldPermissible", PermissibleBase.class);
+ setOldPermissible.setAccessible(true);
+ getOldPermissible = LPPermissible.class.getDeclaredMethod("getOldPermissible");
+ getOldPermissible.setAccessible(true);
+
+ TBMCCoreAPI.RegisterEventsForExceptions(this, mp);
+ }
+
+
+ //Code copied from LuckPerms - me.lucko.luckperms.bukkit.listeners.BukkitConnectionListener
+ @EventHandler(priority = EventPriority.LOWEST)
+ public void onPlayerLogin(PlayerLoginEvent e) {
+ /* Called when the player starts logging into the server.
+ At this point, the users data should be present and loaded. */
+
+ if (!(e.getPlayer() instanceof DiscordFakePlayer))
+ return; //Normal players must be handled by the plugin
+
+ final DiscordFakePlayer player = (DiscordFakePlayer) e.getPlayer();
+
+ if (plugin.getConfiguration().get(ConfigKeys.DEBUG_LOGINS)) {
+ plugin.getLogger().info("Processing login for " + player.getUniqueId() + " - " + player.getName());
+ }
+
+ final User user = plugin.getUserManager().getIfLoaded(player.getUniqueId());
+
+ /* User instance is null for whatever reason. Could be that it was unloaded between asyncpre and now. */
+ if (user == null) {
+ deniedLogin.add(player.getUniqueId());
+
+ if (!connectionListener.getUniqueConnections().contains(player.getUniqueId())) {
+
+ plugin.getLogger().warn("User " + player.getUniqueId() + " - " + player.getName() +
+ " doesn't have data pre-loaded, they have never been processed during pre-login in this session." +
+ " - denying login.");
+
+ try {
+ if ((Boolean) detectedCraftBukkitOfflineMode.get(connectionListener)) {
+ printCraftBukkitOfflineModeError.invoke(connectionListener);
+ e.disallow(PlayerLoginEvent.Result.KICK_OTHER, Message.LOADING_STATE_ERROR_CB_OFFLINE_MODE.asString(plugin.getLocaleManager()));
+ return;
+ }
+ } catch (IllegalAccessException | InvocationTargetException ex) {
+ ex.printStackTrace();
+ }
+
+ } else {
+ plugin.getLogger().warn("User " + player.getUniqueId() + " - " + player.getName() +
+ " doesn't currently have data pre-loaded, but they have been processed before in this session." +
+ " - denying login.");
+ }
+
+ e.disallow(PlayerLoginEvent.Result.KICK_OTHER, Message.LOADING_STATE_ERROR.asString(plugin.getLocaleManager()));
+ return;
+ }
+
+ // User instance is there, now we can inject our custom Permissible into the player.
+ // Care should be taken at this stage to ensure that async tasks which manipulate bukkit data check that the player is still online.
+ try {
+ // get the existing PermissibleBase held by the player
+ PermissibleBase oldPermissible = player.getPerm();
+
+ // Make a new permissible for the user
+ LPPermissible lpPermissible = new LPPermissible(player, user, plugin);
+
+ // Inject into the player
+ inject(player, lpPermissible, oldPermissible);
+
+ } catch (Throwable t) {
+ plugin.getLogger().warn("Exception thrown when setting up permissions for " +
+ player.getUniqueId() + " - " + player.getName() + " - denying login.");
+ t.printStackTrace();
+
+ e.disallow(PlayerLoginEvent.Result.KICK_OTHER, Message.LOADING_SETUP_ERROR.asString(plugin.getLocaleManager()));
+ return;
+ }
+
+ plugin.refreshAutoOp(player, true);
+ }
+
+ // Wait until the last priority to unload, so plugins can still perform permission checks on this event
+ @EventHandler(priority = EventPriority.MONITOR)
+ public void onPlayerQuit(PlayerQuitEvent e) {
+ if (!(e.getPlayer() instanceof DiscordFakePlayer))
+ return;
+
+ final DiscordFakePlayer player = (DiscordFakePlayer) e.getPlayer();
+
+ connectionListener.handleDisconnect(player.getUniqueId());
+
+ // perform unhooking from bukkit objects 1 tick later.
+ // this allows plugins listening after us on MONITOR to still have intact permissions data
+ this.plugin.getBootstrap().getServer().getScheduler().runTaskLaterAsynchronously(this.plugin.getBootstrap(), () -> {
+ // Remove the custom permissible
+ try {
+ uninject(player, true);
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+
+ // Handle auto op
+ if (this.plugin.getConfiguration().get(ConfigKeys.AUTO_OP)) {
+ player.setOp(false);
+ }
+
+ // remove their contexts cache
+ this.plugin.getContextManager().onPlayerQuit(player);
+ }, 1L);
+ }
+
+ //me.lucko.luckperms.bukkit.inject.permissible.PermissibleInjector
+ private void inject(DiscordFakePlayer player, LPPermissible newPermissible, PermissibleBase oldPermissible) throws IllegalAccessException, InvocationTargetException {
+
+ // seems we have already injected into this player.
+ if (oldPermissible instanceof LPPermissible) {
+ throw new IllegalStateException("LPPermissible already injected into player " + player.toString());
+ }
+
+ // Move attachments over from the old permissible
+
+ //noinspection unchecked
+ List attachments = (List) PERMISSIBLE_BASE_ATTACHMENTS_FIELD.get(oldPermissible);
+
+ convertAndAddAttachments.invoke(newPermissible, attachments);
+ attachments.clear();
+ oldPermissible.clearPermissions();
+
+ // Setup the new permissible
+ ((AtomicBoolean) getActive.invoke(newPermissible)).set(true);
+ setOldPermissible.invoke(newPermissible, oldPermissible);
+
+ // inject the new instance
+ player.setPerm(newPermissible);
+ }
+
+ private void uninject(DiscordFakePlayer player, boolean dummy) throws Exception {
+
+ // gets the players current permissible.
+ PermissibleBase permissible = player.getPerm();
+
+ // only uninject if the permissible was a luckperms one.
+ if (permissible instanceof LPPermissible) {
+ LPPermissible lpPermissible = ((LPPermissible) permissible);
+
+ // clear all permissions
+ lpPermissible.clearPermissions();
+
+ // set to inactive
+ ((AtomicBoolean) getActive.invoke(lpPermissible)).set(false);
+
+ // handle the replacement permissible.
+ if (dummy) {
+ // just inject a dummy class. this is used when we know the player is about to quit the server.
+ player.setPerm(DummyPermissibleBase.INSTANCE);
+
+ } else {
+ PermissibleBase newPb = (PermissibleBase) getOldPermissible.invoke(lpPermissible);
+ if (newPb == null) {
+ newPb = new PermissibleBase(player);
+ }
+
+ player.setPerm(newPb);
+ }
+ }
+ }
+}
diff --git a/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java b/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java
index 8806647..0ab0cac 100644
--- a/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java
+++ b/src/main/java/buttondevteam/discordplugin/role/GameRoleModule.java
@@ -4,17 +4,19 @@ import buttondevteam.core.ComponentManager;
import buttondevteam.discordplugin.DPUtils;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.architecture.Component;
-import buttondevteam.lib.architecture.ConfigData;
+import buttondevteam.lib.architecture.ReadOnlyConfigData;
+import discord4j.core.event.domain.role.RoleCreateEvent;
+import discord4j.core.event.domain.role.RoleDeleteEvent;
+import discord4j.core.event.domain.role.RoleEvent;
+import discord4j.core.event.domain.role.RoleUpdateEvent;
+import discord4j.core.object.entity.MessageChannel;
+import discord4j.core.object.entity.Role;
import lombok.val;
import org.bukkit.Bukkit;
-import sx.blah.discord.handle.impl.events.guild.role.RoleCreateEvent;
-import sx.blah.discord.handle.impl.events.guild.role.RoleDeleteEvent;
-import sx.blah.discord.handle.impl.events.guild.role.RoleEvent;
-import sx.blah.discord.handle.impl.events.guild.role.RoleUpdateEvent;
-import sx.blah.discord.handle.obj.IChannel;
-import sx.blah.discord.handle.obj.IRole;
+import reactor.core.publisher.Mono;
import java.awt.*;
+import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@@ -24,7 +26,7 @@ public class GameRoleModule extends Component {
@Override
protected void enable() {
getPlugin().getManager().registerCommand(new RoleCommand(this));
- GameRoles = DiscordPlugin.mainServer.getRoles().stream().filter(this::isGameRole).map(IRole::getName).collect(Collectors.toList());
+ GameRoles = DiscordPlugin.mainServer.getRoles().filterWhen(this::isGameRole).map(Role::getName).collect(Collectors.toList()).block();
}
@Override
@@ -32,7 +34,7 @@ public class GameRoleModule extends Component {
}
- private ConfigData logChannel() {
+ private ReadOnlyConfigData> logChannel() {
return DPUtils.channelData(getConfig(), "logChannel", 239519012529111040L);
}
@@ -43,41 +45,55 @@ public class GameRoleModule extends Component {
val logChannel = grm.logChannel().get();
if (roleEvent instanceof RoleCreateEvent) {
Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> {
- if (roleEvent.getRole().isDeleted() || !grm.isGameRole(roleEvent.getRole()))
- return; //Deleted or not a game role
- GameRoles.add(roleEvent.getRole().getName());
- if (logChannel != null)
- DiscordPlugin.sendMessageToChannel(logChannel, "Added " + roleEvent.getRole().getName() + " as game role. If you don't want this, change the role's color from the default.");
+ Role role=((RoleCreateEvent) roleEvent).getRole();
+ grm.isGameRole(role).flatMap(b -> {
+ if (!b)
+ return Mono.empty(); //Deleted or not a game role
+ GameRoles.add(role.getName());
+ if (logChannel != null)
+ return logChannel.flatMap(ch -> ch.createMessage("Added " + role.getName() + " as game role. If you don't want this, change the role's color from the default."));
+ return Mono.empty();
+ }).subscribe();
}, 100);
} else if (roleEvent instanceof RoleDeleteEvent) {
- if (GameRoles.remove(roleEvent.getRole().getName()) && logChannel != null)
- DiscordPlugin.sendMessageToChannel(logChannel, "Removed " + roleEvent.getRole().getName() + " as a game role.");
+ Role role=((RoleDeleteEvent) roleEvent).getRole().orElse(null);
+ if(role==null) return;
+ if (GameRoles.remove(role.getName()) && logChannel != null)
+ logChannel.flatMap(ch -> ch.createMessage("Removed " + role.getName() + " as a game role.")).subscribe();
} else if (roleEvent instanceof RoleUpdateEvent) {
val event = (RoleUpdateEvent) roleEvent;
- if (!grm.isGameRole(event.getNewRole())) {
- if (GameRoles.remove(event.getOldRole().getName()) && logChannel != null)
- DiscordPlugin.sendMessageToChannel(logChannel, "Removed " + event.getOldRole().getName() + " as a game role because it's color changed.");
- } else {
- if (GameRoles.contains(event.getOldRole().getName()) && event.getOldRole().getName().equals(event.getNewRole().getName()))
- return;
- boolean removed = GameRoles.remove(event.getOldRole().getName()); //Regardless of whether it was a game role
- GameRoles.add(event.getNewRole().getName()); //Add it because it has no color
- if (logChannel != null) {
- if (removed)
- DiscordPlugin.sendMessageToChannel(logChannel, "Changed game role from " + event.getOldRole().getName() + " to " + event.getNewRole().getName() + ".");
- else
- DiscordPlugin.sendMessageToChannel(logChannel, "Added " + event.getNewRole().getName() + " as game role because it has the default color.");
- }
+ if(!event.getOld().isPresent()) {
+ DPUtils.getLogger().warning("Old role not stored, cannot update game role!");
+ return;
}
+ Role or=event.getOld().get();
+ grm.isGameRole(event.getCurrent()).flatMap(b -> {
+ if (!b) {
+ if (GameRoles.remove(or.getName()) && logChannel != null)
+ return logChannel.flatMap(ch -> ch.createMessage("Removed " + or.getName() + " as a game role because it's color changed."));
+ } else {
+ if (GameRoles.contains(or.getName()) && or.getName().equals(event.getCurrent().getName()))
+ return Mono.empty();
+ boolean removed = GameRoles.remove(or.getName()); //Regardless of whether it was a game role
+ GameRoles.add(event.getCurrent().getName()); //Add it because it has no color
+ if (logChannel != null) {
+ if (removed)
+ return logChannel.flatMap(ch -> ch.createMessage("Changed game role from " + or.getName() + " to " + event.getCurrent().getName() + "."));
+ else
+ return logChannel.flatMap(ch -> ch.createMessage("Added " + event.getCurrent().getName() + " as game role because it has the default color."));
+ }
+ }
+ return Mono.empty();
+ }).subscribe();
}
}
- private boolean isGameRole(IRole r) {
- if (r.getGuild().getLongID() != DiscordPlugin.mainServer.getLongID())
- return false; //Only allow on the main server
+ private Mono isGameRole(Role r) {
+ if (r.getGuildId().asLong() != DiscordPlugin.mainServer.getId().asLong())
+ return Mono.just(false); //Only allow on the main server
val rc = new Color(149, 165, 166, 0);
- return r.getColor().equals(rc)
- && DiscordPlugin.dc.getOurUser().getRolesForGuild(DiscordPlugin.mainServer)
- .stream().anyMatch(or -> r.getPosition() < or.getPosition()); //Below one of our roles
+ return Mono.just(r.getColor().equals(rc)).filter(b -> b).flatMap(b ->
+ DiscordPlugin.dc.getSelf().flatMap(u -> u.asMember(DiscordPlugin.mainServer.getId())).flatMap(m -> m.hasHigherRoles(Collections.singleton(r)))) //Below one of our roles
+ .defaultIfEmpty(false);
}
}
diff --git a/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java b/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java
index 3381ae4..eb93eb6 100755
--- a/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java
+++ b/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java
@@ -1,14 +1,13 @@
package buttondevteam.discordplugin.role;
-import buttondevteam.discordplugin.DPUtils;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.commands.Command2DCSender;
import buttondevteam.discordplugin.commands.ICommand2DC;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Command2;
import buttondevteam.lib.chat.CommandClass;
+import discord4j.core.object.entity.Role;
import lombok.val;
-import sx.blah.discord.handle.obj.IRole;
import java.util.List;
import java.util.stream.Collectors;
@@ -27,12 +26,12 @@ public class RoleCommand extends ICommand2DC {
"This command adds a role to your account."
})
public boolean add(Command2DCSender sender, @Command2.TextArg String rolename) {
- final IRole role = checkAndGetRole(sender, rolename);
+ final Role role = checkAndGetRole(sender, rolename);
if (role == null)
return true;
try {
- DPUtils.perform(() -> sender.getMessage().getAuthor().addRole(role));
- sender.sendMessage("added role.");
+ sender.getMessage().getAuthorAsMember()
+ .subscribe(m -> m.addRole(role.getId()).subscribe(r -> sender.sendMessage("added role.")));
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while adding role!", e);
sender.sendMessage("an error occured while adding the role.");
@@ -45,12 +44,12 @@ public class RoleCommand extends ICommand2DC {
"This command removes a role from your account."
})
public boolean remove(Command2DCSender sender, @Command2.TextArg String rolename) {
- final IRole role = checkAndGetRole(sender, rolename);
+ final Role role = checkAndGetRole(sender, rolename);
if (role == null)
return true;
try {
- DPUtils.perform(() -> sender.getMessage().getAuthor().removeRole(role));
- sender.sendMessage("removed role.");
+ sender.getMessage().getAuthorAsMember()
+ .subscribe(m -> m.removeRole(role.getId()).subscribe(r -> sender.sendMessage("removed role.")));
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while removing role!", e);
sender.sendMessage("an error occured while removing the role.");
@@ -61,9 +60,9 @@ public class RoleCommand extends ICommand2DC {
@Command2.Subcommand
public void list(Command2DCSender sender) {
sender.sendMessage("list of roles:\n" + grm.GameRoles.stream().sorted().collect(Collectors.joining("\n")));
- }
+ }
- private IRole checkAndGetRole(Command2DCSender sender, String rolename) {
+ private Role checkAndGetRole(Command2DCSender sender, String rolename) {
String rname = rolename;
if (!grm.GameRoles.contains(rolename)) { //If not found as-is, correct case
val orn = grm.GameRoles.stream().filter(r -> r.equalsIgnoreCase(rolename)).findAny();
@@ -73,18 +72,23 @@ public class RoleCommand extends ICommand2DC {
return null;
}
rname = orn.get();
- }
- final List roles = DiscordPlugin.mainServer.getRolesByName(rname);
- if (roles.size() == 0) {
- sender.sendMessage("the specified role cannot be found on Discord! Removing from the list.");
- grm.GameRoles.remove(rolename);
- return null;
- }
- if (roles.size() > 1) {
- sender.sendMessage("there are multiple roles with this name. Why are there multiple roles with this name?");
- return null;
- }
- return roles.get(0);
- }
+ }
+ val frname = rname;
+ final List roles = DiscordPlugin.mainServer.getRoles().filter(r -> r.getName().equals(frname)).collectList().block();
+ if (roles == null) {
+ sender.sendMessage("an error occured.");
+ return null;
+ }
+ if (roles.size() == 0) {
+ sender.sendMessage("the specified role cannot be found on Discord! Removing from the list.");
+ grm.GameRoles.remove(rolename);
+ return null;
+ }
+ if (roles.size() > 1) {
+ sender.sendMessage("there are multiple roles with this name. Why are there multiple roles with this name?");
+ return null;
+ }
+ return roles.get(0);
+ }
}
diff --git a/src/main/java/buttondevteam/discordplugin/util/Timings.java b/src/main/java/buttondevteam/discordplugin/util/Timings.java
new file mode 100644
index 0000000..12c12f2
--- /dev/null
+++ b/src/main/java/buttondevteam/discordplugin/util/Timings.java
@@ -0,0 +1,16 @@
+package buttondevteam.discordplugin.util;
+
+import buttondevteam.discordplugin.listeners.CommonListeners;
+
+public class Timings {
+ private long start;
+
+ public Timings() {
+ start = System.nanoTime();
+ }
+
+ public void printElapsed(String message) {
+ CommonListeners.debug(message + " (" + (System.nanoTime() - start) / 1000000L + ")");
+ start = System.nanoTime();
+ }
+}