简体   繁体   English

LUIS Bot框架不会从外部调用调用Intent

[英]LUIS Bot framework won't call Intent from external call

I implemented an external login for my BOT. 我为我的BOT实现了外部登录。 When external site calls Bot CallBack method I need to set token and username in PrivateConversationData and then resume chat with a message like "Welcome back [username]!" 当外部站点调用Bot CallBack方法时,我需要在PrivateConversationData设置令牌和用户名,然后使用"Welcome back [username]!"等消息继续聊天"Welcome back [username]!" .

To display this message I send a MessageActivity but this activity never connects to my chat and won't fire the appropriate [LuisIntent("UserIsAuthenticated")] . 要显示此消息,我发送一个MessageActivity但此活动从未连接到我的聊天,并且不会触发相应的[LuisIntent("UserIsAuthenticated")]

Other intents, out of login-flow, works as expected. 登录流程之外的其他意图按预期工作。

This is the callback method: 这是回调方法:

public class OAuthCallbackController : ApiController
{
    [HttpGet]
    [Route("api/OAuthCallback")]
    public async Task OAuthCallback([FromUri] string userId, [FromUri] string botId, [FromUri] string conversationId,
        [FromUri] string channelId, [FromUri] string serviceUrl, [FromUri] string locale,
        [FromUri] CancellationToken cancellationToken, [FromUri] string accessToken, [FromUri] string username)
    {
        var resumptionCookie = new ResumptionCookie(TokenDecoder(userId), TokenDecoder(botId),
            TokenDecoder(conversationId), channelId, TokenDecoder(serviceUrl), locale);

            var container = WebApiApplication.FindContainer();

            var message = resumptionCookie.GetMessage();
            message.Text = "UserIsAuthenticated";

            using (var scope = DialogModule.BeginLifetimeScope(container, message))
            {
                var botData = scope.Resolve<IBotData>();
                await botData.LoadAsync(cancellationToken);

                botData.PrivateConversationData.SetValue("accessToken", accessToken);
                botData.PrivateConversationData.SetValue("username", username);

                ResumptionCookie pending;
                if (botData.PrivateConversationData.TryGetValue("persistedCookie", out pending))
                {
                    botData.PrivateConversationData.RemoveValue("persistedCookie");
                    await botData.FlushAsync(cancellationToken);
                }

                var stack = scope.Resolve<IDialogStack>();
                var child = scope.Resolve<MainDialog>(TypedParameter.From(message));
                var interruption = child.Void<object, IMessageActivity>();

                try
                {
                    stack.Call(interruption, null);

                    await stack.PollAsync(cancellationToken);
                }
                finally
                {
                    await botData.FlushAsync(cancellationToken);
                }
            }
        }
    }   

    public static string TokenDecoder(string token)
    {
        return Encoding.UTF8.GetString(HttpServerUtility.UrlTokenDecode(token));
    }
}

This is the controller: 这是控制器:

public class MessagesController : ApiController
{
    private readonly ILifetimeScope scope;

    public MessagesController(ILifetimeScope scope)
    {
        SetField.NotNull(out this.scope, nameof(scope), scope);
    }

    public async Task<HttpResponseMessage> Post([FromBody] Activity activity, CancellationToken token)
    {
        if (activity != null)
        {
            switch (activity.GetActivityType())
            {
                case ActivityTypes.Message:
                    using (var scope = DialogModule.BeginLifetimeScope(this.scope, activity))
                    {
                        var postToBot = scope.Resolve<IPostToBot>();
                        await postToBot.PostAsync(activity, token);
                    }
                    break;
            }
        }

        return new HttpResponseMessage(HttpStatusCode.Accepted);
    }
}

This is how I registered components: 这是我注册组件的方式:

protected override void Load(ContainerBuilder builder)
    {
        base.Load(builder);

        builder.Register(
            c => new LuisModelAttribute("myId", "SubscriptionKey"))
            .AsSelf()
            .AsImplementedInterfaces()
            .SingleInstance();

        builder.RegisterType<MainDialog>().AsSelf().As<IDialog<object>>().InstancePerDependency();

        builder.RegisterType<LuisService>()
            .Keyed<ILuisService>(FiberModule.Key_DoNotSerialize)
            .AsImplementedInterfaces()
            .SingleInstance();
    }

This is the dialog: 这是对话框:

[Serializable]
public sealed class MainDialog : LuisDialog<object>
{
    public static readonly string AuthTokenKey = "TestToken";
    public readonly ResumptionCookie ResumptionCookie;
    public static readonly Uri CloudocOauthCallback = new Uri("http://localhost:3980/api/OAuthCallback");

    public MainDialog(IMessageActivity activity, ILuisService luis)
        : base(luis)
    {
        ResumptionCookie = new ResumptionCookie(activity);
    }

    [LuisIntent("")]
    public async Task None(IDialogContext context, LuisResult result)
    {
        await context.PostAsync("Sorry cannot understand!");
        context.Wait(MessageReceived);
    }

    [LuisIntent("UserAuthenticated")]
    public async Task UserAuthenticated(IDialogContext context, LuisResult result)
    {
        string username;
        context.PrivateConversationData.TryGetValue("username", out username);

        await context.PostAsync($"Welcome back {username}!");
        context.Wait(MessageReceived);
    }

    [LuisIntent("Login")]
    private async Task LogIn(IDialogContext context, LuisResult result)
    {
        string token;
        if (!context.PrivateConversationData.TryGetValue(AuthTokenKey, out token))
        {
            context.PrivateConversationData.SetValue("persistedCookie", ResumptionCookie);

            var loginUrl = CloudocHelpers.GetLoginURL(ResumptionCookie, OauthCallback.ToString());

            var reply = context.MakeMessage();

            var cardButtons = new List<CardAction>();
            var plButton = new CardAction
            {
                Value = loginUrl,
                Type = ActionTypes.Signin,
                Title = "Connetti a Cloudoc"
            };
            cardButtons.Add(plButton);
            var plCard = new SigninCard("Connect", cardButtons);

            reply.Attachments = new List<Attachment>
            {
                plCard.ToAttachment()
            };

            await context.PostAsync(reply);
            context.Wait(MessageReceived);
        }
        else
        {
            context.Done(token);
        }
    }
}

What I miss? 我想念的是什么?

Update 更新

Also tried with ResumeAsync in callback method: 还在回调方法中尝试使用ResumeAsync

var container = WebApiApplication.FindContainer();

var message = resumptionCookie.GetMessage();
message.Text = "UserIsAuthenticated";

using (var scope = DialogModule.BeginLifetimeScope(container, message))
{
     var botData = scope.Resolve<IBotData>();
     await botData.LoadAsync(cancellationToken);

     botData.PrivateConversationData.SetValue("accessToken", accessToken);
     botData.PrivateConversationData.SetValue("username", username);

     ResumptionCookie pending;
     if (botData.PrivateConversationData.TryGetValue("persistedCookie", out pending))
     {
         botData.PrivateConversationData.RemoveValue("persistedCookie");
         await botData.FlushAsync(cancellationToken);
     }

     await Conversation.ResumeAsync(resumptionCookie, message, cancellationToken);
 }

but it give me the error Operation is not valid due to the current state of the object. 但它给我错误Operation is not valid due to the current state of the object.

Update 2 更新2

Following Ezequiel idea I changed my code this way: 按照Ezequiel的想法,我改变了我的代码:

    [HttpGet]
    [Route("api/OAuthCallback")]
    public async Task OAuthCallback(string state, [FromUri] string accessToken, [FromUri] string username)
    {
        var resumptionCookie = ResumptionCookie.GZipDeserialize(state);
        var message = resumptionCookie.GetMessage();
        message.Text = "UserIsAuthenticated";

        await Conversation.ResumeAsync(resumptionCookie, message);
    }

resumptionCookie seems to be ok: resumptionCookie似乎没问题:

在此输入图像描述

but await Conversation.ResumeAsync(resumptionCookie, message); await Conversation.ResumeAsync(resumptionCookie, message); continue to give me the error Operation is not valid due to the current state of the object. 继续给我错误Operation is not valid due to the current state of the object.

You need to resume the conversation with the bot that's why the message is likely not arriving. 您需要恢复与机器人的对话,这就是消息可能无法到达的原因。

Instead of using the dialog stack, try using 不要使用对话框堆栈,请尝试使用

await Conversation.ResumeAsync(resumptionCookie, message);

Depending on your auth needs, you might want to consider AuthBot . 根据您的身份验证需求,您可能需要考虑AuthBot You can also take a look to the logic on the OAuthCallback controller of the library to get an idea of how they are resuming the conversation with the Bot after auth. 您还可以查看库的OAuthCallback控制器上的逻辑 ,以了解它们在身份验证后如何恢复与Bot的对话。

The ContosoFlowers example, is also using the resume conversation mechanism . ContosoFlowers示例也使用了恢复会话机制 Not for auth purposes, but for showing how to handle a hypotethical credit card payment. 不是为了auth目的,而是为了展示如何处理一个低估的信用卡付款。

I found how to make it works. 我找到了如何使它工作。

Controller: 控制器:

public class MessagesController : ApiController
{
    public async Task<HttpResponseMessage> Post([FromBody] Activity activity, CancellationToken token)
    {
        if (activity != null)
        {
            switch (activity.GetActivityType())
            {
                case ActivityTypes.Message:

                    var container = WebApiApplication.FindContainer();

                    using (var scope = DialogModule.BeginLifetimeScope(container, activity))
                    {
                        await Conversation.SendAsync(activity, () => scope.Resolve<IDialog<object>>(), token);
                    }
                    break;
            }
        }
        return new HttpResponseMessage(HttpStatusCode.Accepted);
    }
}

Global.asax Global.asax中

public class WebApiApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        GlobalConfiguration.Configure(WebApiConfig.Register);

        var builder = new ContainerBuilder();

        builder.RegisterModule(new DialogModule());

        builder.RegisterModule(new MyModule());

        var config = GlobalConfiguration.Configuration;

        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

        builder.RegisterWebApiFilterProvider(config);

        var container = builder.Build();
        config.DependencyResolver = new AutofacWebApiDependencyResolver(container);
    }

    public static ILifetimeScope FindContainer()
    {
        var config = GlobalConfiguration.Configuration;
        var resolver = (AutofacWebApiDependencyResolver)config.DependencyResolver;
        return resolver.Container;
    }
}

MyModule: MyModule的:

public sealed class MyModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        base.Load(builder);

        builder.Register(
            c => new LuisModelAttribute("MyId", "SubId"))
            .AsSelf()
            .AsImplementedInterfaces()
            .SingleInstance();

        builder.RegisterType<MainDialog>().AsSelf().As<IDialog<object>>().InstancePerDependency();

        builder.RegisterType<LuisService>()
            .Keyed<ILuisService>(FiberModule.Key_DoNotSerialize)
            .AsImplementedInterfaces()
            .SingleInstance();
    }
}

Callback method: 回调方法:

public class OAuthCallbackController : ApiController
{

    [HttpGet]
    [Route("api/OAuthCallback")]
    public async Task OAuthCallback(string state, [FromUri] CancellationToken cancellationToken, [FromUri] string accessToken, [FromUri] string username)
    {
        var resumptionCookie = ResumptionCookie.GZipDeserialize(state);
        var message = resumptionCookie.GetMessage();
        message.Text = "UserIsAuthenticated";

        using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message))
        {
            var dataBag = scope.Resolve<IBotData>();
            await dataBag.LoadAsync(cancellationToken);

            dataBag.PrivateConversationData.SetValue("accessToken", accessToken);
            dataBag.PrivateConversationData.SetValue("username", username);

            ResumptionCookie pending;
            if (dataBag.PrivateConversationData.TryGetValue("persistedCookie", out pending))
            {
                dataBag.PrivateConversationData.RemoveValue("persistedCookie");
                await dataBag.FlushAsync(cancellationToken);
            }
        }

        await Conversation.ResumeAsync(resumptionCookie, message, cancellationToken);
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM