2016-11-08 4 views
3

Ich habe eine externe Anmeldung für meine BOT implementiert. Wenn externe Site Bot Bot CallBack-Methode aufruft, muss ich Token und Benutzername in PrivateConversationData setzen und dann den Chat mit einer Nachricht wie "Welcome back [username]!" fortsetzen.LUIS Bot-Framework wird nicht Aufruf von externen Aufruf

Um diese Nachricht anzuzeigen, sende ich eine MessageActivity, aber diese Aktivität verbindet sich nie mit meinem Chat und wird nicht die entsprechende [LuisIntent("UserIsAuthenticated")] auslösen.

Andere Absichten, aus Login-Flow, funktioniert wie erwartet.

Dies ist die Callback-Methode:

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)); 
    } 
} 

Dies ist der 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); 
    } 
} 

Dies ist, wie I-Komponenten registriert:

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(); 
    } 

Dies ist der 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); 
     } 
    } 
} 

Was ich vermisse?

aktualisieren

Auch mit ResumeAsync in Callback-Methode versucht:

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); 
} 

aber es mir den Fehler geben Operation is not valid due to the current state of the object.

Update 2

Ezequiel Nach Idee änderte ich meinen Code auf diese Weise:

[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 scheint in Ordnung zu sein:

enter image description here

aber await Conversation.ResumeAsync(resumptionCookie, message); weiter geben Sie mir den Fehler Operation is not valid due to the current state of the object.

+0

Können Sie Ihren MainDialog hinzufügen? –

+0

Sicher. Ich habe es hinzugefügt! – danyolgiax

+0

Kein Tippfehler hier? Mit zwei verschiedenen Wörtern: UserIsAuthenticated und UserAuthenticated – K48

Antwort

0

Ich habe herausgefunden, wie es funktioniert.

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

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:

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-Methode:

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); 
    } 
0

Sie müssen das Gespräch fortzusetzen mit Das ist der Grund, warum die Nachricht wahrscheinlich nicht ankommt.

Statt den Dialog Stapel verwenden, versuchen

await Conversation.ResumeAsync(resumptionCookie, message); 

auf Auth Bedürfnisse Je verwenden, können Sie AuthBot zu betrachten. Sie können auch einen Blick auf die logic auf dem OAuthCallback-Controller der Bibliothek werfen, um eine Vorstellung davon zu bekommen, wie sie die Konversation mit dem Bot nach der Authentifizierung fortsetzen.

Das ContosoFlowers Beispiel verwendet auch die resume conversation mechanism. Nicht für Authentifizierungszwecke, sondern um zu zeigen, wie mit einer hypothetischen Kreditkartenzahlung umgegangen wird.

+0

Wenn ich versuche mit 'ResumeAsync' anstelle des Dialog-Stacks, erhalte ich den folgenden Fehler:' Die Operation ist aufgrund des aktuellen Status des Objekts nicht gültig.' – danyolgiax

+0

Nun ist es schwer zu wissen, wo das Problem passiert; obwohl der Weg, es zu tun ist, den ResumeAsync zu verwenden, wie in den Beispielen gezeigt, die ich zur Verfügung stellte. Versuchen Sie, den gesamten Code um den Zustand zu kommentieren und lassen Sie einfach die reactionCookie.GetMessage und sehen, was passiert. Sind Sie sicher, dass die Werte, die Sie für die Neuerstellung des Cookies verwenden, die erwarteten sind? Normalerweise ist es besser, den Cookie zu serialisieren und zu deserialisieren (siehe Beispiele) –

+0

Die Frage wurde aktualisiert ... kein Glück! :( – danyolgiax

Verwandte Themen