Kaktushose / jda-commands

A declarative, annotation driven command library for JDA
https://github.com/Kaktushose/jda-commands/wiki
Apache License 2.0
66 stars 11 forks source link

Error on reply to original interaction #137

Closed stijnb1234 closed 1 month ago

stijnb1234 commented 1 month ago

I have a slash command, which sends a reply with a button. When the user clicks on the button, I want to do some function calls (which takes some time) and then reply to the original interaction (replacing the original message).

However, when I do this, (sometimes) I get:

[JDA MainWS-ReadThread] INFO com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandDispatcher - Executing command onVerify for user Member:Stijn | SBDeveloper(id=249212554297999382, user=User:sbdeveloper(id=249212554297999382), guild=Guild:SBDevelopment - Test(id=1262389197449465948))
[JDA RateLimit-Elastic-Worker 1] ERROR com.github.kaktushose.jda.commands.dispatching.reply.ReplyContext - The response request encountered an exception at its execution point!
java.lang.reflect.InvocationTargetException
    at com.github.kaktushose.jda.commands.dispatching.reply.ReplyContext.lambda$new$1(ReplyContext.java:47)
    at net.dv8tion.jda.api.exceptions.ContextException$ContextConsumer.accept(ContextException.java:75)
    at net.dv8tion.jda.api.exceptions.ContextException$ContextConsumer.accept(ContextException.java:60)
    at net.dv8tion.jda.internal.requests.restaction.TriggerRestAction.lambda$queue$3(TriggerRestAction.java:112)
    at java.base/java.lang.Iterable.forEach(Iterable.java:75)
    at net.dv8tion.jda.internal.requests.restaction.TriggerRestAction.lambda$fail$1(TriggerRestAction.java:86)
    at net.dv8tion.jda.api.utils.MiscUtil.locked(MiscUtil.java:148)
    at net.dv8tion.jda.internal.requests.restaction.TriggerRestAction.fail(TriggerRestAction.java:84)
    at net.dv8tion.jda.internal.interactions.InteractionHookImpl.lambda$fail$2(InteractionHookImpl.java:109)
    at java.base/java.lang.Iterable.forEach(Iterable.java:75)
    at net.dv8tion.jda.internal.interactions.InteractionHookImpl.lambda$fail$3(InteractionHookImpl.java:109)
    at net.dv8tion.jda.api.utils.MiscUtil.locked(MiscUtil.java:148)
    at net.dv8tion.jda.internal.interactions.InteractionHookImpl.fail(InteractionHookImpl.java:100)
    at net.dv8tion.jda.internal.interactions.DeferrableInteractionImpl.releaseHook(DeferrableInteractionImpl.java:43)
    at net.dv8tion.jda.internal.requests.restaction.interactions.InteractionCallbackImpl.handleResponse(InteractionCallbackImpl.java:108)
    at net.dv8tion.jda.api.requests.Request.handleResponse(Request.java:304)
    at net.dv8tion.jda.internal.requests.Requester$WorkTask.handleResponse(Requester.java:423)
    at net.dv8tion.jda.internal.requests.Requester$WorkTask.access$300(Requester.java:359)
    at net.dv8tion.jda.internal.requests.Requester.execute(Requester.java:233)
    at net.dv8tion.jda.internal.requests.Requester.execute(Requester.java:145)
    at net.dv8tion.jda.internal.requests.Requester.execute(Requester.java:128)
    at net.dv8tion.jda.internal.requests.Requester$WorkTask.execute(Requester.java:387)
    at net.dv8tion.jda.api.requests.SequentialRestRateLimiter$Bucket.execute(SequentialRestRateLimiter.java:477)
    at net.dv8tion.jda.api.requests.SequentialRestRateLimiter$Bucket.run(SequentialRestRateLimiter.java:517)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
    at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: net.dv8tion.jda.api.exceptions.InteractionFailureException: Cascading failure caused by interaction callback failure
    ... 14 more
Caused by: net.dv8tion.jda.api.exceptions.ContextException
    at net.dv8tion.jda.api.exceptions.ContextException.here(ContextException.java:54)
    at net.dv8tion.jda.internal.requests.restaction.TriggerRestAction.wrapContext(TriggerRestAction.java:156)
    at net.dv8tion.jda.internal.requests.restaction.TriggerRestAction.queue(TriggerRestAction.java:109)
    at com.github.kaktushose.jda.commands.dispatching.reply.ReplyContext.queueReply(ReplyContext.java:244)
    at com.github.kaktushose.jda.commands.dispatching.reply.ReplyContext.queueEdit(ReplyContext.java:217)
    at com.github.kaktushose.jda.commands.dispatching.reply.ReplyContext.queue(ReplyContext.java:206)
    at com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent.reply(CommandEvent.java:39)
    at com.github.kaktushose.jda.commands.dispatching.reply.Replyable.reply(Replyable.java:78)
    at nl.sbdeveloper.sbdevelopmentbot.commands.Verify.onVerify(Verify.java:73)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandDispatcher.onEvent(CommandDispatcher.java:102)
    at com.github.kaktushose.jda.commands.dispatching.DispatcherSupervisor.onGenericInteractionCreate(DispatcherSupervisor.java:92)
    at net.dv8tion.jda.api.hooks.ListenerAdapter.onEvent(ListenerAdapter.java:463)
    at net.dv8tion.jda.api.hooks.InterfacedEventManager.handle(InterfacedEventManager.java:98)
    at net.dv8tion.jda.internal.hooks.EventManagerProxy.handleInternally(EventManagerProxy.java:88)
    at net.dv8tion.jda.internal.hooks.EventManagerProxy.handle(EventManagerProxy.java:70)
    at net.dv8tion.jda.internal.JDAImpl.handleEvent(JDAImpl.java:169)
    at net.dv8tion.jda.internal.handle.InteractionCreateHandler.handleCommand(InteractionCreateHandler.java:112)
    at net.dv8tion.jda.internal.handle.InteractionCreateHandler.handleInternally(InteractionCreateHandler.java:83)
    at net.dv8tion.jda.internal.handle.SocketHandler.handle(SocketHandler.java:39)
    at net.dv8tion.jda.internal.requests.WebSocketClient.onDispatch(WebSocketClient.java:1009)
    at net.dv8tion.jda.internal.requests.WebSocketClient.onEvent(WebSocketClient.java:892)
    at net.dv8tion.jda.internal.requests.WebSocketClient.handleEvent(WebSocketClient.java:870)
    at net.dv8tion.jda.internal.requests.WebSocketClient.onBinaryMessage(WebSocketClient.java:1048)
    at com.neovisionaries.ws.client.ListenerManager.callOnBinaryMessage(ListenerManager.java:385)
    at com.neovisionaries.ws.client.ReadingThread.callOnBinaryMessage(ReadingThread.java:276)
    at com.neovisionaries.ws.client.ReadingThread.handleBinaryFrame(ReadingThread.java:996)
    at com.neovisionaries.ws.client.ReadingThread.handleFrame(ReadingThread.java:755)
    at com.neovisionaries.ws.client.ReadingThread.main(ReadingThread.java:108)
    at com.neovisionaries.ws.client.ReadingThread.runMain(ReadingThread.java:64)
    at com.neovisionaries.ws.client.WebSocketThread.run(WebSocketThread.java:45)
[ForkJoinPool.commonPool-worker-2] ERROR net.dv8tion.jda.api.requests.RestAction - RestAction queue returned failure: [ErrorResponseException] 10062: Unknown interaction
net.dv8tion.jda.api.exceptions.ContextException
    at net.dv8tion.jda.api.exceptions.ContextException.here(ContextException.java:54)
    at net.dv8tion.jda.api.requests.Request.<init>(Request.java:77)
    at net.dv8tion.jda.internal.requests.RestActionImpl.queue(RestActionImpl.java:203)
    at net.dv8tion.jda.internal.requests.restaction.interactions.InteractionCallbackImpl.queue(InteractionCallbackImpl.java:74)
    at net.dv8tion.jda.api.requests.RestAction.queue(RestAction.java:573)
    at net.dv8tion.jda.api.requests.RestAction.queue(RestAction.java:539)
    at com.github.kaktushose.jda.commands.dispatching.reply.ReplyContext.queueReply(ReplyContext.java:242)
    at com.github.kaktushose.jda.commands.dispatching.reply.ReplyContext.queueEdit(ReplyContext.java:217)
    at com.github.kaktushose.jda.commands.dispatching.reply.ReplyContext.queue(ReplyContext.java:206)
    at com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandEvent.reply(CommandEvent.java:39)
    at com.github.kaktushose.jda.commands.dispatching.reply.Replyable.reply(Replyable.java:78)
    at nl.sbdeveloper.sbdevelopmentbot.commands.Verify.onVerify(Verify.java:73)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
    at java.base/java.lang.reflect.Method.invoke(Method.java:580)
    at com.github.kaktushose.jda.commands.dispatching.interactions.commands.CommandDispatcher.onEvent(CommandDispatcher.java:102)
    at com.github.kaktushose.jda.commands.dispatching.DispatcherSupervisor.onGenericInteractionCreate(DispatcherSupervisor.java:92)
    at net.dv8tion.jda.api.hooks.ListenerAdapter.onEvent(ListenerAdapter.java:463)
    at net.dv8tion.jda.api.hooks.InterfacedEventManager.handle(InterfacedEventManager.java:98)
    at net.dv8tion.jda.internal.hooks.EventManagerProxy.handleInternally(EventManagerProxy.java:88)
    at net.dv8tion.jda.internal.hooks.EventManagerProxy.handle(EventManagerProxy.java:70)
    at net.dv8tion.jda.internal.JDAImpl.handleEvent(JDAImpl.java:169)
    at net.dv8tion.jda.internal.handle.InteractionCreateHandler.handleCommand(InteractionCreateHandler.java:112)
    at net.dv8tion.jda.internal.handle.InteractionCreateHandler.handleInternally(InteractionCreateHandler.java:83)
    at net.dv8tion.jda.internal.handle.SocketHandler.handle(SocketHandler.java:39)
    at net.dv8tion.jda.internal.requests.WebSocketClient.onDispatch(WebSocketClient.java:1009)
    at net.dv8tion.jda.internal.requests.WebSocketClient.onEvent(WebSocketClient.java:892)
    at net.dv8tion.jda.internal.requests.WebSocketClient.handleEvent(WebSocketClient.java:870)
    at net.dv8tion.jda.internal.requests.WebSocketClient.onBinaryMessage(WebSocketClient.java:1048)
    at com.neovisionaries.ws.client.ListenerManager.callOnBinaryMessage(ListenerManager.java:385)
    at com.neovisionaries.ws.client.ReadingThread.callOnBinaryMessage(ReadingThread.java:276)
    at com.neovisionaries.ws.client.ReadingThread.handleBinaryFrame(ReadingThread.java:996)
    at com.neovisionaries.ws.client.ReadingThread.handleFrame(ReadingThread.java:755)
    at com.neovisionaries.ws.client.ReadingThread.main(ReadingThread.java:108)
    at com.neovisionaries.ws.client.ReadingThread.runMain(ReadingThread.java:64)
    at com.neovisionaries.ws.client.WebSocketThread.run(WebSocketThread.java:45)

How can I do this correctly?

Example code:


@SlashCommand(value = "rand", desc = "Random command", ephemeral = true)
public void onRand(CommandEvent event) {
    event.withButtons("onRandButton").reply("First message");
}

@Button(value = "Random", emoji = "\uD83D\uDD27", ephemeral = true, style = ButtonStyle.PRIMARY)
public void onRandButton(ComponentEvent event) {
    //TODO Implement some random calls here

    //Error occurs here (below)
    event.reply("Second message, we are done!");
}
Kaktushose commented 1 month ago

Wow, I didn't expect that anyone besides from me is still using this messy und unfinished framework 😅

jda-commands only acknowledges events just before a reply or edit get's send. I can't remember for sure why I've implemented it this way, but I believe there was a reason to it.

When your function calls take longer than 3 seconds, the interaction expires resulting in an unknown interaction error.

There are two possible solutions:

Acknowledge the component event by sending a reply before you do the heavy lifting:

@Button(value = "Random", emoji = "\uD83D\uDD27", ephemeral = true, style = ButtonStyle.PRIMARY)
public void onRandButton(ComponentEvent event) {
    event.reply("Working on it...");

    //time intense method call

    event.reply("Second message, we are done!");
}

Acknowledge the event manually

@Button(value = "Random", emoji = "\uD83D\uDD27", ephemeral = true, style = ButtonStyle.PRIMARY)
public void onRandButton(ComponentEvent event) {
    ((IMessageEditCallback) event.getContext().getEvent()).deferEdit().queue();

    //time intense method call

    event.reply("Second message, we are done!");
}

P.S.: Remember that all these method run on the main WS thread. I don't know what your function calls exactly do, but be sure to not block the event thread.

stijnb1234 commented 1 month ago

It's lovely and makes it a lot easier to work with 😛

I will check that out, thanks.

stijnb1234 commented 1 month ago

Works great, thanks again ;)

stijnb1234 commented 1 month ago

In a SlashCommand interaction, how can I later replace the first reply with the second one? @Kaktushose

Kaktushose commented 1 month ago

I'm afraid that I dont understand your question. Can you give an example?

stijnb1234 commented 1 month ago

I'm afraid that I dont understand your question. Can you give an example?

Sure:

@SlashCommand(value = "roles", desc = "Get the roles", ephemeral = true)
public void onRoles(CommandEvent event) {
    event.reply(Util.getEmbedBuilder()
            .setTitle("Roles")
            .setDescription("Working on it..."));

    //TODO Do something...

    //We are done with that, now replace the Working on it... message with a new one.
    //Like this, it just sends a second reply on the first one. That's not what I want...
    event.reply(Util.getEmbedBuilder()
            .setTitle("Roles")
            .setDescription("You have received the roles!"));
}
Kaktushose commented 1 month ago

event.editReply(true) should do the trick. This is normally set to true by default so I'm a little bit confused why this isn't working for you

stijnb1234 commented 1 month ago

image

Sadly it does not. It still replies to the first one.

Code used to test:

@Interaction
public class Test {
    @SlashCommand("test")
    public void onTest(CommandEvent e) {
        e.editReply(true);

        e.reply("Okay, we are working on it...");

        // Wait 5 seconds
        new java.util.Timer().schedule(new java.util.TimerTask() {
            @Override
            public void run() {
                e.reply("Now we are done! This message should overwrite the first one.");
            }
        }, 5000);
    }
}
stijnb1234 commented 1 month ago

Now it randomly seems to work again. Maybe it was just caused by my bad network connection on this location in combination with a timing issue?

stijnb1234 commented 1 month ago

No, I'm joking. It works in a different interaction (like button), but not in the first one (like the example above)?

Kaktushose commented 1 month ago

I was able to reproduce the bug but didnt find the time yet to fix it

stijnb1234 commented 1 month ago

Hey @Kaktushose. Have you perhaps found time to look into this?

Kaktushose commented 1 month ago

Sadly, no

Kaktushose commented 1 month ago

Okay, I've looked into it:

Inside a CommandEvent you cannot use reply twice and expect the second call to edit the message. Edit reply only works for events that implement IMessageEditCallback but SlashCommandInteractionEvent only implements IReplyCallback.

If you want to edit the message you need to use the success callback:

@SlashCommand("test")
public void onTest(CommandEvent event) {
    event.reply("Original", message -> message.editMessage("Edit").queue());
}

Using the success callback had a bug, which was resolved with the latest commit on development

stijnb1234 commented 1 month ago

Cool, yeah that success callback wasn't an option because of that bug, but now it should work. Let me see, thanks. ;)