public static async Task VerifyScript(ILifetimeScope container, Func <IDialog <object> > makeRoot, bool proactive, StreamReader stream, Action <IDialogStack, string> extraCheck, string[] expected, string locale) { var toBot = DialogTestBase.MakeTestMessage(); if (!string.IsNullOrEmpty(locale)) { toBot.Locale = locale; } string input, label; int current = 0; while ((input = ReadLine(stream, out label)) != null) { var adapter = new TestAdapter(); await adapter.ProcessActivity((Activity)toBot, async (ctx) => { using (var scope = DialogModule.BeginLifetimeScope(container, ctx)) { var task = scope.Resolve <IPostToBot>(); var queue = adapter.ActiveQueue; Action <IDialogStack> check = (stack) => { var count = int.Parse((proactive && current == 0) ? input : stream.ReadLine()); Assert.AreEqual(count, queue.Count); for (var i = 0; i < count; ++i) { var toUser = queue.Dequeue(); var expectedOut = ReadLine(stream, out label); if (label == "ToUserText") { Assert.AreEqual(expectedOut, JsonConvert.SerializeObject(toUser.Text)); } else { Assert.AreEqual(expectedOut, JsonConvert.SerializeObject(toUser.Attachments)); } } extraCheck?.Invoke(stack, ReadLine(stream, out label)); }; Func <IDialog <object> > scriptMakeRoot = () => { return(makeRoot().Do(async(context, value) => context.PrivateConversationData.SetValue("result", JsonConvert.SerializeObject(await value)))); }; scope.Resolve <Func <IDialog <object> > >(TypedParameter.From(scriptMakeRoot)); if (proactive && current == 0) { var loop = scriptMakeRoot().Loop(); var data = scope.Resolve <IBotData>(); await data.LoadAsync(CancellationToken.None); var stack = scope.Resolve <IDialogTask>(); stack.Call(loop, null); await stack.PollAsync(CancellationToken.None); check(stack); input = ReadLine(stream, out label); } if (input.StartsWith("\"")) { try { toBot.Text = input.Substring(1, input.Length - 2); Assert.IsTrue(current < expected.Length && toBot.Text == expected[current++]); await task.PostAsync(toBot, CancellationToken.None); var data = scope.Resolve <IBotData>(); await data.LoadAsync(CancellationToken.None); var stack = scope.Resolve <IDialogStack>(); check(stack); } catch (Exception e) { Assert.AreEqual(ReadLine(stream, out label), e.Message); } } else if (label.ToLower().StartsWith("result")) { var data = scope.Resolve <IBotData>(); await data.LoadAsync(CancellationToken.None); string result; Assert.IsTrue(data.PrivateConversationData.TryGetValue("result", out result)); Assert.AreEqual(input.Trim(), result); } } }); } }
public async Task If_Root_Dialog_Throws_Propagate_Exception_Reset_Store() { const string userId = "testUser"; const string botId = "testBot"; var dialog = new Mock <IDialogThatFails>(MockBehavior.Loose); dialog .Setup(d => d.StartAsync(It.IsAny <IDialogContext>())) .Returns <IDialogContext>(async context => { context.Wait(dialog.Object.MessageReceived); }); dialog .Setup(d => d.MessageReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <Message> >())) .Returns <IDialogContext, IAwaitable <Message> >(async(context, result) => { context.Wait(dialog.Object.Throw); }); dialog .Setup(d => d.Throw(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <Message> >())) .Throws <ApplicationException>(); Func <IDialog <object> > MakeRoot = () => dialog.Object; var toBot = new Message() { From = new ChannelAccount { Id = userId }, ConversationId = Guid.NewGuid().ToString(), To = new ChannelAccount { Id = botId } }; using (new FiberTestBase.ResolveMoqAssembly(dialog.Object)) using (var container = Build(Options.MockConnectorFactory, dialog.Object)) { using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { DialogModule_MakeRoot.Register(scope, MakeRoot); var task = scope.Resolve <IPostToBot>(); await task.PostAsync(toBot, CancellationToken.None); } dialog.Verify(d => d.StartAsync(It.IsAny <IDialogContext>()), Times.Once); dialog.Verify(d => d.MessageReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <Message> >()), Times.Once); dialog.Verify(d => d.Throw(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <Message> >()), Times.Never); using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { DialogModule_MakeRoot.Register(scope, MakeRoot); var task = scope.Resolve <IDialogStack>(); Assert.AreNotEqual(0, task.Frames.Count); } using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { DialogModule_MakeRoot.Register(scope, MakeRoot); var task = scope.Resolve <IPostToBot>(); try { await task.PostAsync(toBot, CancellationToken.None); Assert.Fail(); } catch (ApplicationException) { } catch { Assert.Fail(); } } dialog.Verify(d => d.StartAsync(It.IsAny <IDialogContext>()), Times.Once); dialog.Verify(d => d.MessageReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <Message> >()), Times.Once); dialog.Verify(d => d.Throw(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <Message> >()), Times.Once); //make sure that data is persisted with connector using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { var connectorFactory = scope.Resolve <IConnectorClientFactory>(); var botData = await connectorFactory.Make().Bots.GetPerUserConversationDataAsync(botId, toBot.ConversationId, userId); toBot.BotPerUserInConversationData = botData.Data; } using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { DialogModule_MakeRoot.Register(scope, MakeRoot); var stack = scope.Resolve <IDialogStack>(); Assert.AreEqual(0, stack.Frames.Count); } using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { DialogModule_MakeRoot.Register(scope, MakeRoot); var task = scope.Resolve <IPostToBot>(); await task.PostAsync(toBot, CancellationToken.None); } dialog.Verify(d => d.StartAsync(It.IsAny <IDialogContext>()), Times.Exactly(2)); dialog.Verify(d => d.MessageReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <Message> >()), Times.Exactly(2)); dialog.Verify(d => d.Throw(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <Message> >()), Times.Once); } }
static async Task Interactive <T>(IDialog <T> form) where T : class { // NOTE: I use the DejaVuSansMono fonts as described here: http://stackoverflow.com/questions/21751827/displaying-arabic-characters-in-c-sharp-console-application // But you don't have to reboot. // If you don't want the multi-lingual support just comment this out Console.OutputEncoding = Encoding.GetEncoding(65001); var message = new Activity() { From = new ChannelAccount { Id = "ConsoleUser" }, Conversation = new ConversationAccount { Id = Guid.NewGuid().ToString() }, Recipient = new ChannelAccount { Id = "FormTest" }, ChannelId = "Console", ServiceUrl = "http://localhost:9000/", Text = "" }; var builder = new ContainerBuilder(); builder.RegisterModule(new DialogModule_MakeRoot()); builder.RegisterType <InMemoryDataStore>() .AsSelf() .SingleInstance(); builder.Register(c => new CachingBotDataStore(c.Resolve <InMemoryDataStore>(), CachingBotDataStoreConsistencyPolicy.ETagBasedConsistency)) .As <IBotDataStore <BotData> >() .AsSelf() .InstancePerLifetimeScope(); builder .Register(c => new BotToUserTextWriter(new BotToUserQueue(message, new Queue <IMessageActivity>()), Console.Out)) .As <IBotToUser>() .InstancePerLifetimeScope(); // Trace activities to debug output builder .RegisterType <TraceActivityLogger>() .AsImplementedInterfaces() .InstancePerLifetimeScope(); using (var container = builder.Build()) using (var scope = DialogModule.BeginLifetimeScope(container, message)) { Func <IDialog <object> > MakeRoot = () => form; DialogModule_MakeRoot.Register(scope, MakeRoot); var task = scope.Resolve <IPostToBot>(); await scope.Resolve <IBotData>().LoadAsync(default(CancellationToken)); var stack = scope.Resolve <IDialogTask>(); stack.Call(MakeRoot(), null); await stack.PollAsync(CancellationToken.None); while (true) { message.Timestamp = DateTime.UtcNow; message.Text = await Console.In.ReadLineAsync(); message.Locale = Locale; await task.PostAsync(message, CancellationToken.None); } } }
private async Task <Activity> HandleSystemMessage(Activity message) { if (message.Type == ActivityTypes.DeleteUserData) { // Implement user deletion here // If we handle user deletion, return a real message } else if (message.Type == ActivityTypes.Event && message.Name == "initializeBot") { IConversationUpdateActivity conversationupdate = message; using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var client = scope.Resolve <IConnectorClient>(); var reply = message.CreateReply(); reply.Text = "you asked: " + message.Value; reply.ReplyToId = message.Recipient.Id; await client.Conversations.ReplyToActivityAsync(reply); } } else if (message.Type == ActivityTypes.ConversationUpdate) { // Handle conversation state changes, like members being added and removed // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info // Not available in all channels IConversationUpdateActivity conversationupdate = message; using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var client = scope.Resolve <IConnectorClient>(); if (conversationupdate.MembersAdded.Any()) { var reply = message.CreateReply(); foreach (var newMember in conversationupdate.MembersAdded) { if (newMember.Id != message.Recipient.Id) { reply.Text = $"Welcome to the person bot {newMember.Name}! "; } else { reply.Text = $"Welcome to the person bot {message.From.Name}"; } reply.ReplyToId = message.Recipient.Id; await client.Conversations.ReplyToActivityAsync(reply); } } } } else if (message.Type == ActivityTypes.ContactRelationUpdate) { // Handle add/remove from contact lists // Activity.From + Activity.Action represent what happened } else if (message.Type == ActivityTypes.Typing) { // Handle knowing tha the user is typing } else if (message.Type == ActivityTypes.Ping) { } return(null); }
public async Task DialogTask_Forward() { var dialogOne = new Mock <IDialogFrames <string> >(MockBehavior.Loose); var dialogTwo = new Mock <IDialogFrames <string> >(MockBehavior.Loose); const string testMessage = "foo"; dialogOne .Setup(d => d.StartAsync(It.IsAny <IDialogContext>())) .Returns <IDialogContext>(async context => { context.Wait(dialogOne.Object.ItemReceived); }); dialogOne .Setup(d => d.ItemReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <Message> >())) .Returns <IDialogContext, IAwaitable <Message> >(async(context, message) => { var msg = await message; await context.Forward(dialogTwo.Object, dialogOne.Object.ItemReceived <string>, msg, CancellationToken.None); }); dialogOne .Setup(d => d.ItemReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <string> >())) .Returns <IDialogContext, IAwaitable <string> >(async(context, message) => { var msg = await message; Assert.AreEqual(testMessage, msg); context.Wait(dialogOne.Object.ItemReceived); }); dialogTwo .Setup(d => d.StartAsync(It.IsAny <IDialogContext>())) .Returns <IDialogContext>(async context => { context.Wait(dialogTwo.Object.ItemReceived); }); dialogTwo .Setup(d => d.ItemReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <Message> >())) .Returns <IDialogContext, IAwaitable <Message> >(async(context, message) => { var msg = await message; context.Done(msg.Text); }); Func <IDialog <object> > MakeRoot = () => dialogOne.Object; var toBot = MakeTestMessage(); using (new FiberTestBase.ResolveMoqAssembly(dialogOne.Object, dialogTwo.Object)) using (var container = Build(Options.None, dialogOne.Object, dialogTwo.Object)) { using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { DialogModule_MakeRoot.Register(scope, MakeRoot); var task = scope.Resolve <IPostToBot>(); toBot.Text = testMessage; await task.PostAsync(toBot, CancellationToken.None); dialogOne.Verify(d => d.StartAsync(It.IsAny <IDialogContext>()), Times.Once); dialogOne.Verify(d => d.ItemReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <Message> >()), Times.Once); dialogOne.Verify(d => d.ItemReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <string> >()), Times.Once); dialogTwo.Verify(d => d.StartAsync(It.IsAny <IDialogContext>()), Times.Once); dialogTwo.Verify(d => d.ItemReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <Message> >()), Times.Once); } } }
private async Task EchoDialogFlow(IDialog <object> echoDialog) { // arrange var toBot = DialogTestBase.MakeTestMessage(); toBot.From.Id = Guid.NewGuid().ToString(); toBot.Text = "Test"; Func <IDialog <object> > MakeRoot = () => echoDialog; using (new FiberTestBase.ResolveMoqAssembly(echoDialog)) using (var container = Build(Options.MockConnectorFactory | Options.ScopedQueue, echoDialog)) { // act: sending the message IMessageActivity toUser = await GetResponse(container, MakeRoot, toBot); // assert: check if the dialog returned the right response Assert.IsTrue(toUser.Text.StartsWith("1")); Assert.IsTrue(toUser.Text.Contains("Test")); // act: send the message 10 times for (int i = 0; i < 10; i++) { // pretend we're the intercom switch, and copy the bot data from message to message toBot.Text = toUser.Text; toUser = await GetResponse(container, MakeRoot, toBot); } // assert: check the counter at the end Assert.IsTrue(toUser.Text.StartsWith("11")); toBot.Text = "reset"; toUser = await GetResponse(container, MakeRoot, toBot); // checking if there is any cards in the attachment and promote the card.text to message.text if (toUser.Attachments != null && toUser.Attachments.Count > 0) { var card = (HeroCard)toUser.Attachments.First().Content; toUser.Text = card.Text; } Assert.IsTrue(toUser.Text.ToLower().Contains("are you sure")); toBot.Text = "yes"; toUser = await GetResponse(container, MakeRoot, toBot); Assert.IsTrue(toUser.Text.ToLower().Contains("reset count")); //send a random message and check count toBot.Text = "test"; toUser = await GetResponse(container, MakeRoot, toBot); Assert.IsTrue(toUser.Text.StartsWith("1")); toBot.Text = "/deleteprofile"; toUser = await GetResponse(container, MakeRoot, toBot); Assert.IsTrue(toUser.Text.ToLower().Contains("deleted")); using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { var botData = scope.Resolve <IBotData>(); await botData.LoadAsync(default(CancellationToken)); var stack = scope.Resolve <IDialogStack>(); Assert.AreEqual(0, stack.Frames.Count); } } }
public async Task <HttpResponseMessage> Callback([FromUri] string code, [FromUri] string state, CancellationToken cancellationToken) { try { // Use the state parameter to get correct IAuthProvider and ResumptionCookie var decoded = Encoding.UTF8.GetString(HttpServerUtility.UrlTokenDecode(state)); var queryString = HttpUtility.ParseQueryString(decoded); var assembly = Assembly.Load(queryString["providerassembly"]); var type = assembly.GetType(queryString["providertype"]); var providername = queryString["providername"]; IAuthProvider authProvider; if (type.GetConstructor(new Type[] { typeof(string) }) != null) { authProvider = (IAuthProvider)Activator.CreateInstance(type, providername); } else { authProvider = (IAuthProvider)Activator.CreateInstance(type); } // Get the conversation reference var conversationRef = UrlToken.Decode <ConversationReference>(queryString["conversationRef"]); var message = conversationRef.GetPostToBotMessage(); if (message.From.Id.Contains("sip:")) { message.From.Id = message.From.Id.Replace("sip:", ""); } using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { // Get the UserData from the original conversation IStateClient sc = scope.Resolve <IStateClient>(); BotData userData = await sc.BotState.GetUserDataAsync(message.ChannelId, message.From.Id); // Get Access Token using authorization code var authOptions = userData.GetProperty <AuthenticationOptions>($"{authProvider.Name}{ContextConstants.AuthOptions}"); var token = await authProvider.GetTokenByAuthCodeAsync(authOptions, code); // Generate magic number and attempt to write to userdata int magicNumber = GenerateRandomNumber(); bool writeSuccessful = false; uint writeAttempts = 0; while (!writeSuccessful && writeAttempts++ < MaxWriteAttempts) { try { userData.SetProperty($"{authProvider.Name}{ContextConstants.AuthResultKey}", token); userData.SetProperty($"{authProvider.Name}{ContextConstants.MagicNumberKey}", magicNumber); userData.SetProperty($"{authProvider.Name}{ContextConstants.MagicNumberValidated}", "false"); sc.BotState.SetUserData(message.ChannelId, message.From.Id, userData); writeSuccessful = true; } catch (HttpOperationException) { writeSuccessful = false; } } var resp = new HttpResponseMessage(HttpStatusCode.OK); if (!writeSuccessful) { message.Text = String.Empty; // fail the login process if we can't write UserData await Conversation.ResumeAsync(conversationRef, message); resp.Content = new StringContent("<html><body>Could not log you in at this time, please try again later</body></html>", System.Text.Encoding.UTF8, @"text/html"); } else { await Conversation.ResumeAsync(conversationRef, message); resp.Content = new StringContent($"<html><body>Almost done! Please copy this number and paste it back to your chat so your authentication can complete:<br/> <h1>{magicNumber}</h1>.</body></html>", System.Text.Encoding.UTF8, @"text/html"); } return(resp); } } catch (Exception ex) { // Callback is called with no pending message as a result the login flow cannot be resumed. return(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex)); } }
private async Task <Activity> HandleSystemMessage(Activity message) { if (message.Type == ActivityTypes.DeleteUserData) { // Implement user deletion here // If we handle user deletion, return a real message } else if (message.Type == ActivityTypes.ConversationUpdate) { // Handle conversation state changes, like members being added and removed // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info // Not available in all channels string welcomeMessage = "Welcome"; if (message.MembersAdded != null) { welcomeMessage += " " + message.MembersAdded.FirstOrDefault().Name.ToString(); } var msg = message.CreateReply(welcomeMessage); // await SendToConversationAsync(msg); } else if (message.Type == ActivityTypes.ContactRelationUpdate) { // Handle add/remove from contact lists // Activity.From + Activity.Action represent what happened } else if (message.Type == ActivityTypes.Typing) { // Handle knowing tha the user is typing } else if (message.Type == ActivityTypes.Ping) { } else if (message.Type == ActivityTypes.Event) { using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var cancellationToken = default(CancellationToken); var agentService = scope.Resolve <IAgentService>(); switch (message.AsEventActivity().Name) { case "connect": await agentService.RegisterAgentAsync(message, cancellationToken); break; case "disconnect": await agentService.UnregisterAgentAsync(message, cancellationToken); break; case "stopConversation": await StopConversation(agentService, message, cancellationToken); await agentService.RegisterAgentAsync(message, cancellationToken); break; default: break; } } } return(null); }
protected async void btnLogin_OnClick(object sender, EventArgs e) { try { phInfo.Visible = true; var state = JsonConvert.DeserializeObject <State>((string)Session["state"]); var token = "fakeToken"; if (!string.IsNullOrWhiteSpace(token)) { int magicNumber = GenerateRandomNumber(); // Create the message that is send to conversation to resume the login flow var message = state.ConversationReference.GetPostToUserMessage(); using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var authResult = new AuthResult(); authResult.AccessToken = token; authResult.UserName = txtUserId.Text; IStateClient sc = scope.Resolve <IStateClient>(); bool writeSuccessful = false; uint writeAttempts = 0; //將資料存回 botState 之中 while (!writeSuccessful && writeAttempts++ < MaxWriteAttempts) { try { var userData = sc.BotState.GetUserData(message.ChannelId, message.Recipient.Id); userData.SetProperty(ContextConstants.AuthResultKey, authResult); userData.SetProperty(ContextConstants.MagicNumberKey, magicNumber); userData.SetProperty(ContextConstants.MagicNumberValidated, "false"); sc.BotState.SetUserData(message.ChannelId, message.Recipient.Id, userData); writeSuccessful = true; } catch (HttpOperationException) { writeSuccessful = false; } } if (!writeSuccessful) { message.Text = String.Empty; // fail the login process if we can't write UserData await Conversation.ResumeAsync(state.ConversationReference, message); txtAlertMsg.Text = "無法登入,請再試一次,謝謝您!"; } else { await Conversation.ResumeAsync(state.ConversationReference, message); txtAlertMsg.Text = $"請將以下的{ContextConstants.MagicNumberLength}個數字驗證碼輸入到IM之中,以完成登入程序,謝謝您!<br/> <h1>{magicNumber}</h1>"; } } } else { // login fail txtAlertMsg.Text = "登入失敗! 請重新輸入! "; } } catch { txtAlertMsg.Text = "不是從 IM 進來的!"; } }
/// <summary> /// POST: api/Messages /// Receive a message from a user and reply to it /// </summary> //[ResponseType(typeof(void))] public async Task <HttpResponseMessage> Post([FromBody] Activity activity) { //trust Webchat & SMS channel MicrosoftAppCredentials.TrustServiceUrl(@"https://webchat.botframework.com", DateTime.MaxValue); MicrosoftAppCredentials.TrustServiceUrl(@"https://sms.botframework.com", DateTime.MaxValue); Trace.TraceInformation($"Incoming Activity is {activity.ToJson()}"); if (activity.Type == ActivityTypes.Message) { if (!string.IsNullOrEmpty(activity.Text)) { //detect language of input text var userLanguage = TranslationHandler.DetectLanguage(activity); //save user's LanguageCode to Azure Table Storage var message = activity as IMessageActivity; try { using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var botDataStore = scope.Resolve <IBotDataStore <BotData> >(); var key = new AddressKey() { BotId = message.Recipient.Id, ChannelId = message.ChannelId, UserId = message.From.Id, ConversationId = message.Conversation.Id, ServiceUrl = message.ServiceUrl }; var userData = await botDataStore.LoadAsync(key, BotStoreType.BotUserData, CancellationToken.None); var storedLanguageCode = userData.GetProperty <string>(StringConstants.UserLanguageKey); //update user's language in Azure Table Storage if (storedLanguageCode != userLanguage) { userData.SetProperty(StringConstants.UserLanguageKey, userLanguage); await botDataStore.SaveAsync(key, BotStoreType.BotUserData, userData, CancellationToken.None); await botDataStore.FlushAsync(key, CancellationToken.None); } } } catch (Exception ex) { throw ex; } //translate activity.Text to English before sending to LUIS for intent activity.Text = TranslationHandler.TranslateTextToDefaultLanguage(activity, userLanguage); await Conversation.SendAsync(activity, MakeRoot); } } else { HandleSystemMessage(activity); } var response = Request.CreateResponse(HttpStatusCode.OK); return(response); }
public async Task InMemoryBotDataStoreTest() { var chain = Chain.PostToChain().Select(m => m.Text).ContinueWith <string, string>(async(context, result) => { int t = 0; context.UserData.TryGetValue("count", out t); if (t > 0) { int value; Assert.IsTrue(context.ConversationData.TryGetValue("conversation", out value)); Assert.AreEqual(t - 1, value); Assert.IsTrue(context.UserData.TryGetValue("user", out value)); Assert.AreEqual(t + 1, value); Assert.IsTrue(context.PrivateConversationData.TryGetValue("PrivateConversationData", out value)); Assert.AreEqual(t + 2, value); } context.ConversationData.SetValue("conversation", t); context.UserData.SetValue("user", t + 2); context.PrivateConversationData.SetValue("PrivateConversationData", t + 3); context.UserData.SetValue("count", ++t); return(Chain.Return($"{t}:{await result}")); }).PostToUser(); Func <IDialog <object> > MakeRoot = () => chain; using (new FiberTestBase.ResolveMoqAssembly(chain)) using (var container = Build(Options.InMemoryBotDataStore, new MockConnectorFactory(new BotIdResolver("testBot")), chain)) { var msg = DialogTestBase.MakeTestMessage(); msg.Text = "test"; using (var scope = DialogModule.BeginLifetimeScope(container, msg)) { scope.Resolve <Func <IDialog <object> > >(TypedParameter.From(MakeRoot)); await Conversation.SendAsync(scope, msg); var reply = scope.Resolve <Queue <IMessageActivity> >().Dequeue(); Assert.AreEqual("1:test", reply.Text); var store = scope.Resolve <CachingBotDataStore_LastWriteWins>(); Assert.AreEqual(0, store.cache.Count); var dataStore = scope.Resolve <InMemoryDataStore>(); Assert.AreEqual(3, dataStore.store.Count); } for (int i = 0; i < 10; i++) { using (var scope = DialogModule.BeginLifetimeScope(container, msg)) { scope.Resolve <Func <IDialog <object> > >(TypedParameter.From(MakeRoot)); await Conversation.SendAsync(scope, msg); var reply = scope.Resolve <Queue <IMessageActivity> >().Dequeue(); Assert.AreEqual($"{i+2}:test", reply.Text); var store = scope.Resolve <CachingBotDataStore_LastWriteWins>(); Assert.AreEqual(0, store.cache.Count); var dataStore = scope.Resolve <InMemoryDataStore>(); Assert.AreEqual(3, dataStore.store.Count); string val = string.Empty; Assert.IsTrue(scope.Resolve <IBotData>().PrivateConversationData.TryGetValue(DialogModule.BlobKey, out val)); Assert.AreNotEqual(string.Empty, val); } } } }
private async Task <Activity> HandleSystemMessage(Activity message) { if (message.Type == ActivityTypes.DeleteUserData) { // Implement user deletion here // If we handle user deletion, return a real message } else if (message.Type == ActivityTypes.ConversationUpdate)// && message.ChannelId != "directline") { MicrosoftAppCredentials.TrustServiceUrl(message.ServiceUrl); IConversationUpdateActivity iConversationUpdated = message as IConversationUpdateActivity; if (iConversationUpdated != null) { ConnectorClient connector = new ConnectorClient(new System.Uri(message.ServiceUrl)); foreach (var member in iConversationUpdated.MembersAdded ?? System.Array.Empty <ChannelAccount>()) { if (member.Id == iConversationUpdated.Recipient.Id && message.ChannelId != "directline") { var cardText = await CardService.GetCardAttachment("FirstWelcome"); var reply = RootDialog.GetCardReply(message, cardText); await connector.Conversations.ReplyToActivityAsync(reply); } } } } else if (message.Type == ActivityTypes.ContactRelationUpdate) { // Handle add/remove from contact lists // Activity.From + Activity.Action represent what happened } else if (message.Type == ActivityTypes.Typing) { // Handle knowing tha the user is typing } else if (message.Type == ActivityTypes.Ping) { } else if (message.Type == ActivityTypes.Event) { using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var cancellationToken = default(CancellationToken); var agentService = scope.Resolve <IAgentService>(); switch (message.AsEventActivity().Name) { case "connect": await agentService.RegisterAgentAsync(message, cancellationToken); break; case "disconnect": await agentService.UnregisterAgentAsync(message, cancellationToken); break; case "stopConversation": await StopConversation(agentService, message, cancellationToken); await agentService.RegisterAgentAsync(message, cancellationToken); break; default: break; } } } return(null); }
/// <summary> /// POST: api/Messages /// Receive a message from a user and reply to it /// </summary> public async Task <HttpResponseMessage> Post([FromBody] Activity activity) { if (activity.Type == ActivityTypes.Message) { //to show user that bot is typing //var connector = new ConnectorClient(new Uri(activity.ServiceUrl)); //Activity isTypingReply = activity.CreateReply(); //isTypingReply.Type = ActivityTypes.Typing; //isTypingReply.Text = null; //await connector.Conversations.ReplyToActivityAsync(isTypingReply); if (!string.IsNullOrEmpty(activity.Text)) { var userLanguage = TranslationHandler.DetectLanguage(activity); //save user's LanguageCode to Azure Storage var message = activity as IMessageActivity; try { using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var botDataStore = scope.Resolve <IBotDataStore <BotData> >(); var key = new AddressKey() { BotId = message.Recipient.Id, ChannelId = message.ChannelId, UserId = message.From.Id, ConversationId = message.Conversation.Id, ServiceUrl = message.ServiceUrl }; var userData = await botDataStore.LoadAsync(key, BotStoreType.BotUserData, CancellationToken.None); var storedLanguageCode = userData.GetProperty <string>(StringConstants.UserLanguageKey); //update user's language in Azure Storage if (storedLanguageCode != userLanguage) { userData.SetProperty(StringConstants.UserLanguageKey, userLanguage); await botDataStore.SaveAsync(key, BotStoreType.BotUserData, userData, CancellationToken.None); await botDataStore.FlushAsync(key, CancellationToken.None); } } } catch (Exception ex) { throw ex; } activity.Text = TranslationHandler.TranslateTextToDefaultLanguage(activity, userLanguage); } //await Conversation.SendAsync(activity, MakeRootDialog); //await Conversation.SendAsync(activity, () => new RootDialog()); //await Conversation.SendAsync(activity, () => new Dialogs.SimpleQnADialog()); await SendAsync(activity, (scope) => new RootDialog(scope.Resolve <IUserToAgent>())); } else { await HandleSystemMessage(activity); } var response = Request.CreateResponse(HttpStatusCode.OK); return(response); }
public static async Task RecordScript(ILifetimeScope container, bool proactive, StreamWriter stream, Func <string> extraInfo, params string[] inputs) { var toBot = MakeTestMessage(); var adapter = new TestAdapter(); await adapter.ProcessActivity((Activity)toBot, async (ctx) => { using (var containerScope = DialogModule.BeginLifetimeScope(container, ctx)) { var task = containerScope.Resolve <IPostToBot>(); var queue = adapter.ActiveQueue; Action drain = () => { stream.WriteLine($"{queue.Count()}"); while (queue.Count > 0) { var toUser = queue.Dequeue(); if (!string.IsNullOrEmpty(toUser.Text)) { stream.WriteLine($"ToUserText:{JsonConvert.SerializeObject(toUser.Text)}"); } else { stream.WriteLine($"ToUserButtons:{JsonConvert.SerializeObject(toUser.Attachments)}"); } } }; string result = null; var root = containerScope.Resolve <IDialog <object> >().Do(async(context, value) => result = JsonConvert.SerializeObject(await value)); using (var innerScope = containerScope.BeginLifetimeScope( async(builder) => { if (proactive) { var loop = root.Loop(); var data = containerScope.Resolve <IBotData>(); await data.LoadAsync(CancellationToken.None); var stack = containerScope.Resolve <IDialogTask>(); stack.Call(loop, null); await stack.PollAsync(CancellationToken.None); drain(); } else { builder .RegisterInstance(root) .AsSelf() .As <IDialog <object> >(); } })) { foreach (var input in inputs) { stream.WriteLine($"FromUser:{JsonConvert.SerializeObject(input)}"); toBot.Text = input; try { await task.PostAsync(toBot, CancellationToken.None); drain(); if (extraInfo != null) { var extra = extraInfo(); stream.WriteLine(extra); } } catch (Exception e) { stream.WriteLine($"Exception:{e.Message}"); } } if (result != null) { stream.WriteLine($"Result: {result}"); } } } }); }
public async Task <HttpResponseMessage> OAuthCallback([FromUri] string code, [FromUri] string state) { try { object tokenCache = null; if (string.Equals(AuthSettings.Mode, "v1", StringComparison.OrdinalIgnoreCase)) { tokenCache = new Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache(); } else if (string.Equals(AuthSettings.Mode, "v2", StringComparison.OrdinalIgnoreCase)) { tokenCache = new Microsoft.Identity.Client.TokenCache(); } else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase)) { } // Get the resumption cookie var resumptionCookie = UrlToken.Decode <ResumptionCookie>(state); // Create the message that is send to conversation to resume the login flow var message = resumptionCookie.GetMessage(); using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var client = scope.Resolve <IConnectorClient>(); AuthResult authResult = null; if (string.Equals(AuthSettings.Mode, "v1", StringComparison.OrdinalIgnoreCase)) { // Exchange the Auth code with Access token var token = await AzureActiveDirectoryHelper.GetTokenByAuthCodeAsync(code, (Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache) tokenCache); authResult = token; } else if (string.Equals(AuthSettings.Mode, "v2", StringComparison.OrdinalIgnoreCase)) { //TODO: Scopes definition here // Exchange the Auth code with Access token var token = await AzureActiveDirectoryHelper.GetTokenByAuthCodeAsync(code, (Microsoft.Identity.Client.TokenCache) tokenCache, new string[] { "User.Read" }); authResult = token; } else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase)) { } var reply = await Conversation.ResumeAsync(resumptionCookie, message); var data = await client.Bots.GetPerUserConversationDataAsync(resumptionCookie.BotId, resumptionCookie.ConversationId, resumptionCookie.UserId); reply.SetBotUserData(ContextConstants.AuthResultKey, authResult); int magicNumber = GenerateRandomNumber(); reply.SetBotUserData(ContextConstants.MagicNumberKey, magicNumber); reply.SetBotUserData(ContextConstants.MagicNumberValidated, "false"); //data.SetProperty(ContextConstants.AuthResultKey, authResult); //data.SetProperty(ContextConstants.MagicNumberKey, magicNumber); //data.SetProperty(ContextConstants.MagicNumberValidated, "false"); //await client.Bots.SetUserDataAsync(resumptionCookie.BotId, resumptionCookie.UserId, data); reply.To = message.From; reply.From = message.To; await client.Messages.SendMessageAsync(reply); var resp = new HttpResponseMessage(HttpStatusCode.OK); resp.Content = new StringContent($"<html><body>Almost done! Please copy this number and paste it back to your chat so your authentication can complete: {magicNumber}.</body></html>", System.Text.Encoding.UTF8, @"text/html"); return(resp); } } catch (Exception ex) { // Callback is called with no pending message as a result the login flow cannot be resumed. return(Request.CreateErrorResponse(HttpStatusCode.BadRequest, new InvalidOperationException("Cannot resume!"))); } }
private async Task HandleSystemMessageAsync(Activity message, ConnectorClient connectorClient, CancellationToken cancellationToken) { if (message.Type == ActivityTypes.DeleteUserData) { // Implement user deletion here // If we handle user deletion, return a real message } else if (message.Type == ActivityTypes.ConversationUpdate) { // This shows how to send a welcome message in response to a conversationUpdate event // We're only interested in member added events if (message.MembersAdded?.Count > 0) { // Determine if the bot was added to the team/conversation var botId = message.Recipient.Id; var botWasAdded = message.MembersAdded.Any(member => member.Id == botId); // Create the welcome message to send Activity welcomeMessage = message.CreateReply(); welcomeMessage.Text = Strings.BotWelcomeMessage; if (!(message.Conversation.IsGroup ?? false)) { // 1:1 conversation event // If the user hasn't received a first-run message yet, then send a message to the user // introducing your bot and what it can do. Do NOT send this blindly, as your bot can receive // spurious conversationUpdate events, especially if you use proactive messaging. using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var address = Address.FromActivity(message); var botDataStore = scope.Resolve <IBotDataStore <BotData> >(); var botData = await botDataStore.LoadAsync(address, BotStoreType.BotUserData, cancellationToken); if (!botData.GetProperty <bool>("IsFreSent")) { await connectorClient.Conversations.ReplyToActivityWithRetriesAsync(welcomeMessage, cancellationToken); // Remember that we sent the welcome message already botData.SetProperty("IsFreSent", true); await botDataStore.SaveAsync(address, BotStoreType.BotUserData, botData, cancellationToken); } else { // First-run message has already been sent, so skip sending it again. // Do not remove the check for IsFreSent above. Your bot can receive spurious conversationUpdate // activities from chat service, so if you always respond to all of them, you will send random // welcome messages to users who have already received the welcome. } } } else { // Not 1:1 chat event (bot or user was added to a team or group chat) if (botWasAdded) { // Bot was added to the team // Send a message to the team's channel, introducing your bot and what you can do await connectorClient.Conversations.ReplyToActivityWithRetriesAsync(welcomeMessage, cancellationToken); } else { // Other users were added to the team/conversation } } } } else if (message.Type == ActivityTypes.ContactRelationUpdate) { // Handle add/remove from contact lists // Activity.From + Activity.Action represent what happened } else if (message.Type == ActivityTypes.Typing) { // Handle knowing that the user is typing } else if (message.Type == ActivityTypes.Ping) { } }
public override void Init() { dm = GameMain.GetInstance().GetModule <DialogModule> (); }
/// <summary> /// POST: api/Messages /// Receive a message from a user and reply to it /// </summary> public async Task <HttpResponseMessage> Post([FromBody] Activity activity, CancellationToken cancellationToken) { var connectorClient = new ConnectorClient(new Uri(activity.ServiceUrl)); if (activity.Type == ActivityTypes.Message) { // Special handling for a command to simulate a reset of the bot chat if (!(activity.Conversation.IsGroup ?? false) && (activity.Text == "/resetbotchat")) { return(await HandleResetBotChatAsync(activity, cancellationToken)); } //Set the Locale for Bot activity.Locale = TemplateUtility.GetLocale(activity); //Strip At mention from incoming request text activity = Middleware.StripAtMentionText(activity); //Convert incoming activity text to lower case, to match the intent irrespective of incoming text case activity = Middleware.ConvertActivityTextToLower(activity); //Set the OFFICE_365_TENANT_FILTER key in web.config file with Tenant Information //Validate bot for specific teams tenant if any if (Middleware.RejectMessageBasedOnTenant(activity, activity.GetTenantId())) { Activity replyActivity = activity.CreateReply(); replyActivity.Text = Strings.TenantLevelDeniedAccess; await connectorClient.Conversations.ReplyToActivityAsync(replyActivity); return(Request.CreateResponse(HttpStatusCode.OK)); } await Conversation.SendAsync(activity, () => new Dialogs.RootDialog()); } else if (activity.Type == ActivityTypes.MessageReaction) { var reactionsAdded = activity.ReactionsAdded; var reactionsRemoved = activity.ReactionsRemoved; var replytoId = activity.ReplyToId; Activity reply; if (reactionsAdded != null && reactionsAdded.Count > 0) { reply = activity.CreateReply(Strings.LikeMessage); await connectorClient.Conversations.ReplyToActivityAsync(reply); } else if (reactionsRemoved != null && reactionsRemoved.Count > 0) { reply = activity.CreateReply(Strings.RemoveLike); await connectorClient.Conversations.ReplyToActivityAsync(reply); } return(Request.CreateResponse(HttpStatusCode.OK)); } else if (activity.Type == ActivityTypes.Invoke) // Received an invoke { // Handle ComposeExtension query if (activity.IsComposeExtensionQuery()) { WikipediaComposeExtension wikipediaComposeExtension = new WikipediaComposeExtension(); HttpResponseMessage httpResponse = null; using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity)) { var botDataStore = scope.Resolve <IBotDataStore <BotData> >(); // Handle compose extension selected item if (activity.Name == "composeExtension/selectItem") { // This handler is used to process the event when a user in Teams selects wiki item from wiki result ComposeExtensionResponse selectedItemResponse = await wikipediaComposeExtension.HandleComposeExtensionSelectedItem(activity, botDataStore); httpResponse = Request.CreateResponse <ComposeExtensionResponse>(HttpStatusCode.OK, selectedItemResponse); } else { // Handle the wiki compose extension request and returned the wiki result response ComposeExtensionResponse composeExtensionResponse = await wikipediaComposeExtension.GetComposeExtensionResponse(activity, botDataStore); httpResponse = Request.CreateResponse <ComposeExtensionResponse>(HttpStatusCode.OK, composeExtensionResponse); } var address = Address.FromActivity(activity); await botDataStore.FlushAsync(address, CancellationToken.None); } return(httpResponse); } //Actionable Message else if (activity.IsO365ConnectorCardActionQuery()) { // this will handle the request coming any action on Actionable messages return(await HandleO365ConnectorCardActionQuery(activity)); } //PopUp SignIn else if (activity.Name == "signin/verifyState") { // this will handle the request coming from PopUp SignIn return(await PopUpSignInHandler(activity)); } // Handle rest of the invoke request else { var messageActivity = (IMessageActivity)null; //this will parse the invoke value and change the message activity as well messageActivity = InvokeHandler.HandleInvokeRequest(activity); await Conversation.SendAsync(messageActivity, () => new Dialogs.RootDialog()); return(Request.CreateResponse(HttpStatusCode.OK)); } } else { await HandleSystemMessageAsync(activity, connectorClient, cancellationToken); } var response = Request.CreateResponse(HttpStatusCode.OK); return(response); }
public async Task AlarmDialogFlow() { var luis = new Mock <ILuisService>(); // arrange var now = DateTime.UtcNow; var entityTitle = EntityFor(SimpleAlarmBot.SimpleAlarmDialog.Entity_Alarm_Title, "title"); var entityDate = EntityFor(SimpleAlarmBot.SimpleAlarmDialog.Entity_Alarm_Start_Date, now.ToString("d", DateTimeFormatInfo.InvariantInfo)); var entityTime = EntityFor(SimpleAlarmBot.SimpleAlarmDialog.Entity_Alarm_Start_Time, now.ToString("t", DateTimeFormatInfo.InvariantInfo)); Func <IDialog <object> > MakeRoot = () => new SimpleAlarmBot.SimpleAlarmDialog(luis.Object); var toBot = new Message() { ConversationId = Guid.NewGuid().ToString() }; using (new FiberTestBase.ResolveMoqAssembly(luis.Object)) using (var container = Build(Options.ScopedQueue, luis.Object)) { using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { var task = scope.Resolve <IDialogTask>(); // arrange SetupLuis(luis, a => a.SetAlarm(null, null), entityTitle, entityDate, entityTime); // act await task.PostAsync(toBot, MakeRoot); // assert luis.VerifyAll(); AssertMentions("created", scope); } using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { var task = scope.Resolve <IDialogTask>(); // arrange SetupLuis(luis, a => a.FindAlarm(null, null), entityTitle); // act await task.PostAsync(toBot, MakeRoot); // assert luis.VerifyAll(); AssertMentions("found", scope); } using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { var task = scope.Resolve <IDialogTask>(); // arrange SetupLuis(luis, a => a.AlarmSnooze(null, null), entityTitle); // act await task.PostAsync(toBot, MakeRoot); // assert luis.VerifyAll(); AssertMentions("snoozed", scope); } using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { var task = scope.Resolve <IDialogTask>(); // arrange SetupLuis(luis, a => a.TurnOffAlarm(null, null), entityTitle); // act await task.PostAsync(toBot, MakeRoot); // assert luis.VerifyAll(); AssertMentions("sure", scope); } using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { var task = scope.Resolve <IDialogTask>(); // arrange toBot.Text = "blah"; // act await task.PostAsync(toBot, MakeRoot); // assert luis.VerifyAll(); AssertMentions("sure", scope); } using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { var task = scope.Resolve <IDialogTask>(); // arrange toBot.Text = "yes"; // act await task.PostAsync(toBot, MakeRoot); // assert luis.VerifyAll(); AssertMentions("disabled", scope); } using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { var task = scope.Resolve <IDialogTask>(); // arrange SetupLuis(luis, a => a.DeleteAlarm(null, null), entityTitle); // act await task.PostAsync(toBot, MakeRoot); // assert luis.VerifyAll(); AssertMentions("did not find", scope); } } }
private async Task <Activity> HandleSystemMessage(Activity message) { if (message.Type == ActivityTypes.DeleteUserData) { // Implement user deletion here // If we handle user deletion, return a real message } else if (message.Type == ActivityTypes.ConversationUpdate) { IConversationUpdateActivity update = message; using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var client = scope.Resolve <IConnectorClient>(); if (update.MembersAdded.Any()) { var reply = message.CreateReply(); foreach (var newMember in update.MembersAdded) { //var reply1 = message.CreateReply(); //reply1.Text = "new member" + newMember.Name.ToLower(); //await client.Conversations.ReplyToActivityAsync(reply1); // the bot is always added as a user of the conversation, since we don't // want to display the adaptive card twice ignore the conversation update triggered by the bot if (newMember.Name.ToLower() != "you") { try { // read the json in from our file string json = File.ReadAllText(HttpContext.Current.Request.MapPath("~\\AdaptiveCards\\MyCard.json")); // use Newtonsofts JsonConvert to deserialized the json into a C# AdaptiveCard object AdaptiveCards.AdaptiveCard card = JsonConvert.DeserializeObject <AdaptiveCards.AdaptiveCard>(json); // put the adaptive card as an attachment to the reply message reply.Attachments.Add(new Attachment { ContentType = AdaptiveCard.ContentType, Content = card }); } catch (Exception e) { // if an error occured add the error text as the message reply.Text = e.Message; } await client.Conversations.ReplyToActivityAsync(reply); } } } } } else if (message.Type == ActivityTypes.ContactRelationUpdate) { // Handle add/remove from contact lists // Activity.From + Activity.Action represent what happened } else if (message.Type == ActivityTypes.Typing) { // Handle knowing tha the user is typing } else if (message.Type == ActivityTypes.Ping) { } else if (message.Type == ActivityTypes.Event) { using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var cancellationToken = default(CancellationToken); var agentService = scope.Resolve <IAgentService>(); switch (message.AsEventActivity().Name) { case "connect": await agentService.RegisterAgentAsync(message, cancellationToken); break; case "disconnect": await agentService.UnregisterAgentAsync(message, cancellationToken); break; case "stopConversation": await StopConversation(agentService, message, cancellationToken); await agentService.RegisterAgentAsync(message, cancellationToken); break; default: break; } } } return(null); }
private async Task HandleSystemMessage(Activity message) { if (message.Type == ActivityTypes.DeleteUserData) { // Implement user deletion here // If we handle user deletion, return a real message } else if (message.Type == ActivityTypes.ConversationUpdate) { // Handle conversation state changes, like members being added and removed // Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info // Not available in all channels IConversationUpdateActivity update = message; using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var client = scope.Resolve <IConnectorClient>(); if (update.MembersAdded.Any()) { var reply = message.CreateReply(); foreach (var newMember in update.MembersAdded) { // the bot is always added as a user of the conversation, since we don't // want to display the adaptive card twice ignore the conversation update triggered by the bot if (newMember.Name.ToLower() != "bot") { try { // read the json in from our file //string json = File.ReadAllText(HttpContext.Current.Request.MapPath("~\\AdaptiveCards\\MyCard.json")); // use Newtonsofts JsonConvert to deserialized the json into a C# AdaptiveCard object //AdaptiveCards.AdaptiveCard card = JsonConvert.DeserializeObject<AdaptiveCards.AdaptiveCard>(json); // put the adaptive card as an attachment to the reply message //reply.Attachments.Add(new Attachment //{ // ContentType = AdaptiveCard.ContentType, // Content = card //}); reply.Text = "Welcome message from Bot"; } catch (Exception e) { // if an error occured add the error text as the message reply.Text = e.Message; } await client.Conversations.ReplyToActivityAsync(reply); } } } } } else if (message.Type == ActivityTypes.ContactRelationUpdate) { // Handle add/remove from contact lists // Activity.From + Activity.Action represent what happened } else if (message.Type == ActivityTypes.Typing) { // Handle knowing tha the user is typing } else if (message.Type == ActivityTypes.Ping) { } }
public async Task <HttpResponseMessage> OAuthCallback([FromUri] string code, [FromUri] string state, CancellationToken cancellationToken) { try { object tokenCache = null; if (string.Equals(AuthSettings.Mode, "v1", StringComparison.OrdinalIgnoreCase)) { tokenCache = new Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache(); } else if (string.Equals(AuthSettings.Mode, "v2", StringComparison.OrdinalIgnoreCase)) { tokenCache = new Microsoft.Identity.Client.TokenCache(); } else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase)) { } // Get the resumption cookie var resumptionCookie = UrlToken.Decode <ResumptionCookie>(state); // Create the message that is send to conversation to resume the login flow var message = resumptionCookie.GetMessage(); using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var client = scope.Resolve <IConnectorClient>(); AuthResult authResult = null; if (string.Equals(AuthSettings.Mode, "v1", StringComparison.OrdinalIgnoreCase)) { // Exchange the Auth code with Access token var token = await AzureActiveDirectoryHelper.GetTokenByAuthCodeAsync(code, (Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache) tokenCache); authResult = token; } else if (string.Equals(AuthSettings.Mode, "v2", StringComparison.OrdinalIgnoreCase)) { // Exchange the Auth code with Access token var token = await AzureActiveDirectoryHelper.GetTokenByAuthCodeAsync(code, (Microsoft.Identity.Client.TokenCache) tokenCache, Models.AuthSettings.Scopes); authResult = token; } else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase)) { } IStateClient sc = scope.Resolve <IStateClient>(); //IMPORTANT: DO NOT REMOVE THE MAGIC NUMBER CHECK THAT WE DO HERE. THIS IS AN ABSOLUTE SECURITY REQUIREMENT //REMOVING THIS WILL REMOVE YOUR BOT AND YOUR USERS TO SECURITY VULNERABILITIES. //MAKE SURE YOU UNDERSTAND THE ATTACK VECTORS AND WHY THIS IS IN PLACE. var dataBag = scope.Resolve <IBotData>(); await dataBag.LoadAsync(cancellationToken); int magicNumber = GenerateRandomNumber(); dataBag.UserData.SetValue(ContextConstants.AuthResultKey, authResult); dataBag.UserData.SetValue(ContextConstants.MagicNumberKey, magicNumber); dataBag.UserData.SetValue(ContextConstants.MagicNumberValidated, "false"); await dataBag.FlushAsync(cancellationToken); await Conversation.ResumeAsync(resumptionCookie, message); var resp = new HttpResponseMessage(HttpStatusCode.OK); resp.Content = new StringContent($"<html><body>Almost done! Please copy this number and paste it back to your chat so your authentication can complete: {magicNumber}.</body></html>", System.Text.Encoding.UTF8, @"text/html"); return(resp); } } catch (Exception ex) { // Callback is called with no pending message as a result the login flow cannot be resumed. return(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex)); } }
public async Task If_Root_Dialog_Throws_Propagate_Exception_Reset_Store() { var dialog = new Mock <IDialogThatFails>(MockBehavior.Loose); dialog .Setup(d => d.StartAsync(It.IsAny <IDialogContext>())) .Returns <IDialogContext>(async context => { context.Wait(dialog.Object.MessageReceived); }); dialog .Setup(d => d.MessageReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <IMessageActivity> >())) .Returns <IDialogContext, IAwaitable <IMessageActivity> >(async(context, result) => { context.Wait(dialog.Object.Throw); }); dialog .Setup(d => d.Throw(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <IMessageActivity> >())) .Throws <ApplicationException>(); Func <IDialog <object> > MakeRoot = () => dialog.Object; var toBot = DialogTestBase.MakeTestMessage(); using (new FiberTestBase.ResolveMoqAssembly(dialog.Object)) using (var container = Build(Options.MockConnectorFactory, dialog.Object)) { using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { DialogModule_MakeRoot.Register(scope, MakeRoot); var task = scope.Resolve <IPostToBot>(); await task.PostAsync(toBot, CancellationToken.None); } dialog.Verify(d => d.StartAsync(It.IsAny <IDialogContext>()), Times.Once); dialog.Verify(d => d.MessageReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <IMessageActivity> >()), Times.Once); dialog.Verify(d => d.Throw(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <IMessageActivity> >()), Times.Never); using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { DialogModule_MakeRoot.Register(scope, MakeRoot); await scope.Resolve <IBotData>().LoadAsync(default(CancellationToken)); var task = scope.Resolve <IDialogStack>(); Assert.AreNotEqual(0, task.Frames.Count); } using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { DialogModule_MakeRoot.Register(scope, MakeRoot); var task = scope.Resolve <IPostToBot>(); try { await task.PostAsync(toBot, CancellationToken.None); Assert.Fail(); } catch (ApplicationException) { } catch { Assert.Fail(); } } dialog.Verify(d => d.StartAsync(It.IsAny <IDialogContext>()), Times.Once); dialog.Verify(d => d.MessageReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <IMessageActivity> >()), Times.Once); dialog.Verify(d => d.Throw(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <IMessageActivity> >()), Times.Once); //make sure that data is persisted with connector using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { var connectorFactory = scope.Resolve <IConnectorClientFactory>(); var botDataStore = scope.Resolve <IBotDataStore <BotData> >(); var botData = scope.Resolve <IBotData>(); await botData.LoadAsync(default(CancellationToken)); Assert.AreEqual(1, botData.PrivateConversationData.Count); } using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { DialogModule_MakeRoot.Register(scope, MakeRoot); await scope.Resolve <IBotData>().LoadAsync(default(CancellationToken)); var stack = scope.Resolve <IDialogStack>(); Assert.AreEqual(0, stack.Frames.Count); } using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { DialogModule_MakeRoot.Register(scope, MakeRoot); var task = scope.Resolve <IPostToBot>(); await task.PostAsync(toBot, CancellationToken.None); } dialog.Verify(d => d.StartAsync(It.IsAny <IDialogContext>()), Times.Exactly(2)); dialog.Verify(d => d.MessageReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <IMessageActivity> >()), Times.Exactly(2)); dialog.Verify(d => d.Throw(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <IMessageActivity> >()), Times.Once); } }
/// <summary> /// POST: api/Messages /// Receive a message from a user and reply to it /// </summary> public async Task <HttpResponseMessage> Post([FromBody] Activity activity) { if (activity.Type == ActivityTypes.Message) { await Conversation.SendAsync(activity, () => new RootDialog(CommandsHelper.GetValidTriggers(DialogModule.BeginLifetimeScope(Conversation.Container, activity)).ToArray())); } else { this.HandleSystemMessage(activity); } var response = Request.CreateResponse(HttpStatusCode.OK); return(response); }
public async Task DialogTask_Frame_Scoring() { var dialogOne = new Mock <IDialogFrames <string> >(MockBehavior.Loose); var dialogTwo = new Mock <IDialogFrames <Guid> >(MockBehavior.Loose); var dialogNew = new Mock <IDialogFrames <DateTime> >(MockBehavior.Loose); const string TriggerTextTwo = "foo"; const string TriggerTextNew = "bar"; // IDialogFrames<T> StartAsync and ItemReceived dialogOne .Setup(d => d.StartAsync(It.IsAny <IDialogContext>())) .Returns <IDialogContext>(async context => { context.Call(dialogTwo.Object, dialogOne.Object.ItemReceived); }); dialogOne .Setup(d => d.ItemReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <Guid> >())) .Returns <IDialogContext, IAwaitable <Guid> >(async(context, message) => { context.Wait(dialogOne.Object.ItemReceived); }); dialogTwo .Setup(d => d.StartAsync(It.IsAny <IDialogContext>())) .Returns <IDialogContext>(async context => { context.Wait(dialogTwo.Object.ItemReceived); }); dialogTwo .Setup(d => d.ItemReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <Message> >())) .Returns <IDialogContext, IAwaitable <Message> >(async(context, message) => { if ((await message).Text == TriggerTextTwo) { context.Done(Guid.NewGuid()); } else { context.Wait(dialogTwo.Object.ItemReceived); } }); dialogNew .Setup(d => d.StartAsync(It.IsAny <IDialogContext>())) .Returns <IDialogContext>(async context => { context.Wait(dialogNew.Object.ItemReceived); }); dialogNew .Setup(d => d.ItemReceived(It.IsAny <IDialogContext>(), It.IsAny <IAwaitable <Message> >())) .Returns <IDialogContext, IAwaitable <Message> >(async(context, message) => { context.Done(DateTime.UtcNow); }); // ScoringDialogTask.IScorable dialogOne .As <IScorable <double> >() .Setup(s => s.PrepareAsync(It.IsAny <Message>(), It.IsAny <Delegate>(), It.IsAny <CancellationToken>())) .Returns <Message, Delegate, CancellationToken>(async(m, d, t) => m); double scoreOne = 1.0; dialogOne .As <IScorable <double> >() .Setup(s => s.TryScore(It.IsAny <Message>(), out scoreOne)) .Returns <Message, double>((m, s) => m.Text == TriggerTextNew); dialogTwo .As <IScorable <double> >() .Setup(s => s.PrepareAsync(It.IsAny <Message>(), It.IsAny <Delegate>(), It.IsAny <CancellationToken>())) .Returns <Message, Delegate, CancellationToken>(async(m, d, t) => m); double scoreTwo = 0.5; dialogTwo .As <IScorable <double> >() .Setup(s => s.TryScore(It.IsAny <Message>(), out scoreTwo)) .Returns <Message, double>((m, s) => m.Text == TriggerTextNew); Func <IDialog <object> > MakeRoot = () => dialogOne.Object; var toBot = MakeTestMessage(); using (new FiberTestBase.ResolveMoqAssembly(dialogOne.Object, dialogTwo.Object, dialogNew.Object)) using (var container = Build(Options.None, dialogOne.Object, dialogTwo.Object, dialogNew.Object)) { using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { DialogModule_MakeRoot.Register(scope, MakeRoot); var task = scope.Resolve <IPostToBot>(); var stack = scope.Resolve <IDialogStack>(); // set up dialogOne to call dialogNew when triggered dialogOne .As <IScorable <double> >() .Setup(s => s.PostAsync(It.IsAny <IPostToBot>(), It.IsAny <Message>(), It.IsAny <Message>(), It.IsAny <CancellationToken>())) .Returns <IPostToBot, Message, Message, CancellationToken>(async(inner, message, state, token) => { stack.Call(dialogNew.Object.Void <DateTime, Message>(), null); await stack.PollAsync(token); }); // the stack is empty when we first start Assert.AreEqual(0, stack.Frames.Count); await task.PostAsync(toBot, CancellationToken.None); // now the stack has the looping root frame plus the 1st and 2nd active dialogs // nothing special in the message, so we still have the 1st and 2nd active dialogs Assert.AreEqual(3, stack.Frames.Count); Assert.AreEqual(dialogTwo.Object, stack.Frames[0].Target); Assert.AreEqual(dialogOne.Object, stack.Frames[1].Target); toBot.Text = TriggerTextNew; await task.PostAsync(toBot, CancellationToken.None); // now the trigger has occurred - the interrupting dialog is at the top of the stack, // then the void dialog, then the existing 1st and 2nd dialogs that were interrupted Assert.AreEqual(5, stack.Frames.Count); Assert.AreEqual(dialogNew.Object, stack.Frames[0].Target); Assert.AreEqual(dialogTwo.Object, stack.Frames[2].Target); Assert.AreEqual(dialogOne.Object, stack.Frames[3].Target); toBot.Text = string.Empty; await task.PostAsync(toBot, CancellationToken.None); // now the interrupted dialog will exit, and the void dialog is waiting for original message that // the 2nd dialog had wanted Assert.AreEqual(4, stack.Frames.Count); Assert.AreEqual(dialogTwo.Object, stack.Frames[1].Target); Assert.AreEqual(dialogOne.Object, stack.Frames[2].Target); toBot.Text = TriggerTextTwo; await task.PostAsync(toBot, CancellationToken.None); // and now that the void dialog was able to capture the message, it returns it to the 2nd dialog, // which returns a guid to the 1st dialog Assert.AreEqual(2, stack.Frames.Count); Assert.AreEqual(dialogOne.Object, stack.Frames[0].Target); } } dialogOne.VerifyAll(); dialogTwo.VerifyAll(); dialogNew.VerifyAll(); }
public async Task Can_Fill_In_Scalar_Types() { var mock = new Mock <IFormTarget>(); mock.SetupAllProperties(); Func <IDialog <IFormTarget> > MakeRoot = () => new FormDialog <IFormTarget>(mock.Object); // arrange var toBot = MakeTestMessage(); using (new FiberTests.ResolveMoqAssembly(mock.Object)) using (var container = Build(Options.ScopedQueue, mock.Object)) { using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { DialogModule_MakeRoot.Register(scope, MakeRoot); var task = scope.Resolve <IPostToBot>(); // act await task.PostAsync(toBot, CancellationToken.None); // assert AssertMentions(nameof(mock.Object.Text), scope); } using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { DialogModule_MakeRoot.Register(scope, MakeRoot); var task = scope.Resolve <IPostToBot>(); // arrange // note: this can not be "text" as that is a navigation command toBot.Text = "words"; // act await task.PostAsync(toBot, CancellationToken.None); // assert AssertMentions(nameof(mock.Object.Integer), scope); } using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { DialogModule_MakeRoot.Register(scope, MakeRoot); var task = scope.Resolve <IPostToBot>(); // arrange toBot.Text = "3"; // act await task.PostAsync(toBot, CancellationToken.None); // assert AssertMentions(nameof(mock.Object.Float), scope); } using (var scope = DialogModule.BeginLifetimeScope(container, toBot)) { DialogModule_MakeRoot.Register(scope, MakeRoot); var task = scope.Resolve <IPostToBot>(); // arrange toBot.Text = "3.5"; // act await task.PostAsync(toBot, CancellationToken.None); // assert AssertNoMessages(scope); } mock.VerifyAll(); } }
public async Task <HttpResponseMessage> AuthResume( [FromUri] string code, [FromUri] string state, CancellationToken cancellationToken) { try { var queryParams = state; object tokenCache = null; if (string.Equals(AuthSettings.Mode, "v1", StringComparison.OrdinalIgnoreCase)) { tokenCache = new Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache(); } else if (string.Equals(AuthSettings.Mode, "v2", StringComparison.OrdinalIgnoreCase)) { tokenCache = new Microsoft.Identity.Client.TokenCache(); } else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase)) { } var resumptionCookie = UrlToken.Decode <ResumptionCookie>(queryParams); // Create the message that is send to conversation to resume the login flow var message = resumptionCookie.GetMessage(); using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message)) { var client = scope.Resolve <IConnectorClient>(); AuthResult authResult = null; if (string.Equals(AuthSettings.Mode, "v1", StringComparison.OrdinalIgnoreCase)) { // Exchange the Auth code with Access token var token = await AzureActiveDirectoryHelper.GetTokenByAuthCodeAsync(code, (Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache) tokenCache); authResult = token; } else if (string.Equals(AuthSettings.Mode, "v2", StringComparison.OrdinalIgnoreCase)) { // Exchange the Auth code with Access token var token = await AzureActiveDirectoryHelper.GetTokenByAuthCodeAsync(code, (Microsoft.Identity.Client.TokenCache) tokenCache, Models.AuthSettings.Scopes); authResult = token; } else if (string.Equals(AuthSettings.Mode, "b2c", StringComparison.OrdinalIgnoreCase)) { } IStateClient sc = scope.Resolve <IStateClient>(); //IMPORTANT: DO NOT REMOVE THE MAGIC NUMBER CHECK THAT WE DO HERE. THIS IS AN ABSOLUTE SECURITY REQUIREMENT //REMOVING THIS WILL REMOVE YOUR BOT AND YOUR USERS TO SECURITY VULNERABILITIES. //MAKE SURE YOU UNDERSTAND THE ATTACK VECTORS AND WHY THIS IS IN PLACE. int magicNumber = GenerateRandomNumber(); bool writeSuccessful = false; uint writeAttempts = 0; while (!writeSuccessful && writeAttempts++ < MaxWriteAttempts) { try { BotData userData = sc.BotState.GetUserData(message.ChannelId, message.From.Id); userData.SetProperty(ContextConstants.AuthResultKey, authResult); userData.SetProperty(ContextConstants.MagicNumberKey, magicNumber); userData.SetProperty(ContextConstants.MagicNumberValidated, "false"); sc.BotState.SetUserData(message.ChannelId, message.From.Id, userData); writeSuccessful = true; } catch (HttpOperationException) { writeSuccessful = false; } } var resp = new HttpResponseMessage(HttpStatusCode.OK); if (!writeSuccessful) { message.Text = String.Empty; // fail the login process if we can't write UserData await Conversation.ResumeAsync(resumptionCookie, message); resp.Content = new StringContent("<html><body>Could not log you in at this time, please try again later</body></html>", System.Text.Encoding.UTF8, @"text/html"); } else { await Conversation.ResumeAsync(resumptionCookie, message); resp.Content = new StringContent($"<html><body>Almost done! Please copy this number and paste it back to your chat so your authentication can complete:<br/> <h1>{magicNumber}</h1>.</body></html>", System.Text.Encoding.UTF8, @"text/html"); } return(resp); } } catch (Exception ex) { // Callback is called with no pending message as a result the login flow cannot be resumed. return(Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex)); } }
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable <object> result) { var activity = await result as Activity; //// calculate something for us to return //int length = (activity.Text ?? string.Empty).Length; //// return our reply to the user //await context.PostAsync($"You sent {activity.Text} which was {length} characters"); if (activity.Text == "login") { // create state (passing data) for OAuth var convRef = context.Activity.ToConversationReference(); var stateCol = System.Web.HttpUtility.ParseQueryString(string.Empty); stateCol["userId"] = convRef.User.Id; stateCol["botId"] = convRef.Bot.Id; stateCol["conversationId"] = convRef.Conversation.Id; stateCol["serviceUrl"] = convRef.ServiceUrl; stateCol["channelId"] = convRef.ChannelId; //var uriBuilder = new UriBuilder(ConfigurationManager.AppSettings["OAuthCallbackUrl"]); //var query = System.Web.HttpUtility.ParseQueryString(uriBuilder.Query); //query["userId"] = convRef.User.Id; //query["botId"] = convRef.Bot.Id; //query["conversationId"] = convRef.Conversation.Id; //query["serviceUrl"] = convRef.ServiceUrl; //query["channelId"] = convRef.ChannelId; //uriBuilder.Query = query.ToString(); //var redirectUrl = uriBuilder.ToString(); // create Azure AD signin context var authContext = new AuthenticationContext("https://login.microsoftonline.com/common"); var authUri = authContext.GetAuthorizationRequestUrlAsync( "https://outlook.office365.com/", ConfigurationManager.AppSettings["ClientId"], new Uri(ConfigurationManager.AppSettings["OAuthCallbackUrl"]), UserIdentifier.AnyUser, "&state=" + System.Web.HttpUtility.UrlEncode(stateCol.ToString())); // show SignIn card (setting oauth sign-in url to SignIn card) var reply = context.MakeMessage(); reply.Text = "Authentication Required"; reply.Attachments.Add(SigninCard.Create( "Login", "Please login to Office 365", authUri.Result.ToString()).ToAttachment()); await context.PostAsync(reply); } else if (activity.Text == "get mail") { string accessToken = string.Empty; using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity)) { var dataStore = scope.Resolve <IBotDataStore <BotData> >(); var convRef = context.Activity.ToConversationReference(); var address = new Microsoft.Bot.Builder.Dialogs.Address ( botId: convRef.Bot.Id, channelId: convRef.ChannelId, userId: convRef.User.Id, conversationId: convRef.Conversation.Id, serviceUrl: convRef.ServiceUrl ); var userData = await dataStore.LoadAsync( address, BotStoreType.BotUserData, CancellationToken.None); accessToken = userData.GetProperty <string>("AccessToken"); } if (string.IsNullOrEmpty(accessToken)) { await context.PostAsync("Not logging-in (type \"login\")"); } else { // Get recent 10 e-mail from Office 365 HttpClient cl = new HttpClient(); var acceptHeader = new MediaTypeWithQualityHeaderValue("application/json"); cl.DefaultRequestHeaders.Accept.Add(acceptHeader); cl.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); HttpResponseMessage httpRes = await cl.GetAsync("https://outlook.office365.com/api/v1.0/me/messages?$orderby=DateTimeSent%20desc&$top=10&$select=Subject,From"); if (httpRes.IsSuccessStatusCode) { var strRes = httpRes.Content.ReadAsStringAsync().Result; JObject jRes = await httpRes.Content.ReadAsAsync <JObject>(); JArray jValue = (JArray)jRes["value"]; foreach (JObject jItem in jValue) { string sub = $"Subject={((JValue)jItem["Subject"]).Value}"; sub = sub.Replace('<', ' ').Replace('>', ' ').Replace('[', ' ').Replace(']', ' '); await context.PostAsync(sub); } } else { await context.PostAsync("Failed to get e-mail.\n\nPlease type \"login\" before you get e-mail."); } } } else if (activity.Text == "revoke") { await context.PostAsync("Click [here](https://account.activedirectory.windowsazure.com/), login, and remove this app (Bot with Office 365 Authentication Example)."); } else if (activity.Text == "login_succeed") { await context.PostAsync("You logged in !"); } else { await context.PostAsync("# Bot Help\n\nType the following command. (You need your Office 365 Exchange Online subscription.)\n\n**login** -- Login to Office 365\n\n**get mail** -- Get your e-mail from Office 365\n\n**revoke** -- Revoke permissions for accessing your e-mail"); } context.Wait(MessageReceivedAsync); }
public async Task InputHintTest() { var chain = Chain.PostToChain().Select(m => m.Text).ContinueWith <string, string>(async(context, result) => { var text = await result; if (text.ToLower().StartsWith("inputhint")) { var reply = context.MakeMessage(); reply.Text = "reply"; reply.InputHint = InputHints.ExpectingInput; await context.PostAsync(reply); return(Chain.Return($"{text}")); } else if (!text.ToLower().StartsWith("reset")) { for (int i = 0; i < 10; i++) { await context.PostAsync($"message:{i}"); } return(Chain.Return($"{text}")); } else { return(Chain.From(() => new PromptDialog.PromptConfirm("Are you sure you want to reset the count?", "Didn't get that!", 3, PromptStyle.Keyboard)).ContinueWith <bool, string>(async(ctx, res) => { string reply; if (await res) { ctx.UserData.SetValue("count", 0); reply = "Reset count."; } else { reply = "Did not reset count."; } return Chain.Return(reply); })); } }).PostToUser(); Func <IDialog <object> > MakeRoot = () => chain; using (new FiberTestBase.ResolveMoqAssembly(chain)) using (var container = Build(Options.InMemoryBotDataStore | Options.NeedsInputHint, chain)) { var msg = DialogTestBase.MakeTestMessage(); msg.Text = "test"; await new TestAdapter().ProcessActivity((Activity)msg, async(context) => { using (var scope = DialogModule.BeginLifetimeScope(container, context)) { scope.Resolve <Func <IDialog <object> > >(TypedParameter.From(MakeRoot)); await Conversation.SendAsync(scope, context); var queue = ((TestAdapter)context.Adapter).ActiveQueue; Assert.IsTrue(queue.Count > 0); while (queue.Count > 0) { var toUser = queue.Dequeue(); if (queue.Count > 0) { Assert.IsTrue(toUser.InputHint == InputHints.IgnoringInput); } else { Assert.IsTrue(toUser.InputHint == InputHints.AcceptingInput); } } } }); msg.Text = "inputhint"; await new TestAdapter().ProcessActivity((Activity)msg, async(context) => { using (var scope = DialogModule.BeginLifetimeScope(container, context)) { scope.Resolve <Func <IDialog <object> > >(TypedParameter.From(MakeRoot)); await Conversation.SendAsync(scope, context); var queue = ((TestAdapter)context.Adapter).ActiveQueue; Assert.IsTrue(queue.Count == 2); var toUser = queue.Dequeue(); Assert.AreEqual("reply", toUser.Text); Assert.IsTrue(toUser.InputHint == InputHints.ExpectingInput); } }); msg.Text = "reset"; await new TestAdapter().ProcessActivity((Activity)msg, async(context) => { using (var scope = DialogModule.BeginLifetimeScope(container, context)) { scope.Resolve <Func <IDialog <object> > >(TypedParameter.From(MakeRoot)); await Conversation.SendAsync(scope, context); var queue = ((TestAdapter)context.Adapter).ActiveQueue; Assert.IsTrue(queue.Count == 1); var toUser = queue.Dequeue(); Assert.IsTrue(toUser.InputHint == InputHints.ExpectingInput); Assert.IsNotNull(toUser.LocalTimestamp); } }); } }
/// <summary> /// Processes an <see cref="Activity"/> of <see cref="ActivityTypes.Message"/> type that is an adaptive card action. /// </summary> /// <param name="activity">The incoming activity from Bot Framework.</param> /// <returns>A <see cref="Task"/> that resolves to a <see cref="IHttpActionResult"/> with the response from the API.</returns> private async Task <IHttpActionResult> HandleAdaptiveCardActionAsync(Activity activity) { // Parse out the payload. var payload = activity.Value as JObject; var sessionId = payload.GetValue("sessionId").Value <string>(); var conversationId = payload.GetValue("conversationId").Value <string>(); var choice = (Choices)Enum.Parse(typeof(Choices), payload.GetValue("GameChoice").Value <string>()); Match match = null; // Load the correct conversation data record and update it with the user's choice. using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity)) { var botDataStore = scope.Resolve <IBotDataStore <BotData> >(); var address = new Address(activity.Recipient.Id, activity.ChannelId, activity.From.Id, conversationId, activity.ServiceUrl); var data = await botDataStore.LoadAsync(address, BotStoreType.BotConversationData, CancellationToken.None); match = data.GetProperty <Match>(sessionId); foreach (var result in match.Results) { if (result.User.Id == activity.From.Id) { result.Choice = choice; } } data.SetProperty(sessionId, match); await botDataStore.SaveAsync(address, BotStoreType.BotConversationData, data, CancellationToken.None); await botDataStore.FlushAsync(address, CancellationToken.None); } var connectorClient = new ConnectorClient(new Uri(activity.ServiceUrl)); // Remove the response card. var thankYouResponse = Activity.CreateMessageActivity(); var thanksCard = new ThanksForPlayingCard(choice); thankYouResponse.Attachments.Add(thanksCard.ToAttachment()); await connectorClient.Conversations.UpdateActivityAsync(activity.Conversation.Id, activity.ReplyToId, (Activity)thankYouResponse); // Update the original card. var updateActivity = Activity.CreateMessageActivity(); var resultCard = new ResultCard(match.Results); updateActivity.Attachments.Add(resultCard.ToAttachment()); await connectorClient.Conversations.UpdateActivityAsync(conversationId, match.MessageId, (Activity)updateActivity); // Update service records if the match is done. if (!match.Results.Any(result => result.Choice == Choices.None)) { foreach (var result in match.Results) { var wins = match.Results.Count(otherResult => result.Choice.Beats(otherResult.Choice)); var losses = match.Results.Count(otherResult => otherResult.Choice.Beats(result.Choice)); var ties = match.Results.Count(otherResult => otherResult.User.Id != result.User.Id && !result.Choice.Beats(otherResult.Choice) && !otherResult.Choice.Beats(result.Choice)); var serviceRecord = ServiceRecordStorage.GetServiceRecordForUserId(result.User.Id); if (serviceRecord == null) { serviceRecord = new ServiceRecord() { User = result.User.AsTeamsChannelAccount(), Losses = 0, Wins = 0, Ties = 0 }; } serviceRecord.Wins += wins; serviceRecord.Losses += losses; serviceRecord.Ties += ties; ServiceRecordStorage.SetServiceRecordForUserId(result.User.Id, serviceRecord); } } return(Ok()); }