I am using Visual Studio to create a Skype bot based on their Bot Builder SDK and using the Skype emulator. (I am using this page) I successfully got the bot to connect and receive regular text messages, and it responds properly with:
You sent [message] which was [length] characters
However, I tried adding an event to fire when a user is added, and it should just send "Welcome".
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Message)
{
ConnectorClient connector = new ConnectorClient(new System.Uri(activity.ServiceUrl));
int length = (activity.Text ?? string.Empty).Length;
Activity reply = activity.CreateReply($"You sent {activity.Text} which was {length} characters");
await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
private Activity HandleSystemMessage(Activity message)
{
//The other if statements are the rest of the activity types
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
Activity reply = message.CreateReply("Welcome");
}
This is the code I have currently. I tried adding the same Connector and await lines to the else statement on line 17, but that just makes the bot respond with
You sent which was 0 characters
If any other info is needed to fix this I'll be happy to provide it.
Any help is appreciated!
EDIT: The code I currently have does not respond with anything. It sees the ConversationUpdate event and does nothing with it
I've never used the Skype SDK but I imagine it would look something like this if you did it properly:
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
var connector = new ConnectorClient(new Uri(activity.ServiceUrl));
Activity reply = null;
if (activity.Type == ActivityTypes.Message)
{
int length = (activity.Text ?? string.Empty).Length;
reply = activity.CreateReply($"You sent {activity.Text} which was {length} characters");
}
else if (activity.Type == ActivityTypes.ConversationUpdate)
{
reply = message.CreateReply("Welcome");
}
if (reply != null)
{
await connector.Conversations.SendToConversationAsync((Activity)reply);
}
}
I'd suggest you take a tutorial in C# and maybe read the skype documentation?
https://learn.microsoft.com/en-us/bot-framework/dotnet/bot-builder-dotnet-connector
The above still may not work, if you take a look at the docs link I posted you may need to start a conversation if one does not already exist using the CreateDirectConversationAsync method.
Related
I want to connect my Azure QnA Chat Bot with the translation layer cognitive system. I am using this page as a reference: https://learn.microsoft.com/en-us/azure/cognitive-services/translator/quickstart-csharp-translate
I am doing it in C# and on the online code editor of Microsoft Azure.
Unfortunately, I can not connect to the translation layer (at least it looks like that).
When I tried to debug it, I can see that it stops at this specific part:
var response = await client.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();
I checked the network timeout errors and there are many (20). All of them say "There was an error sending this message to your bot: HTTP status code GatewayTimeout".
I can "build.cmd" normally, without any errors, and when I try to do Debug.WriteLine or Console.WriteLine, nothing is printed out (I even tried in VS and Emulator)
The only thing that I am doing differently, compared to the above link, is that I defined the "host" and "key" outside of the private method:
private static async Task<string> TranslateQuestionToEnglish (...)
So, I take any word and want to translate it into English.
When I take out those two lines of the code, and test a method with static values, it obviously works (all together with QnA and everything else).
Later on, I am calling this method in "Task MessageReceivedAsync".
I created a translation cognitive service, and the only thing that I took from there is the first key from "Keys" and used it here in this method. Is that the only thing that I need from created cognitive service??
Another thing that I am not sure about, and if that thing is making a problems is that when I go to all resources, I can see that my qnatestbot(web app bot) and translator_test(cognitive services) are of type "global" location, while my qnatestbot(app service) is of type "west europe" location. Can the thing that they are in different regions make a problems? Should I put all of them in West Europe (since I am in Germany)?
Although, now that I look at the translator_test(cognitive services) endpoint, I can see that it is ...api.congitivemicrosft.com/.../v1.0.
But, when I was creating a resource it was automatically created like this, without specifying it from my side? How can I change that?
I hope that somebody successfully came across such an issue and can help me. Thank you in advance
I want to connect my Azure QnA Chat Bot with the translation layer cognitive system. I am using this page as a reference: https://learn.microsoft.com/en-us/azure/cognitive-services/translator/quickstart-csharp-translate
I try to create a sample to achieve your requirement: translate user inputs to English and pass translation text to QnAMaker dialog, the sample works fine both on local and Azure, you can refer to it.
In MessagesController:
[BotAuthentication]
public class MessagesController : ApiController
{
static string uri = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=en";
static string key = "{the_key}";
/// <summary>
/// POST: api/Messages
/// receive a message from a user and send replies
/// </summary>
/// <param name="activity"></param>
[ResponseType(typeof(void))]
public virtual async Task<HttpResponseMessage> Post([FromBody] Activity activity)
{
// check if activity is of type message
if (activity.GetActivityType() == ActivityTypes.Message)
{
if (activity.Text != null)
{
var textinEN = await TranslateQuestionToEnglish(activity.Text);
activity.Text = textinEN;
}
await Conversation.SendAsync(activity, () => new RootDialog());
}
else
{
HandleSystemMessage(activity);
}
return new HttpResponseMessage(System.Net.HttpStatusCode.Accepted);
}
private static async Task<string> TranslateQuestionToEnglish(string text)
{
System.Object[] body = new System.Object[] { new { Text = text } };
var requestBody = JsonConvert.SerializeObject(body);
using (var client = new HttpClient())
using (var request = new HttpRequestMessage())
{
request.Method = HttpMethod.Post;
request.RequestUri = new Uri(uri);
request.Content = new StringContent(requestBody, Encoding.UTF8, "application/json");
request.Headers.Add("Ocp-Apim-Subscription-Key", key);
var response = await client.SendAsync(request);
var responseBody = await response.Content.ReadAsStringAsync();
dynamic jsonResponse = JsonConvert.DeserializeObject(responseBody);
var textinen = jsonResponse[0]["translations"][0]["text"].Value;
return textinen;
}
}
private 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
}
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;
}
}
In dialog:
[Serializable]
public class RootDialog : IDialog<object>
{
public async Task StartAsync(IDialogContext context)
{
/* Wait until the first message is received from the conversation and call MessageReceviedAsync
* to process that message. */
context.Wait(this.MessageReceivedAsync);
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
/* When MessageReceivedAsync is called, it's passed an IAwaitable<IMessageActivity>. To get the message,
* await the result. */
var message = await result;
var qnaAuthKey = GetSetting("QnAAuthKey");
var qnaKBId = Utils.GetAppSetting("QnAKnowledgebaseId");
var endpointHostName = Utils.GetAppSetting("QnAEndpointHostName");
// QnA Subscription Key and KnowledgeBase Id null verification
if (!string.IsNullOrEmpty(qnaAuthKey) && !string.IsNullOrEmpty(qnaKBId))
{
// Forward to the appropriate Dialog based on whether the endpoint hostname is present
if (string.IsNullOrEmpty(endpointHostName))
await context.Forward(new BasicQnAMakerPreviewDialog(), AfterAnswerAsync, message, CancellationToken.None);
else
await context.Forward(new BasicQnAMakerDialog(), AfterAnswerAsync, message, CancellationToken.None);
}
else
{
await context.PostAsync("Please set QnAKnowledgebaseId, QnAAuthKey and QnAEndpointHostName (if applicable) in App Settings. Learn how to get them at https://aka.ms/qnaabssetup.");
}
}
private async Task AfterAnswerAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
// wait for the next user message
context.Wait(MessageReceivedAsync);
}
public static string GetSetting(string key)
{
var value = Utils.GetAppSetting(key);
if (String.IsNullOrEmpty(value) && key == "QnAAuthKey")
{
value = Utils.GetAppSetting("QnASubscriptionKey"); // QnASubscriptionKey for backward compatibility with QnAMaker (Preview)
}
return value;
}
}
// Dialog for QnAMaker Preview service
[Serializable]
public class BasicQnAMakerPreviewDialog : QnAMakerDialog
{
// Go to https://qnamaker.ai and feed data, train & publish your QnA Knowledgebase.
// Parameters to QnAMakerService are:
// Required: subscriptionKey, knowledgebaseId,
// Optional: defaultMessage, scoreThreshold[Range 0.0 – 1.0]
public BasicQnAMakerPreviewDialog() : base(new QnAMakerService(new QnAMakerAttribute(RootDialog.GetSetting("QnAAuthKey"), Utils.GetAppSetting("QnAKnowledgebaseId"), "No good match in FAQ.", 0.5)))
{ }
}
// Dialog for QnAMaker GA service
[Serializable]
public class BasicQnAMakerDialog : QnAMakerDialog
{
// Go to https://qnamaker.ai and feed data, train & publish your QnA Knowledgebase.
// Parameters to QnAMakerService are:
// Required: qnaAuthKey, knowledgebaseId, endpointHostName
// Optional: defaultMessage, scoreThreshold[Range 0.0 – 1.0]
public BasicQnAMakerDialog() : base(new QnAMakerService(new QnAMakerAttribute(RootDialog.GetSetting("QnAAuthKey"), Utils.GetAppSetting("QnAKnowledgebaseId"), "No good match in FAQ.", 0.5, 1, Utils.GetAppSetting("QnAEndpointHostName"))))
{ }
}
Test result:
Note: We can use ConfigurationManager.AppSettings["QnAKnowledgebaseId"]; to access QnAKnowledgebaseId etc settings from web.config if run bot application on local. For more information, please refer to this SO thread.
I am trying to have my bot framework bot reply to a user by starting a thread. This way I can keep who the bot is talking to when in a channel with many people straight.
According to the slack documentation what I need to do is set the thread_ts property to the ts property sent to my bot. I have tried a few things and have been unable to accomplish this. This is the most concise example I have:
var reply = (Activity)activity;
reply = reply.CreateReply("reply");
reply.ChannelData = JObject.Parse($"{{thread_ts:'{ts}'}}");
await context.PostAsync(reply);
This is not working for me.
You will need to set the text in the ChannelData in order for your bot to reply in the thread. Right now you are setting it in your activity
reply = reply.CreateReply("reply");
All you need to do is this:
reply.ChannelData = JObject.Parse($"{{text:'reply', thread_ts:'{ts}'}}");
here is a full working method from a dialog:
public async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
var activity = await argument;
var ts = activity.ChannelData?.SlackMessage?.thread_ts
?? activity.ChannelData?.SlackMessage?.ts
?? activity.ChannelData?.SlackMessage["event"].thread_ts
?? activity.ChannelData?.SlackMessage["event"].ts;
var reply = (Activity)activity;
reply = reply.CreateReply();
reply.ChannelData = JObject.Parse($"{{text:'reply', thread_ts:'{ts}'}}");
await context.PostAsync(reply);
}
So to explain my problem, I have to give you the context.
I got a Bot built with microsoft bot framework deployed on slack. Now it can happen these "events" on my backend that the bot communicates with. When a event occurs, I want to notify my bot of it and then let it send a message to all of it's conversations that something has happend. So basicly:
Backend>Microserivce>Bot>users
To do this I have to store all conversations in my backend, which I do in a database there. When a event happends, the backend will post an activity to the bot with all the conversations(basicly their id's) and the event it should show them.
So in essence my backend need to post a message to my bot.
For doing this I found the microsoft directline api which acts as a middleman here, a more abstract way to talk with the bot. The problem is that I don't know how to do it. I followed microsofts own tutorial but it doesn't seem to work for me:
This is the endpoint that my backend uses to notify the bot. "content" contains conversations and events as a json formated string.
[HttpPost]
[Route("conversationsEvents")]
public HttpResponseMessage PostConversationsEvents([FromBody]string content)
{
NotifyBot.Notify(content);
return Request.CreateResponse(HttpStatusCode.NoContent );
}
NotifyBot.Notify(content) looks like this:
private static async Task StartBotConversation( string contents)
{
string directLineSecret = "secret";
string fromUser = "microserviceNotifyEndpoint";
Activity activity = new Activity
{
From = new ChannelAccount(fromUser),
Text = contents,
Type = ActivityTypes.Event
};
DirectLineClient client = new DirectLineClient(directLineSecret);
var conversation = await client.Conversations.StartConversationAsync();
await client.Conversations.PostActivityAsync(conversation.ConversationId, activity);
}
Basicly the execution get's stuck at var conversation = await client.Conversations.StartConversationAsync(); , it just waits forever.
I tried changing it to var conversation = await client.Conversations.StartConversationAsync().ConfigureAwait(continueOnCapturedContext: false);´the execution goes on but the activity doesn't seem to get posted.
I'm not sure why the call to .StartConversationAsync() would freeze in your case. Maybe you haven't enabled the Direct Line channel on dev.botframework.com/bots? Nonetheless, as pointed out by Sergey, the Direct Line is a Channel and not a means for communicating with your bot on other channels.
Check out the Connector Client: bot-builder-dotnet-connector
Here is a static example of using it to proactively send a message to a user from a bot: MicrosoftDX/botFramework-proactiveMessages - sample: ConversationStarter.cs
pertinent code from sample:
public static async Task Resume(string conversationId,string channelId)
{
var userAccount = new ChannelAccount(toId,toName);
var botAccount = new ChannelAccount(fromId, fromName);
var connector = new ConnectorClient(new Uri(serviceUrl));
IMessageActivity message = Activity.CreateMessageActivity();
if (!string.IsNullOrEmpty(conversationId) && !string.IsNullOrEmpty(channelId))
{
message.ChannelId = channelId;
}
else
{
conversationId = (await connector.Conversations.CreateDirectConversationAsync( botAccount, userAccount)).Id;
}
message.From = botAccount;
message.Recipient = userAccount;
message.Conversation = new ConversationAccount(id: conversationId);
message.Text = "Hello, this is a notification";
message.Locale = "en-Us";
await connector.Conversations.SendToConversationAsync((Activity)message);
}
The serviceUrl, the channelId, conversationId, toId, fromId, etc are cached from previous communication by the user to the bot (these are statically stored in this example, so only work for one user). This example shows how it is possible to proactively send a message to a user from a bot. The Direct Line api is not required.
You don't need to use DirectLine, it is designed for creating alternative bot UIs.
To implementing what your want, you may try the following:
First, you need to store users addresses to whom you want to send the messages. It my be done by storing the ResumptionCookie of a user last message in your backend database.
var state = new ResumptionCookie(message).GZipSerialize();
When your PostConversationsEvents is called, you may resume the conversation at the latest point with each users.
var resumptionCookie = ResumptionCookie.GZipDeserialize(state);
var message = resumptionCookie.GetMessage();
message.Text = content;
await Conversation.ResumeAsync(resumptionCookie, message);
It is not the only solution. As I said, in this case you just resumed the conversation with the user at the latest point. Another solution is to save the user address (user the same ResumptionCookie class) but start the conversation when you need to:
var resumptionCookie = ResumptionCookie.GZipDeserialize(state);
var message = cookie.GetMessage();
ConnectorClient client = new ConnectorClient(new Uri(message.ServiceUrl));
var conversation = await
client.Conversations.CreateDirectConversationAsync(message.Recipient, message.From);
message.Conversation.Id = conversation.Id;
var newMessage = message.CreateReply();
newMessage.Text = content;
await client.Conversations.SendToConversationAsync(newMessage);
See more details on BotFramework documentation.
I'm trying to set up a backchannel to my bot/web page so I can send events between the two. I've added the example shown in this question Bot framework get the ServiceUrl of embedded chat control page
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
if (activity.Type == ActivityTypes.Event &&
string.Equals(activity.Name, "buttonClicked", StringComparison.InvariantCultureIgnoreCase))
{
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
// return our reply to the user
Activity reply = activity.CreateReply("I see that you just pushed that button");
await connector.Conversations.ReplyToActivityAsync(reply);
}
if (activity.Type == ActivityTypes.Message)
{
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
// return our reply to the user
var reply = activity.CreateReply();
reply.Type = ActivityTypes.Event;
reply.Name = "changeBackground";
reply.Value = activity.Text;
await connector.Conversations.ReplyToActivityAsync(reply);
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
However ActivityTypes.Event does not exist as well as activity.Name. Do i need a special package to handle backchannel with the bot framework?
Per your comments, it seems you are using an older version of the BotBuilder nuget package; that's why you don't have those properties/values.
Please update to the latest version of BotBuilder v3.5.5 and that will make it.
I have created a MS bot framework API which will be called by more then 1 BOT.
I have created a Class to override the BotAuthentication and add this to the controller.
[BotAuthentication(CredentialProviderType = typeof(BotCredential))]
Here is BotCredential Class file:
public class BotCredential : ICredentialProvider
{
public async Task<string> GetAppPasswordAsync(string appId)
{
//return the app password
}
public async Task<bool> IsAuthenticationDisabledAsync()
{
return false;
}
public async Task<bool> IsValidAppIdAsync(string appId)
{
//return if appid is valid
}
}
Now in the Controller Post method:
MicrosoftAppCredentials credential = new MicrosoftAppCredentials()
{
MicrosoftAppId ="Appid received from the claim",
MicrosoftAppPassword = "Appsecret available from the claim"
};
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl), credential);
Activity reply= activity.CreateReply("Some reply");
await connector.Conversations.ReplyToActivityAsync(reply);
So now everything working fine for the first time.
For Example I have two Bots Bot1 and Bot2:
First I am sending a message from Bot1 and getting the reply. But after that, if I am sending a message from Bot2, the first message is giving exception with message:
doesn't match a known conversation
and for the second message from Bot2, it is giving the reply, and again if I am sending a message from Bot1 same thing is happening, and for the first message I am getting the same error and from second message. Onward it is working fine.