Convert some more classes to Scala

Actually, a lot of them
This commit is contained in:
Norbi Peti 2021-02-25 01:44:43 +01:00
parent 261725dc0f
commit 428361c46c
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
40 changed files with 955 additions and 1146 deletions

View file

@ -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<DiscordPlugin> {
/**
* Channel to post new posts.
*/
public final ReadOnlyConfigData<Mono<MessageChannel>> channel = DPUtils.channelData(getConfig(), "channel");
/**
* Channel where distinguished (moderator) posts go.
*/
private final ReadOnlyConfigData<Mono<MessageChannel>> modChannel = DPUtils.channelData(getConfig(), "modChannel");
/**
* Automatically unpins all messages except the last few. Set to 0 or >50 to disable
*/
private final ConfigData<Short> keepPinned = getConfig().getData("keepPinned", (short) 40);
private final ConfigData<Long> lastAnnouncementTime = getConfig().getData("lastAnnouncementTime", 0L);
private final ConfigData<Long> lastSeenTime = getConfig().getData("lastSeenTime", 0L);
/**
* The subreddit to pull the posts from
*/
private final ConfigData<String> 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<Message> 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();
}
}
}
}

View file

@ -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()
}
}
}

View file

@ -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<DiscordPlugin> {
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) {
}
}
}

View file

@ -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 =>
}
}

View file

@ -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<MethodHandles.Lookup> 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;
}
}

View file

@ -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
}
}

View file

@ -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<ICommand2DC, Command2DCSender> {
@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;
}
}

View file

@ -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
}
}

View file

@ -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");
}
}

View file

@ -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")
}

View file

@ -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<br>
* Value: Discord ID
*/
public static HashBiMap<String, String> 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;
}
}

View file

@ -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<br>
* 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
}
}

View file

@ -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;
}
}

View file

@ -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
}
}

View file

@ -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;
}
}

View file

@ -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
}
}

View file

@ -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<Command2DCSender> {
public <T extends ICommand2> 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;
}

View file

@ -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
}

View file

@ -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<User> 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<User> 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<User> getUsers(Message message, String args) {
final List<User> 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;
}
}

View file

@ -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
}
}

View file

@ -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() //
};
}
}

View file

@ -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
}
}

View file

@ -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<MessageChannel> 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();
}
}
}

View file

@ -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()
}
}

View file

@ -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<DiscordPlugin> implements Listener {
private final List<Throwable> lastthrown = new ArrayList<>();
private final List<String> 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<Role> 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<MessageChannel> getChannel() {
if (instance != null) return instance.channel.get();
return Mono.empty();
}
/**
* The channel to post the errors to.
*/
private final ReadOnlyConfigData<Mono<MessageChannel>> channel = DPUtils.channelData(getConfig(), "channel");
/**
* The role to ping if an error occurs. Set to empty ('') to disable.
*/
private ConfigData<Mono<Role>> pingRole(Mono<Guild> 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;
}
}

View file

@ -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
}

View file

@ -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<DiscordPlugin> 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<String[]> 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<ArrayList<String>> serverReadyAnswers = getConfig().getData("serverReadyAnswers", () -> Lists.newArrayList(serverReadyStrings));
private static final Random serverReadyRandom = new Random();
private static final ArrayList<Short> 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<Mono<Role>> fullHouseDevRole(Mono<Guild> guild) {
return DPUtils.roleData(getConfig(), "fullHouseDevRole", "Developer", guild);
}
/**
* The channel to post the full house to.
*/
private final ReadOnlyConfigData<Mono<MessageChannel>> 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();
}
}

View file

@ -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")
}

View file

@ -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 {

View file

@ -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;
}
}

View file

@ -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
}

View file

@ -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;
}
}

View file

@ -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
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -1,7 +1,5 @@
package buttondevteam.discordplugin.util;
import buttondevteam.discordplugin.listeners.CommonListeners;
public class Timings {
private long start;