场景

我有一个使用Bot Framework和一系列对话框构建的机器人。这些对话框之一为用户提供了通过向他们展示一个按钮来通过网页输入一些复杂数据的选项。单击按钮,然后将它们带到站点,填写数据,保存,然后定向回机器人。

我希望我的机器人暂停对话,直到它从我的网页接收到一个事件,通知我用户已保存数据,然后继续询问用户问题。

之前

我实现了一个版本,通过该版本,我可以在用户单击按钮之前存储一个ConversationReference,然后在发生外部事件时,从Webhook发送卡片和我想显示的下一条消息(不在对话框中),这很好,但是它变得非常复杂/混乱-我宁愿将整个应用程序保持在一个连续的对话框中。

理念1:使用DirectLine API

我进行了一些研究,许多人建议使用DirectLine API。所以我实现了这个:

public async Task SendEventAsync(InternalEventMessage message, ConversationReference reference) {
        var client = new DirectLineClient(!String.IsNullOrEmpty(_settings.DirectLineSecret) ? _settings.DirectLineSecret : null);
        if (_settings.SiteUrl.Contains("localhost")) {
            client.BaseUri = new Uri(_settings.DirectLineServiceUrl);
        }

        var eventMessage = Activity.CreateEventActivity();
        //Wrong way round?!?
        eventMessage.From = reference.Bot;
        eventMessage.Type = ActivityTypes.Event;
        eventMessage.Value = message;
        var conversation = await client.Conversations.PostActivityAsync(reference.Conversation.Id, eventMessage as Activity);
    }


这使用DirectLine客户端使用存储的ConversationReference将事件消息发送到serviceUrl,基本上是在模仿用户(机器人和用户在SDK中似乎是错误的做法)。检查本地主机是为了使DirectLine库指向仿真器服务器而不是https://directline.botframework.com

在我的对话框中,我称:

//method above shows input button and links to web page
context.Wait(WaitForAddressInput);
}

private async Task WaitForAddressInput(IDialogContext context, IAwaitable<IActivity> result) {
    var message = await result;
    switch (message.Type) {
        case ActivityTypes.Message:
            //TODO: Add response
            break;

        case ActivityTypes.Event:
            var eventMessage = message as IEventActivity;
            if (((JObject)eventMessage.Value).ToObject<InternalEventMessage>().Type == EventType.AddressInputComplete) {
                _addressResult = (await _tableService.ReadOrderById(Order.OrderId)).Address;
                await context.PostAsync($"Great}");
                context.Done(_addressResult);
            }
            break;
    }
}


显示按钮后,它将等待来自用户的任何消息,如果我们的事件匹配,则继续进行对话框。

这可以使用仿真器在本地工作,但令人沮丧的是,它没有生效。它无法识别通过网络聊天或Messenger创建的频道。此处说明:Microsoft Bot Framework DirectLine Can't Access Conversations


  出于安全原因,您不能使用DirectLine监视来自
  另一个对话。


因此,我无法访问尚未使用DirectLine创建的频道。

理念2:BotConnector

所以我想我会使用类似的代码尝试BotConnector:

public async Task SendEventAsync(InternalEventMessage message, Microsoft.Bot.Connector.DirectLine.ConversationReference reference) {
    var botAccount = new ChannelAccount(reference.User.Id, reference.User.Name);
    var userAccount = new ChannelAccount(reference.Bot.Id, reference.Bot.Name);

    MicrosoftAppCredentials.TrustServiceUrl(reference.ServiceUrl);
    var connector = new ConnectorClient(new Uri(reference.ServiceUrl), new MicrosoftAppCredentials("xxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxx"));

    connector.Credentials.InitializeServiceClient();

    var eventMessage = Activity.CreateMessageActivity();
    eventMessage.Recipient = botAccount;
    eventMessage.From = userAccount;
    eventMessage.Type = ActivityTypes.Event;
    eventMessage.Conversation = new ConversationAccount(id: reference.Conversation.Id);
    eventMessage.ServiceUrl = reference.ServiceUrl;
    eventMessage.Timestamp = DateTimeOffset.UtcNow;
    eventMessage.LocalTimestamp = DateTime.Now;
    eventMessage.ChannelId = reference.ChannelId;

    var result = await connector.Conversations.SendToConversationAsync(eventMessage as Microsoft.Bot.Connector.Activity);
}


这不会崩溃,并且我可以看到该事件出现在模拟器请求控制台中,但是什么也没有发生,因此它似乎被忽略了!

想法3:尝试模仿称为我的机器人的机器人服务

我还没有尝试过,因为我认为这可能是最耗时的,但是我在阅读here有关服务身份验证的信息,想知道是否有可能模仿托管的机器人服务发送消息并以此方式发送事件与所需的数据?

这似乎是一个相当普遍的情况,所以令我惊讶的是我还没有找到一种方法来做到这一点。如果有人对我如何从外部服务向我的机器人发送事件消息有其他想法,那么我很乐意听到。

更新:
请参阅下面Eric的答案,以了解我的所作所为。

最佳答案

想法1:

DirectLine是一个通道,而不是用于连接到通道的库。 (例如:您不会使用Facebook Messenger连接到Skype)DirectLineClient对于创建通过Direct Line连接器服务连接到DirectLine通道的客户端应用程序很有用。

想法2:

此方法应该起作用。实际上,BotAuth库对CallbackController中的MagicNumber登录流使用此方法:https://github.com/MicrosoftDX/botauth/blob/9a0a9f1b665f4aa95b6d60d09346dda90d8b314e/CSharp/BotAuth/Controllers/CallbackController.cs

对于您的方案,您应该能够构造类型为ActionTypes.OpenUrl的CardAction,该CardAction包含一个值,该值具有在URL中编码的ConversationReference。单击该按钮将调用一个显示页面的mvc控制器(将ConversationReference保存在cookie或其他内容中),当用户在页面上添加地址后,使用ConversationReference将事件发送给机器人(类似于BotAuth的恢复方式) CallbackController中的对话)。

想法3:

这将绕过连接器服务,而不是受支持的方案。您共享的链接详细说明了身份验证在Bot Framework中的工作方式,而不是如何绕过连接器服务。

07-26 09:05