Tailrec announcer method, fix some compile issues

This commit is contained in:
Norbi Peti 2021-03-06 01:27:21 +01:00
parent d416eef144
commit 7296ebd2f8
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
18 changed files with 272 additions and 292 deletions

View file

@ -13,7 +13,7 @@ import java.util
class DiscordSender(user: User, channel: MessageChannel, pname: String) extends DiscordSenderBase(user, channel) with CommandSender { class DiscordSender(user: User, channel: MessageChannel, pname: String) extends DiscordSenderBase(user, channel) with CommandSender {
private val perm = new PermissibleBase(this) private val perm = new PermissibleBase(this)
private val name: String = Option(pname) 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() .onErrorResume(_ => SMono.empty).blockOption()
.map(u => u.getDisplayName))) .map(u => u.getDisplayName)))
.getOrElse("Discord user") .getOrElse("Discord user")

View file

@ -44,21 +44,23 @@ abstract class DiscordSenderBase protected(var user: User, var channel: MessageC
return return
} }
val sendmsg = DPUtils.sanitizeString(message) val sendmsg = DPUtils.sanitizeString(message)
this synchronized msgtosend += "\n" + sendmsg this synchronized {
if (sendtask == null) sendtask = Bukkit.getScheduler.runTaskLaterAsynchronously(DiscordPlugin.plugin, () => { msgtosend += "\n" + sendmsg
def foo(): Unit = { if (sendtask == null) sendtask = Bukkit.getScheduler.runTaskLaterAsynchronously(DiscordPlugin.plugin, () => {
channel.createMessage((if (user != null) user.getMention + "\n" def foo(): Unit = {
else "") + msgtosend.trim).subscribe channel.createMessage((if (user != null) user.getMention + "\n"
sendtask = null else "") + msgtosend.trim).subscribe
msgtosend = "" sendtask = null
} msgtosend = ""
}
foo() foo()
}, 4) // Waits a 0.2 second to gather all/most of the different messages }, 4) // Waits a 0.2 second to gather all/most of the different messages
}
} catch { } catch {
case e: Exception => case e: Exception =>
TBMCCoreAPI.SendException("An error occured while sending message to DiscordSender", e, DiscordPlugin.plugin) 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: _*))
} }

View file

@ -5,9 +5,10 @@ import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.architecture.{Component, ComponentMetadata} import buttondevteam.lib.architecture.{Component, ComponentMetadata}
import buttondevteam.lib.player.ChromaGamerBase import buttondevteam.lib.player.ChromaGamerBase
import com.google.gson.JsonParser import com.google.gson.JsonParser
import discord4j.core.`object`.entity.Message
import discord4j.core.`object`.entity.channel.MessageChannel 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). * 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 = { override protected def enable(): Unit = {
if (DPUtils.disableIfConfigError(this, channel, modChannel)) return if (DPUtils.disableIfConfigError(this, channel, modChannel)) return
AnnouncerModule.stop = false //If not the first time AnnouncerModule.stop = false //If not the first time
val kp = keepPinned.get val kp: Short = keepPinned.get
if (kp eq 0) return if (kp <= 0) return
val msgs: Flux[Message] = channel.get.flatMapMany(_.getPinnedMessages).takeLast(kp) val msgs = channel.get.flatMapMany(_.getPinnedMessages).takeLast(kp)
msgs.subscribe(_.unpin) msgs.subscribe(_.unpin)
new Thread(() => this.AnnouncementGetterThreadMethod()).start() new Thread(() => this.AnnouncementGetterThreadMethod()).start()
} }
override protected def disable(): Unit = AnnouncerModule.stop = true override protected def disable(): Unit = AnnouncerModule.stop = true
private def AnnouncementGetterThreadMethod(): Unit = while ( { @tailrec
!AnnouncerModule.stop private def AnnouncementGetterThreadMethod() {
}) { if (AnnouncerModule.stop) return
try { if (isEnabled) try { //If not enabled, just wait
if (!isEnabled) { //noinspection BusyWait
Thread.sleep(10000)
continue //todo: continue is not supported
}
val body = TBMCCoreAPI.DownloadString(subredditURL.get + "/new/.json?limit=10") val body = TBMCCoreAPI.DownloadString(subredditURL.get + "/new/.json?limit=10")
val json = new JsonParser().parse(body).getAsJsonObject.get("data").getAsJsonObject.get("children").getAsJsonArray val json = new JsonParser().parse(body).getAsJsonObject.get("data").getAsJsonObject.get("children").getAsJsonArray
val msgsb = new StringBuilder val msgsb = new StringBuilder
@ -66,30 +63,32 @@ import reactor.core.publisher.Flux
val data = item.get("data").getAsJsonObject val data = item.get("data").getAsJsonObject
var author = data.get("author").getAsString var author = data.get("author").getAsString
val distinguishedjson = data.get("distinguished") val distinguishedjson = data.get("distinguished")
var distinguished = null val distinguished = if (distinguishedjson.isJsonNull) null else distinguishedjson.getAsString
if (distinguishedjson.isJsonNull) distinguished = null
else distinguished = distinguishedjson.getAsString
val permalink = "https://www.reddit.com" + data.get("permalink").getAsString val permalink = "https://www.reddit.com" + data.get("permalink").getAsString
val date = data.get("created_utc").getAsLong val date = data.get("created_utc").getAsLong
if (date > lastSeenTime.get) lastSeenTime.set(date) if (date > lastSeenTime.get) lastSeenTime.set(date)
else if (date > lastAnnouncementTime.get) { //noinspection ConstantConditions else if (date > lastAnnouncementTime.get) { //noinspection ConstantConditions
do { {
val reddituserclass = ChromaGamerBase.getTypeForFolder("reddit") val reddituserclass = ChromaGamerBase.getTypeForFolder("reddit")
if (reddituserclass == null) break //todo: break is not supported if (reddituserclass != null) {
val user = ChromaGamerBase.getUser(author, reddituserclass) val user = ChromaGamerBase.getUser(author, reddituserclass)
val id = user.getConnectedID(classOf[DiscordPlayer]) val id = user.getConnectedID(classOf[DiscordPlayer])
if (id != null) author = "<@" + id + ">" if (id != null) author = "<@" + id + ">"
} while ( { }
false }
})
if (!author.startsWith("<")) author = "/u/" + author if (!author.startsWith("<")) author = "/u/" + author
(if (distinguished != null && distinguished == "moderator") modmsgsb (if (distinguished != null && distinguished == "moderator") modmsgsb else msgsb)
else msgsb).append("A new post was submitted to the subreddit by ").append(author).append("\n").append(permalink).append("\n") .append("A new post was submitted to the subreddit by ").append(author).append("\n")
.append(permalink).append("\n")
lastanntime = date 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 if (lastAnnouncementTime.get ne lastanntime) lastAnnouncementTime.set(lastanntime) // If sending succeeded
} catch { } catch {
case e: Exception => case e: Exception =>
@ -100,5 +99,6 @@ import reactor.core.publisher.Flux
case ex: InterruptedException => case ex: InterruptedException =>
Thread.currentThread.interrupt() Thread.currentThread.interrupt()
} }
AnnouncementGetterThreadMethod()
} }
} }

View file

@ -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 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")
} }

View file

@ -7,8 +7,9 @@ import com.google.common.collect.HashBiMap
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.entity.Player import org.bukkit.entity.Player
@CommandClass(helpText = Array(Array("Connect command", // @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 { "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<br> * Key: Minecraft name<br>
* Value: Discord ID * Value: Discord ID

View file

@ -3,24 +3,27 @@ package buttondevteam.discordplugin.commands
import buttondevteam.discordplugin.DiscordPlugin import buttondevteam.discordplugin.DiscordPlugin
import buttondevteam.discordplugin.listeners.CommonListeners import buttondevteam.discordplugin.listeners.CommonListeners
import buttondevteam.lib.chat.{Command2, CommandClass} import buttondevteam.lib.chat.{Command2, CommandClass}
import discord4j.common.util.Snowflake
import discord4j.core.`object`.entity.{Member, User} 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 { class DebugCommand extends ICommand2DC {
@Command2.Subcommand @Command2.Subcommand
override def `def`(sender: Command2DCSender): Boolean = { override def `def`(sender: Command2DCSender): Boolean = {
sender.getMessage.getAuthorAsMember.switchIfEmpty(sender.getMessage.getAuthor.map //Support DMs SMono(sender.getMessage.getAuthorAsMember)
((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))) .switchIfEmpty(Option(sender.getMessage.getAuthor.orElse(null)) //Support DMs
.onErrorReturn(false) //Role not found .map((u: User) => SMono(u.asMember(DiscordPlugin.mainServer.getId))).getOrElse(SMono.empty))
.subscribe((success: Any) => { .flatMap((m: Member) => DiscordPlugin.plugin.modRole.get
def foo(success: Any) = { .map(mr => m.getRoleIds.stream.anyMatch((r: Snowflake) => r == mr.getId))
if (success) sender.sendMessage("debug " + (if (CommonListeners.debug) "enabled" .switchIfEmpty(SMono.fromCallable(() => DiscordPlugin.mainServer.getOwnerId.asLong eq m.getId.asLong)))
else "disabled")) .onErrorResume(_ => SMono.just(false)) //Role not found
else sender.sendMessage("you need to be a moderator to use this command.") .subscribe(success => {
} if (success) {
CommonListeners.debug = !CommonListeners.debug;
foo(success) sender.sendMessage("debug " + (if (CommonListeners.debug) "enabled" else "disabled"))
} else
sender.sendMessage("you need to be a moderator to use this command.")
}) })
true true
} }

View file

@ -2,8 +2,8 @@ package buttondevteam.discordplugin.commands
import buttondevteam.lib.chat.{Command2, CommandClass} import buttondevteam.lib.chat.{Command2, CommandClass}
@CommandClass(helpText = Array(Array("Help command", // @CommandClass(helpText = Array("Help command", //
"Shows some info about a command or lists the available commands."))) "Shows some info about a command or lists the available commands."))
class HelpCommand extends ICommand2DC { class HelpCommand extends ICommand2DC {
@Command2.Subcommand @Command2.Subcommand
def `def`(sender: Command2DCSender, @Command2.TextArg @Command2.OptionalArg args: String): Boolean = { def `def`(sender: Command2DCSender, @Command2.TextArg @Command2.OptionalArg args: String): Boolean = {

View file

@ -4,13 +4,12 @@ import buttondevteam.discordplugin.{DiscordPlayer, DiscordPlugin}
import buttondevteam.lib.chat.{Command2, CommandClass} import buttondevteam.lib.chat.{Command2, CommandClass}
import buttondevteam.lib.player.ChromaGamerBase import buttondevteam.lib.player.ChromaGamerBase
import buttondevteam.lib.player.ChromaGamerBase.InfoTarget 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("User information", //
@CommandClass(helpText = Array(Array("User information", //
"Shows some information about users, from Discord, from Minecraft or from Reddit if they have these accounts connected.", "Shows some information about users, from Discord, from Minecraft or from Reddit if they have these accounts connected.",
"If used without args, shows your info."))) "If used without args, shows your info."))
class UserinfoCommand extends ICommand2DC { class UserinfoCommand extends ICommand2DC {
@Command2.Subcommand @Command2.Subcommand
def `def`(sender: Command2DCSender, @Command2.OptionalArg @Command2.TextArg user: String): Boolean = { def `def`(sender: Command2DCSender, @Command2.OptionalArg @Command2.TextArg user: String): Boolean = {
@ -25,16 +24,11 @@ class UserinfoCommand extends ICommand2DC {
else if (user.contains("#")) { else if (user.contains("#")) {
val targettag = user.split("#") val targettag = user.split("#")
val targets = getUsers(message, targettag(0)) val targets = getUsers(message, targettag(0))
if (targets.size == 0) { if (targets.isEmpty) {
channel.createMessage("The user cannot be found (by name): " + user).subscribe channel.createMessage("The user cannot be found (by name): " + user).subscribe
return true return true
} }
for (ptarget <- targets) { targets.collectFirst(_.getDiscriminator.equalsIgnoreCase(targettag(1)))
if (ptarget.getDiscriminator.equalsIgnoreCase(targettag(1))) {
target = ptarget
break //todo: break is not supported
}
}
if (target == null) { if (target == null) {
channel.createMessage("The user cannot be found (by discriminator): " + user + "(Found " + targets.size + " users with the name.)").subscribe channel.createMessage("The user cannot be found (by discriminator): " + user + "(Found " + targets.size + " users with the name.)").subscribe
return true return true
@ -42,7 +36,7 @@ class UserinfoCommand extends ICommand2DC {
} }
else { else {
val targets = getUsers(message, user) val targets = getUsers(message, user)
if (targets.size == 0) { if (targets.isEmpty) {
channel.createMessage("The user cannot be found on Discord: " + user).subscribe channel.createMessage("The user cannot be found on Discord: " + user).subscribe
return true 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 channel.createMessage("Multiple users found with that (nick)name. Please specify the whole tag, like ChromaBot#6338 or use a ping.").subscribe
return true return true
} }
target = targets.get(0) target = targets.head
} }
} }
if (target == null) { if (target == null) {
@ -65,12 +59,11 @@ class UserinfoCommand extends ICommand2DC {
} }
private def getUsers(message: Message, args: String) = { private def getUsers(message: Message, args: String) = {
var targets: util.List[User]
val guild = message.getGuild.block val guild = message.getGuild.block
if (guild == null) { //Private channel 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 else
targets SFlux(guild.getMembers).filter(_.getUsername.equalsIgnoreCase(args)).map(_.asInstanceOf[User]).collectSeq().block()
} }
} }

View file

@ -3,7 +3,7 @@ package buttondevteam.discordplugin.commands
import buttondevteam.discordplugin.DiscordPlugin import buttondevteam.discordplugin.DiscordPlugin
import buttondevteam.lib.chat.{Command2, CommandClass} 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 { object VersionCommand {
def getVersion: Array[String] = { def getVersion: Array[String] = {
val desc = DiscordPlugin.plugin.getDescription 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 { class VersionCommand extends ICommand2DC {
@Command2.Subcommand override def `def`(sender: Command2DCSender): Boolean = { @Command2.Subcommand override def `def`(sender: Command2DCSender): Boolean = {
sender.sendMessage(VersionCommand.getVersion) sender.sendMessage(VersionCommand.getVersion)

View file

@ -9,7 +9,7 @@ import discord4j.core.`object`.entity.{Guild, Role}
import org.apache.commons.lang.exception.ExceptionUtils import org.apache.commons.lang.exception.ExceptionUtils
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.event.{EventHandler, Listener} import org.bukkit.event.{EventHandler, Listener}
import reactor.core.publisher.Mono import reactor.core.scala.publisher.SMono
import java.util import java.util
import java.util.stream.Collectors import java.util.stream.Collectors
@ -20,29 +20,24 @@ import java.util.stream.Collectors
object ExceptionListenerModule { object ExceptionListenerModule {
private def SendException(e: Throwable, sourcemessage: String): Unit = { private def SendException(e: Throwable, sourcemessage: String): Unit = {
if (instance == null) return if (instance == null) return
try getChannel.flatMap((channel: MessageChannel) => { try getChannel.flatMap(channel => {
def foo(channel: MessageChannel) = { val coderRole = channel match {
var coderRole: Mono[Role] = channel match { case ch: GuildChannel => instance.pingRole(SMono(ch.getGuild)).get
case ch: GuildChannel => instance.pingRole(ch.getGuild).get case _ => SMono.empty
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)
})
} }
coderRole.map((role: Role) => if (TBMCCoreAPI.IsTestServer) new StringBuilder
foo(channel) 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 }).subscribe
catch { catch {
case ex: Exception => case ex: Exception =>
@ -52,9 +47,9 @@ object ExceptionListenerModule {
private var instance: ExceptionListenerModule = null private var instance: ExceptionListenerModule = null
def getChannel: Mono[MessageChannel] = { def getChannel: SMono[MessageChannel] = {
if (instance != null) return instance.channel.get 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 = { @EventHandler def onException(e: TBMCExceptionEvent): Unit = {
if (DiscordPlugin.SafeMode || !ComponentManager.isEnabled(getClass)) return 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 if (lastthrown.stream.anyMatch(ex => e.getException.getStackTrace.sameElements(ex.getStackTrace)
else e.getException.getMessage == ex.getMessage)) // e.Exception.Message==ex.Message && (if (e.getException.getMessage == null) ex.getMessage == null else e.getException.getMessage == ex.getMessage))
&& lastsourcemsg.contains(e.getSourceMessage)) { && lastsourcemsg.contains(e.getSourceMessage)) {
return return
} }
ExceptionListenerModule ExceptionListenerModule.SendException(e.getException, e.getSourceMessage)
.SendException(e.getException, e.getSourceMessage)
if (lastthrown.size >= 10) lastthrown.remove(0) if (lastthrown.size >= 10) lastthrown.remove(0)
if (lastsourcemsg.size >= 10) lastsourcemsg.remove(0) if (lastsourcemsg.size >= 10) lastsourcemsg.remove(0)
lastthrown.add(e.getException) 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. * 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 = { override protected def enable(): Unit = {
if (DPUtils.disableIfConfigError(this, channel)) return if (DPUtils.disableIfConfigError(this, channel)) return

View file

@ -5,16 +5,15 @@ import buttondevteam.discordplugin.{DPUtils, DiscordPlugin}
import buttondevteam.lib.TBMCCoreAPI import buttondevteam.lib.TBMCCoreAPI
import buttondevteam.lib.architecture.{Component, ConfigData} import buttondevteam.lib.architecture.{Component, ConfigData}
import com.google.common.collect.Lists import com.google.common.collect.Lists
import discord4j.common.util.Snowflake
import discord4j.core.`object`.entity.channel.{GuildChannel, MessageChannel} import discord4j.core.`object`.entity.channel.{GuildChannel, MessageChannel}
import discord4j.core.`object`.entity.{Guild, Member, Message, Role} import discord4j.core.`object`.entity.{Guild, Message}
import discord4j.core.`object`.presence.{Presence, Status} import discord4j.core.`object`.presence.Status
import discord4j.core.event.domain.PresenceUpdateEvent import discord4j.core.event.domain.PresenceUpdateEvent
import discord4j.core.spec.{EmbedCreateSpec, MessageCreateSpec} import discord4j.core.spec.{EmbedCreateSpec, MessageCreateSpec}
import org.bukkit.Bukkit import org.bukkit.Bukkit
import org.bukkit.event.player.PlayerJoinEvent import org.bukkit.event.player.PlayerJoinEvent
import org.bukkit.event.{EventHandler, Listener} import org.bukkit.event.{EventHandler, Listener}
import reactor.core.publisher.Mono import reactor.core.scala.publisher.{SFlux, SMono}
import java.util import java.util
import java.util.Calendar import java.util.Calendar
@ -50,41 +49,47 @@ object FunModule {
lastlist = 0 lastlist = 0
} }
if (msglowercased == "/list" && Bukkit.getOnlinePlayers.size == lastlistp && { if (msglowercased == "/list" && Bukkit.getOnlinePlayers.size == lastlistp && {
ListC += 1; ListC += 1
ListC - 1 ListC - 1
} > 2) { // Lowered already } > 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 lastlist = 0
lastlistp = Bukkit.getOnlinePlayers.size.toShort lastlistp = Bukkit.getOnlinePlayers.size.toShort
return true //Handled return true //Handled
} }
lastlistp = Bukkit.getOnlinePlayers.size.toShort //Didn't handle 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 var next = 0
if (usableServerReadyStrings.size == 0) fm.createUsableServerReadyStrings() if (usableServerReadyStrings.size == 0) fm.createUsableServerReadyStrings()
next = usableServerReadyStrings.remove(serverReadyRandom.nextInt(usableServerReadyStrings.size)) 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 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 = { def handleFullHouse(event: PresenceUpdateEvent): Unit = {
val fm = ComponentManager.getIfEnabled(classOf[FunModule]) val fm = ComponentManager.getIfEnabled(classOf[FunModule])
if (fm == null) return if (fm == null) return
if (Calendar.getInstance.get(Calendar.DAY_OF_MONTH) % 5 != 0) 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]) 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 .flatMap(channel => fm.fullHouseDevRole(SMono(channel.asInstanceOf[GuildChannel].getGuild)).get
((devrole: Role) => { .filterWhen(devrole => SMono(event.getMember)
def foo(devrole: Role) = { .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) 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"))) 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
foo(devrole)
})).subscribe
} }
} }
@ -109,14 +114,18 @@ class FunModule extends Component[DiscordPlugin] with Listener {
override protected def enable(): Unit = registerListener(this) 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 @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. * 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. * The channel to post the full house to.

View file

@ -12,7 +12,7 @@ import discord4j.core.event.EventDispatcher
import discord4j.core.event.domain.PresenceUpdateEvent import discord4j.core.event.domain.PresenceUpdateEvent
import discord4j.core.event.domain.message.MessageCreateEvent import discord4j.core.event.domain.message.MessageCreateEvent
import discord4j.core.event.domain.role.{RoleCreateEvent, RoleDeleteEvent, RoleUpdateEvent} import discord4j.core.event.domain.role.{RoleCreateEvent, RoleDeleteEvent, RoleUpdateEvent}
import reactor.core.scala.publisher.SMono import reactor.core.scala.publisher.{SFlux, SMono}
object CommonListeners { object CommonListeners {
val timings = new Timings val timings = new Timings
@ -56,25 +56,19 @@ object CommonListeners {
} }
foo(event) 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) => { dispatcher.on(classOf[PresenceUpdateEvent]).subscribe((event: PresenceUpdateEvent) => {
def foo(event: PresenceUpdateEvent) = { if (!DiscordPlugin.SafeMode)
if (DiscordPlugin.SafeMode) return
FunModule.handleFullHouse(event) FunModule.handleFullHouse(event)
}
foo(event)
}) })
dispatcher.on(classOf[RoleCreateEvent]).subscribe(GameRoleModule.handleRoleEvent _) SFlux(dispatcher.on(classOf[RoleCreateEvent])).subscribe(GameRoleModule.handleRoleEvent _)
dispatcher.on(classOf[RoleDeleteEvent]).subscribe(GameRoleModule.handleRoleEvent _) SFlux(dispatcher.on(classOf[RoleDeleteEvent])).subscribe(GameRoleModule.handleRoleEvent _)
dispatcher.on(classOf[RoleUpdateEvent]).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 def debug(debug: String): Unit = if (CommonListeners.debug) { //Debug
DPUtils.getLogger.info(debug) DPUtils.getLogger.info(debug)
} }
def debug(): Unit = debug = !debug
} }

View file

@ -21,7 +21,7 @@ import java.util.{Objects, Optional}
import javax.annotation.Nullable import javax.annotation.Nullable
@SuppressWarnings(Array("SimplifyOptionalCallChains")) //Java 11 @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).", "This command allows you to connect a Minecraft channel to a Discord channel (just like how the global chat is connected to #minecraft-chat).",
"You need to have access to the MC channel and have manage permissions on the Discord channel.", "You need to have access to the MC channel and have manage permissions on the Discord channel.",
"You also need to have your Minecraft account connected. In #bot use /connect <mcname>.", "You also need to have your Minecraft account connected. In #bot use /connect <mcname>.",
@ -30,7 +30,7 @@ import javax.annotation.Nullable
"To remove a connection use @ChromaBot channelcon remove in the channel.", "To remove a connection use @ChromaBot channelcon remove in the channel.",
"Mentioning the bot is needed in this case because the / prefix only works in #bot.", "Mentioning the bot is needed in this case because the / prefix only works in #bot.",
"Invite link: <Unknown>" // "Invite link: <Unknown>" //
))) ))
class ChannelconCommand(private val module: MinecraftChatModule) extends ICommand2DC { class ChannelconCommand(private val module: MinecraftChatModule) extends ICommand2DC {
@Command2.Subcommand def remove(sender: Command2DCSender): Boolean = { @Command2.Subcommand def remove(sender: Command2DCSender): Boolean = {
val message = sender.getMessage val message = sender.getMessage

View file

@ -1,17 +1,17 @@
package buttondevteam.discordplugin.mcchat package buttondevteam.discordplugin.mcchat
import buttondevteam.discordplugin.{DPUtils, DiscordPlayer, DiscordPlugin}
import buttondevteam.discordplugin.commands.{Command2DCSender, ICommand2DC} import buttondevteam.discordplugin.commands.{Command2DCSender, ICommand2DC}
import buttondevteam.discordplugin.{DPUtils, DiscordPlayer, DiscordPlugin}
import buttondevteam.lib.chat.{Command2, CommandClass} import buttondevteam.lib.chat.{Command2, CommandClass}
import buttondevteam.lib.player.ChromaGamerBase import buttondevteam.lib.player.ChromaGamerBase
import discord4j.core.`object`.entity.channel.PrivateChannel import discord4j.core.`object`.entity.channel.PrivateChannel
@CommandClass(helpText = Array(Array( @CommandClass(helpText = Array(
"MC Chat", "MC Chat",
"This command enables or disables the Minecraft chat in private messages.", // "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.", "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." // "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 { class MCChatCommand(private val module: MinecraftChatModule) extends ICommand2DC {
@Command2.Subcommand override def `def`(sender: Command2DCSender): Boolean = { @Command2.Subcommand override def `def`(sender: Command2DCSender): Boolean = {
if (!(module.allowPrivateChat.get)) { if (!(module.allowPrivateChat.get)) {

View file

@ -8,15 +8,14 @@ import discord4j.core.`object`.entity.User
import discord4j.core.`object`.entity.channel.MessageChannel import discord4j.core.`object`.entity.channel.MessageChannel
import lombok.NonNull import lombok.NonNull
import java.util
import java.util.Collections
import javax.annotation.Nullable import javax.annotation.Nullable
import scala.collection.mutable.ListBuffer
object MCChatCustom { object MCChatCustom {
/** /**
* Used for town or nation chats or anything else * 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 = { def addCustomChat(channel: MessageChannel, groupid: String, mcchannel: Channel, user: User, dcp: DiscordConnectedPlayer, toggles: Int, brtoggles: Set[TBMCSystemChatEvent.BroadcastTarget]): Boolean = {
lastmsgCustom synchronized { lastmsgCustom synchronized {
@ -29,34 +28,32 @@ object MCChatCustom {
gid = groupid gid = groupid
} }
val lmd = new MCChatCustom.CustomLMD(channel, user, gid, mcchannel, dcp, toggles, brtoggles) val lmd = new MCChatCustom.CustomLMD(channel, user, gid, mcchannel, dcp, toggles, brtoggles)
lastmsgCustom.add(lmd) lastmsgCustom += lmd
} }
true true
} }
def hasCustomChat(channel: Snowflake): Boolean = 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 = @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 = { def removeCustomChat(channel: Snowflake): Unit = {
lastmsgCustom synchronized MCChatUtils.lastmsgfromd.remove(channel.asLong) lastmsgCustom synchronized {
lastmsgCustom.removeIf((lmd: MCChatCustom.CustomLMD) => { MCChatUtils.lastmsgfromd.remove(channel.asLong)
def foo(lmd: MCChatCustom.CustomLMD): Boolean = { lastmsgCustom.filterInPlace(lmd => {
if (lmd.channel.getId.asLong != channel.asLong) return false if (lmd.channel.getId.asLong != channel.asLong) return true
lmd.mcchannel match { lmd.mcchannel match {
case room: ChatRoom => room.leaveRoom(lmd.dcp) case room: ChatRoom => room.leaveRoom(lmd.dcp)
case _ => case _ =>
} }
true false
} })
}
foo(lmd)
})
} }
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, class CustomLMD private[mcchat](@NonNull channel: MessageChannel, @NonNull user: User, val groupID: String,
@NonNull mcchannel: Channel, val dcp: DiscordConnectedPlayer, var toggles: Int, @NonNull mcchannel: Channel, val dcp: DiscordConnectedPlayer, var toggles: Int,

View file

@ -28,6 +28,7 @@ import java.util.Optional
import java.util.concurrent.{LinkedBlockingQueue, TimeoutException} import java.util.concurrent.{LinkedBlockingQueue, TimeoutException}
import java.util.function.{Consumer, Function, Predicate} import java.util.function.{Consumer, Function, Predicate}
import java.util.stream.Collectors import java.util.stream.Collectors
import scala.jdk.OptionConverters.RichOptional
object MCChatListener { object MCChatListener {
@ -111,21 +112,21 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener {
foo(ecs) foo(ecs)
} }
val nanoTime: Long = System.nanoTime val nanoTime: Long = System.nanoTime
val doit: MCChatListener.InterruptibleConsumer[MCChatUtils.LastMsgData] = (lastmsgdata: MCChatUtils.LastMsgData) => { val doit = (lastmsgdata: MCChatUtils.LastMsgData) => {
def foo(lastmsgdata: MCChatUtils.LastMsgData): Unit = { if (lastmsgdata.message == null
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) { || authorPlayer != lastmsgdata.message.getEmbeds.get(0).getAuthor.toScala.map(_.getName).orNull
lastmsgdata.message = lastmsgdata.channel.createEmbed(embed).block || lastmsgdata.time / 1000000000f < nanoTime / 1000000000f - 120
lastmsgdata.time = nanoTime || !(lastmsgdata.mcchannel.ID == e.getChannel.ID)
lastmsgdata.mcchannel = e.getChannel || lastmsgdata.content.length + e.getMessage.length + 1 > 2048) {
lastmsgdata.content = e.getMessage lastmsgdata.message = lastmsgdata.channel.createEmbed(embed).block
} lastmsgdata.time = nanoTime
else { lastmsgdata.mcchannel = e.getChannel
lastmsgdata.content = lastmsgdata.content + "\n" + e.getMessage // The message object doesn't get updated lastmsgdata.content = e.getMessage
lastmsgdata.message.edit((mes: MessageEditSpec) => mes.setEmbed(embed.andThen((ecs: EmbedCreateSpec) => ecs.setDescription(lastmsgdata.content)))).block }
} 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 // Checks if the given channel is different than where the message was sent from
// Or if it was from MC // 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 (e.getChannel.isGlobal && (e.isFromCommand || isdifferentchannel.test(module.chatChannel.get))) {
if (MCChatUtils.lastmsgdata == null) if (MCChatUtils.lastmsgdata == null)
MCChatUtils.lastmsgdata = new MCChatUtils.LastMsgData(module.chatChannelMono.block, null) MCChatUtils.lastmsgdata = new MCChatUtils.LastMsgData(module.chatChannelMono.block, null)
doit.accept(MCChatUtils.lastmsgdata) doit(MCChatUtils.lastmsgdata)
} }
for (data <- MCChatPrivate.lastmsgPerUser) { for (data <- MCChatPrivate.lastmsgPerUser) {
if ((e.isFromCommand || isdifferentchannel.test(data.channel.getId)) && e.shouldSendTo(MCChatUtils.getSender(data.channel.getId, data.user))) { 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 MCChatCustom.lastmsgCustom synchronized {
val iterator = MCChatCustom.lastmsgCustom.iterator MCChatCustom.lastmsgCustom.filterInPlace(lmd => {
while ( { if ((e.isFromCommand || isdifferentchannel.test(lmd.channel.getId)) //Test if msg is from Discord
iterator.hasNext && 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
val lmd = iterator.next if (e.shouldSendTo(lmd.dcp)) { //Check original user's permissions
if ((e.isFromCommand || isdifferentchannel.test(lmd.channel.getId)) //Test if msg is from Discord doit(lmd)
&& 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 else {
if (e.shouldSendTo(lmd.dcp)) { //Check original user's permissions lmd.channel.createMessage("The user no longer has permission to view the channel, connection removed.").subscribe
doit.accept(lmd) return false //If the user no longer has permission, remove the connection
}
} }
else { true
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
}
}
} }
} catch { } catch {
case ex: InterruptedException => case ex: InterruptedException =>

View file

@ -19,17 +19,20 @@ import org.bukkit.event.Event
import org.bukkit.event.player.{AsyncPlayerPreLoginEvent, PlayerJoinEvent, PlayerLoginEvent, PlayerQuitEvent} import org.bukkit.event.player.{AsyncPlayerPreLoginEvent, PlayerJoinEvent, PlayerLoginEvent, PlayerQuitEvent}
import org.bukkit.plugin.AuthorNagException import org.bukkit.plugin.AuthorNagException
import org.reactivestreams.Publisher import org.reactivestreams.Publisher
import reactor.core.publisher.Mono import reactor.core.scala.publisher.SMono
import java.net.InetAddress import java.net.InetAddress
import java.util import java.util
import java.util._ import java.util._
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
import java.util.function.Supplier
import java.util.logging.Level import java.util.logging.Level
import java.util.stream.{Collectors, Stream} import java.util.stream.Collectors
import javax.annotation.Nullable 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 import scala.jdk.javaapi.CollectionConverters.asScala
object MCChatUtils { object MCChatUtils {
@ -43,13 +46,13 @@ object MCChatUtils {
@Nullable private[mcchat] var lastmsgdata: MCChatUtils.LastMsgData = null @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[mcchat] val lastmsgfromd = new LongObjectHashMap[Message] // Last message sent by a Discord user, used for clearing checkmarks
private var module: MinecraftChatModule = null 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 = { def updatePlayerList(): Unit = {
val mod = getModule val mod = getModule
if (mod == null || !mod.showPlayerListOnDC.get) return if (mod == null || !mod.showPlayerListOnDC.get) return
if (lastmsgdata != null) updatePL(lastmsgdata) 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 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 .filter(_ => C.incrementAndGet > 0) //Always true
.map((p) => DPUtils.sanitizeString(p.getDisplayName)).collect(Collectors.joining(", ")) .map((p) => DPUtils.sanitizeString(p.getDisplayName)).collect(Collectors.joining(", "))
s(0) = C + " player" + (if (C.get != 1) "s" else "") + " online" 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 = { private[mcchat] def checkEssentials(p: Player): Boolean = {
@ -94,12 +98,12 @@ object MCChatUtils {
!ess.getUser(p).isHidden !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) addSender(senders, user.getId.asString, sender)
def addSender[T <: DiscordSenderBase](senders: ConcurrentHashMap[String, ConcurrentHashMap[Snowflake, T]], did: String, sender: T): T = { def addSender[T <: DiscordSenderBase](senders: concurrent.Map[String, ConcurrentHashMap[Snowflake, T]], did: String, sender: T): T = {
var map = senders.get(did) val origMap = senders.get(did)
if (map == null) map = new ConcurrentHashMap[Snowflake, T] val map = if (origMap.isEmpty) new ConcurrentHashMap[Snowflake, T] else origMap.get
map.put(sender.getChannel.getId, sender) map.put(sender.getChannel.getId, sender)
senders.put(did, map) senders.put(did, map)
sender sender
@ -117,15 +121,12 @@ object MCChatUtils {
else null.asInstanceOf else null.asInstanceOf
} }
def forPublicPrivateChat(action: Mono[MessageChannel] => Mono[_]): Mono[_] = { def forPublicPrivateChat(action: SMono[MessageChannel] => SMono[_]): SMono[_] = {
if (notEnabled) return Mono.empty if (notEnabled) return SMono.empty
val list = new util.ArrayList[Mono[_]] val list = MCChatPrivate.lastmsgPerUser.map(data => action(SMono.just(data.channel)))
list.add(action.apply(module.chatChannelMono)) .prepend(action(module.chatChannelMono))
for (data <- MCChatPrivate.lastmsgPerUser) {
list.add(action.apply(Mono.just(data.channel)))
}
// lastmsgCustom.forEach(cc -> action.accept(cc.channel)); - Only send relevant messages to custom chat // 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 toggle The toggle to check
* @param hookmsg Whether the message is also sent from the hook * @param hookmsg Whether the message is also sent from the hook
*/ */
def forCustomAndAllMCChat(action: Mono[MessageChannel] => Mono[_], @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): Mono[_] = { def forCustomAndAllMCChat(action: SMono[MessageChannel] => SMono[_], @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): SMono[_] = {
if (notEnabled) return Mono.empty if (notEnabled) return SMono.empty
val list = new util.ArrayList[Publisher[_]] val list = new ListBuffer[Publisher[_]]
if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) list.add(forPublicPrivateChat(action)) if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) list.append(forPublicPrivateChat(action))
val customLMDFunction = (cc: MCChatCustom.CustomLMD) => action.apply(Mono.just(cc.channel)) val customLMDFunction = (cc: MCChatCustom.CustomLMD) => action(SMono.just(cc.channel))
if (toggle == null) MCChatCustom.lastmsgCustom.stream.map(customLMDFunction).forEach(list.add(_)) if (toggle == null) MCChatCustom.lastmsgCustom.map(customLMDFunction).foreach(list.append(_))
else MCChatCustom.lastmsgCustom.stream.filter((cc) => (cc.toggles & toggle.flag) ne 0).map(customLMDFunction).forEach(list.add(_)) else MCChatCustom.lastmsgCustom.filter((cc) => (cc.toggles & (1 << toggle.id)) ne 0).map(customLMDFunction).foreach(list.append(_))
Mono.whenDelayError(list) 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 sender The sender to check perms of or null to send to all that has it toggled
* @param toggle The toggle to check or null to send to all allowed * @param 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[_] = { def forAllowedCustomMCChat(action: SMono[MessageChannel] => SMono[_], @Nullable sender: CommandSender, @Nullable toggle: ChannelconBroadcast): SMono[_] = {
if (notEnabled) return Mono.empty if (notEnabled) return SMono.empty
val st = MCChatCustom.lastmsgCustom.stream.filter((clmd) => { 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
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)) false //If null then allow
if (toggle != null && ((clmd.toggles & (1 << toggle.id)) eq 0)) return false //If null then allow else if (sender == null) true
if (sender == null) return true else clmd.groupID.equals(clmd.mcchannel.getGroupID(sender))
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)
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
} }
/** /**
@ -174,47 +172,45 @@ object MCChatUtils {
* @param toggle The toggle to check or null to send to all allowed * @param toggle The toggle to check or null to send to all allowed
* @param hookmsg Whether the message is also sent from the hook * @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[_] = { def forAllowedCustomAndAllMCChat(action: SMono[MessageChannel] => SMono[_], @Nullable sender: CommandSender, @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): SMono[_] = {
if (notEnabled) return Mono.empty if (notEnabled) return SMono.empty
val cc = forAllowedCustomMCChat(action, sender, toggle) val cc = forAllowedCustomMCChat(action, sender, toggle)
if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) return Mono.whenDelayError(forPublicPrivateChat(action), cc) if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) return SMono.whenDelayError(Array(forPublicPrivateChat(action), cc))
Mono.whenDelayError(cc) SMono.whenDelayError(Array(cc))
} }
def send(message: String): Mono[MessageChannel] => Mono[_] = (ch: Mono[MessageChannel]) => ch.flatMap((mc: MessageChannel) => { def send(message: String): SMono[MessageChannel] => SMono[_] = _.flatMap((mc: MessageChannel) => {
def foo(mc: MessageChannel) = { resetLastMessage(mc)
resetLastMessage(mc) SMono(mc.createMessage(DPUtils.sanitizeString(message)))
mc.createMessage(DPUtils.sanitizeString(message))
}
foo(mc)
}) })
def forAllowedMCChat(action: Mono[MessageChannel] => Mono[_], event: TBMCSystemChatEvent): Mono[_] = { def forAllowedMCChat(action: SMono[MessageChannel] => SMono[_], event: TBMCSystemChatEvent): SMono[_] = {
if (notEnabled) return Mono.empty if (notEnabled) return SMono.empty
val list = new util.ArrayList[Mono[_]] val list = new ListBuffer[SMono[_]]
if (event.getChannel.isGlobal) list.add(action.apply(module.chatChannelMono)) if (event.getChannel.isGlobal) list.append(action(module.chatChannelMono))
for (data <- MCChatPrivate.lastmsgPerUser) 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?} if (event.shouldSendTo(getSender(data.channel.getId, data.user)))
MCChatCustom.lastmsgCustom.stream.filter((clmd) => { list.append(action(SMono.just(data.channel))) //TODO: Only store ID?
def foo(clmd: CustomLMD): Boolean = { MCChatCustom.lastmsgCustom.filter(clmd =>
clmd.brtoggles.contains(event.getTarget) && event.shouldSendTo(clmd.dcp) clmd.brtoggles.contains(event.getTarget) && event.shouldSendTo(clmd.dcp))
} .map(clmd => action(SMono.just(clmd.channel))).forEach(elem => {
list.append(elem)
foo(clmd) ()
}).map((clmd) => action.apply(Mono.just(clmd.channel))).forEach(list.add(_)) })
Mono.whenDelayError(list) 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. * 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 private[mcchat] def getSender(channel: Snowflake, author: User): DiscordSenderBase = { //noinspection OptionalGetWithoutIsPresent
Stream.of[Supplier[Optional[DiscordSenderBase]]]( // https://stackoverflow.com/a/28833677/2703239 List[() => DiscordSenderBase]( // https://stackoverflow.com/a/28833677/2703239
() => Optional.ofNullable(getSender(OnlineSenders, channel, author)), // Find first non-null () => getSender[DiscordSenderBase](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 () => getSender[DiscordSenderBase](ConnectedSenders, channel, author), // This doesn't support the public chat, but it'll always return null for it
() => Optional.ofNullable(getSender(UnconnectedSenders, channel, author)), // () => getSender[DiscordSenderBase](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 () => 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 = { def resetLastMessage(channel: Channel): Unit = {
if (notEnabled) return if (notEnabled) return
if (channel.getId.asLong == module.chatChannel.get.asLong) { 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 else lastmsgdata.message = null
return return
} // Don't set the whole object to null, the player and channel information should be preserved } // 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] = 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) if (hs == null) Sets.newHashSet(plugin) else if (hs.add(plugin)) hs else hs)
def callEventExcludingSome(event: Event): Unit = { def callEventExcludingSome(event: Event): Unit = {
if (notEnabled) return if (notEnabled) return
val second = staticExcludedPlugins.get(event.getClass) val second = staticExcludedPlugins.get(event.getClass)
val first = module.excludedPlugins.get 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) else util.Arrays.copyOf(first, first.length + second.size)
var i = first.length var i = first.length
if (second != null) { if (second.nonEmpty) {
for (plugin <- second) { for (plugin <- second.get.asScala) {
both({ both(i) = plugin
i += 1; i += 1
i - 1
}) = plugin
} }
} }
callEventExcluding(event, false, both) callEventExcluding(event, false, both: _*)
} }
/** /**
@ -275,21 +269,18 @@ object MCChatUtils {
if (event.isAsynchronous) { if (event.isAsynchronous) {
if (Thread.holdsLock(Bukkit.getPluginManager)) throw new IllegalStateException(event.getEventName + " cannot be triggered asynchronously from inside synchronized code.") 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.") 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 = { private def fireEventExcluding(event: Event, only: Boolean, plugins: String*): Unit = {
val handlers = event.getHandlers // Code taken from SimplePluginManager in Spigot-API val handlers = event.getHandlers // Code taken from SimplePluginManager in Spigot-API
val listeners = handlers.getRegisteredListeners val listeners = handlers.getRegisteredListeners
val server = Bukkit.getServer val server = Bukkit.getServer
for (registration <- listeners) { for (registration <- listeners) { // Modified to exclude plugins
if (!registration.getPlugin.isEnabled || util.Arrays.stream(plugins).anyMatch((p: String) => only ^ p.equalsIgnoreCase(registration.getPlugin.getName))) { if (registration.getPlugin.isEnabled
continue //todo: continue is not supported && !plugins.exists(only ^ _.equalsIgnoreCase(registration.getPlugin.getName))) try registration.callEvent(event)
// Modified to exclude plugins
}
try registration.callEvent(event)
catch { catch {
case ex: AuthorNagException => case ex: AuthorNagException =>
val plugin = registration.getPlugin val plugin = registration.getPlugin
@ -308,12 +299,8 @@ object MCChatUtils {
*/ */
def callLoginEvents(dcp: DiscordConnectedPlayer): Unit = { def callLoginEvents(dcp: DiscordConnectedPlayer): Unit = {
val loginFail = (kickMsg: String) => { val loginFail = (kickMsg: String) => {
def foo(kickMsg: String): Unit = { dcp.sendMessage("Minecraft chat disabled, as the login failed: " + kickMsg)
dcp.sendMessage("Minecraft chat disabled, as the login failed: " + kickMsg) MCChatPrivate.privateMCChat(dcp.getChannel, start = false, dcp.getUser, dcp.getChromaUser)
MCChatPrivate.privateMCChat(dcp.getChannel, start = false, dcp.getUser, dcp.getChromaUser)
}
foo(kickMsg)
} //Probably also happens if the user is banned or so } //Probably also happens if the user is banned or so
val event = new AsyncPlayerPreLoginEvent(dcp.getName, InetAddress.getLoopbackAddress, dcp.getUniqueId) val event = new AsyncPlayerPreLoginEvent(dcp.getName, InetAddress.getLoopbackAddress, dcp.getUniqueId)
callEventExcludingSome(event) callEventExcludingSome(event)

View file

@ -1,22 +1,23 @@
package buttondevteam.discordplugin.mcchat package buttondevteam.discordplugin.mcchat
import buttondevteam.core.component.channel.Channel import buttondevteam.core.component.channel.Channel
import buttondevteam.discordplugin.{ChannelconBroadcast, DPUtils, DiscordConnectedPlayer, DiscordPlugin}
import buttondevteam.discordplugin.playerfaker.ServerWatcher import buttondevteam.discordplugin.playerfaker.ServerWatcher
import buttondevteam.discordplugin.playerfaker.perm.LPInjector import buttondevteam.discordplugin.playerfaker.perm.LPInjector
import buttondevteam.discordplugin.util.DPState 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.architecture.{Component, ConfigData, ReadOnlyConfigData}
import buttondevteam.lib.{TBMCCoreAPI, TBMCSystemChatEvent}
import com.google.common.collect.Lists import com.google.common.collect.Lists
import discord4j.common.util.Snowflake import discord4j.common.util.Snowflake
import discord4j.core.`object`.entity.channel.MessageChannel import discord4j.core.`object`.entity.channel.MessageChannel
import discord4j.rest.util.Color import discord4j.rest.util.Color
import org.bukkit.Bukkit import org.bukkit.Bukkit
import reactor.core.publisher.Mono import reactor.core.publisher.Mono
import reactor.core.scala.publisher.SMono
import java.util import java.util
import java.util.{Objects, UUID}
import java.util.stream.Collectors 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. * 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) 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 * The channel where the plugin can log when it mutes a player on Discord because of a Minecraft mute