skip to Main Content

I am creating game bot, where user should answer some questions, and admin should approve or reject them. I need to find a way, when admin is accepted the answer, to force change scene of the user.

So for example the user is in scene "answering". When user send message, it sends to admin

const gameButtons = [
  [
    { text: `✅ approve`, callback_data: `gTo:appG/uId=${ctx.state.userId}` }, 
    { text: `❌ reject`, callback_data: `gTo:rejG/uId=${ctx.state.userId}` },
  ], 
];
await ctx.telegram.sendMessage(ctx.state.chatTo, `User answer is: ${ctx.message.text}`, {
  reply_markup: JSON.stringify({ inline_keyboard: gameButtons }),
});

So the admin receive message and or will accept or reject

const approveQuestion = async ({ ctx, text }) => {
  const [, user] = text.split("/");
  const [, userId] = user.split("=");

  //somehow change users scene to answered
  await changeUserScene("winner", userId); 
  await ctx.telegram.sendMessage(userId, "It is right answer");
};

Is there way to do that?

2

Answers


  1. Edit: After reading/trying a lot, based on a few github issues. You can only change a user’s scene when you have the context (ctx) of that user.

    The easiest way is to get the ctx is to save it for later once you get an interaction with a user.


    Please see my test code, explanation below:

    const { Telegraf, Scenes, session } = require('telegraf');
    const { BaseScene, Stage } = Scenes;
    
    const botToken = '859163076:gfkjsdgkaslgflalsgflgsadhjg';
    const user1id  = 123456;
    const user2id  = 789456;
    
    const ctxObj = { };
    
    // Scene 1
    const scene_1 = new BaseScene('scene_1');
    scene_1.enter((ctx) => {
    
        // Save ctx
        ctxObj[ctx.message.from.id] = ctx;
        console.log('State: ', ctxObj)
        ctx.reply('You just enetered scene_1')
    });
    scene_1.on('text', async (ctx) => {
        console.log('Got message in scene_1', ctx.message.text, ctx.message.from.id)
    
        // When receiving form user-2, force user-1 scene
        if (ctx.message.from.id === user2id) {
            if (ctxObj[user1id]) {
                ctxObj[user1id].scene.enter('scene_2');
            }
        }
    });
    
    // Scene 2
    const scene_2 = new BaseScene('scene_2');
    scene_2.enter((ctx) => ctx.reply('You just enetered scene_2'));
    scene_2.on('text', (ctx) => {
        console.log('Got message in scene_2', ctx.message.text);
        ctx.scene.leave();
    });
    
    // Stage
    const stage = new Stage([ scene_1, scene_2 ]);
    
    // Bot
    const bot = new Telegraf(botToken);
    bot.use(session());
    bot.use(stage.middleware());
    bot.command('start', (ctx) => ctx.scene.enter('scene_1'));
    bot.launch();
    

    The 2 important parts are:

    scene_1.enter((ctx) => {
    
        // Save ctx
        ctxObj[ctx.message.from.id] = ctx;
        console.log('State: ', ctxObj)
        ctx.reply('You just enetered scene_1')
    });
    

    Here we save the ctx of every user when they enter scene_1.


    scene_1.on('text', async (ctx) => {
        console.log('Got message in scene_1', ctx.message.text, ctx.message.from.id)
    
        // When receiving form user-2, force user-1 scene
        if (ctx.message.from.id === user2id) {
            if (ctxObj[user1id]) {
                ctxObj[user1id].scene.enter('scene_2');
            }
        }
    });
    

    Then, when we receive a message from a user that’s in scene_2, we check (this is pure for testing) if the message if from user_2, if so, we use the ctx of user_1 and call scene.enter to force him into the new scene.


    This works as expected, if needed I can place some screenshots of 2 Telegram accounts talking to that bot.

    It seems like you can create the ctx on the fly, but I’d recommend just saving it if you have the ability to.

    Login or Signup to reply.
  2. Yes run the following. Have inserted comments to aid

    const { session } = require('telegraf')
    const RedisSession = require('telegraf-session-redis')
    
    // initialize Redis session store
    const redisSession = new RedisSession({
      store: {
        host: 'localhost',
        port: 6379,
        db: 0,
      },
    })
    
    // register Redis session middleware
    bot.use(session(redisSession))
    
    // define a function to change user's scene
    const changeUserScene = async (scene, userId) => {
      // update the user's session data with the new scene
      await bot.telegram.setMyCommands(userId, [{ command: '/start', description: 'Start the game' }]);
      await bot.telegram.sendMessage(userId, `You have answered the question correctly! Moving to the next stage...`);
      await bot.telegram.sendMessage(userId, `You are now in the "${scene}" scene.`);
      await bot.telegram.sendMessage(userId, `Type /start to start the next question.`);
      await bot.session.set(userId, { scene })
      await bot.session.save()
    }
    
    const approveQuestion = async ({ ctx, text }) => {
      const [, user] = text.split("/")
      const [, userId] = user.split("=")
    
      // change user's scene to "answered"
      await changeUserScene("answered", userId)
    
      // send response to admin
      await ctx.answerCbQuery("Answer approved!")
    }
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search