MS bot reply twice - botframework

At localhost in the emulator, as soon as I connect to the bot I get the sign in card but in the azure registered channel web chat I don't get the sign in card instead I need to type something then I get it twice. webchat screenshot
How do I get the sign in card without the need to type something in chat.
I've turned the always on option and it still didn't help.
The sign in card come from the community auth middleware Link here
and i use the following code in the onTurn's bot class:
public async Task OnTurn(ITurnContext turnContext)
{
if (turnContext.Activity.UserHasJustJoinedConversation() || turnContext.Activity.UserHasJustSentMessage())
{
var state = turnContext.GetConversationState<Dictionary<string, object>>();
var dialogCtx = dialogs.CreateContext(turnContext, state);
await dialogCtx.Continue();
if (!turnContext.Responded)
{
await dialogCtx.Begin("mainDialog", new Dictionary<string, object>
{
["Value"] = turnContext.Activity.Text
});
}
}
}
and i also use the following two functions
public static bool UserHasJustSentMessage(this Activity activity)
{
return activity.Type == ActivityTypes.Message;
}
public static bool UserHasJustJoinedConversation(this Activity activity)
{
return activity.Type == ActivityTypes.ConversationUpdate && activity.MembersAdded.FirstOrDefault().Id != activity.Recipient.Id;
}

There are two ConversationUpdates that get emitted when a conversation starts: one for the user joining the conversation and one for the bot joining the conversation so you need filter out the bot's one:
public static bool UserHasJustJoinedConversation(this Activity activity)
{
return activity.Type == ActivityTypes.ConversationUpdate
&& activity.MembersAdded.Any(channelAccount => channelAccount.Id != "YourBotName");
}

Related

Manage the state in Production in Bot FrameWork v4

I create One Askstate and AskDialog in Bot FrameWork V4 After release in production sometime state variables are not giving correct value. Below is my code for reference.
the Code Is Working Fine in Emulator though it Not Work Correctly in Production. following is code For referance.
// In Dialogbot class
private readonly IStatePropertyAccessor<AskState> _askStateAccessor;
private readonly UserState _userState;
protected readonly ConversationState _conversationState;
// In Dialogbot constructor
_userState = userState ?? throw new ArgumentNullException(nameof(userState));
_askStateAccessor = _userState.CreateProperty<AskState>(nameof(AskState));
Dialogs = new DialogSet(_dialogStateAccessor);
Dialogs.Add(new AskDialog(_askStateAccessor, _spService, _dataStateAccessor, loggerFactory));
// In OnTurnAsync method
var valueAskState = await _askStateAccessor.GetAsync(turnContext, () => new AskState());
if (valueAskState.IsWhatToWhereKeywords != null)
{
// logic
}
await _conversationState.SaveChangesAsync(turnContext);
await _userState.SaveChangesAsync(turnContext);
// In AskDialog
public IStatePropertyAccessor<AskState> UserProfileAccessor { get; }
// In AskDialog's PromptForEntitiesStepAsync method
var askState = await UserProfileAccessor.GetAsync(stepContext.Context);
if(condition)
{
askState.IsWhatToWhereKeywords = keyword; // setting state variable
}
As per below is a screenshot "aaa" which is keyword should be stored into one state variable.
https://i.stack.imgur.com/cJ12v.png
let me know if anyone has solution.
thanks in Advance.

How can i get the list of reacted users JDA?

I am trying to make a bot that edit embed message after reaction added.
Specifically to addField to old embed with List of reaction users.
How can i get the list of reacted users?
I am using the latest version of the Discord JDA.
public void onMessageReactionAdd(MessageReactionAddEvent e) {
if (e.getReactionEmote().getName().equals("👍")) {
if (!e.getUser().isBot()) {
e.getChannel().retrieveMessageById(e.getMessageId()).queue(p -> {
if (p.getEmbeds().size() > 0) {
if (p.getEmbeds().get(0) != null) {
EmbedBuilder gameEmbedWithPlayers = new EmbedBuilder()
.setTitle(p.getEmbeds().get(0).getTitle())
.setDescription(p.getEmbeds().get(0).getDescription())
.setThumbnail(p.getEmbeds().get(0).getThumbnail().getUrl())
.setFooter(p.getEmbeds().get(0).getFooter().getText(), p.getEmbeds().get(0).getFooter().getIconUrl())
.setColor(p.getEmbeds().get(0).getColor())
//help here
.addField("🔻 Players", REACTIONED USERS, false);
p.editMessage(gameEmbedWithPlayers.build()).queue();
}
}
});
}
}
}
You'll want to use MessageReaction#retrieveUsers(). You can get the MessageReaction by using Message#getReactions().
Example:
List<User> reactedUsers = new ArrayList<>();
for (MessageReaction reaction : msg.getReactions()){
reactedUsers.addAll(reaction.retrieveUsers().complete());
}

Remember previous answers to guide conversation

I'm creating a chatbot using LUIS template and I have an intent called StartTest with utterances like these:
Begin [TestNameEntity] on [GearEntity] and [RPMEntity]
Begin [TestNameEntity] on [GearEntity]
Begin [TestNameEntity]
If the user input matches the first utterance, I have all entities I need. However, if its input matches the second utterance, I have to ask him the RPM. And for the third utterance I have to ask the gear and the RPM.
But for the second and third utterances I need to know what the user has said for the [TestNameEntity], like this:
User: Begin test 1 on second gear
Bot: What RPM?
User: 2500
How can I achieve this?
once your Intent method is called you can use this logic
result.Entities[0].Type to get entity name
so you can iterate result.Entities to get all the entities user has entered. If any of the three entities is missing you can ask for a prompt. This is a sample code of how to achieve it.
//global variables
public string CurrentGear { get; set; }
public string CurrentRpm { get; set; }
[LuisIntent("StartTest")]
public async Task StartTestIntent(IDialogContext context, LuisResult result)
{
if (result.Entities != null && result.Entities.Count >0)
{
//sample you will get selected entity type
//var ent1=result.Entities[0].Type;
var userValues=GetUserEntities(result);
if(userValues["GearEntity"]==null)
showGearPrompt(context);
if(userValues["RPMEntity"]==null)
showRpmPrompt(context);
}
}
private string[] GetUserEntities(LuisResult result)
{
//your logic here
//return list of entities;
}
private async Task showGearPrompt(IDialogContext context)
{
PromptDialog.Text(
context: context,
resume: OnGearOptionReceivedAsync,
prompt: "please enter Gear Value",
retry: "Sorry, I didn't understand that. Please try again."
);
}
public virtual async Task OnGearOptionReceivedAsync(IDialogContext context, IAwaitable<string> gear)
{
string response = await gear;
CurrentGear = response;
}
private async Task showRpmPrompt(IDialogContext context)
{
PromptDialog.Text(
context: context,
resume: OnRpmOptionReceivedAsync,
prompt: "please enter RPM Value",
retry: "Sorry, I didn't understand that. Please try again."
);
}
public virtual async Task OnRpmOptionReceivedAsync(IDialogContext context, IAwaitable<string> rpm)
{
string response = await rpm;
CurrentRpm = response;
}
Link to how to use prompts

Use Bing Spell Check before Microsoft Translate which is called before call to LUIS

So...
I'm trying to use the Bot Framwork with LUIS in Swedish.
Using the samples I implemented translation of the input from Swedish to English and then called the LUIS functionality.
It worked perfect until we got some very strange intent hits from LUIS.
What we found out was that a very small spelling error (in Swedish) caused the translation to create a message that triggered wrong intent.
We can solve the problem by checking the score of the received intent, but the message back to the user "I didn't understand that" isn't especially helpful.
Running the same message through Bing Spell Check and replace the faulty text with the correct one will produce a correct behaviour (mostly).
What I would like to do is to use the result from the Spell Check to ask the user if the text he/she should be replace with the result from Bing.
Now the problem: I can't find a proper way to implement the dialog to the user. (If possible, I would like to avoid using the PromptDialog.Confirm since it is tricky to localize)
What I have now (that doesn't work) is roughly:
if (activity.Type == ActivityTypes.Message)
{
correctedText = await sc.BingSpellCheck(activity.Text, spellValues);
if (spellValues.Count > 0)
{
// Ask the client if the correction is ok
await Conversation.SendAsync(activity, () => new CorrectSpellingDialog());
}
Translate.Current.ToEnglish(activity.Text, "en");
await Conversation.SendAsync(activity, () => new MeBotLuisDialog());
}
What I would like here is to create a CorrectSpellingDialog() that just returns true or false, nad if it is true I will call the ...MeBotLuisDialog().
Sorry for all the text but it's a long problem :-)
Any takers on this?
(The other solution I had was to create an Intent "SpellCheckError" that is trigged from the Bing Spell Check and the in the intent send a message with the corrected message back to the bot (even though I don't know I that is doable in a proper way))
// Tommy
To enable Bing Spell Check API in your bot, you will first get the key from Bing service, then in the Web.config file add the BingSpellCheckApiKey and together enable IsSpellCorrectionEnabled for example:
<appSettings>
<!-- update these with your BotId, Microsoft App Id and your Microsoft App Password-->
<add key="BotId" value="YourBotId" />
<add key="MicrosoftAppId" value="" />
<add key="MicrosoftAppPassword" value="" />
<add key="BingSpellCheckApiKey" value="YourBingSpellApiKey" />
<add key="IsSpellCorrectionEnabled" value="true" />
</appSettings>
Then you can create a component for your bot to use Bing Spell Api, for more information, you can refer to Quickstart for Bing Spell Check API with C#. Here you can a service in your app for example like this Service.
Then in MessagesController, check the spell first before sending message to dialog. and since your want to show a confirm dialog to user if the text should be replace with the result from Bing, you can send a FormFlow first to let user to have a choice. For example:
Code of MessagesController:
private static readonly bool IsSpellCorrectionEnabled = bool.Parse(WebConfigurationManager.AppSettings["IsSpellCorrectionEnabled"]);
private BingSpellCheckService spellService = new BingSpellCheckService();
/// <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)
{
if (IsSpellCorrectionEnabled)
{
try
{
var text = await this.spellService.GetCorrectedTextAsync(activity.Text);
if(text != activity.Text)
{
//if spelling is wrong, go to rootdialog
await Conversation.SendAsync(activity, () => new Dialogs.RootDialog(activity.Text, text));
}
else
{
//if right, direct go to luisdialog
await Conversation.SendAsync(activity, () => new Dialogs.MyLuisDialog());
}
}
catch(Exception ex)
{
Trace.TraceError(ex.ToString());
}
}
else
{
await Conversation.SendAsync(activity, () => new Dialogs.MyLuisDialog());
}
}
else
{
HandleSystemMessage(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
Code of my RootDialog:
[Serializable]
public class RootDialog : IDialog<object>
{
private string otext;
private string ntext;
public RootDialog(string oldtext, string newtext)
{
otext = oldtext;
ntext = newtext;
}
public async Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
}
public async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
var form = new FormDialog<Confirmation>(new Confirmation(otext, ntext),
Confirmation.BuildForm, FormOptions.PromptInStart, null);
context.Call(form, this.GetResultAsync);
}
private async Task GetResultAsync(IDialogContext context, IAwaitable<Confirmation> result)
{
var state = await result;
await context.Forward(new MyLuisDialog(), null, context.Activity, System.Threading.CancellationToken.None);
}
}
[Serializable]
public class Confirmation
{
public string Text { get; set; }
private static string otext;
private static string ntext;
public Confirmation(string oldtext, string newtext)
{
otext = oldtext;
ntext = newtext;
}
public static IForm<Confirmation> BuildForm()
{
return new FormBuilder<Confirmation>()
.Field(new FieldReflector<Confirmation>(nameof(Text))
.SetType(null)
.SetDefine(async(state, field) =>
{
field
.AddDescription(otext, otext)
.AddTerms(otext, otext)
.AddDescription(ntext,ntext)
.AddTerms(ntext, ntext);
return true;
}))
.Build();
}
}

Custom Async Action Filter for Web API 2

I have a web api to consume the data coming from android mobile. This web api will consume the multi part file from along with the form data the web api request. I followed this article to archive.
[CustAuthAsync]
public async Task<HttpResponseMessage> SaveEHSInspectionData()
{
try
{
string root = HttpContext.Current.Server.MapPath("~/App_Data");
MultipartFormDataStreamProvider provider = new MultipartFormDataStreamProvider(root);
//do stuff
var res = await Request.Content.ReadAsMultipartAsync(provider);
// DO SOME STUFF
}
catch (Exception exp)
{
}
return Request.CreateResponse(HttpStatusCode.OK, result);
}
I wanted to do the custom access validation for this web api, so implemented a filter to validate the request.
I have the filter like below
public class CustAuthAsyncAttribute : ActionFilterAttribute
{
public override async Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
InternalOnExecutingAsync(actionContext);
}
}
The internal method like this
protected void InternalOnExecutingAsync(HttpActionContext actionContext)
{
var authValue = actionContext.Request.Headers;
if (authValue.Contains("CustomAccessToken"))
{
string token = authValue.GetValues("CustomAccessToken").First();
var result = // doing some decription
if (result != null)
{
bool validationResult = // validation with database
if (!validationResult)
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{ ReasonPhrase = "Invalid token" };
}
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{ ReasonPhrase = "Invalid token" };
}
}
else
{
actionContext.Response = new HttpResponseMessage(HttpStatusCode.Unauthorized)
{ ReasonPhrase = "Unauthorized Request" };
}
These implementations are working fine in API Client Tools (Example: Postman) if the validation passes, allows the request to the method.
Postman Response screen shot
This is not working in mobile app, Saying the response message as Unauthorized Access. and not allowing the request to the method even the custom access validations are passed.
FYI : This method is working fine in mobile without filter
Help me to get this works in mobile app also.
Thanks in advance.
Your using the wrong type of filter to manage access. You should use an authorization filter. Besides you can't have an async method to authorize. You have to make the calling client wait for clearance. This may cause the side effects you're experiencing.
I'm not sure this has any to do with fact that it's a mobile application, however the authorization phase ir prior to the processing of the request. Verify that your are not using any other form of authorization in your project.
You should implement an authorization filter by inheriting AuthorizeAttribute and overriding IsAuthorized(HttpActionContext actionContext) method:
public class CustAuthAsync : AuthorizeAttribute
{
public CustAuthAsync()
{
///Some initialization if required. Otherwise, not necessary to declare the constructor..
}
protected override bool IsAuthorized(HttpActionContext actionContext)
{
var authValue = actionContext.Request.Headers;
if (authValue.Contains("CustomAccessToken"))
{
string token = authValue.GetValues("CustomAccessToken").First();
var result = // doing some decription
if (result != null)
{
return //database validation
}
else
{
return false;
//No need to create special unauthorized response. You should not hint the reason at this point. You can do this in the HandleUnauthorizedRequest method.
}
}
else
{
return false;//No need to create special unauthorized response.
}
}
}
You can use this attribute to decorate your controllers. You can even pass parameter in the constructor for more granular control on access management, like a required role to access de controller.

Resources