I'm building a twilio function that reacts to incoming phone calls. After a call is received the function should send a SMS to the caller. However, I would like the SMS to be delayed by a couple minutes. Currently, my function looks like this:
exports.handler = function (context, event, callback) {
// Create a new voice response object
const client = context.getTwilioClient();
const client_number = event.From;
const twilio_number = +xxxx;
client.messages.create({ // Send SMS
to: client_number,
from: twilio_number,
body: "Hello on SMS"
}).then(() => { // When request to send SMS is complete, deal with the caller
let twiml = new Twilio.twiml.VoiceResponse();
twiml.say(""); // respond to voice caller
callback(null, twiml);
})
};
Right now the SMS is sent and then the response to the call is sent. I'm not sure how to delay the message.create. I've been reading this but it doesn't work because I can't deal with the call first and then send the message.
https://www.twilio.com/docs/runtime/quickstart/add-delay
Twilio developer evangelist here.
This is not something you can do with just a Twilio Function. While aLittleSalty's answer will work in a situation where your application and event loop continues to run, like in an Express app that you run, but in a Twilio Function once you call the callback method the event loop is terminated so the delay will be cancelled.
Similarly, Twilio Function execution time is limited to 10 seconds, so you can't delay a message by a couple of minutes even if the above would work.
One idea could be that you send the message once the call has completed. Your example code only includes a <Say> so I will work with that, but you could do this in other ways if the call continues differently.
First, update your initial Function to return just the TwiML, but add a <Redirect> after the <Say> so that once the message completes Twilio sends a new webhook.
exports.handler = function (context, event, callback) {
let twiml = new Twilio.twiml.VoiceResponse();
twiml.say(""); // respond to voice caller
twiml.redirect("/after_say");
callback(null, twiml);
};
Then create another Function at the /after_say path that sends the message and hangs up the call.
exports.handler = function (context, event, callback) {
// Create a new voice response object
const client = context.getTwilioClient();
const client_number = event.From;
const twilio_number = +xxxx;
let twiml = new Twilio.twiml.VoiceResponse();
twiml.hangup();
client.messages.create({ // Send SMS
to: client_number,
from: twilio_number,
body: "Hello on SMS"
}).then(() => {
callback(null, twiml);
}).catch(error => {
console.error(error);
callback(null, twiml);
});
}
That's still not going to give you a couple of minutes between the call coming in and the message being sent, but it will hopefully send once the call is complete and be a better experience.
This doesn't work with Twilio Functions check #philnash answer
You have to return the callback first to answer the call, and then in a delayed promise send the SMS
exports.handler = function (context, event, callback) {
const delay = (delayMS) => new Promise((res) => {
setTimeout(res, delayMS)
})
// Create a new voice response object
const client = context.getTwilioClient();
const client_number = event.From;
const twilio_number = +xxxx;
let twiml = new Twilio.twiml.VoiceResponse();
twiml.say(""); // respond to voice caller
callback(null, twiml);
return (
delay(10000)
.then(() =>
client.messages.create({ // Send SMS
to: client_number,
from: twilio_number,
body: "Hello on SMS"
})
)
.then(() => console.log("SMS sent"))
)
}
Hopefully this helps.
Note: delay is an utility function where you pass the amounts of milliseconds you want to wait.
Related
My goal here is for twillio to call a webhook, and then to have a few node js functions record what the user says, and then make an outgoing call to another phone number and play that recording.
However, I can't seem to get an outgoing call set up. The code works in a regular node js file, but as soon as it is called as a part of the webhook, it won't work:
const accountSid =
const authToken =
const client = require('twilio')(accountSid, authToken);
exports.handler = function(context, event, callback) {
console.log(event.CallSid, event.RecordingUrl);
console.log()
console.log("RECORDING URL: " + event.RecordingUrl + ".mp3");
let audioURL = event.RecordingUrl
audioURL+=".mp3";
const response = new Twilio.twiml.VoiceResponse();
response.say({ voice: 'woman', language: 'en-US' }, 'Thank you.');
client.calls
.create({
from: '+14********',
to: '+14***********',
url: 'https://www.soundhelix.com/examples/mp3/SoundHelix-Song-8.mp3'
})
callback(null, response);
};
Twilio developer evangelist here.
The issue here is that you are making the asynchronous request to make the call and then calling the callback immediately. That terminates the execution of the Twilio Function, and because it is immediate cancels that outbound API request. See the documentation on Twilio Function execution for more details.
To fix this you should only call the callback once the create function has completed.
As an extra tip, you can also get an authenticated client from the context object without having to load your accountSid or authToken yourself.
Also, in your example the URL you are providing for the outbound call appears to be an mp3. Your URL should return TwiML, or you can send TwiML as part of the API request.
I would update your function like so:
exports.handler = function(context, event, callback) {
console.log(event.CallSid, event.RecordingUrl);
console.log()
console.log("RECORDING URL: " + event.RecordingUrl + ".mp3");
let audioURL = event.RecordingUrl
audioURL+=".mp3";
const client = context.getTwilioClient();
const response = new Twilio.twiml.VoiceResponse();
response.say({ voice: 'woman', language: 'en-US' }, 'Thank you.');
client.calls
.create({
from: '+14********',
to: '+14***********',
twiml: `<Response><Play>${audioUrl}</Play></Response>`
})
.then(() => {
callback(null, response);
})
.catch(error => {
callback(error);
});
};
I have this function for testing sending notification to all users.
export const sendTestingNotification = functions.https.onRequest((request, response) => {
const message = "Hello world";
const body = "This is body"
const getAllUsersPromise = admin.database().ref('/users').once('value')
const payload = {
notification: {
title: message,
body: body
}
}
return getAllUsersPromise.then(results => {
var tokens : string[] = [];
console.log("Child Snapshot count: ", results.numChildren())
results.forEach(childSnapshot => {
var childData = childSnapshot.val();
var instanceId = String(childData.instanceId)
if (childData.instanceId != null) { tokens.push(instanceId); }
})
console.log('final tokens = ',tokens," notification= ",payload);
return admin.messaging().sendToDevice(tokens, payload).then(response2 => {
console.log ("Done sending notification call. Entering callback.")
const tokensToRemove : string[] = [];
response2.results.forEach ((result, index) => {
const error = result.error;
if (error) {
console.error('Failure sending notification to instance id = ', tokens[index], error);
}
else {
console.log("Successfully send notification to ", tokens[index])
}
});
return Promise.all(tokensToRemove);
})
.catch(console.log.bind(console));
})
.catch(console.log.bind(console));
});
I have only one user in firebase database, and that one user has instanceId.
Here's the console log:
Child Snapshot count: 1
final tokens = [ 'eMZHr5WgHmU:APA91bEKg8wAS5qYMxuSJqn...
Done sending notification call. Entering callback.
Successfully send notification to eMZHr5WgHmU:APA91bEKg8wAS5qYMxuSJqn...
Function execution took 60002 ms, finished with status: 'timeout'
What's left of my function execution so that it finishes properly? The browser that call the function never stops show loading indicator.
If I add a response.send before return Promise, the browser loading finished. But checking at the log showed me that the process still working and returned the "Function execution took 60002 ms, finished with status: 'timeout'" error. How can I fix this?
With HTTP type functions, you need to send a response to the caller to terminate the function. For all code paths in your function, you should be calling response.send() or something that will send that response. Please consult the documentation for more information an examples. In particular, read the section on terminating a function.
I'm trying to make an integration for Slack that queries a server and gets back some results to the user. The search sometimes takes longer than the window Slack allows for responses, so I need to immediately return status 200.
How can I do this with a lambda function? I tried using the callback function and then sending the data to another lambda service, but the original function waits for it to return, meaning I'm still getting held up by the server I'm querying.
Here's what I'm working with
var rp = require('request-promise');
exports.handler = (event, context, callback) =>{
//I wanted this to send back my STATUS 200 so the command wouldn't time out
callback(null, "Working...");
//I want this post to happen asynchronously so that slack gets the callback response while the search is happening
//but this still waits until the post comes back before resolving the callback
var options = {
method: 'POST',
uri: "https://url-to-other.service",
body:{
"team": event.team,
"label": event.label,
"url": event.aresponse_url
},
json:true
};
rp(options);
};
When I run this, the callback text shows up after the result from the other function, meaning this service is waiting for the other to stop running before returning the status.
That won't work in a single lambda function because as soon as you invoke callback() the lambda container dies. What you could do is have this lambda function invoke another lambda function before calling callback() which in turn will POST to your slack channel url.
Here's an example of how that would work (it's not a 100% but should give you a good idea of how it'll work.)
Function 1: (receive slack event, invoke second function, immediatly return 200
let AWS = require('aws-sdk')
exports.handler = (event, context, callback) => {
let lambda = new AWS.Lambda()
let params = {
FunctionName: 'YOUR_SECOND_FUNCTION_NAME',
InvocationType: 'Event', // Ensures asynchronous execution
Payload: JSON.stringify({
team: event.team,
label: event.label,
url: event.aresponse_url
})
}
return lambda.invoke(params).promise() // Returns 200 immediately after invoking the second lambda, not waiting for the result
.then(() => callback(null, 'Working...'))
}
Function 2: (receive first lambda event, wait for search to complete, POST to slack channel)
let rp = require('request-promise')
exports.handler = (event, context, callback) => {
let searchOptions = {
method: 'POST',
uri: 'https://url-to-other.service',
body: {
'team': event.team,
'label': event.label,
'url': event.aresponse_url
},
json:true
}
return rp(searchOptions)
.then(result => {
let slackOptions = {
method: 'POST',
uri: 'YOUR_SLACK_CHANNEL_URL',
body: {
text: JSON.stringify(result) // I stringified the search result for you for ease's sake, not sure what you need here
},
json: true
}
return rp(slackOptions)
})
.then(() => { callback(null, 'good measure') })
}
I had some code working fine for a REST endpoint in which a message was:
created in the database
stepA was processed
when stepA was ok, the response message was returned
stepB was processed.
This was the code:
// POST single message
app.post('/message', (req, res) => {
const url = req.body.properties.url
const image = req.body.properties.image
const extraField = req.body.properties.extraField
db.message.create({
url: url,
image: image,
})
.then(() => myProcess(extraField, 'stepA'))
.then(newMessage => res.json(newMessage))
.then(() => myProcess(extraField, 'stepB'))
})
Now I am trying to have the same using feathersjs, but I do not know how to do 2, 3, 4 exactly.
I have now an AFTER hook for the create method of the message service:
module.exports = function (options = {}) { // eslint-disable-line no-unused-vars
return function processNewMessage (hook) {
const { extraField } = hook.data.properties
Promise.resolve(myProcess(extraField, 'stepA'))
.then( <<NO-IDEA>> ) // Send RESPONSE!!
.then(() => myProcess(extraField, 'stepB'))
return Promise.resolve(hook);
};
};
So my question boils down to: How can I send the response and subsequently trigger 'myProcess stepB' using feathersjs?
Althoug this is 'legacy', I think it might still be relevant.
It is answered in the FAQ of feathersjs!
How to do processing after sending the response to the user:
It depends on the promise that you return in your hook. Here's an example of a hook that sends an email, but doesn't wait for a success message.
function (hook) {
// Send an email by calling to the email service.
hook.app.service('emails').create({
to: 'user#email.com',
body: 'You are so great!'
});
// Send a message to some logging service.
hook.app.service('logging').create(hook.data);
// Return a resolved promise to immediately move to the next hook
// and not wait for the two previous promises to resolve.
return Promise.resolve(hook);
}
I implemented this AWS Lambda that receives events from slack and response back to slack a sentence and I want to monitor their answer back to the lambda to verify that the message arrived and posted.
// Lambda handler
exports.handler = (data, context, callback) => {
switch (data.type) {
case "url_verification": verify(data, callback); break;
case "event_callback": process(data.event, callback); break;
default: callback(null);
}
};
// Post message to Slack - https://api.slack.com/methods/chat.postMessage
function process(event, callback) {
// test the message for a match and not a bot
if (!event.bot_id && /(aws|lambda)/ig.test(event.text)) {
var text = `<#${event.user}> isn't AWS Lambda awesome?`;
var message = {
token: ACCESS_TOKEN,
channel: event.channel,
text: text
};
var query = qs.stringify(message); // prepare the querystring
https.get(`https://slack.com/api/chat.postMessage?${query}`);
}
callback(null);
}
I want to know how can I get the response of my HTTPS request (that send to me by slack) back to my lambda?
If I understood correctly you want to wait for the result of the your get query.
In your code callback is called immediately and lambda finishes its execution.
To be able to wait for the response you need to remove callback from its current position in the code and call it after request was performed.
// Post message to Slack - https://api.slack.com/methods/chat.postMessage
function process(event, callback) {
// test the message for a match and not a bot
if (!event.bot_id && /(aws|lambda)/ig.test(event.text)) {
var text = `<#${event.user}> isn't AWS Lambda awesome?`;
var message = {
token: ACCESS_TOKEN,
channel: event.channel,
text: text
};
var query = qs.stringify(message); // prepare the querystring
https.get(`https://slack.com/api/chat.postMessage?${query}`, (res, err) => {
if (err) return callback(err);
callback(null);
})
}
// callback was here
}
If you can, use request/request-promise to save some lines of code.
To get the http response in your Lambda Function you just need to wait for the response before calling the Lambda Callback.
Eg.:
var request = require('request-promise');
exports.handler = (event, context, callback) => {
request('https://somedomain.com').then((body) => {
//got the response body
callback(null, body);
});
}
It's the same idea if you're using the https module.