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
import buttondevteam.discordplugin.DiscordPlugin.dc
import buttondevteam.discordplugin.announcer.AnnouncerModule
import buttondevteam.discordplugin.broadcaster.GeneralEventBroadcasterModule
import buttondevteam.discordplugin.commands.*
@ -84,7 +85,7 @@ import java.util.Optional
var commandChannel: ReadOnlyConfigData[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.
* If empty (''), then it will only allow for the owner.
*/
var modRole: ReadOnlyConfigData[SMono[Role]] = null
/**
@ -204,19 +205,19 @@ import java.util.Optional
Component.registerComponent(this, new AnnouncerModule)
Component.registerComponent(this, new FunModule)
ChromaBot.updatePlayerList() //The MCChatModule is tested to be enabled
manager.registerCommand(new VersionCommand)
manager.registerCommand(new UserinfoCommand)
manager.registerCommand(new HelpCommand)
manager.registerCommand(new DebugCommand)
manager.registerCommand(new ConnectCommand)
val applicationId = dc.getRestClient.getApplicationId.block()
val guildId = Some(DiscordPlugin.mainServer.getId.asLong())
manager.registerCommand(new VersionCommand, applicationId, guildId)
manager.registerCommand(new UserinfoCommand, applicationId, guildId)
manager.registerCommand(new HelpCommand, applicationId, guildId)
manager.registerCommand(new DebugCommand, applicationId, guildId)
manager.registerCommand(new ConnectCommand, applicationId, guildId)
TBMCCoreAPI.SendUnsentExceptions()
TBMCCoreAPI.SendUnsentDebugMessages()
val blw = new BukkitLogWatcher
blw.start()
LogManager.getRootLogger.asInstanceOf[Logger].addAppender(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()
else DiscordPlugin.dc.updatePresence(ClientPresence.online(ClientActivity.playing("testing"))).subscribe()
getLogger.info("Loaded!")

View file

@ -46,11 +46,11 @@ abstract class DiscordSenderBase protected(var user: User, var channel: MessageC
val sendmsg = DPUtils.sanitizeString(message)
this synchronized {
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()
sendtask = null
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 {
case e: Exception =>

View file

@ -2,12 +2,36 @@ package buttondevteam.discordplugin.commands
import buttondevteam.discordplugin.DiscordPlugin
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
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
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 = {
//return !command.isModOnly() || sender.getMessage().getAuthor().hasRole(DiscordPlugin.plugin.modRole().get()); //TODO: modRole may be null; more customisable way?
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.event.EventDispatcher
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.role.{RoleCreateEvent, RoleDeleteEvent, RoleUpdateEvent}
import reactor.core.Disposable
import reactor.core.publisher.Mono
import reactor.core.scala.publisher.{SFlux, SMono}
object CommonListeners {
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 = {
dispatcher.on(classOf[MessageCreateEvent]).flatMap((event: MessageCreateEvent) => {
SMono.just(event.getMessage).filter(_ => !DiscordPlugin.SafeMode)
.filter(message => message.getAuthor.filter(!_.isBot).isPresent)
.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()
dispatcher.on(classOf[PresenceUpdateEvent]).subscribe((event: PresenceUpdateEvent) => {
if (!DiscordPlugin.SafeMode)
@ -42,6 +39,12 @@ object CommonListeners {
SFlux(dispatcher.on(classOf[RoleCreateEvent])).subscribe(GameRoleModule.handleRoleEvent)
SFlux(dispatcher.on(classOf[RoleDeleteEvent])).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
@ -49,20 +52,4 @@ object CommonListeners {
def debug(debug: String): Unit = if (CommonListeners.debug) { //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.discordplugin.*
import buttondevteam.discordplugin.DPUtils.SpecExtensions
import buttondevteam.discordplugin.listeners.CommandListener
import buttondevteam.discordplugin.playerfaker.{VanillaCommandListener, VanillaCommandListener14, VanillaCommandListener15}
import buttondevteam.lib.*
import buttondevteam.lib.chat.{ChatMessage, TBMCChatAPI}
@ -251,7 +250,6 @@ class MCChatListener(val module: MinecraftChatModule) extends Listener {
SMono(ev.getMessage.getChannel)
.filter(channel => isChatEnabled(channel, author, hasCustomChat))
.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 => {
MCChatUtils.resetLastMessage(channel)
recevents.add(ev)