Ms BotBuilder : firstRun dialog prevents triggering of other dialogs based on LUIS intents - botframework

I have a firstRun dialog defined in the bot like this :
// First run dialog
bot.dialog('firstRun', [
function (session, next) {
session.userData.token = _.get(session, 'message.user.token', null) || _.get(session, 'userData.token', null)
}
]).triggerAction({
onFindAction: function (context, callback) {
var score = 0;
if (session.userData.token doesn't exist or new token recieved in `session.user.message.token`){
score = 1.1;
}
callback(null, score);
}
});
And there's a LUIS model integrated with a dialog that triggers on an intent, let's say Help :
bot.dialog('help', [
(session, args) => {
let entities = _.get(args, 'intent.entities', null);
let topic = _.get(builder.EntityRecognizer.findEntity(entities, 'topic'), 'entity', null) || _.get(args, 'option', null);
session.replaceDialog(topic);
}
])
.triggerAction({
matches: 'Help'
});
The onFindAction triggers on every message. And it triggers firstRun only on the first message when session.userData.token is not set.
Problem is, if the first message is matched to Help intent, it does not get triggered. It works from the second time, when firstRun is not triggered.
How can I ensure any matching intent triggers the corresponding dialog, irrespective of firstRun?
If there's a different way possible to achieve the same thing, please suggest.
Addition
What I am trying to accomplish is this - I have a web service auth token that I want to keep in session.userData.token that refreshes every hour. So right now I trigger onFindAction on every utterance which checks if either session.userData.token doesn't exist (which means its the first utterance) OR a new token has been sent. In both cases I trigger firstRun to update session.userData.token and proceed to trigger any dialog that matched with the LUIS intent of the utterance. But whenever firstRun is triggered, none of the other dialogs are triggered. It would be ideal to have a simpler mechanism to do this i suppose.
Thanks

It sounds like you're trying to have a pass-through intent handler that would trigger before the message is routed to the actual handlers. Middleware would be the best place to handle your token refresh logic, but working with session in your middleware isn't easy. This blog post of mine explains why - http://www.pveller.com/smarter-conversations-part-4-transcript/.
Your best bet is the routing event, I believe. It's synchronous via events and you are given the session object. You should be able to validate and refresh your token as needed before the message reaches the proper intent handler destination.
bot.on('routing', function (session) {
if (!session.userData.token) {
// receive new token
}
});
Unlike middleware though, you are not given the next callback to continue the chain, so you will have to make sure you fetch the token synchronously. The blog post I mentioned previously explains this part as well.

Related

Who should subscribe to NGXS async action - the dispatch action caller or the #Action handler?

I don't know whether this is only a matter of style.
There are at least 2 ways of handling async actions:
subscribe after dispatch
// action is being dispatched and subscribed
this.store.dispatch(new LoadCustomer(customerId)).subscribe(); // <-- subscribe
In the State:
#Action(LoadCustomer)
loadCustomer(context: StateContext<CustomerStateModel>,
customerId: string) {
return this.customerService.loadById(customerId).pipe(
tap(c => context.setState(produce(context.getState(), draft => {
draft.byId[customerId] = c;
})))
); // <-- NO subscribe here, just return the Observable
}
subscribe in #Action handler
// action is being dispatched
this.store.dispatch(new LoadCustomer(customerId)); // <-- no subscribe
In the State:
#Action(LoadCustomer)
loadCustomer(context: StateContext<CustomerStateModel>,
customerId: string) {
this.customerService.loadById(customerId).pipe(
tap(c => context.setState(produce(context.getState(), draft => {
draft.byId[customerId] = c;
})))
).subscribe(); // <-- subscribe is done in action handler
}
Question
Which one is better and why?
Edit / Hint
It turned out that the core issue leading to this question was following:
We had an HttpInterceptor caching "too much" which looked liked if some actions had not been executed. In fact the subscription is already handled correctly by NGXS, but in our case no effect was visible (no request in the network tab).
In our cases the .subscribe() calls could be eliminated. Only where we need to wait for an action to finish, a subscription after the dispatch makes sense.
I think it is somewhat a matter of style, but I'd say (from my usage of NGXS) this is most typical:
On dispatch do this, and only subscribe here if there's some post-action you want to do.
this.store.dispatch(new LoadCustomer(customerId));
And in the state, the option 1 approach, to return the Observable to the NGXS framework and let it handle the subscription itself (see from the docs re: action handling).
Approach number one, as there will be only one subscription and the source component/service will be able to react to it. Subscribing in #Action means that whenever the #Action handled is called then new subscription will be created.

How to read state property accessors outside the dialog in V4 Bot Framework

I'm using bot framework version 4. I would like to access user state properties in the validator method but I didn't find any solution to it.
GitHub
In the GitHub sample above, we have a validator AgePromptValidatorAsync which validates age.
But I would want to access Name which I have stored in State property.
How could that be achieved.
And is it possible to access state/use GetAsync in a method outside dialog which doesn't contain context.
#mdrichardson could you please help me in this.Thank you in advance.
1. Ensure that UserProfile.Name is saved before hitting validation.
That sample doesn't do this on it's own, so you would:
private async Task<DialogTurnResult> NameConfirmStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values["name"] = (string)stepContext.Result;
// ADDED: This code block saves the Name
if (!string.IsNullOrEmpty((string)stepContext.Result)) {
var userProfile = await _userProfileAccessor.GetAsync(stepContext.Context, () => new UserProfile(), cancellationToken);
userProfile.Name = (string)stepContext.Result;
await _userProfileAccessor.SetAsync(stepContext.Context, userProfile);
}
// We can send messages to the user at any point in the WaterfallStep.
await stepContext.Context.SendActivityAsync(MessageFactory.Text($"Thanks {stepContext.Result}."), cancellationToken);
// WaterfallStep always finishes with the end of the Waterfall or with another dialog; here it is a Prompt Dialog.
return await stepContext.PromptAsync(nameof(ConfirmPrompt), new PromptOptions { Prompt = MessageFactory.Text("Would you like to give your age?") }, cancellationToken);
}
2. Access the User Profile
// CHANGED: Since this accesses the userProfile, the method is no longer static. Also must be async
private async Task<bool> AgePromptValidatorAsync(PromptValidatorContext<int> promptContext, CancellationToken cancellationToken)
{
// ADDED: Here is how you can access the Name
// Note: You can use promptContext.Context instead of stepContext.Context since they're both ITurnContext
var userProfile = await _userProfileAccessor.GetAsync(promptContext.Context, () => new UserProfile(), cancellationToken);
var name = userProfile.Name;
// Do whatever you want with the Name
// CHANGED: Since this is now async, we don't return Task.FromResult(). We just return the result
return promptContext.Recognized.Succeeded && promptContext.Recognized.Value > 0 && promptContext.Recognized.Value < 150;
}
Accessing the UserProfile Without Context
This is kind of possible, but you can't do this easily or out-of-the-box. There are some options that you can use, however (mostly in order from least difficult to most):
Pass the context to whatever method/function you need to use it in. Just about every bot method you'd use has some kind of context that you can pass into another method. This is definitely your best option.
Create a separate class that you use to store variables in bot memory
Either Write Directly to Storage or Implement Custom Storage that you use to track the UserProfile. Note, you'd have to pass around your Storage object, so you may as well just pass around the context, instead.
Use the new Adaptive Dialogs, since they do state management differently. I highly recommend against this, though, as these are "experimental", meaning that there's still bugs and we barely use this internally. I'm adding this as an option more for posterity and users that want to play with new stuff.

deferred Rxjs BehaviorSubject mechanism

I have the following requirement.
I have An Angular service with an BehaviorSubject.
A http request is done and when this is done the BehaviorSubject.next method is invoked with the value.
This value can change during the lifecycle of the single page.
Different subscribers are registered to it and get invoked whenever this changes.
The problem is that while the http request is pending the BehaviorSubject already contains a default value and subscribers are already immediately getting this value.
What I would want is that subscribers have to wait till the http request is done (deferred) and get the value when the http request is done and sets the value.
So what I need is some kind of deferred Behavior subject mechanism.
How would i implement this using rxjs?
Another requirement is that if I subscribe to the behaviorsubject in a method we want the subcriber to get the first non default value and that the subscription ends. We don't want local subscriptions in functions to be re-executed.
Use a filter on your behavior subject so your subscribers won't get the first default emitted value:
mySubject$: BehaviorSubject<any> = new BehaviorSubject<any>(null);
httpResponse$: Observable<any> = this.mySubject$.pipe(
filter(response => response)
map(response => {
const modifyResponse = response;
// modify response
return modifyResponse;
}),
take(1)
);
this.httpResponse$.subscribe(response => console.log(response));
this.myHttpCall().subscribe(response => this.mySubject$.next(response));
You can of course wrap the httpResponse$ observable in a method if you need to.
I think the fact that you want to defer the emitted default value, straight away brings into question why you want to use a BehaviorSubject. Let's remember: the primary reason to use a BehaviorSubject (instead of a Subject, or a plain Observable), is to emit a value immediately to any subscriber.
If you need an Observable type where you need control of the producer (via .next([value])) and/or you want multicasting of subscription out of the box, then Subject is appropriate.
If an additional requirement on top of this is that subscribers need a value immediately then you need to consider BehaviorSubject.
If you didn't say you need to update the value from other non-http events/sources, then I would've suggested using a shareReplay(1) pattern. Nevertheless...
private cookieData$: Subject<RelevantDataType> = new
Subject<RelevantDataType>(null);
// Function for triggering http request to update
// internal Subject.
// Consumers of the Subject can potentially invoke this
// themselves if they receive 'null' or no value on subscribe to subject
public loadCookieData(): Observable<RelevantDataType> {
this.http.get('http://someurl.com/api/endpoint')
.map(mapDataToRelevantDataType());
}
// Function for dealing with updating the service's
// internal cookieData$ Subject from external
// consumer which need to update this value
// via non-http events
public setCookieData(data: any): void {
const newCookieValue = this.mapToRelevantDataType(data); // <-- If necessary
this.cookieData$.next(newCookieValue); // <-- updates val for all subscribers
}
get cookieData(): Observable<RelevantDataType> {
return this.cookieData$.asObservable();
}
The solution is based on OPs comments etc.
- deals with subscribing to subject type.
- deals with external subscribers not being able to 'next' a new value directly
- deals with external producers being able to set a new value on the Subject type
- deals with not giving a default value whilst http request is pending

How to use Messaging center in xamarin forms

I am trying to use messaging center instead of Messenger in xamarin forms I have no idea about messaging center I tried Bellow code to subscribe and Send Message in xamarin forms
MessagingCenter.Send(this, "TodoTable", "Todo");
But I have not Idea from where I can subscribe to this message I tried bellow code :
MessagingCenter.Subscribe<TodoClass>(this, Todo, async (sender, arg) =>
{
await RefreshCommand.ExecuteAsync();
});
This is giving me error Any Help will appreciated :)
It is a quirk of XF messaging centre that (it seems) you need to know who will be sending the message, and potentially who will be receiving it.
However it can be object. The signature of subscribe is:
void MessagingCenter.Subscribe<TSender>(object subscriber, string message, Action<TSender> callback, TSender sender = null)
The trick is to subscribe thus:
MessagingCenter.Subscribe<object>(this, "messageName", Action<object> callback)
This says object or anything derived from object can be a sender, ie, everything.
However if you want to only subscribe to messages sent by a particular instance of a type:
MessagingCenter.Subscribe<MyClass>(this, "messageName", Action<MyClass> callback)
The use of the full signature is a bit suspect. Basically it is saying only if sent from the source object are subscribers who used that source object when subscribing.
MessagingCenter.Subscribe<object, string>(this, "Hi",
(sender, arg) =>
{
DisplayAlert("Message Received", "arg=" + arg, "OK");
},
BindingContext);
if you use the following to send the message it wont be received by the subscriber just above:
MessagingCenter.Send<object, string>(this, "Hi", "John");
But the following will be received
MessagingCenter.Send<object, string>(BindingContext, "Hi", "John");
Though why would you want to send a message to yourself. (Assuming the subscribe and send were in the same page in this case).
However if there were multiple pages with the exact same binding context the message will be sent to all such subscribers. Eg, pages bound to the same view model.
To improve the answer by #user2825546, if you wish to subscribe to only messages from your view-models, you need to specify the base class type when sending the message:
MessagingCenter.Subscribe<BaseViewModel, string>(this, "ShowError", (view, message) => { });
public class StartupViewModel : BaseViewModel
{
//Like this...
MessagingCenter.Send<BaseViewModel, string>(this, "ShowError", "Message");
//Or...
MessagingCenter.Send((BaseViewModel)this, "ShowError", "Message");
}
When testing, I tried to send the message as StartupViewModel, but the listener was not receiving the messages. I guessed that it would, since the class derives from the BaseViewModel.
Send Method
MessagingCenter.Send<Application>(Application.Current,"RefreshDocs");
Subscribe Method
MessagingCenter.Subscribe<Application>(Application.Current , "RefreshDocs" , (sender) =>
{
});
The goal of MVVM is to abstract away your Views from your Business Logic. This ensures great code reuse, testability, and is pretty awesome. Many MVVM Frameworks offer tools to enhance this such as data binding and dependency services to make our lives easier. These are built into Xamarin.Forms, which is awesome, but one feature less talked about is the Messaging Center. It’s entire goal is to enable ViewModels or other components to communicate with each other without having to know anything about each other besides a simple Message contract.
So for instance, let’s say you are in a master/detail setup where your MasterViewModel has a list of items and your DetailViewModel allows you to create a new item, update an item, or delete an item. When your user is on the detail page and triggers an event you need to somehow message back to your MasterViewModel that has a list of Items so the UI can react on the Master page when we navigate back.
So let’s say our MasterViewModel subscribes to “Update” and “AddNew” message events. It will then update it’s observable collection based on when it receives messages. Our DetailViewModel would then send a message in our SaveCommand to notify anyone that is subscribed to these specific messages:
public ObservableCollection<TripExpense> Expenses { get; set; }
public ExpensesViewModel()
{
Expenses = new ObservableCollection<TripExpense>();
//Subscibe to insert expenses
MessagingCenter.Subscribe<TripExpense>(this, "AddNew", (expense) =>
{
Expenses.Add(expense);
});
//subscribe to update expenxes
MessagingCenter.Subscribe<TripExpense>(this, "Update", (expense) =>
{
ExecuteUpdateExpense(expense);
});
}
private async Task ExecuteSaveCommand()
{
if (IsBusy)
return;
IsBusy = true;
//Send a message to insert/update the expense to all subscribers
if(isNew)
{
MessagingCenter.Send(expense, "AddNew");
}
else
{
MessagingCenter.Send(expense, "Update");
}
IsBusy = false;
navigation.PopAsync();
}
There you have it, messaging made easy! Don’t forget to unsubscribe if you no longer wish to receive notifications.

RxJS5 WebSocketSubject - how to filter and complete messages?

I'm looking for some guidance on the correct way to setup a WebSocket connection with RxJS 5. I am connecting to a WebSocket that uses JSON-RPC 2.0. I want to be able to execute a function which sends a request to the WS and returns an Observable of the associated response from the server.
I set up my initial WebSocketSubject like so:
const ws = Rx.Observable.webSocket("<URL>")
From this observable, I have been able to send requests using ws.next(myRequest), and I have been able to see responses coming back through the ws` observable.
I have struggled with creating functions that will filter the ws responses to the correct response and then complete. These seem to complete the source subject, stopping all future ws requests.
My intended output is something like:
function makeRequest(msg) {
// 1. send the message
// 2. return an Observable of the response from the message, and complete
}
I tried the following:
function makeRequest(msg) {
const id = msg.id;
ws.next(msg);
return ws
.filter(f => f.id === id)
.take(1);
}
When I do that however, only the first request will work. Subsequent requests won't work, I believe because I am completing with take(1)?
Any thoughts on the appropriate architecture for this type of situation?
There appears to be either a bug or a deliberate design decision to close the WebSocket on unsubscribe if there are no further subscribers. If you are interested here is the relevant source.
Essentially you need to guarantee that there is always a subscriber otherwise the WebSocket will be closed down. You can do this in two ways.
Route A is the more semantic way, essentially you create a published version of the Observable part of the Subject which you have more fine grained control over.
const ws = Rx.Observable.webSocket("<URL>");
const ws$ = ws.publish();
//When ready to start receiving messages
const totem = ws$.connect();
function makeRequest(msg) {
const { id } = msg;
ws.next(msg);
return ws$.first(f => f.id === id)
}
//When finished
totem.unsubscribe();
Route B is to create a token subscription that simply holds the socket, but depending on the actual life cycle of your application you would do well to attach to some sort of closing event just to make sure it always gets closed down. i.e.
const ws = Rx.Observable.webSocket("<URL>");
const totem = ws.subscribe();
//Later when closing:
totem.unsubscribe();
As you can see both approaches are fairly similar, since they both create a subscription. B's primary disadvantage is that you create an empty subscription which will get pumped all the events only to throw them away. They only advantage of B is that you can refer to the Subject for emission and subscription using the same variable whereas A you must be careful that you are using ws$ for subscription.
If you were really so inclined you could refine Route A using the Subject creation function:
const safeWS = Rx.Subject.create(ws, ws$);
The above would allow you to use the same variable, but you would still be responsible for shutting down ws$ and transitively, the WebSocket, when you are done with it.

Resources