diff --git a/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.java b/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.java deleted file mode 100644 index e27a30f..0000000 --- a/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.java +++ /dev/null @@ -1,135 +0,0 @@ -package buttondevteam.discordplugin.announcer; - -import buttondevteam.discordplugin.DPUtils; -import buttondevteam.discordplugin.DiscordPlayer; -import buttondevteam.discordplugin.DiscordPlugin; -import buttondevteam.lib.TBMCCoreAPI; -import buttondevteam.lib.architecture.Component; -import buttondevteam.lib.architecture.ComponentMetadata; -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.channel.MessageChannel; -import lombok.val; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Mono; - -/** - * Posts new posts from Reddit to the specified channel(s). It will pin the regular posts (not the mod posts). - */ -@ComponentMetadata(enabledByDefault = false) -public class AnnouncerModule extends Component { - /** - * Channel to post new posts. - */ - public final ReadOnlyConfigData> channel = DPUtils.channelData(getConfig(), "channel"); - - /** - * Channel where distinguished (moderator) posts go. - */ - private final ReadOnlyConfigData> modChannel = DPUtils.channelData(getConfig(), "modChannel"); - - /** - * Automatically unpins all messages except the last few. Set to 0 or >50 to disable - */ - private final ConfigData keepPinned = getConfig().getData("keepPinned", (short) 40); - - private final ConfigData lastAnnouncementTime = getConfig().getData("lastAnnouncementTime", 0L); - - private final ConfigData lastSeenTime = getConfig().getData("lastSeenTime", 0L); - - /** - * The subreddit to pull the posts from - */ - private final ConfigData subredditURL = getConfig().getData("subredditURL", "https://www.reddit.com/r/ChromaGamers"); - - private static boolean stop = false; - - @Override - protected void enable() { - if (DPUtils.disableIfConfigError(this, channel, modChannel)) return; - stop = false; //If not the first time - val kp = keepPinned.get(); - if (kp == 0) return; - Flux msgs = channel.get().flatMapMany(MessageChannel::getPinnedMessages).takeLast(kp); - msgs.subscribe(Message::unpin); - new Thread(this::AnnouncementGetterThreadMethod).start(); - } - - @Override - protected void disable() { - stop = true; - } - - private void AnnouncementGetterThreadMethod() { - while (!stop) { - try { - if (!isEnabled()) { - //noinspection BusyWait - Thread.sleep(10000); - continue; - } - String body = TBMCCoreAPI.DownloadString(subredditURL.get() + "/new/.json?limit=10"); - JsonArray json = new JsonParser().parse(body).getAsJsonObject().get("data").getAsJsonObject() - .get("children").getAsJsonArray(); - StringBuilder msgsb = new StringBuilder(); - StringBuilder modmsgsb = new StringBuilder(); - 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(); - String author = data.get("author").getAsString(); - JsonElement distinguishedjson = data.get("distinguished"); - String distinguished; - if (distinguishedjson.isJsonNull()) - distinguished = null; - else - 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()) { - //noinspection ConstantConditions - do { - val reddituserclass = ChromaGamerBase.getTypeForFolder("reddit"); - if (reddituserclass == null) - break; - val user = ChromaGamerBase.getUser(author, reddituserclass); - String id = user.getConnectedID(DiscordPlayer.class); - if (id != null) - author = "<@" + id + ">"; - } while (false); - if (!author.startsWith("<")) - author = "/u/" + author; - (distinguished != null && distinguished.equals("moderator") ? modmsgsb : msgsb) - .append("A new post was submitted to the subreddit by ").append(author).append("\n") - .append(permalink).append("\n"); - lastanntime = date; - } - } - if (msgsb.length() > 0) - channel.get().flatMap(ch -> ch.createMessage(msgsb.toString())) - .flatMap(Message::pin).subscribe(); - if (modmsgsb.length() > 0) - 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(); - } - try { - //noinspection BusyWait - Thread.sleep(10000); - } catch (InterruptedException ex) { - Thread.currentThread().interrupt(); - } - } - } -} diff --git a/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.scala b/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.scala new file mode 100644 index 0000000..118ec9f --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.scala @@ -0,0 +1,103 @@ +package buttondevteam.discordplugin.announcer + +import buttondevteam.discordplugin.{DPUtils, DiscordPlayer, DiscordPlugin} +import buttondevteam.lib.TBMCCoreAPI +import buttondevteam.lib.architecture.{Component, ComponentMetadata} +import buttondevteam.lib.player.ChromaGamerBase +import com.google.gson.JsonParser +import discord4j.core.`object`.entity.Message +import discord4j.core.`object`.entity.channel.MessageChannel +import reactor.core.publisher.Flux + +/** + * Posts new posts from Reddit to the specified channel(s). It will pin the regular posts (not the mod posts). + */ +@ComponentMetadata(enabledByDefault = false) object AnnouncerModule { + private var stop = false +} + +@ComponentMetadata(enabledByDefault = false) class AnnouncerModule extends Component[DiscordPlugin] { + /** + * Channel to post new posts. + */ + final val channel = DPUtils.channelData(getConfig, "channel") + /** + * Channel where distinguished (moderator) posts go. + */ + final private val modChannel = DPUtils.channelData(getConfig, "modChannel") + /** + * Automatically unpins all messages except the last few. Set to 0 or >50 to disable + */ + final private val keepPinned = getConfig.getData("keepPinned", 40.toShort) + final private val lastAnnouncementTime = getConfig.getData("lastAnnouncementTime", 0L) + final private val lastSeenTime = getConfig.getData("lastSeenTime", 0L) + /** + * The subreddit to pull the posts from + */ + final private val subredditURL = getConfig.getData("subredditURL", "https://www.reddit.com/r/ChromaGamers") + + override protected def enable(): Unit = { + if (DPUtils.disableIfConfigError(this, channel, modChannel)) return + AnnouncerModule.stop = false //If not the first time + val kp = keepPinned.get + if (kp eq 0) return + val msgs: Flux[Message] = channel.get.flatMapMany(_.getPinnedMessages).takeLast(kp) + msgs.subscribe(_.unpin) + new Thread(() => this.AnnouncementGetterThreadMethod()).start() + } + + override protected def disable(): Unit = AnnouncerModule.stop = true + + private def AnnouncementGetterThreadMethod(): Unit = while ( { + !AnnouncerModule.stop + }) { + try { + if (!isEnabled) { //noinspection BusyWait + Thread.sleep(10000) + continue //todo: continue is not supported + } + val body = TBMCCoreAPI.DownloadString(subredditURL.get + "/new/.json?limit=10") + val json = new JsonParser().parse(body).getAsJsonObject.get("data").getAsJsonObject.get("children").getAsJsonArray + val msgsb = new StringBuilder + val modmsgsb = new StringBuilder + var lastanntime = lastAnnouncementTime.get + for (i <- json.size - 1 to 0 by -1) { + val item = json.get(i).getAsJsonObject + val data = item.get("data").getAsJsonObject + var author = data.get("author").getAsString + val distinguishedjson = data.get("distinguished") + var distinguished = null + if (distinguishedjson.isJsonNull) distinguished = null + else distinguished = distinguishedjson.getAsString + val permalink = "https://www.reddit.com" + data.get("permalink").getAsString + val date = data.get("created_utc").getAsLong + if (date > lastSeenTime.get) lastSeenTime.set(date) + else if (date > lastAnnouncementTime.get) { //noinspection ConstantConditions + do { + val reddituserclass = ChromaGamerBase.getTypeForFolder("reddit") + if (reddituserclass == null) break //todo: break is not supported + val user = ChromaGamerBase.getUser(author, reddituserclass) + val id = user.getConnectedID(classOf[DiscordPlayer]) + if (id != null) author = "<@" + id + ">" + } while ( { + false + }) + if (!author.startsWith("<")) author = "/u/" + author + (if (distinguished != null && distinguished == "moderator") modmsgsb + else msgsb).append("A new post was submitted to the subreddit by ").append(author).append("\n").append(permalink).append("\n") + lastanntime = date + } + } + if (msgsb.length > 0) channel.get.flatMap((ch: MessageChannel) => ch.createMessage(msgsb.toString)).flatMap(Message.pin).subscribe + if (modmsgsb.length > 0) modChannel.get.flatMap((ch: MessageChannel) => ch.createMessage(modmsgsb.toString)).flatMap(Message.pin).subscribe + if (lastAnnouncementTime.get ne lastanntime) lastAnnouncementTime.set(lastanntime) // If sending succeeded} catch { + case e: Exception => + e.printStackTrace() + } + try Thread.sleep(10000) + catch { + case ex: InterruptedException => + Thread.currentThread.interrupt() + } + } +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java b/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java deleted file mode 100644 index a6ef5e2..0000000 --- a/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.java +++ /dev/null @@ -1,45 +0,0 @@ -package buttondevteam.discordplugin.broadcaster; - -import buttondevteam.discordplugin.DiscordPlugin; -import buttondevteam.lib.TBMCCoreAPI; -import buttondevteam.lib.architecture.Component; -import buttondevteam.lib.architecture.ComponentMetadata; -import lombok.Getter; - -/** - * Uses a bit of a hacky method of getting all broadcasted messages, including advancements and any other message that's for everyone. - * If this component is enabled then these messages will show up on Discord. - */ -@ComponentMetadata(enabledByDefault = false) -public class GeneralEventBroadcasterModule extends Component { - private static @Getter boolean hooked = false; - - @Override - protected void enable() { - try { - PlayerListWatcher.hookUpDown(true, this); - log("Finished hooking into the player list"); - hooked = true; - } catch (Exception e) { - TBMCCoreAPI.SendException("Error while hacking the player list! Disable this module if you're on an incompatible version.", e, this); - } catch (NoClassDefFoundError e) { - logWarn("Error while hacking the player list! Disable this module if you're on an incompatible version."); - } - - } - - @Override - protected void disable() { - try { - if (!hooked) return; - if (PlayerListWatcher.hookUpDown(false, this)) - log("Finished unhooking the player list!"); - else - log("Didn't have the player list hooked."); - hooked = false; - } catch (Exception e) { - TBMCCoreAPI.SendException("Error while hacking the player list!", e, this); - } catch (NoClassDefFoundError ignored) { - } - } -} diff --git a/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.scala b/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.scala new file mode 100644 index 0000000..99e0ac4 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/broadcaster/GeneralEventBroadcasterModule.scala @@ -0,0 +1,39 @@ +package buttondevteam.discordplugin.broadcaster + +import buttondevteam.discordplugin.DiscordPlugin +import buttondevteam.lib.TBMCCoreAPI +import buttondevteam.lib.architecture.{Component, ComponentMetadata} + +/** + * Uses a bit of a hacky method of getting all broadcasted messages, including advancements and any other message that's for everyone. + * If this component is enabled then these messages will show up on Discord. + */ +@ComponentMetadata(enabledByDefault = false) object GeneralEventBroadcasterModule { + def isHooked: Boolean = GeneralEventBroadcasterModule.hooked + + private var hooked = false +} + +@ComponentMetadata(enabledByDefault = false) class GeneralEventBroadcasterModule extends Component[DiscordPlugin] { + override protected def enable(): Unit = try { + PlayerListWatcher.hookUpDown(true, this) + log("Finished hooking into the player list") + GeneralEventBroadcasterModule.hooked = true + } catch { + case e: Exception => + TBMCCoreAPI.SendException("Error while hacking the player list! Disable this module if you're on an incompatible version.", e, this) + case _: NoClassDefFoundError => + logWarn("Error while hacking the player list! Disable this module if you're on an incompatible version.") + } + + override protected def disable(): Unit = try { + if (!GeneralEventBroadcasterModule.hooked) return + if (PlayerListWatcher.hookUpDown(false, this)) log("Finished unhooking the player list!") + else log("Didn't have the player list hooked.") + GeneralEventBroadcasterModule.hooked = false + } catch { + case e: Exception => + TBMCCoreAPI.SendException("Error while hacking the player list!", e, this) + case _: NoClassDefFoundError => + } +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java deleted file mode 100755 index 5fd86d7..0000000 --- a/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.java +++ /dev/null @@ -1,176 +0,0 @@ -package buttondevteam.discordplugin.broadcaster; - -import buttondevteam.discordplugin.mcchat.MCChatUtils; -import buttondevteam.discordplugin.playerfaker.DelegatingMockMaker; -import buttondevteam.lib.TBMCCoreAPI; -import lombok.val; -import org.bukkit.Bukkit; -import org.mockito.Mockito; -import org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.reflect.Constructor; -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.UUID; - -public class PlayerListWatcher { - private static Object plist; - private static Object mock; - private static MethodHandle fHandle; //Handle for PlayerList.f(EntityPlayer) - Only needed for 1.16 - - static boolean hookUpDown(boolean up, GeneralEventBroadcasterModule module) throws Exception { - val csc = Bukkit.getServer().getClass(); - Field conf = csc.getDeclaredField("console"); - conf.setAccessible(true); - val server = conf.get(Bukkit.getServer()); - val nms = server.getClass().getPackage().getName(); - val dplc = Class.forName(nms + ".DedicatedPlayerList"); - val currentPL = server.getClass().getMethod("getPlayerList").invoke(server); - if (up) { - if (currentPL == mock) { - module.logWarn("Player list already mocked!"); - return false; - } - DelegatingMockMaker.getInstance().setMockMaker(new SubclassByteBuddyMockMaker()); - val icbcl = Class.forName(nms + ".IChatBaseComponent"); - Method sendMessageTemp; - try { - sendMessageTemp = server.getClass().getMethod("sendMessage", icbcl, UUID.class); - } catch (NoSuchMethodException e) { - sendMessageTemp = server.getClass().getMethod("sendMessage", icbcl); - } - val sendMessage = sendMessageTemp; - val cmtcl = Class.forName(nms + ".ChatMessageType"); - val systemType = cmtcl.getDeclaredField("SYSTEM").get(null); - val chatType = cmtcl.getDeclaredField("CHAT").get(null); - - val obc = csc.getPackage().getName(); - val ccmcl = Class.forName(obc + ".util.CraftChatMessage"); - val fixComponent = ccmcl.getMethod("fixComponent", icbcl); - val ppoc = Class.forName(nms + ".PacketPlayOutChat"); - Constructor ppocCTemp; - try { - ppocCTemp = ppoc.getConstructor(icbcl, cmtcl, UUID.class); - } catch (Exception e) { - ppocCTemp = ppoc.getConstructor(icbcl, cmtcl); - } - val ppocC = ppocCTemp; - val sendAll = dplc.getMethod("sendAll", Class.forName(nms + ".Packet")); - Method tpt; - try { - tpt = icbcl.getMethod("toPlainText"); - } catch (NoSuchMethodException e) { - tpt = icbcl.getMethod("getString"); - } - val toPlainText = tpt; - val sysb = Class.forName(nms + ".SystemUtils").getField("b"); - - //Find the original method without overrides - Constructor lookupConstructor; - if (nms.contains("1_16")) { - lookupConstructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class); - lookupConstructor.setAccessible(true); //Create lookup with a given class instead of caller - } else lookupConstructor = null; - mock = Mockito.mock(dplc, Mockito.withSettings().defaultAnswer(new Answer<>() { // Cannot call super constructor - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - final Method method = invocation.getMethod(); - if (!method.getName().equals("sendMessage")) { - if (method.getName().equals("sendAll")) { - sendAll(invocation.getArgument(0)); - return null; - } - //In 1.16 it passes a reference to the player list to advancement data for each player - if (nms.contains("1_16") && method.getName().equals("f") && method.getParameterCount() > 0 && method.getParameterTypes()[0].getSimpleName().equals("EntityPlayer")) { - method.setAccessible(true); - if (fHandle == null) { - assert lookupConstructor != null; - var lookup = lookupConstructor.newInstance(mock.getClass()); - fHandle = lookup.unreflectSpecial(method, mock.getClass()); //Special: super.method() - } - return fHandle.invoke(mock, invocation.getArgument(0)); //Invoke with our instance, so it passes that to advancement data, we have the fields as well - } - return method.invoke(plist, invocation.getArguments()); - } - val args = invocation.getArguments(); - val params = method.getParameterTypes(); - if (params.length == 0) { - TBMCCoreAPI.SendException("Found a strange method", - new Exception("Found a sendMessage() method without arguments."), module); - return null; - } - if (params[0].getSimpleName().equals("IChatBaseComponent[]")) - for (val arg : (Object[]) args[0]) - sendMessage(arg, true); - else if (params[0].getSimpleName().equals("IChatBaseComponent")) - if (params.length > 1 && params[1].getSimpleName().equalsIgnoreCase("boolean")) - sendMessage(args[0], (Boolean) args[1]); - else - sendMessage(args[0], true); - else - TBMCCoreAPI.SendException("Found a method with interesting params", - new Exception("Found a sendMessage(" + params[0].getSimpleName() + ") method"), module); - return null; - } - - private void sendMessage(Object chatComponent, boolean system) { - try { //Converted to use reflection - if (sendMessage.getParameterCount() == 2) - sendMessage.invoke(server, chatComponent, sysb.get(null)); - else - sendMessage.invoke(server, chatComponent); - Object chatmessagetype = system ? systemType : chatType; - - // CraftBukkit start - we run this through our processor first so we can get web links etc - var comp = fixComponent.invoke(null, chatComponent); - var packet = ppocC.getParameterCount() == 3 - ? ppocC.newInstance(comp, chatmessagetype, sysb.get(null)) - : ppocC.newInstance(comp, chatmessagetype); - this.sendAll(packet); - // CraftBukkit end - } catch (Exception e) { - TBMCCoreAPI.SendException("An error occurred while passing a vanilla message through the player list", e, module); - } - } - - private void sendAll(Object packet) { - try { // Some messages get sent by directly constructing a packet - sendAll.invoke(plist, packet); - if (packet.getClass() == ppoc) { - Field msgf = ppoc.getDeclaredField("a"); - msgf.setAccessible(true); - MCChatUtils.forPublicPrivateChat(MCChatUtils.send((String) toPlainText.invoke(msgf.get(packet)))).subscribe(); - } - } catch (Exception e) { - TBMCCoreAPI.SendException("Failed to broadcast message sent to all players - hacking failed.", e, module); - } - } - }).stubOnly()); - plist = currentPL; - for (var plc = dplc; plc != null; plc = plc.getSuperclass()) { //Set all fields - for (var f : plc.getDeclaredFields()) { - f.setAccessible(true); - Field modf = f.getClass().getDeclaredField("modifiers"); - modf.setAccessible(true); - modf.set(f, f.getModifiers() & ~Modifier.FINAL); - f.set(mock, f.get(plist)); - } - } - } - try { - server.getClass().getMethod("a", dplc).invoke(server, up ? mock : plist); - } catch (NoSuchMethodException e) { - server.getClass().getMethod("a", Class.forName(server.getClass().getPackage().getName() + ".PlayerList")) - .invoke(server, up ? mock : plist); - } - Field pllf = csc.getDeclaredField("playerList"); - pllf.setAccessible(true); - pllf.set(Bukkit.getServer(), up ? mock : plist); - return true; - } -} diff --git a/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.scala b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.scala new file mode 100644 index 0000000..7e563d1 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/broadcaster/PlayerListWatcher.scala @@ -0,0 +1,168 @@ +package buttondevteam.discordplugin.broadcaster + +import buttondevteam.discordplugin.mcchat.MCChatUtils +import buttondevteam.discordplugin.playerfaker.DelegatingMockMaker +import buttondevteam.lib.TBMCCoreAPI +import org.bukkit.Bukkit +import org.mockito.Mockito +import org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker +import org.mockito.invocation.InvocationOnMock +import org.mockito.stubbing.Answer + +import java.lang.invoke.{MethodHandle, MethodHandles} +import java.lang.reflect.{Constructor, Method, Modifier} +import java.util.UUID + +object PlayerListWatcher { + private var plist: AnyRef = null + private var mock: AnyRef = null + private var fHandle: MethodHandle = null //Handle for PlayerList.f(EntityPlayer) - Only needed for 1.16 + @throws[Exception] + private[broadcaster] def hookUpDown(up: Boolean, module: GeneralEventBroadcasterModule): Boolean = { + val csc = Bukkit.getServer.getClass + val conf = csc.getDeclaredField("console") + conf.setAccessible(true) + val server = conf.get(Bukkit.getServer) + val nms = server.getClass.getPackage.getName + val dplc = Class.forName(nms + ".DedicatedPlayerList") + val currentPL = server.getClass.getMethod("getPlayerList").invoke(server) + if (up) { + if (currentPL eq mock) { + module.logWarn("Player list already mocked!") + return false + } + DelegatingMockMaker.getInstance.setMockMaker(new SubclassByteBuddyMockMaker) + val icbcl = Class.forName(nms + ".IChatBaseComponent") + var sendMessageTemp: Method = null + try sendMessageTemp = server.getClass.getMethod("sendMessage", icbcl, classOf[UUID]) + catch { + case e: NoSuchMethodException => + sendMessageTemp = server.getClass.getMethod("sendMessage", icbcl) + } + val sendMessageMethod = sendMessageTemp + val cmtcl = Class.forName(nms + ".ChatMessageType") + val systemType = cmtcl.getDeclaredField("SYSTEM").get(null) + val chatType = cmtcl.getDeclaredField("CHAT").get(null) + val obc = csc.getPackage.getName + val ccmcl = Class.forName(obc + ".util.CraftChatMessage") + val fixComponent = ccmcl.getMethod("fixComponent", icbcl) + val ppoc = Class.forName(nms + ".PacketPlayOutChat") + var ppocCTemp: Constructor[_] = null + try ppocCTemp = ppoc.getConstructor(icbcl, cmtcl, classOf[UUID]) + catch { + case _: Exception => + ppocCTemp = ppoc.getConstructor(icbcl, cmtcl) + } + val ppocC = ppocCTemp + val sendAllMethod = dplc.getMethod("sendAll", Class.forName(nms + ".Packet")) + var tpt: Method = null + try tpt = icbcl.getMethod("toPlainText") + catch { + case _: NoSuchMethodException => + tpt = icbcl.getMethod("getString") + } + val toPlainText = tpt + val sysb = Class.forName(nms + ".SystemUtils").getField("b") + //Find the original method without overrides + var lookupConstructor: Constructor[MethodHandles.Lookup] = null + if (nms.contains("1_16")) { + lookupConstructor = classOf[MethodHandles.Lookup].getDeclaredConstructor(classOf[Class[_]]) + lookupConstructor.setAccessible(true) //Create lookup with a given class instead of caller + } + else lookupConstructor = null + mock = Mockito.mock(dplc, Mockito.withSettings.defaultAnswer(new Answer[AnyRef]() { // Cannot call super constructor + @throws[Throwable] + override def answer(invocation: InvocationOnMock): Any = { + val method = invocation.getMethod + if (!(method.getName == "sendMessage")) { + if (method.getName == "sendAll") { + sendAll(invocation.getArgument(0)) + return null + } + //In 1.16 it passes a reference to the player list to advancement data for each player + if (nms.contains("1_16") && method.getName == "f" && method.getParameterCount > 0 && method.getParameterTypes()(0).getSimpleName == "EntityPlayer") { + method.setAccessible(true) + if (fHandle == null) { + assert(lookupConstructor != null) + val lookup = lookupConstructor.newInstance(mock.getClass) + fHandle = lookup.unreflectSpecial(method, mock.getClass) //Special: super.method() + } + return fHandle.invoke(mock, invocation.getArgument(0)) //Invoke with our instance, so it passes that to advancement data, we have the fields as well + } + return method.invoke(plist, invocation.getArguments) + } + val args = invocation.getArguments + val params = method.getParameterTypes + if (params.isEmpty) { + TBMCCoreAPI.SendException("Found a strange method", new Exception("Found a sendMessage() method without arguments."), module) + return null + } + if (params(0).getSimpleName == "IChatBaseComponent[]") for (arg <- args(0).asInstanceOf[Array[AnyRef]]) { + sendMessage(arg, system = true) + } + else if (params(0).getSimpleName == "IChatBaseComponent") if (params.length > 1 && params(1).getSimpleName.equalsIgnoreCase("boolean")) sendMessage(args(0), args(1).asInstanceOf[Boolean]) + else sendMessage(args(0), system = true) + else TBMCCoreAPI.SendException("Found a method with interesting params", new Exception("Found a sendMessage(" + params(0).getSimpleName + ") method"), module) + null + } + + private + + def sendMessage(chatComponent: Any, system: Boolean) = try { //Converted to use reflection + if (sendMessageMethod.getParameterCount == 2) sendMessageMethod.invoke(server, chatComponent, sysb.get(null)) + else sendMessageMethod.invoke(server, chatComponent) + val chatmessagetype = if (system) systemType + else chatType + // CraftBukkit start - we run this through our processor first so we can get web links etc + val comp = fixComponent.invoke(null, chatComponent) + val packet = if (ppocC.getParameterCount == 3) ppocC.newInstance(comp, chatmessagetype, sysb.get(null)) + else ppocC.newInstance(comp, chatmessagetype) + this.sendAll(packet) + } catch { + case e: Exception => + TBMCCoreAPI.SendException("An error occurred while passing a vanilla message through the player list", e, module) + } + + private + + def sendAll(packet: Any) = try { // Some messages get sent by directly constructing a packet + sendAllMethod.invoke(plist, packet) + if (packet.getClass eq ppoc) { + val msgf = ppoc.getDeclaredField("a") + msgf.setAccessible(true) + MCChatUtils.forPublicPrivateChat(MCChatUtils.send(toPlainText.invoke(msgf.get(packet)).asInstanceOf[String])).subscribe + } + } catch { + case e: Exception => + TBMCCoreAPI.SendException("Failed to broadcast message sent to all players - hacking failed.", e, module) + } + }).stubOnly).asInstanceOf + plist = currentPL + var plc = dplc + while ( { + plc != null + }) { //Set all fields + for (f <- plc.getDeclaredFields) { + f.setAccessible(true) + val modf = f.getClass.getDeclaredField("modifiers") + modf.setAccessible(true) + modf.set(f, f.getModifiers & ~Modifier.FINAL) + f.set(mock, f.get(plist)) + } + plc = plc.getSuperclass + } + } + try server.getClass.getMethod("a", dplc).invoke(server, if (up) mock + else plist) + catch { + case e: NoSuchMethodException => + server.getClass.getMethod("a", Class.forName(server.getClass.getPackage.getName + ".PlayerList")).invoke(server, if (up) mock + else plist) + } + val pllf = csc.getDeclaredField("playerList") + pllf.setAccessible(true) + pllf.set(Bukkit.getServer, if (up) mock + else plist) + true + } +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/commands/Command2DC.java b/src/main/java/buttondevteam/discordplugin/commands/Command2DC.java deleted file mode 100644 index ab56eb8..0000000 --- a/src/main/java/buttondevteam/discordplugin/commands/Command2DC.java +++ /dev/null @@ -1,19 +0,0 @@ -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) { - super.registerCommand(command, DiscordPlugin.getPrefix()); //Needs to be configurable for the helps - } - - @Override - 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/Command2DC.scala b/src/main/java/buttondevteam/discordplugin/commands/Command2DC.scala new file mode 100644 index 0000000..690cb2d --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/commands/Command2DC.scala @@ -0,0 +1,15 @@ +package buttondevteam.discordplugin.commands + +import buttondevteam.discordplugin.DiscordPlugin +import buttondevteam.lib.chat.Command2 + +import java.lang.reflect.Method + +class Command2DC extends Command2[ICommand2DC, Command2DCSender] { + override def registerCommand(command: ICommand2DC): Unit = + super.registerCommand(command, DiscordPlugin.getPrefix) //Needs to be configurable for the helps + override def hasPermission(sender: Command2DCSender, command: ICommand2DC, method: Method): Boolean = { + //return !command.isModOnly() || sender.getMessage().getAuthor().hasRole(DiscordPlugin.plugin.modRole().get()); //TODO: modRole may be null; more customisable way? + true + } +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.java b/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.java deleted file mode 100644 index ab22e37..0000000 --- a/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.java +++ /dev/null @@ -1,39 +0,0 @@ -package buttondevteam.discordplugin.commands; - -import buttondevteam.discordplugin.DPUtils; -import buttondevteam.lib.chat.Command2Sender; -import discord4j.core.object.entity.Message; -import discord4j.core.object.entity.User; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.val; - -@RequiredArgsConstructor -public class Command2DCSender implements Command2Sender { - 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); - 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 - public void sendMessage(String[] message) { - sendMessage(String.join("\n", message)); - } - - @Override - public String getName() { - return message.getAuthor().map(User::getUsername).orElse("Discord"); - } -} diff --git a/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.scala b/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.scala new file mode 100644 index 0000000..f898fe8 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.scala @@ -0,0 +1,22 @@ +package buttondevteam.discordplugin.commands + +import buttondevteam.discordplugin.DPUtils +import buttondevteam.lib.chat.Command2Sender +import discord4j.core.`object`.entity.channel.MessageChannel +import discord4j.core.`object`.entity.{Message, User} +import lombok.RequiredArgsConstructor + +@RequiredArgsConstructor class Command2DCSender(val message: Message) extends Command2Sender { + def getMessage: Message = this.message + + override def sendMessage(message: String): Unit = { + if (message.isEmpty) return + var msg = DPUtils.sanitizeString(message) + msg = Character.toLowerCase(message.charAt(0)) + message.substring(1) + this.message.getChannel.flatMap((ch: MessageChannel) => ch.createMessage(this.message.getAuthor.map((u: User) => DPUtils.nickMention(u.getId) + ", ").orElse("") + msg)).subscribe + } + + override def sendMessage(message: Array[String]): Unit = sendMessage(String.join("\n", message)) + + override def getName = message.getAuthor.map(_.getUsername).orElse("Discord") +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.java b/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.java deleted file mode 100755 index f0d7fe9..0000000 --- a/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.java +++ /dev/null @@ -1,59 +0,0 @@ -package buttondevteam.discordplugin.commands; - -import buttondevteam.discordplugin.DiscordPlayer; -import buttondevteam.lib.chat.Command2; -import buttondevteam.lib.chat.CommandClass; -import buttondevteam.lib.player.TBMCPlayer; -import buttondevteam.lib.player.TBMCPlayerBase; -import com.google.common.collect.HashBiMap; -import lombok.val; -import org.bukkit.Bukkit; -import org.bukkit.OfflinePlayer; -import org.bukkit.entity.Player; - -@CommandClass(helpText = { - "Connect command", // - "This command lets you connect your account with a Minecraft account. This allows using the private Minecraft chat and other things.", // -}) -public class ConnectCommand extends ICommand2DC { - - /** - * Key: Minecraft name
- * Value: Discord ID - */ - public static HashBiMap WaitingToConnect = HashBiMap.create(); - - @Command2.Subcommand - public boolean def(Command2DCSender sender, String Minecraftname) { - val message = sender.getMessage(); - 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) { - channel.createMessage("The specified Minecraft player cannot be found").subscribe(); - return true; - } - TBMCPlayer pl = TBMCPlayerBase.getPlayer(p.getUniqueId(), TBMCPlayer.class); - DiscordPlayer dp = pl.getAs(DiscordPlayer.class); - if (dp != null && author.getId().asString().equals(dp.getDiscordID())) { - channel.createMessage("You already have this account connected.").subscribe(); - return true; - } - 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 by running this command again.").subscribe(); - if (p.isOnline()) - ((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/ConnectCommand.scala b/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.scala new file mode 100644 index 0000000..db1d9a9 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.scala @@ -0,0 +1,46 @@ +package buttondevteam.discordplugin.commands + +import buttondevteam.discordplugin.DiscordPlayer +import buttondevteam.lib.chat.{Command2, CommandClass} +import buttondevteam.lib.player.{TBMCPlayer, TBMCPlayerBase} +import com.google.common.collect.HashBiMap +import org.bukkit.Bukkit +import org.bukkit.entity.Player + +@CommandClass(helpText = Array(Array("Connect command", // + "This command lets you connect your account with a Minecraft account. This allows using the private Minecraft chat and other things."))) object ConnectCommand { + /** + * Key: Minecraft name
+ * Value: Discord ID + */ + var WaitingToConnect: HashBiMap[String, String] = HashBiMap.create +} + +@CommandClass(helpText = Array(Array("Connect command", "This command lets you connect your account with a Minecraft account. This allows using the private Minecraft chat and other things."))) class ConnectCommand extends ICommand2DC { + @Command2.Subcommand def `def`(sender: Command2DCSender, Minecraftname: String): Boolean = { + val message = sender.getMessage + val channel = message.getChannel.block + val author = message.getAuthor.orElse(null) + if (author == null || channel == null) return true + if (ConnectCommand.WaitingToConnect.inverse.containsKey(author.getId.asString)) { + channel.createMessage("Replacing " + ConnectCommand.WaitingToConnect.inverse.get(author.getId.asString) + " with " + Minecraftname).subscribe + ConnectCommand.WaitingToConnect.inverse.remove(author.getId.asString) + } + //noinspection ScalaDeprecation + val p = Bukkit.getOfflinePlayer(Minecraftname) + if (p == null) { + channel.createMessage("The specified Minecraft player cannot be found").subscribe + return true + } + val pl = TBMCPlayerBase.getPlayer(p.getUniqueId, classOf[TBMCPlayer]) + val dp = pl.getAs(classOf[DiscordPlayer]) + if (dp != null && author.getId.asString == dp.getDiscordID) { + channel.createMessage("You already have this account connected.").subscribe + return true + } + ConnectCommand.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 by running this command again.").subscribe + if (p.isOnline) p.asInstanceOf[Player].sendMessage("§bTo connect with the Discord account " + author.getUsername + "#" + author.getDiscriminator + " do /discord accept") + true + } +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.java b/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.java deleted file mode 100644 index 951cc71..0000000 --- a/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.java +++ /dev/null @@ -1,30 +0,0 @@ -package buttondevteam.discordplugin.commands; - -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) { - 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 - .onErrorReturn(false).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/DebugCommand.scala b/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.scala new file mode 100644 index 0000000..67fee6e --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.scala @@ -0,0 +1,27 @@ +package buttondevteam.discordplugin.commands + +import buttondevteam.discordplugin.DiscordPlugin +import buttondevteam.discordplugin.listeners.CommonListeners +import buttondevteam.lib.chat.{Command2, CommandClass} +import discord4j.core.`object`.entity.{Member, User} +import reactor.core.publisher.Mono + +@CommandClass(helpText = Array(Array("Switches debug mode."))) +class DebugCommand extends ICommand2DC { + @Command2.Subcommand + override def `def`(sender: Command2DCSender): Boolean = { + sender.getMessage.getAuthorAsMember.switchIfEmpty(sender.getMessage.getAuthor.map //Support DMs + ((u: User) => u.asMember(DiscordPlugin.mainServer.getId)).orElse(Mono.empty)).flatMap((m: Member) => DiscordPlugin.plugin.modRole.get.map((mr) => m.getRoleIds.stream.anyMatch((r: Snowflake) => r == mr.getId)).switchIfEmpty(Mono.fromSupplier(() => DiscordPlugin.mainServer.getOwnerId.asLong eq m.getId.asLong))) + .onErrorReturn(false) //Role not found + .subscribe((success: Any) => { + def foo(success: Any) = { + if (success) sender.sendMessage("debug " + (if (CommonListeners.debug) "enabled" + else "disabled")) + else sender.sendMessage("you need to be a moderator to use this command.") + } + + foo(success) + }) + true + } +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.java b/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.java deleted file mode 100755 index 546d4ee..0000000 --- a/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.java +++ /dev/null @@ -1,24 +0,0 @@ -package buttondevteam.discordplugin.commands; - -import buttondevteam.lib.chat.Command2; -import buttondevteam.lib.chat.CommandClass; - -@CommandClass(helpText = { - "Help command", // - "Shows some info about a command or lists the available commands.", // -}) -public class HelpCommand extends ICommand2DC { - @Command2.Subcommand - public boolean def(Command2DCSender sender, @Command2.TextArg @Command2.OptionalArg String args) { - if (args == null || args.length() == 0) - sender.sendMessage(getManager().getCommandsText()); - 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/HelpCommand.scala b/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.scala new file mode 100644 index 0000000..c3d1029 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.scala @@ -0,0 +1,18 @@ +package buttondevteam.discordplugin.commands + +import buttondevteam.lib.chat.{Command2, CommandClass} + +@CommandClass(helpText = Array(Array("Help command", // + "Shows some info about a command or lists the available commands."))) +class HelpCommand extends ICommand2DC { + @Command2.Subcommand + def `def`(sender: Command2DCSender, @Command2.TextArg @Command2.OptionalArg args: String): Boolean = { + if (args == null || args.isEmpty) sender.sendMessage(getManager.getCommandsText) + else { + val ht = getManager.getHelpText(args) + if (ht == null) sender.sendMessage("Command not found: " + args) + else sender.sendMessage(ht) + } + true + } +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/commands/ICommand2DC.java b/src/main/java/buttondevteam/discordplugin/commands/ICommand2DC.java deleted file mode 100644 index 6aae802..0000000 --- a/src/main/java/buttondevteam/discordplugin/commands/ICommand2DC.java +++ /dev/null @@ -1,20 +0,0 @@ -package buttondevteam.discordplugin.commands; - -import buttondevteam.discordplugin.DiscordPlugin; -import buttondevteam.lib.chat.CommandClass; -import buttondevteam.lib.chat.ICommand2; -import lombok.Getter; -import lombok.val; - -public abstract class ICommand2DC extends ICommand2 { - public ICommand2DC() { - super(DiscordPlugin.plugin.getManager()); - val ann = getClass().getAnnotation(CommandClass.class); - if (ann == null) - modOnly = false; - else - modOnly = ann.modOnly(); - } - - private final @Getter boolean modOnly; -} diff --git a/src/main/java/buttondevteam/discordplugin/commands/ICommand2DC.scala b/src/main/java/buttondevteam/discordplugin/commands/ICommand2DC.scala new file mode 100644 index 0000000..31ae228 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/commands/ICommand2DC.scala @@ -0,0 +1,16 @@ +package buttondevteam.discordplugin.commands + +import buttondevteam.discordplugin.DiscordPlugin +import buttondevteam.lib.chat.{CommandClass, ICommand2} + +abstract class ICommand2DC() extends ICommand2[Command2DCSender](DiscordPlugin.plugin.manager) { + final private var modOnly = false + + { + val ann: CommandClass = getClass.getAnnotation(classOf[CommandClass]) + if (ann == null) modOnly = false + else modOnly = ann.modOnly + } + + def isModOnly: Boolean = this.modOnly +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.java b/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.java deleted file mode 100755 index 46a06ce..0000000 --- a/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.java +++ /dev/null @@ -1,88 +0,0 @@ -package buttondevteam.discordplugin.commands; - -import buttondevteam.discordplugin.DiscordPlayer; -import buttondevteam.discordplugin.DiscordPlugin; -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 java.util.List; - -@CommandClass(helpText = { - "User information", // - "Shows some information about users, from Discord, from Minecraft or from Reddit if they have these accounts connected.", // - "If used without args, shows your info.", // -}) -public class UserinfoCommand extends ICommand2DC { - @Command2.Subcommand - public boolean def(Command2DCSender sender, @Command2.OptionalArg @Command2.TextArg String user) { - val message = sender.getMessage(); - User target = null; - val channel = message.getChannel().block(); - assert channel != null; - if (user == null || user.length() == 0) - target = message.getAuthor().orElse(null); - else { - final User firstmention = message.getUserMentions() - .filter(m -> !m.getId().asString().equals(DiscordPlugin.dc.getSelfId().asString())).blockFirst(); - if (firstmention != null) - target = firstmention; - else if (user.contains("#")) { - String[] targettag = user.split("#"); - final List targets = getUsers(message, targettag[0]); - if (targets.size() == 0) { - channel.createMessage("The user cannot be found (by name): " + user).subscribe(); - return true; - } - for (User ptarget : targets) { - if (ptarget.getDiscriminator().equalsIgnoreCase(targettag[1])) { - target = ptarget; - break; - } - } - if (target == null) { - 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); - if (targets.size() == 0) { - channel.createMessage("The user cannot be found on Discord: " + user).subscribe(); - return true; - } - if (targets.size() > 1) { - 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); - } - } - if (target == null) { - sender.sendMessage("An error occurred."); - return true; - } - 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(); - return true; - } - - 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 = guild.getMembers().filter(m -> m.getUsername().equalsIgnoreCase(args)) - .map(m -> (User) m).collectList().block(); - return targets; - } - -} diff --git a/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.scala b/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.scala new file mode 100644 index 0000000..c9e7203 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.scala @@ -0,0 +1,76 @@ +package buttondevteam.discordplugin.commands + +import buttondevteam.discordplugin.{DiscordPlayer, DiscordPlugin} +import buttondevteam.lib.chat.{Command2, CommandClass} +import buttondevteam.lib.player.ChromaGamerBase +import buttondevteam.lib.player.ChromaGamerBase.InfoTarget +import discord4j.core.`object`.entity.{Member, Message, User} + +import java.util + +@CommandClass(helpText = Array(Array("User information", // + "Shows some information about users, from Discord, from Minecraft or from Reddit if they have these accounts connected.", + "If used without args, shows your info."))) +class UserinfoCommand extends ICommand2DC { + @Command2.Subcommand + def `def`(sender: Command2DCSender, @Command2.OptionalArg @Command2.TextArg user: String): Boolean = { + val message = sender.getMessage + var target: User = null + val channel = message.getChannel.block + assert(channel != null) + if (user == null || user.isEmpty) target = message.getAuthor.orElse(null) + else { + val firstmention = message.getUserMentions.filter((m: User) => !(m.getId.asString == DiscordPlugin.dc.getSelfId.asString)).blockFirst + if (firstmention != null) target = firstmention + else if (user.contains("#")) { + val targettag = user.split("#") + val targets = getUsers(message, targettag(0)) + if (targets.size == 0) { + channel.createMessage("The user cannot be found (by name): " + user).subscribe + return true + } + for (ptarget <- targets) { + if (ptarget.getDiscriminator.equalsIgnoreCase(targettag(1))) { + target = ptarget + break //todo: break is not supported + } + } + if (target == null) { + channel.createMessage("The user cannot be found (by discriminator): " + user + "(Found " + targets.size + " users with the name.)").subscribe + return true + } + } + else { + val targets = getUsers(message, user) + if (targets.size == 0) { + channel.createMessage("The user cannot be found on Discord: " + user).subscribe + return true + } + if (targets.size > 1) { + 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) + } + } + if (target == null) { + sender.sendMessage("An error occurred.") + return true + } + val dp = ChromaGamerBase.getUser(target.getId.asString, classOf[DiscordPlayer]) + val uinfo = new StringBuilder("User info for ").append(target.getUsername).append(":\n") + uinfo.append(dp.getInfo(InfoTarget.Discord)) + channel.createMessage(uinfo.toString).subscribe + true + } + + private def getUsers(message: Message, args: String) = { + var targets: util.List[User] + val guild = message.getGuild.block + if (guild == null) { //Private channel + targets = DiscordPlugin.dc.getUsers.filter((u) => u.getUsername.equalsIgnoreCase(args)).collectList.block + } + else targets = guild.getMembers.filter((m: Member) => m.getUsername.equalsIgnoreCase(args)).map((m: Member) => m.asInstanceOf[User]).collectList.block + targets + } +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/commands/VersionCommand.java b/src/main/java/buttondevteam/discordplugin/commands/VersionCommand.java deleted file mode 100644 index ac83243..0000000 --- a/src/main/java/buttondevteam/discordplugin/commands/VersionCommand.java +++ /dev/null @@ -1,26 +0,0 @@ -package buttondevteam.discordplugin.commands; - -import buttondevteam.discordplugin.DiscordPlugin; -import buttondevteam.lib.chat.Command2; -import buttondevteam.lib.chat.CommandClass; -import lombok.val; - -@CommandClass(helpText = { - "Version", - "Returns the plugin's version" -}) -public class VersionCommand extends ICommand2DC { - @Command2.Subcommand - public boolean def(Command2DCSender sender) { - sender.sendMessage(getVersion()); - return true; - } - - public static String[] getVersion() { - val desc = DiscordPlugin.plugin.getDescription(); - return new String[]{ // - desc.getFullName(), // - desc.getWebsite() // - }; - } -} diff --git a/src/main/java/buttondevteam/discordplugin/commands/VersionCommand.scala b/src/main/java/buttondevteam/discordplugin/commands/VersionCommand.scala new file mode 100644 index 0000000..59be8e3 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/commands/VersionCommand.scala @@ -0,0 +1,21 @@ +package buttondevteam.discordplugin.commands + +import buttondevteam.discordplugin.DiscordPlugin +import buttondevteam.lib.chat.{Command2, CommandClass} + +@CommandClass(helpText = Array(Array("Version", "Returns the plugin's version"))) +object VersionCommand { + def getVersion: Array[String] = { + val desc = DiscordPlugin.plugin.getDescription + Array[String]( // + desc.getFullName, desc.getWebsite //) + } +} + +@CommandClass(helpText = Array(Array("Version", "Returns the plugin's version"))) +class VersionCommand extends ICommand2DC { + @Command2.Subcommand override def `def`(sender: Command2DCSender): Boolean = { + sender.sendMessage(VersionCommand.getVersion) + true + } +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/exceptions/DebugMessageListener.java b/src/main/java/buttondevteam/discordplugin/exceptions/DebugMessageListener.java deleted file mode 100755 index f90f0f3..0000000 --- a/src/main/java/buttondevteam/discordplugin/exceptions/DebugMessageListener.java +++ /dev/null @@ -1,36 +0,0 @@ -package buttondevteam.discordplugin.exceptions; - -import buttondevteam.core.ComponentManager; -import buttondevteam.discordplugin.DiscordPlugin; -import buttondevteam.lib.TBMCDebugMessageEvent; -import discord4j.core.object.entity.channel.MessageChannel; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import reactor.core.publisher.Mono; - -public class DebugMessageListener implements Listener { - @EventHandler - public void onDebugMessage(TBMCDebugMessageEvent e) { - SendMessage(e.getDebugMessage()); - e.setSent(); - } - - private static void SendMessage(String message) { - 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("```"); - mc.flatMap(ch -> ch.createMessage(sb.toString())).subscribe(); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - -} diff --git a/src/main/java/buttondevteam/discordplugin/exceptions/DebugMessageListener.scala b/src/main/java/buttondevteam/discordplugin/exceptions/DebugMessageListener.scala new file mode 100644 index 0000000..265cdb6 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/exceptions/DebugMessageListener.scala @@ -0,0 +1,32 @@ +package buttondevteam.discordplugin.exceptions + +import buttondevteam.core.ComponentManager +import buttondevteam.discordplugin.DiscordPlugin +import buttondevteam.lib.TBMCDebugMessageEvent +import discord4j.core.`object`.entity.channel.MessageChannel +import org.bukkit.event.{EventHandler, Listener} + +object DebugMessageListener { + private def SendMessage(message: String): Unit = { + if (DiscordPlugin.SafeMode || !ComponentManager.isEnabled(classOf[ExceptionListenerModule])) return + try { + val mc = ExceptionListenerModule.getChannel + if (mc == null) return + val sb = new StringBuilder + sb.append("```").append("\n") + sb.append(if (message.length > 2000) message.substring(0, 2000) else message).append("\n") + sb.append("```") + mc.flatMap((ch: MessageChannel) => ch.createMessage(sb.toString)).subscribe + } catch { + case ex: Exception => + ex.printStackTrace() + } + } +} + +class DebugMessageListener extends Listener { + @EventHandler def onDebugMessage(e: TBMCDebugMessageEvent): Unit = { + DebugMessageListener.SendMessage(e.getDebugMessage) + e.setSent() + } +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java deleted file mode 100755 index 16d1e93..0000000 --- a/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.java +++ /dev/null @@ -1,114 +0,0 @@ -package buttondevteam.discordplugin.exceptions; - -import buttondevteam.core.ComponentManager; -import buttondevteam.discordplugin.DPUtils; -import buttondevteam.discordplugin.DiscordPlugin; -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.Role; -import discord4j.core.object.entity.channel.GuildChannel; -import discord4j.core.object.entity.channel.MessageChannel; -import org.apache.commons.lang.exception.ExceptionUtils; -import org.bukkit.Bukkit; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import reactor.core.publisher.Mono; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -/** - * Listens for errors from the Chroma plugins and posts them to Discord, ignoring repeating errors so it's not that spammy. - */ -public class ExceptionListenerModule extends Component implements Listener { - private final List lastthrown = new ArrayList<>(); - private final 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(); - } - - private static void SendException(Throwable e, String sourcemessage) { - if (instance == null) return; - try { - getChannel().flatMap(channel -> { - Mono coderRole; - if (channel instanceof GuildChannel) - coderRole = instance.pingRole(((GuildChannel) channel).getGuild()).get(); - else - coderRole = Mono.empty(); - return 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.createMessage(sb.toString()); - }); - }).subscribe(); - } catch (Exception ex) { - ex.printStackTrace(); - } - } - - private static ExceptionListenerModule instance; - - public static Mono getChannel() { - if (instance != null) return instance.channel.get(); - return Mono.empty(); - } - - /** - * The channel to post the errors to. - */ - private final ReadOnlyConfigData> channel = DPUtils.channelData(getConfig(), "channel"); - - /** - * The role to ping if an error occurs. Set to empty ('') to disable. - */ - private ConfigData> pingRole(Mono guild) { - return DPUtils.roleData(getConfig(), "pingRole", "Coder", guild); - } - - @Override - protected void enable() { - if (DPUtils.disableIfConfigError(this, channel)) return; - instance = this; - Bukkit.getPluginManager().registerEvents(new ExceptionListenerModule(), getPlugin()); - TBMCCoreAPI.RegisterEventsForExceptions(new DebugMessageListener(), getPlugin()); - } - - @Override - protected void disable() { - instance = null; - } -} diff --git a/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.scala b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.scala new file mode 100644 index 0000000..79f3e3e --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.scala @@ -0,0 +1,96 @@ +package buttondevteam.discordplugin.exceptions + +import buttondevteam.core.ComponentManager +import buttondevteam.discordplugin.exceptions.ExceptionListenerModule.SendException +import buttondevteam.discordplugin.{DPUtils, DiscordPlugin} +import buttondevteam.lib.architecture.Component +import buttondevteam.lib.{TBMCCoreAPI, TBMCExceptionEvent} +import discord4j.core.`object`.entity.channel.{GuildChannel, MessageChannel} +import discord4j.core.`object`.entity.{Guild, Role} +import org.apache.commons.lang.exception.ExceptionUtils +import org.bukkit.Bukkit +import org.bukkit.event.{EventHandler, Listener} +import reactor.core.publisher.Mono + +import java.util.stream.Collectors + +/** + * Listens for errors from the Chroma plugins and posts them to Discord, ignoring repeating errors so it's not that spammy. + */ +object ExceptionListenerModule { + private def SendException(e: Throwable, sourcemessage: String): Unit = { + if (instance == null) return + try getChannel.flatMap((channel: MessageChannel) => { + def foo(channel: MessageChannel) = { + var coderRole: Mono[Role] = channel match { + case ch: GuildChannel => instance.pingRole(ch.getGuild).get + case _ => Mono.empty + } + coderRole.map((role: Role) => if (TBMCCoreAPI.IsTestServer) new StringBuilder + else new StringBuilder(role.getMention).append("\n")).defaultIfEmpty(new StringBuilder).flatMap((sb: StringBuilder) => { + def foo(sb: StringBuilder) = { + sb.append(sourcemessage).append("\n") + sb.append("```").append("\n") + var stackTrace = util.Arrays.stream(ExceptionUtils.getStackTrace(e).split("\\n")).filter((s: String) => !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("```") + channel.createMessage(sb.toString) + } + + foo(sb) + }) + } + + foo(channel) + }).subscribe + catch { + case ex: Exception => + ex.printStackTrace() + } + } + + private var instance: ExceptionListenerModule = null + + def getChannel: Mono[MessageChannel] = { + if (instance != null) return instance.channel.get + Mono.empty + } +} + +class ExceptionListenerModule extends Component[DiscordPlugin] with Listener { + final private val lastthrown = new util.ArrayList[Throwable] + final private val lastsourcemsg = new util.ArrayList[String] + + @EventHandler def onException(e: TBMCExceptionEvent): Unit = { + if (DiscordPlugin.SafeMode || !ComponentManager.isEnabled(getClass)) return + if (lastthrown.stream.anyMatch((ex: Throwable) => util.Arrays.equals(e.getException.getStackTrace, ex.getStackTrace) && (if (e.getException.getMessage == null) ex.getMessage == null + else e.getException.getMessage == ex.getMessage)) // e.Exception.Message==ex.Message && lastsourcemsg.contains(e.getSourceMessage)) { return } + ExceptionListenerModule + .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() + } + + /** + * The channel to post the errors to. + */ + final private val channel = DPUtils.channelData(getConfig, "channel") + + /** + * The role to ping if an error occurs. Set to empty ('') to disable. + */ + private def pingRole(guild: Mono[Guild]) = DPUtils.roleData(getConfig, "pingRole", "Coder", guild) + + override protected def enable(): Unit = { + if (DPUtils.disableIfConfigError(this, channel)) return + ExceptionListenerModule.instance = this + Bukkit.getPluginManager.registerEvents(new ExceptionListenerModule, getPlugin) + TBMCCoreAPI.RegisterEventsForExceptions(new DebugMessageListener, getPlugin) + } + + override protected def disable(): Unit = ExceptionListenerModule.instance = null +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/fun/FunModule.java b/src/main/java/buttondevteam/discordplugin/fun/FunModule.java deleted file mode 100644 index a0997ac..0000000 --- a/src/main/java/buttondevteam/discordplugin/fun/FunModule.java +++ /dev/null @@ -1,162 +0,0 @@ -package buttondevteam.discordplugin.fun; - -import buttondevteam.core.ComponentManager; -import buttondevteam.discordplugin.DPUtils; -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.Guild; -import discord4j.core.object.entity.Member; -import discord4j.core.object.entity.Message; -import discord4j.core.object.entity.Role; -import discord4j.core.object.entity.channel.GuildChannel; -import discord4j.core.object.entity.channel.MessageChannel; -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 reactor.core.publisher.Mono; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Calendar; -import java.util.Random; -import java.util.concurrent.TimeUnit; -import java.util.stream.IntStream; - -/** - * All kinds of random things. - * 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 - }; - - /** - * Questions that the bot will choose a random answer to give to. - */ - private final ConfigData serverReady = 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 final ConfigData> serverReadyAnswers = getConfig().getData("serverReadyAnswers", () -> Lists.newArrayList(serverReadyStrings)); - - private static final Random serverReadyRandom = new Random(); - private static final ArrayList usableServerReadyStrings = new ArrayList<>(0); - - private void createUsableServerReadyStrings() { - IntStream.range(0, serverReadyAnswers.get().size()) - .forEach(i -> FunModule.usableServerReadyStrings.add((short) i)); - } - - @Override - protected void enable() { - registerListener(this); - } - - @Override - protected void disable() { - lastlist = lastlistp = ListC = 0; - } - - private static short lastlist = 0; - private static short lastlistp = 0; - - private static short ListC = 0; - - public static boolean executeMemes(Message message) { - val fm = ComponentManager.getIfEnabled(FunModule.class); - if (fm == null) return false; - String msglowercased = message.getContent().toLowerCase(); - lastlist++; - if (lastlist > 5) { - ListC = 0; - lastlist = 0; - } - if (msglowercased.equals("/list") && Bukkit.getOnlinePlayers().size() == lastlistp && ListC++ > 2) // Lowered already - { - DPUtils.reply(message, Mono.empty(), "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 (!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, Mono.empty(), fm.serverReadyAnswers.get().get(next)).subscribe(); - return false; //Still process it as a command/mcchat if needed - } - return false; - } - - @EventHandler - public void onPlayerJoin(PlayerJoinEvent event) { - ListC = 0; - } - - /** - * If all of the people who have this role are online, the bot will post a full house. - */ - private ConfigData> fullHouseDevRole(Mono guild) { - return DPUtils.roleData(getConfig(), "fullHouseDevRole", "Developer", guild); - } - - - /** - * The channel to post the full house to. - */ - private final ReadOnlyConfigData> fullHouseChannel = DPUtils.channelData(getConfig(), "fullHouseChannel"); - - private static long lasttime = 0; - - public static void handleFullHouse(PresenceUpdateEvent event) { - val fm = ComponentManager.getIfEnabled(FunModule.class); - if (fm == null) return; - 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/fun/FunModule.scala b/src/main/java/buttondevteam/discordplugin/fun/FunModule.scala new file mode 100644 index 0000000..c5f20d1 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/fun/FunModule.scala @@ -0,0 +1,125 @@ +package buttondevteam.discordplugin.fun + +import buttondevteam.core.ComponentManager +import buttondevteam.discordplugin.{DPUtils, DiscordPlugin} +import buttondevteam.lib.TBMCCoreAPI +import buttondevteam.lib.architecture.{Component, ConfigData} +import com.google.common.collect.Lists +import discord4j.common.util.Snowflake +import discord4j.core.`object`.entity.channel.{GuildChannel, MessageChannel} +import discord4j.core.`object`.entity.{Guild, Member, Message, Role} +import discord4j.core.`object`.presence.{Presence, Status} +import discord4j.core.event.domain.PresenceUpdateEvent +import discord4j.core.spec.{EmbedCreateSpec, MessageCreateSpec} +import org.bukkit.Bukkit +import org.bukkit.event.player.PlayerJoinEvent +import org.bukkit.event.{EventHandler, Listener} +import reactor.core.publisher.Mono + +import java.util +import java.util.Calendar +import java.util.concurrent.TimeUnit +import java.util.stream.IntStream +import scala.util.Random + +/** + * All kinds of random things. + * The YEEHAW event uses an emoji named :YEEHAW: if available + */ +object FunModule { + private val serverReadyStrings = Array[String]("in one week from now", // Ali + "between now and the heat-death of the universe.", // Ghostise + "soon™", "ask again this time next month", "in about 3 seconds", // Nicolai + "after we finish 8 plugins", "tomorrow.", "after one tiiiny feature", + "next commit", "after we finish strangling Towny", "when we kill every *fucking* bug", + "once the server stops screaming.", "after HL3 comes out", "next time you ask", + "when will *you* be open?") // Ali + private val serverReadyRandom = new Random + private val usableServerReadyStrings = new java.util.ArrayList[Short](0) + private var lastlist = 0 + private var lastlistp = 0 + private var ListC = 0 + + def executeMemes(message: Message): Boolean = { + val fm = ComponentManager.getIfEnabled(classOf[FunModule]) + if (fm == null) return false + val msglowercased = message.getContent.toLowerCase + lastlist += 1 + if (lastlist > 5) { + ListC = 0 + lastlist = 0 + } + if (msglowercased == "/list" && Bukkit.getOnlinePlayers.size == lastlistp && { + ListC += 1; + ListC - 1 + } > 2) { // Lowered already + DPUtils.reply(message, Mono.empty, "stop it. You know the answer.").subscribe + lastlist = 0 + lastlistp = Bukkit.getOnlinePlayers.size.toShort + return true //Handled + } + lastlistp = Bukkit.getOnlinePlayers.size.toShort //Didn't handle + if (!TBMCCoreAPI.IsTestServer && util.Arrays.stream(fm.serverReady.get.anyMatch(msglowercased.contains)) { + var next = 0 + if (usableServerReadyStrings.size == 0) fm.createUsableServerReadyStrings() + next = usableServerReadyStrings.remove(serverReadyRandom.nextInt(usableServerReadyStrings.size)) + DPUtils.reply(message, Mono.empty, fm.serverReadyAnswers.get.get(next)).subscribe + return false //Still process it as a command/mcchat if needed + } + false + } + + private var lasttime = 0 + + def handleFullHouse(event: PresenceUpdateEvent): Unit = { + val fm = ComponentManager.getIfEnabled(classOf[FunModule]) + if (fm == null) return + if (Calendar.getInstance.get(Calendar.DAY_OF_MONTH) % 5 != 0) return + fm.fullHouseChannel.get.filter((ch: MessageChannel) => ch.isInstanceOf[GuildChannel]) + .flatMap((channel: MessageChannel) => fm.fullHouseDevRole(channel.asInstanceOf[GuildChannel].getGuild).get.filter((role: Role) => event.getOld.map((p: Presence) => p.getStatus == Status.OFFLINE).orElse(false)).filter((role: Role) => !(event.getCurrent.getStatus == Status.OFFLINE)).filterWhen((devrole: Role) => event.getMember.flatMap((m: Member) => m.getRoles.any((r: Role) => r.getId.asLong == devrole.getId.asLong))).filterWhen((devrole: Role) => event.getGuild.flatMapMany((g: Guild) => g.getMembers.filter((m: Member) => m.getRoleIds.stream.anyMatch((s: Snowflake) => s == devrole.getId))).flatMap(Member.getPresence).all((pr: Presence) => !(pr.getStatus == Status.OFFLINE))).filter((devrole: Role) => lasttime + 10 < TimeUnit.NANOSECONDS.toHours(System.nanoTime)).flatMap //This should stay so it checks this last + ((devrole: Role) => { + def foo(devrole: Role) = { + lasttime = TimeUnit.NANOSECONDS.toHours(System.nanoTime) + channel.createMessage((mcs: MessageCreateSpec) => mcs.setContent("Full house!").setEmbed((ecs: EmbedCreateSpec) => ecs.setImage("https://cdn.discordapp.com/attachments/249295547263877121/249687682618359808/poker-hand-full-house-aces-kings-playing-cards-15553791.png"))) + } + + foo(devrole) + })).subscribe + } +} + +class FunModule extends Component[DiscordPlugin] with Listener { + /** + * Questions that the bot will choose a random answer to give to. + */ + final private val serverReady: ConfigData[Array[String]] = + getConfig.getData("serverReady", () => Array[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. + */ + final private val serverReadyAnswers: ConfigData[util.ArrayList[String]] = + getConfig.getData("serverReadyAnswers", () => Lists.newArrayList(FunModule.serverReadyStrings)) + + private def createUsableServerReadyStrings(): Unit = + IntStream.range(0, serverReadyAnswers.get.size).forEach((i: Int) => FunModule.usableServerReadyStrings.add(i.toShort)) + + override protected def enable(): Unit = registerListener(this) + + override protected def disable(): Unit = FunModule.lastlist = FunModule.lastlistp = FunModule.ListC = 0 + + @EventHandler def onPlayerJoin(event: PlayerJoinEvent): Unit = FunModule.ListC = 0 + + /** + * If all of the people who have this role are online, the bot will post a full house. + */ + private def fullHouseDevRole(guild: Mono[Guild]) = DPUtils.roleData(getConfig, "fullHouseDevRole", "Developer", guild) + + /** + * The channel to post the full house to. + */ + final private val fullHouseChannel = DPUtils.channelData(getConfig, "fullHouseChannel") +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.scala b/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.scala index a810307..8f41efe 100644 --- a/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.scala +++ b/src/main/java/buttondevteam/discordplugin/listeners/CommandListener.scala @@ -1,12 +1,12 @@ package buttondevteam.discordplugin.listeners -import buttondevteam.discordplugin.{DPUtils, DiscordPlugin} import buttondevteam.discordplugin.commands.Command2DCSender +import buttondevteam.discordplugin.{DPUtils, DiscordPlugin} import buttondevteam.lib.TBMCCoreAPI import discord4j.common.util.Snowflake import discord4j.core.`object`.entity.channel.{MessageChannel, PrivateChannel} import discord4j.core.`object`.entity.{Member, Message, Role, User} -import reactor.core.publisher.Mono +import reactor.core.publisher.{Flux, Mono} import java.util.concurrent.atomic.AtomicBoolean @@ -39,7 +39,7 @@ object CommandListener { val gotmention = new AtomicBoolean timings.printElapsed("Before self") tmp.flatMapMany((x: Boolean) => DiscordPlugin.dc.getSelf.flatMap((self: User) => self.asMember(DiscordPlugin.mainServer.getId)).flatMapMany((self: Member) => { - def foo(self: Member) = { + def foo(self: Member): Flux[String] = { timings.printElapsed("D") gotmention.set(checkanddeletemention(cmdwithargs, self.getMention, message)) gotmention.set(checkanddeletemention(cmdwithargs, self.getNicknameMention, message) || gotmention.get) @@ -49,14 +49,14 @@ object CommandListener { foo(self) }).map((mentionRole: String) => { - def foo(mentionRole: String) = { + def foo(mentionRole: String): Boolean = { timings.printElapsed("E") gotmention.set(checkanddeletemention(cmdwithargs, mentionRole, message) || gotmention.get) // Delete all mentions !mentionedonly || gotmention.get //Stops here if false } foo(mentionRole) - }).switchIfEmpty(Mono.fromSupplier(() => !mentionedonly || gotmention.get))).filter((b: Boolean) => b).last(false).filter((b: Boolean) => b).doOnNext((b: Boolean) => channel.`type`.subscribe).flatMap((b: Boolean) => { + }: Boolean)[Mono[Boolean]].switchIfEmpty(Mono.fromSupplier[Boolean](() => !mentionedonly || gotmention.get)))[Mono[Boolean]].filter((b: Boolean) => b).last(false).filter((b: Boolean) => b).doOnNext((b: Boolean) => channel.`type`.subscribe).flatMap((b: Boolean) => { def foo(): Mono[Boolean] = { val cmdwithargsString = cmdwithargs.toString try { diff --git a/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.java b/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.java deleted file mode 100755 index 57985aa..0000000 --- a/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.java +++ /dev/null @@ -1,88 +0,0 @@ -package buttondevteam.discordplugin.listeners; - -import buttondevteam.discordplugin.DPUtils; -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.channel.PrivateChannel; -import lombok.val; -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) - - MessageReceivedEvent: - - v CommandListener (starts with mention, in #bot or a connected chat) - - Minecraft chat (is enabled in the channel and message isn't [/]mcchat) - - CommandListener (with the correct prefix in #bot, or in private) - */ - 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(); - return 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().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(), commandChannel, 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.just(true); //Wasn't handled, continue - }).filterWhen(ch -> { - timings.printElapsed("Run command 2"); - return CommandListener.runCommand(event.getMessage(), commandChannel, false); - }); - }).onErrorContinue((err, obj) -> TBMCCoreAPI.SendException("An error occured while handling a message!", err, DiscordPlugin.plugin)) - .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; - - public static void debug(String debug) { - if (CommonListeners.debug) //Debug - DPUtils.getLogger().info(debug); - } - - public static boolean debug() { - return debug = !debug; - } -} diff --git a/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.scala b/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.scala new file mode 100644 index 0000000..afb28f2 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.scala @@ -0,0 +1,92 @@ +package buttondevteam.discordplugin.listeners + +import buttondevteam.discordplugin.fun.FunModule +import buttondevteam.discordplugin.mcchat.MinecraftChatModule +import buttondevteam.discordplugin.role.GameRoleModule +import buttondevteam.discordplugin.util.Timings +import buttondevteam.discordplugin.{DPUtils, DiscordPlugin} +import buttondevteam.lib.TBMCCoreAPI +import buttondevteam.lib.architecture.Component +import discord4j.core.`object`.entity.channel.{MessageChannel, PrivateChannel} +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, RoleDeleteEvent, RoleUpdateEvent} +import reactor.core.publisher.Mono + +object CommonListeners { + val timings = new Timings + + /* + MentionEvent: + - CommandListener (starts with mention, only 'channelcon' and not in #bot) + + MessageReceivedEvent: + - v CommandListener (starts with mention, in #bot or a connected chat) + - Minecraft chat (is enabled in the channel and message isn't [/]mcchat) + - CommandListener (with the correct prefix in #bot, or in private) + */ + def register(dispatcher: EventDispatcher) = { + dispatcher.on(classOf[MessageCreateEvent]).flatMap((event: MessageCreateEvent) => { + def foo(event: MessageCreateEvent) = { + 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 + event.getMessage.getChannel.map((mch: MessageChannel) => (commandChannel != null && mch.getId.asLong == commandChannel.asLong) //If mentioned, that's higher than chat + || mch.isInstanceOf[PrivateChannel] || event.getMessage.getContent.contains("channelcon")).flatMap( + (shouldRun: Boolean) => { //Only 'channelcon' is allowed in other channels + def foo(shouldRun: Boolean): Mono[Boolean] = { //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") + CommandListener.runCommand(event.getMessage, commandChannel, mentionedonly = true) //#bot is handled here + } + + foo(shouldRun) + }).filterWhen((ch: Any) => { + def foo(): Mono[Boolean] = { + timings.printElapsed("mcchat") + val mcchat = Component.getComponents.get(classOf[MinecraftChatModule]) + if (mcchat != null && mcchat.isEnabled) { //ComponentManager.isEnabled() searches the component again + return mcchat.asInstanceOf[MinecraftChatModule].getListener.handleDiscord(event) //Also runs Discord commands in chat channels + } + Mono.just(true) //Wasn't handled, continue + } + + foo() + }).filterWhen((ch: Any) => { + def foo(ch: Any) = { + timings.printElapsed("Run command 2") + CommandListener.runCommand(event.getMessage, commandChannel, mentionedonly = false) + } + + foo(ch) + }) + } + + foo(event) + }).onErrorContinue((err: Throwable, obj: Any) => TBMCCoreAPI.SendException("An error occured while handling a message!", err, DiscordPlugin.plugin)).subscribe + dispatcher.on(classOf[PresenceUpdateEvent]).subscribe((event: PresenceUpdateEvent) => { + def foo(event: PresenceUpdateEvent) = { + if (DiscordPlugin.SafeMode) return + FunModule.handleFullHouse(event) + } + + foo(event) + }) + dispatcher.on(classOf[RoleCreateEvent]).subscribe(GameRoleModule.handleRoleEvent _) + dispatcher.on(classOf[RoleDeleteEvent]).subscribe(GameRoleModule.handleRoleEvent _) + dispatcher.on(classOf[RoleUpdateEvent]).subscribe(GameRoleModule.handleRoleEvent _) + } + + private var debug = false + + def debug(debug: String): Unit = if (CommonListeners.debug) { //Debug + DPUtils.getLogger.info(debug) + } + + def debug(): Unit = debug = !debug +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java deleted file mode 100755 index febe7ea..0000000 --- a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.java +++ /dev/null @@ -1,68 +0,0 @@ -package buttondevteam.discordplugin.listeners; - -import buttondevteam.discordplugin.DiscordPlayer; -import buttondevteam.discordplugin.DiscordPlugin; -import buttondevteam.discordplugin.commands.ConnectCommand; -import buttondevteam.discordplugin.mcchat.MinecraftChatModule; -import buttondevteam.discordplugin.util.DPState; -import buttondevteam.lib.ScheduledServerRestartEvent; -import buttondevteam.lib.player.TBMCPlayerGetInfoEvent; -import discord4j.common.util.Snowflake; -import discord4j.core.object.entity.Member; -import discord4j.core.object.entity.User; -import lombok.val; -import org.bukkit.event.EventHandler; -import org.bukkit.event.Listener; -import org.bukkit.event.player.PlayerJoinEvent; -import reactor.core.publisher.Mono; - -public class MCListener implements Listener { - @EventHandler - public void onPlayerJoin(PlayerJoinEvent e) { - if (ConnectCommand.WaitingToConnect.containsKey(e.getPlayer().getName())) { - @SuppressWarnings("ConstantConditions") User user = DiscordPlugin.dc - .getUserById(Snowflake.of(ConnectCommand.WaitingToConnect.get(e.getPlayer().getName()))).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; - val userOpt = DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID())).onErrorResume(t -> Mono.empty()).blockOptional(); - if (!userOpt.isPresent()) return; - User user = userOpt.get(); - e.addInfo("Discord tag: " + user.getUsername() + "#" + user.getDiscriminator()); - val memberOpt = user.asMember(DiscordPlugin.mainServer.getId()).onErrorResume(t -> Mono.empty()).blockOptional(); - if (!memberOpt.isPresent()) return; - Member member = memberOpt.get(); - val prOpt = member.getPresence().blockOptional(); - if (!prOpt.isPresent()) return; - val pr = prOpt.get(); - e.addInfo(pr.getStatus().toString()); - if (pr.getActivity().isPresent()) { - val activity = pr.getActivity().get(); - e.addInfo(activity.getType() + ": " + activity.getName()); - } - } - - /*@EventHandler - public void onCommandPreprocess(TBMCCommandPreprocessEvent e) { - if (e.getMessage().equalsIgnoreCase("/stop")) - MinecraftChatModule.state = DPState.STOPPING_SERVER; - else if (e.getMessage().equalsIgnoreCase("/restart")) - MinecraftChatModule.state = DPState.RESTARTING_SERVER; - }*/ - - @EventHandler //We don't really need this with the logger stuff but hey - public void onScheduledRestart(ScheduledServerRestartEvent e) { - MinecraftChatModule.state = DPState.RESTARTING_SERVER; - } -} diff --git a/src/main/java/buttondevteam/discordplugin/listeners/MCListener.scala b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.scala new file mode 100644 index 0000000..d464d84 --- /dev/null +++ b/src/main/java/buttondevteam/discordplugin/listeners/MCListener.scala @@ -0,0 +1,54 @@ +package buttondevteam.discordplugin.listeners + +import buttondevteam.discordplugin.commands.ConnectCommand +import buttondevteam.discordplugin.mcchat.MinecraftChatModule +import buttondevteam.discordplugin.util.DPState +import buttondevteam.discordplugin.{DiscordPlayer, DiscordPlugin} +import buttondevteam.lib.ScheduledServerRestartEvent +import buttondevteam.lib.player.TBMCPlayerGetInfoEvent +import discord4j.common.util.Snowflake +import org.bukkit.event.player.PlayerJoinEvent +import org.bukkit.event.{EventHandler, Listener} +import reactor.core.publisher.Mono + +class MCListener extends Listener { + @EventHandler def onPlayerJoin(e: PlayerJoinEvent): Unit = + if (ConnectCommand.WaitingToConnect.containsKey(e.getPlayer.getName)) { + @SuppressWarnings(Array("ConstantConditions")) val user = DiscordPlugin.dc.getUserById(Snowflake.of(ConnectCommand.WaitingToConnect.get(e.getPlayer.getName))).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 def onGetInfo(e: TBMCPlayerGetInfoEvent): Unit = { + if (DiscordPlugin.SafeMode) return + val dp = e.getPlayer.getAs(classOf[DiscordPlayer]) + if (dp == null || dp.getDiscordID == null || dp.getDiscordID == "") return + val userOpt = DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID)).onErrorResume(_ => Mono.empty).blockOptional + if (!userOpt.isPresent) return + val user = userOpt.get + e.addInfo("Discord tag: " + user.getUsername + "#" + user.getDiscriminator) + val memberOpt = user.asMember(DiscordPlugin.mainServer.getId).onErrorResume((t: Throwable) => Mono.empty).blockOptional + if (!memberOpt.isPresent) return + val member = memberOpt.get + val prOpt = member.getPresence.blockOptional + if (!prOpt.isPresent) return + val pr = prOpt.get + e.addInfo(pr.getStatus.toString) + if (pr.getActivity.isPresent) { + val activity = pr.getActivity.get + e.addInfo(activity.getType + ": " + activity.getName) + } + } + + /*@EventHandler + public void onCommandPreprocess(TBMCCommandPreprocessEvent e) { + if (e.getMessage().equalsIgnoreCase("/stop")) + MinecraftChatModule.state = DPState.STOPPING_SERVER; + else if (e.getMessage().equalsIgnoreCase("/restart")) + MinecraftChatModule.state = DPState.RESTARTING_SERVER; + }*/ @EventHandler //We don't really need this with the logger stuff but hey + def onScheduledRestart(e: ScheduledServerRestartEvent): Unit = + MinecraftChatModule.state = DPState.RESTARTING_SERVER + +} \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java b/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java index 190c10e..b7cc116 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.java @@ -3,8 +3,6 @@ package buttondevteam.discordplugin.mcchat; import buttondevteam.core.component.channel.Channel; import buttondevteam.core.component.channel.ChatRoom; import buttondevteam.discordplugin.*; -import buttondevteam.discordplugin.commands.Command2DCSender; -import buttondevteam.discordplugin.commands.ICommand2DC; import buttondevteam.lib.TBMCSystemChatEvent; import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.CommandClass; diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.java index df7f6ad..0f60db5 100755 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.java @@ -3,8 +3,6 @@ package buttondevteam.discordplugin.mcchat; import buttondevteam.discordplugin.DPUtils; import buttondevteam.discordplugin.DiscordPlayer; import buttondevteam.discordplugin.DiscordPlugin; -import buttondevteam.discordplugin.commands.Command2DCSender; -import buttondevteam.discordplugin.commands.ICommand2DC; import buttondevteam.lib.chat.Command2; import buttondevteam.lib.chat.CommandClass; import discord4j.core.object.entity.channel.PrivateChannel; diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java index 007b90c..3c257ad 100755 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.java @@ -3,7 +3,6 @@ package buttondevteam.discordplugin.mcchat; import buttondevteam.core.ComponentManager; import buttondevteam.discordplugin.*; import buttondevteam.discordplugin.listeners.CommandListener; -import buttondevteam.discordplugin.listeners.CommonListeners; import buttondevteam.discordplugin.playerfaker.VanillaCommandListener; import buttondevteam.discordplugin.playerfaker.VanillaCommandListener14; import buttondevteam.discordplugin.playerfaker.VanillaCommandListener15; diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java index b90a328..4319d3a 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.java @@ -3,7 +3,6 @@ package buttondevteam.discordplugin.mcchat; import buttondevteam.core.ComponentManager; import buttondevteam.core.MainPlugin; import buttondevteam.discordplugin.*; -import buttondevteam.discordplugin.broadcaster.GeneralEventBroadcasterModule; import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCSystemChatEvent; import com.google.common.collect.Sets; diff --git a/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java b/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java index 35e78a7..91eb7ee 100644 --- a/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java +++ b/src/main/java/buttondevteam/discordplugin/mccommands/DiscordMCCommand.java @@ -4,8 +4,6 @@ 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.discordplugin.mcchat.MinecraftChatModule; import buttondevteam.discordplugin.util.DPState; diff --git a/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java b/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java index 07fd0e2..aac0aae 100755 --- a/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java +++ b/src/main/java/buttondevteam/discordplugin/role/RoleCommand.java @@ -1,8 +1,6 @@ package buttondevteam.discordplugin.role; 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; diff --git a/src/main/java/buttondevteam/discordplugin/util/Timings.java b/src/main/java/buttondevteam/discordplugin/util/Timings.java index 12c12f2..af91b0f 100644 --- a/src/main/java/buttondevteam/discordplugin/util/Timings.java +++ b/src/main/java/buttondevteam/discordplugin/util/Timings.java @@ -1,7 +1,5 @@ package buttondevteam.discordplugin.util; -import buttondevteam.discordplugin.listeners.CommonListeners; - public class Timings { private long start;