Microsoft Bot Framework dialog with multiple users - c#

I am using the Microsoft Bot Framework for .NET to create a simple question/answer bot, as an ASP.NET MVC WebApi, written in C#, that will be used primarily on Slack channels with multiple users.
I have created a dialog which I initiate from my message controller as per the framework documentation:
await Conversation.SendAsync(activity, () => new QuestionDialog());
The lambda expression indicates to the Bot Framework which constructor to use for a dialog. QuestionDialog implements IDialog. After a dialog is initiated the dialog class is serialized and stored in the Bot Connector cloud, saving the current question and expected answer, then deserialized whenever a new message is received in the current conversation.
I want any user to be able to answer a random question presented by the Bot regardless of which user initiated the dialog but it seems that a new QuestionDialog is created for each user interacting with the bot so only the user initiating the question can answer it.
Is there any way of creating a dialog that is tied to the conversation/channel only rather than the combination of conversation/channel and user? Does it even make sense to attempt using a dialog in this way?

I guess the way I would implement this is to:
Store the state (the latest question) for each of the channels separately. For example, create a serializable class "QuestionState" which has at least the following properties: Service URL, conversation account ID and the question itself.
For each incoming activity (message), check the service URL and the conversation account ID of the sender and compare that against your list of states. If you find a match, evaluate the message (answer) in respect to the latest question. If there are no matches, this is a channel without previous state, so you need to create one and ask the first question.
Azure Table storage is a handy service to store the states in. You can, of course, use any service you like. Just keep in mind that in-memory state storage only works for testing.
In addition, you don't have necessarily have to use dialogs for this solution at all - you can do all you need to do in the MessagesController.

Related

How to retrieve a payment method on the client side?

We are running an App with a Angular/Typescript frontend and a .NET backend, using Stripe Elements and Stripe.NET respectively.
We are currently using the "Sources" API.
The frontend can create sources, the backend saves them to our specific users. When you open the frontend again, the backend sends a list of source ids. The frontend then collects the data it needs to display those sources directly from Stripe so the user can pick one of his saved sources to pay and does not have to enter all the data again.
Enter the Payment Method / Payment Intend API.
Due to EU regulations Stripe has a new API that requires us to create cards no longer as "source" but as a "PaymentMethod". So I implemented that in the backend, opened the frontend in my IDE, updated the #types/stripe-v3 package and found the new payment intent API.
The only thing missing: I cannot figure out how the frontend is to access the payment method data, once created. I can create it. Send it to the backend. The backend can retrieve it. Send back the ID to the frontend... and now what? How to display the payment methods available?
I had expected a stripe.retrievePaymentMethod() as there is a stripe.retrieveSource(). But no such luck.
The only option I currently see to present the user with a list of existing payment methods is getting this info on the backend and piping it all, class by class, property by property to the client. Basically copying every single data class stripe has into our own backend REST definition. That cannot be right.
What am I missing? Why is there no stripe.retrievePaymentMethod() on the frontend? Did I not understand some fundamental facts about what those APIs should do?
After contacting Stripe directly, it was confirmed that that's just the way it is:
I think it's just an oversight that we didn't add one.
There are similar functions in the mobile SDK so I don't see why we shouldn't have it
There are no immediate plans to add the functionality back in in the very near future,
so as a workaround, I will tunnel all the data through our backend(s).
If I understand as well, I think your problem is following and the sequence of that. I hope this helps you.
I have implemented a payment gateway like ccAvenue with DotNet and angular, in my case, I send the data to the server, and from the server, I tried to redirect to the payment gateway, but APIs return some result, and the result can not be redirected.
So I created a web-form with implementation, I redirected my app to web-forms page and from there I called the ccAvenue page, and in the response URL, I send the response page of webforms only and after saving the response I redirected to my angular app.
Here is workaround if you want to process 3Dsecure cards and still support other methods like SEPA.
You could attach both, confirmed PaymentIntent (payment method) or Source to the Customer object.
On your frontend you could implement both (StripeElements with client secret for 3Dsecure cards) and IBAN element for SEPA.
I could provide my code example how I save payment intent to the customer. It's in PHP, but for other languages logic should be the same.
Assuming that our client already confirmed PaymentIntent and we have it's id:
$intent = \Stripe\PaymentIntent::retrieve($stripe_intent_id);
$payment_method = \Stripe\PaymentMethod::retrieve($intent->payment_method);
$stripe_customer = Stripe\Customer::create([
'payment_method' => $intent->payment_method,
]);
In case you've already created Customer object before you could use attach method:
$payment_method->attach(['customer' => 'cus_FTkGe4lv5LfyI0']);
Then you'll be able to charge using Customer object PaymentMethod or Source;
I didn't try to attach both methods to the same customer object (we only allow customer to have one payment option at the same time), but it should work. Let me know if it works for you.

Botframework V4: Reset Bot in Messenger

Hello i have recently published my bot to azure and deployed it to messenger for testing purposes. The first part of the bot ask the user for name and age. How can i reset the whole conversation when the user type "reset"? Like delete all the data saved in memory storage. I am using C# and i save the user data to in-memory storage.
IStorage dataStore = new MemoryStorage();
var conversationState = new ConversationState(dataStore);
options.State.Add(conversationState);
First, I would echo what #JJ_Wailes has said about using the MemoryStorage provider in a production bot: just don't. 😊
That said, to answer your question directly, yes you can delete all state for a given BotState (e.g. ConversationState, UserState, etc) using the DeleteAsync API.
Hihi!
In-memory data storage is intended for testing only. This storage is volatile and temporary. The data is cleared each time the bot is restarted. There is no need to 'delete' it, it deletes itself the second you restart the bot. That being said, to 'reset' a bot in the FB messenger channel, when you're on the https://www.messenger.com page, navigate to the gear icon in the upper right:
There will be an option to 'delete' the conversation. Click this, then go to the right side, where there's a list of people. Search for your bot's name once more, and the bot conversation will be started over.
and voila! Reset:

How can I have my bot start in a different dialog depending on user

Basically, I want to have my bot on different pages using iframes and Bot Framework Web Chat.
Depending on what page the user opens the bot, a different dialog is called.
Without Direct Line, this is as simple as adding a query parameter to api/messages and adjust the controller. But with Direct Line, I can only specify one endpoint in the portal.
I tried to change user.id in the JavaScript BotChat.App call to a different identifier and then select the correct dialog in my MessagesController based on that.
BUT: my custom id doesn't seem to be available as early as the activity "ConversationUpdate" occures where I send my welcome message. I need to send a dialog-specific welcome message though (so I need to navigate there as soon as possible and not only when the user types his first message).
Take a look at the backchannel. In your scenario you can pass a parameter from the Javascript via the back channel to set some value in UserData. Now your parameter will exist in state (in this case the IDataBag UserData) when your user first hits the bot.
Here is a C# sample of a 2-way backchannel
another C# sample using a 1 way backchannel

How to use command pattern in a WinForms client application?

Background
I'm building a two-tiered C# .net application:
Tier 1: Winforms client application using the MVP (Model-View-Presenter) design pattern.
Tier 2: WebAPI RESTful service sitting on top of Entity Framework and SQL Server.
If you would like more detail on the application I'm building, I gave a probably too thorough explanation here.
Current Development
Currently, I'm working on the Winforms client. Particularly, I'm trying to hash out a adequate implementation of the command pattern within this client. I was fortunate enough to stumble across this excellent blog post that outlines a solid command architecture. To complement that post, the author followed up by explaining how he separates queries from commands. After reading those blogs, it becomes very clear that my tier 2 (web api service) would greatly benefit from implementing both of these. The generic implementation allows for fantastic flexibility, testability, and extensibility.
Question
What is less clear to me is how I go about implementing these patterns on the winforms client side of things (tier 1). Do queries and commands continue to be considered separate here? Consider a basic action, such as a login attempt. Is that a query or a command? Ultimately, you need data back (user information on the server) from the web service, so that would make me think it is a query. What about another case, such as a request to create a new user. I understand that you would create a command object that stores the user information and send that off to the service. Commands are supposed to be fire and forget, but wouldn't you want some sort of confirmation from the service that the command was successful? Furthermore, if a command handler returns void, how would you tell the presenter whether or not the user creation request was successful?
At the end of the day, for any given UI task (say the user creation request), does it end up that you end up having a winforms client based query/command, as well as a web api service version of the command/query which handles the request on that end?
Do queries and commands continue to be considered separate here?
Yes, typically you would fire a command and if you need to update the UI after this action has been performed you would perform a query to get the new information. An example will make this clear.
Let's say you would assign a specific guard to a certain area. The only information the command (which is only a DTO) needs is the Id of the guard and the Id of the area. The associated CommandHandler will perform all tasks to handle this, e.g. removing that guard from another area, booking him as unavailable etc.
Now your UI would want to show the change. The UI has probably some kind of list with all guards and their assigned area. This list will be populated by a single GetActiveGuardsAndAreaQuery which will return a List<GuardWithAreaInformationDto>. This DTO could contain all kinds of information about all guards. Returning this information from the command is not a clean separation of concerns, because the atomic command handling could be very well used from a similar but slightly different UI, which will require a slightly different update of the UI information.
such as a login attempt. Is that a query or a command?
IMO a login attempt is neither. It is a cross cutting concern, an implementation detail that the data is hidden behind a secure connection. The application however should not be concerned with this detail. Consider using the application with another customer where you could host the WebApi service in and Active Directory domain where you can use Windows Authentication. In that case the user only has to login to his machine and the security is handled by the client and server OS while communicating.
With the patterns you're referring to this can be nicely done using a AuthenticateToWebApiServiceCommandHandlerDecorator which makes sure their are login credentials to serve to the service by asking the user in a modal form, reading it from a config file, or whatever.
Checking if the credentials worked can be done by performing a kind of a standard Query your application always needs such as CheckIfUpdateIsAvailableQuery. If the query succeeds the login attempt succeeded otherwise it failed.
if a command handler returns void, how would you tell the presenter whether or not the user creation request was successful?
While it seems that void doesn't return anything this is not really true. Because if it doesn't fail with some exception (with a clear message what went wrong!) it must have succeeded.
In a follow up of the mentioned blog posts #dotnetjunkie describes a way to return information from commands but make notice of the added comment on the top of post.
To summarize, throw clear exceptions from failed commands. You can add an extra layer of abstraction client side to handle this nicely. Instead of injecting a commandhandler directly into the different presenters you can inject an IPromptableCommandHandler which has only one open generic implementation at compile time:
public interface IPromptableCommandHandler<TCommand>
{
void Handle(TCommand command, Action succesAction);
}
public class PromptableCommandHandler<TCommand> : IPromptableCommandHandler<TCommand>
{
private readonly ICommandHandler<TCommand> commandHandler;
public PromptableCommandHandler(ICommandHandler<TCommand> commandHandler)
{
this.commandHandler = commandHandler;
}
public void Handle(TCommand command, Action succesAction)
{
try
{
this.commandHandler.Handle(command);
succesAction.Invoke();
}
catch (Exception)
{
MessageBox.Show("An error occured, please try again.");
// possible other actions like logging
}
}
}
// use as:
public void SetGuardActive(Guid guardId)
{
this.promptableCommandHandler.Handle(new SetGuardActiveCommand(guardId),() =>
this.RefreshGuardsList());
}
At the end of the day, for any given UI task (say the user creation request), does it end up that you end up having a winforms client based query/command, as well as a web api service version of the command/query which handles the request on that end?
No!
Client side you should create a single open generic CommandHandlerProxy which solely task is to pass the command dto to the WebApi service.
For the service side architecture you should read another follow up: Writing Highly Maintainable WCF Services which describes an server side architecture to handle this very nicely. The linked project also contains an implementation for WebApi!

Google Admin Directory API SDK User Last Logged In Not Fully Accurate

I use the .Net API for managing my organization's users within Google Apps. Within the directory API you can "RetrieveUser". This returns a User object that has a date property of "LastLoginTime".
Google used to separate out their Last Login Time for an account into three categories using the previous api.
last_login_time - the last time you directly logged into a google service using a UI
last_web_mail_time - the last time you logged into gmail.com webmail
last_pop_time - the last time you popped or imap'ed from their server. (indirect login)
In the new SDK, I don't see a specific "How we populate this" comment within their documentation. I'm wondering, and having trouble testing to figure out the rules myself:
How this is populated?
If it is not all encompassing usage that updates this date (usage meaning ANY interaction between the user and their account), how do I get other dates?
I use the last usage date to recycle idle users. Thus I need an accurate representation of what this date is. I've tested, and it appears popping from a google account is not represented within the SDK LastLoggedIn property, even though you need to log in to pop. Thus, any user that pops from the account and doesn't "Log In" could be deleted by accident.
API Reference
Appreciate any help.
These three (And more) properties still exist, just not attached to the "LastLoginTime". If you want to know if an account is truly idle, you'll need to use the Google.Apis.Admin.Reports.reports_v1 API. You can install via NuGet.
After you make your service object (Many stack answers can show you how to do this), usage is below:
UserUsageReportResource resource = _service.UserUsageReport;
UserUsageReportResource.GetRequest request = resource.Get("User#domain.ca", "yyyy-mm-dd");
UsageReports report = request.Execute();
All the interaction dates will need to be searched through, including LastLoginTime, and then take the latest. Each application has different dates all pertaining to when the last time the user did X action.
LastLoginTime appears to be simply the last time a user directly, or indirectly (via device), logged into the Gmail service. This does not include logging in for pop etc.

Categories

Resources