How to have delay between two steps in Bot Framework v4? - node.js

I want to have dialog flow like this
step1 -> delay -> step2
async step1(stepContext) {
await stepContext.context.sendActivity('Give user some task to do');
return await stepContext.next();
}
async delay(stepContext) {
await stepContext.context.sendActivity({type: 'delay', value:5000});
return await stepContext.next();
}
async step2(stepContext) {}
The above code is not working as wanted. When I run the bot it waits for 5 seconds and then executes step1 and step2. I want the delay to be after step1.
Actually, I want the delay to be of 2 minutes. I was wondering that won't the bot go on sleep or something. I am sorry I am new to this.

I think the best way to accomplish this while using waterfall dialogs is going to be via the use of setTimeout and Proactive Messages. I'm going to make some assumptions here that you are familiar with things like Conversation State, Bot Framework Adapter, and Turn Handlers, but if you need further guidance on anything let me know.
First, foundationally you are going to need to capture the conversation reference in order to send the proactive message. I find this easiest to just do in your onTurn function and save it to conversation state every turn as follows:
const conversationData = await this.dialogState.get(context, {});
conversationData.conversationReference = TurnContext.getConversationReference(context.activity);
await this.conversationState.saveChanges(context);
Now, you can set up your dialog as follows. You could probably do this several different ways. I typically try to make each step a prompt, but you could probably switch these actions around and still make it work.
async step1(stepContext) {
// First give the user the task
await stepContext.context.sendActivity('Give user some task to do');
// Next set up a timeout interval. The action here is to send a proactive message.
const conversationData = await this.dialogState.get(stepContext.context, {});
this.inactivityTimer = setTimeout(async function(conversationReference) {
try {
const adapter = new BotFrameworkAdapter({
appId: process.env.microsoftAppID,
appPassword: process.env.microsoftAppPassword
});
await adapter.continueConversation(conversationReference, async turnContext => {
await turnContext.sendActivity('Were you able to successfully complete the task?');
});
} catch (error) {
//console.log('Bad Request. Please ensure your message contains the conversation reference and message text.');
console.log(error);
}
}, 120000, conversationData.conversationReference);
// Give the user a confirmation prompt
return await stepContext.prompt('choicePrompt', 'Please confirm the results of the task when you are finished',['Passed','Failed']);
}
async step2(stepContext) {
// Clear timeout to prevent the proactive message if user has already responded
clearTimeout(this.inactivityTimer);
/* REST OF YOUR DIALOG HERE */
return await stepContext.endDialog();
}
As mentioned, make sure you are importing your Bot Framework Adapter, setting up your prompt (I used choice but obviously it can be whatever), importing conversation state, etc. Also, one thing to be aware of is that if the user responds very close to the timer (2 minutes as I've set it up here), it is possible that the proactive message will be invoked before the bot reaches the clearTimeout statement. I think this would be an annoyance at most, but you would need to decide if that UX is ok based on how frequently someone would complete the task in near exactly 2 minutes.
One final note, you could probably put the proactive message invocation in a separate helper function, especially if you are going to use this in many different dialogs. That way you don't have to create multiple instances of the Adapter, in addition to making your code easier to update. That said, if you only need it in only place like I did, I find it much easier just to insert it into the dialog since it's not that much code.

Related

How can I reassign the result of a listener function that is executed repeatedly?

I have a function which consumes a Queue from RabbitMQ and I want to save the result of every time it's executed to run some other code (e.g. save something in database).
The problem here is that it's only saved once, the listener keeps working fine and getting that information as the code within the function keeps being executed as I add more events to the queue but without reassigning the result of the execution to that variable:
Here's my code:
Controller (which calls the consumer)
async run() {
const eventData = await this.eventManager.consume(QueuesToConsume.USER_CREATED)
await this.createUserUseCase.run(eventData);
}
RabbitMQ consumer
async consume(queue: string): Promise<DomainEvent> {
let eventData: DomainEvent;
return new Promise<DomainEvent>(async (resolve, reject) => {
await this.channel.consume(queue, async (msg: Message) => {
console.log(`Message: \n ${Buffer.from(msg.content)} \n received successfully!`)
await this.channel.ack(msg)
eventData = JSON.parse(Buffer.from(msg.content).toString('utf8'))
console.log('Message acknowledged successfully')
resolve(eventData);
}).catch(err => {
console.log(`Error consuming the message: \n ${err}`)
reject(err)
});
})
}
So this is not working properly as eventData in the controller doesn't get every response and the useCase can only be executed the first time.
How can I fix this for eventData to get every result the consumer es returning?
PS: Note that I didn't copied the whole piece of code because it is not necessary, I can happily copy it if you need it to give me a proper answer!
I have found some other stack overflow forums that discussed this and in summary this can not be done, whether it is done with promises or with traditional async await, a variable can't just be reassigned to a value every time the listener listens to something.
I have found a workaround that allows me to achieve what I wanted at the very beginning using the Observer/Subject design pattern in which the RabbitMQ consumer that will be the subject will also notify the observers with the new event.
This way also helps a lot if you want that event to trigger two actions or usecases rather than one, you just have to add the new action as an observer and you're good to go!

The dialog is run again if the user types something during a delay

When I set a delay between two messages in a waterfall dialog and the user types something before the delay finishes, the dialog step - including the delay - is run again and the delay is repeated.
class Delay extends ComponentDialog {
constructor(id) {
super(id);
this.addDialog(new WaterfallDialog(WATER_FALL_DIALOG_ID, [
this.startStep.bind(this),
this.delay.bind(this),
this.endStep.bind(this)
]));
}
async startStep(stepContext) {
await stepContext.context.sendActivity('Start Delay dialog');
return await stepContext.next();
}
async delay(stepContext) {
await stepContext.context.sendActivities([
{ type: ActivityTypes.Message, text: 'message 1' },
{ type: 'delay', value: 5000 },
{ type: ActivityTypes.Message, text: 'message 2' }
]);
return stepContext.next();
}
async endStep(stepContext) {
await stepContext.context.sendActivity('End Delay dialog');
return await stepContext.endDialog();
}
}
I want to know how I can have the bot ignore any inputs during the delay step, or at least not repeat the delay step and messages.
I like that the bot ignores the messages sent from the user during the delay and doesn't take any other action, I just don't want it to repeat the delay step.
I can see WHY you would want something like this, but the purpose of a bot is to accept input from a user, and reply to it, so there really isn't an official way to have a bot ignore input from user. I would recommend you add a message to your dialog flow along the lines of 'Getting results, please wait', then another message to indicate that your bot is ready to accept input again "Here's what I found. Is there another XYZ you're looking for?"
That being said, if you're using webchat as the channel, there is a way to hide the input box. https://github.com/microsoft/BotFramework-WebChat/issues/2427 This could be an option, but it only works for webchat, and other channels do not support this particular functionality.

How to stop class/functions from continuing to execute code in Node.js

I have made a few questions about this already, but maybe this question would result in better answers(i'm bad at questions)
I have one class, called FOO, where I call an async Start function, that starts the process that the class FOO was made to do. This FOO class does a lot of different calculations, as well as posting/getting the calculations using the node.js "requets" module.
-I'm using electron UI's (by pressing buttons, that executes a function etc..) to create and Start the FOO class-
class FOO {
async Start(){
console.log("Start")
await this.GetCalculations();
await this.PostResults()
}
async PostResults(){
//REQUESTS STUFF
const response = {statusCode: 200} //Request including this.Cal
console.log(response)
//Send with IPC
//ipc.send("status", response.statusCode)
}
async GetCalculations(){
for(var i = 0; i < 10; i++){
await this.GetCalculation()
}
console.log(this.Cal)
}
async GetCalculation(){
//REQUEST STUFF
const response = {body: "This is a calculation"} //Since request module cant be used in here.
if(!this.Cal) this.Cal = [];
this.Cal.push(response)
}
}
var F1 = new FOO();
F1.Start();
Now imagine this code but with A LOT more steps and more requests ect. where it might take seconds/minutes to finish all tasks in the class FOO.
-Electron got a stop button that the user can hit when he wants the calculations to stop-
How would I go about stopping the entire class from continuing?
In some cases, the user might stop and start right after, so I have been trying to figure out a way to STOP the code from running entirely, but where the user would still be able to create a new class and start that, without the other class running in the background.
I have been thinking about "tiny-worker" module, but on the creation of the worker, it takes 1-2 seconds, and this decreases the purpose of a fast calculation program.
Hopefully, this question is better than the other ones.
Update:
Applying the logic behind the different answers I came up with this:
await Promise.race([this.cancelDeferred, new Promise( async (res, req) => {
var options ={
uri: "http://httpstat.us/200?sleep=5000"
}
const response = await request(options);
console.log(response.statusCode)
})])
But even when the
this.cancelDeferred.reject(new Error("User Stop"));
Is called, the response from the request "statuscode" still gets printed out when the request is finished.
The answares I got, shows some good logic, that I didn't know about, but the problem is that they all only stop the request, the code hanlding the request response will still execute, and in some cases trigger a new request. This means that I have to spam the Stop function until it fully stops it.
Framing the problem as a whole bunch of function calls that make serialized asynchronous operations and you want the user to be able to hit a Cancel/Stop button and cause the chain of asynchronous operations to abort (e.g. stop doing any more and bail on getting whatever eventual result it was trying to get).
There are several schemes I can think of.
1. Each operation checks some state property. You make these operations all part of some object that has a aborted state property. The code for every single asynchronous operation must check that state property after it completes. The Cancel/Stop button can be hooked up to set this state variable. When the current asynchronous operation finishes, it will abort the rest of the operation. If you are using promises for sequencing your operations (which it appears you are), then you can reject the current promise causing the whole chain to abort.
2. Create some async wrapper function that incorporates the cancel state for you automatically. If all your actual asynchronous operations are of some small group of operations (such as all using the request module), then you can create a wrapper function around whichever request operations you use that when any operation completes, it checks the state variable for you or merges it into the returned promise and if it has been stopped, it rejects the returned promise which causes the whole promise chain to abort. This has the advantage that you only have to do the if checks in one place and the rest of your code just switches to using your wrapped version of the request function instead of the regular one.
3. Put all the async steps/logic into another process that you can kill. This seems (to me) like using a sledge hammer for a small problem, but you could launch a child_process (which can also be a node.js program) to do your multi-step async operations and when the user presses stop/cancel, then you just kill the child process. Your code that is monitoring the child_process and waiting for a result will either get a final result or an indication that it was stopped. You probably want to use an actual process here rather than worker threads so you get a full and complete abort and so all memory and other resources used by that process gets properly reclaimed.
Please note that none of these solutions use any sort of infinite loop or polling loop.
For example, suppose your actual asynchronous operation was using the request() module.
You could define a high scoped promise that gets rejected if the user clicks the cancel/stop button:
function Deferred() {
let p = this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
this.then = this.promise.then.bind(p);
this.catch = this.promise.catch.bind(p);
this.finally = this.promise.finally.bind(p);
}
// higher scoped variable that persists
let cancelDeferred = new Deferred();
// function that gets called when stop button is hit
function stop() {
// reject the current deferred which will cause
// existing operations to cancel
cancelDeferred.reject(new Error("User Stop"));
// put a new deferred in place for future operations
cancelDeferred = new Deferred();
}
const rp = require('request-promise');
// wrapper around request-promise
function rpWrap(options) {
return Promise.race([cancelDeferred, rp(options)]);
}
Then, you just call rpWrap() everywhere instead of calling rp() and it will automatically reject if the stop button is hit. You need to then code your asynchronous logic so that if any reject, it will abort (which is generally the default and automatic behavior for promises anywa).
Asynchronous functions do not run code in a separate thread, they just encapsulate an asynchronous control flow in syntactic sugar and return an object that represents its completion state (i.e. pending / resolved / rejected).
The reason for making this distinction is that once you start the control flow by calling the async function, it must continue until completion, or until the first uncaught error.
If you want to be able to cancel it, you must declare a status flag and check it at all or some sequence points, i.e. before an await expression, and return early (or throw) if the flag is set. There are three ways to do this.
You can provide a cancel() function to the caller which will be able set the status.
You can accept an isCancelled() function from the caller which will return the status, or conditionally throw based on the status.
You can accept a function that returns a Promise which will throw when cancellation is requested, then at each of your sequence points, change await yourAsyncFunction(); to await Promise.race([cancellationPromise, yourAsyncFunction()]);
Below is an example of the last approach.
async function delay (ms, cancellationPromise) {
return Promise.race([
cancellationPromise,
new Promise(resolve => {
setTimeout(resolve, ms);
})
]);
}
function cancellation () {
const token = {};
token.promise = new Promise((_, reject) => {
token.cancel = () => reject(new Error('cancelled'));
});
return token;
}
const myCancellation = cancellation();
delay(500, myCancellation.promise).then(() => {
console.log('finished');
}).catch(error => {
console.log(error.message);
});
setTimeout(myCancellation.cancel, Math.random() * 1000);

pass result from AwaitReactions out of function discord.js

I am new to async functions. I want to utilize the code below to ask the user a question and react to the question with an X or check mark to get the users answer on whether or not to delete something to make room for a new entry.
The function below works perfectly fine. However, I want to pass the result from the function out of the function so I can make an if else statement outside of it and that is where I am stuck.
I've looked around online and saw several things related to callbacks being used, but each example I've seen is different for something I think is similar, so I am just confused. And none of these examples have been used for Reactions on Discord, so I'm just not sure where to go.
const agree = "✅"
const disagree = "❌"
let msg = await message.author.send("You have made the maximum number of decks. Would you like to delete one of your decks in order to make a new one? Please react with one of the following...")
await msg.react(agree)
await msg.react(disagree)
const filter = (reaction, user) => {
return ['✅', '❌'].includes(reaction.emoji.name) && user.id === message.author.id;
};
const reactions = await msg.awaitReactions(filter, {
max: 1
}).then(collected => {
const result = collected.last();
})
return result;
}
deleteDeckQuestion(function(result){
console.log(result)
}).catch(err => console.error(err))
The above code results in 'undefined' being logged to the console when I run deleteDeckQuestion. No errors otherwise. I would like it to make the Results variable accessible to me outside the function so I can make an if else statement based upon which reaction the user added to the question.
I tried putting the if else statement I wanted to use with the results of deleteDeckQuestion inside the async function and it operated fine, but then inside the "Yes" result of that function, I want to put another Async function to ask which deck 1, 2 or 3 should be deleted and have the same reaction-determines-answer-to-question scenario. Just saves the user typing more than necessary at the ease of mobile users.
Would it be easier I just put an async function inside another async function? Something tells me that isn't the best idea in terms of efficiency. Eventually these reactions will lead to using mysql queries, which I am comfortable with using, but it will get pretty lengthy and functions inside other functions just seems like a mess... not sure if that is part of the "callback hell" I've read the joys of though...
Thanks for any help in advance.
collected within your then() callback and reactions are the exact same object. However, result's scope is limited to within the callback.
In this example code, collected is the result of msg.awaitReactions(...)'s fulfilled promise. Then, result is declared in the same scope, and therefore accessible where you need it to be.
const collected = await msg.awaitReactions(filter, { max: 1 })
.catch(console.error);
const result = collected.first();
MDN: Async Programming, await, then(), scope
Discord.js: Message.awaitReactions()

integration test geteventstore using rxjs has race condition

Sorry, this one is a bit messy. My project is in nodejs. I have a test in mocha. In it I open a connection to geteventstore and subscribe to a stream. This essentially starts emitting events.
I wrap that event subscription in an rxjs observable and then write it to the console.
half of the time I get a stream full of events half of the time I don't.
I get the sense that the eventloop starts listening, doesn't hear anything and closes before the geteventstore can start blasting it with events.
I'm at a bit of a loss. I can tell the geteventstore is sending data cuz half the time I get it. My understanding is that as long as there is an someone is subscribed to an event, e.g. there is an eventlistener, the loop will stay open.
So perhaps the problem is with rxjs?
I don't know, any help would be greatly appreciated.
----EDIT
I don't know if this will help but the test looks like this.
context('when calling subscription', ()=> {
it('should stay open', function () {
mut = bootstrap.getInstanceOf('gesConnection');
var rx = bootstrap.getInstanceOf('rx');
var subscription = mut.subscribeToAllFrom();
rx.Observable.fromEvent(subscription, 'event').forEach(x=> console.log(x));
subscription.on('event', function (payload) {
console.log('event received by dispatcher');
console.log('event processed by dispatcher');
});
mut._handler._connectingPhase.must.equal('Connected');
})
});
so the mut is a connection to geteventstore, rx is rxjs, and the subscription object is an event emmiter that pumps data out of the geteventstore.
I understand that the problem is conflated by the fact that it deals wit at least two somewhat unusual products, the geteventstore, and the rxjs.
I mean I"m pretty confident that the gesConnection and subscription are, in fact, connecting and emitting. I just don't know how to test/investigate further.
Thanks
I don't see you making use of Mocha's async testing facilities.
MochaJs does not know that it should wait around for your test longer than it takes for your function to return.
Usually you'd return a promise:
it('must stay open', () => {
mut = bootstrap.getInstanceOf('gesConnection');
var rx = bootstrap.getInstanceOf('rx');
var subscription = mut.subscribeToAllFrom();
subscription.on('event', function (payload) {
console.log('event received by dispatcher');
console.log('event processed by dispatcher');
});
var promise = rx.Observable
.fromEvent(subscription, 'event')
.take(100) // stop test after 100 events
.do(x => console.log(x))
.finally(() => {
// do any cleanup here.
// such as close your connection
// or "subscription" variable
})
.toPromise();
mut._handler._connectingPhase.must.equal('Connected');
// tells Mocha to wait until the observable completes
return promise;
});

Resources