Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stage.enter doesn't start the wizard

I created an application which display a survey wizard to the user. When the user launch the /start command, I call the AddProject:

const Telegraf = require('telegraf');
const bot = new Telegraf(process.env.BOT_TOKEN);
const session = require('telegraf/session');
bot.use(session());

const main = new TelegrafInlineMenu("Welcome.");
main.setCommand('start');
main.simpleButton('Start Survey', 'START_SURVEY', {
  doFunc: async ctx => surveyController.AddProject(ctx, bot)
});

essentially the code above create a menu that display the label Welcome and a button to start the survey. When the user click on the button, the method AddProject is called from the surveyController:

const Composer = require('telegraf/composer');
const stepHandler = new Composer();
const Stage = require('telegraf/stage');
const WizardScene = require('telegraf/scenes/wizard');
const userController = require('../controllers/user.controller');

module.exports = {

  AddProject: async function (ctx, bot) {

    const superWizard = new WizardScene('super-wizard',
      (ctx) => {
        ctx.reply('Step 1', Markup.inlineKeyboard([
          Markup.urlButton('❤️', 'http://telegraf.js.org'),
          Markup.callbackButton('➡️ Next', 'next'),
        ]).extra());
        return ctx.wizard.next();
      },
      (ctx) => {
        ctx.reply('Step 2');
        return ctx.wizard.next();
      },
      (ctx) => {
        ctx.reply('Done');
        return ctx.wizard.leave();
      },
    );

    const stage = new Stage([superWizard]);
    bot.use(stage.middleware());
    Stage.enter('super-wizard');
  },
};

the method AddProject is firing correctly, but the wizard is not displayed, what I did wrong?

like image 241
sfarzoso Avatar asked Apr 18 '19 15:04

sfarzoso


1 Answers

After a lot of attempts I finally achieved my goal. I'm not an expert of TelegrafJS and I found the documentation really hard to understand, specially if you are a newbie (missing examples, some concept like WizardScene missing etc...).

Create a Scene

So basically to achieve my target I have to use a Scene and TelegrafJS makes available different types of Scenes.

My goal was to wait for user input, and validate it. For this, I have used the WizardScene, this is my implementation:

const userWizard = new WizardScene('user-wizard',
    (ctx) => {
        ctx.reply("What is your name?");

        //Necessary for store the input
        ctx.scene.session.user = {};

        //Store the telegram user id
        ctx.scene.session.user.userId = ctx.update.callback_query.from.id;
        return ctx.wizard.next();
    },
    (ctx) => {

        //Validate the name
        if (ctx.message.text.length < 1 || ctx.message.text.length > 12) {
            return ctx.reply("Name entered has an invalid length!");
        }

        //Store the entered name
        ctx.scene.session.user.name = ctx.message.text;
        ctx.reply("What is your last name?");
        return ctx.wizard.next();
    },
    async (ctx) => {

        //Validate last name
        if (ctx.message.text.length > 30) {
            return ctx.reply("Last name has an invalid length");
        }

        ctx.scene.session.user.lastName = ctx.message.text;

        //Store the user in a separate controller
        await userController.StoreUser(ctx.scene.session.user);
        return ctx.scene.leave(); //<- Leaving a scene will clear the session automatically
    }
);

Register a Scene

the WizardScene above need to be registered in a Stage, so we can use this Scene in a Middleware. In this way, we can access to the Scene in a separate class or module:

const stage = new Stage([userWizard]);
stage.command('cancel', (ctx) => {
    ctx.reply("Operation canceled");
    return ctx.scene.leave();
});
bot.use(stage.middleware())

I also told to the Stage to leave the Scene if the /cancel command is reiceved, so if the user want cancel the operation, typing /cancel is the way.

Start a Scene

Now for enter in the wizard you can do the following:

await ctx.scene.enter('user-wizard');

so basically you have the Scene middleware registered in the context of your application, and you need to type .enter with the id of your Scene which is in my case user-wizard.

This will start the wizard.

I hope the documentation will be enhanced with more example, because I found this really hard to implement and understand, specially for me that I'm a newbie on TelegrafJS.

Kind regards.

like image 97
sfarzoso Avatar answered Nov 15 '22 11:11

sfarzoso