2016-11-08 73 views
3

我爲我的BOT實施了外部登錄。當外部網站調用Bot CallBack方法時,我需要在PrivateConversationData中設置令牌和用戶名,然後通過類似"Welcome back [username]!"的消息繼續聊天。LUIS Bot框架將不會從外部調用調用意圖

要顯示此消息,我發送一個MessageActivity,但此活動永遠不會連接到我的聊天,並且不會觸發相應的[LuisIntent("UserIsAuthenticated")]

其他意圖從登錄流程中按預期工作。

這是回調方法:

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

這是控制器:

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

這是我註冊的組件:

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

這是對話框:

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

我想念什麼?

更新

在回調方法與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); 
} 

,但它給我的錯誤Operation is not valid due to the current state of the object.

更新2

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似乎是確定:

enter image description here

await Conversation.ResumeAsync(resumptionCookie, message);繼續給我的錯誤Operation is not valid due to the current state of the object.

+0

你可以添加你的MainDialog嗎? –

+0

當然。我加了! – danyolgiax

+0

這裏沒有錯別字?使用兩個不同的詞:UserIsAuthenticated和UserAuthenticated – K48

回答

0

我發現如何使它工作。

控制器:

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

回調方法:

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

您需要恢復與對話這就是爲什麼消息可能沒有到達的機器人。

而不是使用對話框棧,請嘗試使用

await Conversation.ResumeAsync(resumptionCookie, message); 

根據您的身份驗證需求,你可能要考慮AuthBot。您還可以查看庫的OAuthCallback控制器上的logic,以瞭解他們在身份驗證後如何恢復與Bot的對話。

ContosoFlowers例子,也是使用resume conversation mechanism。不是用於授權目的,而是爲了顯示如何處理低估的信用卡付款。

+0

如果我嘗試使用'ResumeAsync'而不是對話框堆棧,則會收到以下錯誤:'由於對象的當前狀態,操作無效。「 – danyolgiax

+0

很難知道問題出在哪裏;儘管實現它的方法是使用我提供的示例中所示的ResumeAsync。嘗試註釋掉狀態周圍的所有代碼,只留下resumptionCookie.GetMessage並查看會發生什麼。你確定你用來重新創建cookie的值是否是預期值?通常最好序列化cookie並反序列化它(請參閱示例) –

+0

更新了問題...沒有運氣! :( – danyolgiax