EdJoPaTo / grammy-inline-menu

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

replyMenuToContext not working on submenutemplate #119

Closed jlanza closed 4 years ago

jlanza commented 4 years ago

I want to use replyMenuToContext to return to a menu generated when clicking on an option of a choice to chooseIntoSubmenu.

Currently this is not working and it is only possible to return to the full list of options.

const subMenu = new MenuTemplate<TelegrafContext>('Configure')
subMenu.chooseIntoSubmenu('items', ['item1', 'item2'], subMenuTemplate, {columns: 1});

menu.submenu('Configure', 'configure', submenu);

const question = new TelegrafStatelessQuestion('myvalue', async (ctx) => {
  await replyMenuToContext(subMenuTemplate, ctx, '/configure/items:item1);
});

This one is returning:

TypeError: Cannot read property '1' of undefined

However if I put await replyMenuToContext(subMenu, ctx, '/configure/items:item1); it works but returns to the list of options.

Is there any way you can achieve that or the only way I have is to manually create all the menus? I can do that by running a foreach on all the items in the array and generating a real menu for each one, but I'd rather prefer the chooseIntoSubmenu.

EdJoPaTo commented 4 years ago

I'm not sure if I understand what you are trying to achieve. You want to access submenu entries either via menu or from the question answer?

Also I'm not sure where this TypeError occurs. Can you give me the call stack where this happens?

jlanza commented 4 years ago

Better than the TypeError, I provide you with almost a MWE.

const menu = new MenuTemplate<TelegrafContext>(() => 'Main Menu\n' + new Date().toISOString())

const menuTemplate = new MenuTemplate<TelegrafContext>(ctx => `Menu Template for ${ctx.match![1]}`)
const statelessQuestion = new TelegrafStatelessQuestion('referral', async (ctx) => {
  const shopRegex = new RegExp('(/submenu/items:(.*))/default$');
  const res = ctx.message.reply_to_message.entities![0].url!.match(shopRegex);
  const menuPath = res![1];
  await replyMenuToContext(WHAT_DO_IH_AVE_TO_PUT_HERE, ctx, menuPath);
});

bot.use(referralQuestion.middleware());
menuTemplate.interact('Default', 'default', {
  do: async (ctx, path) => {
    statelessQuestion.replyWithMarkdown(ctx, 
      'Please enter the new text for ' + ctx.match![1] + '.[ ](http://t.me/#menupath' 
      + path //ctx.match![0] 
      + ')');
    return true;
  }
});

const subMenu = new MenuTemplate<TelegrafContext>('Submenu')
subMenu.chooseIntoSubmenu('items', ['item1', 'item2'], menuTemplate, {columns: 1});

menu.submenu('Submenu', 'submenu', subMenu);

export const menuMiddleware = new MenuMiddleware<TelegrafContext>('/', menu);

The menu structure should be something like /submenu/items:(.*)/default

The idea is that when I click on /submenu/items:item1 I will get a button with the text Default. Clicking on this button I will be asked to enter some data which will be retrieved using the statelessQuestion. Using the trick with the "hided link", the path could be extracted from the response. within the statelessQuestion callback.

What I want is to get back to/display /submenu/items:item1 after answering the question.

Here is what happens. If I replace WHAT_DO_IH_AVE_TO_PUT_HERE by subMenu the code runs flawlessly but I get back to /submenu. If I replace it by menuTemplate I get an exception.

Is there any way to return to a submenu generated using chooseIntoSubmenu? Otherwise I would have to create the menus "manually" per item in stead of using this functionality.

EdJoPaTo commented 4 years ago

Awesome, now I understand what you are trying to do. The menuPath you are using afterwards with replyMenuToContext does not end with a '/'. A basic assumption this library does is "a menu ends with /, a button does not". When you include that final slash, the correct menu is opened.

Send Menu Functions like replyMenuToContext should complain when the path isnt ending with '/' so other people will not fall for the same mistake.

Your exception then results from the ctx.match which doesnt have the entry for the submenu. ~Thats something this library should do, but sadly I dont have the time for that currently.~ Edit: which is not possible from within the library as the menu given has no clue about their parent menues. But you can use menuMiddleware.replyToContext(context, path) which is filling ctx.match correctly.

As you are familiar with RegExp and the path, you can also do it outside of the library in your code.

jlanza commented 4 years ago

Awesome, now I understand what you are trying to do.

Glad you like ;) I will be uploading the bot to github in the next few days. I would like to solve the other issue I just referenced here. I have to thank you as the library helps quite a lot.

Edit: which is not possible from within the library as the menu given has no clue about their parent menues. But you can use menuMiddleware.replyToContext(context, path) which is filling ctx.match correctly.

Great... This works. Using directly replyToContext as you initially said, didn't work.

EdJoPaTo commented 4 years ago

Great... This works. Using directly replyToContext as you initially said, didn't work.

When changing the regex group so it includes the last / it should work: const shopRegex = new RegExp('(/submenu/items:(.*))/default$');

It should result in the Error again, which is the attempt of accessing [1] from ctx.match which doesnt exist (sadly) (here: Menu Template for ${ctx.match![1]})

jlanza commented 4 years ago

When I said "Great" I had already added the '/' to the group ;) Thanks a lot.

EdJoPaTo commented 4 years ago

I'll just close this due to inactivity. If you have any questions feel free to ask ahead!