Remove custom command handling and start slash commands

They don't work yet, but show up on Discord so that's something
This commit is contained in:
Norbi Peti 2022-01-05 01:31:24 +01:00
parent cafd8096fa
commit 2a985509fb
No known key found for this signature in database
GPG key ID: DBA4C4549A927E56
6 changed files with 49 additions and 149 deletions

View file

@ -1,5 +1,6 @@
package buttondevteam.discordplugin package buttondevteam.discordplugin
import buttondevteam.discordplugin.DiscordPlugin.dc
import buttondevteam.discordplugin.announcer.AnnouncerModule import buttondevteam.discordplugin.announcer.AnnouncerModule
import buttondevteam.discordplugin.broadcaster.GeneralEventBroadcasterModule import buttondevteam.discordplugin.broadcaster.GeneralEventBroadcasterModule
import buttondevteam.discordplugin.commands.* import buttondevteam.discordplugin.commands.*
@ -84,7 +85,7 @@ import java.util.Optional
var commandChannel: ReadOnlyConfigData[Snowflake] = DPUtils.snowflakeData(getIConfig, "commandChannel", 0L) var commandChannel: ReadOnlyConfigData[Snowflake] = DPUtils.snowflakeData(getIConfig, "commandChannel", 0L)
/** /**
* The role that allows using mod-only Discord commands. * The role that allows using mod-only Discord commands.
* If empty (''), then it will only allow for the owner. * If empty (''), then it will only allow for the owner.
*/ */
var modRole: ReadOnlyConfigData[SMono[Role]] = null var modRole: ReadOnlyConfigData[SMono[Role]] = null
/** /**
@ -204,19 +205,19 @@ import java.util.Optional
Component.registerComponent(this, new AnnouncerModule) Component.registerComponent(this, new AnnouncerModule)
Component.registerComponent(this, new FunModule) Component.registerComponent(this, new FunModule)
ChromaBot.updatePlayerList() //The MCChatModule is tested to be enabled ChromaBot.updatePlayerList() //The MCChatModule is tested to be enabled
manager.registerCommand(new VersionCommand) val applicationId = dc.getRestClient.getApplicationId.block()
manager.registerCommand(new UserinfoCommand) val guildId = Some(DiscordPlugin.mainServer.getId.asLong())
manager.registerCommand(new HelpCommand) manager.registerCommand(new VersionCommand, applicationId, guildId)
manager.registerCommand(new DebugCommand) manager.registerCommand(new UserinfoCommand, applicationId, guildId)
manager.registerCommand(new ConnectCommand) manager.registerCommand(new HelpCommand, applicationId, guildId)
manager.registerCommand(new DebugCommand, applicationId, guildId)
manager.registerCommand(new ConnectCommand, applicationId, guildId)
TBMCCoreAPI.SendUnsentExceptions() TBMCCoreAPI.SendUnsentExceptions()
TBMCCoreAPI.SendUnsentDebugMessages() TBMCCoreAPI.SendUnsentDebugMessages()
val blw = new BukkitLogWatcher val blw = new BukkitLogWatcher
blw.start() blw.start()
LogManager.getRootLogger.asInstanceOf[Logger].addAppender(blw) LogManager.getRootLogger.asInstanceOf[Logger].addAppender(blw)
logWatcher = blw logWatcher = blw
Interactions.create().onCommand("teszt", Interactions.createHandler()
.guild(gi => gi.acknowledge().withFollowup(_.createFollowupMessage("Teszt"))).build());
if (!TBMCCoreAPI.IsTestServer) DiscordPlugin.dc.updatePresence(ClientPresence.online(ClientActivity.playing("Minecraft"))).subscribe() if (!TBMCCoreAPI.IsTestServer) DiscordPlugin.dc.updatePresence(ClientPresence.online(ClientActivity.playing("Minecraft"))).subscribe()
else DiscordPlugin.dc.updatePresence(ClientPresence.online(ClientActivity.playing("testing"))).subscribe() else DiscordPlugin.dc.updatePresence(ClientPresence.online(ClientActivity.playing("testing"))).subscribe()
getLogger.info("Loaded!") getLogger.info("Loaded!")

View file

@ -46,11 +46,11 @@ abstract class DiscordSenderBase protected(var user: User, var channel: MessageC
val sendmsg = DPUtils.sanitizeString(message) val sendmsg = DPUtils.sanitizeString(message)
this synchronized { this synchronized {
msgtosend += "\n" + sendmsg msgtosend += "\n" + sendmsg
if (sendtask == null) sendtask = Bukkit.getScheduler.runTaskLaterAsynchronously(DiscordPlugin.plugin, () => { if (sendtask == null) sendtask = Bukkit.getScheduler.runTaskLaterAsynchronously(DiscordPlugin.plugin, (() => {
channel.createMessage((if (user != null) user.getMention + "\n" else "") + msgtosend.trim).subscribe() channel.createMessage((if (user != null) user.getMention + "\n" else "") + msgtosend.trim).subscribe()
sendtask = null sendtask = null
msgtosend = "" msgtosend = ""
}, 4) // Waits a 0.2 second to gather all/most of the different messages }): Runnable, 4) // Waits a 0.2 second to gather all/most of the different messages
} }
} catch { } catch {
case e: Exception => case e: Exception =>

View file

@ -2,12 +2,36 @@ package buttondevteam.discordplugin.commands
import buttondevteam.discordplugin.DiscordPlugin import buttondevteam.discordplugin.DiscordPlugin
import buttondevteam.lib.chat.Command2 import buttondevteam.lib.chat.Command2
import discord4j.common.util.Snowflake
import discord4j.core.`object`.command.ApplicationCommandOption
import discord4j.discordjson.json.{ApplicationCommandOptionData, ApplicationCommandRequest}
import java.lang.reflect.Method import java.lang.reflect.Method
class Command2DC extends Command2[ICommand2DC, Command2DCSender] { class Command2DC extends Command2[ICommand2DC, Command2DCSender] {
override def registerCommand(command: ICommand2DC): Unit = override def registerCommand(command: ICommand2DC): Unit = {
registerCommand(command, DiscordPlugin.dc.getApplicationInfo.block().getId.asLong())
}
def registerCommand(command: ICommand2DC, appId: Long, guildId: Option[Long] = None): Unit = {
super.registerCommand(command, DiscordPlugin.getPrefix) //Needs to be configurable for the helps super.registerCommand(command, DiscordPlugin.getPrefix) //Needs to be configurable for the helps
val greetCmdRequest = ApplicationCommandRequest.builder()
.name(command.getCommandPath) //TODO: Main path
.description("A ChromaBot command.") //TODO: Description
.addOption(ApplicationCommandOptionData.builder()
.name("name")
.description("Your name")
.`type`(ApplicationCommandOption.Type.STRING.getValue)
.required(true)
.build()
).build()
val service = DiscordPlugin.dc.getRestClient.getApplicationService
guildId match {
case Some(id) => service.createGuildApplicationCommand(appId, id, greetCmdRequest).subscribe()
case None => service.createGlobalApplicationCommand(appId, greetCmdRequest).subscribe()
}
}
override def hasPermission(sender: Command2DCSender, command: ICommand2DC, method: Method): Boolean = { override def hasPermission(sender: Command2DCSender, command: ICommand2DC, method: Method): Boolean = {
//return !command.isModOnly() || sender.getMessage().getAuthor().hasRole(DiscordPlugin.plugin.modRole().get()); //TODO: modRole may be null; more customisable way? //return !command.isModOnly() || sender.getMessage().getAuthor().hasRole(DiscordPlugin.plugin.modRole().get()); //TODO: modRole may be null; more customisable way?
true true

View file

@ -1,110 +0,0 @@
package buttondevteam.discordplugin.listeners
import buttondevteam.discordplugin.commands.Command2DCSender
import buttondevteam.discordplugin.{DPUtils, DiscordPlugin}
import buttondevteam.lib.TBMCCoreAPI
import discord4j.common.util.Snowflake
import discord4j.core.`object`.entity.channel.{MessageChannel, PrivateChannel}
import discord4j.core.`object`.entity.{Member, Message, Role, User}
import reactor.core.scala.publisher.{SFlux, SMono}
import java.util.concurrent.atomic.AtomicBoolean
object CommandListener {
/**
* Runs a ChromaBot command. If mentionedonly is false, it will only execute the command if it was in #bot with the correct prefix or in private.
*
* @param message The Discord message
* @param mentionedonly Only run the command if ChromaBot is mentioned at the start of the message
* @return Whether it <b>did not run</b> the command
*/
def runCommand(message: Message, commandChannelID: Snowflake, mentionedonly: Boolean): SMono[Boolean] = {
val timings = CommonListeners.timings
val ret = SMono.just(true)
if (message.getContent.isEmpty) return ret //Pin messages and such, let the mcchat listener deal with it
val content = message.getContent
timings.printElapsed("A")
SMono(message.getChannel).flatMap((channel: MessageChannel) => {
def foo(channel: MessageChannel): SMono[Boolean] = {
var tmp = ret
if (!mentionedonly) { //mentionedonly conditions are in CommonListeners
timings.printElapsed("B")
if (!channel.isInstanceOf[PrivateChannel] && !(content.charAt(0) == DiscordPlugin.getPrefix && channel.getId.asLong == commandChannelID.asLong)) { //
return ret
}
timings.printElapsed("C")
tmp = ret.`then`(SMono(channel.`type`)).`then`(ret) // Fun (this true is ignored - x)
}
val cmdwithargs = new StringBuilder(content)
val gotmention = new AtomicBoolean
timings.printElapsed("Before self")
tmp.flatMapMany((_: Boolean) => SMono(DiscordPlugin.dc.getSelf)
.flatMap((self: User) => SMono(self.asMember(DiscordPlugin.mainServer.getId)))
.flatMapMany((self: Member) => {
def foo(self: Member) = {
timings.printElapsed("D")
gotmention.set(checkanddeletemention(cmdwithargs, self.getMention, message))
gotmention.set(checkanddeletemention(cmdwithargs, self.getNicknameMention, message) || gotmention.get)
val mentions = SFlux(message.getRoleMentions)
SFlux(self.getRoles).filterWhen((r: Role) => mentions.any((rr: Role) => rr.getName == r.getName)).map(_.getMention)
}
foo(self)
}).map((mentionRole: String) => {
def foo(mentionRole: String): Boolean = {
timings.printElapsed("E")
gotmention.set(checkanddeletemention(cmdwithargs, mentionRole, message) || gotmention.get) // Delete all mentions
!mentionedonly || gotmention.get //Stops here if false
}
foo(mentionRole)
}).switchIfEmpty(SMono.fromCallable(() => !mentionedonly || gotmention.get))).filter(b => b)
.last(Option(false)).filter(b => b)
.doOnNext(_ => channel.`type`.subscribe).flatMap(_ => {
def foo(): SMono[Boolean] = {
val cmdwithargsString = cmdwithargs.toString
try {
timings.printElapsed("F")
if (!DiscordPlugin.plugin.manager.handleCommand(new Command2DCSender(message), cmdwithargsString))
return DPUtils.reply(message, channel, "unknown command. Do " + DiscordPlugin.getPrefix + "help for help.").map(_ => false)
} catch {
case e: Exception =>
TBMCCoreAPI.SendException("Failed to process Discord command: " + cmdwithargsString, e, DiscordPlugin.plugin)
}
SMono.just(false) //If the command succeeded or there was an error, return false
}
foo()
}).defaultIfEmpty(true)
}
foo(channel)
})
}
private def checkanddeletemention(cmdwithargs: StringBuilder, mention: String, message: Message): Boolean = {
val prefix = DiscordPlugin.getPrefix
if (message.getContent.startsWith(mention)) { // TODO: Resolve mentions: Compound arguments, either a mention or text
if (cmdwithargs.length > mention.length + 1) {
var i = cmdwithargs.indexOf(" ", mention.length)
if (i == -1) i = mention.length
else { //noinspection StatementWithEmptyBody
while ( {
i < cmdwithargs.length && cmdwithargs.charAt(i) == ' '
}) { //Removes any space before the command
i += 1
}
}
cmdwithargs.delete(0, i)
cmdwithargs.insert(0, prefix) //Always use the prefix for processing
}
else cmdwithargs.replace(0, cmdwithargs.length, prefix + "help")
}
else {
if (cmdwithargs.isEmpty) cmdwithargs.replace(0, 0, prefix + "help")
else if (cmdwithargs.charAt(0) != prefix) cmdwithargs.insert(0, prefix)
return false //Don't treat / as mention, mentions can be used in public mcchat
}
true
}
}

View file

@ -11,29 +11,26 @@ import discord4j.core.`object`.entity.Message
import discord4j.core.`object`.entity.channel.{MessageChannel, PrivateChannel} import discord4j.core.`object`.entity.channel.{MessageChannel, PrivateChannel}
import discord4j.core.event.EventDispatcher import discord4j.core.event.EventDispatcher
import discord4j.core.event.domain.PresenceUpdateEvent import discord4j.core.event.domain.PresenceUpdateEvent
import discord4j.core.event.domain.interaction.{ChatInputInteractionEvent, MessageInteractionEvent}
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.Disposable import reactor.core.Disposable
import reactor.core.publisher.Mono
import reactor.core.scala.publisher.{SFlux, SMono} import reactor.core.scala.publisher.{SFlux, SMono}
object CommonListeners { object CommonListeners {
val timings = new Timings val timings = new Timings
/*
MentionEvent:
- CommandListener (starts with mention, only 'channelcon' and not in #bot)
MessageReceivedEvent:
- v CommandListener (starts with mention, in #bot or a connected chat)
- Minecraft chat (is enabled in the channel and message isn't [/]mcchat)
- CommandListener (with the correct prefix in #bot, or in private)
*/
def register(dispatcher: EventDispatcher): Unit = { def register(dispatcher: EventDispatcher): Unit = {
dispatcher.on(classOf[MessageCreateEvent]).flatMap((event: MessageCreateEvent) => { dispatcher.on(classOf[MessageCreateEvent]).flatMap((event: MessageCreateEvent) => {
SMono.just(event.getMessage).filter(_ => !DiscordPlugin.SafeMode) SMono.just(event.getMessage).filter(_ => !DiscordPlugin.SafeMode)
.filter(message => message.getAuthor.filter(!_.isBot).isPresent) .filter(message => message.getAuthor.filter(!_.isBot).isPresent)
.filter(message => !FunModule.executeMemes(message)) .filter(message => !FunModule.executeMemes(message))
.flatMap(handleMessage(event)) .filterWhen(message => {
Option(Component.getComponents.get(classOf[MinecraftChatModule])).filter(_.isEnabled)
.map(_.asInstanceOf[MinecraftChatModule].getListener.handleDiscord(event))
.getOrElse(SMono.just(true)) //Wasn't handled, continue
})
}).onErrorContinue((err, _) => TBMCCoreAPI.SendException("An error occured while handling a message!", err, DiscordPlugin.plugin)).subscribe() }).onErrorContinue((err, _) => 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) => {
if (!DiscordPlugin.SafeMode) if (!DiscordPlugin.SafeMode)
@ -42,6 +39,12 @@ object CommonListeners {
SFlux(dispatcher.on(classOf[RoleCreateEvent])).subscribe(GameRoleModule.handleRoleEvent) SFlux(dispatcher.on(classOf[RoleCreateEvent])).subscribe(GameRoleModule.handleRoleEvent)
SFlux(dispatcher.on(classOf[RoleDeleteEvent])).subscribe(GameRoleModule.handleRoleEvent) SFlux(dispatcher.on(classOf[RoleDeleteEvent])).subscribe(GameRoleModule.handleRoleEvent)
SFlux(dispatcher.on(classOf[RoleUpdateEvent])).subscribe(GameRoleModule.handleRoleEvent) SFlux(dispatcher.on(classOf[RoleUpdateEvent])).subscribe(GameRoleModule.handleRoleEvent)
SFlux(dispatcher.on(classOf[ChatInputInteractionEvent], event => {
if(event.getCommandName() eq "help")
event.reply("Hello there")
else
Mono.empty()
})).subscribe()
} }
var debug = false var debug = false
@ -49,20 +52,4 @@ object CommonListeners {
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)
} }
private def handleMessage(event: MessageCreateEvent) = {
(message: Message) => {
val commandChannel = Option(DiscordPlugin.plugin.commandChannel.get)
SMono(message.getChannel).filter(mch => commandChannel.isDefined && mch.getId.asLong() == commandChannel.get.asLong() //If mentioned, that's higher than chat
|| mch.isInstanceOf[PrivateChannel] || message.getContent.contains("channelcon")).flatMap(_ => { //Only 'channelcon' is allowed in other channels
//Only continue if this doesn't handle the event
CommandListener.runCommand(message, commandChannel.get, mentionedonly = true) //#bot is handled here
}).`then`(SMono.just(true)) //The condition is only for the first command execution, not mcchat
.filterWhen(_ => {
Option(Component.getComponents.get(classOf[MinecraftChatModule])).filter(_.isEnabled)
.map(_.asInstanceOf[MinecraftChatModule].getListener.handleDiscord(event)) //Also runs Discord commands in chat channels
.getOrElse(SMono.just(true)) //Wasn't handled, continue
}).filterWhen(_ => CommandListener.runCommand(event.getMessage, commandChannel.get, mentionedonly = false))
}
}
} }

View file

@ -3,7 +3,6 @@ package buttondevteam.discordplugin.mcchat
import buttondevteam.core.ComponentManager import buttondevteam.core.ComponentManager
import buttondevteam.discordplugin.* import buttondevteam.discordplugin.*
import buttondevteam.discordplugin.DPUtils.SpecExtensions import buttondevteam.discordplugin.DPUtils.SpecExtensions
import buttondevteam.discordplugin.listeners.CommandListener
import buttondevteam.discordplugin.playerfaker.{VanillaCommandListener, VanillaCommandListener14, VanillaCommandListener15} import buttondevteam.discordplugin.playerfaker.{VanillaCommandListener, VanillaCommandListener14, VanillaCommandListener15}
import buttondevteam.lib.* import buttondevteam.lib.*
import buttondevteam.lib.chat.{ChatMessage, TBMCChatAPI} import buttondevteam.lib.chat.{ChatMessage, TBMCChatAPI}
@ -251,7 +250,6 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener {
SMono(ev.getMessage.getChannel) SMono(ev.getMessage.getChannel)
.filter(channel => isChatEnabled(channel, author, hasCustomChat)) .filter(channel => isChatEnabled(channel, author, hasCustomChat))
.filter(channel => !isRunningMCChatCommand(channel, ev.getMessage.getContent, prefix)) .filter(channel => !isRunningMCChatCommand(channel, ev.getMessage.getContent, prefix))
.filterWhen(_ => CommandListener.runCommand(ev.getMessage, DiscordPlugin.plugin.commandChannel.get, mentionedonly = true)) //Allow running commands in chat channels
.filter(channel => { .filter(channel => {
MCChatUtils.resetLastMessage(channel) MCChatUtils.resetLastMessage(channel)
recevents.add(ev) recevents.add(ev)