Is it possible to get different message from same intent - c#

I am trying to get different message from same intent. Let us consider that i have a intent Greeting , so when user say 'Hi' to Bot then bot call the Greeting intent then bot show the message "hello and welcome" to the user.
If you say Hi again to the bot then i want the different message from the bot like "hello and welcome again".
here is my intent code
new OnIntent("Greeting")
{
Actions = new List<Dialog>()
{
new CodeAction(async (dialogContext, options) =>
{
var now = DateTime.Now.TimeOfDay;
var time = now < new TimeSpan(12, 0, 0)
? "morning"
: now > new TimeSpan(19, 0, 0)
? "evening"
: "afternoon";
dialogContext.State.SetValue("dialog.greetingTime", time);
dialogContext.State.SetValue("user.name",CustmerName);
return await dialogContext.EndDialogAsync(options);
}),
new SendActivity("${HelpRootDialog()}")
}
},

You can make use of conversation state. Add a boolean field called isUserGreeted, when the intent is hit for the first time, set it as true.
When the intent is hit again, check the conversation state to see if the user was already greeted, if yes you can send the user the second hello message.
If you want to reset the greeted flag based on a date, you can also store the date information to see if the user was greeted today.

Related

How do I check if a user exists in Dsharplus (C# discord bot)

Basically I want if someone types "whois (mention user)" the bot to send a message like "(user mentioned in first message) is (random message from array)"
I just need to know 2 things
How do I only send a message if it's an actual mentioned user
and How do I set a variable something only if a certain user (my disc account) is mention
[Description("Mention someone after and it will tell you all about them")]
public async Task WhoIs(CommandContext ctx, [Description("Mention a User")] string name)
{ string[] descriptions = {"", "", "", "", ""};
Random rng = new Random();
int rngOk = rng.Next(descriptions.Length);
//if (name == "a certain user")
//{
// rngOk = 3;
//}
await ctx.Channel.SendMessageAsync(name + descriptions[rngOk]).ConfigureAwait(false);
}
This is my code so far
If you need any additional information, just ask
Any help would be appreciated
I don't know precisely what you are trying to ask, your question is very vague but I will do my best to answer it
First Question
For the first part of your question "How do I only send a message if it's an actual mentioned user", you need to have a DiscordUser parameter in your command like this
[Command("test")]
public async Task MentionUserExample(CommandContext ctx, DiscordUser user)
{
if (user.Id == "Put the ID of the user here")
{
//Write whatever you are trying to do here. This will only trigger if the
//user you mention, matches the ID of the one you set here
}
else
{
//Show an error message or something similar
}
}
By doing this, when a user does "!help #RandomUser", the if statement will compare the ID of the mentioned user, to the ID of the user you set in the if statement
Second Question
Your second question "How do I set a variable something only if a certain user (my disc account) is mention", I don't understand properly. What are you trying to achieve here.
If we are mentioning your account as an example, why do you want to store it in a variable, if you are using it for something else it makes sense but once again your question is very vague
Once again you need the DiscordUser parameter in your command
[Command("test")]
public async Task MentionUserExample(CommandContext ctx, DiscordUser user)
{
if (user.Id = "The ID of the user you want to store")
{
DiscordUser UserToStore = user;
}
}
From here on you can use this variable and access many different properties as you would normally like Id, Username etc

botFramework v4 how to handle dialog response after LUIS call

I have a bot written in C# that is using LUIS to determine intents. I have a method that makes a call to the LUIS service and then looks for an 'Open_Case' intent. The model has a CaseNumber entity defined which may or may not be included in the response from the LUIS service.
If the response doesn't have a case number entity I start a dialog to ask the user for the case number.
Once I have a case number I then want to return a card with case information.
Here's the code I have:-
/// <summary>
/// Dispatches the turn to the requested LUIS model.
/// </summary>
private async Task DispatchToLuisModelAsync(ITurnContext context, string appName, DialogContext dc, CancellationToken cancellationToken =
default (CancellationToken)) {
var result = await botServices.LuisServices[appName].RecognizeAsync(context, cancellationToken);
var intent = result.Intents ? .FirstOrDefault();
string caseNumber = null;
if (intent ? .Key == "Open_Case") {
if (!result.Entities.ContainsKey("Case_CaseNumber")) {
var dialogResult = await dc.BeginDialogAsync(CaseNumberDialogId, null, cancellationToken);
} else {
caseNumber = (string)((Newtonsoft.Json.Linq.JValue) result.Entities["Case_CaseNumber"].First).Value;
var cardAttachment = botServices.CaseInfoServices.LookupCase(caseNumber);
var reply = context.Activity.CreateReply();
reply.Attachments = new List < Attachment > () {
cardAttachment
};
await context.SendActivityAsync(reply, cancellationToken);
}
}
}
What I'm struggling with is where the code send the card response should sit.
In the code I currently have I send the card if the number was returned in the LUIS response, but if there was no number and I start the dialog then I only get access to the number either in the final step of the dialog or in the dialog result in the root turn handler. I've currently duplicated the reply inside the final step in the dialog, but it feels wrong and inelegant.
I'm sure there must be a way that I can collect the number from LUIS or the dialog and THEN send the response from a single place instead of duplicating code.
Any suggestions gratefully received...
I came to the conclusion that I need to put the code that displays the card into a method on the bot class, then call it from the else in code snippet and also from the turn handler when the dialogTurnStatus is equal to Complete

Bot Framework Context Wait not waiting for next message

I'm trying to build a Dialog using the Microsoft Bot Framework which helps users consult purchase order status (currently, just a mock). I am using a LuisDialog which, when it detects the "ConsultPO" intent, it's supposed to ask for the user's 'customer id' and wait a follow up message from the user. However, it keeps going back to the start of the Luis Dialog and processing the intent instead of resuming from the waited method. This is the intent's code, which runs correctly:
[LuisIntent("ConsultPO")]
public async Task POIntent(IDialogContext context, LuisResult result)
{
string PO = "";
foreach (var entity in result.Entities)
{
if (entity.Type == "purchaseOrder")
PO = entity.Entity;
}
if (PO.Length != 0)
{
po_query = PO;
}
await context.PostAsync("Ok, can you confirm your customer id and I'll check for you?");
context.Wait(confirmCustomer_getPO);
}
This is the code I would expect to be executed after the user responds with a follow up message:
public async Task confirmCustomer_getPO(IDialogContext context, IAwaitable<object> argument)
{
await context.PostAsync("DEBUG TEST");
IMessageActivity activity = (IMessageActivity)await argument;
customer_query = activity.Text;
if (po_query.Length > 0)
{
PurchaseOrder po = POservice.findPO(po_query, customer_query);
await buildSendResponse(po, context);
//more non relevant code
When I answer to the bot's inquiry after context.Wait(confirmCustomer_getPO) is executed, it just goes into LUIS then runs the code respective to "None" intent. The message "DEBUG TEST" is never sent.
Why is "confirmCustomer_getPO" never getting called?
EDIT:
I added a debug message in the StartAsync method. I'm not sure whether this is supposed to happen but it pops up every time I send a message to the bot, which makes me believe the Dialog is simply restarting every time I message the bot:
public class EchoDialog : LuisDialog<object>
{
public EchoDialog() : base(new LuisService(new LuisModelAttribute(
ConfigurationManager.AppSettings["LuisAppId"],
ConfigurationManager.AppSettings["LuisAPIKey"],
domain: ConfigurationManager.AppSettings["LuisAPIHostName"])))
{
}
public override Task StartAsync(IDialogContext context)
{
context.PostAsync("I'm in startAsync");
return base.StartAsync(context);
}
Local debugging shows no exceptions are occurring and that any breakpoint in the waited method is never reached, although the context.Wait call does happen.
I figured out the issue myself after fighting with it for a while. The issue was with the bot store. I was using an InMemoryDataStore which was not working - switching to TableBotDataStore fixed the problem. The issue with the DataStore meant that states weren't being saved so my "waits" and "forwards" were not being saved into the dialog stack - any new incoming message was sent to the RootDialog.
Broken - not working while this was in global.asax.cs:
Conversation.UpdateContainer(
builder =>
{
builder.RegisterModule(new AzureModule(Assembly.GetExecutingAssembly()));
var store = new InMemoryDataStore(); // volatile in-memory store
builder.Register(c => store)
.Keyed<IBotDataStore<BotData>>(AzureModule.Key_DataStore)
.AsSelf()
.SingleInstance();
});
GlobalConfiguration.Configure(WebApiConfig.Register);
As soon as I updated store to:
var store = new TableBotDataStore(ConfigurationManager.AppSettings["AzureWebJobsStorage"]);
Having a valid "AzureWebJobsStorage" setting in web.config from my application settings in Azure, the problem was fixed without any other changes in the code.

Microsoft Bot Framework WebChat: Disable AdaptiveCards submit buttons of previous message

How to disable input/submit button actions in the previous conversation of BotChat - AdaptiveCards in the Microsoft Bot Framework (C#)
I'm imagining you want to display a card to the user that's meant to be used only once, such as a calendar reminder like the one seen in this example.
Bots are mostly meant to have the same kind of access to a channel that a human would, so they can't go back and modify the messages that have already been sent (unless the specific channel allows edits like Slack does). While you can't disable a button in a card that's already part of the conversation history, you can change the way your bot responds to the messages that are generated by that card. What you'll want to do is keep track of whether a button has been clicked and then respond differently when the button is clicked subsequent times.
Here's a basic example of some Dialog code that can respond to messages in three ways. If you type any message and send it to the bot, it will display a card with a button on it. If you click the button, it will say "You did it!" along with the ID of the button you clicked. If you click the same button again, it will say "You already did that!" again attaching the ID.
/// <summary>
/// You'll want a label like this to identify the activity
/// that gets generated in response to your submit button.
/// </summary>
private const string DO_SOMETHING = "DoSomething";
/// <summary>
/// This is passed into context.Wait() in your StartAsync method.
/// </summary>
private async Task MessageReceivedAsync(IDialogContext context,
IAwaitable<IMessageActivity> result)
{
var msg = await result;
if (!string.IsNullOrWhiteSpace(msg.Text))
{
// If msg.Text isn't null or white space then that means the user
// actually typed something, and we're responding to that with a card.
var reply = context.MakeMessage();
var attachment = MakeAdaptiveCardAttachment();
reply.Attachments.Add(attachment);
await context.PostAsync(reply);
}
else
{
// If the user didn't type anything then this could be an activity
// that was generated by your submit button. But we want to make sure
// it is by checking msg.Value.
dynamic value = msg.Value;
try
{
// If value doesn't have a type then this will throw a RuntimeBinderException
if (value != null && value.type == DO_SOMETHING)
{
string id = value.id;
// Check the ID to see if that particular card has been clicked before.
if (!context.PrivateConversationData.ContainsKey(id))
{
// This is how your bot will keep track of what's been clicked.
context.PrivateConversationData.SetValue(id, true);
await context.PostAsync("You did it! " + id);
}
else
{
await context.PostAsync("You already did that! " + id);
}
}
}
catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException)
{
// Respond to messages that don't have values with a type (or id).
}
}
context.Wait(MessageReceivedAsync);
}
private static Attachment MakeAdaptiveCardAttachment()
{
var card = new AdaptiveCard();
// We need to identify this specific card if we want to allow multiple
// instances of the card to be clicked.
// A timestamp could work but a GUID will do.
var cardId = Guid.NewGuid().ToString();
card.Body.Add(new TextBlock() { Text = cardId });
card.Actions.Add(new SubmitAction()
{
Title = "Do something",
// The data we put inside this action will persist.
// I've found setting DataJson to be more reliable than using the Data property.
// Note that if your WebApiConfig.cs has a CamelCasePropertyNamesContractResolver
// (which is a default) and you use capitalized (Pascal case) identifiers,
// they may be converted to camel case and you won't be able to retrieve
// the data with the same identifiers.
DataJson = JsonConvert.SerializeObject(new
{
// We need a type to differentiate this action from other actions.
type = DO_SOMETHING,
// We need an id to differentiate this card from other cards.
id = cardId,
}),
});
return new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = card,
};
}
Here's what it looks like in Bot Framework Emulator. Note that even after you've clicked one card and can't get the first response from that card, you can still get the first response from the other card.

Discord C# Bot - Delete the latest message

I am using the rogue exception package for my discord bot.
When a user calls the bot by a command, I want the bot delete his message before executing the command.
So in my "MessageReceived"-Event i have this code so far:
private async Task MessageReceived(SocketMessage s)
{
var msg = s as SocketUserMessage; // the input
if (msg == null || !msg.Content.StartsWith("!") || msg.Author.IsBot) // return if null, msg is no command, msg is written by the bot
return;
if (msg.Channel is SocketGuildChannel guildChannel) // just delete the command if the msg is written in the guild channel, not a private message channel
await ??? // Delete the command
}
So does someone know, what to write there =?
I can see that you are using Discord.NET API for your bot.
So from this documentation here.
Take a look at the Method list in the properties of SocketMessage. (By the way, take a look at the right side of the webpage, you should be able to see a bar that allows you to navigate easily)
In case you are wondering why we are looking at SocketMessage, that is because your delegate will run whenever a user posts a message, and that message is your SocketMessage, hence that is what you would want to delete.
You should be able to see a method called: DeleteAsync. That would be what you want.
(Screenshot is below)
For the arguments, you can see that its a RequestOption datatype and by default it has been set null for you. You can change that, but I highly recommend using the default.
Also a good thing to take note that is the bot will do nothing (and return an exception) if it does not have the permission to manage messages.
Example:
(sorry, but some of the code strings are in Portuguese... this is a voting system)
[Command("Votar")]
[Summary("Abro uma votação, com opções de Sim e Não.")]
[RequireBotPermission(ChannelPermission.AddReactions)]
public async Task NovoVoto([Remainder] string secondPart)
{
Log.CMDLog(Context.User.Username, "Votar", Context.Guild.Name);
if (secondPart.Length >= 200)
{
await Context.Channel.SendMessageAsync("Perdão, porém seu voto não deve ter mais de 200 caracteres");
return;
}
var embed = new EmbedBuilder();
embed.WithColor(new Color(126, 211, 33));
embed.WithTitle("VOTE AQUI!");
embed.WithDescription(secondPart);
embed.WithFooter($"Votação criada por: {Context.User.Username}");
RestUserMessage msg = await Context.Channel.SendMessageAsync("", embed: embed);
await msg.AddReactionAsync(new Emoji("✅"));
await msg.AddReactionAsync(new Emoji("❌"));
//Delete the command message from the user
await Context.Message.DeleteAsync();
}

Categories

Resources