diff --git a/src/main/java/buttondevteam/discordplugin/DiscordSender.scala b/src/main/java/buttondevteam/discordplugin/DiscordSender.scala index dadb809..4dca813 100644 --- a/src/main/java/buttondevteam/discordplugin/DiscordSender.scala +++ b/src/main/java/buttondevteam/discordplugin/DiscordSender.scala @@ -13,7 +13,7 @@ import java.util class DiscordSender(user: User, channel: MessageChannel, pname: String) extends DiscordSenderBase(user, channel) with CommandSender { private val perm = new PermissibleBase(this) private val name: String = Option(pname) - .orElse(Option(user).map(u => SMono(u.asMember(DiscordPlugin.mainServer.getId)) + .orElse(Option(user).flatMap(u => SMono(u.asMember(DiscordPlugin.mainServer.getId)) .onErrorResume(_ => SMono.empty).blockOption() .map(u => u.getDisplayName))) .getOrElse("Discord user") diff --git a/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.scala b/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.scala index 83121aa..63592fa 100644 --- a/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.scala +++ b/src/main/java/buttondevteam/discordplugin/DiscordSenderBase.scala @@ -44,21 +44,23 @@ abstract class DiscordSenderBase protected(var user: User, var channel: MessageC return } val sendmsg = DPUtils.sanitizeString(message) - this synchronized msgtosend += "\n" + sendmsg - if (sendtask == null) sendtask = Bukkit.getScheduler.runTaskLaterAsynchronously(DiscordPlugin.plugin, () => { - def foo(): Unit = { - channel.createMessage((if (user != null) user.getMention + "\n" - else "") + msgtosend.trim).subscribe - sendtask = null - msgtosend = "" - } + this synchronized { + msgtosend += "\n" + sendmsg + if (sendtask == null) sendtask = Bukkit.getScheduler.runTaskLaterAsynchronously(DiscordPlugin.plugin, () => { + def foo(): Unit = { + channel.createMessage((if (user != null) user.getMention + "\n" + else "") + msgtosend.trim).subscribe + sendtask = null + msgtosend = "" + } - foo() - }, 4) // Waits a 0.2 second to gather all/most of the different messages + foo() + }, 4) // Waits a 0.2 second to gather all/most of the different messages + } } catch { case e: Exception => TBMCCoreAPI.SendException("An error occured while sending message to DiscordSender", e, DiscordPlugin.plugin) } - override def sendMessage(messages: Array[String]): Unit = sendMessage(String.join("\n", messages)) + override def sendMessage(messages: Array[String]): Unit = sendMessage(String.join("\n", messages: _*)) } \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.scala b/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.scala index e54764b..31cb524 100644 --- a/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.scala +++ b/src/main/java/buttondevteam/discordplugin/announcer/AnnouncerModule.scala @@ -5,9 +5,10 @@ import buttondevteam.lib.TBMCCoreAPI import buttondevteam.lib.architecture.{Component, ComponentMetadata} import buttondevteam.lib.player.ChromaGamerBase import com.google.gson.JsonParser -import discord4j.core.`object`.entity.Message import discord4j.core.`object`.entity.channel.MessageChannel -import reactor.core.publisher.Flux +import reactor.core.scala.publisher.SMono + +import scala.annotation.tailrec /** * Posts new posts from Reddit to the specified channel(s). It will pin the regular posts (not the mod posts). @@ -39,23 +40,19 @@ import reactor.core.publisher.Flux override protected def enable(): Unit = { if (DPUtils.disableIfConfigError(this, channel, modChannel)) return AnnouncerModule.stop = false //If not the first time - val kp = keepPinned.get - if (kp eq 0) return - val msgs: Flux[Message] = channel.get.flatMapMany(_.getPinnedMessages).takeLast(kp) + val kp: Short = keepPinned.get + if (kp <= 0) return + val msgs = channel.get.flatMapMany(_.getPinnedMessages).takeLast(kp) msgs.subscribe(_.unpin) new Thread(() => this.AnnouncementGetterThreadMethod()).start() } override protected def disable(): Unit = AnnouncerModule.stop = true - private def AnnouncementGetterThreadMethod(): Unit = while ( { - !AnnouncerModule.stop - }) { - try { - if (!isEnabled) { //noinspection BusyWait - Thread.sleep(10000) - continue //todo: continue is not supported - } + @tailrec + private def AnnouncementGetterThreadMethod() { + if (AnnouncerModule.stop) return + if (isEnabled) try { //If not enabled, just wait val body = TBMCCoreAPI.DownloadString(subredditURL.get + "/new/.json?limit=10") val json = new JsonParser().parse(body).getAsJsonObject.get("data").getAsJsonObject.get("children").getAsJsonArray val msgsb = new StringBuilder @@ -66,30 +63,32 @@ import reactor.core.publisher.Flux val data = item.get("data").getAsJsonObject var author = data.get("author").getAsString val distinguishedjson = data.get("distinguished") - var distinguished = null - if (distinguishedjson.isJsonNull) distinguished = null - else distinguished = distinguishedjson.getAsString + val distinguished = if (distinguishedjson.isJsonNull) null else distinguishedjson.getAsString val permalink = "https://www.reddit.com" + data.get("permalink").getAsString val date = data.get("created_utc").getAsLong if (date > lastSeenTime.get) lastSeenTime.set(date) else if (date > lastAnnouncementTime.get) { //noinspection ConstantConditions - do { + { val reddituserclass = ChromaGamerBase.getTypeForFolder("reddit") - if (reddituserclass == null) break //todo: break is not supported - val user = ChromaGamerBase.getUser(author, reddituserclass) - val id = user.getConnectedID(classOf[DiscordPlayer]) - if (id != null) author = "<@" + id + ">" - } while ( { - false - }) + if (reddituserclass != null) { + val user = ChromaGamerBase.getUser(author, reddituserclass) + val id = user.getConnectedID(classOf[DiscordPlayer]) + if (id != null) author = "<@" + id + ">" + } + } if (!author.startsWith("<")) author = "/u/" + author - (if (distinguished != null && distinguished == "moderator") modmsgsb - else msgsb).append("A new post was submitted to the subreddit by ").append(author).append("\n").append(permalink).append("\n") + (if (distinguished != null && distinguished == "moderator") modmsgsb else msgsb) + .append("A new post was submitted to the subreddit by ").append(author).append("\n") + .append(permalink).append("\n") lastanntime = date } } - if (msgsb.length > 0) channel.get.flatMap((ch: MessageChannel) => ch.createMessage(msgsb.toString)).flatMap(Message.pin).subscribe - if (modmsgsb.length > 0) modChannel.get.flatMap((ch: MessageChannel) => ch.createMessage(modmsgsb.toString)).flatMap(Message.pin).subscribe + + def sendMsg(ch: SMono[MessageChannel], msg: String) = + ch.asJava().flatMap(c => c.createMessage(msg)).flatMap(_.pin).subscribe + + if (msgsb.nonEmpty) sendMsg(channel.get(), msgsb.toString()) + if (modmsgsb.nonEmpty) sendMsg(modChannel.get(), modmsgsb.toString()) if (lastAnnouncementTime.get ne lastanntime) lastAnnouncementTime.set(lastanntime) // If sending succeeded } catch { case e: Exception => @@ -100,5 +99,6 @@ import reactor.core.publisher.Flux case ex: InterruptedException => Thread.currentThread.interrupt() } + AnnouncementGetterThreadMethod() } } \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.scala b/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.scala index f898fe8..6b7a4f9 100644 --- a/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.scala +++ b/src/main/java/buttondevteam/discordplugin/commands/Command2DCSender.scala @@ -16,7 +16,7 @@ import lombok.RequiredArgsConstructor this.message.getChannel.flatMap((ch: MessageChannel) => ch.createMessage(this.message.getAuthor.map((u: User) => DPUtils.nickMention(u.getId) + ", ").orElse("") + msg)).subscribe } - override def sendMessage(message: Array[String]): Unit = sendMessage(String.join("\n", message)) + override def sendMessage(message: Array[String]): Unit = sendMessage(String.join("\n", message: _*)) - override def getName = message.getAuthor.map(_.getUsername).orElse("Discord") + override def getName: String = Option(message.getAuthor.orElse(null)).map(_.getUsername).getOrElse("Discord") } \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.scala b/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.scala index db1d9a9..09a51ad 100644 --- a/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.scala +++ b/src/main/java/buttondevteam/discordplugin/commands/ConnectCommand.scala @@ -7,8 +7,9 @@ import com.google.common.collect.HashBiMap import org.bukkit.Bukkit import org.bukkit.entity.Player -@CommandClass(helpText = Array(Array("Connect command", // - "This command lets you connect your account with a Minecraft account. This allows using the private Minecraft chat and other things."))) object ConnectCommand { +@CommandClass(helpText = Array("Connect command", // + "This command lets you connect your account with a Minecraft account." + + " This allows using the private Minecraft chat and other things.")) object ConnectCommand { /** * Key: Minecraft name
* Value: Discord ID diff --git a/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.scala b/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.scala index 67fee6e..79f3d9c 100644 --- a/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.scala +++ b/src/main/java/buttondevteam/discordplugin/commands/DebugCommand.scala @@ -3,24 +3,27 @@ package buttondevteam.discordplugin.commands import buttondevteam.discordplugin.DiscordPlugin import buttondevteam.discordplugin.listeners.CommonListeners import buttondevteam.lib.chat.{Command2, CommandClass} +import discord4j.common.util.Snowflake import discord4j.core.`object`.entity.{Member, User} -import reactor.core.publisher.Mono +import reactor.core.scala.publisher.SMono -@CommandClass(helpText = Array(Array("Switches debug mode."))) +@CommandClass(helpText = Array("Switches debug mode.")) class DebugCommand extends ICommand2DC { @Command2.Subcommand override def `def`(sender: Command2DCSender): Boolean = { - sender.getMessage.getAuthorAsMember.switchIfEmpty(sender.getMessage.getAuthor.map //Support DMs - ((u: User) => u.asMember(DiscordPlugin.mainServer.getId)).orElse(Mono.empty)).flatMap((m: Member) => DiscordPlugin.plugin.modRole.get.map((mr) => m.getRoleIds.stream.anyMatch((r: Snowflake) => r == mr.getId)).switchIfEmpty(Mono.fromSupplier(() => DiscordPlugin.mainServer.getOwnerId.asLong eq m.getId.asLong))) - .onErrorReturn(false) //Role not found - .subscribe((success: Any) => { - def foo(success: Any) = { - if (success) sender.sendMessage("debug " + (if (CommonListeners.debug) "enabled" - else "disabled")) - else sender.sendMessage("you need to be a moderator to use this command.") - } - - foo(success) + SMono(sender.getMessage.getAuthorAsMember) + .switchIfEmpty(Option(sender.getMessage.getAuthor.orElse(null)) //Support DMs + .map((u: User) => SMono(u.asMember(DiscordPlugin.mainServer.getId))).getOrElse(SMono.empty)) + .flatMap((m: Member) => DiscordPlugin.plugin.modRole.get + .map(mr => m.getRoleIds.stream.anyMatch((r: Snowflake) => r == mr.getId)) + .switchIfEmpty(SMono.fromCallable(() => DiscordPlugin.mainServer.getOwnerId.asLong eq m.getId.asLong))) + .onErrorResume(_ => SMono.just(false)) //Role not found + .subscribe(success => { + if (success) { + CommonListeners.debug = !CommonListeners.debug; + sender.sendMessage("debug " + (if (CommonListeners.debug) "enabled" else "disabled")) + } else + sender.sendMessage("you need to be a moderator to use this command.") }) true } diff --git a/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.scala b/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.scala index c3d1029..77d51df 100644 --- a/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.scala +++ b/src/main/java/buttondevteam/discordplugin/commands/HelpCommand.scala @@ -2,8 +2,8 @@ package buttondevteam.discordplugin.commands import buttondevteam.lib.chat.{Command2, CommandClass} -@CommandClass(helpText = Array(Array("Help command", // - "Shows some info about a command or lists the available commands."))) +@CommandClass(helpText = Array("Help command", // + "Shows some info about a command or lists the available commands.")) class HelpCommand extends ICommand2DC { @Command2.Subcommand def `def`(sender: Command2DCSender, @Command2.TextArg @Command2.OptionalArg args: String): Boolean = { diff --git a/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.scala b/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.scala index c9e7203..7768b44 100644 --- a/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.scala +++ b/src/main/java/buttondevteam/discordplugin/commands/UserinfoCommand.scala @@ -4,13 +4,12 @@ import buttondevteam.discordplugin.{DiscordPlayer, DiscordPlugin} import buttondevteam.lib.chat.{Command2, CommandClass} import buttondevteam.lib.player.ChromaGamerBase import buttondevteam.lib.player.ChromaGamerBase.InfoTarget -import discord4j.core.`object`.entity.{Member, Message, User} +import discord4j.core.`object`.entity.{Message, User} +import reactor.core.scala.publisher.SFlux -import java.util - -@CommandClass(helpText = Array(Array("User information", // +@CommandClass(helpText = Array("User information", // "Shows some information about users, from Discord, from Minecraft or from Reddit if they have these accounts connected.", - "If used without args, shows your info."))) + "If used without args, shows your info.")) class UserinfoCommand extends ICommand2DC { @Command2.Subcommand def `def`(sender: Command2DCSender, @Command2.OptionalArg @Command2.TextArg user: String): Boolean = { @@ -25,16 +24,11 @@ class UserinfoCommand extends ICommand2DC { else if (user.contains("#")) { val targettag = user.split("#") val targets = getUsers(message, targettag(0)) - if (targets.size == 0) { + if (targets.isEmpty) { channel.createMessage("The user cannot be found (by name): " + user).subscribe return true } - for (ptarget <- targets) { - if (ptarget.getDiscriminator.equalsIgnoreCase(targettag(1))) { - target = ptarget - break //todo: break is not supported - } - } + targets.collectFirst(_.getDiscriminator.equalsIgnoreCase(targettag(1))) if (target == null) { channel.createMessage("The user cannot be found (by discriminator): " + user + "(Found " + targets.size + " users with the name.)").subscribe return true @@ -42,7 +36,7 @@ class UserinfoCommand extends ICommand2DC { } else { val targets = getUsers(message, user) - if (targets.size == 0) { + if (targets.isEmpty) { channel.createMessage("The user cannot be found on Discord: " + user).subscribe return true } @@ -50,7 +44,7 @@ class UserinfoCommand extends ICommand2DC { channel.createMessage("Multiple users found with that (nick)name. Please specify the whole tag, like ChromaBot#6338 or use a ping.").subscribe return true } - target = targets.get(0) + target = targets.head } } if (target == null) { @@ -65,12 +59,11 @@ class UserinfoCommand extends ICommand2DC { } private def getUsers(message: Message, args: String) = { - var targets: util.List[User] val guild = message.getGuild.block if (guild == null) { //Private channel - targets = DiscordPlugin.dc.getUsers.filter((u) => u.getUsername.equalsIgnoreCase(args)).collectList.block + SFlux(DiscordPlugin.dc.getUsers).filter(u => u.getUsername.equalsIgnoreCase(args)).collectSeq().block() } - else targets = guild.getMembers.filter((m: Member) => m.getUsername.equalsIgnoreCase(args)).map((m: Member) => m.asInstanceOf[User]).collectList.block - targets + else + SFlux(guild.getMembers).filter(_.getUsername.equalsIgnoreCase(args)).map(_.asInstanceOf[User]).collectSeq().block() } } \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/commands/VersionCommand.scala b/src/main/java/buttondevteam/discordplugin/commands/VersionCommand.scala index 137f869..9f8544c 100644 --- a/src/main/java/buttondevteam/discordplugin/commands/VersionCommand.scala +++ b/src/main/java/buttondevteam/discordplugin/commands/VersionCommand.scala @@ -3,7 +3,7 @@ package buttondevteam.discordplugin.commands import buttondevteam.discordplugin.DiscordPlugin import buttondevteam.lib.chat.{Command2, CommandClass} -@CommandClass(helpText = Array(Array("Version", "Returns the plugin's version"))) +@CommandClass(helpText = Array("Version", "Returns the plugin's version")) object VersionCommand { def getVersion: Array[String] = { val desc = DiscordPlugin.plugin.getDescription @@ -11,7 +11,7 @@ object VersionCommand { } } -@CommandClass(helpText = Array(Array("Version", "Returns the plugin's version"))) +@CommandClass(helpText = Array("Version", "Returns the plugin's version")) class VersionCommand extends ICommand2DC { @Command2.Subcommand override def `def`(sender: Command2DCSender): Boolean = { sender.sendMessage(VersionCommand.getVersion) diff --git a/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.scala b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.scala index 14b9ba5..fdf04ca 100644 --- a/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.scala +++ b/src/main/java/buttondevteam/discordplugin/exceptions/ExceptionListenerModule.scala @@ -9,7 +9,7 @@ import discord4j.core.`object`.entity.{Guild, Role} import org.apache.commons.lang.exception.ExceptionUtils import org.bukkit.Bukkit import org.bukkit.event.{EventHandler, Listener} -import reactor.core.publisher.Mono +import reactor.core.scala.publisher.SMono import java.util import java.util.stream.Collectors @@ -20,29 +20,24 @@ import java.util.stream.Collectors object ExceptionListenerModule { private def SendException(e: Throwable, sourcemessage: String): Unit = { if (instance == null) return - try getChannel.flatMap((channel: MessageChannel) => { - def foo(channel: MessageChannel) = { - var coderRole: Mono[Role] = channel match { - case ch: GuildChannel => instance.pingRole(ch.getGuild).get - case _ => Mono.empty - } - coderRole.map((role: Role) => if (TBMCCoreAPI.IsTestServer) new StringBuilder - else new StringBuilder(role.getMention).append("\n")).defaultIfEmpty(new StringBuilder).flatMap((sb: StringBuilder) => { - def foo(sb: StringBuilder) = { - sb.append(sourcemessage).append("\n") - sb.append("```").append("\n") - var stackTrace = util.Arrays.stream(ExceptionUtils.getStackTrace(e).split("\\n")).filter((s: String) => !s.contains("\tat ") || s.contains("\tat buttondevteam.")).collect(Collectors.joining("\n")) - if (sb.length + stackTrace.length >= 1980) stackTrace = stackTrace.substring(0, 1980 - sb.length) - sb.append(stackTrace).append("\n") - sb.append("```") - channel.createMessage(sb.toString) - } - - foo(sb) - }) + try getChannel.flatMap(channel => { + val coderRole = channel match { + case ch: GuildChannel => instance.pingRole(SMono(ch.getGuild)).get + case _ => SMono.empty } - - foo(channel) + coderRole.map((role: Role) => if (TBMCCoreAPI.IsTestServer) new StringBuilder + else new StringBuilder(role.getMention).append("\n")) + .defaultIfEmpty(new StringBuilder).flatMap(sb => { + sb.append(sourcemessage).append("\n") + sb.append("```").append("\n") + var stackTrace = util.Arrays.stream(ExceptionUtils.getStackTrace(e).split("\\n")) + .filter(s => !s.contains("\tat ") || s.contains("\tat buttondevteam.")) + .collect(Collectors.joining("\n")) + if (sb.length + stackTrace.length >= 1980) stackTrace = stackTrace.substring(0, 1980 - sb.length) + sb.append(stackTrace).append("\n") + sb.append("```") + SMono(channel.createMessage(sb.toString)) + }) }).subscribe catch { case ex: Exception => @@ -52,9 +47,9 @@ object ExceptionListenerModule { private var instance: ExceptionListenerModule = null - def getChannel: Mono[MessageChannel] = { + def getChannel: SMono[MessageChannel] = { if (instance != null) return instance.channel.get - Mono.empty + SMono.empty } } @@ -64,13 +59,12 @@ class ExceptionListenerModule extends Component[DiscordPlugin] with Listener { @EventHandler def onException(e: TBMCExceptionEvent): Unit = { if (DiscordPlugin.SafeMode || !ComponentManager.isEnabled(getClass)) return - if (lastthrown.stream.anyMatch((ex: Throwable) => util.Arrays.equals(e.getException.getStackTrace, ex.getStackTrace) && (if (e.getException.getMessage == null) ex.getMessage == null - else e.getException.getMessage == ex.getMessage)) // e.Exception.Message==ex.Message + if (lastthrown.stream.anyMatch(ex => e.getException.getStackTrace.sameElements(ex.getStackTrace) + && (if (e.getException.getMessage == null) ex.getMessage == null else e.getException.getMessage == ex.getMessage)) && lastsourcemsg.contains(e.getSourceMessage)) { return } - ExceptionListenerModule - .SendException(e.getException, e.getSourceMessage) + ExceptionListenerModule.SendException(e.getException, e.getSourceMessage) if (lastthrown.size >= 10) lastthrown.remove(0) if (lastsourcemsg.size >= 10) lastsourcemsg.remove(0) lastthrown.add(e.getException) @@ -86,7 +80,7 @@ class ExceptionListenerModule extends Component[DiscordPlugin] with Listener { /** * The role to ping if an error occurs. Set to empty ('') to disable. */ - private def pingRole(guild: Mono[Guild]) = DPUtils.roleData(getConfig, "pingRole", "Coder", guild) + private def pingRole(guild: SMono[Guild]) = DPUtils.roleData(getConfig, "pingRole", "Coder", guild) override protected def enable(): Unit = { if (DPUtils.disableIfConfigError(this, channel)) return diff --git a/src/main/java/buttondevteam/discordplugin/fun/FunModule.scala b/src/main/java/buttondevteam/discordplugin/fun/FunModule.scala index 4b71d87..f919932 100644 --- a/src/main/java/buttondevteam/discordplugin/fun/FunModule.scala +++ b/src/main/java/buttondevteam/discordplugin/fun/FunModule.scala @@ -5,16 +5,15 @@ import buttondevteam.discordplugin.{DPUtils, DiscordPlugin} import buttondevteam.lib.TBMCCoreAPI import buttondevteam.lib.architecture.{Component, ConfigData} import com.google.common.collect.Lists -import discord4j.common.util.Snowflake import discord4j.core.`object`.entity.channel.{GuildChannel, MessageChannel} -import discord4j.core.`object`.entity.{Guild, Member, Message, Role} -import discord4j.core.`object`.presence.{Presence, Status} +import discord4j.core.`object`.entity.{Guild, Message} +import discord4j.core.`object`.presence.Status import discord4j.core.event.domain.PresenceUpdateEvent import discord4j.core.spec.{EmbedCreateSpec, MessageCreateSpec} import org.bukkit.Bukkit import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.{EventHandler, Listener} -import reactor.core.publisher.Mono +import reactor.core.scala.publisher.{SFlux, SMono} import java.util import java.util.Calendar @@ -50,41 +49,47 @@ object FunModule { lastlist = 0 } if (msglowercased == "/list" && Bukkit.getOnlinePlayers.size == lastlistp && { - ListC += 1; + ListC += 1 ListC - 1 } > 2) { // Lowered already - DPUtils.reply(message, Mono.empty, "stop it. You know the answer.").subscribe + DPUtils.reply(message, SMono.empty, "stop it. You know the answer.").subscribe lastlist = 0 lastlistp = Bukkit.getOnlinePlayers.size.toShort return true //Handled } lastlistp = Bukkit.getOnlinePlayers.size.toShort //Didn't handle - if (!TBMCCoreAPI.IsTestServer && util.Arrays.stream(fm.serverReady).get.anyMatch(msglowercased.contains _)) { + if (!TBMCCoreAPI.IsTestServer && fm.serverReady.get.exists(msglowercased.contains)) { var next = 0 if (usableServerReadyStrings.size == 0) fm.createUsableServerReadyStrings() next = usableServerReadyStrings.remove(serverReadyRandom.nextInt(usableServerReadyStrings.size)) - DPUtils.reply(message, Mono.empty, fm.serverReadyAnswers.get.get(next)).subscribe + DPUtils.reply(message, SMono.empty, fm.serverReadyAnswers.get.get(next)).subscribe return false //Still process it as a command/mcchat if needed } - false + false } - private var lasttime = 0 + private var lasttime: Long = 0 def handleFullHouse(event: PresenceUpdateEvent): Unit = { val fm = ComponentManager.getIfEnabled(classOf[FunModule]) if (fm == null) return if (Calendar.getInstance.get(Calendar.DAY_OF_MONTH) % 5 != 0) return + if (!Option(event.getOld.orElse(null)).exists(_.getStatus == Status.OFFLINE) + || event.getCurrent.getStatus == Status.OFFLINE) + return //If it's not an offline -> online change fm.fullHouseChannel.get.filter((ch: MessageChannel) => ch.isInstanceOf[GuildChannel]) - .flatMap((channel: MessageChannel) => fm.fullHouseDevRole(channel.asInstanceOf[GuildChannel].getGuild).get.filter((role: Role) => event.getOld.map((p: Presence) => p.getStatus == Status.OFFLINE).orElse(false)).filter((role: Role) => !(event.getCurrent.getStatus == Status.OFFLINE)).filterWhen((devrole: Role) => event.getMember.flatMap((m: Member) => m.getRoles.any((r: Role) => r.getId.asLong == devrole.getId.asLong))).filterWhen((devrole: Role) => event.getGuild.flatMapMany((g: Guild) => g.getMembers.filter((m: Member) => m.getRoleIds.stream.anyMatch((s: Snowflake) => s == devrole.getId))).flatMap(Member.getPresence).all((pr: Presence) => !(pr.getStatus == Status.OFFLINE))).filter((devrole: Role) => lasttime + 10 < TimeUnit.NANOSECONDS.toHours(System.nanoTime)).flatMap //This should stay so it checks this last - ((devrole: Role) => { - def foo(devrole: Role) = { + .flatMap(channel => fm.fullHouseDevRole(SMono(channel.asInstanceOf[GuildChannel].getGuild)).get + .filterWhen(devrole => SMono(event.getMember) + .flatMap(m => SFlux(m.getRoles).any(_.getId.asLong == devrole.getId.asLong))) + .filterWhen(devrole => SMono(event.getGuild) + .flatMapMany(g => SFlux(g.getMembers).filter(_.getRoleIds.stream.anyMatch(_ == devrole.getId))) + .flatMap(_.getPresence).all(_.getStatus != Status.OFFLINE)) + .filter(_ => lasttime + 10 < TimeUnit.NANOSECONDS.toHours(System.nanoTime)) //This should stay so it checks this last + .flatMap(_ => { lasttime = TimeUnit.NANOSECONDS.toHours(System.nanoTime) - channel.createMessage((mcs: MessageCreateSpec) => mcs.setContent("Full house!").setEmbed((ecs: EmbedCreateSpec) => ecs.setImage("https://cdn.discordapp.com/attachments/249295547263877121/249687682618359808/poker-hand-full-house-aces-kings-playing-cards-15553791.png"))) - } - - foo(devrole) - })).subscribe + SMono(channel.createMessage((mcs: MessageCreateSpec) => mcs.setContent("Full house!") + .setEmbed((ecs: EmbedCreateSpec) => ecs.setImage("https://cdn.discordapp.com/attachments/249295547263877121/249687682618359808/poker-hand-full-house-aces-kings-playing-cards-15553791.png")))) + })).subscribe } } @@ -109,14 +114,18 @@ class FunModule extends Component[DiscordPlugin] with Listener { override protected def enable(): Unit = registerListener(this) - override protected def disable(): Unit = FunModule.lastlist = FunModule.lastlistp = FunModule.ListC = 0 + override protected def disable(): Unit = { + FunModule.lastlist = 0 + FunModule.lastlistp = 0 + FunModule.ListC = 0 + } @EventHandler def onPlayerJoin(event: PlayerJoinEvent): Unit = FunModule.ListC = 0 /** * If all of the people who have this role are online, the bot will post a full house. */ - private def fullHouseDevRole(guild: Mono[Guild]) = DPUtils.roleData(getConfig, "fullHouseDevRole", "Developer", guild) + private def fullHouseDevRole(guild: SMono[Guild]) = DPUtils.roleData(getConfig, "fullHouseDevRole", "Developer", guild) /** * The channel to post the full house to. diff --git a/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.scala b/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.scala index 57f6d8b..5c776ca 100644 --- a/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.scala +++ b/src/main/java/buttondevteam/discordplugin/listeners/CommonListeners.scala @@ -12,7 +12,7 @@ import discord4j.core.event.EventDispatcher import discord4j.core.event.domain.PresenceUpdateEvent import discord4j.core.event.domain.message.MessageCreateEvent import discord4j.core.event.domain.role.{RoleCreateEvent, RoleDeleteEvent, RoleUpdateEvent} -import reactor.core.scala.publisher.SMono +import reactor.core.scala.publisher.{SFlux, SMono} object CommonListeners { val timings = new Timings @@ -56,25 +56,19 @@ object CommonListeners { } foo(event) - }).onErrorContinue((err: Throwable, obj: Any) => TBMCCoreAPI.SendException("An error occured while handling a message!", err, DiscordPlugin.plugin)).subscribe + }).onErrorContinue((err: Throwable, _) => TBMCCoreAPI.SendException("An error occured while handling a message!", err, DiscordPlugin.plugin)).subscribe dispatcher.on(classOf[PresenceUpdateEvent]).subscribe((event: PresenceUpdateEvent) => { - def foo(event: PresenceUpdateEvent) = { - if (DiscordPlugin.SafeMode) return + if (!DiscordPlugin.SafeMode) FunModule.handleFullHouse(event) - } - - foo(event) }) - dispatcher.on(classOf[RoleCreateEvent]).subscribe(GameRoleModule.handleRoleEvent _) - dispatcher.on(classOf[RoleDeleteEvent]).subscribe(GameRoleModule.handleRoleEvent _) - dispatcher.on(classOf[RoleUpdateEvent]).subscribe(GameRoleModule.handleRoleEvent _) + SFlux(dispatcher.on(classOf[RoleCreateEvent])).subscribe(GameRoleModule.handleRoleEvent _) + SFlux(dispatcher.on(classOf[RoleDeleteEvent])).subscribe(GameRoleModule.handleRoleEvent _) + SFlux(dispatcher.on(classOf[RoleUpdateEvent])).subscribe(GameRoleModule.handleRoleEvent _) } - private var debug = false + var debug = false def debug(debug: String): Unit = if (CommonListeners.debug) { //Debug DPUtils.getLogger.info(debug) } - - def debug(): Unit = debug = !debug } \ No newline at end of file diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.scala b/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.scala index 94ad744..e69fb53 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.scala +++ b/src/main/java/buttondevteam/discordplugin/mcchat/ChannelconCommand.scala @@ -21,7 +21,7 @@ import java.util.{Objects, Optional} import javax.annotation.Nullable @SuppressWarnings(Array("SimplifyOptionalCallChains")) //Java 11 -@CommandClass(helpText = Array(Array("Channel connect", // +@CommandClass(helpText = Array("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 .", @@ -30,7 +30,7 @@ import javax.annotation.Nullable "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: " // -))) +)) class ChannelconCommand(private val module: MinecraftChatModule) extends ICommand2DC { @Command2.Subcommand def remove(sender: Command2DCSender): Boolean = { val message = sender.getMessage diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.scala b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.scala index 7b76572..78e7085 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.scala +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCommand.scala @@ -1,17 +1,17 @@ package buttondevteam.discordplugin.mcchat -import buttondevteam.discordplugin.{DPUtils, DiscordPlayer, DiscordPlugin} import buttondevteam.discordplugin.commands.{Command2DCSender, ICommand2DC} +import buttondevteam.discordplugin.{DPUtils, DiscordPlayer, DiscordPlugin} import buttondevteam.lib.chat.{Command2, CommandClass} import buttondevteam.lib.player.ChromaGamerBase import discord4j.core.`object`.entity.channel.PrivateChannel -@CommandClass(helpText = Array(Array( +@CommandClass(helpText = Array( "MC Chat", "This command enables or disables the Minecraft chat in private messages.", // "It can be useful if you don't want your messages to be visible, for example when talking in a private channel.", "You can also run all of the ingame commands you have access to using this command, if you have your accounts connected." // -))) +)) class MCChatCommand(private val module: MinecraftChatModule) extends ICommand2DC { @Command2.Subcommand override def `def`(sender: Command2DCSender): Boolean = { if (!(module.allowPrivateChat.get)) { diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.scala b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.scala index 25eb781..163b6ec 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.scala +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatCustom.scala @@ -8,15 +8,14 @@ import discord4j.core.`object`.entity.User import discord4j.core.`object`.entity.channel.MessageChannel import lombok.NonNull -import java.util -import java.util.Collections import javax.annotation.Nullable +import scala.collection.mutable.ListBuffer object MCChatCustom { /** * Used for town or nation chats or anything else */ - private[mcchat] val lastmsgCustom = new util.ArrayList[MCChatCustom.CustomLMD] + private[mcchat] val lastmsgCustom = new ListBuffer[MCChatCustom.CustomLMD] def addCustomChat(channel: MessageChannel, groupid: String, mcchannel: Channel, user: User, dcp: DiscordConnectedPlayer, toggles: Int, brtoggles: Set[TBMCSystemChatEvent.BroadcastTarget]): Boolean = { lastmsgCustom synchronized { @@ -29,34 +28,32 @@ object MCChatCustom { gid = groupid } val lmd = new MCChatCustom.CustomLMD(channel, user, gid, mcchannel, dcp, toggles, brtoggles) - lastmsgCustom.add(lmd) + lastmsgCustom += lmd } true } def hasCustomChat(channel: Snowflake): Boolean = - lastmsgCustom.stream.anyMatch((lmd: MCChatCustom.CustomLMD) => lmd.channel.getId.asLong == channel.asLong) + lastmsgCustom.exists(_.channel.getId.asLong == channel.asLong) @Nullable def getCustomChat(channel: Snowflake): CustomLMD = - lastmsgCustom.stream.filter((lmd: MCChatCustom.CustomLMD) => lmd.channel.getId.asLong == channel.asLong).findAny.orElse(null) + lastmsgCustom.find(_.channel.getId.asLong == channel.asLong).orNull - def removeCustomChat(channel: Snowflake): Boolean = { - lastmsgCustom synchronized MCChatUtils.lastmsgfromd.remove(channel.asLong) - lastmsgCustom.removeIf((lmd: MCChatCustom.CustomLMD) => { - def foo(lmd: MCChatCustom.CustomLMD): Boolean = { - if (lmd.channel.getId.asLong != channel.asLong) return false + def removeCustomChat(channel: Snowflake): Unit = { + lastmsgCustom synchronized { + MCChatUtils.lastmsgfromd.remove(channel.asLong) + lastmsgCustom.filterInPlace(lmd => { + if (lmd.channel.getId.asLong != channel.asLong) return true lmd.mcchannel match { case room: ChatRoom => room.leaveRoom(lmd.dcp) case _ => } - true - } - - foo(lmd) - }) + false + }) + } } - def getCustomChats: util.List[CustomLMD] = Collections.unmodifiableList(lastmsgCustom) + def getCustomChats: List[CustomLMD] = lastmsgCustom.toList class CustomLMD private[mcchat](@NonNull channel: MessageChannel, @NonNull user: User, val groupID: String, @NonNull mcchannel: Channel, val dcp: DiscordConnectedPlayer, var toggles: Int, diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.scala b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.scala index d4c837b..c5bd052 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.scala +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatListener.scala @@ -28,6 +28,7 @@ import java.util.Optional import java.util.concurrent.{LinkedBlockingQueue, TimeoutException} import java.util.function.{Consumer, Function, Predicate} import java.util.stream.Collectors +import scala.jdk.OptionConverters.RichOptional object MCChatListener { @@ -111,21 +112,21 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { foo(ecs) } val nanoTime: Long = System.nanoTime - val doit: MCChatListener.InterruptibleConsumer[MCChatUtils.LastMsgData] = (lastmsgdata: MCChatUtils.LastMsgData) => { - def foo(lastmsgdata: MCChatUtils.LastMsgData): Unit = { - if (lastmsgdata.message == null || !(authorPlayer == lastmsgdata.message.getEmbeds.get(0).getAuthor.map(_.getName).orElse(null)) || lastmsgdata.time / 1000000000f < nanoTime / 1000000000f - 120 || !(lastmsgdata.mcchannel.ID == e.getChannel.ID) || lastmsgdata.content.length + e.getMessage.length + 1 > 2048) { - lastmsgdata.message = lastmsgdata.channel.createEmbed(embed).block - lastmsgdata.time = nanoTime - lastmsgdata.mcchannel = e.getChannel - lastmsgdata.content = e.getMessage - } - else { - lastmsgdata.content = lastmsgdata.content + "\n" + e.getMessage // The message object doesn't get updated - lastmsgdata.message.edit((mes: MessageEditSpec) => mes.setEmbed(embed.andThen((ecs: EmbedCreateSpec) => ecs.setDescription(lastmsgdata.content)))).block - } + val doit = (lastmsgdata: MCChatUtils.LastMsgData) => { + if (lastmsgdata.message == null + || authorPlayer != lastmsgdata.message.getEmbeds.get(0).getAuthor.toScala.map(_.getName).orNull + || lastmsgdata.time / 1000000000f < nanoTime / 1000000000f - 120 + || !(lastmsgdata.mcchannel.ID == e.getChannel.ID) + || lastmsgdata.content.length + e.getMessage.length + 1 > 2048) { + lastmsgdata.message = lastmsgdata.channel.createEmbed(embed).block + lastmsgdata.time = nanoTime + lastmsgdata.mcchannel = e.getChannel + lastmsgdata.content = e.getMessage + } + else { + lastmsgdata.content = lastmsgdata.content + "\n" + e.getMessage // The message object doesn't get updated + lastmsgdata.message.edit((mes: MessageEditSpec) => mes.setEmbed(embed.andThen((ecs: EmbedCreateSpec) => ecs.setDescription(lastmsgdata.content)))).block } - - foo(lastmsgdata) } // Checks if the given channel is different than where the message was sent from // Or if it was from MC @@ -133,31 +134,29 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { if (e.getChannel.isGlobal && (e.isFromCommand || isdifferentchannel.test(module.chatChannel.get))) { if (MCChatUtils.lastmsgdata == null) MCChatUtils.lastmsgdata = new MCChatUtils.LastMsgData(module.chatChannelMono.block, null) - doit.accept(MCChatUtils.lastmsgdata) + doit(MCChatUtils.lastmsgdata) } for (data <- MCChatPrivate.lastmsgPerUser) { if ((e.isFromCommand || isdifferentchannel.test(data.channel.getId)) && e.shouldSendTo(MCChatUtils.getSender(data.channel.getId, data.user))) { - doit.accept(data) + doit(data) } } - MCChatCustom.lastmsgCustom synchronized - val iterator = MCChatCustom.lastmsgCustom.iterator - while ( { - iterator.hasNext - }) { - val lmd = iterator.next - if ((e.isFromCommand || isdifferentchannel.test(lmd.channel.getId)) //Test if msg is from Discord - && e.getChannel.ID == lmd.mcchannel.ID //If it's from a command, the command msg has been deleted, so we need to send it - && e.getGroupID == lmd.groupID) { //Check if this is the group we want to test - #58 - if (e.shouldSendTo(lmd.dcp)) { //Check original user's permissions - doit.accept(lmd) + MCChatCustom.lastmsgCustom synchronized { + MCChatCustom.lastmsgCustom.filterInPlace(lmd => { + if ((e.isFromCommand || isdifferentchannel.test(lmd.channel.getId)) //Test if msg is from Discord + && e.getChannel.ID == lmd.mcchannel.ID //If it's from a command, the command msg has been deleted, so we need to send it + && e.getGroupID == lmd.groupID) { //Check if this is the group we want to test - #58 + if (e.shouldSendTo(lmd.dcp)) { //Check original user's permissions + doit(lmd) + } + else { + lmd.channel.createMessage("The user no longer has permission to view the channel, connection removed.").subscribe + return false //If the user no longer has permission, remove the connection + } } - else { - iterator.remove() //If the user no longer has permission, remove the connection - lmd.channel.createMessage("The user no longer has permission to view the channel, connection removed.").subscribe - } - } + true + }) } } catch { case ex: InterruptedException => diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.scala b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.scala index 1926cab..4f10b2d 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.scala +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MCChatUtils.scala @@ -19,17 +19,20 @@ import org.bukkit.event.Event import org.bukkit.event.player.{AsyncPlayerPreLoginEvent, PlayerJoinEvent, PlayerLoginEvent, PlayerQuitEvent} import org.bukkit.plugin.AuthorNagException import org.reactivestreams.Publisher -import reactor.core.publisher.Mono +import reactor.core.scala.publisher.SMono import java.net.InetAddress import java.util import java.util._ import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.atomic.AtomicInteger -import java.util.function.Supplier import java.util.logging.Level -import java.util.stream.{Collectors, Stream} +import java.util.stream.Collectors import javax.annotation.Nullable +import scala.collection.concurrent +import scala.collection.convert.ImplicitConversions.`map AsJavaMap` +import scala.collection.mutable.ListBuffer +import scala.jdk.CollectionConverters.CollectionHasAsScala import scala.jdk.javaapi.CollectionConverters.asScala object MCChatUtils { @@ -43,13 +46,13 @@ object MCChatUtils { @Nullable private[mcchat] var lastmsgdata: MCChatUtils.LastMsgData = null private[mcchat] val lastmsgfromd = new LongObjectHashMap[Message] // Last message sent by a Discord user, used for clearing checkmarks private var module: MinecraftChatModule = null - private val staticExcludedPlugins = new util.HashMap[Class[_ <: Event], util.HashSet[String]] + private val staticExcludedPlugins = Map[Class[_ <: Event], util.HashSet[String]]() def updatePlayerList(): Unit = { val mod = getModule if (mod == null || !mod.showPlayerListOnDC.get) return if (lastmsgdata != null) updatePL(lastmsgdata) - MCChatCustom.lastmsgCustom.forEach(MCChatUtils.updatePL) + MCChatCustom.lastmsgCustom.foreach(MCChatUtils.updatePL) } private def notEnabled = (module == null || !module.disabling) && getModule == null //Allow using things while disabling the module @@ -85,7 +88,8 @@ object MCChatUtils { .filter(_ => C.incrementAndGet > 0) //Always true .map((p) => DPUtils.sanitizeString(p.getDisplayName)).collect(Collectors.joining(", ")) s(0) = C + " player" + (if (C.get != 1) "s" else "") + " online" - lmd.channel.asInstanceOf[TextChannel].edit((tce: TextChannelEditSpec) => tce.setTopic(String.join("\n----\n", s)).setReason("Player list update")).subscribe //Don't wait + lmd.channel.asInstanceOf[TextChannel].edit((tce: TextChannelEditSpec) => + tce.setTopic(String.join("\n----\n", s: _*)).setReason("Player list update")).subscribe //Don't wait } private[mcchat] def checkEssentials(p: Player): Boolean = { @@ -94,12 +98,12 @@ object MCChatUtils { !ess.getUser(p).isHidden } - def addSender[T <: DiscordSenderBase](senders: ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, T]], user: User, sender: T): T = + def addSender[T <: DiscordSenderBase](senders: concurrent.Map[String, ConcurrentHashMap[Snowflake, T]], user: User, sender: T): T = addSender(senders, user.getId.asString, sender) - def addSender[T <: DiscordSenderBase](senders: ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, T]], did: String, sender: T): T = { - var map = senders.get(did) - if (map == null) map = new ConcurrentHashMap[Snowflake, T] + def addSender[T <: DiscordSenderBase](senders: concurrent.Map[String, ConcurrentHashMap[Snowflake, T]], did: String, sender: T): T = { + val origMap = senders.get(did) + val map = if (origMap.isEmpty) new ConcurrentHashMap[Snowflake, T] else origMap.get map.put(sender.getChannel.getId, sender) senders.put(did, map) sender @@ -117,15 +121,12 @@ object MCChatUtils { else null.asInstanceOf } - def forPublicPrivateChat(action: Mono[MessageChannel] => Mono[_]): Mono[_] = { - if (notEnabled) return Mono.empty - val list = new util.ArrayList[Mono[_]] - list.add(action.apply(module.chatChannelMono)) - for (data <- MCChatPrivate.lastmsgPerUser) { - list.add(action.apply(Mono.just(data.channel))) - } + def forPublicPrivateChat(action: SMono[MessageChannel] => SMono[_]): SMono[_] = { + if (notEnabled) return SMono.empty + val list = MCChatPrivate.lastmsgPerUser.map(data => action(SMono.just(data.channel))) + .prepend(action(module.chatChannelMono)) // lastmsgCustom.forEach(cc -> action.accept(cc.channel)); - Only send relevant messages to custom chat - Mono.whenDelayError(list) + SMono.whenDelayError(list) } /** @@ -135,14 +136,14 @@ object MCChatUtils { * @param toggle The toggle to check * @param hookmsg Whether the message is also sent from the hook */ - def forCustomAndAllMCChat(action: Mono[MessageChannel] => Mono[_], @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): Mono[_] = { - if (notEnabled) return Mono.empty - val list = new util.ArrayList[Publisher[_]] - if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) list.add(forPublicPrivateChat(action)) - val customLMDFunction = (cc: MCChatCustom.CustomLMD) => action.apply(Mono.just(cc.channel)) - if (toggle == null) MCChatCustom.lastmsgCustom.stream.map(customLMDFunction).forEach(list.add(_)) - else MCChatCustom.lastmsgCustom.stream.filter((cc) => (cc.toggles & toggle.flag) ne 0).map(customLMDFunction).forEach(list.add(_)) - Mono.whenDelayError(list) + def forCustomAndAllMCChat(action: SMono[MessageChannel] => SMono[_], @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): SMono[_] = { + if (notEnabled) return SMono.empty + val list = new ListBuffer[Publisher[_]] + if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) list.append(forPublicPrivateChat(action)) + val customLMDFunction = (cc: MCChatCustom.CustomLMD) => action(SMono.just(cc.channel)) + if (toggle == null) MCChatCustom.lastmsgCustom.map(customLMDFunction).foreach(list.append(_)) + else MCChatCustom.lastmsgCustom.filter((cc) => (cc.toggles & (1 << toggle.id)) ne 0).map(customLMDFunction).foreach(list.append(_)) + SMono.whenDelayError(list) } /** @@ -152,18 +153,15 @@ object MCChatUtils { * @param sender The sender to check perms of or null to send to all that has it toggled * @param toggle The toggle to check or null to send to all allowed */ - def forAllowedCustomMCChat(action: Mono[MessageChannel] => Mono[_], @Nullable sender: CommandSender, @Nullable toggle: ChannelconBroadcast): Mono[_] = { - if (notEnabled) return Mono.empty - val st = MCChatCustom.lastmsgCustom.stream.filter((clmd) => { - def foo(clmd: CustomLMD): Boolean = { //new TBMCChannelConnectFakeEvent(sender, clmd.mcchannel).shouldSendTo(clmd.dcp) - Thought it was this simple hehe - Wait, it *should* be this simple - if (toggle != null && ((clmd.toggles & (1 << toggle.id)) eq 0)) return false //If null then allow - if (sender == null) return true - clmd.groupID.equals(clmd.mcchannel.getGroupID(sender)) - } - - foo(clmd) - }).map((cc) => action.apply(Mono.just(cc.channel))) //TODO: Send error messages on channel connect - Mono.whenDelayError(st.iterator) //Can't convert as an iterator or inside the stream, but I can convert it as a stream + def forAllowedCustomMCChat(action: SMono[MessageChannel] => SMono[_], @Nullable sender: CommandSender, @Nullable toggle: ChannelconBroadcast): SMono[_] = { + if (notEnabled) return SMono.empty + val st = MCChatCustom.lastmsgCustom.filter(clmd => { //new TBMCChannelConnectFakeEvent(sender, clmd.mcchannel).shouldSendTo(clmd.dcp) - Thought it was this simple hehe - Wait, it *should* be this simple + if (toggle != null && ((clmd.toggles & (1 << toggle.id)) eq 0)) false //If null then allow + else if (sender == null) true + else clmd.groupID.equals(clmd.mcchannel.getGroupID(sender)) + }).map(cc => action.apply(SMono.just(cc.channel))) //TODO: Send error messages on channel connect + //Mono.whenDelayError((() => st.iterator).asInstanceOf[java.lang.Iterable[Mono[_]]]) //Can't convert as an iterator or inside the stream, but I can convert it as a stream + SMono.whenDelayError(st) } /** @@ -174,47 +172,45 @@ object MCChatUtils { * @param toggle The toggle to check or null to send to all allowed * @param hookmsg Whether the message is also sent from the hook */ - def forAllowedCustomAndAllMCChat(action: Mono[MessageChannel] => Mono[_], @Nullable sender: CommandSender, @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): Mono[_] = { - if (notEnabled) return Mono.empty + def forAllowedCustomAndAllMCChat(action: SMono[MessageChannel] => SMono[_], @Nullable sender: CommandSender, @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): SMono[_] = { + if (notEnabled) return SMono.empty val cc = forAllowedCustomMCChat(action, sender, toggle) - if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) return Mono.whenDelayError(forPublicPrivateChat(action), cc) - Mono.whenDelayError(cc) + if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) return SMono.whenDelayError(Array(forPublicPrivateChat(action), cc)) + SMono.whenDelayError(Array(cc)) } - def send(message: String): Mono[MessageChannel] => Mono[_] = (ch: Mono[MessageChannel]) => ch.flatMap((mc: MessageChannel) => { - def foo(mc: MessageChannel) = { - resetLastMessage(mc) - mc.createMessage(DPUtils.sanitizeString(message)) - } - - foo(mc) + def send(message: String): SMono[MessageChannel] => SMono[_] = _.flatMap((mc: MessageChannel) => { + resetLastMessage(mc) + SMono(mc.createMessage(DPUtils.sanitizeString(message))) }) - def forAllowedMCChat(action: Mono[MessageChannel] => Mono[_], event: TBMCSystemChatEvent): Mono[_] = { - if (notEnabled) return Mono.empty - val list = new util.ArrayList[Mono[_]] - if (event.getChannel.isGlobal) list.add(action.apply(module.chatChannelMono)) + def forAllowedMCChat(action: SMono[MessageChannel] => SMono[_], event: TBMCSystemChatEvent): SMono[_] = { + if (notEnabled) return SMono.empty + val list = new ListBuffer[SMono[_]] + if (event.getChannel.isGlobal) list.append(action(module.chatChannelMono)) for (data <- MCChatPrivate.lastmsgPerUser) - if (event.shouldSendTo(getSender(data.channel.getId, data.user))) list.add(action.apply(Mono.just(data.channel))) //TODO: Only store ID?} - MCChatCustom.lastmsgCustom.stream.filter((clmd) => { - def foo(clmd: CustomLMD): Boolean = { - clmd.brtoggles.contains(event.getTarget) && event.shouldSendTo(clmd.dcp) - } - - foo(clmd) - }).map((clmd) => action.apply(Mono.just(clmd.channel))).forEach(list.add(_)) - Mono.whenDelayError(list) + if (event.shouldSendTo(getSender(data.channel.getId, data.user))) + list.append(action(SMono.just(data.channel))) //TODO: Only store ID? + MCChatCustom.lastmsgCustom.filter(clmd => + clmd.brtoggles.contains(event.getTarget) && event.shouldSendTo(clmd.dcp)) + .map(clmd => action(SMono.just(clmd.channel))).forEach(elem => { + list.append(elem) + () + }) + SMono.whenDelayError(list) } /** * 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[mcchat] def getSender(channel: Snowflake, author: User) = { //noinspection OptionalGetWithoutIsPresent - Stream.of[Supplier[Optional[DiscordSenderBase]]]( // https://stackoverflow.com/a/28833677/2703239 - () => Optional.ofNullable(getSender(OnlineSenders, channel, author)), // Find first non-null - () => Optional.ofNullable(getSender(ConnectedSenders, channel, author)), // This doesn't support the public chat, but it'll always return null for it - () => Optional.ofNullable(getSender(UnconnectedSenders, channel, author)), // - () => Optional.of(addSender(UnconnectedSenders, author, new DiscordSender(author, DiscordPlugin.dc.getChannelById(channel).block.asInstanceOf[MessageChannel])))).map(_.get).filter(_.isPresent).map(_.get).findFirst.get + private[mcchat] def getSender(channel: Snowflake, author: User): DiscordSenderBase = { //noinspection OptionalGetWithoutIsPresent + List[() => DiscordSenderBase]( // https://stackoverflow.com/a/28833677/2703239 + () => getSender[DiscordSenderBase](OnlineSenders, channel, author), // Find first non-null + () => getSender[DiscordSenderBase](ConnectedSenders, channel, author), // This doesn't support the public chat, but it'll always return null for it + () => getSender[DiscordSenderBase](UnconnectedSenders, channel, author), // + () => addSender[DiscordSenderBase](UnconnectedSenders, author, + new DiscordSender(author, SMono(DiscordPlugin.dc.getChannelById(channel)).block().asInstanceOf[MessageChannel]))) + .map(_.apply()).find(sender => sender != null).get } /** @@ -226,7 +222,7 @@ object MCChatUtils { def resetLastMessage(channel: Channel): Unit = { if (notEnabled) return if (channel.getId.asLong == module.chatChannel.get.asLong) { - if (lastmsgdata == null) lastmsgdata = new MCChatUtils.LastMsgData(module.chatChannelMono.block, null) + if (lastmsgdata == null) lastmsgdata = new MCChatUtils.LastMsgData(module.chatChannelMono.block(), null) else lastmsgdata.message = null return } // Don't set the whole object to null, the player and channel information should be preserved @@ -241,25 +237,23 @@ object MCChatUtils { } def addStaticExcludedPlugin(event: Class[_ <: Event], plugin: String): util.HashSet[String] = - staticExcludedPlugins.compute(event, (e: Class[_ <: Event], hs: util.HashSet[String]) => + staticExcludedPlugins.compute(event, (_, hs: util.HashSet[String]) => if (hs == null) Sets.newHashSet(plugin) else if (hs.add(plugin)) hs else hs) def callEventExcludingSome(event: Event): Unit = { if (notEnabled) return val second = staticExcludedPlugins.get(event.getClass) val first = module.excludedPlugins.get - val both = if (second == null) first + val both = if (second.isEmpty) first else util.Arrays.copyOf(first, first.length + second.size) var i = first.length - if (second != null) { - for (plugin <- second) { - both({ - i += 1; - i - 1 - }) = plugin + if (second.nonEmpty) { + for (plugin <- second.get.asScala) { + both(i) = plugin + i += 1 } } - callEventExcluding(event, false, both) + callEventExcluding(event, false, both: _*) } /** @@ -275,21 +269,18 @@ object MCChatUtils { if (event.isAsynchronous) { if (Thread.holdsLock(Bukkit.getPluginManager)) throw new IllegalStateException(event.getEventName + " cannot be triggered asynchronously from inside synchronized code.") if (Bukkit.getServer.isPrimaryThread) throw new IllegalStateException(event.getEventName + " cannot be triggered asynchronously from primary server thread.") - fireEventExcluding(event, only, plugins) + fireEventExcluding(event, only, plugins: _*) } - else Bukkit.getPluginManager synchronized fireEventExcluding(event, only, plugins) + else Bukkit.getPluginManager synchronized fireEventExcluding(event, only, plugins: _*) } private def fireEventExcluding(event: Event, only: Boolean, plugins: String*): Unit = { val handlers = event.getHandlers // Code taken from SimplePluginManager in Spigot-API val listeners = handlers.getRegisteredListeners val server = Bukkit.getServer - for (registration <- listeners) { - if (!registration.getPlugin.isEnabled || util.Arrays.stream(plugins).anyMatch((p: String) => only ^ p.equalsIgnoreCase(registration.getPlugin.getName))) { - continue //todo: continue is not supported - // Modified to exclude plugins - } - try registration.callEvent(event) + for (registration <- listeners) { // Modified to exclude plugins + if (registration.getPlugin.isEnabled + && !plugins.exists(only ^ _.equalsIgnoreCase(registration.getPlugin.getName))) try registration.callEvent(event) catch { case ex: AuthorNagException => val plugin = registration.getPlugin @@ -308,12 +299,8 @@ object MCChatUtils { */ def callLoginEvents(dcp: DiscordConnectedPlayer): Unit = { val loginFail = (kickMsg: String) => { - def foo(kickMsg: String): Unit = { - dcp.sendMessage("Minecraft chat disabled, as the login failed: " + kickMsg) - MCChatPrivate.privateMCChat(dcp.getChannel, start = false, dcp.getUser, dcp.getChromaUser) - } - - foo(kickMsg) + dcp.sendMessage("Minecraft chat disabled, as the login failed: " + kickMsg) + MCChatPrivate.privateMCChat(dcp.getChannel, start = false, dcp.getUser, dcp.getChromaUser) } //Probably also happens if the user is banned or so val event = new AsyncPlayerPreLoginEvent(dcp.getName, InetAddress.getLoopbackAddress, dcp.getUniqueId) callEventExcludingSome(event) diff --git a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.scala b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.scala index 5b8aa3f..f469019 100644 --- a/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.scala +++ b/src/main/java/buttondevteam/discordplugin/mcchat/MinecraftChatModule.scala @@ -1,22 +1,23 @@ package buttondevteam.discordplugin.mcchat import buttondevteam.core.component.channel.Channel -import buttondevteam.discordplugin.{ChannelconBroadcast, DPUtils, DiscordConnectedPlayer, DiscordPlugin} import buttondevteam.discordplugin.playerfaker.ServerWatcher import buttondevteam.discordplugin.playerfaker.perm.LPInjector import buttondevteam.discordplugin.util.DPState -import buttondevteam.lib.{TBMCCoreAPI, TBMCSystemChatEvent} +import buttondevteam.discordplugin.{ChannelconBroadcast, DPUtils, DiscordConnectedPlayer, DiscordPlugin} import buttondevteam.lib.architecture.{Component, ConfigData, ReadOnlyConfigData} +import buttondevteam.lib.{TBMCCoreAPI, TBMCSystemChatEvent} import com.google.common.collect.Lists import discord4j.common.util.Snowflake import discord4j.core.`object`.entity.channel.MessageChannel import discord4j.rest.util.Color import org.bukkit.Bukkit import reactor.core.publisher.Mono +import reactor.core.scala.publisher.SMono import java.util -import java.util.{Objects, UUID} import java.util.stream.Collectors +import java.util.{Objects, UUID} /** * Provides Minecraft chat connection to Discord. Commands may be used either in a public chat (limited) or in a DM. @@ -45,7 +46,7 @@ class MinecraftChatModule extends Component[DiscordPlugin] { */ val chatChannel: ReadOnlyConfigData[Snowflake] = DPUtils.snowflakeData(getConfig, "chatChannel", 0L) - def chatChannelMono: Mono[MessageChannel] = DPUtils.getMessageChannel(chatChannel.getPath, chatChannel.get) + def chatChannelMono: SMono[MessageChannel] = DPUtils.getMessageChannel(chatChannel.getPath, chatChannel.get) /** * The channel where the plugin can log when it mutes a player on Discord because of a Minecraft mute