Merge pull request #86 from TBMCPlugins/dev

Reorganized lots of stuff (components)
This commit is contained in:
Norbi Peti 2019-01-03 21:39:52 +01:00 committed by GitHub
commit db743dcd50
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 1159 additions and 873 deletions

27
pom.xml
View file

@ -106,6 +106,9 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<branch>
master
</branch> <!-- Should be master if building ButtonCore locally - the CI will overwrite it (see below) -->
</properties> </properties>
<repositories> <repositories>
@ -159,10 +162,15 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/com.discord4j/Discord4J --> <!-- https://mvnrepository.com/artifact/com.discord4j/Discord4J -->
<dependency> <!-- <dependency>
<groupId>com.discord4j</groupId> <groupId>com.discord4j</groupId>
<artifactId>Discord4J</artifactId> <artifactId>Discord4J</artifactId>
<version>2.10.1</version> <version>2.10.1</version>
</dependency> -->
<dependency>
<groupId>com.github.SizableShrimp</groupId>
<artifactId>Discord4J</artifactId>
<version>httprequestchange-SNAPSHOT</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-jdk14 --> <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-jdk14 -->
<dependency> <dependency>
@ -173,7 +181,7 @@
<dependency> <dependency>
<groupId>com.github.TBMCPlugins.ButtonCore</groupId> <groupId>com.github.TBMCPlugins.ButtonCore</groupId>
<artifactId>ButtonCore</artifactId> <artifactId>ButtonCore</artifactId>
<version>master-SNAPSHOT</version> <version>${branch}-SNAPSHOT</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency> <dependency>
@ -225,4 +233,19 @@
<version>4.0.0</version> <version>4.0.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
<profiles>
<profile>
<id>ci</id>
<activation>
<property>
<name>env.TRAVIS_BRANCH</name>
</property>
</activation>
<properties>
<!-- Override only if necessary -->
<branch>${env.TRAVIS_BRANCH}</branch>
</properties>
</profile>
</profiles>
</project> </project>

View file

@ -0,0 +1,27 @@
package buttondevteam.discordplugin;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
@RequiredArgsConstructor
public class AsyncDiscordEvent<T extends sx.blah.discord.api.events.Event> extends Event implements Cancellable {
private final @Getter T event;
@Getter
@Setter
private boolean cancelled;
private static final HandlerList handlers = new HandlerList();
@Override
public HandlerList getHandlers() {
return handlers;
}
public static HandlerList getHandlerList() {
return handlers;
}
}

View file

@ -1,8 +1,7 @@
package buttondevteam.discordplugin; package buttondevteam.discordplugin;
import buttondevteam.discordplugin.listeners.MCChatListener; import buttondevteam.discordplugin.mcchat.MCChatUtils;
import lombok.Getter; import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitScheduler;
import sx.blah.discord.api.internal.json.objects.EmbedObject; import sx.blah.discord.api.internal.json.objects.EmbedObject;
@ -11,8 +10,6 @@ import sx.blah.discord.util.EmbedBuilder;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.awt.*; import java.awt.*;
import java.util.Arrays;
import java.util.stream.Collectors;
public class ChromaBot { public class ChromaBot {
/** /**
@ -43,7 +40,7 @@ public class ChromaBot {
* The message to send, duh * The message to send, duh
*/ */
public void sendMessage(String message) { public void sendMessage(String message) {
MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message)); MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message));
} }
/** /**
@ -55,7 +52,7 @@ public class ChromaBot {
* Custom fancy stuff, use {@link EmbedBuilder} to create one * Custom fancy stuff, use {@link EmbedBuilder} to create one
*/ */
public void sendMessage(String message, EmbedObject embed) { public void sendMessage(String message, EmbedObject embed) {
MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, embed)); MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, embed));
} }
/** /**
@ -66,7 +63,7 @@ public class ChromaBot {
* @param toggle The toggle type for channelcon * @param toggle The toggle type for channelcon
*/ */
public void sendMessageCustomAsWell(String message, EmbedObject embed, @Nullable ChannelconBroadcast toggle) { public void sendMessageCustomAsWell(String message, EmbedObject embed, @Nullable ChannelconBroadcast toggle) {
MCChatListener.forCustomAndAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, embed), toggle, false); MCChatUtils.forCustomAndAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, embed), toggle, false);
} }
/** /**
@ -92,7 +89,7 @@ public class ChromaBot {
* The color of the line before the text * The color of the line before the text
*/ */
public void sendMessage(String message, Color color) { public void sendMessage(String message, Color color) {
MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message,
new EmbedBuilder().withTitle(message).withColor(color).build())); new EmbedBuilder().withTitle(message).withColor(color).build()));
} }
@ -107,7 +104,7 @@ public class ChromaBot {
* The name of the Minecraft player who is the author of this message * The name of the Minecraft player who is the author of this message
*/ */
public void sendMessage(String message, Color color, String mcauthor) { public void sendMessage(String message, Color color, String mcauthor) {
MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message,
DPUtils.embedWithHead(new EmbedBuilder().withTitle(message).withColor(color), mcauthor).build())); DPUtils.embedWithHead(new EmbedBuilder().withTitle(message).withColor(color), mcauthor).build()));
} }
@ -124,7 +121,7 @@ public class ChromaBot {
* The URL of the avatar image for this message's author * The URL of the avatar image for this message's author
*/ */
public void sendMessage(String message, Color color, String authorname, String authorimg) { public void sendMessage(String message, Color color, String authorname, String authorimg) {
MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, new EmbedBuilder() MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, new EmbedBuilder()
.withTitle(message).withColor(color).withAuthorName(authorname).withAuthorIcon(authorimg).build())); .withTitle(message).withColor(color).withAuthorName(authorname).withAuthorIcon(authorimg).build()));
} }
@ -139,20 +136,11 @@ public class ChromaBot {
* The player who sends this message * The player who sends this message
*/ */
public void sendMessage(String message, Color color, Player sender) { public void sendMessage(String message, Color color, Player sender) {
MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, DPUtils MCChatUtils.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannel(ch, message, DPUtils
.embedWithHead(new EmbedBuilder().withTitle(message).withColor(color), sender.getName()).build())); .embedWithHead(new EmbedBuilder().withTitle(message).withColor(color), sender.getName()).build()));
} }
public void updatePlayerList() { public void updatePlayerList() {
DPUtils.performNoWait(() -> { MCChatUtils.updatePlayerList();
String[] s = DiscordPlugin.chatchannel.getTopic().split("\\n----\\n");
if (s.length < 3)
return;
s[0] = Bukkit.getOnlinePlayers().size() + " player" + (Bukkit.getOnlinePlayers().size() != 1 ? "s" : "")
+ " online";
s[s.length - 1] = "Players: " + Bukkit.getOnlinePlayers().stream()
.map(p -> DPUtils.sanitizeString(p.getDisplayName())).collect(Collectors.joining(", "));
DiscordPlugin.chatchannel.changeTopic(Arrays.stream(s).collect(Collectors.joining("\n----\n")));
});
} }
} }

View file

@ -9,6 +9,7 @@ import sx.blah.discord.util.RequestBuffer.IVoidRequest;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import java.util.regex.Matcher; import java.util.regex.Matcher;
public final class DPUtils { public final class DPUtils {
@ -96,4 +97,10 @@ public final class DPUtils {
return message.replaceAll("([*_~])", Matcher.quoteReplacement("\\")+"$1"); return message.replaceAll("([*_~])", Matcher.quoteReplacement("\\")+"$1");
} }
public static Logger getLogger() {
if (DiscordPlugin.plugin == null || DiscordPlugin.plugin.getLogger() == null)
return Logger.getLogger("DiscordPlugin");
return DiscordPlugin.plugin.getLogger();
}
} }

View file

@ -1,6 +1,6 @@
package buttondevteam.discordplugin; package buttondevteam.discordplugin;
import buttondevteam.discordplugin.listeners.MCChatListener; import buttondevteam.discordplugin.mcchat.MCChatPrivate;
import buttondevteam.lib.player.ChromaGamerBase; import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.UserClass; import buttondevteam.lib.player.UserClass;
@ -20,9 +20,9 @@ public class DiscordPlayer extends ChromaGamerBase {
/** /**
* Returns true if player has the private Minecraft chat enabled. For setting the value, see * Returns true if player has the private Minecraft chat enabled. For setting the value, see
* {@link MCChatListener#privateMCChat(sx.blah.discord.handle.obj.IChannel, boolean, sx.blah.discord.handle.obj.IUser, DiscordPlayer)} * {@link MCChatPrivate#privateMCChat(sx.blah.discord.handle.obj.IChannel, boolean, sx.blah.discord.handle.obj.IUser, DiscordPlayer)}
*/ */
public boolean isMinecraftChatEnabled() { public boolean isMinecraftChatEnabled() {
return MCChatListener.isMinecraftChatEnabled(this); return MCChatPrivate.isMinecraftChatEnabled(this);
} }
} }

View file

@ -1,13 +1,17 @@
package buttondevteam.discordplugin; package buttondevteam.discordplugin;
import buttondevteam.discordplugin.broadcaster.GeneralEventBroadcasterModule;
import buttondevteam.discordplugin.commands.DiscordCommandBase; import buttondevteam.discordplugin.commands.DiscordCommandBase;
import buttondevteam.discordplugin.listeners.CommandListener; import buttondevteam.discordplugin.exceptions.ExceptionListenerModule;
import buttondevteam.discordplugin.listeners.ExceptionListener; import buttondevteam.discordplugin.listeners.CommonListeners;
import buttondevteam.discordplugin.listeners.MCChatListener;
import buttondevteam.discordplugin.listeners.MCListener; import buttondevteam.discordplugin.listeners.MCListener;
import buttondevteam.discordplugin.mcchat.*;
import buttondevteam.discordplugin.mccommands.DiscordMCCommandBase; import buttondevteam.discordplugin.mccommands.DiscordMCCommandBase;
import buttondevteam.discordplugin.mccommands.ResetMCCommand; import buttondevteam.discordplugin.mccommands.ResetMCCommand;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.ButtonPlugin;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
import buttondevteam.lib.chat.Channel; import buttondevteam.lib.chat.Channel;
import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.player.ChromaGamerBase; import buttondevteam.lib.player.ChromaGamerBase;
@ -20,9 +24,7 @@ import lombok.val;
import net.milkbowl.vault.permission.Permission; import net.milkbowl.vault.permission.Permission;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.plugin.RegisteredServiceProvider; import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import sx.blah.discord.api.ClientBuilder; import sx.blah.discord.api.ClientBuilder;
import sx.blah.discord.api.IDiscordClient; import sx.blah.discord.api.IDiscordClient;
@ -44,29 +46,28 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> { public class DiscordPlugin extends ButtonPlugin implements IListener<ReadyEvent> {
private static final String SubredditURL = "https://www.reddit.com/r/ChromaGamers"; private static final String SubredditURL = "https://www.reddit.com/r/ChromaGamers";
private static boolean stop = false; private static boolean stop = false;
public static IDiscordClient dc; public static IDiscordClient dc;
public static DiscordPlugin plugin; public static DiscordPlugin plugin;
public static boolean SafeMode = true; public static boolean SafeMode = true;
public static List<String> GameRoles; public static List<String> GameRoles;
public static boolean hooked = false;
@SuppressWarnings("unchecked") public ConfigData<Character> Prefix() {
return getData("prefix", '/');
}
public static char getPrefix() {
if (plugin == null) return '/';
return plugin.Prefix().get();
}
@Override @Override
public void onEnable() { public void pluginEnable() {
stop = false; //If not the first time stop = false; //If not the first time
try { try {
Bukkit.getLogger().info("Initializing DiscordPlugin..."); Bukkit.getLogger().info("Initializing DiscordPlugin...");
try {
PlayerListWatcher.hookUp();
hooked = true;
Bukkit.getLogger().info("Finished hooking into the player list");
} catch (Throwable e) {
e.printStackTrace();
Bukkit.getLogger().warning("Couldn't hook into the player list!");
}
plugin = this; plugin = this;
lastannouncementtime = getConfig().getLong("lastannouncementtime"); lastannouncementtime = getConfig().getLong("lastannouncementtime");
lastseentime = getConfig().getLong("lastseentime"); lastseentime = getConfig().getLong("lastseentime");
@ -156,7 +157,7 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
val toggles = chcon.getInt("toggles"); val toggles = chcon.getInt("toggles");
if (!mcch.isPresent() || ch == null || user == null || groupid == null) if (!mcch.isPresent() || ch == null || user == null || groupid == null)
continue; continue;
MCChatListener.addCustomChat(ch, groupid, mcch.get(), user, dcp, toggles); MCChatCustom.addCustomChat(ch, groupid, mcch.get(), user, dcp, toggles);
} }
} }
@ -212,21 +213,16 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
}*/ }*/
} }
}, 0, 10); }, 0, 10);
for (IListener<?> listener : CommandListener.getListeners()) for (IListener<?> listener : CommonListeners.getListeners())
dc.getDispatcher().registerListener(listener); dc.getDispatcher().registerListener(listener);
MCChatListener mcchat = new MCChatListener(); Component.registerComponent(this, new GeneralEventBroadcasterModule());
dc.getDispatcher().registerListener(mcchat); Component.registerComponent(this, new MinecraftChatModule());
TBMCCoreAPI.RegisterEventsForExceptions(mcchat, this); Component.registerComponent(this, new ExceptionListenerModule());
Bukkit.getPluginManager().registerEvents(new ExceptionListener(), this);
TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), this); TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), this);
TBMCChatAPI.AddCommands(this, DiscordMCCommandBase.class); TBMCChatAPI.AddCommands(this, DiscordMCCommandBase.class);
TBMCCoreAPI.RegisterUserClass(DiscordPlayer.class); TBMCCoreAPI.RegisterUserClass(DiscordPlayer.class);
ChromaGamerBase.addConverter(sender -> { ChromaGamerBase.addConverter(sender -> Optional.ofNullable(sender instanceof DiscordSenderBase
//System.out.println("Discord converter queried: "+sender+" "+sender.getName()); - TODO: Remove ? ((DiscordSenderBase) sender).getChromaUser() : null));
//System.out.println(((DiscordSenderBase) sender).getChromaUser().channel().get().ID); //TODO: TMP
return Optional.ofNullable(sender instanceof DiscordSenderBase
? ((DiscordSenderBase) sender).getChromaUser() : null);
});
new Thread(this::AnnouncementGetterThreadMethod).start(); new Thread(this::AnnouncementGetterThreadMethod).start();
setupProviders(); setupProviders();
} catch (Exception e) { } catch (Exception e) {
@ -248,17 +244,14 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
public static boolean Restart; public static boolean Restart;
@Override @Override
public void onDisable() { public void pluginDisable() {
stop = true; stop = true;
for (val entry : MCChatListener.ConnectedSenders.entrySet()) MCChatPrivate.logoutAll();
for (val valueEntry : entry.getValue().entrySet())
MCListener.callEventExcludingSome(new PlayerQuitEvent(valueEntry.getValue(), ""));
MCChatListener.ConnectedSenders.clear();
getConfig().set("lastannouncementtime", lastannouncementtime); getConfig().set("lastannouncementtime", lastannouncementtime);
getConfig().set("lastseentime", lastseentime); getConfig().set("lastseentime", lastseentime);
getConfig().set("serverup", false); getConfig().set("serverup", false);
val chcons = MCChatListener.getCustomChats(); val chcons = MCChatCustom.getCustomChats();
val chconsc = getConfig().createSection("chcons"); val chconsc = getConfig().createSection("chcons");
for (val chcon : chcons) { for (val chcon : chcons) {
val chconc = chconsc.createSection(chcon.channel.getStringID()); val chconc = chconsc.createSection(chcon.channel.getStringID());
@ -287,7 +280,7 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
+ "kicked the hell out.") //TODO: Make configurable + "kicked the hell out.") //TODO: Make configurable
: "") //If 'restart' is disabled then this isn't shown even if joinleave is enabled : "") //If 'restart' is disabled then this isn't shown even if joinleave is enabled
.build(); .build();
MCChatListener.forCustomAndAllMCChat(ch -> { MCChatUtils.forCustomAndAllMCChat(ch -> {
try { try {
DiscordPlugin.sendMessageToChannelWait(ch, "", DiscordPlugin.sendMessageToChannelWait(ch, "",
embed, 5, TimeUnit.SECONDS); embed, 5, TimeUnit.SECONDS);
@ -299,16 +292,6 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
try { try {
SafeMode = true; // Stop interacting with Discord SafeMode = true; // Stop interacting with Discord
MCChatListener.stop(true); MCChatListener.stop(true);
try {
if (PlayerListWatcher.hookDown())
System.out.println("Finished unhooking the player list!");
else
System.out.println("Didn't have the player list hooked.");
hooked = false;
} catch (Throwable e) {
e.printStackTrace();
Bukkit.getLogger().warning("Couldn't unhook the player list!");
}
ChromaBot.delete(); ChromaBot.delete();
dc.changePresence(StatusType.IDLE, ActivityType.PLAYING, "Chromacraft"); //No longer using the same account for testing dc.changePresence(StatusType.IDLE, ActivityType.PLAYING, "Chromacraft"); //No longer using the same account for testing
dc.logout(); dc.logout();
@ -424,7 +407,7 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
.warning("Message was too long to send to discord and got truncated. In " + channel.getName()); .warning("Message was too long to send to discord and got truncated. In " + channel.getName());
} }
try { try {
MCChatListener.resetLastMessage(channel); // If this is a chat message, it'll be set again MCChatUtils.resetLastMessage(channel); // If this is a chat message, it'll be set again
final String content = message; final String content = message;
RequestBuffer.IRequest<IMessage> r = () -> embed == null ? channel.sendMessage(content) RequestBuffer.IRequest<IMessage> r = () -> embed == null ? channel.sendMessage(content)
: channel.sendMessage(content, embed, false); : channel.sendMessage(content, embed, false);

View file

@ -1,13 +1,13 @@
package buttondevteam.discordplugin; package buttondevteam.discordplugin;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.IDiscordSender;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import sx.blah.discord.handle.obj.IChannel; import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IUser; import sx.blah.discord.handle.obj.IUser;
public abstract class DiscordSenderBase implements IDiscordSender { public abstract class DiscordSenderBase implements CommandSender {
/** /**
* May be null. * May be null.
*/ */
@ -51,6 +51,7 @@ public abstract class DiscordSenderBase implements IDiscordSender {
public void sendMessage(String message) { public void sendMessage(String message) {
try { try {
final boolean broadcast = new Exception().getStackTrace()[2].getMethodName().contains("broadcast"); final boolean broadcast = new Exception().getStackTrace()[2].getMethodName().contains("broadcast");
//if (broadcast && DiscordPlugin.hooked) - TODO: What should happen if unhooked
if (broadcast) if (broadcast)
return; return;
final String sendmsg = DPUtils.sanitizeString(message); final String sendmsg = DPUtils.sanitizeString(message);

View file

@ -0,0 +1,35 @@
package buttondevteam.discordplugin.broadcaster;
import buttondevteam.discordplugin.DPUtils;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.Component;
import lombok.Getter;
import org.bukkit.Bukkit;
public class GeneralEventBroadcasterModule extends Component {
private static @Getter boolean hooked = false;
@Override
protected void enable() {
try {
PlayerListWatcher.hookUp();
Bukkit.getLogger().info("Finished hooking into the player list");
hooked = true;
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while hacking the player list!", e);
}
}
@Override
protected void disable() {
try {
if (PlayerListWatcher.hookDown())
DPUtils.getLogger().info("Finished unhooking the player list!");
else
DPUtils.getLogger().info("Didn't have the player list hooked.");
hooked = false;
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while hacking the player list!", e);
}
}
}

View file

@ -1,6 +1,6 @@
package buttondevteam.discordplugin; package buttondevteam.discordplugin.broadcaster;
import buttondevteam.discordplugin.listeners.MCChatListener; import buttondevteam.discordplugin.mcchat.MCChatUtils;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import lombok.val; import lombok.val;
@ -30,7 +30,7 @@ public class PlayerListWatcher extends DedicatedPlayerList {
if (packet instanceof PacketPlayOutChat) { if (packet instanceof PacketPlayOutChat) {
Field msgf = PacketPlayOutChat.class.getDeclaredField("a"); Field msgf = PacketPlayOutChat.class.getDeclaredField("a");
msgf.setAccessible(true); msgf.setAccessible(true);
MCChatListener.forAllMCChat(MCChatListener.send(((IChatBaseComponent) msgf.get(packet)).toPlainText())); MCChatUtils.forAllMCChat(MCChatUtils.send(((IChatBaseComponent) msgf.get(packet)).toPlainText()));
} }
} catch (Exception e) { } catch (Exception e) {
TBMCCoreAPI.SendException("Failed to broadcast message sent to all players - hacking failed.", e); TBMCCoreAPI.SendException("Failed to broadcast message sent to all players - hacking failed.", e);
@ -53,14 +53,13 @@ public class PlayerListWatcher extends DedicatedPlayerList {
} }
@Override @Override
public void sendMessage(IChatBaseComponent[] iChatBaseComponents) { // Needed so it calls the overriden method public void sendMessage(IChatBaseComponent[] iChatBaseComponents) { // Needed so it calls the overridden method
for (IChatBaseComponent component : iChatBaseComponents) { for (IChatBaseComponent component : iChatBaseComponents) {
sendMessage(component, true); sendMessage(component, true);
} }
} }
public static void hookUp() { static void hookUp() throws Exception {
try {
Field conf = CraftServer.class.getDeclaredField("console"); Field conf = CraftServer.class.getDeclaredField("console");
conf.setAccessible(true); conf.setAccessible(true);
val server = (MinecraftServer) conf.get(Bukkit.getServer()); val server = (MinecraftServer) conf.get(Bukkit.getServer());
@ -77,13 +76,9 @@ public class PlayerListWatcher extends DedicatedPlayerList {
Field pllf = CraftServer.class.getDeclaredField("playerList"); Field pllf = CraftServer.class.getDeclaredField("playerList");
pllf.setAccessible(true); pllf.setAccessible(true);
pllf.set(Bukkit.getServer(), plw); pllf.set(Bukkit.getServer(), plw);
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while hacking the player list!", e);
}
} }
public static boolean hookDown() { static boolean hookDown() throws Exception {
try {
Field conf = CraftServer.class.getDeclaredField("console"); Field conf = CraftServer.class.getDeclaredField("console");
conf.setAccessible(true); conf.setAccessible(true);
val server = (MinecraftServer) conf.get(Bukkit.getServer()); val server = (MinecraftServer) conf.get(Bukkit.getServer());
@ -95,10 +90,6 @@ public class PlayerListWatcher extends DedicatedPlayerList {
pllf.setAccessible(true); pllf.setAccessible(true);
pllf.set(Bukkit.getServer(), ((PlayerListWatcher) plist).plist); pllf.set(Bukkit.getServer(), ((PlayerListWatcher) plist).plist);
return true; return true;
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while hacking the player list!", e);
return true;
}
} }
public void a(EntityHuman entityhuman, IChatBaseComponent ichatbasecomponent) { public void a(EntityHuman entityhuman, IChatBaseComponent ichatbasecomponent) {

View file

@ -3,7 +3,8 @@ package buttondevteam.discordplugin.commands;
import buttondevteam.discordplugin.ChannelconBroadcast; import buttondevteam.discordplugin.ChannelconBroadcast;
import buttondevteam.discordplugin.DiscordConnectedPlayer; import buttondevteam.discordplugin.DiscordConnectedPlayer;
import buttondevteam.discordplugin.DiscordPlayer; import buttondevteam.discordplugin.DiscordPlayer;
import buttondevteam.discordplugin.listeners.MCChatListener; import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.mcchat.MCChatCustom;
import buttondevteam.lib.chat.Channel; import buttondevteam.lib.chat.Channel;
import buttondevteam.lib.player.TBMCPlayer; import buttondevteam.lib.player.TBMCPlayer;
import lombok.val; import lombok.val;
@ -30,16 +31,16 @@ public class ChannelconCommand extends DiscordCommandBase {
message.reply("you need to have manage permissions for this channel!"); message.reply("you need to have manage permissions for this channel!");
return true; return true;
} }
if (MCChatListener.hasCustomChat(message.getChannel())) { if (MCChatCustom.hasCustomChat(message.getChannel())) {
if (args.toLowerCase().startsWith("remove")) { if (args.toLowerCase().startsWith("remove")) {
if (MCChatListener.removeCustomChat(message.getChannel())) if (MCChatCustom.removeCustomChat(message.getChannel()))
message.reply("channel connection removed."); message.reply("channel connection removed.");
else else
message.reply("wait what, couldn't remove channel connection."); message.reply("wait what, couldn't remove channel connection.");
return true; return true;
} }
if (args.toLowerCase().startsWith("toggle")) { if (args.toLowerCase().startsWith("toggle")) {
val cc = MCChatListener.getCustomChat(message.getChannel()); val cc = MCChatCustom.getCustomChat(message.getChannel());
Supplier<String> togglesString = () -> Arrays.stream(ChannelconBroadcast.values()).map(t -> t.toString().toLowerCase() + ": " + ((cc.toggles & t.flag) == 0 ? "disabled" : "enabled")).collect(Collectors.joining("\n")); Supplier<String> togglesString = () -> Arrays.stream(ChannelconBroadcast.values()).map(t -> t.toString().toLowerCase() + ": " + ((cc.toggles & t.flag) == 0 ? "disabled" : "enabled")).collect(Collectors.joining("\n"));
String[] argsa = args.split(" "); String[] argsa = args.split(" ");
if (argsa.length < 2) { if (argsa.length < 2) {
@ -88,7 +89,7 @@ public class ChannelconCommand extends DiscordCommandBase {
message.reply("sorry, this MC chat is already connected to a different channel, multiple channels are not supported atm."); message.reply("sorry, this MC chat is already connected to a different channel, multiple channels are not supported atm.");
return true; return true;
}*/ //TODO: "Channel admins" that can connect channels? }*/ //TODO: "Channel admins" that can connect channels?
MCChatListener.addCustomChat(message.getChannel(), groupid, chan.get(), message.getAuthor(), dcp, 0); MCChatCustom.addCustomChat(message.getChannel(), groupid, chan.get(), message.getAuthor(), dcp, 0);
message.reply("alright, connection made to group `" + groupid + "`!"); message.reply("alright, connection made to group `" + groupid + "`!");
return true; return true;
} }
@ -99,10 +100,12 @@ public class ChannelconCommand extends DiscordCommandBase {
"---- Channel connect ---", // "---- Channel connect ---", //
"This command allows you to connect a Minecraft channel to a Discord channel (just like how the global chat is connected to #minecraft-chat).", // "This command allows you to connect a Minecraft channel to a Discord channel (just like how the global chat is connected to #minecraft-chat).", //
"You need to have access to the MC channel and have manage permissions on the Discord channel.", // "You need to have access to the MC channel and have manage permissions on the Discord channel.", //
"You also need to have your Minecraft account connected. In #bot use /connect <mcname>.", // "You also need to have your Minecraft account connected. In #bot use " + DiscordPlugin.getPrefix() + "connect <mcname>.", //
"Call this command from the channel you want to use. Usage: @ChromaBot channelcon <mcchannel>", // "Call this command from the channel you want to use.", //
"Usage: @" + DiscordPlugin.dc.getOurUser().getName() + " channelcon <mcchannel>", //
"Use the ID (command) of the channel, for example `g` for the global chat.", //
"To remove a connection use @ChromaBot channelcon remove in the channel.", // "To remove a connection use @ChromaBot channelcon remove in the channel.", //
"Mentioning the bot is needed in this case because the / prefix only works in #bot.", // "Mentioning the bot is needed in this case because the " + DiscordPlugin.getPrefix() + " prefix only works in #bot.", //
"Invite link: <https://discordapp.com/oauth2/authorize?client_id=226443037893591041&scope=bot&permissions=268509264>" // "Invite link: <https://discordapp.com/oauth2/authorize?client_id=226443037893591041&scope=bot&permissions=268509264>" //
}; };
} }

View file

@ -30,7 +30,7 @@ public class ConnectCommand extends DiscordCommandBase {
return false; return false;
if (args.contains(" ")) { if (args.contains(" ")) {
DiscordPlugin.sendMessageToChannel(message.getChannel(), DiscordPlugin.sendMessageToChannel(message.getChannel(),
"Too many arguments.\nUsage: connect <Minecraftname>"); "Too many arguments.\nUsage: " + DiscordPlugin.getPrefix() + "connect <Minecraftname>");
return true; return true;
} }
if (WaitingToConnect.inverse().containsKey(message.getAuthor().getStringID())) { if (WaitingToConnect.inverse().containsKey(message.getAuthor().getStringID())) {
@ -68,8 +68,8 @@ public class ConnectCommand extends DiscordCommandBase {
public String[] getHelpText() { public String[] getHelpText() {
return new String[] { // return new String[] { //
"---- Connect command ----", // "---- Connect command ----", //
"This commands let's you connect your acoount with a Minecraft account. This'd allow using the Minecraft chat and other things.", // "This command lets you connect your account with a Minecraft account. This allows using the Minecraft chat and other things.", //
"Usage: connect <Minecraftname>" // "Usage: /connect <Minecraftname>" //
}; };
} }

View file

@ -1,7 +1,7 @@
package buttondevteam.discordplugin.commands; package buttondevteam.discordplugin.commands;
import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.listeners.CommandListener; import buttondevteam.discordplugin.listeners.CommonListeners;
import sx.blah.discord.handle.obj.IMessage; import sx.blah.discord.handle.obj.IMessage;
public class DebugCommand extends DiscordCommandBase { public class DebugCommand extends DiscordCommandBase {
@ -13,7 +13,7 @@ public class DebugCommand extends DiscordCommandBase {
@Override @Override
public boolean run(IMessage message, String args) { public boolean run(IMessage message, String args) {
if (message.getAuthor().hasRole(DiscordPlugin.mainServer.getRoleByID(126030201472811008L))) if (message.getAuthor().hasRole(DiscordPlugin.mainServer.getRoleByID(126030201472811008L)))
message.reply("Debug " + (CommandListener.debug() ? "enabled" : "disabled")); message.reply("Debug " + (CommonListeners.debug() ? "enabled" : "disabled"));
else else
message.reply("You need to be a moderator to use this command."); message.reply("You need to be a moderator to use this command.");
return true; return true;

View file

@ -1,6 +1,7 @@
package buttondevteam.discordplugin.commands; package buttondevteam.discordplugin.commands;
import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.mcchat.MCChatCommand;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import sx.blah.discord.handle.obj.IMessage; import sx.blah.discord.handle.obj.IMessage;
@ -8,7 +9,7 @@ import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static buttondevteam.discordplugin.listeners.CommandListener.debug; import static buttondevteam.discordplugin.listeners.CommonListeners.debug;
public abstract class DiscordCommandBase { public abstract class DiscordCommandBase {
public abstract String getCommandName(); public abstract String getCommandName();

View file

@ -3,7 +3,6 @@ package buttondevteam.discordplugin.commands;
import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.discordplugin.DiscordPlugin;
import sx.blah.discord.handle.obj.IMessage; import sx.blah.discord.handle.obj.IMessage;
import java.util.Arrays;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class HelpCommand extends DiscordCommandBase { public class HelpCommand extends DiscordCommandBase {
@ -19,11 +18,11 @@ public class HelpCommand extends DiscordCommandBase {
if (args.length() == 0) if (args.length() == 0)
DiscordPlugin.sendMessageToChannel(message.getChannel(), DiscordPlugin.sendMessageToChannel(message.getChannel(),
"Available commands:\n" + DiscordCommandBase.commands.values().stream() "Available commands:\n" + DiscordCommandBase.commands.values().stream()
.map(dc -> dc.getCommandName()).collect(Collectors.joining("\n"))); .map(dc -> DiscordPlugin.getPrefix() + dc.getCommandName()).collect(Collectors.joining("\n")));
else else
DiscordPlugin.sendMessageToChannel(message.getChannel(), DiscordPlugin.sendMessageToChannel(message.getChannel(),
(argdc = DiscordCommandBase.commands.get(args)) == null ? "Command not found: " + args (argdc = DiscordCommandBase.commands.get(args)) == null ? "Command not found: " + args
: Arrays.stream(argdc.getHelpText()).collect(Collectors.joining("\n"))); : String.join("\n", argdc.getHelpText()));
return true; return true;
} }
@ -32,7 +31,7 @@ public class HelpCommand extends DiscordCommandBase {
return new String[] { // return new String[] { //
"---- Help command ----", // "---- Help command ----", //
"Shows some info about a command or lists the available commands.", // "Shows some info about a command or lists the available commands.", //
"Usage: help [command]"// "Usage: " + DiscordPlugin.getPrefix() + "help [command]"//
}; };
} }

View file

@ -86,7 +86,7 @@ public class RoleCommand extends DiscordCommandBase {
public String[] getHelpText() { public String[] getHelpText() {
return new String[]{ // return new String[]{ //
"Add or remove game roles from yourself.", // "Add or remove game roles from yourself.", //
"Usage: role add|remove <name> or role list", // "Usage: " + DiscordPlugin.getPrefix() + "role add|remove <name> or role list", //
}; };
} }

View file

@ -91,7 +91,8 @@ public class UserinfoCommand extends DiscordCommandBase {
"---- User information ----", // "---- User information ----", //
"Shows some information about users, from Discord, from Minecraft or from Reddit if they have these accounts connected.", // "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.", // "If used without args, shows your info.", //
"Usage: userinfo [username/nickname[#tag]/ping]\nExamples:\nuserinfo ChromaBot\nuserinfo ChromaBot#6338\nuserinfo @ChromaBot#6338" // "Usage: " + DiscordPlugin.getPrefix() + "userinfo [username/nickname[#tag]/ping]", //
"Examples:\n" + DiscordPlugin.getPrefix() + "userinfo ChromaBot\n" + DiscordPlugin.getPrefix() + "userinfo ChromaBot#6338\n" + DiscordPlugin.getPrefix() + "userinfo @ChromaBot#6338" //
}; };
} }

View file

@ -1,4 +1,4 @@
package buttondevteam.discordplugin.listeners; package buttondevteam.discordplugin.exceptions;
import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.TBMCDebugMessageEvent; import buttondevteam.lib.TBMCDebugMessageEvent;

View file

@ -1,9 +1,12 @@
package buttondevteam.discordplugin.listeners; package buttondevteam.discordplugin.exceptions;
import buttondevteam.core.ComponentManager;
import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.TBMCExceptionEvent; import buttondevteam.lib.TBMCExceptionEvent;
import buttondevteam.lib.architecture.Component;
import org.apache.commons.lang.exception.ExceptionUtils; import org.apache.commons.lang.exception.ExceptionUtils;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import sx.blah.discord.handle.obj.IRole; import sx.blah.discord.handle.obj.IRole;
@ -13,13 +16,13 @@ import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
public class ExceptionListener implements Listener { public class ExceptionListenerModule extends Component implements Listener {
private List<Throwable> lastthrown = new ArrayList<>(); private List<Throwable> lastthrown = new ArrayList<>();
private List<String> lastsourcemsg = new ArrayList<>(); private List<String> lastsourcemsg = new ArrayList<>();
@EventHandler @EventHandler
public void onException(TBMCExceptionEvent e) { public void onException(TBMCExceptionEvent e) {
if (DiscordPlugin.SafeMode) if (DiscordPlugin.SafeMode || !ComponentManager.isEnabled(getClass()))
return; return;
if (lastthrown.stream() if (lastthrown.stream()
.anyMatch(ex -> Arrays.equals(e.getException().getStackTrace(), ex.getStackTrace()) .anyMatch(ex -> Arrays.equals(e.getException().getStackTrace(), ex.getStackTrace())
@ -59,4 +62,15 @@ public class ExceptionListener implements Listener {
ex.printStackTrace(); ex.printStackTrace();
} }
} }
@Override
protected void enable() {
Bukkit.getPluginManager().registerEvents(new ExceptionListenerModule(), getPlugin());
TBMCCoreAPI.RegisterEventsForExceptions(new DebugMessageListener(), getPlugin());
}
@Override
protected void disable() {
}
} }

View file

@ -0,0 +1,86 @@
package buttondevteam.discordplugin.fun;
import buttondevteam.core.ComponentManager;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.Component;
import buttondevteam.lib.architecture.ConfigData;
import sx.blah.discord.handle.obj.IMessage;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
public class FunModule extends Component {
private static FunModule mod;
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
};
private ConfigData<Boolean> serverReady() {
return getData("serverReady", true);
}
private ConfigData<List<String>> serverReadyAnswers() {
return getData("serverReadyAnswers", Arrays.asList(serverReadyStrings),
data -> (List<String>) data, data -> data); //TODO: Test
}
private static final String[] serverReadyQuestions = new String[]{"when will the server be open",
"when will the server be ready", "when will the server be done", "when will the server be complete",
"when will the server be finished", "when's the server ready", "when's the server open",
"Vhen vill ze server be open?"};
private static final Random serverReadyRandom = new Random();
private static final ArrayList<Short> usableServerReadyStrings = new ArrayList<Short>(serverReadyStrings.length) {
private static final long serialVersionUID = 2213771460909848770L;
{
createUsableServerReadyStrings(this);
}
};
private static void createUsableServerReadyStrings(ArrayList<Short> list) {
for (short i = 0; i < serverReadyStrings.length; i++)
list.add(i);
}
@Override
protected void enable() {
mod = this;
}
@Override
protected void disable() {
}
public static boolean executeMemes(IMessage message) {
if (!ComponentManager.isEnabled(FunModule.class)) return false;
if (mod.serverReady().get()) {
if (!TBMCCoreAPI.IsTestServer()
&& Arrays.stream(serverReadyQuestions).anyMatch(s -> message.getContent().toLowerCase().contains(s))) {
int next;
if (usableServerReadyStrings.size() == 0)
createUsableServerReadyStrings(usableServerReadyStrings);
next = usableServerReadyStrings.remove(serverReadyRandom.nextInt(usableServerReadyStrings.size()));
DiscordPlugin.sendMessageToChannel(message.getChannel(), serverReadyStrings[next]);
return true;
}
}
return false;
}
}

View file

@ -2,183 +2,40 @@ package buttondevteam.discordplugin.listeners;
import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.commands.DiscordCommandBase; import buttondevteam.discordplugin.commands.DiscordCommandBase;
import buttondevteam.lib.TBMCCoreAPI;
import lombok.val;
import org.bukkit.Bukkit;
import sx.blah.discord.api.events.IListener;
import sx.blah.discord.handle.impl.events.guild.channel.message.MentionEvent;
import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent;
import sx.blah.discord.handle.impl.events.guild.role.RoleCreateEvent;
import sx.blah.discord.handle.impl.events.guild.role.RoleDeleteEvent;
import sx.blah.discord.handle.impl.events.guild.role.RoleUpdateEvent;
import sx.blah.discord.handle.impl.events.user.PresenceUpdateEvent;
import sx.blah.discord.handle.obj.IChannel; import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IMessage; import sx.blah.discord.handle.obj.IMessage;
import sx.blah.discord.handle.obj.StatusType; import sx.blah.discord.handle.obj.IRole;
import sx.blah.discord.util.EmbedBuilder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class CommandListener { public class CommandListener {
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
};
private static final String[] serverReadyQuestions = new String[]{"when will the server be open",
"when will the server be ready", "when will the server be done", "when will the server be complete",
"when will the server be finished", "when's the server ready", "when's the server open",
"Vhen vill ze server be open?"};
private static final Random serverReadyRandom = new Random();
private static final ArrayList<Short> usableServerReadyStrings = new ArrayList<Short>(serverReadyStrings.length) {
private static final long serialVersionUID = 2213771460909848770L;
{
createUsableServerReadyStrings(this);
}
};
private static void createUsableServerReadyStrings(ArrayList<Short> list) {
for (short i = 0; i < serverReadyStrings.length; i++)
list.add(i);
}
private static long lasttime = 0;
public static IListener<?>[] getListeners() {
return new IListener[]{new IListener<MentionEvent>() {
@Override
public void handle(MentionEvent event) {
if (DiscordPlugin.SafeMode)
return;
if (event.getMessage().getAuthor().isBot())
return;
final IChannel channel = event.getMessage().getChannel();
if (!channel.getStringID().equals(DiscordPlugin.botchannel.getStringID())
&& (!event.getMessage().getContent().contains("channelcon") || MCChatListener.hasCustomChat(channel))) //Allow channelcon in other servers but avoid double handling when it's enabled
return;
if (channel.getStringID().equals(DiscordPlugin.chatchannel.getStringID()))
return; // The chat code already handles this - Right now while testing botchannel is the same as chatchannel
event.getMessage().getChannel().setTypingStatus(true); // Fun
runCommand(event.getMessage(), true);
}
}, new IListener<MessageReceivedEvent>() {
@Override
public void handle(MessageReceivedEvent event) {
if (DiscordPlugin.SafeMode)
return;
final String msglowercase = event.getMessage().getContent().toLowerCase();
if (!TBMCCoreAPI.IsTestServer()
&& Arrays.stream(serverReadyQuestions).anyMatch(s -> msglowercase.contains(s))) {
int next;
if (usableServerReadyStrings.size() == 0)
createUsableServerReadyStrings(usableServerReadyStrings);
next = usableServerReadyStrings.remove(serverReadyRandom.nextInt(usableServerReadyStrings.size()));
DiscordPlugin.sendMessageToChannel(event.getMessage().getChannel(), serverReadyStrings[next]);
return;
}
if (!event.getMessage().getChannel().isPrivate()
&& !(event.getMessage().getContent().startsWith("/")
&& event.getChannel().getStringID().equals(DiscordPlugin.botchannel.getStringID()))) //
return;
if (MCChatListener.isMinecraftChatEnabled(event.getAuthor().toString()))
if (!event.getMessage().getContent().equalsIgnoreCase("mcchat"))
return;
if (event.getMessage().getAuthor().isBot())
return;
runCommand(event.getMessage(), false);
}
}, new IListener<sx.blah.discord.handle.impl.events.user.PresenceUpdateEvent>() {
@Override
public void handle(PresenceUpdateEvent event) {
if (DiscordPlugin.SafeMode)
return;
val devrole = DiscordPlugin.devServer.getRolesByName("Developer").get(0);
if (event.getOldPresence().getStatus().equals(StatusType.OFFLINE)
&& !event.getNewPresence().getStatus().equals(StatusType.OFFLINE)
&& event.getUser().getRolesForGuild(DiscordPlugin.devServer).stream()
.anyMatch(r -> r.getLongID() == devrole.getLongID())
&& DiscordPlugin.devServer.getUsersByRole(devrole).stream()
.noneMatch(u -> u.getPresence().getStatus().equals(StatusType.OFFLINE))
&& lasttime + 10 < TimeUnit.NANOSECONDS.toHours(System.nanoTime())
&& Calendar.getInstance().get(Calendar.DAY_OF_MONTH) % 5 == 0) {
DiscordPlugin.sendMessageToChannel(DiscordPlugin.devofficechannel, "Full house!",
new EmbedBuilder()
.withImage(
"https://cdn.discordapp.com/attachments/249295547263877121/249687682618359808/poker-hand-full-house-aces-kings-playing-cards-15553791.png")
.build());
lasttime = TimeUnit.NANOSECONDS.toHours(System.nanoTime());
}
}
}, (IListener<RoleCreateEvent>) event -> {
Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> {
if (event.getRole().isDeleted() || !DiscordPlugin.plugin.isGameRole(event.getRole()))
return; //Deleted or not a game role
DiscordPlugin.GameRoles.add(event.getRole().getName());
DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Added " + event.getRole().getName() + " as game role. If you don't want this, change the role's color from the default.");
}, 100);
}, (IListener<RoleDeleteEvent>) event -> {
if (DiscordPlugin.GameRoles.remove(event.getRole().getName()))
DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Removed " + event.getRole().getName() + " as a game role.");
}, (IListener<RoleUpdateEvent>) event -> { //Role update event
if (!DiscordPlugin.plugin.isGameRole(event.getNewRole())) {
if (DiscordPlugin.GameRoles.remove(event.getOldRole().getName()))
DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Removed " + event.getOldRole().getName() + " as a game role because it's color changed.");
} else {
if (DiscordPlugin.GameRoles.contains(event.getOldRole().getName()) && event.getOldRole().getName().equals(event.getNewRole().getName()))
return;
boolean removed = DiscordPlugin.GameRoles.remove(event.getOldRole().getName()); //Regardless of whether it was a game role
DiscordPlugin.GameRoles.add(event.getNewRole().getName()); //Add it because it has no color
if (removed)
DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Changed game role from " + event.getOldRole().getName() + " to " + event.getNewRole().getName() + ".");
else
DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Added " + event.getNewRole().getName() + " as game role because it has the default color.");
}
}};
}
/** /**
* Runs a ChromaBot command. * Runs a ChromaBot command. If mentionedonly is false, it will only execute the command if it was in #bot with the correct prefix or in private.
* *
* @param message The Discord message * @param message The Discord message
* @param mentionedonly Only run the command if ChromaBot is mentioned at the start of the message * @param mentionedonly Only run the command if ChromaBot is mentioned at the start of the message
* @return Whether it ran the command (always true if mentionedonly is false) * @return Whether it ran the command
*/ */
public static boolean runCommand(IMessage message, boolean mentionedonly) { public static boolean runCommand(IMessage message, boolean mentionedonly) {
debug("A"); if (message.getContent().length() == 0)
if (DiscordPlugin.SafeMode) return false; //Pin messages and such, let the mcchat listener deal with it
return true; final IChannel channel = message.getChannel();
debug("B"); if (!mentionedonly) { //mentionedonly conditions are in CommonListeners
if (!message.getChannel().isPrivate()
&& !(message.getContent().charAt(0) == DiscordPlugin.getPrefix()
&& channel.getStringID().equals(DiscordPlugin.botchannel.getStringID()))) //
return false;
message.getChannel().setTypingStatus(true); // Fun
}
final StringBuilder cmdwithargs = new StringBuilder(message.getContent()); final StringBuilder cmdwithargs = new StringBuilder(message.getContent());
final String mention = DiscordPlugin.dc.getOurUser().mention(false); final String mention = DiscordPlugin.dc.getOurUser().mention(false);
final String mentionNick = DiscordPlugin.dc.getOurUser().mention(true); final String mentionNick = DiscordPlugin.dc.getOurUser().mention(true);
boolean gotmention = checkanddeletemention(cmdwithargs, mention, message); boolean gotmention = checkanddeletemention(cmdwithargs, mention, message);
gotmention = checkanddeletemention(cmdwithargs, mentionNick, message) || gotmention; gotmention = checkanddeletemention(cmdwithargs, mentionNick, message) || gotmention;
for (String mentionRole : (Iterable<String>) message.getRoleMentions().stream().filter(r -> DiscordPlugin.dc.getOurUser().hasRole(r)).map(r -> r.mention())::iterator) for (String mentionRole : (Iterable<String>) message.getRoleMentions().stream().filter(r -> DiscordPlugin.dc.getOurUser().hasRole(r)).map(IRole::mention)::iterator)
gotmention = checkanddeletemention(cmdwithargs, mentionRole, message) || gotmention; // Delete all mentions gotmention = checkanddeletemention(cmdwithargs, mentionRole, message) || gotmention; // Delete all mentions
debug("C");
if (mentionedonly && !gotmention) { if (mentionedonly && !gotmention) {
message.getChannel().setTypingStatus(false); message.getChannel().setTypingStatus(false);
return false; return false;
} }
debug("D");
message.getChannel().setTypingStatus(true); message.getChannel().setTypingStatus(true);
String cmdwithargsString = cmdwithargs.toString().trim(); //Remove spaces between mention and command String cmdwithargsString = cmdwithargs.toString().trim(); //Remove spaces between mention and command
int index = cmdwithargsString.indexOf(" "); int index = cmdwithargsString.indexOf(" ");
@ -191,23 +48,11 @@ public class CommandListener {
cmd = cmdwithargsString.substring(0, index); cmd = cmdwithargsString.substring(0, index);
args = cmdwithargsString.substring(index + 1).trim(); //In case there are multiple spaces args = cmdwithargsString.substring(index + 1).trim(); //In case there are multiple spaces
} }
debug("E");
DiscordCommandBase.runCommand(cmd.toLowerCase(), args, message); DiscordCommandBase.runCommand(cmd.toLowerCase(), args, message);
message.getChannel().setTypingStatus(false); message.getChannel().setTypingStatus(false);
return true; return true;
} }
private static boolean debug = false;
public static void debug(String debug) {
if (CommandListener.debug) //Debug
System.out.println(debug);
}
public static boolean debug() {
return debug = !debug;
}
private static boolean checkanddeletemention(StringBuilder cmdwithargs, String mention, IMessage message) { private static boolean checkanddeletemention(StringBuilder cmdwithargs, String mention, IMessage message) {
if (message.getContent().startsWith(mention)) // TODO: Resolve mentions: Compound arguments, either a mention or text if (message.getContent().startsWith(mention)) // TODO: Resolve mentions: Compound arguments, either a mention or text
if (cmdwithargs.length() > mention.length() + 1) if (cmdwithargs.length() > mention.length() + 1)

View file

@ -0,0 +1,128 @@
package buttondevteam.discordplugin.listeners;
import buttondevteam.discordplugin.DPUtils;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.mcchat.MinecraftChatModule;
import buttondevteam.lib.architecture.Component;
import lombok.val;
import org.bukkit.Bukkit;
import sx.blah.discord.api.events.IListener;
import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent;
import sx.blah.discord.handle.impl.events.guild.role.RoleCreateEvent;
import sx.blah.discord.handle.impl.events.guild.role.RoleDeleteEvent;
import sx.blah.discord.handle.impl.events.guild.role.RoleUpdateEvent;
import sx.blah.discord.handle.impl.events.user.PresenceUpdateEvent;
import sx.blah.discord.handle.obj.StatusType;
import sx.blah.discord.util.EmbedBuilder;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
public class CommonListeners {
/*private static ArrayList<Object> dcListeners=new ArrayList<>();
public static void registerDiscordListener(DiscordListener listener) {
//Step 1: Get all events that are handled by us
//Step 2: Find methods that handle these
//...or just simply call the methods in the right order
}
private static void callDiscordEvent(Event event) {
String name=event.getClass().getSimpleName();
name=Character.toLowerCase(name.charAt(0))+name.substring(1);
for (Object listener : dcListeners) {
listener.getClass().getMethods(name, AsyncDiscordEvent.class);
}
}*/
private static long lasttime = 0;
/*
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 IListener<?>[] getListeners() {
return new IListener[]{new IListener<MessageReceivedEvent>() {
@Override
public void handle(MessageReceivedEvent event) {
if (DiscordPlugin.SafeMode)
return;
if (event.getMessage().getAuthor().isBot())
return;
boolean handled = false;
if (event.getChannel().getLongID() == DiscordPlugin.botchannel.getLongID() //If mentioned, that's higher than chat
|| event.getMessage().getContent().contains("channelcon")) //Only 'channelcon' is allowed in other channels
handled = CommandListener.runCommand(event.getMessage(), true); //#bot is handled here
if (handled) return;
val mcchat = Component.getComponents().get(MinecraftChatModule.class);
if (mcchat != null && mcchat.isEnabled()) //ComponentManager.isEnabled() searches the component again
handled = ((MinecraftChatModule) mcchat).getListener().handleDiscord(event); //Also runs Discord commands in chat channels
if (!handled)
handled = CommandListener.runCommand(event.getMessage(), false);
}
}, new IListener<sx.blah.discord.handle.impl.events.user.PresenceUpdateEvent>() {
@Override
public void handle(PresenceUpdateEvent event) {
if (DiscordPlugin.SafeMode)
return;
val devrole = DiscordPlugin.devServer.getRolesByName("Developer").get(0);
if (event.getOldPresence().getStatus().equals(StatusType.OFFLINE)
&& !event.getNewPresence().getStatus().equals(StatusType.OFFLINE)
&& event.getUser().getRolesForGuild(DiscordPlugin.devServer).stream()
.anyMatch(r -> r.getLongID() == devrole.getLongID())
&& DiscordPlugin.devServer.getUsersByRole(devrole).stream()
.noneMatch(u -> u.getPresence().getStatus().equals(StatusType.OFFLINE))
&& lasttime + 10 < TimeUnit.NANOSECONDS.toHours(System.nanoTime())
&& Calendar.getInstance().get(Calendar.DAY_OF_MONTH) % 5 == 0) {
DiscordPlugin.sendMessageToChannel(DiscordPlugin.devofficechannel, "Full house!",
new EmbedBuilder()
.withImage(
"https://cdn.discordapp.com/attachments/249295547263877121/249687682618359808/poker-hand-full-house-aces-kings-playing-cards-15553791.png")
.build());
lasttime = TimeUnit.NANOSECONDS.toHours(System.nanoTime());
}
}
}, (IListener<RoleCreateEvent>) event -> {
Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin, () -> {
if (event.getRole().isDeleted() || !DiscordPlugin.plugin.isGameRole(event.getRole()))
return; //Deleted or not a game role
DiscordPlugin.GameRoles.add(event.getRole().getName());
DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Added " + event.getRole().getName() + " as game role. If you don't want this, change the role's color from the default.");
}, 100);
}, (IListener<RoleDeleteEvent>) event -> {
if (DiscordPlugin.GameRoles.remove(event.getRole().getName()))
DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Removed " + event.getRole().getName() + " as a game role.");
}, (IListener<RoleUpdateEvent>) event -> { //Role update event
if (!DiscordPlugin.plugin.isGameRole(event.getNewRole())) {
if (DiscordPlugin.GameRoles.remove(event.getOldRole().getName()))
DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Removed " + event.getOldRole().getName() + " as a game role because it's color changed.");
} else {
if (DiscordPlugin.GameRoles.contains(event.getOldRole().getName()) && event.getOldRole().getName().equals(event.getNewRole().getName()))
return;
boolean removed = DiscordPlugin.GameRoles.remove(event.getOldRole().getName()); //Regardless of whether it was a game role
DiscordPlugin.GameRoles.add(event.getNewRole().getName()); //Add it because it has no color
if (removed)
DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Changed game role from " + event.getOldRole().getName() + " to " + event.getNewRole().getName() + ".");
else
DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, "Added " + event.getNewRole().getName() + " as game role because it has the default color.");
}
}};
}
private static boolean debug = false;
public static void debug(String debug) {
if (CommonListeners.debug) //Debug
DPUtils.getLogger().info(debug);
}
public static boolean debug() {
return debug = !debug;
}
}

View file

@ -0,0 +1,4 @@
package buttondevteam.discordplugin.listeners;
public interface DiscordListener {
}

View file

@ -1,98 +1,24 @@
package buttondevteam.discordplugin.listeners; package buttondevteam.discordplugin.listeners;
import buttondevteam.discordplugin.*; import buttondevteam.discordplugin.DiscordPlayer;
import buttondevteam.discordplugin.commands.ConnectCommand; import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.player.TBMCPlayerGetInfoEvent;
import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.player.*;
import com.earth2me.essentials.CommandSource;
import lombok.val; import lombok.val;
import net.ess3.api.events.AfkStatusChangeEvent;
import net.ess3.api.events.MuteStatusChangeEvent;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.event.Event;
import org.bukkit.event.*; import org.bukkit.event.EventHandler;
import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.event.HandlerList;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.server.BroadcastMessageEvent;
import org.bukkit.event.server.ServerCommandEvent; import org.bukkit.event.server.ServerCommandEvent;
import org.bukkit.plugin.AuthorNagException; import org.bukkit.plugin.AuthorNagException;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.RegisteredListener; import org.bukkit.plugin.RegisteredListener;
import sx.blah.discord.handle.obj.IRole;
import sx.blah.discord.handle.obj.IUser; import sx.blah.discord.handle.obj.IUser;
import sx.blah.discord.util.DiscordException;
import sx.blah.discord.util.MissingPermissionsException;
import java.util.Arrays; import java.util.Arrays;
import java.util.logging.Level; import java.util.logging.Level;
public class MCListener implements Listener { public class MCListener implements Listener {
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerLogin(PlayerLoginEvent e) {
if (e.getResult() != Result.ALLOWED)
return;
MCChatListener.ConnectedSenders.values().stream().flatMap(v -> v.values().stream()) //Only private mcchat should be in ConnectedSenders
.filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny()
.ifPresent(dcp -> callEventExcludingSome(new PlayerQuitEvent(dcp, "")));
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerJoin(TBMCPlayerJoinEvent e) {
if (e.getPlayer() instanceof DiscordConnectedPlayer)
return; // Don't show the joined message for the fake player
Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, () -> {
final Player p = e.getPlayer();
DiscordPlayer dp = e.GetPlayer().getAs(DiscordPlayer.class);
if (dp != null) {
val user = DiscordPlugin.dc.getUserByID(Long.parseLong(dp.getDiscordID()));
MCChatListener.addSender(MCChatListener.OnlineSenders, dp.getDiscordID(),
new DiscordPlayerSender(user, user.getOrCreatePMChannel(), p));
MCChatListener.addSender(MCChatListener.OnlineSenders, dp.getDiscordID(),
new DiscordPlayerSender(user, DiscordPlugin.chatchannel, p)); //Stored per-channel
}
if (ConnectCommand.WaitingToConnect.containsKey(e.GetPlayer().PlayerName().get())) {
IUser user = DiscordPlugin.dc
.getUserByID(Long.parseLong(ConnectCommand.WaitingToConnect.get(e.GetPlayer().PlayerName().get())));
p.sendMessage("§bTo connect with the Discord account @" + user.getName() + "#" + user.getDiscriminator()
+ " do /discord accept");
p.sendMessage("§bIf it wasn't you, do /discord decline");
}
final String message = e.GetPlayer().PlayerName().get() + " joined the game";
MCChatListener.forAllowedCustomAndAllMCChat(MCChatListener.send(message), e.getPlayer(), ChannelconBroadcast.JOINLEAVE, true);
//System.out.println("Does this appear more than once?"); //No
MCChatListener.ListC = 0;
ChromaBot.getInstance().updatePlayerList();
});
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerLeave(TBMCPlayerQuitEvent e) {
if (e.getPlayer() instanceof DiscordConnectedPlayer)
return; // Only care about real users
MCChatListener.OnlineSenders.entrySet()
.removeIf(entry -> entry.getValue().entrySet().stream().anyMatch(p -> p.getValue().getUniqueId().equals(e.getPlayer().getUniqueId())));
Bukkit.getScheduler().runTask(DiscordPlugin.plugin,
() -> MCChatListener.ConnectedSenders.values().stream().flatMap(v -> v.values().stream())
.filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny()
.ifPresent(dcp -> callEventExcludingSome(new PlayerJoinEvent(dcp, ""))));
Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin,
ChromaBot.getInstance()::updatePlayerList, 5);
final String message = e.GetPlayer().PlayerName().get() + " left the game";
MCChatListener.forAllowedCustomAndAllMCChat(MCChatListener.send(message), e.getPlayer(), ChannelconBroadcast.JOINLEAVE, true);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerKick(PlayerKickEvent e) {
/*if (!DiscordPlugin.hooked && !e.getReason().equals("The server is restarting")
&& !e.getReason().equals("Server closed")) // The leave messages errored with the previous setup, I could make it wait since I moved it here, but instead I have a special
MCChatListener.forAllowedCustomAndAllMCChat(e.getPlayer().getName() + " left the game"); // message for this - Oh wait this doesn't even send normally because of the hook*/
}
@EventHandler @EventHandler
public void onGetInfo(TBMCPlayerGetInfoEvent e) { public void onGetInfo(TBMCPlayerGetInfoEvent e) {
if (DiscordPlugin.SafeMode) if (DiscordPlugin.SafeMode)
@ -107,70 +33,15 @@ public class MCListener implements Listener {
e.addInfo(user.getPresence().getActivity().get() + ": " + user.getPresence().getText().get()); e.addInfo(user.getPresence().getActivity().get() + ": " + user.getPresence().getText().get());
} }
@EventHandler(priority = EventPriority.LOW)
public void onPlayerDeath(PlayerDeathEvent e) {
MCChatListener.forAllowedCustomAndAllMCChat(MCChatListener.send(e.getDeathMessage()), e.getEntity(), ChannelconBroadcast.DEATH, true);
}
@EventHandler
public void onPlayerAFK(AfkStatusChangeEvent e) {
final Player base = e.getAffected().getBase();
if (e.isCancelled() || !base.isOnline())
return;
final String msg = base.getDisplayName()
+ " is " + (e.getValue() ? "now" : "no longer") + " AFK.";
MCChatListener.forAllowedCustomAndAllMCChat(MCChatListener.send(msg), base, ChannelconBroadcast.AFK, false);
}
@EventHandler @EventHandler
public void onServerCommand(ServerCommandEvent e) { public void onServerCommand(ServerCommandEvent e) {
DiscordPlugin.Restart = !e.getCommand().equalsIgnoreCase("stop"); // The variable is always true except if stopped DiscordPlugin.Restart = !e.getCommand().equalsIgnoreCase("stop"); // The variable is always true except if stopped
} }
@EventHandler private static final String[] EXCLUDED_PLUGINS = {"ProtocolLib", "LibsDisguises", "JourneyMapServer"}; //TODO: Make configurable
public void onPlayerMute(MuteStatusChangeEvent e) {
try {
DPUtils.performNoWait(() -> {
final IRole role = DiscordPlugin.dc.getRoleByID(164090010461667328L);
final CommandSource source = e.getAffected().getSource();
if (!source.isPlayer())
return;
final IUser user = DiscordPlugin.dc.getUserByID(
Long.parseLong(TBMCPlayerBase.getPlayer(source.getPlayer().getUniqueId(), TBMCPlayer.class)
.getAs(DiscordPlayer.class).getDiscordID()));
if (e.getValue())
user.addRole(role);
else
user.removeRole(role);
});
} catch (DiscordException | MissingPermissionsException ex) {
TBMCCoreAPI.SendException("Failed to give/take Muted role to player " + e.getAffected().getName() + "!",
ex);
}
}
@EventHandler
public void onChatSystemMessage(TBMCSystemChatEvent event) {
MCChatListener.forAllowedMCChat(MCChatListener.send(event.getMessage()), event);
}
@EventHandler
public void onBroadcastMessage(BroadcastMessageEvent event) {
MCChatListener.forCustomAndAllMCChat(MCChatListener.send(event.getMessage()), ChannelconBroadcast.BROADCAST, false);
}
@EventHandler
public void onYEEHAW(TBMCYEEHAWEvent event) { //TODO: Inherit from the chat event base to have channel support
String name = event.getSender() instanceof Player ? ((Player) event.getSender()).getDisplayName()
: event.getSender().getName();
//Channel channel = ChromaGamerBase.getFromSender(event.getSender()).channel().get(); - TODO
MCChatListener.forAllMCChat(MCChatListener.send(name + " <:YEEHAW:" + DiscordPlugin.mainServer.getEmojiByName("YEEHAW").getStringID() + ">s"));
}
private static final String[] EXCLUDED_PLUGINS = {"ProtocolLib", "LibsDisguises"};
public static void callEventExcludingSome(Event event) { public static void callEventExcludingSome(Event event) {
callEventExcluding(event, EXCLUDED_PLUGINS); callEventExcluding(event, false, EXCLUDED_PLUGINS);
} }
/** /**
@ -179,9 +50,10 @@ public class MCListener implements Listener {
* This method only synchronizes when the event is not asynchronous. * This method only synchronizes when the event is not asynchronous.
* *
* @param event Event details * @param event Event details
* @param only Flips the operation and <b>includes</b> the listed plugins
* @param plugins The plugins to exclude. Not case sensitive. * @param plugins The plugins to exclude. Not case sensitive.
*/ */
private static void callEventExcluding(Event event, String... plugins) { // Copied from Spigot-API and modified a bit public static void callEventExcluding(Event event, boolean only, String... plugins) { // Copied from Spigot-API and modified a bit
if (event.isAsynchronous()) { if (event.isAsynchronous()) {
if (Thread.holdsLock(Bukkit.getPluginManager())) { if (Thread.holdsLock(Bukkit.getPluginManager())) {
throw new IllegalStateException( throw new IllegalStateException(
@ -191,22 +63,22 @@ public class MCListener implements Listener {
throw new IllegalStateException( throw new IllegalStateException(
event.getEventName() + " cannot be triggered asynchronously from primary server thread."); event.getEventName() + " cannot be triggered asynchronously from primary server thread.");
} }
fireEventExcluding(event, plugins); fireEventExcluding(event, only, plugins);
} else { } else {
synchronized (Bukkit.getPluginManager()) { synchronized (Bukkit.getPluginManager()) {
fireEventExcluding(event, plugins); fireEventExcluding(event, only, plugins);
} }
} }
} }
private static void fireEventExcluding(Event event, String... plugins) { private static void fireEventExcluding(Event event, boolean only, String... plugins) {
HandlerList handlers = event.getHandlers(); // Code taken from SimplePluginManager in Spigot-API HandlerList handlers = event.getHandlers(); // Code taken from SimplePluginManager in Spigot-API
RegisteredListener[] listeners = handlers.getRegisteredListeners(); RegisteredListener[] listeners = handlers.getRegisteredListeners();
val server = Bukkit.getServer(); val server = Bukkit.getServer();
for (RegisteredListener registration : listeners) { for (RegisteredListener registration : listeners) {
if (!registration.getPlugin().isEnabled() if (!registration.getPlugin().isEnabled()
|| Arrays.stream(plugins).anyMatch(p -> p.equalsIgnoreCase(registration.getPlugin().getName()))) || Arrays.stream(plugins).anyMatch(p -> only ^ p.equalsIgnoreCase(registration.getPlugin().getName())))
continue; // Modified to exclude plugins continue; // Modified to exclude plugins
try { try {

View file

@ -1,8 +1,8 @@
package buttondevteam.discordplugin.commands; package buttondevteam.discordplugin.mcchat;
import buttondevteam.discordplugin.DiscordPlayer; import buttondevteam.discordplugin.DiscordPlayer;
import buttondevteam.discordplugin.DiscordPlugin; import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.listeners.MCChatListener; import buttondevteam.discordplugin.commands.DiscordCommandBase;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import sx.blah.discord.handle.obj.IMessage; import sx.blah.discord.handle.obj.IMessage;
@ -13,7 +13,7 @@ public class MCChatCommand extends DiscordCommandBase {
return "mcchat"; return "mcchat";
} }
@Override @Override //TODO: Only register if module is enabled
public boolean run(IMessage message, String args) { public boolean run(IMessage message, String args) {
if (!message.getChannel().isPrivate()) { if (!message.getChannel().isPrivate()) {
DiscordPlugin.sendMessageToChannel(message.getChannel(), DiscordPlugin.sendMessageToChannel(message.getChannel(),
@ -22,10 +22,10 @@ public class MCChatCommand extends DiscordCommandBase {
} }
try (final DiscordPlayer user = DiscordPlayer.getUser(message.getAuthor().getStringID(), DiscordPlayer.class)) { try (final DiscordPlayer user = DiscordPlayer.getUser(message.getAuthor().getStringID(), DiscordPlayer.class)) {
boolean mcchat = !user.isMinecraftChatEnabled(); boolean mcchat = !user.isMinecraftChatEnabled();
MCChatListener.privateMCChat(message.getChannel(), mcchat, message.getAuthor(), user); MCChatPrivate.privateMCChat(message.getChannel(), mcchat, message.getAuthor(), user);
DiscordPlugin.sendMessageToChannel(message.getChannel(), DiscordPlugin.sendMessageToChannel(message.getChannel(),
"Minecraft chat " + (mcchat // "Minecraft chat " + (mcchat //
? "enabled. Use '/mcchat' again to turn it off." // ? "enabled. Use '" + DiscordPlugin.getPrefix() + "mcchat' again to turn it off." //
: "disabled.")); : "disabled."));
} catch (Exception e) { } catch (Exception e) {
TBMCCoreAPI.SendException("Error while setting mcchat for user" + message.getAuthor().getName(), e); TBMCCoreAPI.SendException("Error while setting mcchat for user" + message.getAuthor().getName(), e);
@ -36,8 +36,9 @@ public class MCChatCommand extends DiscordCommandBase {
@Override @Override
public String[] getHelpText() { public String[] getHelpText() {
return new String[] { // return new String[] { //
"mcchat enables or disables the Minecraft chat in private messages.", // DiscordPlugin.getPrefix() + "mcchat enables or disables the Minecraft chat in private messages.", //
"It can be useful if you don't want your messages to be visible, for example when talking a private channel." // "It can be useful if you don't want your messages to be visible, for example when talking in a private channel.", //
"You can also run all of the ingame commands you have access to using this command, if you have your accounts connected." //
}; // TODO: Pin channel switching to indicate the current channel }; // TODO: Pin channel switching to indicate the current channel
} }

View file

@ -0,0 +1,59 @@
package buttondevteam.discordplugin.mcchat;
import buttondevteam.discordplugin.DiscordConnectedPlayer;
import buttondevteam.lib.chat.Channel;
import lombok.NonNull;
import lombok.val;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IUser;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MCChatCustom {
/**
* Used for town or nation chats or anything else
*/
static ArrayList<CustomLMD> lastmsgCustom = new ArrayList<>();
public static void addCustomChat(IChannel channel, String groupid, Channel mcchannel, IUser user, DiscordConnectedPlayer dcp, int toggles) {
val lmd = new CustomLMD(channel, user, groupid, mcchannel, dcp, toggles);
lastmsgCustom.add(lmd);
}
public static boolean hasCustomChat(IChannel channel) {
return lastmsgCustom.stream().anyMatch(lmd -> lmd.channel.getLongID() == channel.getLongID());
}
@Nullable
public static CustomLMD getCustomChat(IChannel channel) {
return lastmsgCustom.stream().filter(lmd -> lmd.channel.getLongID() == channel.getLongID()).findAny().orElse(null);
}
public static boolean removeCustomChat(IChannel channel) {
MCChatUtils.lastmsgfromd.remove(channel.getLongID());
return lastmsgCustom.removeIf(lmd -> lmd.channel.getLongID() == channel.getLongID());
}
public static List<CustomLMD> getCustomChats() {
return Collections.unmodifiableList(lastmsgCustom);
}
public static class CustomLMD extends MCChatUtils.LastMsgData {
public final String groupID;
public final Channel mcchannel;
public final DiscordConnectedPlayer dcp;
public int toggles;
private CustomLMD(@NonNull IChannel channel, @NonNull IUser user,
@NonNull String groupid, @NonNull Channel mcchannel, @NonNull DiscordConnectedPlayer dcp, int toggles) {
super(channel, user);
groupID = groupid;
this.mcchannel = mcchannel;
this.dcp = dcp;
this.toggles = toggles;
}
}
}

View file

@ -1,56 +1,48 @@
package buttondevteam.discordplugin.listeners; package buttondevteam.discordplugin.mcchat;
import buttondevteam.discordplugin.*; import buttondevteam.core.ComponentManager;
import buttondevteam.discordplugin.DPUtils;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.DiscordSender;
import buttondevteam.discordplugin.DiscordSenderBase;
import buttondevteam.discordplugin.listeners.CommandListener;
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener; import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
import buttondevteam.lib.TBMCChatEvent; import buttondevteam.lib.TBMCChatEvent;
import buttondevteam.lib.TBMCChatPreprocessEvent; import buttondevteam.lib.TBMCChatPreprocessEvent;
import buttondevteam.lib.TBMCCoreAPI; import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.chat.Channel; import buttondevteam.lib.chat.Channel;
import buttondevteam.lib.chat.ChatMessage; import buttondevteam.lib.chat.ChatMessage;
import buttondevteam.lib.chat.ChatRoom; import buttondevteam.lib.chat.ChatRoom;
import buttondevteam.lib.chat.TBMCChatAPI; import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.player.TBMCPlayer; import buttondevteam.lib.player.TBMCPlayer;
import com.vdurmont.emoji.EmojiParser; import com.vdurmont.emoji.EmojiParser;
import io.netty.util.collection.LongObjectHashMap;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.experimental.var;
import lombok.val; import lombok.val;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.scheduler.BukkitTask; import org.bukkit.scheduler.BukkitTask;
import sx.blah.discord.api.events.IListener;
import sx.blah.discord.api.internal.json.objects.EmbedObject; import sx.blah.discord.api.internal.json.objects.EmbedObject;
import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent; import sx.blah.discord.handle.impl.events.guild.channel.message.MessageReceivedEvent;
import sx.blah.discord.handle.obj.IChannel; import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IMessage; import sx.blah.discord.handle.obj.IMessage;
import sx.blah.discord.handle.obj.IPrivateChannel;
import sx.blah.discord.handle.obj.IUser; import sx.blah.discord.handle.obj.IUser;
import sx.blah.discord.util.DiscordException; import sx.blah.discord.util.DiscordException;
import sx.blah.discord.util.EmbedBuilder; import sx.blah.discord.util.EmbedBuilder;
import sx.blah.discord.util.MissingPermissionsException; import sx.blah.discord.util.MissingPermissionsException;
import javax.annotation.Nullable;
import java.awt.*; import java.awt.*;
import java.time.Instant; import java.time.Instant;
import java.util.*; import java.util.AbstractMap;
import java.util.List; import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
public class MCChatListener implements Listener, IListener<MessageReceivedEvent> { public class MCChatListener implements Listener {
private BukkitTask sendtask; private BukkitTask sendtask;
private LinkedBlockingQueue<AbstractMap.SimpleEntry<TBMCChatEvent, Instant>> sendevents = new LinkedBlockingQueue<>(); private LinkedBlockingQueue<AbstractMap.SimpleEntry<TBMCChatEvent, Instant>> sendevents = new LinkedBlockingQueue<>();
private Runnable sendrunnable; private Runnable sendrunnable;
@ -58,7 +50,7 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
@EventHandler // Minecraft @EventHandler // Minecraft
public void onMCChat(TBMCChatEvent ev) { public void onMCChat(TBMCChatEvent ev) {
if (DiscordPlugin.SafeMode || ev.isCancelled()) //SafeMode: Needed so it doesn't restart after server shutdown if (!ComponentManager.isEnabled(MinecraftChatModule.class) || ev.isCancelled()) //SafeMode: Needed so it doesn't restart after server shutdown
return; return;
sendevents.add(new AbstractMap.SimpleEntry<>(ev, Instant.now())); sendevents.add(new AbstractMap.SimpleEntry<>(ev, Instant.now()));
if (sendtask != null) if (sendtask != null)
@ -81,7 +73,7 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
time = se.getValue(); time = se.getValue();
final String authorPlayer = "[" + DPUtils.sanitizeStringNoEscape(e.getChannel().DisplayName) + "] " // final String authorPlayer = "[" + DPUtils.sanitizeStringNoEscape(e.getChannel().DisplayName) + "] " //
+ (e.getSender() instanceof DiscordSenderBase ? "[D]" : "") // + ("Minecraft".equals(e.getOrigin()) ? "" : "[" + e.getOrigin().substring(0, 1) + "]") //
+ (DPUtils.sanitizeStringNoEscape(e.getSender() instanceof Player // + (DPUtils.sanitizeStringNoEscape(e.getSender() instanceof Player //
? ((Player) e.getSender()).getDisplayName() // ? ((Player) e.getSender()).getDisplayName() //
: e.getSender().getName())); : e.getSender().getName()));
@ -102,7 +94,7 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
// embed.withFooterText(e.getChannel().DisplayName); // embed.withFooterText(e.getChannel().DisplayName);
embed.withTimestamp(time); embed.withTimestamp(time);
final long nanoTime = System.nanoTime(); final long nanoTime = System.nanoTime();
InterruptibleConsumer<LastMsgData> doit = lastmsgdata -> { InterruptibleConsumer<MCChatUtils.LastMsgData> doit = lastmsgdata -> {
final EmbedObject embedObject = embed.build(); final EmbedObject embedObject = embed.build();
if (lastmsgdata.message == null || lastmsgdata.message.isDeleted() if (lastmsgdata.message == null || lastmsgdata.message.isDeleted()
|| !authorPlayer.equals(lastmsgdata.message.getEmbeds().get(0).getAuthor().getName()) || !authorPlayer.equals(lastmsgdata.message.getEmbeds().get(0).getAuthor().getName())
@ -117,7 +109,7 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
try { try {
lastmsgdata.content = embedObject.description = lastmsgdata.content + "\n" lastmsgdata.content = embedObject.description = lastmsgdata.content + "\n"
+ embedObject.description;// The message object doesn't get updated + embedObject.description;// The message object doesn't get updated
final LastMsgData _lastmsgdata = lastmsgdata; final MCChatUtils.LastMsgData _lastmsgdata = lastmsgdata;
DPUtils.perform(() -> _lastmsgdata.message.edit("", embedObject)); DPUtils.perform(() -> _lastmsgdata.message.edit("", embedObject));
} catch (MissingPermissionsException | DiscordException e1) { } catch (MissingPermissionsException | DiscordException e1) {
TBMCCoreAPI.SendException("An error occurred while editing chat message!", e1); TBMCCoreAPI.SendException("An error occurred while editing chat message!", e1);
@ -129,21 +121,21 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
|| ((DiscordSenderBase) e.getSender()).getChannel().getLongID() != ch.getLongID(); || ((DiscordSenderBase) e.getSender()).getChannel().getLongID() != ch.getLongID();
if (e.getChannel().isGlobal() if (e.getChannel().isGlobal()
&& (e.isFromcmd() || isdifferentchannel.test(DiscordPlugin.chatchannel))) && (e.isFromCommand() || isdifferentchannel.test(DiscordPlugin.chatchannel)))
doit.accept(lastmsgdata == null doit.accept(MCChatUtils.lastmsgdata == null
? lastmsgdata = new LastMsgData(DiscordPlugin.chatchannel, null) ? MCChatUtils.lastmsgdata = new MCChatUtils.LastMsgData(DiscordPlugin.chatchannel, null)
: lastmsgdata); : MCChatUtils.lastmsgdata);
for (LastMsgData data : lastmsgPerUser) { for (MCChatUtils.LastMsgData data : MCChatPrivate.lastmsgPerUser) {
if ((e.isFromcmd() || isdifferentchannel.test(data.channel)) if ((e.isFromCommand() || isdifferentchannel.test(data.channel))
&& e.shouldSendTo(getSender(data.channel, data.user))) && e.shouldSendTo(MCChatUtils.getSender(data.channel, data.user)))
doit.accept(data); doit.accept(data);
} }
val iterator = lastmsgCustom.iterator(); val iterator = MCChatCustom.lastmsgCustom.iterator();
while (iterator.hasNext()) { while (iterator.hasNext()) {
val lmd = iterator.next(); val lmd = iterator.next();
if ((e.isFromcmd() || isdifferentchannel.test(lmd.channel)) //Test if msg is from Discord if ((e.isFromCommand() || isdifferentchannel.test(lmd.channel)) //Test if msg is from Discord
&& e.getChannel().ID.equals(lmd.mcchannel.ID) //If it's from a command, the command msg has been deleted, so we need to send it && e.getChannel().ID.equals(lmd.mcchannel.ID) //If it's from a command, the command msg has been deleted, so we need to send it
&& e.getGroupID().equals(lmd.groupID)) { //Check if this is the group we want to test - #58 && e.getGroupID().equals(lmd.groupID)) { //Check if this is the group we want to test - #58
if (e.shouldSendTo(lmd.dcp)) //Check original user's permissions if (e.shouldSendTo(lmd.dcp)) //Check original user's permissions
@ -162,32 +154,6 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
} }
} }
@RequiredArgsConstructor
public static class LastMsgData {
public IMessage message;
public long time;
public String content;
public final IChannel channel;
public Channel mcchannel;
public final IUser user;
}
public static class CustomLMD extends LastMsgData {
public final String groupID;
public final Channel mcchannel;
public final DiscordConnectedPlayer dcp;
public int toggles;
private CustomLMD(@NonNull IChannel channel, @NonNull IUser user,
@NonNull String groupid, @NonNull Channel mcchannel, @NonNull DiscordConnectedPlayer dcp, int toggles) {
super(channel, user);
groupID = groupid;
this.mcchannel = mcchannel;
this.dcp = dcp;
this.toggles = toggles;
}
}
@EventHandler @EventHandler
public void onChatPreprocess(TBMCChatPreprocessEvent event) { public void onChatPreprocess(TBMCChatPreprocessEvent event) {
int start = -1; int start = -1;
@ -211,72 +177,8 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
private static final String[] UnconnectedCmds = new String[]{"list", "u", "shrug", "tableflip", "unflip", "mwiki", private static final String[] UnconnectedCmds = new String[]{"list", "u", "shrug", "tableflip", "unflip", "mwiki",
"yeehaw", "lenny", "rp", "plugins"}; "yeehaw", "lenny", "rp", "plugins"};
private static LastMsgData lastmsgdata;
private static short lastlist = 0; private static short lastlist = 0;
private static short lastlistp = 0; private static short lastlistp = 0;
/**
* Used for messages in PMs (mcchat).
*/
private static ArrayList<LastMsgData> lastmsgPerUser = new ArrayList<LastMsgData>();
/**
* Used for town or nation chats or anything else
*/
private static ArrayList<CustomLMD> lastmsgCustom = new ArrayList<>();
private static LongObjectHashMap<IMessage> lastmsgfromd = new LongObjectHashMap<>(); // Last message sent by a Discord user, used for clearing checkmarks
public static boolean privateMCChat(IChannel channel, boolean start, IUser user, DiscordPlayer dp) {
TBMCPlayer mcp = dp.getAs(TBMCPlayer.class);
if (mcp != null) { // If the accounts aren't connected, can't make a connected sender
val p = Bukkit.getPlayer(mcp.getUUID());
val op = Bukkit.getOfflinePlayer(mcp.getUUID());
if (start) {
val sender = new DiscordConnectedPlayer(user, channel, mcp.getUUID(), op.getName());
addSender(ConnectedSenders, user, sender);
if (p == null)// Player is offline - If the player is online, that takes precedence
MCListener.callEventExcludingSome(new PlayerJoinEvent(sender, ""));
} else {
val sender = removeSender(ConnectedSenders, channel, user);
if (p == null)// Player is offline - If the player is online, that takes precedence
MCListener.callEventExcludingSome(new PlayerQuitEvent(sender, ""));
}
}
if (!start)
lastmsgfromd.remove(channel.getLongID());
return start //
? lastmsgPerUser.add(new LastMsgData(channel, user)) // Doesn't support group DMs
: lastmsgPerUser.removeIf(lmd -> lmd.channel.getLongID() == channel.getLongID());
}
public static <T extends DiscordSenderBase> T addSender(HashMap<String, HashMap<IChannel, T>> senders,
IUser user, T sender) {
return addSender(senders, user.getStringID(), sender);
}
public static <T extends DiscordSenderBase> T addSender(HashMap<String, HashMap<IChannel, T>> senders,
String did, T sender) {
var map = senders.get(did);
if (map == null)
map = new HashMap<>();
map.put(sender.getChannel(), sender);
senders.put(did, map);
return sender;
}
public static <T extends DiscordSenderBase> T getSender(HashMap<String, HashMap<IChannel, T>> senders,
IChannel channel, IUser user) {
var map = senders.get(user.getStringID());
if (map != null)
return map.get(channel);
return null;
}
public static <T extends DiscordSenderBase> T removeSender(HashMap<String, HashMap<IChannel, T>> senders,
IChannel channel, IUser user) {
var map = senders.get(user.getStringID());
if (map != null)
return map.remove(channel);
return null;
}
// ......................DiscordSender....DiscordConnectedPlayer.DiscordPlayerSender // ......................DiscordSender....DiscordConnectedPlayer.DiscordPlayerSender
// Offline public chat......x............................................ // Offline public chat......x............................................
@ -289,143 +191,8 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
// If online and disabling private chat, don't logout // If online and disabling private chat, don't logout
// The maps may not contain the senders for UnconnectedSenders // The maps may not contain the senders for UnconnectedSenders
public static boolean isMinecraftChatEnabled(DiscordPlayer dp) {
return isMinecraftChatEnabled(dp.getDiscordID());
}
public static boolean isMinecraftChatEnabled(String did) { // Don't load the player data just for this
return lastmsgPerUser.stream()
.anyMatch(lmd -> ((IPrivateChannel) lmd.channel).getRecipient().getStringID().equals(did));
}
public static void addCustomChat(IChannel channel, String groupid, Channel mcchannel, IUser user, DiscordConnectedPlayer dcp, int toggles) {
val lmd = new CustomLMD(channel, user, groupid, mcchannel, dcp, toggles);
lastmsgCustom.add(lmd);
}
public static boolean hasCustomChat(IChannel channel) {
return lastmsgCustom.stream().anyMatch(lmd -> lmd.channel.getLongID() == channel.getLongID());
}
@Nullable
public static CustomLMD getCustomChat(IChannel channel) {
return lastmsgCustom.stream().filter(lmd -> lmd.channel.getLongID() == channel.getLongID()).findAny().orElse(null);
}
public static boolean removeCustomChat(IChannel channel) {
lastmsgfromd.remove(channel.getLongID());
return lastmsgCustom.removeIf(lmd -> lmd.channel.getLongID() == channel.getLongID());
}
public static List<CustomLMD> getCustomChats() {
return Collections.unmodifiableList(lastmsgCustom);
}
/**
* May contain P&lt;DiscordID&gt; as key for public chat
*/
public static final HashMap<String, HashMap<IChannel, DiscordSender>> UnconnectedSenders = new HashMap<>();
public static final HashMap<String, HashMap<IChannel, DiscordConnectedPlayer>> ConnectedSenders = new HashMap<>();
/**
* May contain P&lt;DiscordID&gt; as key for public chat
*/
public static final HashMap<String, HashMap<IChannel, DiscordPlayerSender>> OnlineSenders = new HashMap<>();
public static short ListC = 0; public static short ListC = 0;
/**
* Resets the last message, so it will start a new one instead of appending to it.
* This is used when someone (even the bot) sends a message to the channel.
*
* @param channel The channel to reset in - the process is slightly different for the public, private and custom chats
*/
public static void resetLastMessage(IChannel channel) {
if (channel.getLongID() == DiscordPlugin.chatchannel.getLongID()) {
(lastmsgdata == null ? lastmsgdata = new LastMsgData(DiscordPlugin.chatchannel, null)
: lastmsgdata).message = null;
return;
} // Don't set the whole object to null, the player and channel information should be preserved
for (LastMsgData data : channel.isPrivate() ? lastmsgPerUser : lastmsgCustom) {
if (data.channel.getLongID() == channel.getLongID()) {
data.message = null;
return;
}
}
//If it gets here, it's sending a message to a non-chat channel
}
public static void forAllMCChat(Consumer<IChannel> action) {
action.accept(DiscordPlugin.chatchannel);
for (LastMsgData data : lastmsgPerUser)
action.accept(data.channel);
// lastmsgCustom.forEach(cc -> action.accept(cc.channel)); - Only send relevant messages to custom chat
}
/**
* For custom and all MC chat
*
* @param action The action to act
* @param toggle The toggle to check
* @param hookmsg Whether the message is also sent from the hook
*/
public static void forCustomAndAllMCChat(Consumer<IChannel> action, @Nullable ChannelconBroadcast toggle, boolean hookmsg) {
if (!DiscordPlugin.hooked || !hookmsg)
forAllMCChat(action);
final Consumer<CustomLMD> customLMDConsumer = cc -> action.accept(cc.channel);
if (toggle == null)
lastmsgCustom.forEach(customLMDConsumer);
else
lastmsgCustom.stream().filter(cc -> (cc.toggles & toggle.flag) != 0).forEach(customLMDConsumer);
}
/**
* Do the {@code action} for each custom chat the {@code sender} have access to and has that broadcast type enabled.
*
* @param action The action to do
* @param sender The sender to check perms of or null to send to all that has it toggled
* @param toggle The toggle to check or null to send to all allowed
*/
public static void forAllowedCustomMCChat(Consumer<IChannel> action, @Nullable CommandSender sender, @Nullable ChannelconBroadcast toggle) {
lastmsgCustom.stream().filter(clmd -> {
//new TBMCChannelConnectFakeEvent(sender, clmd.mcchannel).shouldSendTo(clmd.dcp) - Thought it was this simple hehe - Wait, it *should* be this simple
if (toggle != null && (clmd.toggles & toggle.flag) == 0)
return false; //If null then allow
if (sender == null)
return true;
return clmd.groupID.equals(clmd.mcchannel.getGroupID(sender));
}).forEach(cc -> action.accept(cc.channel)); //TODO: Send error messages on channel connect
}
/**
* Do the {@code action} for each custom chat the {@code sender} have access to and has that broadcast type enabled.
*
* @param action The action to do
* @param sender The sender to check perms of or null to send to all that has it toggled
* @param toggle The toggle to check or null to send to all allowed
* @param hookmsg Whether the message is also sent from the hook
*/
public static void forAllowedCustomAndAllMCChat(Consumer<IChannel> action, @Nullable CommandSender sender, @Nullable ChannelconBroadcast toggle, boolean hookmsg) {
if (!DiscordPlugin.hooked || !hookmsg)
forAllMCChat(action);
forAllowedCustomMCChat(action, sender, toggle);
}
public static Consumer<IChannel> send(String message) {
return ch -> DiscordPlugin.sendMessageToChannel(ch, DPUtils.sanitizeString(message));
}
public static void forAllowedMCChat(Consumer<IChannel> action, TBMCSystemChatEvent event) {
if (event.getChannel().isGlobal())
action.accept(DiscordPlugin.chatchannel);
for (LastMsgData data : lastmsgPerUser)
if (event.shouldSendTo(getSender(data.channel, data.user)))
action.accept(data.channel);
lastmsgCustom.stream().filter(clmd -> {
if ((clmd.toggles & ChannelconBroadcast.BROADCAST.flag) == 0)
return false;
return event.shouldSendTo(clmd.dcp);
}).map(clmd -> clmd.channel).forEach(action);
}
/** /**
* Stop the listener. Any calls to onMCChat will restart it as long as we're not in safe mode. * Stop the listener. Any calls to onMCChat will restart it as long as we're not in safe mode.
* *
@ -445,13 +212,13 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
if (wait) if (wait)
recthread.join(5000); recthread.join(5000);
} }
lastmsgdata = null; MCChatUtils.lastmsgdata = null;
lastmsgPerUser.clear(); MCChatPrivate.lastmsgPerUser.clear();
lastmsgCustom.clear(); MCChatCustom.lastmsgCustom.clear();
lastmsgfromd.clear(); MCChatUtils.lastmsgfromd.clear();
ConnectedSenders.clear(); MCChatUtils.ConnectedSenders.clear();
lastlist = lastlistp = ListC = 0; lastlist = lastlistp = ListC = 0;
UnconnectedSenders.clear(); MCChatUtils.UnconnectedSenders.clear();
recthread = sendthread = null; recthread = sendthread = null;
} catch (InterruptedException e) { } catch (InterruptedException e) {
e.printStackTrace(); //This thread shouldn't be interrupted e.printStackTrace(); //This thread shouldn't be interrupted
@ -463,27 +230,28 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
private Runnable recrun; private Runnable recrun;
private static Thread recthread; private static Thread recthread;
@Override // Discord // Discord
public void handle(MessageReceivedEvent ev) { public boolean handleDiscord(MessageReceivedEvent ev) {
if (DiscordPlugin.SafeMode) if (!ComponentManager.isEnabled(MinecraftChatModule.class))
return; return false;
val author = ev.getMessage().getAuthor(); val author = ev.getMessage().getAuthor();
if (author.isBot()) final boolean hasCustomChat = MCChatCustom.hasCustomChat(ev.getChannel());
return; if (ev.getMessage().getChannel().getLongID() != DiscordPlugin.chatchannel.getLongID()
final boolean hasCustomChat = hasCustomChat(ev.getChannel()); && !(ev.getMessage().getChannel().isPrivate() && MCChatPrivate.isMinecraftChatEnabled(author.getStringID()))
if (!ev.getMessage().getChannel().getStringID().equals(DiscordPlugin.chatchannel.getStringID())
&& !(ev.getMessage().getChannel().isPrivate() && isMinecraftChatEnabled(author.getStringID()))
&& !hasCustomChat) && !hasCustomChat)
return; return false; //Chat isn't enabled on this channel
if (ev.getMessage().getContent().equalsIgnoreCase("mcchat")) if (ev.getMessage().getChannel().isPrivate() //Only in private chat
return; // Race condition: If it gets here after it enabled mcchat it says it - I might as well allow disabling with this (CommandListener) && ev.getMessage().getContent().length() < "/mcchat<>".length()
&& ev.getMessage().getContent().replace("/", "")
.equalsIgnoreCase("mcchat")) //Either mcchat or /mcchat
return false; //Allow disabling the chat if needed
if (CommandListener.runCommand(ev.getMessage(), true)) if (CommandListener.runCommand(ev.getMessage(), true))
return; return true; //Allow running commands in chat channels
resetLastMessage(ev.getChannel()); MCChatUtils.resetLastMessage(ev.getChannel());
lastlist++; lastlist++;
recevents.add(ev); recevents.add(ev);
if (rectask != null) if (rectask != null)
return; return true;
recrun = () -> { //Don't return in a while loop next time recrun = () -> { //Don't return in a while loop next time
recthread = Thread.currentThread(); recthread = Thread.currentThread();
processDiscordToMC(); processDiscordToMC();
@ -491,6 +259,7 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Continue message processing rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Continue message processing
}; };
rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Start message processing rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Start message processing
return true;
} }
private void processDiscordToMC() { private void processDiscordToMC() {
@ -505,7 +274,7 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
val sender = event.getMessage().getAuthor(); val sender = event.getMessage().getAuthor();
String dmessage = event.getMessage().getContent(); String dmessage = event.getMessage().getContent();
try { try {
final DiscordSenderBase dsender = getSender(event.getMessage().getChannel(), sender); final DiscordSenderBase dsender = MCChatUtils.getSender(event.getMessage().getChannel(), sender);
val user = dsender.getChromaUser(); val user = dsender.getChromaUser();
for (IUser u : event.getMessage().getMentions()) { for (IUser u : event.getMessage().getMentions()) {
@ -525,7 +294,7 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
.getAttachments().stream().map(IMessage.Attachment::getUrl).collect(Collectors.joining("\n")) .getAttachments().stream().map(IMessage.Attachment::getUrl).collect(Collectors.joining("\n"))
: ""); : "");
CustomLMD clmd = getCustomChat(event.getChannel()); MCChatCustom.CustomLMD clmd = MCChatCustom.getCustomChat(event.getChannel());
boolean react = false; boolean react = false;
@ -570,18 +339,13 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
() -> { //TODO: Better handling... () -> { //TODO: Better handling...
val channel = user.channel(); val channel = user.channel();
val chtmp = channel.get(); val chtmp = channel.get();
//System.out.println("1: "+chtmp.ID);
//System.out.println("clmd: "+clmd);
if (clmd != null) { if (clmd != null) {
channel.set(clmd.mcchannel); //Hack to send command in the channel channel.set(clmd.mcchannel); //Hack to send command in the channel
//System.out.println("clmd chan: "+clmd.mcchannel.ID);
} //TODO: Permcheck isn't implemented for commands } //TODO: Permcheck isn't implemented for commands
//System.out.println("2: "+channel.get().ID);
VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd); VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd);
Bukkit.getLogger().info(dsender.getName() + " issued command from Discord: /" + cmdlowercased); Bukkit.getLogger().info(dsender.getName() + " issued command from Discord: /" + cmdlowercased);
if (clmd != null) if (clmd != null)
channel.set(chtmp); channel.set(chtmp);
//System.out.println("3: "+channel.get().ID); - TODO: Remove
}); });
else { else {
Channel chc = ch.get(); Channel chc = ch.get();
@ -619,8 +383,9 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
} else {// Not a command } else {// Not a command
if (dmessage.length() == 0 && event.getMessage().getAttachments().size() == 0 if (dmessage.length() == 0 && event.getMessage().getAttachments().size() == 0
&& !event.getChannel().isPrivate() && event.getMessage().isSystemMessage()) { && !event.getChannel().isPrivate() && event.getMessage().isSystemMessage()) {
val rtr = clmd != null ? clmd.mcchannel.filteranderrormsg.apply(clmd.dcp) : dsender.getChromaUser().channel().get().filteranderrormsg.apply(dsender); val rtr = clmd != null ? clmd.mcchannel.getRTR(clmd.dcp)
TBMCChatAPI.SendSystemMessage(clmd != null ? clmd.mcchannel : dsender.getChromaUser().channel().get(), rtr.score, rtr.groupID, : dsender.getChromaUser().channel().get().getRTR(dsender);
TBMCChatAPI.SendSystemMessage(clmd != null ? clmd.mcchannel : dsender.getChromaUser().channel().get(), rtr,
(dsender instanceof Player ? ((Player) dsender).getDisplayName() (dsender instanceof Player ? ((Player) dsender).getDisplayName()
: dsender.getName()) + " pinned a message on Discord."); : dsender.getName()) + " pinned a message on Discord.");
} }
@ -635,7 +400,7 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
} }
if (react) { if (react) {
try { try {
val lmfd = lastmsgfromd.get(event.getChannel().getLongID()); val lmfd = MCChatUtils.lastmsgfromd.get(event.getChannel().getLongID());
if (lmfd != null) { if (lmfd != null) {
DPUtils.perform(() -> lmfd.removeReaction(DiscordPlugin.dc.getOurUser(), DPUtils.perform(() -> lmfd.removeReaction(DiscordPlugin.dc.getOurUser(),
DiscordPlugin.DELIVERED_REACTION)); // Remove it no matter what, we know it's there 99.99% of the time DiscordPlugin.DELIVERED_REACTION)); // Remove it no matter what, we know it's there 99.99% of the time
@ -643,7 +408,7 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
} catch (Exception e) { } catch (Exception e) {
TBMCCoreAPI.SendException("An error occured while removing reactions from chat!", e); TBMCCoreAPI.SendException("An error occured while removing reactions from chat!", e);
} }
lastmsgfromd.put(event.getChannel().getLongID(), event.getMessage()); MCChatUtils.lastmsgfromd.put(event.getChannel().getLongID(), event.getMessage());
DPUtils.perform(() -> event.getMessage().addReaction(DiscordPlugin.DELIVERED_REACTION)); DPUtils.perform(() -> event.getMessage().addReaction(DiscordPlugin.DELIVERED_REACTION));
} }
} catch (Exception e) { } catch (Exception e) {
@ -651,19 +416,6 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
} }
} }
/**
* This method will find the best sender to use: if the player is online, use that, if not but connected then use that etc.
*/
private static DiscordSenderBase getSender(IChannel channel, final IUser author) {
//noinspection OptionalGetWithoutIsPresent
return Stream.<Supplier<Optional<DiscordSenderBase>>>of( // https://stackoverflow.com/a/28833677/2703239
() -> Optional.ofNullable(getSender(OnlineSenders, channel, author)), // Find first non-null
() -> Optional.ofNullable(getSender(ConnectedSenders, channel, author)), // This doesn't support the public chat, but it'll always return null for it
() -> Optional.ofNullable(getSender(OnlineSenders, channel, author)), //
() -> Optional.of(addSender(UnconnectedSenders, author,
new DiscordSender(author, channel)))).map(Supplier::get).filter(Optional::isPresent).map(Optional::get).findFirst().get();
}
@FunctionalInterface @FunctionalInterface
private interface InterruptibleConsumer<T> { private interface InterruptibleConsumer<T> {
void accept(T value) throws TimeoutException, InterruptedException; void accept(T value) throws TimeoutException, InterruptedException;

View file

@ -0,0 +1,69 @@
package buttondevteam.discordplugin.mcchat;
import buttondevteam.discordplugin.DiscordConnectedPlayer;
import buttondevteam.discordplugin.DiscordPlayer;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.player.TBMCPlayer;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.event.Event;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IPrivateChannel;
import sx.blah.discord.handle.obj.IUser;
import java.util.ArrayList;
import static buttondevteam.discordplugin.listeners.MCListener.callEventExcludingSome;
public class MCChatPrivate {
/**
* Used for messages in PMs (mcchat).
*/
static ArrayList<MCChatUtils.LastMsgData> lastmsgPerUser = new ArrayList<>();
public static boolean privateMCChat(IChannel channel, boolean start, IUser user, DiscordPlayer dp) {
TBMCPlayer mcp = dp.getAs(TBMCPlayer.class);
if (mcp != null) { // If the accounts aren't connected, can't make a connected sender
val p = Bukkit.getPlayer(mcp.getUUID());
val op = Bukkit.getOfflinePlayer(mcp.getUUID());
if (start) {
val sender = new DiscordConnectedPlayer(user, channel, mcp.getUUID(), op.getName());
MCChatUtils.addSender(MCChatUtils.ConnectedSenders, user, sender);
if (p == null)// Player is offline - If the player is online, that takes precedence
callEventSync(new PlayerJoinEvent(sender, ""));
} else {
val sender = MCChatUtils.removeSender(MCChatUtils.ConnectedSenders, channel, user);
if (p == null)// Player is offline - If the player is online, that takes precedence
callEventSync(new PlayerQuitEvent(sender, ""));
}
} // ---- PermissionsEx warning is normal on logout ----
if (!start)
MCChatUtils.lastmsgfromd.remove(channel.getLongID());
return start //
? lastmsgPerUser.add(new MCChatUtils.LastMsgData(channel, user)) // Doesn't support group DMs
: lastmsgPerUser.removeIf(lmd -> lmd.channel.getLongID() == channel.getLongID());
}
public static boolean isMinecraftChatEnabled(DiscordPlayer dp) {
return isMinecraftChatEnabled(dp.getDiscordID());
}
public static boolean isMinecraftChatEnabled(String did) { // Don't load the player data just for this
return lastmsgPerUser.stream()
.anyMatch(lmd -> ((IPrivateChannel) lmd.channel).getRecipient().getStringID().equals(did));
}
public static void logoutAll() {
for (val entry : MCChatUtils.ConnectedSenders.entrySet())
for (val valueEntry : entry.getValue().entrySet())
callEventExcludingSome(new PlayerQuitEvent(valueEntry.getValue(), "")); //This is sync
MCChatUtils.ConnectedSenders.clear();
}
private static void callEventSync(Event event) {
Bukkit.getScheduler().runTask(DiscordPlugin.plugin, () -> callEventExcludingSome(event));
}
}

View file

@ -0,0 +1,218 @@
package buttondevteam.discordplugin.mcchat;
import buttondevteam.core.ComponentManager;
import buttondevteam.discordplugin.*;
import buttondevteam.discordplugin.broadcaster.GeneralEventBroadcasterModule;
import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.chat.Channel;
import io.netty.util.collection.LongObjectHashMap;
import lombok.RequiredArgsConstructor;
import lombok.experimental.var;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IMessage;
import sx.blah.discord.handle.obj.IUser;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class MCChatUtils {
/**
* May contain P&lt;DiscordID&gt; as key for public chat
*/
public static final HashMap<String, HashMap<IChannel, DiscordSender>> UnconnectedSenders = new HashMap<>();
public static final HashMap<String, HashMap<IChannel, DiscordConnectedPlayer>> ConnectedSenders = new HashMap<>();
/**
* May contain P&lt;DiscordID&gt; as key for public chat
*/
public static final HashMap<String, HashMap<IChannel, DiscordPlayerSender>> OnlineSenders = new HashMap<>();
static @Nullable LastMsgData lastmsgdata;
static LongObjectHashMap<IMessage> lastmsgfromd = new LongObjectHashMap<>(); // Last message sent by a Discord user, used for clearing checkmarks
public static void updatePlayerList() {
if (notEnabled()) return;
DPUtils.performNoWait(() -> {
if (lastmsgdata != null)
updatePL(lastmsgdata);
MCChatCustom.lastmsgCustom.forEach(MCChatUtils::updatePL);
});
}
private static boolean notEnabled() {
return !ComponentManager.isEnabled(MinecraftChatModule.class);
}
private static void updatePL(LastMsgData lmd) {
String topic = lmd.channel.getTopic();
if (topic == null || topic.length() == 0)
topic = ".\n----\nMinecraft chat\n----\n.";
String[] s = topic.split("\\n----\\n");
if (s.length < 3)
return;
s[0] = Bukkit.getOnlinePlayers().size() + " player" + (Bukkit.getOnlinePlayers().size() != 1 ? "s" : "")
+ " online";
s[s.length - 1] = "Players: " + Bukkit.getOnlinePlayers().stream()
.map(p -> DPUtils.sanitizeString(p.getDisplayName())).collect(Collectors.joining(", "));
lmd.channel.changeTopic(String.join("\n----\n", s));
}
public static <T extends DiscordSenderBase> T addSender(HashMap<String, HashMap<IChannel, T>> senders,
IUser user, T sender) {
return addSender(senders, user.getStringID(), sender);
}
public static <T extends DiscordSenderBase> T addSender(HashMap<String, HashMap<IChannel, T>> senders,
String did, T sender) {
var map = senders.get(did);
if (map == null)
map = new HashMap<>();
map.put(sender.getChannel(), sender);
senders.put(did, map);
return sender;
}
public static <T extends DiscordSenderBase> T getSender(HashMap<String, HashMap<IChannel, T>> senders,
IChannel channel, IUser user) {
var map = senders.get(user.getStringID());
if (map != null)
return map.get(channel);
return null;
}
public static <T extends DiscordSenderBase> T removeSender(HashMap<String, HashMap<IChannel, T>> senders,
IChannel channel, IUser user) {
var map = senders.get(user.getStringID());
if (map != null)
return map.remove(channel);
return null;
}
public static void forAllMCChat(Consumer<IChannel> action) {
if (notEnabled()) return;
action.accept(DiscordPlugin.chatchannel);
for (LastMsgData data : MCChatPrivate.lastmsgPerUser)
action.accept(data.channel);
// lastmsgCustom.forEach(cc -> action.accept(cc.channel)); - Only send relevant messages to custom chat
}
/**
* For custom and all MC chat
*
* @param action The action to act
* @param toggle The toggle to check
* @param hookmsg Whether the message is also sent from the hook
*/
public static void forCustomAndAllMCChat(Consumer<IChannel> action, @Nullable ChannelconBroadcast toggle, boolean hookmsg) {
if (notEnabled()) return;
if (!GeneralEventBroadcasterModule.isHooked() || !hookmsg)
forAllMCChat(action);
final Consumer<MCChatCustom.CustomLMD> customLMDConsumer = cc -> action.accept(cc.channel);
if (toggle == null)
MCChatCustom.lastmsgCustom.forEach(customLMDConsumer);
else
MCChatCustom.lastmsgCustom.stream().filter(cc -> (cc.toggles & toggle.flag) != 0).forEach(customLMDConsumer);
}
/**
* Do the {@code action} for each custom chat the {@code sender} have access to and has that broadcast type enabled.
*
* @param action The action to do
* @param sender The sender to check perms of or null to send to all that has it toggled
* @param toggle The toggle to check or null to send to all allowed
*/
public static void forAllowedCustomMCChat(Consumer<IChannel> action, @Nullable CommandSender sender, @Nullable ChannelconBroadcast toggle) {
if (notEnabled()) return;
MCChatCustom.lastmsgCustom.stream().filter(clmd -> {
//new TBMCChannelConnectFakeEvent(sender, clmd.mcchannel).shouldSendTo(clmd.dcp) - Thought it was this simple hehe - Wait, it *should* be this simple
if (toggle != null && (clmd.toggles & toggle.flag) == 0)
return false; //If null then allow
if (sender == null)
return true;
return clmd.groupID.equals(clmd.mcchannel.getGroupID(sender));
}).forEach(cc -> action.accept(cc.channel)); //TODO: Send error messages on channel connect
}
/**
* Do the {@code action} for each custom chat the {@code sender} have access to and has that broadcast type enabled.
*
* @param action The action to do
* @param sender The sender to check perms of or null to send to all that has it toggled
* @param toggle The toggle to check or null to send to all allowed
* @param hookmsg Whether the message is also sent from the hook
*/
public static void forAllowedCustomAndAllMCChat(Consumer<IChannel> action, @Nullable CommandSender sender, @Nullable ChannelconBroadcast toggle, boolean hookmsg) {
if (notEnabled()) return;
if (!GeneralEventBroadcasterModule.isHooked() || !hookmsg)
forAllMCChat(action);
forAllowedCustomMCChat(action, sender, toggle);
}
public static Consumer<IChannel> send(String message) {
return ch -> DiscordPlugin.sendMessageToChannel(ch, DPUtils.sanitizeString(message));
}
public static void forAllowedMCChat(Consumer<IChannel> action, TBMCSystemChatEvent event) {
if (notEnabled()) return;
if (event.getChannel().isGlobal())
action.accept(DiscordPlugin.chatchannel);
for (LastMsgData data : MCChatPrivate.lastmsgPerUser)
if (event.shouldSendTo(getSender(data.channel, data.user)))
action.accept(data.channel);
MCChatCustom.lastmsgCustom.stream().filter(clmd -> {
if ((clmd.toggles & ChannelconBroadcast.BROADCAST.flag) == 0)
return false;
return event.shouldSendTo(clmd.dcp);
}).map(clmd -> clmd.channel).forEach(action);
}
/**
* This method will find the best sender to use: if the player is online, use that, if not but connected then use that etc.
*/
static DiscordSenderBase getSender(IChannel channel, final IUser author) {
//noinspection OptionalGetWithoutIsPresent
return Stream.<Supplier<Optional<DiscordSenderBase>>>of( // https://stackoverflow.com/a/28833677/2703239
() -> Optional.ofNullable(getSender(OnlineSenders, channel, author)), // Find first non-null
() -> Optional.ofNullable(getSender(ConnectedSenders, channel, author)), // This doesn't support the public chat, but it'll always return null for it
() -> Optional.ofNullable(getSender(UnconnectedSenders, channel, author)), //
() -> Optional.of(addSender(UnconnectedSenders, author,
new DiscordSender(author, channel)))).map(Supplier::get).filter(Optional::isPresent).map(Optional::get).findFirst().get();
}
/**
* Resets the last message, so it will start a new one instead of appending to it.
* This is used when someone (even the bot) sends a message to the channel.
*
* @param channel The channel to reset in - the process is slightly different for the public, private and custom chats
*/
public static void resetLastMessage(IChannel channel) {
if (notEnabled()) return;
if (channel.getLongID() == DiscordPlugin.chatchannel.getLongID()) {
(lastmsgdata == null ? lastmsgdata = new LastMsgData(DiscordPlugin.chatchannel, null)
: lastmsgdata).message = null;
return;
} // Don't set the whole object to null, the player and channel information should be preserved
for (LastMsgData data : channel.isPrivate() ? MCChatPrivate.lastmsgPerUser : MCChatCustom.lastmsgCustom) {
if (data.channel.getLongID() == channel.getLongID()) {
data.message = null;
return;
}
}
//If it gets here, it's sending a message to a non-chat channel
}
@RequiredArgsConstructor
public static class LastMsgData {
public IMessage message;
public long time;
public String content;
public final IChannel channel;
public Channel mcchannel;
public final IUser user;
}
}

View file

@ -0,0 +1,153 @@
package buttondevteam.discordplugin.mcchat;
import buttondevteam.discordplugin.*;
import buttondevteam.discordplugin.commands.ConnectCommand;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.TBMCSystemChatEvent;
import buttondevteam.lib.player.*;
import com.earth2me.essentials.CommandSource;
import lombok.val;
import net.ess3.api.events.AfkStatusChangeEvent;
import net.ess3.api.events.MuteStatusChangeEvent;
import net.ess3.api.events.NickChangeEvent;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerKickEvent;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.player.PlayerLoginEvent.Result;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.server.BroadcastMessageEvent;
import sx.blah.discord.handle.obj.IRole;
import sx.blah.discord.handle.obj.IUser;
import sx.blah.discord.util.DiscordException;
import sx.blah.discord.util.MissingPermissionsException;
class MCListener implements Listener {
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerLogin(PlayerLoginEvent e) {
if (e.getResult() != Result.ALLOWED)
return;
MCChatUtils.ConnectedSenders.values().stream().flatMap(v -> v.values().stream()) //Only private mcchat should be in ConnectedSenders
.filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny()
.ifPresent(dcp -> buttondevteam.discordplugin.listeners.MCListener.callEventExcludingSome(new PlayerQuitEvent(dcp, "")));
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerJoin(TBMCPlayerJoinEvent e) {
if (e.getPlayer() instanceof DiscordConnectedPlayer)
return; // Don't show the joined message for the fake player
Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, () -> {
final Player p = e.getPlayer();
DiscordPlayer dp = e.GetPlayer().getAs(DiscordPlayer.class);
if (dp != null) {
val user = DiscordPlugin.dc.getUserByID(Long.parseLong(dp.getDiscordID()));
MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(),
new DiscordPlayerSender(user, user.getOrCreatePMChannel(), p));
MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID(),
new DiscordPlayerSender(user, DiscordPlugin.chatchannel, p)); //Stored per-channel
}
if (ConnectCommand.WaitingToConnect.containsKey(e.GetPlayer().PlayerName().get())) {
IUser user = DiscordPlugin.dc
.getUserByID(Long.parseLong(ConnectCommand.WaitingToConnect.get(e.GetPlayer().PlayerName().get())));
p.sendMessage("§bTo connect with the Discord account @" + user.getName() + "#" + user.getDiscriminator()
+ " do /discord accept");
p.sendMessage("§bIf it wasn't you, do /discord decline");
}
final String message = e.GetPlayer().PlayerName().get() + " joined the game";
MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(message), e.getPlayer(), ChannelconBroadcast.JOINLEAVE, true);
MCChatListener.ListC = 0;
ChromaBot.getInstance().updatePlayerList();
});
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerLeave(TBMCPlayerQuitEvent e) {
if (e.getPlayer() instanceof DiscordConnectedPlayer)
return; // Only care about real users
MCChatUtils.OnlineSenders.entrySet()
.removeIf(entry -> entry.getValue().entrySet().stream().anyMatch(p -> p.getValue().getUniqueId().equals(e.getPlayer().getUniqueId())));
Bukkit.getScheduler().runTask(DiscordPlugin.plugin,
() -> MCChatUtils.ConnectedSenders.values().stream().flatMap(v -> v.values().stream())
.filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny()
.ifPresent(dcp -> buttondevteam.discordplugin.listeners.MCListener.callEventExcludingSome(new PlayerJoinEvent(dcp, ""))));
Bukkit.getScheduler().runTaskLaterAsynchronously(DiscordPlugin.plugin,
ChromaBot.getInstance()::updatePlayerList, 5);
final String message = e.GetPlayer().PlayerName().get() + " left the game";
MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(message), e.getPlayer(), ChannelconBroadcast.JOINLEAVE, true);
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerKick(PlayerKickEvent e) {
/*if (!DiscordPlugin.hooked && !e.getReason().equals("The server is restarting")
&& !e.getReason().equals("Server closed")) // The leave messages errored with the previous setup, I could make it wait since I moved it here, but instead I have a special
MCChatListener.forAllowedCustomAndAllMCChat(e.getPlayer().getName() + " left the game"); // message for this - Oh wait this doesn't even send normally because of the hook*/
}
@EventHandler(priority = EventPriority.LOW)
public void onPlayerDeath(PlayerDeathEvent e) {
MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(e.getDeathMessage()), e.getEntity(), ChannelconBroadcast.DEATH, true);
}
@EventHandler
public void onPlayerAFK(AfkStatusChangeEvent e) {
final Player base = e.getAffected().getBase();
if (e.isCancelled() || !base.isOnline())
return;
final String msg = base.getDisplayName()
+ " is " + (e.getValue() ? "now" : "no longer") + " AFK.";
MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(msg), base, ChannelconBroadcast.AFK, false);
}
@EventHandler
public void onPlayerMute(MuteStatusChangeEvent e) {
try {
DPUtils.performNoWait(() -> {
final IRole role = DiscordPlugin.dc.getRoleByID(164090010461667328L);
final CommandSource source = e.getAffected().getSource();
if (!source.isPlayer())
return;
final DiscordPlayer p = TBMCPlayerBase.getPlayer(source.getPlayer().getUniqueId(), TBMCPlayer.class)
.getAs(DiscordPlayer.class);
if (p == null) return;
final IUser user = DiscordPlugin.dc.getUserByID(
Long.parseLong(p.getDiscordID()));
if (e.getValue())
user.addRole(role);
else
user.removeRole(role);
DiscordPlugin.sendMessageToChannel(DiscordPlugin.modlogchannel, (e.getValue() ? "M" : "Unm") + "uted user: " + user.getName());
});
} catch (DiscordException | MissingPermissionsException ex) {
TBMCCoreAPI.SendException("Failed to give/take Muted role to player " + e.getAffected().getName() + "!",
ex);
}
}
@EventHandler
public void onChatSystemMessage(TBMCSystemChatEvent event) {
MCChatUtils.forAllowedMCChat(MCChatUtils.send(event.getMessage()), event);
}
@EventHandler
public void onBroadcastMessage(BroadcastMessageEvent event) {
MCChatUtils.forCustomAndAllMCChat(MCChatUtils.send(event.getMessage()), ChannelconBroadcast.BROADCAST, false);
}
@EventHandler
public void onYEEHAW(TBMCYEEHAWEvent event) { //TODO: Inherit from the chat event base to have channel support
String name = event.getSender() instanceof Player ? ((Player) event.getSender()).getDisplayName()
: event.getSender().getName();
//Channel channel = ChromaGamerBase.getFromSender(event.getSender()).channel().get(); - TODO
MCChatUtils.forAllMCChat(MCChatUtils.send(name + " <:YEEHAW:" + DiscordPlugin.mainServer.getEmojiByName("YEEHAW").getStringID() + ">s"));
}
@EventHandler
public void onNickChange(NickChangeEvent event) {
MCChatUtils.updatePlayerList();
}
}

View file

@ -0,0 +1,26 @@
package buttondevteam.discordplugin.mcchat;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.architecture.Component;
import lombok.Getter;
public class MinecraftChatModule extends Component {
private @Getter MCChatListener listener;
public MCChatListener getListener() { //It doesn't want to generate
return listener;
}
@Override
protected void enable() {
listener = new MCChatListener();
DiscordPlugin.dc.getDispatcher().registerListener(listener);
TBMCCoreAPI.RegisterEventsForExceptions(listener, getPlugin());
TBMCCoreAPI.RegisterEventsForExceptions(new MCListener(), getPlugin());
}
@Override
protected void disable() {
//These get undone if restarting/resetting - it will ignore events if disabled
} //TODO: Use ComponentManager.isEnabled() at other places too, instead of SafeMode
}

View file

@ -2,7 +2,7 @@ package buttondevteam.discordplugin.mccommands;
import buttondevteam.discordplugin.DiscordPlayer; import buttondevteam.discordplugin.DiscordPlayer;
import buttondevteam.discordplugin.commands.ConnectCommand; import buttondevteam.discordplugin.commands.ConnectCommand;
import buttondevteam.discordplugin.listeners.MCChatListener; import buttondevteam.discordplugin.mcchat.MCChatUtils;
import buttondevteam.lib.chat.CommandClass; import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.player.ChromaGamerBase; import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.TBMCPlayer; import buttondevteam.lib.player.TBMCPlayer;
@ -35,7 +35,7 @@ public class AcceptMCCommand extends DiscordMCCommandBase {
dp.save(); dp.save();
mcp.save(); mcp.save();
ConnectCommand.WaitingToConnect.remove(player.getName()); ConnectCommand.WaitingToConnect.remove(player.getName());
MCChatListener.UnconnectedSenders.remove(did); //Remove all unconnected, will be recreated where needed MCChatUtils.UnconnectedSenders.remove(did); //Remove all unconnected, will be recreated where needed
player.sendMessage("§bAccounts connected."); player.sendMessage("§bAccounts connected.");
return true; return true;
} }