I'm trying to implement the custom connector for my Microsoft bot inside a webapi so that it could be called from different applications via Rest API(for exmp: from rasberripy).The intention is to implement the custom connector inside a WebAPI using directline API so that other application can just use rest calls to talk to this API which will in turn talk to my bot using dirtectline API. I have tested the following code from micrososft which is working inside a console application. I have moved the same code to WebAPI, here the same code behaving differently.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Threading.Tasks;
using Microsoft.Bot.Connector.DirectLine;
using Newtonsoft.Json;
using System.Configuration;
namespace BotConnector.Web.Controllers
{
public class BotController : Controller
{
private static string directLineSecret = ConfigurationManager.AppSettings["DirectLineSecret"];
private static string botId = ConfigurationManager.AppSettings["BotId"];
private static string fromUser = "APISampleClientUser";
// GET: Bot
public ActionResult Index()
{
return View();
}
public string Connect()
{
var conversation=StartBotConversation();
return conversation.ConversationId;
}
public string ReadBotMessages(string conversationId,string message)
{
//DirectLineClient client = (Session["client"] == null) ? new DirectLineClient(directLineSecret) : Session["client"] as DirectLineClient;
//Session["client"] = client;
DirectLineClient client = new DirectLineClient(directLineSecret);
var activity = new Activity
{
From = new ChannelAccount(fromUser),
Text = message,
Type = ActivityTypes.Message
};
client.Conversations.PostActivityAsync(conversationId, activity);
var response = ReadBotMessagesAsync(client, conversationId);
//var heroCard = JsonConvert.DeserializeObject<HeroCard>(response[0].Content.ToString());
return response.Result;
}
private static Conversation StartBotConversation()
{
//var session = System.Web.HttpContext.Current.Session["client"];
//DirectLineClient client = (session == null) ? new DirectLineClient(directLineSecret) : session as DirectLineClient;
//System.Web.HttpContext.Current.Session["client"] = client;
var client = new DirectLineClient(directLineSecret);
var conversation = client.Conversations.StartConversationAsync().Result;
//new System.Threading.Thread(async () => await ReadBotMessagesAsync(client, conversation.ConversationId)).Start();
return conversation;
}
private static async Task<string> ReadBotMessagesAsync(DirectLineClient client, string conversationId)
{
string watermark = null;
var attachments = new List<Attachment>();
string message = string.Empty;
while (true)
{
var activitySet = client.Conversations.GetActivities(conversationId, watermark);
watermark = activitySet?.Watermark;
var activities = from x in activitySet.Activities
where x.From.Id == botId
select x;
foreach (Activity activity in activities)
{
message = activity.Text;
if (activity.Attachments != null)
{
foreach (Attachment attachment in activity.Attachments)
{
if (attachment.ContentType== "application/vnd.microsoft.card.hero")
{
var heroCard = JsonConvert.DeserializeObject<HeroCard>(attachment.Content.ToString());
message = heroCard.Text;
//attachments.Add(attachment);
}
}
}
}
//if (attachments.Count > 0) break;
if (!string.IsNullOrEmpty(message)) break;
//await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);
}
return message;
}
}
}
First i'll call Bot/Connect to generate conversationId, and from later on i'll call Bot/ReadBotMessages with Id and user-question.
The problem i'm facing is the code
var activitySet = client.Conversations.GetActivities(conversationId, watermark);
returning multiple activities every time it got hit, with responses from previous questions as well (),but I should get single activity related to current response,The same logic is working fine in the console application from github which is returning only the one single current activity. Can anyone suggest a workaround or am i doing anything worng ?
In order to just get the most recent activity, you'll want to make sure to store the current watermark somewhere in your connector.
A call to the following method:
Client.Conversations.PostActivityAsync(conversationId, activity);
will return a string formatted as follows:
"IRBO9ZhxkwID1QnNbAoOHX|0000008"
i.e.
"{ConversationId|Watermark}"
You will want to save the watermark portion of this call somewhere in your connector, as using it later on in the message reception call for your connector will allow you to get only the immediate responses to your message:
private static async Task<string> ReadBotMessagesAsync(DirectLineClient client, string conversationId)
{
string watermark = null; //this should be the current watermark
Basically, the endpoint for conversation GET calls:
/v3/directline/conversations/{conversationID}/activities?watermark={watermark}
returns the conversation as stored by the bot starting from "watermark" or, if it is set to null, the portion of the conversation available in the bot memory starting from the last message the bot sent before reconnecting with the bot (which is what you were seeing).