I have enabled signin card in my BOT framework to redirect to the client application URL and it worked perfectly find in the BOT emulator and from the WEBCHAT. But i have issues in the Cortana channel.
While clicking on the button, instead of opening a default browser with the client application ur, a popup opens and says 'we cant connect to the service right now. check your network connection or try again'
Here is my code sample in the BOT framework::
public async Task getmypersonalData(IDialogContext context, LuisResult result)
{
string convid = _convid;
ConnectorClient connector = new ConnectorClient(new Uri(_serviceURL));
var replyToConversation = context.MakeMessage();
// Activity replyToConversation = _activity.CreateReply();
replyToConversation.Recipient = new ChannelAccount(id: _fromAddess);// _activity.From;
replyToConversation.Type = "message";
replyToConversation.Attachments = new List<Attachment>();
List<CardAction> cardButtons = new List<CardAction>();
CardAction plButton = new CardAction()
{
Value = $"{System.Configuration.ConfigurationManager.AppSettings["AppWebSite"]}?conversationid={HttpUtility.UrlEncode(convid)}",
Type = "signin",
Title = "Authentication Required"
};
cardButtons.Add(plButton);
SigninCard plCard = new SigninCard("Please login to the application to access this feature", new List<CardAction>() { plButton });
Attachment plAttachment = plCard.ToAttachment();
replyToConversation.Attachments.Add(plAttachment);
await context.PostAsync(replyToConversation);
}
Related
I am receiving bad request when I including an attachment as an inline picture for the MS Teams Bot using the following code (the code works without attachments):
var message = Activity.CreateMessageActivity();
message.Text = "message here";
message.Attachments = new List<Attachment>();
var webClient = new WebClient();
byte[] imageBytes = webClient.DownloadData("https://img.icons8.com/windows/452/showing-small-size.png");
string url = "data:image/png;base64," + Convert.ToBase64String(imageBytes);
message.Attachments.Add(new Attachment
{
ContentType = "image/png",
ContentUrl = url
});
var conversationParameters = new ConversationParameters
{
IsGroup = true,
ChannelData = new TeamsChannelData
{
Channel = new ChannelInfo(outputChannelId),
},
Activity = (Activity)message
};
var response = await connectorClient.Conversations.CreateConversationAsync(conversationParameters);
I received the following error:
Microsoft.Bot.Schema.ErrorResponseException: Operation returned an invalid status code 'BadRequest'
at Microsoft.Bot.Connector.Conversations.CreateConversationWithHttpMessagesAsync
Thanks in advance!
Rather than including the image as an attachment, consider instead sending an Adaptive Card and you can display the image directly to the user. See more on Adaptive Cards here.
As an alternative if you don't want to show the image but it's hosted somewhere, just include a link to it in the text message, using the markdown format that regular text message support in Teams (see more here).
I want to connect my bot emulator to teams for sending a query(This is working)
Once we receive a query in teams, answer to the query should be sent back to bot emulator
I'm not receiving answer from teams to bot emulator
To connect from web chat to teams I'm using below code :
string teamsChannelId = "****************************";
string serviceUrl = "****************************";
string botClientID = "****************************";
string botClientSecret = "****************************";
MicrosoftAppCredentials.TrustServiceUrl(serviceUrl);
var connectorClient = new ConnectorClient(new Uri(serviceUrl), new MicrosoftAppCredentials(botClientID, botClientSecret));
var topLevelMessageActivity = MessageFactory.Text(saveconv);
var conversationParameters = new ConversationParameters
{
IsGroup = true,
ChannelData = new TeamsChannelData
{
Channel = new ChannelInfo(teamsChannelId),
},
Activity = topLevelMessageActivity
};
await connectorClient.Conversations.CreateConversationAsync(conversationParameters);
To send response from Microsoft teams to web chatbot below code worked:
var userAccount = new ChannelAccount(id: "userid", name: "username", role: "user", aadObjectId: null);
var botAccount = new ChannelAccount(id: "botid", name: "botname", role: "bot", aadObjectId: null);
string botClientID = "****************************";
string botClientSecret = "****************************";
string serviceUrl = "****************************";
MicrosoftAppCredentials.TrustServiceUrl(serviceUrl);
var connector = new ConnectorClient(new Uri(serviceUrl), new MicrosoftAppCredentials(botClientID, botClientSecret));
IMessageActivity message = Activity.CreateMessageActivity();
message.ChannelId = "channelid";
message.From = botAccount;
message.Recipient = userAccount;
message.Conversation = new ConversationAccount(id: "conversationid");
message.Text = "Reply from Microsoft Teams: *" + turnContext.Activity.Text;
message.Locale = "en-us";
await connector.Conversations.SendToConversationAsync((Activity)message);
I made a bot with bot framework v4, using C#, and it's on a webpage, https://websitebotv2.azurewebsites.net/, if there's only 1 user it works fine but the moment I open it on a new tab it gives a IndexOutOfRangeException when I start the conversation.
What do I need to do to make it work with multiple tabs open?
When my bot stars it creates a waterfall dialog asking the name and greeting the user:
public dialogBotBot(dialogBotAccessors accessors, LuisRecognizer luis, QnAMaker qna)
{
// Set the _accessors
_accessors = accessors ?? throw new ArgumentNullException(nameof(accessors));
// The DialogSet needs a DialogState accessor, it will call it when it has a turn context.
_dialogs = new DialogSet(accessors.ConversationDialogState);
// This array defines how the Waterfall will execute.
var waterfallSteps = new WaterfallStep[] {
NameStepAsync,
NameConfirmStepAsync,
};
// The incoming luis variable is the LUIS Recognizer we added above.
this.Recognizer = luis ?? throw new System.ArgumentNullException(nameof(luis));
// The incoming QnA variable is the QnAMaker we added above.
this.QnA = qna ?? throw new System.ArgumentNullException(nameof(qna));
// Add named dialogs to the DialogSet. These names are saved in the dialog state.
_dialogs.Add(new WaterfallDialog("details", waterfallSteps));
_dialogs.Add(new TextPrompt("name"));
}
Then I will save his name on UserProfile class, which contains the field Name and Context, the Context has the purpose of saving the conversation.
This works the first time, but if I open a new tab or refresh the current tab for a new conversation the bot will fetch the first conversation data.
The Exception is thrown in Startup.cs in:
services.AddBot<dialogBotBot>(options =>
{
options.CredentialProvider = new ConfigurationCredentialProvider(Configuration);
// Catches any errors that occur during a conversation turn and logs them to currently
// configured ILogger.
ILogger logger = _loggerFactory.CreateLogger<dialogBotBot>();
options.OnTurnError = async (context, exception) =>
{
logger.LogError($"Exception caught : {exception}");
await context.SendActivityAsync(exception + "\nSorry, it looks like something went wrong.\n" + exception.Message);
};
// Create and add conversation state.
var conversationState = new ConversationState(dataStore);
options.State.Add(conversationState);
// Create and add user state.
var userState = new UserState(dataStore);
options.State.Add(userState);
});
My onTurnAsync method is:
public async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
// Handle Message activity type, which is the main activity type for shown within a conversational interface
// Message activities may contain text, speech, interactive cards, and binary or unknown attachments.
// see https://aka.ms/about-bot-activity-message to learn more about the message and other activity types
if (turnContext.Activity.Type == ActivityTypes.Message)
{
//Get the current user profile
userProfile = await _accessors.UserProfile.GetAsync(turnContext, () => new UserProfile(), cancellationToken);
userProfile.Contexto.Add(turnContext.Activity.Text);
foreach (string s in userProfile.Contexto)
await turnContext.SendActivityAsync(s);
// Get the conversation state from the turn context.
var state = await _accessors.CounterState.GetAsync(turnContext, () => new CounterState());
// Bump the turn count for this conversation.
state.TurnCount++;
// Check LUIS model
var recognizerResult = await this.Recognizer.RecognizeAsync(turnContext, cancellationToken);
var topIntent = recognizerResult?.GetTopScoringIntent();
// Get the Intent as a string
string strIntent = (topIntent != null) ? topIntent.Value.intent : "";
// Get the IntentScore as a double
double dblIntentScore = (topIntent != null) ? topIntent.Value.score : 0.0;
// Only proceed with LUIS if there is an Intent
// and the score for the Intent is greater than 95
if (strIntent != "" && (dblIntentScore > 2))
{
switch (strIntent)
{
case "None":
//add the bot response to contexto
await turnContext.SendActivityAsync("Desculpa, não percebi.");
break;
case "Utilities_Help":
//add the bot response to contexto
await turnContext.SendActivityAsync("Quero-te ajudar!\nO que precisas?");
break;
default:
// Received an intent we didn't expect, so send its name and score.
//add the bot response to contexto
await turnContext.SendActivityAsync($"Intent: {topIntent.Value.intent} ({topIntent.Value.score}).");
break;
}
}
else
{
if (userProfile.Name == null)
{
// Run the DialogSet - let the framework identify the current state of the dialog from the dialog stack and figure out what (if any) is the active dialog.
var dialogContext = await _dialogs.CreateContextAsync(turnContext, cancellationToken);
var results = await dialogContext.ContinueDialogAsync(cancellationToken);
// If the DialogTurnStatus is Empty we should start a new dialog.
if (results.Status == DialogTurnStatus.Empty)
{
await dialogContext.BeginDialogAsync("details", null, cancellationToken);
}
}
else
{
var answers = await this.QnA.GetAnswersAsync(turnContext);
if (answers.Any() && answers[0].Score > 0.7)
{
// If the service produced one or more answers, send the first one.
await turnContext.SendActivityAsync(answers[0].Answer + "\n" + state.TurnCount);
}
else
{
var responseMessage = $"Ainda não sei a resposta mas vou averiguar\nPosso-te ajudar com mais alguma coisa?";
String connectionString = "Data Source=botdataserverv1.database.windows.net;" +
"Initial Catalog=botDataBase;" +
"User id=AzureAdmin#botdataserverv1.database.windows.net;" +
"Password=admin_123;";
SqlConnection connection = new SqlConnection(connectionString);
SqlDataAdapter adapter = new SqlDataAdapter();
SqlCommand command;
String sms = turnContext.Activity.Text;
float result = answers[0].Score;
String insertMessage = "insert into Mensagem(texto,contexto,grauCerteza)" +
"values('" + sms + "', 'Falta apurar o contexto' ," + result + ")";
connection.Open();
command = new SqlCommand(insertMessage, connection);
adapter.InsertCommand = new SqlCommand(insertMessage, connection);
adapter.InsertCommand.ExecuteNonQuery();
command.Dispose();
connection.Close();
await turnContext.SendActivityAsync(responseMessage);
}
}
// Save the user profile updates into the user state.
await _accessors.UserState.SaveChangesAsync(turnContext, false, cancellationToken);
// Set the property using the accessor.
await _accessors.CounterState.SetAsync(turnContext, state);
// Save the new turn count into the conversation state.
await _accessors.ConversationState.SaveChangesAsync(turnContext);
}
}
}
Your problem is here:
float result = answers[0].Score;
You have:
// Check to see if we have any answers
if (answers.Any() && answers[0].Score > 0.7)
{
[...]
// This is fine
}
else // else, WE HAVE NO ANSWERS
{
[...]
// At this point, answers is an empty array, so answers[0] throws an IndexOutOfRangeException
float result = answers[0].Score;
The reason this happens on refresh is because the new tab's user uses the same User Id. The bot already knows their name, so doesn't show the dialog, and when it calls await this.Recognizer.RecognizeAsync(turnContext, cancellationToken);, the user hasn't entered anything in the new turnContext, so it returns an empty array.
Sidenote: You can set the userID in WebChat with this:
window.WebChat.renderWebChat(
{
directLine: directLine,
userID: "USER_ID" // Make is use Math.random() or something if you want it to be random for each refresh
},
this.botWindowElement.nativeElement
);
I'm using the Microsoft Bot Framework with C# and I'm trying to create a reply message with an image like so:
IMessageActivity m = Activity.CreateMessageActivity();
var images = doc.DocumentNode.SelectNodes("//img[#src]").ToList();
var src = images[10].GetAttributeValue("src", null);
Attachment att = new Attachment();
att.ContentType = "image";
att.ContentUrl = src;
m.Attachments.Add(att);
await context.PostAsync(m);
context.Wait(MessageReceived);
I know that the image source is being extracted correctly but I still get the error:
Microsoft.Rest.ValidationException
What is the cause of this and how do I fix it?
I expect to have a message with the image attached but instead I just get the default bot code error message.
Edit: this is the exception message - 'ReplyToId' cannot be null.
If you are creating a message using the Activity.CreateMessageActivity() then you need to specify the details of user account(ToId,ToName), bot account(FromId, FromName) and conversation(conservationId, channelId).
eg:
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 = "The text you want to send";
//You can add your attachment here
message.Locale = "en-Us";
await connector.Conversations.SendToConversationAsync((Activity)message);
Looking at your code it looks like you can access the context of the Dialog since you are posting the message using context.PostAsync, so the easier way would be to send a message using the context since the context will already contain the details of user, bot and conversation.
You can do this by using context.MakeMessage()
eg:
IMessageActivity reply = context.MakeMessage();
var images = doc.DocumentNode.SelectNodes("//img[#src]").ToList();
var src = images[10].GetAttributeValue("src", null);
Attachment att = new Attachment();
att.ContentType = "image/png";
att.ContentUrl = src;
reply.Attachments.Add(att);
await context.PostAsync(reply);
context.Wait(MessageReceived);
var msg = context.MakeMessage();
msg.Attachments = new List<Attachment>();
SigninCard card = new SigninCard()
{
Text= "link it",
Buttons = new List<CardAction>
{
new CardAction
{
Value = "account linking url https",
Type = "account_link",
Title = "Link"
},
}
};
msg.Attachments.Add(card.ToAttachment());
await context.PostAsync(msg);
I'm trying to use SignInCard to link Facebook account.
This error appears:
{"error":{"message":"(#100) Web url cannot be empty for url type
button","type":"OAuthException","code":100,"error_subcode":2018041,"fbtrace_id":"GclYUUuTL2D"}}
But there is a string https url in url field.
Any idea ?
I figured this out.
var msg = context.MakeMessage();
dynamic messageData = new JObject();
messageData.attachment = new JObject();
messageData.attachment.type = "template";
messageData.attachment.payload = new JObject();
messageData.attachment.payload.template_type = "generic";
messageData.attachment.payload.elements
= new JArray(
new JObject(
new JProperty("title", "title"),
new JProperty("subtitle", "Link your account"),
new JProperty("buttons",
new JArray(
new JObject(
new JProperty("type", "account_link"),
new JProperty("url", "yourUrl")
)
)
)
)
);
msg.ChannelData = messageData;
await context.PostAsync(msg);
You don't have to get into channel-specific ChannelData. In your SigninCard's Buttons initializer, instead of
Type = "account_link",
use
Type = ActionTypes.Signin,
Worked on my Facebook Messenger channel.