grammyjs / conversations

Conversational interfaces for grammY.
https://grammy.dev/plugins/conversations
MIT License
44 stars 17 forks source link

waitFor not restarting #83

Closed fegloff closed 9 months ago

fegloff commented 11 months ago

I have a bot that uses the waitFor with maxMilliseconds parameter to wait for user input. The expected behavior is that once the user writes, the maxMilliseconds should restart and wait for the next user input. The current behavior is the conversation ends once reaches the maxMilliseconds value, despite user input.

check this sscce https://github.com/fegloff/grammy-conversation-test

Also, is it possible to have an event that handles when a conversation abruptly ends?

KnorpelSenf commented 11 months ago

It is the intended behaviour that wait calls simply exit the conversation if the response arrives after the timeout has expired. There is no way for a library to run code reliably after a timeout, so we cannot throw an error after the timeout has expired. All we could do is to add a handler that will be called when the conversation actually is left (as soon as the first update arrives after the timeout has expired).

KnorpelSenf commented 9 months ago

Works as intended.

doaortu commented 3 weeks ago

All we could do is to add a handler that will be called when the conversation actually is left (as soon as the first update arrives after the timeout has expired).

Hi, sorry for resurrecting old closed issue, I'm just wondering, how to do this exactly? in reliable and non-confusing way. suppose we have helloConversation:

async function helloConversation(conversation: MyConversation, ctx: MyContext) {
    await ctx.reply("please enter the pass key:")
    const { message } = await conversation.waitFor('message:text', {
      maxMilliseconds: 5 * 1000, // 5 secs
    })
}

bot.command("hello", async ctx => {
  const activeConv = await ctx.conversation.active()
  if('helloConversation' in activeConv) {
    await ctx.reply("already in conversation")
    return;
  }
  await ctx.conversation.enter("helloConversation");
})

did we need to add additional bot.on("message:text") and handle this case there? how to reliably know a user is trying to continue this expired conversation? how about in groups when there's a lot user giving messages unrelated to this conversation? How to avoid clashing with another handler?

Honestly, a afterTimeout option for this, that allows me to do ctx.reply would suffice for me, it doesn't have to be executed exactly after the timeout expire, but just to notify the user, this specific conversation is already expired, and ask them to start it again. Because silently ignoring the user message is so frustrating for the users.

KnorpelSenf commented 3 weeks ago

Currently, the plugin behaves as if the conversation had never been entered in the first place. This means that your code would work the same way as it would if no conversation was active. In your case, a text message would be handled as if the user had not sent /hello first. So yes, if you want to handle text messages, you have to use bot.on("message:text").

If somebody wants to implement an afterTimeout handler then I would be happy to review these changes.

You can already implement it in your bot if you inspect the active conversations, then await next() to let the plugin handle the update, and check again in the active conversations if the conversation in question was left. This is a bit cumbersome, but until the afterTimeout handler is added to the plugin, it can be your workaround.