I'm implementing a Facebook Messenger Chatbot, and in one of the conversation flows, the bot is supposed to send 6 messages, one after the other.
I'd like those messages to be delayed by 1 second, and between them, display a sender action, to make the conversation feel natural (vs. dumping 6 messages all at once, which forces the user to scroll up to read them all).
I tried 2 different webhook implementations, but neither of them work. One was in Python/Flask: between each message, I've put time.sleep(delay), but it didn't work. The other was in Javascript/NodeJS: between each message, I've put setTimeout(function() {sendMessage(recipient_id);}, delay), but it also didn't work. Both versions work perfectly without the delay.
Can anyone help?
You can use the below code, it waits for 1 second and then replies back using async/await.
const messages = ["first", "second", "third", "forth", "fifth", "sixth"];
const sleep = delay => {
return new Promise(function(resolve) {
setTimeout(resolve, delay);
});
};
const displayMessage = async messages => {
for (let message of messages) {
await sleep(1 * 1000);
console.log(message);
}
};
displayMessage(messages);
You can use settimeout for such scenarios. But for showing sender_action let's say if you want to show text like typing... from the bot inside messenger then facebook provides functionalities in its messenger API to include sender actions with different tags. Here is how I am doing it.
sender_action: 'typing...',
messaging_type: 'MESSAGE_TAG',
tag: 'NON_PROMOTIONAL_SUBSCRIPTION',
Please look into the following link for more information.
https://developers.facebook.com/docs/messenger-platform/send-messages/sender-actions
It'd be better if you provide more code when asking the question. I have the suspicion that you actually do this:
setTimeout(function() {sendMessage(recipient_id);}, delay)
setTimeout(function() {sendMessage(recipient_id);}, delay)
setTimeout(function() {sendMessage(recipient_id);}, delay)
setTimeout(function() {sendMessage(recipient_id);}, delay)
setTimeout(function() {sendMessage(recipient_id);}, delay)
setTimeout(function() {sendMessage(recipient_id);}, delay)
setTimeout is asynchronous, it means that your code will wait 1 second and then send 6 messages in a row. You're probably looking for something like this:
await setTimeout(function() {sendMessage(recipient_id);}, delay)
await setTimeout(function() {sendMessage(recipient_id);}, delay)
await setTimeout(function() {sendMessage(recipient_id);}, delay)
await setTimeout(function() {sendMessage(recipient_id);}, delay)
await setTimeout(function() {sendMessage(recipient_id);}, delay)
await setTimeout(function() {sendMessage(recipient_id);}, delay)
You can check out the wingbot library here on the github. It helps with building a simple bot. Like this:
const { Router } = require('wingbot');
const bot = new Router();
bot.use('start', (req, res) => {
res.typingOn()
.wait(1000)
.text('Hello');
});
Related
This is one of many (imho rather incomplete) examples in the docs:
var button = document.querySelector('#submit-button');
braintree.dropin.create({
authorization: 'CLIENT_AUTHORIZATION',
container: '#dropin-container'
}, function (createErr, instance) {
button.addEventListener('click', function () {
instance.requestPaymentMethod(function (requestPaymentMethodErr, payload) {
// Submit payload.nonce to your server
});
});
});
It's all nice and easy but I don't see how I can change the state of button according to the state of "is the user done with adding a payment method?".
Is this even possible? It seems that the click on the button actually performs the fetching of the nonce (which comes as payload.nonce). However, how can I disable button until the user has finished his conversation with Braintree/PayPal?
And the answer is look in the docs (no not those docs) - those docs.
I still don't know how I found that link.
The dropin instance has a on() function where you can register a callback for certain events (just do it - look at the docs already):
instance.on('paymentMethodRequestable', function(event) {
thiz._logger.info('Payment method is now requestable');
setTimeout(() => thiz.paymentMethodAvailable = true, 400);
});
I have a modal that shows up in a protractor test. When I run the test case solo, it works perfectly, but when I run it as part of the larger file (currently 10 cases, some lengthy) it slows things down and, as a result, the modal is slower to open and close. The chain effect is that, when I try to click on a button on the page, it crashes since the modal (which was supposed to be closed by now) is still there and blocking the button.
How can I properly sense when the modal is open/closed so that I can work around this?
Thanks!
(Also, this is an AngularJS application)
These are my helper functions for manual waiting:
static async forElementAbsent(element) {
return await new Promise<any>((resolve) => {
browser.driver.wait(ExpectedConditions.stalenessOf(element), 10000,
`Timed out waiting for element to be absent`).then(() => {
resolve();
});
});
}
static async forElementPresent(element) {
return await new Promise<any>((resolve) => {
browser.driver.wait(ExpectedConditions.presenceOf(element), 10000,
`Timed out waiting for element to be present`).then(() => {
resolve();
});
});
}
In our tests, we are waiting for modals to be displayed manually. We have helper functions such as
export async function forCondition(condition: () => boolean | PromiseLike<boolean>, timeout = 20000): Promise<boolean> {
try {
return await browser.wait(condition, timeout);
} catch (err) {
return false;
}
}
Given function waits for a condition to fullfill. So in your particular case you would have to find out the css selector of the modal which is displayed.
Let's say the selector is by.css('.modal.visible'), then we would have to write something like the following if we wanted to wait for the modal to be displayed:
t.it('modal should be displayed', async function() {
// wait for modal to be displayed
var result = await forCondition(function() {
return element(by.css('.modal.visible')).isDisplayed();
});
expect(result).toBeTruthy();
});
I need to locate elemet "username" using Protractor.
I have used
browser.waitForAngularEnabled(false);
due to the fact that the login page is not Angular.
My script is as simple as this:
describe('slowchat', function() {
it('start test', function() {
browser.waitForAngularEnabled(false);
browser.get('https://www.test.no/eai/bankid20');
element(by.css('[value="Logg inn"]')).click();
browser.waitForAngularEnabled(true);
});
});
However I get error:
Failed: No element found using locator: By(css selector, [value="Logg inn"])
Please see attachment for the place I want to locate.
Any idea why this is not able to locate the button?
Now i saw that in circle is input with username but you ask about button to submit the form, btw you can turn off angular by flag
//entering non-angular site
browser.ignoreSynchronization=true;
//Some code
element(by.css('button[onclick="mySubmit()"])).click();
//when you go back to angular site
browser.ignoreSynchronization=false;
So when you disable waiting for angular or ignore synchronization, this means that you will need to roll your own waiting for some sort of stability. There are two ways you can accomplish this: 1. Turn off the control flow and use async await. 2. Resolve your promises before moving to the next action.
Resolving promises
I think you need to use the done() method call here because of the async calls. My general take away on resolving promises is ew... prefer using async / await and removing the control flow.
describe('slowchat', () => {
it('start test', (done) => {
browser.waitForAngularEnabled(false);
browser.get('https://www.test.no/eai/bankid20').then(() => {
return element(by.css('[value="Logg inn"]')).click().then() => {
browser.waitForAngularEnabled(true);
done();
});
});
});
});
Using async / await and removing the control flow
The control flow is being deprecated so this will get you ahead of the curve. I would suggest doing it this way. It will make your tests easier to debug.
describe('slowchat', () => {
it('start test', async() => {
await browser.waitForAngularEnabled(false);
await browser.get('https://www.test.no/eai/bankid20');
await element(by.css('[value="Logg inn"]')).click();
await browser.waitForAngularEnabled(true);
});
});
When launching this you will need to set the flag to turn off the control flow. In your config, you'll need to add this: SELENIUM_PROMISE_MANAGER: false
I am trying to wait for spinner to disappear and then for my steps to execute but nothing is working for me.
browser.wait(function () {
return this.spinner.isDisplayed().then(function (result) {
return !result;});}, 20000);
and i even tried with
browser.wait(function () {
return !browser.isElementPresent(this.spinner);}, 20000);
even with below method
browser.sleep(1000);
this.spinner.isPresent().then(function (result) {
if (result === true) {
var EC = protractor.ExpectedConditions;
browser.wait(EC.invisibilityOf(this.spinner), 10000);}});
then only thing that works is
browse.sleep(10000);
i don't want to use sleep in my code. can anyone help me with how to wait for complete http request to complete and then process with testing
you should consider using Expected Conditions since they return true/false based on current conditions
http://www.protractortest.org/#/api?view=ProtractorExpectedConditions.prototype.invisibilityOf
so your test case would become:
browser.wait(EC.invisibilityOf(this.spinner),20000).then(function(){
...continue test, spinner gone
});
UPDATE
in order to use done, you would generally pass this cb into your it() function. This means your test could look like
describe("example describe",function(){
it("should be an example only", function(done){
request.get("www.google.com",function(res){
//done with async request, now call done
done();
})
})
});
Since your entire code isn't posted up here, you should have something similar to:
it("should wait for spinner to go bye-bye",function(done){
browser.wait(EC.invisibilityOf(this.spinner),20000).then(function(){
done()
});
});
I am uploading a file (using multer) and doing some processing on it and i want to show progress while i am working on it. my issue is that the client receives all the updates after the processing is finished. so the progress bar goes from 0 to 100% instead of incrementally updating as the code progresses.
The server side
var multer = require('multer')
var uploading = multer({ dest: 'uploads/' })
app.post('/result', uploading.single('audio'), function(req, res) {
var oldPath = req.file.path;
var newPath = req.file.destination + req.file.originalname;
fs.renameSync(oldPath, newPath)
var ext = newPath.substring(newPath.length-3);
io.emit('message',{'message': 'upload completed !'});
console.log('1st update');
if (ext =='mp3' || ext == 'wav')
{
//console.log('running audio code');
io.emit('update',{'step': 'Converting Audio',percent: 2/10});
audio_code(newPath );
//res.render('Results_Audio', { });
}
else if (ext =='avi' || ext == 'mp4')
{
console.log('run video code');
res.render('Results_Video', { });
}
});
on the client side
socket.on('update',function(data) {
console.log(data);
Update(data);});
function Update(data){
document.getElementById('NameArea').innerHTML = data.step;
console.log(data.percent*100);
document.getElementById('ProgressBar').style.width = data.percent*100 + '%';
document.getElementById('percent').innerHTML = (Math.round((data.percent*100))) + '%';
}
As soon as your post hits the server it will run the block
app.post('/result', uploading.single('audio'), function(req, res) {
...
}
Which means you will immediately get
io.emit('message',{'message': 'upload completed !'});
io.emit('update',{'step': 'Converting Audio',percent: 2/10});
Then you wait while this runs
audio_code(newPath );
Nothing is output.
then when audio_code completes you will get
to the finish.
Are you emitting any progress inside of audio_code ? If not then that's why you get no progress increments.
I solved the issue. switching from socket to primus showed me that since my code was a bit computationally intensive and running synchronously it blocked the server for a time long enough for the websocket to timeout and close. so all i had to do is increase the time allowed for the server to respond to the ping pong check before timing out.