javascript - 处理提醒对话框的答案

标签 javascript node.js botframework

我想在向用户发送主动消息时启动“提醒对话框”。对话框已发布。但在处理答案时,它会返回主对话框。

目前我创建的机器人如下:

const conversationState = new ConversationState(mongoStorage);
const userState = new UserState(memoryStorage);

const bot = new DialogAndWelcomeBot(conversationState, userState, logger);

// Listen for incoming activities and route them to your bot main dialog.
server.post("/api/messages", (req, res) => {
  adapter.use(new GraphQLMiddleware(bot.getAuthState()));

  // Route received a request to adapter for processing
  adapter.processActivity(req, res, async turnContext => {
    await bot.run(turnContext);
  });
});

并发送这样的主动消息:

await adapter.createConversation(conversationReferenceAdapted, bot.remind);

其中DialogAndWelcomeBot具有以下功能:

remind = async turnContext => {
  const reminderDialog = new ReminderDialog(this.logger);
  await reminderDialog.run(turnContext, this.dialogState);
  await this.conversationState.saveChanges(turnContext, false);
  await this.userState.saveChanges(turnContext, false);
};

但是,ReminderDialog 会正确触发(带有"is"和“否”按钮)。但是当我按下任何一个按钮时我得到:

[onTurnError]: Error: DialogContext.continue(): Can't continue dialog. A dialog with an id of 'ReminderDialog' wasn't found.

这让我怀疑它不知道原始流程中的ReminderDialog(通过实例化MainDialogDialogAndWelcomeBot的构造函数)并运行)。

关于如何解决这个问题有什么想法吗?

所需功能

我有一个询问有关用户的一些详细信息的主流程。可以通过向机器人发送任何短信来调用此流程。然后它会回复请求一些输入。

我尝试实现的流程是主要流程的替代流程。它应该与用户联系(例如每天)。因此,它应该开始一个(不同于主要的)对话,要求输入他运动的时间,然后确认。

简而言之:

  • 主流程:用户输入 hi -> 机器人回复“你的年龄” -> 用户输入 28 -> 机器人回复“好的,谢谢”

  • 主动流程:机器人询问用户“您今天运动了多长时间?” -> 用户输入 1 小时 -> 机器人回复“好的,1 小时正确吗?” -> 用户点击"is"按钮 -> 机器人回复“谢谢,明天见”

最佳答案

您将需要为此工作设置一个 cron 作业以及主动消息传递。幸运的是,您已经可以引用一些帖子来了解如何执行这两项操作。

cron 作业将允许您设置一天中执行主动消息的时间。此堆栈溢出post讨论如何创建一个可以与您的机器人一起运行的简单项目。或者,您也可以在 Azure Function 内运行 cron 作业。同样,它会按照设定的时间表调用您的主动消息 API。

关于主动消息,请查看此 Stack Overflow post这详细介绍了设置此服务。特定于该用户问题的某些要点不适用于您,可以忽略。这个sample BotBuilder-Samples 存储库中的内容也可以作为一个很好的引用点。

希望得到帮助!

<小时/>

[编辑]

以下是调用 API 发送主动消息的基本设置,该消息还会启动特定的对话流。显然,您需要进行更改以满足您的需求,但这应该会让您走上正确的道路。

简而言之,会针对您的机器人公开的 API 进行调用,其中包含 conversationId 作为参数。当调用 API 时,会创建一个 conversationReference 并用于发送英雄卡。 Hero 卡会询问用户是否接受过训练(是/否),并在用户响应时发送 PostBack。 PostBack 是通过中断方法在组件对话框中监视的触发器。当匹配成功后,“主动”对话框就会开始。当用户结束时,“主动”对话框将从堆栈中弹出,然后用户返回到对话停止的地方(如果他们在对话中)。

请注意,主动消息需要 token 和对话 ID。用户之前需要与机器人进行过对话,或者您需要事先通过机器人生成 token 和对话 ID,然后将其与用户的 Slack user.id 一起发送以开始。

索引.js

const conversationReferences = {};

const dialog = new MainDialog( 'MainDialog', userState, conversationState );
const bot = new WelcomeBot( conversationState, userState, dialog, conversationReferences );

server.post( '/api/message', async ( req, res ) => {
[...]
}

server.get( '/api/notify/:conversationID', async ( req, res ) => {
  const { conversationID, query } = req.params;
  const conversationReference = conversationReferences[ conversationID ];

  await adapter.continueConversation( conversationReference, async turnContext => {  
    var reply = { type: ActivityTypes.Message };

    const yesBtn = { type: ActionTypes.PostBack, title: 'Yes', value: 'Yes' };
    const noBtn = { type: ActionTypes.PostBack, title: 'No', value: 'No' };

    const card = CardFactory.heroCard(
      'Have you trained today?',
      null,
      [ yesBtn, noBtn ]
    );

    reply.attachments = [ card ];

    await turnContext.sendActivity( reply );
    return { status: DialogTurnStatus.waiting };
  } );


  res.setHeader( 'Content-Type', 'text/html' );
  res.writeHead( 200 );
  res.write( '<html><body><h1>Proactive messages have been sent.</h1></body></html>' );
  res.end();
} );

mainDialog.js

const { DialogSet } = require( 'botbuilder-dialogs' );
const { InterruptionDialog} = require( './interruptionDialog' );

const MAIN_WATERFALL_DIALOG = 'MainWaterfallDialog';
const ADAPTIVE_CARD = 'AdaptiveCard';

class MainDialog extends CancelAndHelpDialog {
  constructor ( id, userState, conversationState ) {
    this.mainId = id;
    this.userState = userState;
    this.conversationState = conversationState;
    [...]
  };

  async run ( turnContext, accessor ) {
    const dialogSet = new DialogSet( accessor );
    this.id = this.mainId;
    dialogSet.add( this );

    const dialogContext = await dialogSet.createContext( turnContext );
    const results = await dialogContext.continueDialog();
    if ( results.status === DialogTurnStatus.empty ) {
      return await dialogContext.beginDialog( this.id );
    }
  };

  [...]
};

interruptionDialog.js

const { ProactiveDialog, PROACTIVE_DIALOG } = require( './proactiveDialog' );

class InterruptionDialog extends ComponentDialog {
  constructor ( id ) {
    super( id );
    this.addDialog( new ConfirmPrompt( 'ConfirmPrompt' ) );
    this.addDialog( new ProactiveDialog() );
  }

  async onBeginDialog ( innerDc, options ) {
    const result = await this.interrupt( innerDc );
    if ( result ) {
      return result;
    }
    return await super.onBeginDialog( innerDc, options );
  }

  async onContinueDialog ( innerDc ) {
    const result = await this.interrupt( innerDc );
    if ( result ) {
      return result;
    }
    return await super.onContinueDialog( innerDc );
  }

  async onEndDialog ( innerDc ) {
    const result = await this.interrupt( innerDc );
    if ( result ) {
      return result;
    }
    return await super.onEndDialog( innerDc );
  }

  async interrupt ( innerDc, next ) {
    if ( innerDc.context.activity.type === 'message' ) {
      if ( activity.channelId === 'slack' && activity.channelData.Payload ) {
        if ( activity.channelData.Payload.actions[ 0 ].name === 'postBack' ) {
          return await innerDc.beginDialog( PROACTIVE_DIALOG );          
        }
      } 
    }
  }
}

module.exports.InterruptionDialog = InterruptionDialog;

proactiveDialog.js

const {
  NumberPrompt,
  ComponentDialog,
  DialogTurnStatus,
  WaterfallDialog
} = require( 'botbuilder-dialogs' );

const PROACTIVE_DIALOG = 'proactiveDialog';
const WATERFALL_DIALOG = 'WATERFALL_DIALOG';
const NUMBER_PROMPT = 'NUMBER_PROMPT';

class ProactiveDialog extends ComponentDialog {
  constructor () {
    super( PROACTIVE_DIALOG );

    this.addDialog( new NumberPrompt( NUMBER_PROMPT ) );
    this.addDialog( new WaterfallDialog( WATERFALL_DIALOG, [
      this.didTrainStep.bind( this ),
      this.trainingStep.bind( this )
    ] ) );
    this.initialDialogId = WATERFALL_DIALOG;
  }

  async didTrainStep ( stepContext ) {
    const activity = stepContext.context.activity;
    const response = activity.channelData.Payload.actions[ 0 ].value.toLowerCase();
    if ( response === 'yes' ) {
      return await stepContext.prompt( NUMBER_PROMPT, 'Fantastic! How many minutes?' )
    } else if ( response === 'no' ) {
      await stepContext.context.sendActivity( 'Rubbish...serious rubbish.' )
    }
    return await stepContext.next();
  }

  async trainingStep ( stepContext ) {
    const activity = stepContext.context.activity;
    const stepResult = stepContext.result;
    const textResponse = activity.text.toLowerCase();

    if ( textResponse === 'no' ) {
      await stepContext.context.sendActivity( "I would recommend at least 5-10 mins of training." )
    } else
      if ( typeof ( stepResult ) === 'number' && stepResult > 0 ) {
        await stepContext.context.sendActivity( "I'll log that for you." );
      } else if ( stepResult <= 0 ) {
        await stepContext.context.sendActivity( "I can't log that value." )
      }
    return { status: DialogTurnStatus.complete }
  }
}
module.exports.ProactiveDialog = ProactiveDialog;
module.exports.PROACTIVE_DIALOG = PROACTIVE_DIALOG;

关于javascript - 处理提醒对话框的答案,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57715944/

相关文章:

javascript - 重置样式继承在 shadow dom 中不起作用

javascript - 允许用户在页面中输入 JavaScript 并与其他用户共享?

javascript - 如何在 iphone 上禁用 url 和底栏

javascript - 检测客户端断线WebSocket

performance - Node API 占用 100% CPU,node-tick-processor 输出看起来很神秘

javascript - Bot框架直线502错误

javascript - 如何找到页面的平均服务时间?

node.js - 尝试让 Node Express 应用程序运行

node.js - 机器人在网络聊天中工作,但在团队 channel 中不起作用

c# - 路易斯在两个意图之间做出选择