Channel connect, fixes

- To do or not to do
- Fixed args handling regarding whitespace
- Showing the help text for cmds when needed
- Fixed userinfo cmd not accepting spaces while Discord does
- Moved command registration to start, not the initializer
-- Prevents possible deadlocks according to IntelliJ
- Other fixes
This commit is contained in:
Norbi Peti 2018-05-22 22:43:36 +02:00
parent fba5c5b49a
commit aabdd3a914
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
14 changed files with 129 additions and 129 deletions

2
.gitignore vendored
View file

@ -131,7 +131,7 @@ publish/
*.publishproj
# NuGet Packages Directory
## TODO: If you have NuGet Package Restore enabled, uncomment the next line
## TO!DO: If you have NuGet Package Restore enabled, uncomment the next line
#packages/
# Windows Azure Build Output

View file

@ -58,7 +58,7 @@
<exclude>org.spigotmc:spigot-api</exclude>
<exclude>com.github.TBMCPlugins.ButtonCore:ButtonCore</exclude>
<exclude>net.ess3:Essentials</exclude>
</excludes> <!-- TODO: http://stackoverflow.com/questions/28458058/maven-shade-plugin-exclude-a-dependency-and-all-its-transitive-dependencies -->
</excludes> <!-- http://stackoverflow.com/questions/28458058/maven-shade-plugin-exclude-a-dependency-and-all-its-transitive-dependencies -->
</artifactSet>
</configuration>
</execution>
@ -180,6 +180,7 @@
<groupId>net.ess3</groupId>
<artifactId>Essentials</artifactId>
<version>2.13.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.xaanit</groupId>

View file

@ -1,5 +1,6 @@
package buttondevteam.discordplugin;
import buttondevteam.discordplugin.commands.DiscordCommandBase;
import buttondevteam.discordplugin.listeners.*;
import buttondevteam.discordplugin.mccommands.DiscordMCCommandBase;
import buttondevteam.lib.TBMCCoreAPI;
@ -132,6 +133,7 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
new ChromaBot(this).updatePlayerList();
//Get all roles with the default color
GameRoles = mainServer.getRoles().stream().filter(r -> r.getColor().getAlpha() == 0).map(IRole::getName).collect(Collectors.toList());
DiscordCommandBase.registerCommands();
if (getConfig().getBoolean("serverup", false)) {
ChromaBot.getInstance().sendMessage("", new EmbedBuilder().withColor(Color.YELLOW)
.withTitle("Server recovered from a crash - chat connected.").build());

View file

@ -0,0 +1,68 @@
package buttondevteam.discordplugin.commands;
import buttondevteam.discordplugin.DiscordConnectedPlayer;
import buttondevteam.discordplugin.DiscordPlayer;
import buttondevteam.discordplugin.listeners.MCChatListener;
import buttondevteam.lib.TBMCChannelConnectEvent;
import buttondevteam.lib.chat.Channel;
import buttondevteam.lib.player.TBMCPlayer;
import lombok.val;
import org.bukkit.Bukkit;
import sx.blah.discord.handle.obj.IMessage;
import sx.blah.discord.handle.obj.Permissions;
import sx.blah.discord.util.PermissionUtils;
import java.util.Arrays;
public class ChannelconCommand extends DiscordCommandBase {
@Override
public String getCommandName() {
return "here";
}
@Override
public boolean run(IMessage message, String args) {
if (args.length() == 0)
return false;
if (!PermissionUtils.hasPermissions(message.getChannel(), message.getAuthor(), Permissions.MANAGE_CHANNEL)) {
message.reply("you need to have manage permissions for this channel!");
return true;
}
//TODO: What if they no longer have permission to view the channel
if (MCChatListener.hasCustomChat(message.getChannel())) { //TODO: Remove command
message.reply("this channel is already connected to a Minecraft channel.");
return true;
}
val chan = Channel.getChannels().stream().filter(ch -> ch.ID.equalsIgnoreCase(args) || (ch.IDs != null && Arrays.stream(ch.IDs).anyMatch(cid -> cid.equalsIgnoreCase(args)))).findAny();
if (!chan.isPresent()) { //TODO: Red embed that disappears over time (kinda like the highlight messages in OW)
message.reply("MC channel with ID '" + args + "' not found! The ID is the command for it without the /.");
return true;
}
val chp = DiscordPlayer.getUser(message.getAuthor().getStringID(), DiscordPlayer.class).getAs(TBMCPlayer.class);
if (chp == null) {
message.reply("you need to connect your Minecraft account. In #bot do @ChromaBot connect <MCname>");
return true;
}
val ev = new TBMCChannelConnectEvent(new DiscordConnectedPlayer(message.getAuthor(), message.getChannel(), chp.getUUID(), Bukkit.getOfflinePlayer(chp.getUUID()).getName()), chan.get());
Bukkit.getPluginManager().callEvent(ev); //Using a fake player with no login/logout, should be fine for this event
if (ev.isCancelled() || ev.getGroupid() == null) {
message.reply("sorry, that didn't work. You cannot use that Minecraft channel.");
return true;
}
MCChatListener.addCustomChat(message.getChannel(), args, ev.getChannel());
message.reply("alright, connection made!");
return true;
}
@Override
public String[] getHelpText() {
return new String[]{ //
"§6---- 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).", //
"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 @ChromaBot connect <mcname>.", //
"Call this command from the channel you want to use. Usage: @ChromaBot channelcon <mcchannel>", //
"Invite link: https://discordapp.com/oauth2/authorize?client_id=226443037893591041&scope=bot" //
};
}
}

View file

@ -25,15 +25,13 @@ public class ConnectCommand extends DiscordCommandBase {
public static HashBiMap<String, String> WaitingToConnect = HashBiMap.create();
@Override
public void run(IMessage message, String args) {
if (args.length() == 0) {
DiscordPlugin.sendMessageToChannel(message.getChannel(), "Usage: connect <Minecraftname>");
return;
}
public boolean run(IMessage message, String args) {
if (args.length() == 0)
return true;
if (args.contains(" ")) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"Too many arguments.\nUsage: connect <Minecraftname>");
return;
return true;
}
if (WaitingToConnect.inverse().containsKey(message.getAuthor().getStringID())) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
@ -44,13 +42,13 @@ public class ConnectCommand extends DiscordCommandBase {
OfflinePlayer p = Bukkit.getOfflinePlayer(args);
if (p == null) {
DiscordPlugin.sendMessageToChannel(message.getChannel(), "The specified Minecraft player cannot be found");
return;
return true;
}
try (TBMCPlayer pl = TBMCPlayerBase.getPlayer(p.getUniqueId(), TBMCPlayer.class)) {
DiscordPlayer dp = pl.getAs(DiscordPlayer.class);
if (dp != null && message.getAuthor().getStringID().equals(dp.getDiscordID())) {
DiscordPlugin.sendMessageToChannel(message.getChannel(), "You already have this account connected.");
return;
return true;
}
} catch (Exception e) {
TBMCCoreAPI.SendException("An error occured while connecting a Discord account!", e);
@ -63,6 +61,7 @@ public class ConnectCommand extends DiscordCommandBase {
if (p.isOnline())
((Player) p).sendMessage("§bTo connect with the Discord account " + message.getAuthor().getName() + "#"
+ message.getAuthor().getDiscriminator() + " do /discord accept");
return true;
}
@Override

View file

@ -4,23 +4,26 @@ import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.TBMCCoreAPI;
import sx.blah.discord.handle.obj.IMessage;
import java.util.Arrays;
import java.util.HashMap;
import java.util.stream.Collectors;
public abstract class DiscordCommandBase {
public abstract String getCommandName();
public abstract void run(IMessage message, String args);
public abstract boolean run(IMessage message, String args);
public abstract String[] getHelpText();
static final HashMap<String, DiscordCommandBase> commands = new HashMap<String, DiscordCommandBase>();
static {
public static void registerCommands() {
commands.put("connect", new ConnectCommand()); // TODO: API for adding commands?
commands.put("userinfo", new UserinfoCommand());
commands.put("help", new HelpCommand());
commands.put("role", new RoleCommand());
commands.put("mcchat", new MCChatCommand());
commands.put("channelcon", new ChannelconCommand());
}
public static void runCommand(String cmd, String args, IMessage message) {
@ -33,11 +36,16 @@ public abstract class DiscordCommandBase {
return;
}
try {
command.run(message, args);
if (!command.run(message, args))
DiscordPlugin.sendMessageToChannel(message.getChannel(), Arrays.stream(command.getHelpText()).collect(Collectors.joining("\n")));
} catch (Exception e) {
TBMCCoreAPI.SendException("An error occured while executing command " + cmd + "!", e);
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"An internal error occured while executing this command. For more technical details see the server-issues channel on the dev Discord.");
}
}
protected String[] splitargs(String args) {
return args.split("\\s+");
}
}

View file

@ -14,7 +14,7 @@ public class HelpCommand extends DiscordCommandBase {
}
@Override
public void run(IMessage message, String args) {
public boolean run(IMessage message, String args) {
DiscordCommandBase argdc;
if (args.length() == 0)
DiscordPlugin.sendMessageToChannel(message.getChannel(),
@ -24,6 +24,7 @@ public class HelpCommand extends DiscordCommandBase {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
(argdc = DiscordCommandBase.commands.get(args)) == null ? "Command not found: " + args
: Arrays.stream(argdc.getHelpText()).collect(Collectors.joining("\n")));
return true;
}
@Override

View file

@ -1,25 +0,0 @@
package buttondevteam.discordplugin.commands;
import buttondevteam.discordplugin.mccommands.ChannelconMCCommand;
import lombok.val;
import sx.blah.discord.handle.obj.IMessage;
public class HereCommand extends DiscordCommandBase {
@Override
public String getCommandName() {
return "here";
}
@Override
public void run(IMessage message, String args) {
val chgroup = ChannelconMCCommand.PendingConnections.get(message.getAuthor().getStringID());
if (chgroup == null) {
message.reply("no pending connection found! "); //TODO
}
}
@Override
public String[] getHelpText() {
return new String[0];
}
}

View file

@ -14,11 +14,11 @@ public class MCChatCommand extends DiscordCommandBase {
}
@Override
public void run(IMessage message, String args) {
public boolean run(IMessage message, String args) {
if (!message.getChannel().isPrivate()) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"This command can only be issued in a direct message with the bot.");
return;
return true;
}
try (final DiscordPlayer user = DiscordPlayer.getUser(message.getAuthor().getStringID(), DiscordPlayer.class)) {
boolean mcchat = !user.isMinecraftChatEnabled();
@ -31,6 +31,7 @@ public class MCChatCommand extends DiscordCommandBase {
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while setting mcchat for user" + message.getAuthor().getName(), e);
}
return true;
}
@Override

View file

@ -17,17 +17,14 @@ public class RoleCommand extends DiscordCommandBase {
}
@Override
public void run(IMessage message, String args) {
final String usagemsg = "Subcommands: add, remove, list";
if (args.length() == 0) {
DiscordPlugin.sendMessageToChannel(message.getChannel(), usagemsg);
return;
}
String[] argsa = args.split(" ");
public boolean run(IMessage message, String args) {
if (args.length() == 0)
return false;
String[] argsa = splitargs(args);
if (argsa[0].equalsIgnoreCase("add")) {
final IRole role = checkAndGetRole(message, argsa, "This command adds a game role to your account.");
if (role == null)
return;
return true;
try {
DPUtils.perform(() -> message.getAuthor().addRole(role));
DiscordPlugin.sendMessageToChannel(message.getChannel(), "Added game role.");
@ -38,7 +35,7 @@ public class RoleCommand extends DiscordCommandBase {
} else if (argsa[0].equalsIgnoreCase("remove")) {
final IRole role = checkAndGetRole(message, argsa, "This command removes a game role from your account.");
if (role == null)
return;
return true;
try {
DPUtils.perform(() -> message.getAuthor().removeRole(role));
DiscordPlugin.sendMessageToChannel(message.getChannel(), "Removed game role.");
@ -49,7 +46,8 @@ public class RoleCommand extends DiscordCommandBase {
} else if (argsa[0].equalsIgnoreCase("list")) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"List of game roles:\n" + DiscordPlugin.GameRoles.stream().sorted().collect(Collectors.joining("\n")));
} else DiscordPlugin.sendMessageToChannel(message.getChannel(), usagemsg);
} else return false;
return true;
}
private IRole checkAndGetRole(IMessage message, String[] argsa, String usage) {
@ -87,7 +85,7 @@ public class RoleCommand extends DiscordCommandBase {
return new String[] { //
"Add or remove game roles from yourself.", //
"Usage: role add|remove <name> or role list", //
"Mods can use role addrole <name> to add a role as a game role" };
};
}
}

View file

@ -20,12 +20,7 @@ public class UserinfoCommand extends DiscordCommandBase {
}
@Override
public void run(IMessage message, String args) {
if (args.contains(" ")) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"Too many arguments.\nUsage: userinfo [username/nickname[#tag]/ping]\nExamples:\nuserinfo ChromaBot\nuserinfo ChromaBot#6338\nuserinfo @ChromaBot#6338");
return;
}
public boolean run(IMessage message, String args) {
IUser target = null;
if (args.length() == 0)
target = message.getAuthor();
@ -40,7 +35,7 @@ public class UserinfoCommand extends DiscordCommandBase {
if (targets.size() == 0) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"The user cannot be found (by name): " + args);
return;
return true;
}
for (IUser ptarget : targets) {
if (ptarget.getDiscriminator().equalsIgnoreCase(targettag[1])) {
@ -52,19 +47,19 @@ public class UserinfoCommand extends DiscordCommandBase {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"The user cannot be found (by discriminator): " + args + "(Found " + targets.size()
+ " users with the name.)");
return;
return true;
}
} else {
final List<IUser> targets = getUsers(message, args);
if (targets.size() == 0) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"The user cannot be found on Discord: " + args);
return;
return true;
}
if (targets.size() > 1) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"Multiple users found with that (nick)name. Please specify the whole tag, like ChromaBot#6338 or use a ping.");
return;
return true;
}
target = targets.get(0);
}
@ -77,6 +72,7 @@ public class UserinfoCommand extends DiscordCommandBase {
DiscordPlugin.sendMessageToChannel(message.getChannel(), "An error occured while getting the user!");
TBMCCoreAPI.SendException("Error while getting info about " + target.getName() + "!", e);
}
return true;
}
private List<IUser> getUsers(IMessage message, String args) {
@ -94,7 +90,8 @@ public class UserinfoCommand extends DiscordCommandBase {
return new String[] { //
"---- User information ----", //
"Shows some information about users, from Discord, from Minecraft or from Reddit if they have these accounts connected.", //
"Usage: userinfo <Discordname>" //
"If used without args, shows your info.", //
"Usage: userinfo [username/nickname[#tag]/ping]\nExamples:\nuserinfo ChromaBot\nuserinfo ChromaBot#6338\nuserinfo @ChromaBot#6338" //
};
}

View file

@ -182,7 +182,7 @@ public class CommandListener {
args = "";
} else {
cmd = cmdwithargs.substring(0, index);
args = cmdwithargs.substring(index + 1);
args = cmdwithargs.substring(index + 1).trim(); //In case there are multiple spaces
}
DiscordCommandBase.runCommand(cmd.toLowerCase(), args, message);
message.getChannel().setTypingStatus(false);

View file

@ -46,12 +46,6 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
private LinkedBlockingQueue<AbstractMap.SimpleEntry<TBMCChatEvent, Instant>> sendevents = new LinkedBlockingQueue<>();
private Runnable sendrunnable;
public static void addCustomChat(IChannel channel, String groupid, Channel mcchannel) {
val lmd = new LastMsgData(channel, null, null);
lmd.mcchannel = mcchannel;
lastmsgCustom.put(groupid, lmd);
}
@EventHandler // Minecraft
public void onMCChat(TBMCChatEvent ev) {
if (ev.isCancelled())
@ -186,7 +180,7 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
/**
* Used for town or nation chats or anything else
*/
private static HashMap<String, LastMsgData> lastmsgCustom = new HashMap<>();
private static HashMap<LastMsgData, String> lastmsgCustom = new HashMap<>();
public static boolean privateMCChat(IChannel channel, boolean start, IUser user, DiscordPlayer dp) {
TBMCPlayer mcp = dp.getAs(TBMCPlayer.class);
@ -230,6 +224,20 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
.anyMatch(lmd -> ((IPrivateChannel) lmd.channel).getRecipient().getStringID().equals(did));
}
public static void addCustomChat(IChannel channel, String groupid, Channel mcchannel) {
val lmd = new LastMsgData(channel, null, null);
lmd.mcchannel = mcchannel;
lastmsgCustom.put(lmd, groupid);
}
public static boolean hasCustomChat(IChannel channel) {
return lastmsgCustom.entrySet().stream().anyMatch(lmd -> lmd.getKey().channel.getLongID() == channel.getLongID());
}
public static boolean removeCustomChat(IChannel channel) {
return lastmsgCustom.entrySet().removeIf(lmd -> lmd.getKey().channel.getLongID() == channel.getLongID());
}
/**
* May contain P&lt;DiscordID&gt; as key for public chat
*/

View file

@ -1,58 +0,0 @@
package buttondevteam.discordplugin.mccommands;
import buttondevteam.discordplugin.DiscordPlayer;
import buttondevteam.lib.TBMCChannelConnectEvent;
import buttondevteam.lib.chat.Channel;
import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.player.TBMCPlayer;
import lombok.val;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.HashMap;
@CommandClass(modOnly = false, path = "channelcon")
public class ChannelconMCCommand extends DiscordMCCommandBase {
@Override //TODO: Since we require connecting the accounts, it can be done entirely on Discord.
public boolean OnCommand(Player player, String alias, String[] args) {
if (args.length < 1)
return false;
val chan = Channel.getChannels().stream().filter(ch -> ch.ID.equalsIgnoreCase(args[0]) || (ch.IDs != null && Arrays.stream(ch.IDs).anyMatch(cid -> cid.equalsIgnoreCase(args[0])))).findAny();
if (!chan.isPresent()) {
player.sendMessage("§cChannel with ID '" + args[0] + "' not found! The ID is the command for it without the /.");
return true;
}
val dp = TBMCPlayer.getPlayer(player.getUniqueId(), TBMCPlayer.class).getAs(DiscordPlayer.class);
if (dp == null) {
player.sendMessage("§cYou need to connect your Discord account. In #bot do @ChromaBot connect " + player.getName());
return true;
}
val ev = new TBMCChannelConnectEvent(player, chan.get());
Bukkit.getPluginManager().callEvent(ev);
if (ev.isCancelled() || ev.getGroupid() == null) {
player.sendMessage("§cSorry, that didn't work. You cannot use that channel.");
return true;
}
//MCChatListener.addCustomChat() - TODO: Call in Discord cmd
PendingConnections.put(dp.getDiscordID(), new AbstractMap.SimpleEntry<>(ev.getChannel(), ev.getGroupid()));
player.sendMessage("§bAlright! Now invite me to your server then show me the channel to use (@ChromaBot here).");
return true;
}
@Override
public String[] GetHelpText(String s) {
return new String[]{//
"§6---- Channel connect ---", //
"This command allows you to connect a Minecraft channel to a Discord channel.", //
"You need to have access to the MC channel and have manage permissions on the Discord channel." //
};
}
/**
* Key: Discord ID
* Value of value. Channel Group ID
*/
public static HashMap<String, AbstractMap.SimpleEntry<Channel, String>> PendingConnections = new HashMap<>();
}