I've been working on this issue for a few hours with no success. I am triggering the below function to run every 15 minutes. I can see from the CloudWatch that the function is firing. From my logs, I know that the function successfully getting appointments that require notification be sent. I am using 2 try/catch blocks to try and get some error message coming through, but no errors are being logged.
When I run this function using sls invoke local, it works fine and sends the expected text message to the correct numbers. However, when deployed and run on a cron-basis, its not working but also not erroring out - so I am at a loss.
I think it's an async/await issue. Basically, the console logs show up in the Cloudwatch logs, but nothing inside the twilioClient.messages.create function comes out in the logs.
Any help would be appreciated - I am sure it's something simple, but been staring at this for a few hours now without any success!
function sendNotifications(appointments) {
console.log('require notifications', appointments.length);
appointments.forEach(function(appointment) {
// Create options to send the message
// Appointments show in the logs
console.log('appt', appointment);
console.log('from', process.env.TWILIO_PHONE_NUMBER);
try {
const options = {
to: `${appointment.meta.contact.number}`,
from: process.env.TWILIO_PHONE_NUMBER,
/* eslint-disable max-len */
body: `Hi ${appointment.meta.contact.name}. Just a reminder that you have an property viewing for ${appointment.meta.property.name} at ${appointment.date}, ${appointment.time}. Please reply with CONFIRM to confirm that you'll be attending this viewing or CANCEL BOOKING to cancel this viewing.`
/* eslint-enable max-len */
};
// Send the message! - this log displays
console.log('about to send message');
twilioClient.messages.create(options, function(err, response, callback) {
// Nothing in this block gets printed to the logs
if (err) {
// Just log it for now
console.log('ERROR', err);
} else {
// Log the last few digits of a phone number
let masked = appointment.meta.contact.number.substr(
0,
appointment.meta.contact.number.length - 5
);
masked += '*****';
console.log(`Message sent to ${masked}`);
try {
updateBooking({ booking: appointment, message: response });
} catch (e) {
console.log(e.message);
}
}
// Don't wait on success/failure, just indicate all messages have been
// queued for delivery
if (callback) {
callback.call();
}
});
} catch (e) {
console.log('ERR', e.message, appointment);
}
});
}
From the doc https://www.twilio.com/docs/sms/quickstart/node
twilioClient.messages.create does not have a callback params, but returns a Promise
Try to execute the Lambda with:
twilioClient.messages
.create({
body: 'just for test',
from: 'a verified number',
to: 'a verified number'
})
.then(message => console.log(message.sid));
If this works, then that was the issue.
Otherwise could be process.env.TWILIO_PHONE_NUMBER is not set correctly or ${appointment.meta.contact.number} may be missing or invalid. Maybe print those also.
Twilio could also not have been correctly set up. Make sure accountSid and authToken are set.
Related
This is the code:
router.post("/form", async (req, res) => {
try {
let { name, email, password } = req.body;
let user = await User.create({
name,
email,
password,
});
if (!user) return res.status(501).send("Something went wrong");
let token = await user.getSignedToken();
console.log(token); //Server logs this
user.emailToken = await user.verifyEmail();// This property is not being added in the saved mongodb document. It should, but again idk why it's not
await user.save({ validateBeforeSave: false });
console.log(user); // Server doesn't log this
const options = {
expires: new Date(
Date.now() + process.env.JWT_COOKIE_EXPIRE * 24 * 60 * 60 * 1000
),
};
console.log(options); // Server doesn't log this
res.cookie("token", token, options);
res.send("Check email for activation");
} catch (ex) {
res.send(ex);
}
});
Below is the verifyEmail method (in userSchema.. I'm using mongoose):
userSchema.methods.verifyEmail = async function () {
let emailToken = this._id + crypto.randomBytes(10).toString("hex");
let emailTokenHashed = await crypto
.createHash("sha256")
.update(passwordToken)
.digest("hex");
this.emailToken = emailTokenHashed;
return emailToken;
};
Now, it should send "Check email for activation", right? But, it's sending an empty response object. Also, if you see in the code, the server is logging only one console.log (i.e. the first console.log). But, it is sending a cookie (I checked via postman). So, what's the issue here?
This code sends res.send("Check email for activation"); or res.send(ex);. Since you say that:
console.log(options); // Server doesn't log this
then one of your await statements must be hitting a rejected promise and sending you to the catch. Add console.log(ex) right before res.send(ex) to see what the error is so you have this:
} catch (ex) {
console.log("got error", ex);
res.send(ex);
}
And, then see what you get in the log.
In general you never want to send an error object directly because most of its properties are non-enumerable so they won't get sent as JSON and that may make the error object completely empty when it gets converted to JSON and thus isn't useful to the client at all. Instead, log the error on your server and send a 500 status:
res.sendStatus(500);
Or, if you want to send an error object construct your own error object with regular, enumerable properties and send that.
Anyway, once you add the console.log(ex) to your server, it should tell you what the actual rejected promise is and that will show you what's going wrong with the request. ALWAYS log errors on the server to help you see and solve problems like this.
I am working on a Serverless AWS service that uses Twilio's programmable SMS to deliver text messages.
My setup is consistently delivering messages successfully when I run the stack locally (e.g. sls offline start), but in the deployed environment I seem to be unable to even call the method on the Twilio client.
Here's how the message delivery is set up:
const twilio = require('twilio');
const twilioClient = twilio(
process.env.TWILIO_SID,
process.env.TWILIO_TOKEN,
{
lazyLoading: true,
}
);
export function sendMessage(user, message) {
twilioClient.messages.create({
from: process.env.TWILIO_NUMBER,
to: user.phone,
body: message,
}, function(err, message) {
console.log('error', err);
console.log('message', message);
});
}
// And then usage in a Serverless Function Handler
function example(event, context, callback) {
context.callbackWaitsForEmptyEventLoop = false;
// user is also determined here
sendMessage(user, 'This is a message');
return {
body: JSON.stringify({}),
statusCode: 200
};
}
Locally, running this works and I am able to see the output of the message log, with nothing on the error log. However, when deployed, running this yields nothing -- the method appears to not even get called (and I can verify in the Twilio logs that no API call was made), therefor no error or message logs are made in the callback.
In debugging I've tried the following:
I've logged all the environment variables (Twilio SSID, auth token, phone number), as well as the function arguments, and they all appear to be in place. I also checked out the Lambda function itself to make sure the environment variables exist.
I've examined my CloudWatch logs; no errors or exceptions are logged. Other than the Twilio method not getting called the Lambda function is executed without issue.
I've tried logging things like twilio and twilioClient.messages.create to make sure the client and function definition didn't get wiped out somehow.
I thought maybe it had to do with context.callbackWaitsForEmptyEventLoop so I changing it from false to true.
I have come up empty, I can't figure out why this would be working locally, but not when deployed.
Edit: per the Twilio client example, if you omit the callback function the method will return a Promise. I went ahead and tried to await the response of the method:
export function sendMessage(user, message) {
return twilioClient.messages.create({
from: process.env.TWILIO_NUMBER!,
to: user.phone,
body: message,
});
}
// Usage...
async function example(event, context, callback) {
context.callbackWaitsForEmptyEventLoop = false;
try {
const message = await sendMessage(user, 'This is a message');
console.log('message', message)
} catch (error) {
console.log('error', error);
}
return {
body: JSON.stringify({}),
statusCode: 200
};
}
In this example the Lambda function is successful, but neither the message nor the error are logged.
I tried this and it works. I tried to make my code as similar to use, with a few changes.
const twilio = require('twilio');
const twilioClient = twilio(
process.env.TWILIO_SID,
process.env.TWILIO_TOKEN
);
let user = '+14075551212';
function sendMessage(user, message) {
return twilioClient.messages.create({
from: process.env.TWILIO_NUMBER,
to: user,
body: message,
});
}
exports.handler = async function(event, context, callback) {
try {
const message = await sendMessage(user, 'This is a message');
console.log('message', message);
callback(null, {result: 'success'});
} catch (error) {
console.log('error', error);
callback("error");
}
};
I have the following firebase cloud function
exports.sendToUser = functions.region('europe-west1').https.onRequest((request, response) => {
console.log('start');
const receiverUid = request.body.receiverId;
const requestingUid = getUserIdFromRequest(request);
// Notification details.
const payload = {
"data": {
"type": request.body.type,
"mainData": request.body.mainData
}
};
const options = {
"priority": "high"
}
console.log('check');
requestingUid.then(uid => {
console.log('complete');
payload.data.senderId = uid;
sendMessageToUser(receiverUid, payload, options);
response.status(200).send();
}).catch(err => {
console.error(err);
response.status(401).send(err);
});
});
My problem with this function is that it prints "start" and "check" all fine but doesn't print the "complete" or the "receiver" nor does it execute the "sendMessageToUser" function. However, the status 200 is responded and the function terminates which I can see in the logs in the developer console and on the client device.
Also, when replacing line response.status(200).send(); with response.status(400).send(); or some other response code the correct status (400) is sent but the logs are not printed and the "sendMessageToUser" function not executed.
From time to time, however, the function is properly executed so I'm assuming that the function is somehow prematurely terminated. But how can the correct response code be sent then? I'm aware of the necessity to return a promise from asynchronus functions but as this is a http-triggered (synchronus) function I wouldn't know how to handle this.
Besides, yesterday was the first time this particular error occured. Before that, it worked fine. So can anybody explain what is happening here?
I have a node app (version 8.11.1 using Typescript 2.8.1) that catches uncaught exceptions using the uncaughtException hook like this:
process.on('uncaughtException', (err) => {
await sendEmail(recipient, subject, body);
});
I'm calling an asynchronous method inside the handler to send out an email and the code isn't working. It appears that the node process dies before the async call can finish and I never receive the email.
It looks like you may not be able to successfully use async methods inside this handler. The documentation doesn't say that outright but it implies it stating
The correct use of 'uncaughtException' is to perform synchronous
cleanup of allocated resources
I'm not trying resume operation or do anything funky. All I want to do is send out and email stating that the system crashed. I am not able to find any libraries that will synchronously send emails so I'm at a bit of a loss on how to handle this.
I've seen one suggestion to synchronously write the data to a file (or database) and have some outside processing polling for the existence of that file and sending the email if it exists. I suppose that would work but it's pretty hacky. Any better solutions?
Update:
Okay well after running some more tests it looks like you can actually run async code from inside the uncaughtException handler just fine. The following works:
const mailer = require('nodemailer');
process.on('uncaughtException', async err => {
const transport = mailer.createTransport({
service: 'gmail',
auth: {
user: 'someone#email.com',
pass: 'abc'
}
});
const mailOptions = {
from: 'someone#email.com',
to: 'someone.else#email.com',
subject: 'hi',
text: 'there'
};
transport.sendMail(mailOptions, (error, info) => {
if (error) {
console.log(error);
}
else {
console.log(info);
}
});
});
throw new Error('boom');
The above code works fine as a standalone app, but if I copy it into my codebase, it doesn't work. The code executes but I don't get an email (presumably the app dies before it can finish sending). So there must be something else going on in my environment that is preventing it from working. I'll keep digging.
I don't know what library you use to send an email and what version of node js you use but if you are using node js version greater than 7 you can use async/await and send an email as below
var mailgun = require('mailgun-js')({apiKey: api_key, domain: domain});
process.on('uncaughtException', async (err) => {
var data = {
from: 'User <me#samples.mailgun.org>',
to: 'serobnic#mail.ru',
subject: 'ERROR MESSAGE',
text: `Caught exception: ${err}\n`,
};
var body = await mailgun.messages().send(data);
console.log(body);
});
// using callback - supported for all versions
process.on('uncaughtException', (err) => {
var data = {
from: 'User <me#samples.mailgun.org>',
to: 'serobnic#mail.ru',
subject: 'ERROR MESSAGE',
text: 'Caught exception:' + err,
};
mailgun.messages().send(data, function (err, body) {
console.log(body);
});
});
I am trying to build a facebook messenger bot using nodejs. I got the bot developed with core features. While testing for
a negative scenario where the user sends in a GIF or sticker, it has to respond "I couldn't get you. Sorry". It does send
that message but it hangs and keeps sending that message thereafter every few minutes. I noticed that the ngrok server threw an 500 HTTP
Internal Server Error for the POST request. On further debugging, I was able to find out that res.send(200) is not getting executed properly.
The console.log stmt that I have after res.send(200) never gets printed. Not sure what I may be missing. Any help is appreciated. Tried restarting the server and resubscribed to the app with a new ngork https link. The same message continues to get printed out :(.
Here is my code.
server.post('/', (req, res, next) => {
// Extract the body of the POST request
let data = req.body;
let incomingMessage = '';
if(data.object === 'page') {
data.entry.forEach(pageObj => {
// Iterate through the messaging Array
pageObj.messaging.forEach(msgEvent => {
incomingMessage = {
sender: msgEvent.sender.id,
timeOfMessage: msgEvent.timestamp,
message: msgEvent.message
}
});
});
}
const {
message,
sender
} = incomingMessage
if(message.text) {
f.txt(sender, 'Hi there!!!');
} else {
f.txt(sender, `I couldn't get you. Sorry :(`);
//res.send(200);
}
res.send(200);
console.log('We are at the end of post');
return next();
});
Maybe this answer doesn't resolve your problem but it can be helpful.
If you want to send a 200 HTTP code use this instead:
res.sendStatus(200); // equivalent to res.status(200).send('OK')
On the other hand, if this is not a middleware, you can remove the return next(); line.