// Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using Microsoft.Bot.Builder; using Microsoft.Bot.Connector.DirectLine; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using DirectLineActivity = Microsoft.Bot.Connector.DirectLine.Activity; using DirectLineActivityTypes = Microsoft.Bot.Connector.DirectLine.ActivityTypes; using IConversationUpdateActivity = Microsoft.Bot.Schema.IConversationUpdateActivity; using IMessageActivity = Microsoft.Bot.Schema.IMessageActivity; namespace Microsoft.PowerVirtualAgents.Samples.RelayBotSample.Bots { /// /// This IBot implementation shows how to connect /// an external Azure Bot Service channel bot (external bot) /// to your Power Virtual Agent bot /// public class RelayBot : ActivityHandler { private const int WaitForBotResponseMaxMilSec = 5 * 1000; private const int PollForBotResponseIntervalMilSec = 1000; private static ConversationManager s_conversationManager = ConversationManager.Instance; private ResponseConverter _responseConverter; private IBotService _botService; public RelayBot(IBotService botService, ConversationManager conversationManager) { _botService = botService; _responseConverter = new ResponseConverter(); } // Invoked when a conversation update activity is received from the external Azure Bot Service channel // Start a Power Virtual Agents bot conversation and store the mapping protected override async Task OnConversationUpdateActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) { await s_conversationManager.GetOrCreateBotConversationAsync(turnContext.Activity.Conversation.Id, _botService); } // Invoked when a message activity is received from the user // Send the user message to Power Virtual Agent bot and get response protected override async Task OnMessageActivityAsync(ITurnContext turnContext, CancellationToken cancellationToken) { var currentConversation = await s_conversationManager.GetOrCreateBotConversationAsync(turnContext.Activity.Conversation.Id, _botService); using (DirectLineClient client = new DirectLineClient(currentConversation.Token)) { // Send user message using directlineClient await client.Conversations.PostActivityAsync(currentConversation.ConversationtId, new DirectLineActivity() { Type = DirectLineActivityTypes.Message, From = new ChannelAccount { Id = turnContext.Activity.From.Id, Name = turnContext.Activity.From.Name }, Text = turnContext.Activity.Text, TextFormat = turnContext.Activity.TextFormat, Locale = turnContext.Activity.Locale, }); await RespondPowerVirtualAgentsBotReplyAsync(client, currentConversation, turnContext); } // Update LastConversationUpdateTime for session management currentConversation.LastConversationUpdateTime = DateTime.Now; } private async Task RespondPowerVirtualAgentsBotReplyAsync(DirectLineClient client, RelayConversation currentConversation, ITurnContext turnContext) { var retryMax = WaitForBotResponseMaxMilSec / PollForBotResponseIntervalMilSec; for (int retry = 0; retry < retryMax; retry++) { // Get bot response using directlineClient, // response contains whole conversation history including user & bot's message ActivitySet response = await client.Conversations.GetActivitiesAsync(currentConversation.ConversationtId, currentConversation.WaterMark); // Filter bot's reply message from response List botResponses = response?.Activities?.Where(x => x.Type == DirectLineActivityTypes.Message && string.Equals(x.From.Name, _botService.GetBotName(), StringComparison.Ordinal)).ToList(); if (botResponses?.Count() > 0) { if (int.Parse(response?.Watermark ?? "0") <= int.Parse(currentConversation.WaterMark ?? "0")) { // means user sends new message, should break previous response poll return; } currentConversation.WaterMark = response.Watermark; await turnContext.SendActivitiesAsync(_responseConverter.ConvertToBotSchemaActivities(botResponses).ToArray()); } Thread.Sleep(PollForBotResponseIntervalMilSec); } } } }