You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Telegraf WizardScene步骤回退功能实现技术求助

Telegraf WizardScene 步骤回退问题解决

问题描述

在Telegraf的WizardScene中实现多步骤表单,每次用户提交数据后编辑机器人消息展示进度。需求是除第一步外,支持从任意步骤回退至上一步。尝试用ctx.wizard.steps[ctx.wizard.cursor - 2]触发上一步逻辑,虽然能回到上一步的提问,但用户在回退后的步骤提交新内容时,流程会直接跳转到回退前位置的下一个步骤,流程示例:

  • 步骤1:机器人提示“发送文本”→ 用户输入文本(进入步骤2)
  • 步骤2:机器人提示“发送其他内容”→ 用户点击“返回”(回到步骤1)
  • 步骤1:机器人提示“发送文本”→ 用户输入新内容→ 直接跳转到步骤3

核心原因

直接调用步骤函数但未修改ctx.wizard.cursor的值,cursor仍保留回退前的索引。当回退后的步骤执行ctx.wizard.next()时,会基于旧的cursor递增,导致跳转到错误的步骤。

解决方案

使用Telegraf WizardScene提供的selectStep()方法,该方法会同时更新cursor并执行目标步骤的处理函数。同时修复代码中的变量错误,确保步骤逻辑统一。

修改后的完整代码

const { Scenes, Markup } = require("telegraf");

module.exports = () => {
  const scene = new Scenes.WizardScene(
    "newSceneWithStapBacks",
    async (ctx) => {
      // 第一步:用户通过内联按钮进入场景
      await ctx.editMessageText(`Send text`, {
        ...Markup.inlineKeyboard([
          [Markup.button.callback(`Cancel`, `cancel`)], // 第一步只有取消按钮
        ]),
        parse_mode: "HTML",
      });

      // 初始化数据存储对象
      ctx.wizard.state.data = {};
      // 保存初始消息的上下文,用于后续编辑
      ctx.wizard.state.data.firstCtx = ctx;

      return ctx.wizard.next();
    },

    async (ctx) => {
      // 验证输入:非文本消息直接删除
      if (!ctx?.message?.text) return ctx.deleteMessage();

      const smthData = ctx.message.text;
      // 删除用户输入的消息
      await ctx.deleteMessage();

      // 编辑机器人消息,展示已提交的数据并提示下一步
      await ctx.wizard.state.data.firstCtx.editMessageText(
        `1. Your data: ${smthData}
        
        Send something else.`,
        {
          ...Markup.inlineKeyboard([
            [
              Markup.button.callback(`Back`, `back`),
              Markup.button.callback(`Cancel`, `cancel`),
            ],
          ]),
          parse_mode: "HTML",
        }
      );

      // 保存数据到会话
      ctx.wizard.state.data.smthData = smthData;
      return ctx.wizard.next();
    },

    async (ctx) => {
      // 验证输入:非文本消息直接删除
      if (!ctx?.message?.text) return ctx.deleteMessage();

      const smthData2 = ctx.message.text;
      // 删除用户输入的消息
      await ctx.deleteMessage();

      // 编辑机器人消息,展示进度
      await ctx.wizard.state.data.firstCtx.editMessageText(
        `1. Your data: ${ctx.wizard.state.data.smthData}
        2. Your data: ${smthData2}
        
        Send something else.`,
        {
          ...Markup.inlineKeyboard([
            [
              Markup.button.callback(`Back`, `back`),
              Markup.button.callback(`Cancel`, `cancel`),
            ],
          ]),
          parse_mode: "HTML",
        }
      );

      // 保存数据
      ctx.wizard.state.data.smthData2 = smthData2;
      return ctx.wizard.next();
    },

    async (ctx) => {
      // 验证输入:非文本消息直接删除
      if (!ctx?.message?.text) return ctx.deleteMessage();

      const smthData3 = ctx.message.text;
      // 删除用户输入的消息
      await ctx.deleteMessage();

      // 编辑机器人消息,展示最终结果
      await ctx.wizard.state.data.firstCtx.editMessageText(
        `1. Your data: ${ctx.wizard.state.data.smthData}
        2. Your data: ${ctx.wizard.state.data.smthData2}
        3. Your data: ${smthData3}
        
        🎉 End.`,
        {
          parse_mode: "HTML",
        }
      );

      // 保存最后一项数据
      ctx.wizard.state.data.smthData3 = smthData3;
      return ctx.wizard.leave();
    }
  );

  // 回退功能实现
  scene.action(/^(?:back)$/i, async (ctx) => {
    // 禁止从第一步回退
    if (ctx.wizard.cursor <= 1) {
      return ctx.answerCbQuery("无法继续回退");
    }
    // 计算目标步骤索引:当前cursor是当前步骤的索引,回退一步即cursor-1
    const targetStepIndex = ctx.wizard.cursor - 1;
    // 切换到目标步骤,自动更新cursor并执行步骤函数
    await ctx.wizard.selectStep(targetStepIndex);
    return ctx.answerCbQuery();
  });

  // 取消场景
  scene.action(/^(?:cancel)$/i, async (ctx) => {
    await ctx.reply(`已退出!`);
    return ctx.scene.leave();
  });

  return scene;
};

关键修改点

  1. 回退逻辑:使用ctx.wizard.selectStep(targetStepIndex)替代直接调用步骤函数,确保cursor被正确更新,后续的next()能基于当前步骤正常跳转。
  2. 变量统一:将步骤函数中的参数统一为ctx,修复原代码中错误使用userCtx导致的逻辑异常。
  3. 数据保存:在最后一步正确保存smthData3,原代码中错误重复保存smthData2。
  4. 操作反馈:添加ctx.answerCbQuery()给用户按钮点击的反馈,提升交互体验。

内容的提问来源于stack exchange,提问作者David

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 09:29:51