Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Interactions, Slash-Commands, and Buttons #1501

Merged
merged 207 commits into from
Jun 4, 2021

Conversation

MinnDevelopment
Copy link
Member

@MinnDevelopment MinnDevelopment commented Jan 27, 2021

Pull Request Etiquette

Changes

  • Internal code
  • Library interface (affecting end-user code)
  • Documentation
  • Other: _____

Closes Issue: NaN

Description

This pull request adds support for Slash Commands and webhook execution.

Example: SlashBotExample
Documentation: https://javadoc.jitpack.io/com/github/dv8fromtheworld/jda/bf71f22/javadoc/index.html
Guide: https://github.com/DV8FromTheWorld/JDA/wiki/Interactions (wip)

How can I test a preview of this?

Add this branch as a dependency through jitpack: How?

The snapshot would be com.github.dv8fromtheworld:jda:commit from jitpack. (replace commit with the latest commit hash in this PR)

In order to create guild commands you need the applications.commands scope authorized for that guild. So instead of scope=bot in your authorization URL, you must have scope=bot+applications.commands.

It is highly recommended to use guild-specific slash commands for testing as they can be updated a lot more frequently than global commands. Create a test guild and invite your bot with the aforementioned scopes, then use Guild#updateCommands.

What is possible?

TODO

  • SlashCommandEvent and InteractionCreateEvent
  • Creating Commands (global/guild)
  • Deleting Commands (global/guild)
  • Editing Commands (global/guild)
  • Ephemeral messages
  • Interaction webhooks
  • Application Command Create/Update/Delete events
  • Slash Command Permissions
  • Components aka Buttons
  • Example (See SlashBotExample)
  • Documentation
  • Checks
  • Outgoing Webhook support (interactions through webservers) Putting this on hold for now

@discord-jda discord-jda locked and limited conversation to collaborators Jan 27, 2021
@MinnDevelopment
Copy link
Member Author

This is still in very early stages and needs a lot more polishing, just posting this to show some progress. I'm still very busy with other projects besides JDA for another month or so.

@MinnDevelopment MinnDevelopment added this to the v4.3.0 milestone Mar 4, 2021
Also add handling for resolved and types!
@MinnDevelopment
Copy link
Member Author

MinnDevelopment commented Mar 11, 2021

Here is my current testing code in kotlin which abuses MethodHandles to provide a simpler implementation:

// This is just a helper class to allow using method names as commands
open class CommandManager : EventListener {
    private val lookup = MethodHandles.lookup()
    private val methods = mutableMapOf<String, MethodHandle>()
    private val type = MethodType.methodType(Void.TYPE, SlashCommandEvent::class.java)

    override fun onEvent(event: GenericEvent) {
        if (event !is SlashCommandEvent)
            return

        val name = event.name.toLowerCase()
        try {
            val method = methods.computeIfAbsent(name) {
                lookup.findVirtual(this.javaClass, name, type)
            }

            method.invokeWithArguments(this, event)
        } catch (ignored: Exception) {
            val argTypes = event.options.map {
                when (it.type) {
                    Command.OptionType.USER -> Member::class.java // For simplicity lets assume this
                    Command.OptionType.ROLE -> Role::class.java
                    else -> Any::class.java
                }
            }
            println("${name}(${argTypes.joinToString(",")})")
            try {
                val method = methods.computeIfAbsent(name) { lookup.findVirtual(this.javaClass, name, MethodType.methodType(Void.TYPE, SlashCommandEvent::class.java, *argTypes.toTypedArray())) }
                method.invokeWithArguments(this, event, *event.options.map {
                    when (it.type) {
                        Command.OptionType.USER -> it.asMember // For simplicity lets assume this
                        Command.OptionType.ROLE -> it.asRole
                        else -> it
                    }
                }.toTypedArray())
            } catch (ignored: NoSuchMethodException) {}
        }
    }
}


class Moderation : CommandManager() {
    fun ping(event: SlashCommandEvent) { // function name = command name
        event.thread.setEphemeral(true) // Now all follow up messages are ephemeral
        for (i in 1..6) {
            // This is actually rate limited (major parameter is the APPLICATION ID here, pretty insane imo)
            event.thread.editOriginal("Test followup $i").queue()
        }
        // You can queue up followups before even acknowledging the command! They just execute after, so there is no need for callbacks!
        event.reply("Test reply").setEphemeral(true).queue() // This will pretty much instantly be edited
    }

    fun kick(event: SlashCommandEvent, target: Member) {
        event.acknowledge().queue()
        event.thread.setEphemeral(false) // This is the default but we can explicitly disable ephemeral here too
        target.kick()
            .flatMap {
                event.thread.sendMessage("User was kicked successfully!") // This will send *after* the acknowledge callback completes
            }.onErrorFlatMap {
                it.printStackTrace()
                event.thread.sendMessage("Was unable to kick this user :(")
            }.queue()
    }
}

As you can see, the callbacks are not necessary anymore due to the way I delay followup calls with the new TriggerRestAction that can be triggered later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Slash Commands and Interactions