Convert some more classes to Scala
Actually, a lot of them
This commit is contained in:
parent
261725dc0f
commit
428361c46c
40 changed files with 955 additions and 1146 deletions
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 =>
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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() //
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
125
src/main/java/buttondevteam/discordplugin/fun/FunModule.scala
Normal file
125
src/main/java/buttondevteam/discordplugin/fun/FunModule.scala
Normal 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")
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package buttondevteam.discordplugin.util;
|
||||
|
||||
import buttondevteam.discordplugin.listeners.CommonListeners;
|
||||
|
||||
public class Timings {
|
||||
private long start;
|
||||
|
||||
|
|
Loading…
Reference in a new issue