Merge branch 'dev' (#54)

# Conflicts:
#	pom.xml
This commit is contained in:
Norbi Peti 2018-06-15 18:49:21 +02:00
commit b3259239e5
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
44 changed files with 820 additions and 501 deletions

2
.gitignore vendored Normal file → Executable file
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

0
.travis.yml Normal file → Executable file
View file

0
License.md Normal file → Executable file
View file

0
README.md Normal file → Executable file
View file

2
deploy.sh Normal file → Executable file
View file

@ -6,5 +6,5 @@ if [ $1 = 'production' ]; then
echo Production mode
echo $UPLOAD_KEY > upload_key
chmod 400 upload_key
yes | scp -B -i upload_key -o StrictHostKeyChecking=no $FILENAME travis@server.figytuna.com:/minecraft/main/plugins
yes | scp -B -i upload_key -o StrictHostKeyChecking=no $FILENAME travis@server.figytuna.com:/minecraft/main/pluginupdates
fi

17
pom.xml Normal file → Executable file
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>
@ -151,11 +151,11 @@
<version>1.12.2-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.discord4j/Discord4J -->
<!-- https://mvnrepository.com/artifact/com.discord4j/Discord4J -->
<dependency>
<groupId>com.discord4j</groupId>
<artifactId>Discord4J</artifactId>
<version>2.10.1</version>
<groupId>com.discord4j</groupId>
<artifactId>Discord4J</artifactId>
<version>2.10.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-jdk14 -->
<dependency>
@ -179,6 +179,7 @@
<groupId>net.ess3</groupId>
<artifactId>Essentials</artifactId>
<version>2.13.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.github.xaanit</groupId>
@ -211,6 +212,10 @@
<version>2.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.vdurmont</groupId>
<artifactId>emoji-java</artifactId>
<version>4.0.0</version>
</dependency>
</dependencies>
</project>

View file

@ -1,19 +1,18 @@
package buttondevteam.discordplugin;
import java.awt.Color;
import java.util.Arrays;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitScheduler;
import buttondevteam.discordplugin.listeners.MCChatListener;
import lombok.Getter;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitScheduler;
import sx.blah.discord.api.internal.json.objects.EmbedObject;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.util.EmbedBuilder;
import java.awt.*;
import java.util.Arrays;
import java.util.stream.Collectors;
public class ChromaBot {
/**
* May be null if it's not initialized. Initialization happens after the server is done loading (using {@link BukkitScheduler#runTaskAsynchronously(org.bukkit.plugin.Plugin, Runnable)})
@ -47,7 +46,7 @@ public class ChromaBot {
}
/**
* Send a message to the chat channel and private chats.
* Send a message to the chat channels and private chats.
*
* @param message
* The message to send, duh
@ -73,7 +72,7 @@ public class ChromaBot {
}
/**
* Send a fancy message to the chat channel. This will show a bold text with a colored line.
* Send a fancy message to the chat channels. This will show a bold text with a colored line.
*
* @param message
* The message to send, duh
@ -86,7 +85,7 @@ public class ChromaBot {
}
/**
* Send a fancy message to the chat channel. This will show a bold text with a colored line.
* Send a fancy message to the chat channels. This will show a bold text with a colored line.
*
* @param message
* The message to send, duh
@ -101,7 +100,7 @@ public class ChromaBot {
}
/**
* Send a fancy message to the chat channel. This will show a bold text with a colored line.
* Send a fancy message to the chat channels. This will show a bold text with a colored line.
*
* @param message
* The message to send, duh
@ -118,7 +117,7 @@ public class ChromaBot {
}
/**
* Send a message to the chat channel. This will show a bold text with a colored line.
* Send a message to the chat channels. This will show a bold text with a colored line.
*
* @param message
* The message to send, duh

39
src/main/java/buttondevteam/discordplugin/DPUtils.java Normal file → Executable file
View file

@ -1,10 +1,15 @@
package buttondevteam.discordplugin;
import buttondevteam.lib.TBMCCoreAPI;
import org.bukkit.Bukkit;
import sx.blah.discord.util.EmbedBuilder;
import sx.blah.discord.util.RequestBuffer;
import sx.blah.discord.util.RequestBuffer.IRequest;
import sx.blah.discord.util.RequestBuffer.IVoidRequest;
import javax.annotation.Nullable;
import java.util.concurrent.TimeUnit;
public final class DPUtils {
public static EmbedBuilder embedWithHead(EmbedBuilder builder, String playername) {
@ -33,13 +38,33 @@ public final class DPUtils {
/**
* Performs Discord actions, retrying when ratelimited. May return null if action fails too many times or in safe mode.
*/
public static <T> T perform(IRequest<T> action) {
if (DiscordPlugin.SafeMode)
return null;
// if (Thread.currentThread() == DiscordPlugin.mainThread) - TODO: Ignore shutdown message <--
// throw new RuntimeException("Tried to wait for a Discord request on the main thread. This could cause lag.");
return RequestBuffer.request(action).get(); // Let the pros handle this
}
@Nullable
public static <T> T perform(IRequest<T> action, long timeout, TimeUnit unit) {
if (DiscordPlugin.SafeMode)
return null;
if (Thread.currentThread() == DiscordPlugin.mainThread) // TODO: Ignore shutdown message <--
// throw new RuntimeException("Tried to wait for a Discord request on the main thread. This could cause lag.");
Bukkit.getLogger().warning("Waiting for a Discord request on the main thread!");
try {
return RequestBuffer.request(action).get(timeout, unit); // Let the pros handle this
} catch (Exception e) {
TBMCCoreAPI.SendException("Couldn't perform Discord action!", e);
return null;
}
}
/**
* Performs Discord actions, retrying when ratelimited. May return null if action fails too many times or in safe mode.
*/
@Nullable
public static <T> T perform(IRequest<T> action) {
if (DiscordPlugin.SafeMode)
return null;
if (Thread.currentThread() == DiscordPlugin.mainThread) // TODO: Ignore shutdown message <--
// throw new RuntimeException("Tried to wait for a Discord request on the main thread. This could cause lag.");
Bukkit.getLogger().warning("Waiting for a Discord request on the main thread!");
return RequestBuffer.request(action).get(); // Let the pros handle this
}
/**
* Performs Discord actions, retrying when ratelimited.

View file

@ -1,13 +1,13 @@
package buttondevteam.discordplugin;
import java.util.UUID;
import buttondevteam.discordplugin.playerfaker.DiscordFakePlayer;
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
import lombok.Getter;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IUser;
import java.util.UUID;
public class DiscordConnectedPlayer extends DiscordFakePlayer implements IMCPlayer<DiscordConnectedPlayer> {
private static int nextEntityId = 10000;
private @Getter VanillaCommandListener<DiscordConnectedPlayer> vanillaCmdListener;

View file

View file

@ -1,13 +1,7 @@
package buttondevteam.discordplugin;
import java.net.InetSocketAddress;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
import lombok.Getter;
import org.bukkit.*;
import org.bukkit.advancement.Advancement;
import org.bukkit.advancement.AdvancementProgress;
@ -24,18 +18,20 @@ import org.bukkit.inventory.*;
import org.bukkit.inventory.InventoryView.Property;
import org.bukkit.map.MapView;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.permissions.*;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scoreboard.Scoreboard;
import org.bukkit.util.Vector;
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
import lombok.Getter;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IUser;
import java.net.InetSocketAddress;
import java.util.*;
@SuppressWarnings("deprecation")
public class DiscordPlayerSender extends DiscordSenderBase implements IMCPlayer<DiscordPlayerSender> {

View file

@ -1,8 +1,10 @@
package buttondevteam.discordplugin;
import buttondevteam.discordplugin.commands.DiscordCommandBase;
import buttondevteam.discordplugin.listeners.*;
import buttondevteam.discordplugin.mccommands.DiscordMCCommandBase;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Channel;
import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.player.ChromaGamerBase;
import com.google.common.io.Files;
@ -30,8 +32,9 @@ import sx.blah.discord.util.RequestBuffer;
import java.awt.*;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
@ -60,7 +63,6 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
plugin = this;
lastannouncementtime = getConfig().getLong("lastannouncementtime");
lastseentime = getConfig().getLong("lastseentime");
GameRoles = (List<String>) getConfig().getList("gameroles", new ArrayList<String>());
ClientBuilder cb = new ClientBuilder();
cb.withToken(Files.readFirstLine(new File("TBMC", "Token.txt"), StandardCharsets.UTF_8));
dc = cb.login();
@ -77,6 +79,7 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
public static IChannel genchannel;
public static IChannel chatchannel;
public static IChannel botroomchannel;
public static IChannel modlogchannel;
/**
* Don't send messages, just receive, the same channel is used when testing
*/
@ -109,6 +112,7 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
officechannel = devServer.getChannelByID(219626707458457603L); // developers-office
updatechannel = devServer.getChannelByID(233724163519414272L); // server-updates
devofficechannel = officechannel; // developers-office
modlogchannel = mainServer.getChannelByID(283840717275791360L); // modlog
dc.changePresence(StatusType.ONLINE, ActivityType.PLAYING, "Chromacraft");
} else {
botchannel = devServer.getChannelByID(239519012529111040L); // bot-room
@ -119,6 +123,7 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
officechannel = devServer.getChannelByID(219626707458457603L); // developers-office
updatechannel = botchannel;
devofficechannel = botchannel;// bot-room
modlogchannel = botchannel; // bot-room
dc.changePresence(StatusType.ONLINE, ActivityType.PLAYING, "testing");
}
if (botchannel == null || annchannel == null || genchannel == null || botroomchannel == null
@ -129,6 +134,27 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
task.cancel();
if (!sent) {
new ChromaBot(this).updatePlayerList();
GameRoles = mainServer.getRoles().stream().filter(this::isGameRole).map(IRole::getName).collect(Collectors.toList());
val chcons = getConfig().getConfigurationSection("chcons");
if (chcons != null) {
val chconkeys = chcons.getKeys(false);
for (val chconkey : chconkeys) {
val chcon = chcons.getConfigurationSection(chconkey);
val mcch = Channel.getChannels().stream().filter(ch -> ch.ID.equals(chcon.getString("mcchid"))).findAny();
val ch = dc.getChannelByID(chcon.getLong("chid"));
val did = chcon.getLong("did");
val dp = DiscordPlayer.getUser(Long.toString(did), DiscordPlayer.class);
val user = dc.fetchUser(did);
val dcp = new DiscordConnectedPlayer(user, ch, UUID.fromString(chcon.getString("mcuid")), chcon.getString("mcname"));
val groupid = chcon.getString("groupid");
if (!mcch.isPresent() || ch == null || user == null || groupid == null)
continue;
MCChatListener.addCustomChat(ch, groupid, mcch.get(), dp, user, dcp);
}
}
DiscordCommandBase.registerCommands();
if (getConfig().getBoolean("serverup", false)) {
ChromaBot.getInstance().sendMessage("", new EmbedBuilder().withColor(Color.YELLOW)
.withTitle("Server recovered from a crash - chat connected.").build());
@ -199,6 +225,14 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
}
}
public boolean isGameRole(IRole r) {
if (r.getGuild().getLongID() != mainServer.getLongID())
return false; //Only allow on the main server
val rc = new Color(149, 165, 166, 0);
return r.getColor().equals(rc)
&& r.getPosition() < mainServer.getRoleByID(234343495735836672L).getPosition(); //Below the ChromaBot role
}
/**
* Always true, except when running "stop" from console
*/
@ -211,8 +245,20 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
MCListener.callEventExcludingSome(new PlayerQuitEvent(entry.getValue(), ""));
getConfig().set("lastannouncementtime", lastannouncementtime);
getConfig().set("lastseentime", lastseentime);
getConfig().set("gameroles", GameRoles);
getConfig().set("serverup", false);
val chcons = MCChatListener.getCustomChats();
val chconsc = getConfig().createSection("chcons");
for (val chcon : chcons) {
val chconc = chconsc.createSection(chcon.channel.getStringID());
chconc.set("mcchid", chcon.mcchannel.ID);
chconc.set("chid", chcon.channel.getLongID());
chconc.set("did", chcon.user.getLongID());
chconc.set("mcuid", chcon.dcp.getUniqueId().toString());
chconc.set("mcname", chcon.dcp.getName());
chconc.set("groupid", chcon.groupID);
}
saveConfig();
MCChatListener.forAllMCChat(ch -> DiscordPlugin.sendMessageToChannelWait(ch, "",
new EmbedBuilder().withColor(Restart ? Color.ORANGE : Color.RED)
@ -225,10 +271,11 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
+ (Bukkit.getOnlinePlayers().size() == 1 ? " was " : " were ")
+ "asked *politely* to leave the server for a bit.")
: "")
.build()));
.build(), 5, TimeUnit.SECONDS));
ChromaBot.getInstance().updatePlayerList();
try {
SafeMode = true; // Stop interacting with Discord
MCChatListener.stop();
ChromaBot.delete();
dc.changePresence(StatusType.IDLE, ActivityType.PLAYING, "Chromacraft"); //No longer using the same account for testing
dc.logout();
@ -323,7 +370,15 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
return sendMessageToChannel(channel, message, embed, true);
}
public static IMessage sendMessageToChannelWait(IChannel channel, String message, EmbedObject embed, long timeout, TimeUnit unit) {
return sendMessageToChannel(channel, message, embed, true, timeout, unit);
}
private static IMessage sendMessageToChannel(IChannel channel, String message, EmbedObject embed, boolean wait) {
return sendMessageToChannel(channel, message, embed, wait, -1, null);
}
private static IMessage sendMessageToChannel(IChannel channel, String message, EmbedObject embed, boolean wait, long timeout, TimeUnit unit) {
if (message.length() > 1980) {
message = message.substring(0, 1980);
Bukkit.getLogger()
@ -337,10 +392,16 @@ public class DiscordPlugin extends JavaPlugin implements IListener<ReadyEvent> {
final String content = message;
RequestBuffer.IRequest<IMessage> r = () -> embed == null ? channel.sendMessage(content)
: channel.sendMessage(content, embed, false);
if (wait)
return DPUtils.perform(r);
else {
DPUtils.performNoWait(r);
if (wait) {
if (unit != null)
return DPUtils.perform(r, timeout, unit);
else
return DPUtils.perform(r);
} else {
if (unit != null)
plugin.getLogger().warning("Tried to set timeout for non-waiting call.");
else
DPUtils.performNoWait(r);
return null;
}
} catch (Exception e) {

View file

View file

@ -1,15 +1,18 @@
package buttondevteam.discordplugin;
import java.util.Set;
import org.bukkit.Bukkit;
import org.bukkit.Server;
import org.bukkit.command.CommandSender;
import org.bukkit.permissions.*;
import org.bukkit.permissions.PermissibleBase;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionAttachment;
import org.bukkit.permissions.PermissionAttachmentInfo;
import org.bukkit.plugin.Plugin;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IUser;
import java.util.Set;
public class DiscordSender extends DiscordSenderBase implements CommandSender {
private PermissibleBase perm = new PermissibleBase(this);

View file

@ -1,20 +1,19 @@
package buttondevteam.discordplugin;
import java.util.Arrays;
import java.util.stream.Collectors;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitTask;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.chat.Channel;
import buttondevteam.lib.chat.IDiscordSender;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
import org.bukkit.Bukkit;
import org.bukkit.scheduler.BukkitTask;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IUser;
import java.util.Arrays;
import java.util.stream.Collectors;
public abstract class DiscordSenderBase implements IDiscordSender {
/**
* May be null.

View file

View file

@ -1,8 +1,7 @@
package buttondevteam.discordplugin;
import org.bukkit.entity.Player;
import buttondevteam.discordplugin.playerfaker.VanillaCommandListener;
import org.bukkit.entity.Player;
public interface IMCPlayer<T extends DiscordSenderBase & IMCPlayer<T>> extends Player {
VanillaCommandListener<T> getVanillaCmdListener();

View file

@ -1,10 +1,10 @@
package buttondevteam.discordplugin;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.UUID;
import buttondevteam.discordplugin.listeners.MCChatListener;
import buttondevteam.lib.TBMCCoreAPI;
import com.mojang.authlib.GameProfile;
import lombok.val;
import net.minecraft.server.v1_12_R1.*;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.craftbukkit.v1_12_R1.CraftServer;
@ -12,33 +12,10 @@ import org.bukkit.craftbukkit.v1_12_R1.util.CraftChatMessage;
import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.objenesis.ObjenesisStd;
import com.mojang.authlib.GameProfile;
import buttondevteam.discordplugin.listeners.MCChatListener;
import buttondevteam.lib.TBMCCoreAPI;
import lombok.val;
import net.minecraft.server.v1_12_R1.AdvancementDataPlayer;
import net.minecraft.server.v1_12_R1.ChatMessageType;
import net.minecraft.server.v1_12_R1.DedicatedPlayerList;
import net.minecraft.server.v1_12_R1.DedicatedServer;
import net.minecraft.server.v1_12_R1.Entity;
import net.minecraft.server.v1_12_R1.EntityHuman;
import net.minecraft.server.v1_12_R1.EntityPlayer;
import net.minecraft.server.v1_12_R1.GameProfileBanList;
import net.minecraft.server.v1_12_R1.IChatBaseComponent;
import net.minecraft.server.v1_12_R1.IpBanList;
import net.minecraft.server.v1_12_R1.LoginListener;
import net.minecraft.server.v1_12_R1.MinecraftServer;
import net.minecraft.server.v1_12_R1.NBTTagCompound;
import net.minecraft.server.v1_12_R1.NetworkManager;
import net.minecraft.server.v1_12_R1.OpList;
import net.minecraft.server.v1_12_R1.Packet;
import net.minecraft.server.v1_12_R1.PacketPlayOutChat;
import net.minecraft.server.v1_12_R1.ScoreboardServer;
import net.minecraft.server.v1_12_R1.ServerStatisticManager;
import net.minecraft.server.v1_12_R1.WhiteList;
import net.minecraft.server.v1_12_R1.World;
import net.minecraft.server.v1_12_R1.WorldServer;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.UUID;
public class PlayerListWatcher extends DedicatedPlayerList {
private DedicatedPlayerList plist;

View file

@ -0,0 +1,83 @@
package buttondevteam.discordplugin.commands;
import buttondevteam.discordplugin.DiscordConnectedPlayer;
import buttondevteam.discordplugin.DiscordPlayer;
import buttondevteam.discordplugin.listeners.MCChatListener;
import buttondevteam.lib.TBMCChannelConnectFakeEvent;
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 "channelcon";
}
@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;
}
if (MCChatListener.hasCustomChat(message.getChannel())) {
if (args.equalsIgnoreCase("remove")) {
if (MCChatListener.removeCustomChat(message.getChannel()))
message.reply("channel connection removed.");
else
message.reply("wait what, couldn't remove channel connection.");
return true;
}
message.reply("this channel is already connected to a Minecraft channel. Use `@ChromaBot channelcon remove` to remove it.");
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 dp = DiscordPlayer.getUser(message.getAuthor().getStringID(), DiscordPlayer.class);
val chp = dp.getAs(TBMCPlayer.class);
if (chp == null) {
message.reply("you need to connect your Minecraft account. On our server in #bot do /connect <MCname>");
return true;
}
DiscordConnectedPlayer dcp = new DiscordConnectedPlayer(message.getAuthor(), message.getChannel(), chp.getUUID(), Bukkit.getOfflinePlayer(chp.getUUID()).getName());
val ev = new TBMCChannelConnectFakeEvent(dcp, chan.get());
//Using a fake player with no login/logout, should be fine for this event
String groupid = ev.getGroupID(ev.getSender()); //We're not trying to send in a specific group, we want to know which group the user belongs to (so not getGroupID())
if (groupid == null) {
message.reply("sorry, that didn't work. You cannot use that Minecraft channel.");
return true;
}
if (MCChatListener.getCustomChats().stream().anyMatch(cc -> cc.groupID.equals(groupid) && cc.mcchannel.ID.equals(chan.get().ID))) {
message.reply("sorry, this MC chat is already connected to a different channel, multiple channels are not supported atm.");
return true;
}
MCChatListener.addCustomChat(message.getChannel(), groupid, ev.getChannel(), dp, message.getAuthor(), dcp);
message.reply("alright, connection made to group `" + groupid + "`!");
return true;
}
@Override
public String[] getHelpText() {
return new String[]{ //
"---- 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 /connect <mcname>.", //
"Call this command from the channel you want to use. Usage: @ChromaBot channelcon <mcchannel>", //
"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.", //
"Invite link: <https://discordapp.com/oauth2/authorize?client_id=226443037893591041&scope=bot&permissions=268509264>" //
};
}
}

View file

@ -1,16 +1,14 @@
package buttondevteam.discordplugin.commands;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import com.google.common.collect.HashBiMap;
import buttondevteam.discordplugin.DiscordPlayer;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.TBMCCoreAPI;
import buttondevteam.lib.player.TBMCPlayer;
import buttondevteam.lib.player.TBMCPlayerBase;
import com.google.common.collect.HashBiMap;
import org.bukkit.Bukkit;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Player;
import sx.blah.discord.handle.obj.IMessage;
public class ConnectCommand extends DiscordCommandBase {
@ -27,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(),
@ -46,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);
@ -60,11 +56,12 @@ public class ConnectCommand extends DiscordCommandBase {
}
WaitingToConnect.put(p.getName(), message.getAuthor().getStringID());
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"Pending connection - accept connection in Minecraft from the account " + args
+ " before the server gets restarted. You can also adjust the Minecraft name you want to connect to with the same command.");
"Alright! Now accept the connection in Minecraft from the account " + args
+ " before the next server restart. You can also adjust the Minecraft name you want to connect to with the same command.");
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

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

View file

@ -1,29 +1,36 @@
package buttondevteam.discordplugin.commands;
import java.util.HashMap;
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;
import static buttondevteam.discordplugin.listeners.CommandListener.debug;
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());
commands.put("debug", new DebugCommand());
}
public static void runCommand(String cmd, String args, IMessage message) {
debug("F"); //Not sure if needed
DiscordCommandBase command = commands.get(cmd);
if (command == null) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
@ -32,12 +39,19 @@ public abstract class DiscordCommandBase {
+ "help' for help");
return;
}
debug("G");
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.");
}
debug("H");
}
protected String[] splitargs(String args) {
return args.split("\\s+");
}
}

View file

@ -1,11 +1,11 @@
package buttondevteam.discordplugin.commands;
import java.util.Arrays;
import java.util.stream.Collectors;
import buttondevteam.discordplugin.DiscordPlugin;
import sx.blah.discord.handle.obj.IMessage;
import java.util.Arrays;
import java.util.stream.Collectors;
public class HelpCommand extends DiscordCommandBase {
@Override
@ -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

@ -14,23 +14,23 @@ 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();
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." //
? "enabled. Use '/mcchat' again to turn it off." //
: "disabled."));
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while setting mcchat for user" + message.getAuthor().getName(), e);
}
return true;
}
@Override

View file

@ -6,115 +6,88 @@ import buttondevteam.lib.TBMCCoreAPI;
import sx.blah.discord.handle.obj.IMessage;
import sx.blah.discord.handle.obj.IRole;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class RoleCommand extends DiscordCommandBase {
@Override
public String getCommandName() {
return "role";
}
@Override
public String getCommandName() {
return "role";
}
@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(" ");
if (argsa[0].equalsIgnoreCase("add")) {
final IRole role = checkAndGetRole(message, argsa, "This command adds a game role to your account.");
if (role == null)
return;
try {
DPUtils.perform(() -> message.getAuthor().addRole(role));
DiscordPlugin.sendMessageToChannel(message.getChannel(), "Added game role.");
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while adding role!", e);
DiscordPlugin.sendMessageToChannel(message.getChannel(), "An error occured while adding the role.");
}
} 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;
try {
DPUtils.perform(() -> message.getAuthor().removeRole(role));
DiscordPlugin.sendMessageToChannel(message.getChannel(), "Removed game role.");
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while removing role!", e);
DiscordPlugin.sendMessageToChannel(message.getChannel(), "An error occured while removing the role.");
}
} else if (argsa[0].equalsIgnoreCase("list")) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"List of game roles:\n" + DiscordPlugin.GameRoles.stream().collect(Collectors.joining("\n")));
} else if (argsa[0].equalsIgnoreCase("admin") && argsa.length > 1 && argsa[1].equalsIgnoreCase("addrole")) {
if (message.getAuthor().getRolesForGuild(DiscordPlugin.mainServer).stream()
.noneMatch(r -> r.getLongID() == 126030201472811008L)) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"You need to be a moderator to use this command.");
return;
}
if (argsa.length < 3) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"Add a role to the game role list.\nUsage: " + argsa[0] + " <rolename>");
return;
}
String rolename = Arrays.stream(argsa).skip(2).collect(Collectors.joining(" "));
final List<IRole> roles = (TBMCCoreAPI.IsTestServer() ? DiscordPlugin.devServer : DiscordPlugin.mainServer)
.getRolesByName(rolename);
if (roles.size() == 0) {
DiscordPlugin.sendMessageToChannel(message.getChannel(), "That role cannot be found on Discord.");
return;
}
if (roles.size() > 1) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"There are more roles with this name. Why are there more roles with this name?");
return;
}
DiscordPlugin.GameRoles.add(roles.get(0).getName());
DiscordPlugin.sendMessageToChannel(message.getChannel(), "Game role added.");
}
}
@Override
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 true;
try {
DPUtils.perform(() -> message.getAuthor().addRole(role));
DiscordPlugin.sendMessageToChannel(message.getChannel(), "Added game role.");
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while adding role!", e);
DiscordPlugin.sendMessageToChannel(message.getChannel(), "An error occured while adding the role.");
}
} 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 true;
try {
DPUtils.perform(() -> message.getAuthor().removeRole(role));
DiscordPlugin.sendMessageToChannel(message.getChannel(), "Removed game role.");
} catch (Exception e) {
TBMCCoreAPI.SendException("Error while removing role!", e);
DiscordPlugin.sendMessageToChannel(message.getChannel(), "An error occured while removing the role.");
}
} else if (argsa[0].equalsIgnoreCase("list")) {
listRoles(message);
} else return false;
return true;
}
private IRole checkAndGetRole(IMessage message, String[] argsa, String usage) {
if (argsa.length < 2) {
DiscordPlugin.sendMessageToChannel(message.getChannel(), usage + "\nUsage: " + argsa[0] + " <rolename>");
return null;
}
String rolename = argsa[1];
for (int i = 2; i < argsa.length; i++)
rolename += " " + argsa[i];
if (!DiscordPlugin.GameRoles.contains(rolename)) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"That game role cannot be found.\nList of game roles:\n"
+ DiscordPlugin.GameRoles.stream().collect(Collectors.joining("\n")));
return null;
}
final List<IRole> roles = (TBMCCoreAPI.IsTestServer() ? DiscordPlugin.devServer : DiscordPlugin.mainServer)
.getRolesByName(rolename);
if (roles.size() == 0) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"The specified role cannot be found on Discord! Removing from the list.");
DiscordPlugin.GameRoles.remove(rolename);
return null;
}
if (roles.size() > 1) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"There are more roles with this name. Why are there more roles with this name?");
return null;
}
return roles.get(0);
}
private void listRoles(IMessage message) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"List of game roles:\n" + DiscordPlugin.GameRoles.stream().sorted().collect(Collectors.joining("\n")));
}
@Override
public String[] getHelpText() {
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" };
}
private IRole checkAndGetRole(IMessage message, String[] argsa, String usage) {
if (argsa.length < 2) {
DiscordPlugin.sendMessageToChannel(message.getChannel(), usage + "\nUsage: " + argsa[0] + " <rolename>");
return null;
}
String rolename = argsa[1];
for (int i = 2; i < argsa.length; i++)
rolename += " " + argsa[i];
if (!DiscordPlugin.GameRoles.contains(rolename)) {
DiscordPlugin.sendMessageToChannel(message.getChannel(), "That game role cannot be found.");
listRoles(message);
return null;
}
final List<IRole> roles = DiscordPlugin.mainServer.getRolesByName(rolename);
if (roles.size() == 0) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"The specified role cannot be found on Discord! Removing from the list.");
DiscordPlugin.GameRoles.remove(rolename);
return null;
}
if (roles.size() > 1) {
DiscordPlugin.sendMessageToChannel(message.getChannel(),
"There are more roles with this name. Why are there more roles with this name?");
return null;
}
return roles.get(0);
}
@Override
public String[] getHelpText() {
return new String[]{ //
"Add or remove game roles from yourself.", //
"Usage: role add|remove <name> or role list", //
};
}
}

View file

@ -1,8 +1,5 @@
package buttondevteam.discordplugin.commands;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import buttondevteam.discordplugin.DiscordPlayer;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.TBMCCoreAPI;
@ -11,6 +8,10 @@ import buttondevteam.lib.player.ChromaGamerBase.InfoTarget;
import sx.blah.discord.handle.obj.IMessage;
import sx.blah.discord.handle.obj.IUser;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class UserinfoCommand extends DiscordCommandBase {
@Override
@ -19,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();
@ -39,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])) {
@ -51,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);
}
@ -76,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) {
@ -93,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

@ -1,12 +1,11 @@
package buttondevteam.discordplugin.listeners;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import buttondevteam.discordplugin.DPUtils;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.PluginUpdater;
import buttondevteam.lib.TBMCCoreAPI;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class AutoUpdaterListener implements Listener {
@EventHandler

View file

@ -5,9 +5,13 @@ import buttondevteam.discordplugin.DiscordPlugin;
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.IMessage;
@ -22,158 +26,202 @@ import java.util.concurrent.TimeUnit;
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[] 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 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 final Random serverReadyRandom = new Random();
private static final ArrayList<Short> usableServerReadyStrings = new ArrayList<Short>(serverReadyStrings.length) {
private static final long serialVersionUID = 2213771460909848770L;
private static void createUsableServerReadyStrings(ArrayList<Short> list) {
for (short i = 0; i < serverReadyStrings.length; i++)
list.add(i);
}
{
createUsableServerReadyStrings(this);
}
};
private static long lasttime = 0;
private static void createUsableServerReadyStrings(ArrayList<Short> list) {
for (short i = 0; i < serverReadyStrings.length; i++)
list.add(i);
}
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()))
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()) //
return;
if (DiscordPlayer.getUser(event.getAuthor().getStringID(), DiscordPlayer.class)
.isMinecraftChatEnabled())
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());
}
}
} };
}
private static long lasttime = 0;
/**
* Runs a ChromaBot command.
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 (DiscordPlayer.getUser(event.getAuthor().getStringID(), DiscordPlayer.class)
.isMinecraftChatEnabled())
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 {
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.
*
* @param message
* The Discord 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)
*/
public static boolean runCommand(IMessage message, boolean mentionedonly) {
if (DiscordPlugin.SafeMode)
return true;
final StringBuilder cmdwithargs = new StringBuilder(message.getContent());
final String mention = DiscordPlugin.dc.getOurUser().mention(false);
final String mentionNick = DiscordPlugin.dc.getOurUser().mention(true);
boolean gotmention = checkanddeletemention(cmdwithargs, mention, message);
gotmention = checkanddeletemention(cmdwithargs, mentionNick, message) || gotmention;
* @param message The Discord 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)
*/
public static boolean runCommand(IMessage message, boolean mentionedonly) {
debug("A");
if (DiscordPlugin.SafeMode)
return true;
debug("B");
final StringBuilder cmdwithargs = new StringBuilder(message.getContent());
final String mention = DiscordPlugin.dc.getOurUser().mention(false);
final String mentionNick = DiscordPlugin.dc.getOurUser().mention(true);
boolean gotmention = checkanddeletemention(cmdwithargs, mention, message);
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)
gotmention = checkanddeletemention(cmdwithargs, mentionRole, message) || gotmention; // Delete all mentions
if (mentionedonly && !gotmention) {
message.getChannel().setTypingStatus(false);
return false;
}
message.getChannel().setTypingStatus(true);
int index = cmdwithargs.indexOf(" ");
String cmd;
String args;
if (index == -1) {
cmd = cmdwithargs.toString();
args = "";
} else {
cmd = cmdwithargs.substring(0, index);
args = cmdwithargs.substring(index + 1);
}
DiscordCommandBase.runCommand(cmd.toLowerCase(), args, message);
message.getChannel().setTypingStatus(false);
return true;
}
gotmention = checkanddeletemention(cmdwithargs, mentionRole, message) || gotmention; // Delete all mentions
debug("C");
if (mentionedonly && !gotmention) {
message.getChannel().setTypingStatus(false);
return false;
}
debug("D");
message.getChannel().setTypingStatus(true);
String cmdwithargsString = cmdwithargs.toString().trim(); //Remove spaces between mention and command
int index = cmdwithargsString.indexOf(" ");
String cmd;
String args;
if (index == -1) {
cmd = cmdwithargsString;
args = "";
} else {
cmd = cmdwithargsString.substring(0, index);
args = cmdwithargsString.substring(index + 1).trim(); //In case there are multiple spaces
}
debug("E");
DiscordCommandBase.runCommand(cmd.toLowerCase(), args, message);
message.getChannel().setTypingStatus(false);
return true;
}
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 (cmdwithargs.length() > mention.length() + 1)
cmdwithargs = cmdwithargs.delete(0,
cmdwithargs.charAt(mention.length()) == ' ' ? mention.length() + 1 : mention.length());
else
cmdwithargs.replace(0, cmdwithargs.length(), "help");
else
return false;
if (cmdwithargs.length() == 0)
cmdwithargs.replace(0, cmdwithargs.length(), "help");
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) {
if (message.getContent().startsWith(mention)) // TODO: Resolve mentions: Compound arguments, either a mention or text
if (cmdwithargs.length() > mention.length() + 1)
cmdwithargs = cmdwithargs.delete(0,
cmdwithargs.charAt(mention.length()) == ' ' ? mention.length() + 1 : mention.length());
else
cmdwithargs.replace(0, cmdwithargs.length(), "help");
else {
if (cmdwithargs.length() > 0 && cmdwithargs.charAt(0) == '/')
cmdwithargs.deleteCharAt(0); //Don't treat / as mention, mentions can be used in public mcchat
return false;
}
if (cmdwithargs.length() == 0)
cmdwithargs.replace(0, cmdwithargs.length(), "help");
return true;
}
}

View file

@ -1,9 +1,9 @@
package buttondevteam.discordplugin.listeners;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import buttondevteam.discordplugin.DiscordPlugin;
import buttondevteam.lib.TBMCDebugMessageEvent;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
public class DebugMessageListener implements Listener{
@EventHandler

View file

@ -23,8 +23,8 @@ public class ExceptionListener implements Listener {
return;
if (lastthrown.stream()
.anyMatch(ex -> Arrays.equals(e.getException().getStackTrace(), ex.getStackTrace())
&& e.getException().getMessage() == null ? ex.getMessage() == null
: e.getException().getMessage().equals(ex.getMessage())) // e.Exception.Message==ex.Message
&& (e.getException().getMessage() == null ? ex.getMessage() == null
: e.getException().getMessage().equals(ex.getMessage()))) // e.Exception.Message==ex.Message
&& lastsourcemsg.contains(e.getSourceMessage()))
return;
SendException(e.getException(), e.getSourceMessage());

View file

@ -10,6 +10,8 @@ import buttondevteam.lib.chat.Channel;
import buttondevteam.lib.chat.ChatRoom;
import buttondevteam.lib.chat.TBMCChatAPI;
import buttondevteam.lib.player.TBMCPlayer;
import com.vdurmont.emoji.EmojiParser;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.bukkit.Bukkit;
@ -33,9 +35,10 @@ import sx.blah.discord.util.MissingPermissionsException;
import java.awt.*;
import java.time.Instant;
import java.util.*;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@ -45,6 +48,7 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
private BukkitTask sendtask;
private LinkedBlockingQueue<AbstractMap.SimpleEntry<TBMCChatEvent, Instant>> sendevents = new LinkedBlockingQueue<>();
private Runnable sendrunnable;
private static Thread sendthread;
@EventHandler // Minecraft
public void onMCChat(TBMCChatEvent ev) {
@ -54,8 +58,10 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
if (sendtask != null)
return;
sendrunnable = () -> {
sendthread = Thread.currentThread();
processMCToDiscord();
sendtask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, sendrunnable);
if (DiscordPlugin.plugin.isEnabled()) //Don't run again if shutting down
sendtask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, sendrunnable);
};
sendtask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, sendrunnable);
}
@ -117,27 +123,41 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
}
};
// Checks if the given channel is different than where the message was sent from
// Or if it was from MC
Predicate<IChannel> isdifferentchannel = ch -> !(e.getSender() instanceof DiscordSenderBase)
|| ((DiscordSenderBase) e.getSender()).getChannel().getLongID() != ch.getLongID();
if ((e.getChannel() == Channel.GlobalChat || e.getChannel().ID.equals("rp"))
&& isdifferentchannel.test(DiscordPlugin.chatchannel))
&& (e.isFromcmd() || isdifferentchannel.test(DiscordPlugin.chatchannel)))
doit.accept(lastmsgdata == null
? lastmsgdata = new LastMsgData(DiscordPlugin.chatchannel, null, null)
: lastmsgdata);
for (LastMsgData data : lastmsgPerUser) {
if (data.dp.isMinecraftChatEnabled() && isdifferentchannel.test(data.channel)
&& e.shouldSendTo(getSender(data.channel, data.user, data.dp)))
if (data.dp.isMinecraftChatEnabled() && (e.isFromcmd() || isdifferentchannel.test(data.channel))
&& e.shouldSendTo(getSender(data.channel, data.user)))
doit.accept(data);
}
val iterator = lastmsgCustom.iterator();
while (iterator.hasNext()) { //TODO: Add cmd to fix mcchat
val lmd = iterator.next();
if ((e.isFromcmd() || 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
if (e.shouldSendTo(lmd.dcp) && e.getGroupID().equals(lmd.groupID)) //Check original user's permissions
doit.accept(lmd);
else {
iterator.remove(); //If the user no longer has permission, remove the connection
DiscordPlugin.sendMessageToChannel(lmd.channel, "The user no longer has permission to view the channel, connection removed.");
}
}
} catch (Exception ex) {
TBMCCoreAPI.SendException("Error while sending message to Discord!", ex);
}
}
@RequiredArgsConstructor
private static class LastMsgData {
public static class LastMsgData {
public IMessage message;
public long time;
public String content;
@ -147,6 +167,20 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
public final DiscordPlayer dp;
}
public static class CustomLMD extends LastMsgData {
public final String groupID;
public final Channel mcchannel;
public final DiscordConnectedPlayer dcp;
public CustomLMD(@NonNull IChannel channel, @NonNull IUser user, @NonNull DiscordPlayer dp,
@NonNull String groupid, @NonNull Channel mcchannel, @NonNull DiscordConnectedPlayer dcp) {
super(channel, user, dp);
groupID = groupid;
this.mcchannel = mcchannel;
this.dcp = dcp;
}
}
@EventHandler
public void onChatPreprocess(TBMCChatPreprocessEvent event) {
int start = -1;
@ -168,7 +202,7 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
}
private static final String[] UnconnectedCmds = new String[]{"list", "u", "shrug", "tableflip", "unflip", "mwiki",
"yeehaw"};
"yeehaw", "lenny", "rp", "plugins"};
private static LastMsgData lastmsgdata;
private static short lastlist = 0;
@ -177,6 +211,10 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
* 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<>();
public static boolean privateMCChat(IChannel channel, boolean start, IUser user, DiscordPlayer dp) {
TBMCPlayer mcp = dp.getAs(TBMCPlayer.class);
@ -220,6 +258,27 @@ 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, DiscordPlayer dp, IUser user, DiscordConnectedPlayer dcp) {
val lmd = new CustomLMD(channel, user, dp, groupid, mcchannel, dcp);
lastmsgCustom.add(lmd);
}
public static boolean hasCustomChat(IChannel channel) {
return lastmsgCustom.stream().anyMatch(lmd -> lmd.channel.getLongID() == channel.getLongID());
}
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) {
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
*/
@ -242,6 +301,12 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
data.message = null; // Since only private channels are stored, only those will work anyways
}
public static void resetLastMessageCustom(IChannel channel) {
for (LastMsgData data : lastmsgCustom)
if (data.channel.getLongID() == channel.getLongID())
data.message = null;
}
/**
* This overload sends it to the global chat.
*/
@ -258,37 +323,50 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
action.accept(DiscordPlugin.chatchannel);
for (LastMsgData data : lastmsgPerUser)
action.accept(data.channel);
lastmsgCustom.forEach(cc -> action.accept(cc.channel));
}
private static void forAllowedMCChat(Consumer<IChannel> action, TBMCSystemChatEvent event) {
if (Channel.GlobalChat.ID.equals(event.getChannel().ID))
action.accept(DiscordPlugin.chatchannel);
for (LastMsgData data : lastmsgPerUser)
if (event.shouldSendTo(getSender(data.channel, data.user, data.dp)))
if (event.shouldSendTo(getSender(data.channel, data.user)))
action.accept(data.channel);
lastmsgCustom.stream().filter(data -> event.shouldSendTo(data.dcp))
.map(data -> data.channel).forEach(action);
}
public static void stop() {
if (sendthread != null) sendthread.interrupt();
if (recthread != null) recthread.interrupt();
}
private BukkitTask rectask;
private LinkedBlockingQueue<MessageReceivedEvent> recevents = new LinkedBlockingQueue<>();
private IMessage lastmsgfromd; // Last message sent by a Discord user, used for clearing checkmarks
private Runnable recrun;
private static Thread recthread;
@Override // Discord
public void handle(MessageReceivedEvent ev) {
if (DiscordPlugin.SafeMode)
return;
val author = ev.getMessage().getAuthor();
if (!ev.getMessage().getChannel().getStringID().equals(DiscordPlugin.chatchannel.getStringID())
&& !(ev.getMessage().getChannel().isPrivate() && isMinecraftChatEnabled(author.getStringID())))
return;
if (author.isBot())
return;
final boolean hasCustomChat = hasCustomChat(ev.getChannel());
if (!ev.getMessage().getChannel().getStringID().equals(DiscordPlugin.chatchannel.getStringID())
&& !(ev.getMessage().getChannel().isPrivate() && isMinecraftChatEnabled(author.getStringID()))
&& !hasCustomChat)
return;
if (ev.getMessage().getContent().equalsIgnoreCase("mcchat"))
return; // Race condition: If it gets here after it enabled mcchat it says it - I might as well allow disabling with this (CommandListener)
if (CommandListener.runCommand(ev.getMessage(), true))
return;
if (!ev.getMessage().getChannel().isPrivate())
resetLastMessage();
else if (hasCustomChat)
resetLastMessageCustom(ev.getChannel());
else
resetLastMessage(ev.getMessage().getChannel());
lastlist++;
@ -296,8 +374,10 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
if (rectask != null)
return;
recrun = () -> { //Don't return in a while loop next time
recthread = Thread.currentThread();
processDiscordToMC();
rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Continue message processing
if (DiscordPlugin.plugin.isEnabled()) //Don't run again if shutting down
rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Continue message processing
};
rectask = Bukkit.getScheduler().runTaskAsynchronously(DiscordPlugin.plugin, recrun); //Start message processing
}
@ -315,7 +395,7 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
val user = DiscordPlayer.getUser(sender.getStringID(), DiscordPlayer.class);
String dmessage = event.getMessage().getContent();
try {
final DiscordSenderBase dsender = getSender(event.getMessage().getChannel(), sender, user);
final DiscordSenderBase dsender = getSender(event.getMessage().getChannel(), sender);
for (IUser u : event.getMessage().getMentions()) {
dmessage = dmessage.replace(u.mention(false), "@" + u.getName()); // TODO: IG Formatting
@ -323,11 +403,15 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
dmessage = dmessage.replace(u.mention(true), "@" + (nick != null ? nick : u.getName()));
}
BiConsumer<Channel, String> sendChatMessage = (channel, msg) -> //
TBMCChatAPI.SendChatMessage(channel, dsender,
msg + (event.getMessage().getAttachments().size() > 0 ? "\n" + event.getMessage()
dmessage = EmojiParser.parseToAliases(dmessage, EmojiParser.FitzpatrickAction.PARSE); //Converts emoji to text- TODO: Add option to disable (resource pack?)
dmessage = dmessage.replaceAll(":(\\S+)\\|type_(?:(\\d)|(1)_2):", ":$1::skin-tone-$2:"); //Convert to Discord's format so it still shows up
Function<String, String> getChatMessage = msg -> //
msg + (event.getMessage().getAttachments().size() > 0 ? "\n" + event.getMessage()
.getAttachments().stream().map(a -> a.getUrl()).collect(Collectors.joining("\n"))
: ""));
: "");
CustomLMD clmd = getCustomChat(event.getChannel());
boolean react = false;
@ -336,40 +420,46 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
if (!event.getMessage().isDeleted() && !event.getChannel().isPrivate())
event.getMessage().delete();
});
final String cmd = dmessage.substring(1).toLowerCase();
//preprocessChat(dsender, dmessage); - Same is done below
final String cmdlowercased = dmessage.substring(1).toLowerCase();
if (dsender instanceof DiscordSender && Arrays.stream(UnconnectedCmds)
.noneMatch(s -> cmd.equals(s) || cmd.startsWith(s + " "))) {
.noneMatch(s -> cmdlowercased.equals(s) || cmdlowercased.startsWith(s + " "))) {
// Command not whitelisted
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 "
? "\nTo access your commands, first please connect your accounts, using /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`!"));
+ "\nThen y"
: "\nY")
+ "ou can access all of your regular commands (even offline) in private chat: DM me `mcchat`!");
return;
}
if (lastlist > 5) {
ListC = 0;
lastlist = 0;
}
if (cmd.equals("list") && Bukkit.getOnlinePlayers().size() == lastlistp && ListC++ > 2) // Lowered already
if (cmdlowercased.equals("list") && Bukkit.getOnlinePlayers().size() == lastlistp && ListC++ > 2) // Lowered already
{
dsender.sendMessage("Stop it. You know the answer.");
lastlist = 0;
} else {
int spi = cmd.indexOf(' ');
final String topcmd = spi == -1 ? cmd : cmd.substring(0, spi);
int spi = cmdlowercased.indexOf(' ');
final String topcmd = spi == -1 ? cmdlowercased : cmdlowercased.substring(0, spi);
Optional<Channel> ch = Channel.getChannels().stream()
.filter(c -> c.ID.equalsIgnoreCase(topcmd)).findAny();
.filter(c -> c.ID.equalsIgnoreCase(topcmd)
|| (c.IDs != null && c.IDs.length > 0
&& Arrays.stream(c.IDs).anyMatch(id -> id.equalsIgnoreCase(topcmd)))).findAny();
if (!ch.isPresent())
Bukkit.getScheduler().runTask(DiscordPlugin.plugin,
() -> VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmd));
() -> {
VanillaCommandListener.runBukkitOrVanillaCommand(dsender, cmdlowercased);
Bukkit.getLogger().info(dsender.getName() + " issued command from Discord: /" + cmdlowercased);
});
else {
Channel chc = ch.get();
if (!chc.ID.equals(Channel.GlobalChat.ID)
&& !event.getMessage().getChannel().isPrivate())
if (!chc.ID.equals(Channel.GlobalChat.ID) && !chc.ID.equals("rp") && !event.getMessage().getChannel().isPrivate())
dsender.sendMessage(
"You can only talk in global in the public chat. DM `mcchat` to enable private chat to talk in the other channels.");
else {
@ -387,7 +477,11 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
dsender.sendMessage("You're now talking in: "
+ DPUtils.sanitizeString(dsender.getMcchannel().DisplayName));
} else { // Send single message
sendChatMessage.accept(chc, cmd.substring(spi + 1));
final String msg = event.getMessage().getContent().substring(spi + 2);
if (clmd == null)
TBMCChatAPI.SendChatMessage(chc, dsender, getChatMessage.apply(msg), true);
else
TBMCChatAPI.SendChatMessageDontCheckSender(chc, dsender, getChatMessage.apply(msg), true, clmd.dcp);
react = true;
}
}
@ -396,21 +490,20 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
lastlistp = (short) Bukkit.getOnlinePlayers().size();
} else {// Not a command
if (dmessage.length() == 0 && event.getMessage().getAttachments().size() == 0
&& !event.getChannel().isPrivate())
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, 0,
&& !event.getChannel().isPrivate() && event.getMessage().isSystemMessage())
TBMCChatAPI.SendSystemMessage(Channel.GlobalChat, 0, "everyone",
(dsender instanceof Player ? ((Player) dsender).getDisplayName()
: dsender.getName()) + " pinned a message on Discord.");
else {
sendChatMessage.accept(dsender.getMcchannel(), dmessage);
if (clmd != null)
TBMCChatAPI.SendChatMessageDontCheckSender(clmd.mcchannel, dsender, getChatMessage.apply(dmessage), false, clmd.dcp);
else
TBMCChatAPI.SendChatMessage(dsender.getMcchannel(), dsender, getChatMessage.apply(dmessage));
react = true;
}
}
if (react) {
try {
/*
* System.out.println("Got message: " + m.getContent() + " with embeds: " + m.getEmbeds().stream().map(e -> e.getTitle() + " " + e.getDescription())
* .collect(Collectors.joining("\n")));
*/
if (lastmsgfromd != null) {
DPUtils.perform(() -> lastmsgfromd.removeReaction(DiscordPlugin.dc.getOurUser(),
DiscordPlugin.DELIVERED_REACTION)); // Remove it no matter what, we know it's there 99.99% of the time
@ -426,10 +519,46 @@ public class MCChatListener implements Listener, IListener<MessageReceivedEvent>
}
}
private boolean preprocessChat(DiscordSenderBase dsender, String dmessage) {
if (dmessage.length() < 2)
return false;
int index = dmessage.indexOf(" ");
String cmd;
if (index == -1) { // Only the command is run
cmd = dmessage;
for (Channel channel : Channel.getChannels()) {
if (cmd.equalsIgnoreCase(channel.ID) || (channel.IDs != null && Arrays.stream(channel.IDs).anyMatch(cmd::equalsIgnoreCase))) {
Channel oldch = dsender.getMcchannel();
if (oldch instanceof ChatRoom)
((ChatRoom) oldch).leaveRoom(dsender);
if (oldch.equals(channel))
dsender.setMcchannel(Channel.GlobalChat);
else {
dsender.setMcchannel(channel);
if (channel instanceof ChatRoom)
((ChatRoom) channel).joinRoom(dsender);
}
dsender.sendMessage("You are now talking in: " + dsender.getMcchannel().DisplayName);
return true;
}
}
} else { // We have arguments
cmd = dmessage.substring(0, index);
for (Channel channel : Channel.getChannels()) {
if (cmd.equalsIgnoreCase(channel.ID) || (channel.IDs != null && Arrays.stream(channel.IDs).anyMatch(cmd::equalsIgnoreCase))) {
TBMCChatAPI.SendChatMessage(channel, dsender, dmessage.substring(index + 1));
return true;
}
}
// TODO: Target selectors
}
return false;
}
/**
* 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, DiscordPlayer dp) {
private static DiscordSenderBase getSender(IChannel channel, final IUser author) {
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

View file

@ -45,26 +45,28 @@ public class MCListener implements Listener {
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) {
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));
}
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");
}
if (!DiscordPlugin.hooked)
MCChatListener.sendSystemMessageToChat(e.GetPlayer().PlayerName().get() + " joined the game");
MCChatListener.ListC = 0;
ChromaBot.getInstance().updatePlayerList();
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.OnlineSenders.put(dp.getDiscordID(),
new DiscordPlayerSender(user, user.getOrCreatePMChannel(), p));
MCChatListener.OnlineSenders.put("P" + dp.getDiscordID(),
new DiscordPlayerSender(user, DiscordPlugin.chatchannel, p));
}
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");
}
if (!DiscordPlugin.hooked)
MCChatListener.sendSystemMessageToChat(e.GetPlayer().PlayerName().get() + " joined the game");
MCChatListener.ListC = 0;
ChromaBot.getInstance().updatePlayerList();
});
}
@EventHandler(priority = EventPriority.HIGHEST)
@ -93,7 +95,7 @@ public class MCListener implements Listener {
if (DiscordPlugin.SafeMode)
return;
DiscordPlayer dp = e.getPlayer().getAs(DiscordPlayer.class);
if (dp == null || dp.getDiscordID() == null || dp.getDiscordID() == "")
if (dp == null || dp.getDiscordID() == null || dp.getDiscordID().equals(""))
return;
IUser user = DiscordPlugin.dc.getUserByID(Long.parseLong(dp.getDiscordID()));
e.addInfo("Discord tag: " + user.getName() + "#" + user.getDiscriminator());

View file

@ -1,7 +1,5 @@
package buttondevteam.discordplugin.mccommands;
import org.bukkit.entity.Player;
import buttondevteam.discordplugin.DiscordPlayer;
import buttondevteam.discordplugin.commands.ConnectCommand;
import buttondevteam.discordplugin.listeners.MCChatListener;
@ -9,6 +7,7 @@ import buttondevteam.lib.chat.CommandClass;
import buttondevteam.lib.player.ChromaGamerBase;
import buttondevteam.lib.player.TBMCPlayer;
import buttondevteam.lib.player.TBMCPlayerBase;
import org.bukkit.entity.Player;
@CommandClass(modOnly = false, path = "accept")
public class AcceptMCCommand extends DiscordMCCommandBase {
@ -18,7 +17,7 @@ public class AcceptMCCommand extends DiscordMCCommandBase {
return new String[] { //
"§6---- Accept Discord connection ----", //
"Accept a pending connection between your Discord and Minecraft account.", //
"To start the connection process, do §b@ChromaBot connect <MCname>§r in the #bot channel on Discord", //
"To start the connection process, do §b/connect <MCname>§r in the #bot channel on Discord", //
"Usage: /" + alias + " accept" //
};
}

View file

@ -1,9 +1,8 @@
package buttondevteam.discordplugin.mccommands;
import org.bukkit.entity.Player;
import buttondevteam.discordplugin.commands.ConnectCommand;
import buttondevteam.lib.chat.CommandClass;
import org.bukkit.entity.Player;
@CommandClass(modOnly = false, path = "decline")
public class DeclineMCCommand extends DiscordMCCommandBase {
@ -13,7 +12,7 @@ public class DeclineMCCommand extends DiscordMCCommandBase {
return new String[] { //
"§6---- Decline Discord connection ----", //
"Decline a pending connection between your Discord and Minecraft account.", //
"To start the connection process, do §b@ChromaBot connect <MCname>§r in the #bot channel on Discord", //
"To start the connection process, do §b/connect <MCname>§r in the #bot channel on Discord", //
"Usage: /" + alias + " decline" //
};
}

View file

@ -1,7 +1,8 @@
package buttondevteam.discordplugin.playerfaker;
import java.util.*;
import buttondevteam.discordplugin.DiscordSenderBase;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.*;
import org.bukkit.block.PistonMoveReaction;
import org.bukkit.entity.Entity;
@ -10,13 +11,11 @@ import org.bukkit.event.player.PlayerTeleportEvent.TeleportCause;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.plugin.Plugin;
import org.bukkit.util.Vector;
import buttondevteam.discordplugin.DiscordSenderBase;
import lombok.Getter;
import lombok.Setter;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IUser;
import java.util.*;
@Getter
@Setter
@SuppressWarnings("deprecated")

View file

@ -1,25 +1,27 @@
package buttondevteam.discordplugin.playerfaker;
import java.net.InetSocketAddress;
import java.util.*;
import buttondevteam.discordplugin.DiscordPlugin;
import lombok.Getter;
import lombok.experimental.Delegate;
import org.bukkit.*;
import org.bukkit.advancement.Advancement;
import org.bukkit.advancement.AdvancementProgress;
import org.bukkit.conversations.Conversation;
import org.bukkit.conversations.ConversationAbandonedEvent;
import org.bukkit.entity.*;
import org.bukkit.entity.Entity;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.map.MapView;
import org.bukkit.permissions.PermissibleBase;
import org.bukkit.plugin.Plugin;
import org.bukkit.scoreboard.Scoreboard;
import buttondevteam.discordplugin.DiscordPlugin;
import lombok.experimental.Delegate;
import lombok.Getter;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IUser;
import java.net.InetSocketAddress;
import java.util.*;
public class DiscordFakePlayer extends DiscordHumanEntity implements Player {
protected DiscordFakePlayer(IUser user, IChannel channel, int entityId, UUID uuid, String mcname) {
super(user, channel, entityId, uuid);

View file

@ -1,7 +1,5 @@
package buttondevteam.discordplugin.playerfaker;
import java.util.UUID;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
@ -10,10 +8,11 @@ import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Villager;
import org.bukkit.inventory.*;
import org.bukkit.inventory.InventoryView.Property;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IUser;
import java.util.UUID;
public abstract class DiscordHumanEntity extends DiscordLivingEntity implements HumanEntity {
protected DiscordHumanEntity(IUser user, IChannel channel, int entityId, UUID uuid) {
super(user, channel, entityId, uuid);

View file

@ -1,12 +1,5 @@
package buttondevteam.discordplugin.playerfaker;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.HumanEntity;
@ -14,6 +7,13 @@ import org.bukkit.event.inventory.InventoryType;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class DiscordInventory implements Inventory {
public DiscordInventory(DiscordHumanEntity holder) {
this.holder = holder;

View file

@ -1,12 +1,7 @@
package buttondevteam.discordplugin.playerfaker;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.attribute.Attribute;
@ -21,12 +16,11 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.util.Vector;
import lombok.Getter;
import lombok.Setter;
import sx.blah.discord.handle.obj.IChannel;
import sx.blah.discord.handle.obj.IUser;
import java.util.*;
public abstract class DiscordLivingEntity extends DiscordEntity implements LivingEntity {
protected DiscordLivingEntity(IUser user, IChannel channel, int entityId, UUID uuid) {

View file

@ -1,7 +1,10 @@
package buttondevteam.discordplugin.playerfaker;
import java.util.Arrays;
import buttondevteam.discordplugin.DiscordSenderBase;
import buttondevteam.discordplugin.IMCPlayer;
import lombok.Getter;
import lombok.val;
import net.minecraft.server.v1_12_R1.*;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.v1_12_R1.CraftServer;
import org.bukkit.craftbukkit.v1_12_R1.CraftWorld;
@ -9,17 +12,7 @@ import org.bukkit.craftbukkit.v1_12_R1.command.VanillaCommandWrapper;
import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
import org.bukkit.entity.Player;
import buttondevteam.discordplugin.DiscordSenderBase;
import buttondevteam.discordplugin.IMCPlayer;
import lombok.Getter;
import lombok.val;
import net.minecraft.server.v1_12_R1.ChatMessage;
import net.minecraft.server.v1_12_R1.CommandException;
import net.minecraft.server.v1_12_R1.EnumChatFormat;
import net.minecraft.server.v1_12_R1.IChatBaseComponent;
import net.minecraft.server.v1_12_R1.ICommandListener;
import net.minecraft.server.v1_12_R1.MinecraftServer;
import net.minecraft.server.v1_12_R1.World;
import java.util.Arrays;
public class VanillaCommandListener<T extends DiscordSenderBase & IMCPlayer<T>> implements ICommandListener {
private @Getter T player;

0
src/main/resources/plugin.yml Normal file → Executable file
View file

0
src/test/java/buttondevteam/DiscordPlugin/AppTest.java Normal file → Executable file
View file