EdJoPaTo / grammy-inline-menu

Inline Menus for Telegram made simple. Successor of telegraf-inline-menu.
MIT License
357 stars 44 forks source link

All menu buttons stop working after the `do()` function is executed on a button click. #209

Closed snurfer0 closed 1 year ago

snurfer0 commented 1 year ago

Describe the bug After the do() function is executed on a button click, all menu buttons stop working. No errors are shown. Am I handling the button execution logic in a wrong way? the menu text is updating with the new context session data, but it breaks after the update.

Versions

To Reproduce Steps to reproduce the behavior:

  1. Create a menu
  2. Add a couple of buttons having some logic in the do() function

    this.menu.interact(
            getButtonText(REGISTER_CALLER_ACTION.ADD_USERNAME),
            REGISTER_CALLER_ACTION.ADD_USERNAME,
            {
                do: async (ctx) => {
                    // Reply to user
                    const replyMessage = await ctx.reply(
                        'Please set an username for this profile.',
                        {
                            parse_mode: 'HTML',
                            reply_markup: { force_reply: true },
                        }
                    );
    
                    // Wait for user input
                    const inputMessage = await this.botApiService.waitForReply(ctx, replyMessage);
    
                    // Validate input and update session
                    if (!inputMessage?.text) {
                        const errorMessage = await ctx.reply('Invalid username, please try again.');
                        await this.botApiService.deleteMessage(ctx, errorMessage, 3000);
                    } else {
                        Object.assign(ctx, {
                            ...ctx,
                            session: {
                                ...ctx.session,
                                callerRegisterData: {
                                    ...ctx.session.callerRegisterData,
                                    username: inputMessage.text,
                                },
                            },
                        });
                    }
    
                    // Delete messages
                    await this.botApiService.deleteMessage(ctx, inputMessage);
                    await this.botApiService.deleteMessage(ctx, replyMessage);
    
                    return true;
                },
            }
        );
  3. Click on one button, send an input and watch menu to update
  4. Click on some button again, see bug

Expected behavior Buttons should work.

snurfer0 commented 1 year ago

The issue is happening due to waitForReply() method, is there a better way to get the user input?

/**
* @dev Waits for user's reply and returns the reply message
* @param {MyContext} ctx Context
* @param {Message} message Message
* @param tries How many tries
* @returns {Promise<Message | undefined>} The message
*/
public async waitForReply(
        ctx: MyContext,
        message: Message,
        tries = 30 // 15 seconds
    ): Promise<Message | undefined> {
        while (tries > 0) {
            // Wait for 500 ms
            await sleep(500);

            // Get latest updates
            const [update] = await ctx.api.getUpdates({
                allowed_updates: ['message'],
                offset: -1,
                limit: 1,
            });

            // Continue if there are no updates
            if (!update) {
                continue;
            }

            // Return reply message if found
            if (update.message?.reply_to_message?.message_id === message.message_id) {
                return update.message;
            }

            // Decrement tries
            tries -= 1;
        }
    }
EdJoPaTo commented 1 year ago

Maybe you are looking for this? https://grammy.dev/plugins/stateless-question

snurfer0 commented 1 year ago

Is it a good idea to use this plugin inside the do() function?

snurfer0 commented 1 year ago

it seems like this stateless question is not awaitable and I wont be able to update the menu in the do() function

EdJoPaTo commented 1 year ago

Await would only work as long as the server is still running. Restarting the server would break this feature so you shouldn’t rely on something like that for a production bot.