Lot of bugfixes and improvements for mcchat

#12 (PM support should be finished)
#13 works
In no particular order:
Fixed duplicate responses, messages, wrong quit/join event calls, made
the private chat enabled state only persist until the server is
restarted, testing message always appears in PMs, added separate senders
for public and private chat, sending all kinds of messages to private
chat as well, adjusted message for non-whitelisted cmds, changed a lot
on sender getting etc.

1-2 hours of sleep, then work, then programming in the morning, more
sleep, more programming throughout the day.
This commit is contained in:
Norbi Peti 2017-07-12 00:00:19 +02:00
parent abe8af501e
commit a501d9d457
7 changed files with 97 additions and 75 deletions

View file

@ -1,12 +1,13 @@
package buttondevteam.discordplugin;
import buttondevteam.discordplugin.listeners.MCChatListener;
import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.PlayerData;
import buttondevteam.lib.player.UserClass;
@UserClass(foldername = "discord")
public class DiscordPlayer extends ChromaGamerBase {
private String did;
// private @Getter @Setter boolean minecraftChatEnabled;
public DiscordPlayer() {
}
@ -17,7 +18,11 @@ public class DiscordPlayer extends ChromaGamerBase {
return did;
}
public PlayerData<Boolean> minecraftChat() {
return data(false);
/**
* 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)}
*/
public boolean isMinecraftChatEnabled() {
return MCChatListener.isMinecraftChatEnabled(this);
}
}

View file

@ -256,7 +256,8 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
if (channel == chatchannel)
MCChatListener.resetLastMessage(); // If this is a chat message, it'll be set again
final String content = TBMCCoreAPI.IsTestServer() && channel != chatchannel || channel == botroomchannel // Both are the same for testing
? "*The following message is from a test server*\n" + message : message;
|| channel.isPrivate() //
? "*The following message is from a test server*\n" + message : message;
return perform(
() -> embed == null ? channel.sendMessage(content) : channel.sendMessage(content, embed, false));
} catch (Exception e) {

View file

@ -1,9 +1,9 @@
package buttondevteam.discordplugin.commands;
import buttondevteam.discordplugin.DiscordPlayer;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.listeners.MCChatListener;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.player.PlayerData;
import sx.blah.discord.handle.obj.IMessage;
public class MCChatCommand extends DiscordCommandBase {
@ -16,17 +16,18 @@ public class MCChatCommand extends DiscordCommandBase {
@Override
public void run(IMessage message, String args) {
if (!message.getChannel().isPrivate()) {
message.reply("This command can only be issued in a direct message with the bot.");
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"This command can only be issued in a direct message with the bot.");
return;
}
try (final DiscordPlayer user = DiscordPlayer.getUser(message.getAuthor().getStringID(), DiscordPlayer.class)) {
PlayerData<Boolean> mcchat = user.minecraftChat();
mcchat.set(!mcchat.get());
MCChatListener.privateMCChat(message.getChannel(), mcchat.get(), message.getAuthor(), user);
message.reply("Minecraft chat " + (mcchat.get() //
? "enabled. Use '" + message.getClient().getOurUser().mention()
+ " mcchat' (with the mention) to disable." //
: "disabled."));
boolean mcchat = !user.isMinecraftChatEnabled();
MCChatListener.privateMCChat(message.getChannel(), mcchat, message.getAuthor(), user);
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"Minecraft chat " + (mcchat //
? "enabled. Use '" + message.getClient().getOurUser().mention()
+ " mcchat' (with the mention) to disable." //
: "disabled."));
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while setting mcchat for user" + message.getAuthor().getName(), e);
}

View file

@ -59,13 +59,10 @@ public class CommandListener {
if (event.getMessage().getAuthor().isBot())
return;
final IChannel channel = event.getMessage().getChannel();
if (!channel.getStringID().equals(DiscordPlugin.botchannel.getStringID())
&& (!channel.isPrivate() || DiscordPlugin.checkIfSomeoneIsTestingWhileWeArent()))
if (!channel.getStringID().equals(DiscordPlugin.botchannel.getStringID()))
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
if (DiscordPlayer.getUser(event.getAuthor().getStringID(), DiscordPlayer.class).minecraftChat().get()) // Let the MCChatListener handle it
return;
event.getMessage().getChannel().setTypingStatus(true); // Fun
runCommand(event.getMessage(), true);
}
@ -84,8 +81,8 @@ public class CommandListener {
DiscordPlugin.sendMessageToChannel(event.getMessage().getChannel(), serverReadyStrings[next]);
}
if (!event.getMessage().getChannel().isPrivate() //
|| DiscordPlayer.getUser(event.getAuthor().getStringID(), DiscordPlayer.class).minecraftChat()
.get()
|| DiscordPlayer.getUser(event.getAuthor().getStringID(), DiscordPlayer.class)
.isMinecraftChatEnabled()
|| DiscordPlugin.checkIfSomeoneIsTestingWhileWeArent())
return;
if (event.getMessage().getAuthor().isBot())

View file

@ -4,11 +4,12 @@ import java.awt.Color;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.UUID;
import java.util.function.BiFunction;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@ -75,7 +76,7 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
final IUser iUser = data.channel.getUsersHere().stream()
.filter(u -> u.getLongID() != u.getClient().getOurUser().getLongID()).findFirst().get(); // Doesn't support group DMs
final DiscordPlayer user = DiscordPlayer.getUser(iUser.getStringID(), DiscordPlayer.class);
if (user.minecraftChat().get() && e.shouldSendTo(getSender(data.channel, iUser, user)))
if (user.isMinecraftChatEnabled() && e.shouldSendTo(getSender(data.channel, iUser, user)))
doit.accept(data);
}
} // TODO: Author URL
@ -132,11 +133,11 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
if (start) {
val sender = new DiscordConnectedPlayer(user, channel, mcp.getUUID());
ConnectedSenders.put(user.getStringID(), sender);
if (p == null)// If the player is online, that takes precedence
if (p == null)// Player is offline - If the player is online, that takes precedence
Bukkit.getPluginManager().callEvent(new PlayerJoinEvent(sender, ""));
} else {
val sender = ConnectedSenders.remove(user.getStringID());
if (p == null)// If the player is online, that takes precedence
if (p == null)// Player is offline - If the player is online, that takes precedence
Bukkit.getPluginManager().callEvent(new PlayerQuitEvent(sender, ""));
}
}
@ -145,7 +146,6 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
: lastmsgPerUser.removeIf(lmd -> lmd.channel.getLongID() == channel.getLongID());
}
//
// ......................DiscordSender....DiscordConnectedPlayer.DiscordPlayerSender
// Offline public chat......x............................................
// Online public chat.......x...........................................x
@ -155,10 +155,21 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
// If leaving the server and private chat is enabled (has ConnectedPlayer), call login in a task on lowest priority
// If private chat is enabled and joining the server, logout the fake player on highest priority
// If online and disabling private chat, don't logout
// The maps may not contain the senders except for DiscordPlayerSender
// The maps may not contain the senders for UnconnectedSenders
public static boolean isMinecraftChatEnabled(DiscordPlayer dp) {
return lastmsgPerUser.stream().anyMatch(
lmd -> ((IPrivateChannel) lmd.channel).getRecipient().getStringID().equals(dp.getDiscordID()));
}
/**
* May contain P&lt;DiscordID&gt; as key for public chat
*/
public static final HashMap<String, DiscordSender> UnconnectedSenders = new HashMap<>();
public static final HashMap<String, DiscordConnectedPlayer> ConnectedSenders = new HashMap<>();
/**
* May contain P&lt;DiscordID&gt; as key for public chat
*/
public static final HashMap<String, DiscordPlayerSender> OnlineSenders = new HashMap<>();
public static short ListC = 0;
@ -166,12 +177,18 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
(lastmsgdata == null ? lastmsgdata = new LastMsgData(DiscordPlugin.chatchannel) : lastmsgdata).message = null; // Don't set the whole object to null, the player and channel information should
} // be preserved
public static void sendSystemMessageToChat(String msg) {
DiscordPlugin.sendMessageToChannel(DiscordPlugin.chatchannel, msg);
for (LastMsgData data : lastmsgPerUser)
DiscordPlugin.sendMessageToChannel(data.channel, msg);
}
@Override // Discord
public void handle(MessageReceivedEvent event) {
val author = event.getMessage().getAuthor();
val user = DiscordPlayer.getUser(author.getStringID(), DiscordPlayer.class);
if (!event.getMessage().getChannel().getStringID().equals(DiscordPlugin.chatchannel.getStringID())
&& !(event.getMessage().getChannel().isPrivate() && user.minecraftChat().get()
&& !(event.getMessage().getChannel().isPrivate() && user.isMinecraftChatEnabled()
&& !DiscordPlugin.checkIfSomeoneIsTestingWhileWeArent()))
return;
resetLastMessage();
@ -200,12 +217,13 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
if (dsender instanceof DiscordSender && !Arrays.stream(UnconnectedCmds)
.anyMatch(s -> cmd.equals(s) || cmd.startsWith(s + " "))) {
// Command not whitelisted
dsender.sendMessage( // TODO
"Sorry, you need to be online on the server and have your accounts connected, you can only access these commands:\n"
+ Arrays.stream(UnconnectedCmds).map(uc -> "/" + uc)
.collect(Collectors.joining(", "))
+ "\nTo connect your accounts, use @ChromaBot connect in "
+ DiscordPlugin.botchannel.mention());
dsender.sendMessage("Sorry, you can only access these commands:\n"
+ Arrays.stream(UnconnectedCmds).map(uc -> "/" + uc).collect(Collectors.joining(", "))
+ (user.getConnectedID(TBMCPlayer.class) == null
? "\nTo access your commands, first please connect your accounts, using @ChromaBot connect in "
+ DiscordPlugin.botchannel.mention()
+ "\nThen you can access all of your regular commands (even offline) in private chat: DM me `mcchat`!"
: "\nYou can access all of your regular commands (even offline) in private chat: DM me `mcchat`!"));
return;
}
if (lastlist > 5) {
@ -248,29 +266,18 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
}
}
@SuppressWarnings("unchecked")
private <T extends DiscordSenderBase> DiscordSenderBase getSender(IChannel channel, final IUser author,
DiscordPlayer dp) {
final DiscordSenderBase dsender;
final Player mcp;
final String cid;
BiFunction<HashMap<String, T>, Supplier<T>, DiscordSenderBase> getsender = (senders, maker) -> {
if (!senders.containsKey(author.getStringID()))
senders.put(author.getStringID(), maker.get());
return senders.get(author.getStringID());
};
if ((cid = dp.getConnectedID(TBMCPlayer.class)) != null) { // Connected?
if ((mcp = Bukkit.getPlayer(UUID.fromString(cid))) != null) // Online? - Execute as ingame player
dsender = getsender.apply((HashMap<String, T>) OnlineSenders,
() -> (T) new DiscordPlayerSender(author, channel, mcp));
else // Offline
dsender = getsender.apply((HashMap<String, T>) ConnectedSenders,
() -> (T) new DiscordConnectedPlayer(author, channel, UUID.fromString(cid)));
} else { // Not connected
TBMCPlayer p = dp.getAs(TBMCPlayer.class);
dsender = getsender.apply((HashMap<String, T>) UnconnectedSenders,
() -> (T) new DiscordSender(author, channel, p == null ? null : p.PlayerName().get())); // Display the playername, if found
}
return dsender;
/**
* 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 DiscordSenderBase getSender(IChannel channel, final IUser author, DiscordPlayer dp) {
val key = (channel.isPrivate() ? "" : "P") + author.getStringID();
return Stream.<Supplier<Optional<DiscordSenderBase>>>of( // https://stackoverflow.com/a/28833677/2703239
() -> Optional.ofNullable(OnlineSenders.get(key)), // Find first non-null
() -> Optional.ofNullable(ConnectedSenders.get(author.getStringID())), // This doesn't support it
() -> Optional.ofNullable(UnconnectedSenders.get(key)), () -> {
val dsender = new DiscordSender(author, channel);
UnconnectedSenders.put(key, dsender);
return Optional.of(dsender);
}).map(Supplier::get).filter(Optional::isPresent).map(Optional::get).findFirst().get();
}
}

View file

@ -1,10 +1,13 @@
package buttondevteam.discordplugin.listeners;
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.PlayerQuitEvent;
import org.bukkit.event.server.ServerCommandEvent;
import com.earth2me.essentials.CommandSource;
@ -16,6 +19,7 @@ import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.discordplugin.commands.ConnectCommand;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.player.*;
import lombok.val;
import net.ess3.api.events.*;
import sx.blah.discord.handle.obj.IRole;
import sx.blah.discord.handle.obj.IUser;
@ -23,14 +27,22 @@ import sx.blah.discord.util.DiscordException;
import sx.blah.discord.util.MissingPermissionsException;
public class MCListener implements Listener {
@EventHandler
@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
final Player p = e.getPlayer();
DiscordPlayer dp = e.GetPlayer().getAs(DiscordPlayer.class);
// if(dp!=null) //TODO
// MCChatListener.OnlineSenders.put(dp.getDiscordID(), new DiscordPlayerSender(DiscordPlugin.dc.getUserByID(Long.parseLong(dp.getDiscordID()), channel, player))
if (dp != null) {
val user = DiscordPlugin.dc.getUserByID(Long.parseLong(dp.getDiscordID()));
MCChatListener.OnlineSenders.put(dp.getDiscordID(),
new DiscordPlayerSender(user, user.getOrCreatePMChannel(), p));
MCChatListener.OnlineSenders.put("P" + dp.getDiscordID(),
new DiscordPlayerSender(user, DiscordPlugin.chatchannel, p));
MCChatListener.ConnectedSenders.values().stream()
.filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny()
.ifPresent(dcp -> Bukkit.getPluginManager().callEvent(new PlayerQuitEvent(dcp, "")));
}
if (ConnectCommand.WaitingToConnect.containsKey(e.GetPlayer().PlayerName().get())) {
IUser user = DiscordPlugin.dc
.getUserByID(Long.parseLong(ConnectCommand.WaitingToConnect.get(e.GetPlayer().PlayerName().get())));
@ -38,18 +50,20 @@ public class MCListener implements Listener {
+ " do /discord accept");
p.sendMessage("§bIf it wasn't you, do /discord decline");
}
DiscordPlugin.sendMessageToChannel(DiscordPlugin.chatchannel,
e.GetPlayer().PlayerName().get() + " joined the game");
MCChatListener.sendSystemMessageToChat(e.GetPlayer().PlayerName().get() + " joined the game");
MCChatListener.ListC = 0;
}
@EventHandler
@EventHandler(priority = EventPriority.HIGHEST)
public void onPlayerLeave(TBMCPlayerQuitEvent e) {
if (MCChatListener.OnlineSenders.entrySet()
.removeIf(entry -> entry.getValue().getUniqueId().equals(e.getPlayer().getUniqueId())))
; // TODO
DiscordPlugin.sendMessageToChannel(DiscordPlugin.chatchannel,
e.GetPlayer().PlayerName().get() + " left the game");
if (e.getPlayer() instanceof DiscordConnectedPlayer)
return; // Only care about real users
MCChatListener.OnlineSenders.entrySet()
.removeIf(entry -> entry.getValue().getUniqueId().equals(e.getPlayer().getUniqueId()));
MCChatListener.ConnectedSenders.values().stream()
.filter(s -> s.getUniqueId().equals(e.getPlayer().getUniqueId())).findAny()
.ifPresent(dcp -> Bukkit.getPluginManager().callEvent(new PlayerJoinEvent(dcp, "")));
MCChatListener.sendSystemMessageToChat(e.GetPlayer().PlayerName().get() + " left the game");
}
@EventHandler
@ -70,16 +84,15 @@ public class MCListener implements Listener {
@EventHandler(priority = EventPriority.LOW)
public void onPlayerDeath(PlayerDeathEvent e) {
DiscordPlugin.sendMessageToChannel(DiscordPlugin.chatchannel, e.getDeathMessage());
MCChatListener.sendSystemMessageToChat(e.getDeathMessage());
}
@EventHandler
public void onPlayerAFK(AfkStatusChangeEvent e) {
if (e.isCancelled())
if (e.isCancelled() || !e.getAffected().getBase().isOnline())
return;
DiscordPlugin.sendMessageToChannel(DiscordPlugin.chatchannel,
DiscordPlugin.sanitizeString(e.getAffected().getBase().getDisplayName()) + " is "
+ (e.getValue() ? "now" : "no longer") + " AFK.");
MCChatListener.sendSystemMessageToChat(DiscordPlugin.sanitizeString(e.getAffected().getBase().getDisplayName())
+ " is " + (e.getValue() ? "now" : "no longer") + " AFK.");
}
@EventHandler

View file

@ -9,7 +9,6 @@ import org.bukkit.conversations.Conversation;
import org.bukkit.conversations.ConversationAbandonedEvent;
import org.bukkit.entity.*;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.map.MapView;
import org.bukkit.permissions.PermissibleBase;
import org.bukkit.permissions.ServerOperator;
@ -29,7 +28,6 @@ public class DiscordFakePlayer extends DiscordHumanEntity implements Player {
perm = new PermissibleBase(new ServerOperator() {
private @Getter @Setter boolean op;
});
Bukkit.getPluginManager().callEvent(new PlayerJoinEvent(this, "Discord fake player joined"));
}
@Delegate