From 02e05e360edb114ba2811c4537edc239cb257534 Mon Sep 17 00:00:00 2001 From: NorbiPeti Date: Thu, 29 Jun 2023 02:10:20 +0200 Subject: [PATCH] Update config options and fix other issues and update Scala --- build.sbt | 2 +- .../buttondevteam/discordplugin/DPUtils.scala | 15 +++--- .../discordplugin/DiscordPlugin.scala | 7 +-- .../discordplugin/commands/Command2DC.scala | 5 +- .../commands/Command2DCSender.scala | 2 +- .../discordplugin/fun/FunModule.scala | 5 +- .../mcchat/ChannelconCommand.scala | 2 +- .../discordplugin/mcchat/MCChatCommand.scala | 1 + .../discordplugin/mcchat/MCChatCustom.scala | 7 +-- .../discordplugin/mcchat/MCChatListener.scala | 48 ++++++++++++------- .../discordplugin/mcchat/MCChatPrivate.scala | 6 +-- .../discordplugin/mcchat/MCChatUtils.scala | 39 +++++++-------- .../discordplugin/mcchat/MCListener.scala | 12 ++--- .../mcchat/MinecraftChatModule.scala | 21 ++++---- .../mcchat/sender/DiscordPlayer.scala | 9 ++++ .../mcchat/sender/DiscordSenderBase.scala | 2 +- 16 files changed, 107 insertions(+), 76 deletions(-) diff --git a/build.sbt b/build.sbt index c6f02c5..3f327b6 100644 --- a/build.sbt +++ b/build.sbt @@ -8,7 +8,7 @@ name := "Chroma-Discord" version := "1.1" -scalaVersion := "3.2.2" +scalaVersion := "3.3.0" resolvers += "spigot-repo" at "https://hub.spigotmc.org/nexus/content/repositories/snapshots/" resolvers += "jitpack.io" at "https://jitpack.io" diff --git a/src/main/scala/buttondevteam/discordplugin/DPUtils.scala b/src/main/scala/buttondevteam/discordplugin/DPUtils.scala index db4659b..89d13bc 100644 --- a/src/main/scala/buttondevteam/discordplugin/DPUtils.scala +++ b/src/main/scala/buttondevteam/discordplugin/DPUtils.scala @@ -1,6 +1,7 @@ package buttondevteam.discordplugin import buttondevteam.lib.TBMCCoreAPI +import buttondevteam.lib.architecture.config.IConfigData import buttondevteam.lib.architecture.{Component, ConfigData, IHaveConfig} import discord4j.common.util.Snowflake import discord4j.core.`object`.entity.channel.{Channel, MessageChannel} @@ -68,17 +69,17 @@ object DPUtils { else DiscordPlugin.plugin.getLogger } - def channelData(config: IHaveConfig, key: String): ConfigData[Mono[MessageChannel]] = + def channelData(config: IHaveConfig, key: String): IConfigData[Mono[MessageChannel]] = config.getData(key, id => getMessageChannel(key, Snowflake.of(id.asInstanceOf[Long])), (_: Mono[MessageChannel]) => 0L, 0L, true) //We can afford to search for the channel in the cache once (instead of using mainServer) - def roleData(config: IHaveConfig, key: String, defName: String): ConfigData[Mono[Role]] = + def roleData(config: IHaveConfig, key: String, defName: String): IConfigData[Mono[Role]] = roleData(config, key, defName, Mono.just(DiscordPlugin.mainServer)) /** * Needs to be a [[ConfigData]] for checking if it's set */ - def roleData(config: IHaveConfig, key: String, defName: String, guild: Mono[Guild]): ConfigData[Mono[Role]] = config.getData(key, name => { + def roleData(config: IHaveConfig, key: String, defName: String, guild: Mono[Guild]): IConfigData[Mono[Role]] = config.getData(key, name => { if (!name.isInstanceOf[String] || name.asInstanceOf[String].isEmpty) Mono.empty[Role] else guild.flatMapMany(_.getRoles).filter(_.getName == name).onErrorResume(e => { getLogger.warning("Failed to get role data for " + key + "=" + name + " - " + e.getMessage) @@ -86,7 +87,7 @@ object DPUtils { }).next }, _ => defName, defName, true) - def snowflakeData(config: IHaveConfig, key: String, defID: Long): ConfigData[Snowflake] = + def snowflakeData(config: IHaveConfig, key: String, defID: Long): IConfigData[Snowflake] = config.getData(key, id => Snowflake.of(id.asInstanceOf[Long]), _.asLong, defID, true) /** @@ -106,7 +107,7 @@ object DPUtils { * @param configs The configs to check for null * @return Whether the component got disabled and a warning logged */ - def disableIfConfigError(@Nullable component: Component[DiscordPlugin], configs: ConfigData[_]*): Boolean = { + def disableIfConfigError(@Nullable component: Component[DiscordPlugin], configs: IConfigData[_]*): Boolean = { for (config <- configs) { val v = config.get if (disableIfConfigErrorRes(component, config, v)) return true @@ -122,7 +123,7 @@ object DPUtils { * @param result The result of getting the value * @return Whether the component got disabled and a warning logged */ - def disableIfConfigErrorRes(@Nullable component: Component[DiscordPlugin], config: ConfigData[_], result: Any): Boolean = { + def disableIfConfigErrorRes(@Nullable component: Component[DiscordPlugin], config: IConfigData[_], result: Any): Boolean = { //noinspection ConstantConditions if (result == null || (result.isInstanceOf[Mono[_]] && !result.asInstanceOf[Mono[_]].hasElement.block())) { var path: String = null @@ -185,7 +186,7 @@ object DPUtils { }).filter(ch => ch.isInstanceOf[MessageChannel]).cast(classOf[MessageChannel]) } - def getMessageChannel(config: ConfigData[Snowflake]): Mono[MessageChannel] = + def getMessageChannel(config: IConfigData[Snowflake]): Mono[MessageChannel] = getMessageChannel(config.getPath, config.get) def ignoreError[T](mono: Mono[T]): Mono[T] = mono.onErrorResume((_: Throwable) => Mono.empty) diff --git a/src/main/scala/buttondevteam/discordplugin/DiscordPlugin.scala b/src/main/scala/buttondevteam/discordplugin/DiscordPlugin.scala index 7c1a97a..cecde1e 100644 --- a/src/main/scala/buttondevteam/discordplugin/DiscordPlugin.scala +++ b/src/main/scala/buttondevteam/discordplugin/DiscordPlugin.scala @@ -14,6 +14,7 @@ import buttondevteam.discordplugin.role.GameRoleModule import buttondevteam.discordplugin.util.{DPState, Timings} import buttondevteam.lib.TBMCCoreAPI import buttondevteam.lib.architecture.* +import buttondevteam.lib.architecture.config.IConfigData import buttondevteam.lib.player.ChromaGamerBase import com.google.common.io.Files import discord4j.common.util.Snowflake @@ -77,16 +78,16 @@ import scala.jdk.OptionConverters.* /** * The (bot) channel to use for Discord commands like /role. */ - def commandChannel: ConfigData[Snowflake] = DPUtils.snowflakeData(getIConfig, "commandChannel", 0L) + def commandChannel: IConfigData[Snowflake] = DPUtils.snowflakeData(getIConfig, "commandChannel", 0L) /** * The role that allows using mod-only Discord commands. * If empty (''), then it will only allow for the owner. */ - def modRole: ConfigData[Mono[Role]] = DPUtils.roleData(getIConfig, "modRole", "Moderator") + def modRole: IConfigData[Mono[Role]] = DPUtils.roleData(getIConfig, "modRole", "Moderator") /** * The invite link to show by /discord invite. If empty, it defaults to the first invite if the bot has access. */ - def inviteLink: ConfigData[String] = getIConfig.getData("inviteLink", "") + def inviteLink: IConfigData[String] = getIConfig.getData("inviteLink", "") override def onLoad(): Unit = { //Needed by ServerWatcher val thread = Thread.currentThread diff --git a/src/main/scala/buttondevteam/discordplugin/commands/Command2DC.scala b/src/main/scala/buttondevteam/discordplugin/commands/Command2DC.scala index acc3aeb..03987ef 100644 --- a/src/main/scala/buttondevteam/discordplugin/commands/Command2DC.scala +++ b/src/main/scala/buttondevteam/discordplugin/commands/Command2DC.scala @@ -15,9 +15,10 @@ class Command2DC extends Command2[ICommand2DC, Command2DCSender]('/', false) { } def registerCommand(command: ICommand2DC, appId: Long, guildId: Option[Long] = None): Unit = { - super.registerCommandSuper(command) //Needs to be configurable for the helps + val mainNode = super.registerCommandSuper(command) //Needs to be configurable for the helps + // TODO: Go through all subcommands and register them val greetCmdRequest = ApplicationCommandRequest.builder() - .name(command.getCommandPath) //TODO: Main path + .name(mainNode.getName) .description("Connect your Minecraft account.") //TODO: Description .addOption(ApplicationCommandOptionData.builder() .name("name") diff --git a/src/main/scala/buttondevteam/discordplugin/commands/Command2DCSender.scala b/src/main/scala/buttondevteam/discordplugin/commands/Command2DCSender.scala index b9d9887..1ead519 100644 --- a/src/main/scala/buttondevteam/discordplugin/commands/Command2DCSender.scala +++ b/src/main/scala/buttondevteam/discordplugin/commands/Command2DCSender.scala @@ -15,7 +15,7 @@ class Command2DCSender(val event: ChatInputInteractionEvent) extends Command2Sen override def sendMessage(message: String): Unit = { if (message.isEmpty) return () //Some(message) map DPUtils.sanitizeString map { (msg: String) => Character.toLowerCase(msg.charAt(0)) + msg.substring(1) } foreach event.reply - don't even need this - event.reply(message); + event.reply(message) } override def sendMessage(message: Array[String]): Unit = sendMessage(String.join("\n", message: _*)) diff --git a/src/main/scala/buttondevteam/discordplugin/fun/FunModule.scala b/src/main/scala/buttondevteam/discordplugin/fun/FunModule.scala index 54177e7..36f642e 100644 --- a/src/main/scala/buttondevteam/discordplugin/fun/FunModule.scala +++ b/src/main/scala/buttondevteam/discordplugin/fun/FunModule.scala @@ -3,6 +3,7 @@ package buttondevteam.discordplugin.fun import buttondevteam.core.ComponentManager import buttondevteam.discordplugin.{DPUtils, DiscordPlugin} import buttondevteam.lib.TBMCCoreAPI +import buttondevteam.lib.architecture.config.IConfigData import buttondevteam.lib.architecture.{Component, ConfigData} import com.google.common.collect.Lists import discord4j.core.`object`.entity.channel.{GuildChannel, MessageChannel} @@ -97,7 +98,7 @@ class FunModule extends Component[DiscordPlugin] with Listener { /** * Questions that the bot will choose a random answer to give to. */ - final private def serverReady: ConfigData[Array[String]] = + final private def serverReady: IConfigData[Array[String]] = getConfig.getData("serverReady", Array[String]( "when will the server be open", "when will the server be ready", "when will the server be done", "when will the server be complete", @@ -106,7 +107,7 @@ class FunModule extends Component[DiscordPlugin] with Listener { /** * Answers for a recognized question. Selected randomly. */ - final private def serverReadyAnswers: ConfigData[util.ArrayList[String]] = + final private def serverReadyAnswers: IConfigData[util.ArrayList[String]] = getConfig.getData("serverReadyAnswers", Lists.newArrayList(FunModule.serverReadyStrings: _*)) private def createUsableServerReadyStrings(): Unit = diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/ChannelconCommand.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/ChannelconCommand.scala index e49d962..a458253 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/ChannelconCommand.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/ChannelconCommand.scala @@ -123,7 +123,7 @@ class ChannelconCommand(private val module: MinecraftChatModule) extends IComman } val dcp: DiscordConnectedPlayer = DiscordConnectedPlayer.create(message.getAuthor.get, channel, chp.getUniqueId, Bukkit.getOfflinePlayer(chp.getUniqueId).getName, module) //Using a fake player with no login/logout, should be fine for this event - val groupid: String = chan.get.getGroupID(dcp) + val groupid: String = chan.get.getGroupID(chp) if (groupid == null && !((chan.get.isInstanceOf[ChatRoom]))) { //ChatRooms don't allow it unless the user joins, which happens later DPUtils.reply(message, channel, "sorry, you cannot use that Minecraft channel.").subscribe() return true diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatCommand.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatCommand.scala index 60fab51..b745a25 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatCommand.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatCommand.scala @@ -15,6 +15,7 @@ import discord4j.core.`object`.entity.channel.PrivateChannel )) class MCChatCommand(private val module: MinecraftChatModule) extends ICommand2DC { @Command2.Subcommand override def `def`(sender: Command2DCSender): Boolean = { + // TODO: If the user is logged in, don't let the object be removed from the cache (test first) if (!module.allowPrivateChat.get) { sender.sendMessage("Using the private chat is not allowed on this Minecraft server.") return true diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatCustom.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatCustom.scala index 103babd..2ae259b 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatCustom.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatCustom.scala @@ -21,8 +21,8 @@ object MCChatCustom { var gid: String = null mcchannel match { case room: ChatRoom => - room.joinRoom(dcp) - gid = if (groupid == null) mcchannel.getGroupID(dcp) else groupid + room.joinRoom(dcp.getChromaUser) + gid = if (groupid == null) mcchannel.getGroupID(dcp.getChromaUser) else groupid case _ => gid = groupid } @@ -46,7 +46,7 @@ object MCChatCustom { if (lmd.channel.getId.asLong != channel.asLong) true else { lmd.mcchannel match { - case room: ChatRoom => room.leaveRoom(lmd.dcp) + case room: ChatRoom => room.leaveRoom(lmd.dcp.getChromaUser) case _ => } false @@ -58,6 +58,7 @@ object MCChatCustom { def getCustomChats: List[CustomLMD] = lastmsgCustom.toList + // TODO: Store Chroma user only class CustomLMD private[mcchat](channel: MessageChannel, user: User, val groupID: String, mcchannel: Channel, val dcp: DiscordConnectedPlayer, var toggles: Int, var brtoggles: Set[TBMCSystemChatEvent.BroadcastTarget]) extends MCChatUtils.LastMsgData(channel, user, mcchannel) { diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatListener.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatListener.scala index 326e8af..9571696 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatListener.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatListener.scala @@ -14,7 +14,9 @@ import discord4j.core.`object`.entity.{Member, Message, User} import discord4j.core.event.domain.message.MessageCreateEvent import discord4j.core.spec.EmbedCreateSpec import discord4j.core.spec.legacy.{LegacyEmbedCreateSpec, LegacyMessageEditSpec} +import discord4j.rest.http.client.ClientException import discord4j.rest.util.Color +import io.netty.handler.codec.http.HttpResponseStatus import org.bukkit.Bukkit import org.bukkit.entity.Player import org.bukkit.event.{EventHandler, Listener} @@ -88,22 +90,22 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { time = se.getValue val authorPlayer: String = "[" + DPUtils.sanitizeStringNoEscape(e.getChannel.displayName.get) + "] " + // (if ("Minecraft" == e.getOrigin) "" else "[" + e.getOrigin.charAt(0) + "]") + - DPUtils.sanitizeStringNoEscape(ChromaUtils.getDisplayName(e.getSender)) + DPUtils.sanitizeStringNoEscape(e.getUser.getName) val color: chat.Color = e.getChannel.color.get val embed = () => { val ecs = EmbedCreateSpec.builder() ecs.description(e.getMessage).color(Color.of(color.getRed, color.getGreen, color.getBlue)) val url: String = module.profileURL.get - e.getSender match { - case player: Player => - DPUtils.embedWithHead(ecs, authorPlayer, e.getSender.getName, + e.getUser match { + case player: TBMCPlayer => + DPUtils.embedWithHead(ecs, authorPlayer, e.getUser.getName, if (url.nonEmpty) url + "?type=minecraft&id=" + player.getUniqueId else null) - case dsender: DiscordSenderBase => + case dsender: DiscordPlayer => ecs.author(authorPlayer, - if (url.nonEmpty) url + "?type=discord&id=" + dsender.getUser.getId.asString else null, - dsender.getUser.getAvatarUrl) + if (url.nonEmpty) url + "?type=discord&id=" + dsender.getDiscordID else null, + dsender.getDiscordUser.getAvatarUrl) case _ => - DPUtils.embedWithHead(ecs, authorPlayer, e.getSender.getName, null) + DPUtils.embedWithHead(ecs, authorPlayer, e.getUser.getName, null) } ecs.timestamp(time) } @@ -120,13 +122,25 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { lastmsgdata.content = e.getMessage } else { + //TODO: The editing shouldn't be blocking, this whole method should be reactive lastmsgdata.content = lastmsgdata.content + "\n" + e.getMessage // The message object doesn't get updated - lastmsgdata.message.edit().withEmbeds(embed().description(lastmsgdata.content).build()).block + try { + lastmsgdata.message.edit().withEmbeds(embed().description(lastmsgdata.content).build()).block + } catch { + case e: ClientException => { + // The message was deleted + if (e.getStatus ne HttpResponseStatus.NOT_FOUND) { + throw e + } + } + } } } // Checks if the given channel is different than where the message was sent from // Or if it was from MC - val isdifferentchannel: Predicate[Snowflake] = (id: Snowflake) => !((e.getSender.isInstanceOf[DiscordSenderBase])) || (e.getSender.asInstanceOf[DiscordSenderBase]).getChannel.getId.asLong != id.asLong + val isdifferentchannel: Predicate[Snowflake] = { + id => !e.getUser.isInstanceOf[DiscordPlayer] || e.getUser.asInstanceOf[DiscordSender].getChannel.getId.asLong != id.asLong + } if (e.getChannel.isGlobal && (e.isFromCommand || isdifferentchannel.test(module.chatChannel.get))) { if (MCChatUtils.lastmsgdata == null) MCChatUtils.lastmsgdata = new MCChatUtils.LastMsgData(module.chatChannelMono.block(), null) @@ -143,7 +157,7 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { if ((e.isFromCommand || isdifferentchannel.test(lmd.channel.getId)) //Test if msg is from Discord && e.getChannel.getIdentifier == lmd.mcchannel.getIdentifier //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 + if (e.shouldSendTo(lmd.dcp.getChromaUser)) { //Check original user's permissions doit(lmd) true } @@ -370,8 +384,8 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { } if (event.getMessage.getType eq Message.Type.CHANNEL_PINNED_MESSAGE) { - val mcchannel = if (clmd != null) clmd.mcchannel else dsender.getChromaUser.channel.get - val rtr = mcchannel getRTR (if (clmd != null) clmd.dcp else dsender) + val mcchannel = if (clmd != null) clmd.mcchannel else dsender.getChromaUser.getChannel.get + val rtr = mcchannel getRTR (if (clmd != null) clmd.dcp.getChromaUser else user) TBMCChatAPI.SendSystemMessage(mcchannel, rtr, (dsender match { case player: Player => player.getDisplayName case _ => dsender.getName @@ -379,9 +393,9 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { false } else { - val cmb = ChatMessage.builder(dsender, user, dmessage + getAttachmentText).fromCommand(false) + val cmb = ChatMessage.builder(user, dmessage + getAttachmentText).fromCommand(false) if (clmd != null) - TBMCChatAPI.sendChatMessage(cmb.permCheck(clmd.dcp).build, clmd.mcchannel) + TBMCChatAPI.sendChatMessage(cmb.permCheck(clmd.dcp.getChromaUser).build, clmd.mcchannel) else TBMCChatAPI.sendChatMessage(cmb.build) true @@ -422,8 +436,8 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener { if (dsender.isInstanceOf[DiscordSender] && runCustomCommand(dsender, cmdlowercased)) { return () } - val channel = if (clmd == null) user.channel.get else clmd.mcchannel - val ev = new TBMCCommandPreprocessEvent(dsender, channel, dmessage, if (clmd == null) dsender else clmd.dcp) + val channel = if (clmd == null) user.getChannel.get else clmd.mcchannel + val ev = new TBMCCommandPreprocessEvent(user, channel, dmessage, if (clmd == null) user else clmd.dcp.getChromaUser) Bukkit.getScheduler.runTask(DiscordPlugin.plugin, () => { //Commands need to be run sync Bukkit.getPluginManager.callEvent(ev) if (!ev.isCancelled) diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatPrivate.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatPrivate.scala index 38ab3ff..1cc2e02 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatPrivate.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatPrivate.scala @@ -27,14 +27,14 @@ object MCChatPrivate { val mcm = ComponentManager.getIfEnabled(classOf[MinecraftChatModule]) if (start) { val sender = DiscordConnectedPlayer.create(user, channel, mcp.getUniqueId, op.getName, mcm) - MCChatUtils.addSender(MCChatUtils.ConnectedSenders, user, sender) + MCChatUtils.addSenderTo(MCChatUtils.ConnectedSenders, user, sender) MCChatUtils.LoggedInPlayers.put(mcp.getUniqueId, sender) if (p == null) { // Player is offline - If the player is online, that takes precedence MCChatUtils.callLoginEvents(sender) } } else { - val sender = MCChatUtils.removeSender(MCChatUtils.ConnectedSenders, channel.getId, user) + val sender = MCChatUtils.removeSenderFrom(MCChatUtils.ConnectedSenders, channel.getId, user) assert(sender != null) Bukkit.getScheduler.runTask(DiscordPlugin.plugin, () => { def foo(): Unit = { @@ -69,7 +69,7 @@ object MCChatPrivate { MCChatUtils.ConnectedSenders synchronized { for ((_, userMap) <- MCChatUtils.ConnectedSenders) { for (valueEntry <- asScala(userMap.entrySet)) { - if (MCChatUtils.getSender(MCChatUtils.OnlineSenders, valueEntry.getKey, valueEntry.getValue.getUser) == null) { //If the player is online then the fake player was already logged out + if (MCChatUtils.getSenderFrom(MCChatUtils.OnlineSenders, valueEntry.getKey, valueEntry.getValue.getUser) == null) { //If the player is online then the fake player was already logged out MCChatUtils.callLogoutEvent(valueEntry.getValue, !Bukkit.isPrimaryThread) } } diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatUtils.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatUtils.scala index 9ac98b1..db206cc 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatUtils.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/MCChatUtils.scala @@ -7,6 +7,7 @@ import buttondevteam.discordplugin.DPUtils.SpecExtensions import buttondevteam.discordplugin.broadcaster.GeneralEventBroadcasterModule import buttondevteam.discordplugin.mcchat.MCChatCustom.CustomLMD import buttondevteam.discordplugin.mcchat.sender.{DiscordConnectedPlayer, DiscordPlayerSender, DiscordSender, DiscordSenderBase} +import buttondevteam.lib.player.{ChromaGamerBase, TBMCPlayerBase} import buttondevteam.lib.{TBMCCoreAPI, TBMCSystemChatEvent} import com.google.common.collect.Sets import discord4j.common.util.Snowflake @@ -35,7 +36,7 @@ import scala.collection.convert.ImplicitConversions.`map AsJavaMap` import scala.collection.mutable.ListBuffer import scala.jdk.CollectionConverters.{CollectionHasAsScala, SeqHasAsJava} import scala.jdk.javaapi.CollectionConverters.asScala -import scala.jdk.javaapi.OptionConverters._ +import scala.jdk.javaapi.OptionConverters.* object MCChatUtils { /** @@ -87,7 +88,7 @@ object MCChatUtils { gid == buttondevteam.core.component.channel.Channel.GROUP_EVERYONE //If null, allow if public (custom chats will have their channel stored anyway) } else { - gid == lmd.mcchannel.getGroupID(p) + gid == lmd.mcchannel.getGroupID(ChromaGamerBase.getFromSender(p)) } ).filter(MCChatUtils.checkEssentials) //If they can see it .filter(_ => C.incrementAndGet > 0) //Always true @@ -102,10 +103,10 @@ object MCChatUtils { !ess.getUser(p).isHidden } - def addSender[T <: DiscordSenderBase](senders: concurrent.Map[String, ConcurrentHashMap[Snowflake, T]], user: User, sender: T): T = - addSender(senders, user.getId.asString, sender) + def addSenderTo[T <: DiscordSenderBase](senders: concurrent.Map[String, ConcurrentHashMap[Snowflake, T]], user: User, sender: T): T = + addSenderTo(senders, user.getId.asString, sender) - def addSender[T <: DiscordSenderBase](senders: concurrent.Map[String, ConcurrentHashMap[Snowflake, T]], did: String, sender: T): T = { + def addSenderTo[T <: DiscordSenderBase](senders: concurrent.Map[String, ConcurrentHashMap[Snowflake, T]], did: String, sender: T): T = { val mapOpt = senders.get(did) val map = if (mapOpt.isEmpty) new ConcurrentHashMap[Snowflake, T] else mapOpt.get map.put(sender.getChannel.getId, sender) @@ -113,13 +114,13 @@ object MCChatUtils { sender } - def getSender[T <: DiscordSenderBase](senders: concurrent.Map[String, ConcurrentHashMap[Snowflake, T]], channel: Snowflake, user: User): T = { + def getSenderFrom[T <: DiscordSenderBase](senders: concurrent.Map[String, ConcurrentHashMap[Snowflake, T]], channel: Snowflake, user: User): T = { val mapOpt = senders.get(user.getId.asString) if (mapOpt.nonEmpty) mapOpt.get.get(channel) else null.asInstanceOf } - def removeSender[T <: DiscordSenderBase](senders: concurrent.Map[String, ConcurrentHashMap[Snowflake, T]], channel: Snowflake, user: User): T = { + def removeSenderFrom[T <: DiscordSenderBase](senders: concurrent.Map[String, ConcurrentHashMap[Snowflake, T]], channel: Snowflake, user: User): T = { val mapOpt = senders.get(user.getId.asString) if (mapOpt.nonEmpty) mapOpt.get.remove(channel) else null.asInstanceOf @@ -153,15 +154,15 @@ object MCChatUtils { * Do the {@code action} for each custom chat the {@code sender} have access to and has that broadcast type enabled. * * @param action The action to do - * @param sender The sender to check perms of or null to send to all that has it toggled + * @param permUser The user 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[_] = { + def forAllowedCustomMCChat(action: Mono[MessageChannel] => Mono[_], @Nullable permUser: ChromaGamerBase, @Nullable toggle: ChannelconBroadcast): Mono[_] = { if (notEnabled) return Mono.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)) == 0)) false //If null then allow - else if (sender == null) true - else clmd.groupID.equals(clmd.mcchannel.getGroupID(sender)) + else if (permUser == null) true + else clmd.groupID.equals(clmd.mcchannel.getGroupID(permUser)) }).map(cc => action.apply(Mono.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 Mono.whenDelayError(st.asJava) @@ -171,13 +172,13 @@ object MCChatUtils { * Do the {@code action} for each custom chat the {@code sender} have access to and has that broadcast type enabled. * * @param action The action to do - * @param sender The sender to check perms of or null to send to all that has it toggled + * @param permUser The user 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 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: Mono[MessageChannel] => Mono[_], @Nullable permUser: ChromaGamerBase, @Nullable toggle: ChannelconBroadcast, hookmsg: Boolean): Mono[_] = { if (notEnabled) return Mono.empty - val cc = forAllowedCustomMCChat(action, sender, toggle) + val cc = forAllowedCustomMCChat(action, permUser, toggle) if (!GeneralEventBroadcasterModule.isHooked || !hookmsg) return Mono.whenDelayError(forPublicPrivateChat(action), cc) Mono.whenDelayError(cc) } @@ -195,7 +196,7 @@ object MCChatUtils { if (event.shouldSendTo(getSender(data.channel.getId, data.user))) list.append(action(Mono.just(data.channel))) //TODO: Only store ID? MCChatCustom.lastmsgCustom.filter(clmd => - clmd.brtoggles.contains(event.getTarget) && event.shouldSendTo(clmd.dcp)) + clmd.brtoggles.contains(event.getTarget) && event.shouldSendTo(clmd.dcp.getChromaUser)) .map(clmd => action(Mono.just(clmd.channel))).foreach(elem => { list.append(elem) () @@ -207,10 +208,10 @@ object MCChatUtils { * 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): DiscordSenderBase = { //noinspection OptionalGetWithoutIsPresent - Option(getSender(OnlineSenders, channel, author)) // Find first non-null - .orElse(Option(getSender(ConnectedSenders, channel, author))) // This doesn't support the public chat, but it'll always return null for it - .orElse(Option(getSender(UnconnectedSenders, channel, author))) // - .orElse(Option(addSender(UnconnectedSenders, author, + Option(getSenderFrom(OnlineSenders, channel, author)) // Find first non-null + .orElse(Option(getSenderFrom(ConnectedSenders, channel, author))) // This doesn't support the public chat, but it'll always return null for it + .orElse(Option(getSenderFrom(UnconnectedSenders, channel, author))) // + .orElse(Option(addSenderTo(UnconnectedSenders, author, new DiscordSender(author, DiscordPlugin.dc.getChannelById(channel).block().asInstanceOf[MessageChannel])))) .get } diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/MCListener.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/MCListener.scala index bef2bb8..54d6cdf 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/MCListener.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/MCListener.scala @@ -3,7 +3,7 @@ package buttondevteam.discordplugin.mcchat import buttondevteam.discordplugin.* import buttondevteam.discordplugin.mcchat.sender.{DiscordConnectedPlayer, DiscordPlayer, DiscordPlayerSender} import buttondevteam.lib.TBMCSystemChatEvent -import buttondevteam.lib.player.{TBMCPlayer, TBMCPlayerBase, TBMCYEEHAWEvent} +import buttondevteam.lib.player.{ChromaGamerBase, TBMCPlayer, TBMCPlayerBase, TBMCYEEHAWEvent} import discord4j.common.util.Snowflake import discord4j.core.`object`.entity.Role import discord4j.core.`object`.entity.channel.MessageChannel @@ -36,8 +36,8 @@ class MCListener(val module: MinecraftChatModule) extends Listener { if (dp != null) DiscordPlugin.dc.getUserById(Snowflake.of(dp.getDiscordID)).flatMap(user => user.getPrivateChannel.flatMap(chan => module.chatChannelMono.flatMap(cc => { - MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID, DiscordPlayerSender.create(user, chan, p, module)) - MCChatUtils.addSender(MCChatUtils.OnlineSenders, dp.getDiscordID, DiscordPlayerSender.create(user, cc, p, module)) //Stored per-channel + MCChatUtils.addSenderTo(MCChatUtils.OnlineSenders, dp.getDiscordID, DiscordPlayerSender.create(user, chan, p, module)) + MCChatUtils.addSenderTo(MCChatUtils.OnlineSenders, dp.getDiscordID, DiscordPlayerSender.create(user, cc, p, module)) //Stored per-channel Mono.empty }))).subscribe() val message = e.getJoinMessage @@ -51,7 +51,7 @@ class MCListener(val module: MinecraftChatModule) extends Listener { private def sendJoinLeaveMessage(message: String, player: Player): Unit = if (message != null && message.trim.nonEmpty) - MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(message), player, ChannelconBroadcast.JOINLEAVE, hookmsg = true).subscribe() + MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(message), ChromaGamerBase.getFromSender(player), ChannelconBroadcast.JOINLEAVE, hookmsg = true).subscribe() @EventHandler(priority = EventPriority.MONITOR) def onPlayerLeave(e: PlayerQuitEvent): Unit = { if (e.getPlayer.isInstanceOf[DiscordConnectedPlayer]) return () // Only care about real users @@ -69,14 +69,14 @@ class MCListener(val module: MinecraftChatModule) extends Listener { } @EventHandler(priority = EventPriority.LOW) def onPlayerDeath(e: PlayerDeathEvent): Unit = - MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(e.getDeathMessage), e.getEntity, ChannelconBroadcast.DEATH, hookmsg = true).subscribe() + MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(e.getDeathMessage), ChromaGamerBase.getFromSender(e.getEntity), ChannelconBroadcast.DEATH, hookmsg = true).subscribe() @EventHandler def onPlayerAFK(e: AfkStatusChangeEvent): Unit = { val base = e.getAffected.getBase if (e.isCancelled || !base.isOnline) return () val msg = base.getDisplayName + " is " + (if (e.getValue) "now" else "no longer") + " AFK." - MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(msg), base, ChannelconBroadcast.AFK, hookmsg = false).subscribe() + MCChatUtils.forAllowedCustomAndAllMCChat(MCChatUtils.send(msg), ChromaGamerBase.getFromSender(base), ChannelconBroadcast.AFK, hookmsg = false).subscribe() } @EventHandler def onPlayerMute(e: MuteStatusChangeEvent): Unit = { diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/MinecraftChatModule.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/MinecraftChatModule.scala index 9dfef39..142d49d 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/MinecraftChatModule.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/MinecraftChatModule.scala @@ -4,6 +4,7 @@ import buttondevteam.core.component.channel.Channel import buttondevteam.discordplugin.mcchat.sender.DiscordConnectedPlayer import buttondevteam.discordplugin.util.DPState import buttondevteam.discordplugin.{ChannelconBroadcast, DPUtils, DiscordPlugin} +import buttondevteam.lib.architecture.config.IConfigData import buttondevteam.lib.architecture.{Component, ConfigData} import buttondevteam.lib.{TBMCCoreAPI, TBMCSystemChatEvent} import com.google.common.collect.Lists @@ -37,56 +38,56 @@ class MinecraftChatModule extends Component[DiscordPlugin] { /** * A list of commands that can be used in public chats - Warning: Some plugins will treat players as OPs, always test before allowing a command! */ - def whitelistedCommands: ConfigData[util.ArrayList[String]] = getConfig.getData("whitelistedCommands", + def whitelistedCommands: IConfigData[util.ArrayList[String]] = getConfig.getData("whitelistedCommands", Lists.newArrayList("list", "u", "shrug", "tableflip", "unflip", "mwiki", "yeehaw", "lenny", "rp", "plugins")) /** * The channel to use as the public Minecraft chat - everything public gets broadcasted here */ - def chatChannel: ConfigData[Snowflake] = DPUtils.snowflakeData(getConfig, "chatChannel", 0L) + def chatChannel: IConfigData[Snowflake] = DPUtils.snowflakeData(getConfig, "chatChannel", 0L) def chatChannelMono: Mono[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 */ - def modlogChannel: ConfigData[Mono[MessageChannel]] = DPUtils.channelData(getConfig, "modlogChannel") + def modlogChannel: IConfigData[Mono[MessageChannel]] = DPUtils.channelData(getConfig, "modlogChannel") /** * The plugins to exclude from fake player events used for the 'mcchat' command - some plugins may crash, add them here */ - def excludedPlugins: ConfigData[Array[String]] = getConfig.getData("excludedPlugins", Array[String]("ProtocolLib", "LibsDisguises", "JourneyMapServer")) + def excludedPlugins: IConfigData[Array[String]] = getConfig.getData("excludedPlugins", Array[String]("ProtocolLib", "LibsDisguises", "JourneyMapServer")) /** * If this setting is on then players logged in through the 'mcchat' command will be able to teleport using plugin commands. * They can then use commands like /tpahere to teleport others to that place.
* If this is off, then teleporting will have no effect. */ - def allowFakePlayerTeleports: ConfigData[Boolean] = getConfig.getData("allowFakePlayerTeleports", false) + def allowFakePlayerTeleports: IConfigData[Boolean] = getConfig.getData("allowFakePlayerTeleports", false) /** * If this is on, each chat channel will have a player list in their description. * It only gets added if there's no description yet or there are (at least) two lines of "----" following each other. * Note that it will replace everything above the first and below the last "----" but it will only detect exactly four dashes. * So if you want to use dashes for something else in the description, make sure it's either less or more dashes in one line. */ - def showPlayerListOnDC: ConfigData[Boolean] = getConfig.getData("showPlayerListOnDC", true) + def showPlayerListOnDC: IConfigData[Boolean] = getConfig.getData("showPlayerListOnDC", true) /** * This setting controls whether custom chat connections can be created (existing connections will always work). * Custom chat connections can be created using the channelcon command and they allow players to display town chat in a Discord channel for example. * See the channelcon command for more details. */ - def allowCustomChat: ConfigData[Boolean] = getConfig.getData("allowCustomChat", true) + def allowCustomChat: IConfigData[Boolean] = getConfig.getData("allowCustomChat", true) /** * This setting allows you to control if players can DM the bot to log on the server from Discord. * This allows them to both chat and perform any command they can in-game. */ - def allowPrivateChat: ConfigData[Boolean] = getConfig.getData("allowPrivateChat", true) + def allowPrivateChat: IConfigData[Boolean] = getConfig.getData("allowPrivateChat", true) /** * If set, message authors appearing on Discord will link to this URL. A 'type' and 'id' parameter will be added with the user's platform (Discord, Minecraft, ...) and ID. */ - def profileURL: ConfigData[String] = getConfig.getData("profileURL", "") + def profileURL: IConfigData[String] = getConfig.getData("profileURL", "") /** * Enables support for running vanilla commands through Discord, if you ever need it. */ - def enableVanillaCommands: ConfigData[Boolean] = getConfig.getData("enableVanillaCommands", true) + def enableVanillaCommands: IConfigData[Boolean] = getConfig.getData("enableVanillaCommands", true) /** * Whether players logged on from Discord (mcchat command) should be recognised by other plugins. Some plugins might break if it's turned off. * But it's really hacky. diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordPlayer.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordPlayer.scala index b436169..1596e56 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordPlayer.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordPlayer.scala @@ -1,10 +1,14 @@ package buttondevteam.discordplugin.mcchat.sender +import buttondevteam.discordplugin.DiscordPlugin import buttondevteam.discordplugin.mcchat.MCChatPrivate import buttondevteam.lib.player.{ChromaGamerBase, UserClass} +import discord4j.common.util.Snowflake +import discord4j.core.`object`.entity.User @UserClass(foldername = "discord") class DiscordPlayer() extends ChromaGamerBase { private var did: String = null + private var discordUser: User = null // private @Getter @Setter boolean minecraftChatEnabled; def getDiscordID: String = { @@ -12,6 +16,11 @@ import buttondevteam.lib.player.{ChromaGamerBase, UserClass} did } + def getDiscordUser: User = { + if (discordUser == null) discordUser = DiscordPlugin.dc.getUserById(Snowflake.of(getDiscordID)).block() // TODO: Don't do it blocking + discordUser + } + /** * Returns true if player has the private Minecraft chat enabled. For setting the value, see * [[MCChatPrivate.privateMCChat]] diff --git a/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordSenderBase.scala b/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordSenderBase.scala index 1c371c3..ff49c7b 100644 --- a/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordSenderBase.scala +++ b/src/main/scala/buttondevteam/discordplugin/mcchat/sender/DiscordSenderBase.scala @@ -17,7 +17,7 @@ import java.util.UUID * @param user May be null. * @param channel May not be null. */ -abstract class DiscordSenderBase protected(var user: User, var channel: MessageChannel) extends CommandSender { +abstract class DiscordSenderBase protected(var user: User, var channel: MessageChannel) extends CommandSender { // TODO: Move most of this to DiscordUser private var msgtosend = "" private var sendtask: BukkitTask = null