Indicating a WhatsApp message is received, via the Twilio API, in NodeJS? - node.js

We are creating a service that will receive WhatsApp messages via the Twilio service. This works, but our issue is that we can't work out how to tell the sender that our server has 'read' the message. The messages always appear as being 'delivered' and never 'read', even after responding to the message. We have looked in the documentation, but can't seem to see how to do this.
Our server is written in NodeJS and is using Express for HTTP side.
Below is an equivalent of the code we are using (not a running example):
import { twiml } from 'twilio';
const { MessagingResponse } = twiml;
async receiveMessage(req: Request, res: Response, next: NextFunction) {
const message = req.body;
// Send back an empty response, we will process asynchronously
const immediateResponse = new MessagingResponse();
res.setHeader('content-type', 'text/xml');
res.send(immediateResponse.toString());
// TODO indicate message as read
// Do what ever logic is needed for given message
const replyMessage = await processMessage(message);
const messageToTwilio = {
body: replyMessage,
from: message.To,
to: message.From
};
const twilioResponse = await this.client.messages.create(messageToTwilio);
// Record value of twilioResponse in DB
}
Can anyone suggest what in the API I should be using for this?

I contacted Twilio on this issue and it turns out this is not currently possible. While they consider this a useful functionality, it is not currently a priority for implementation.
Note, It is possible to get the delivery status of outgoing messages, via the status webhook, but it is not possible to indicate to the remote party that the incoming message was 'read'.

Related

Botkit Slackbot responds with 401 error every time

I'm trying to create a very simple Slack bot using botkit and Google App Engine, but for some reason I keep getting 401 errors any time I message the bot. The weird thing is that the Slack Event Subscription URL (the one ending in /api/messages) validates correctly, and I get a 200 response in GAE logs and validation within Slack.
But whenever I actually message the bot it always gets a 401 error with no message explaining the error at all. I've tried various combinations of the code below, and have now stripped it down to the bare minimum as found here. Aside from dependencies and a code to decrypt credentials (which I've verified is working as expected), this is my full code at the moment:
botInit();
async function botInit () {
const credentialsRaw = await getCredentials();
const credentials = JSON.parse(credentialsRaw);
const adapter = new SlackAdapter(credentials);
const controller = new Botkit({
adapter: adapter
});
controller.on('message', async(bot, message) => {
await bot.reply(message, 'I heard a message!');
});
}
I have also tried this for the messaging function:
controller.ready(() => {
controller.hears(['hello', 'hi'], ['message', 'direct_message'],
async (bot, message) => {
await bot.reply(message, 'Meow. :smile_cat:')
})
})
and this for setting up the controller:
const controller = new Botkit({
webhook_uri: '/api/messages',
adapter: adapter
});
And everything gives back the same exact 401 error, despite all of them working with the Event Subscription URL verification on Slack.
I had same issue but figured out the problem.
I had been using Client Secret as clientSigningSecret
But I should use Signing Secret !

Parsing request body prohibits request signature verification

I'm trying to build a serverless Slackbot using Lambda function. I end up with an error while verifying the Request URL through the Slack event API. #slack/events-api is the dependency that I'm using to capture the slack events.
Here is my code.
const sls = require('serverless-http');
const { createEventAdapter } = require('#slack/events-api');
require('dotenv').config();
const { SLACK_SIGNING_SECRET } = process.env
const slackEvents = createEventAdapter( SLACK_SIGNING_SECRET || '' );
slackEvents.on('message', async event => {
console.log('received!')
});
module.exports.server = sls(slackEvents.requestListener());
This is the error that I'm getting while verifing the request url
Slack Request URL verification
Can someone help me with this?
Just ran into this exact issue, and took a look into http-handler.js in node-slack-events.
All we have to do is store the raw request body as rawBody before serverless-http parses it.
serverless-http lets you transform the request, before it is sent to the app—great opportunity for a fix:
module.exports.handler = serverless(app, {
request(request) {
request.rawBody = request.body;
},
});
I'm not sure how to solve your problem exactly, but I do know what's causing it.
The library you're using, serverless-http parses the JSON body sent by Slack. This causes an error to be thrown, because the slack-api-sdk expects to parse the raw request body itself.
Could you try removing serverless-http and just respond to the API Gateway event?

Using Twilio API resources within Twilio Functions

I've nearly finished my phone system with twilio and the final part is a voicemail playback. If the call fails or is declined, the calling party can leave a message and it saves to the twilio recordings. All server side code is done in Twilio Functions.
Now I want the end party to be able to check their voicemail by dialing an extension and playing back the saved messages. Everything up to the playing back of messages is done because I can't get the recording uri from a list of recordings.
Note NodeJS is not my strong suite.
I have tried playing back all recordings with:
exports.handler = function(context, event, callback) {
const twilioClient = context.getTwilioClient();
let response = new Twilio.twiml.VoiceResponse();
twilioClient.recordings.each(recording => response.play(recording.uri));
callback(null, response);
}
But I don't the expected result (i.e. I get a 200 OK and the <Response/> xml). I have Enable ACCOUNT_SID and AUTH_TOKEN enabled in the functions configuration.
I feel like it has something to do with the callback and the request to the api being asynchronous but I couldn't find much information on the recordings docs (https://www.twilio.com/docs/api/voice/recording).
Found the documentation I was after in the automated docs (https://www.twilio.com/docs/libraries/reference/twilio-node/3.11.2/Twilio.Api.V2010.AccountContext.RecordingList.html).
client.recordings.each({
callback: function (recording) {
response.say('New Message');
const recordingSid = recording.sid;
},
done: function () {
callback(null, response);
},
});

Attach messagingServiceSid to a twiml response in nodejs library

I have a webhook setup, and I am able to receive messages and reply to them. I would like to have the responses sent by my webhook to have messagingServiceSid attached to them.
I didn't find on documentation a way to configure that for responses from my webhook, only for new SMS using
client.sendMessage({
messagingServiceSid: 'MG9752274e9e519418a7406176694466fa',
to: '+16518675309',
body: 'Phantom Menace was clearly the best of the prequel trilogy.'
}, function(err, message) {
console.log(message);
});
Is there something similar for this code? Is it doable through the UI?
app.post('/foo/bar/sms', twilio.webhook({
host:'gassy-ocelot-129.herokuapp.com',
protocol:'https'
}), function(request, response) {
var twiml = new twilio.TwimlResponse();
twiml.message('This HTTP request came from Twilio!');
response.send(twiml);
});
Images:
No messagingService on reply messages sent using twiml response
Message Detail view from logs
Twilio developer evangelist here.
As far as I'm aware, there's no way to reply to message from a message service with TwiML.
However, rather than using TwiML, you could just send the SMS back to your user from the REST API and return an empty <Response> to the incoming webhook. Something a bit like this:
app.post('/foo/bar/sms', twilio.webhook({
host:'gassy-ocelot-129.herokuapp.com',
protocol:'https'
}), function(request, response) {
// send the message from the message service
client.sendMessage({
messagingServiceSid: 'MG9752274e9e519418a7406176694466fa',
to: request.body.From,
body: 'Your message'
}, function(err, message) {
console.log(message);
});
// send empty TwiML response
var twiml = new twilio.TwimlResponse();
response.send(twiml);
})
Let me know if that helps at all.
If you receive an incoming SMS on a phone number currently set up to that Messaging Service (via the web ui or phone number REST), then the incoming requests will have MessagingServiceSid in the query string.

How to confirm subscribe and get SNS Notifications at a HTTP endpoint?

I'm using Amazon Elastic Beanstalk to run a nodejs web page. I just want to send notifications to this webpage from AWS SNS and catch them in real time. So, when i publish to the HTTP endpoint, nothing happens and i don't know how to get the notification.
As Http endpoint, i set my AWS Elastic-Beanstalk http address.
I'm reading the Amazon docs but nowhere i can find how to catch the sns message once at the http endpoint.
Please, any help will be very appreciated . Thanks.
Try this:
const express = require('express');
const router = express.Router();
const request = require('request');
var bodyParser = require('body-parser')
router.post('/',bodyParser.text(),handleSNSMessage);
module.exports = router;
var handleSubscriptionResponse = function (error, response) {
if (!error && response.statusCode == 200) {
console.log('Yess! We have accepted the confirmation from AWS');
}
else {
throw new Error(`Unable to subscribe to given URL`);
//console.error(error)
}
}
async function handleSNSMessage(req, resp, next) {
try {
let payloadStr = req.body
payload = JSON.parse(payloadStr)
console.log(JSON.stringify(payload))
if (req.header('x-amz-sns-message-type') === 'SubscriptionConfirmation') {
const url = payload.SubscribeURL;
await request(url, handleSubscriptionResponse)
} else if (req.header('x-amz-sns-message-type') === 'Notification') {
console.log(payload)
//process data here
} else {
throw new Error(`Invalid message type ${payload.Type}`);
}
} catch (err) {
console.error(err)
resp.status(500).send('Oops')
}
resp.send('Ok')
}
Note: I didn't use app.use as that will impact all my other endpoints.
The moment you give your HTTP/HTTPS endpoint and create subscription from aws console, what happens is , the Amazon sends a subscription msg to that endpoint. Now this is a rest call, and your app must have a handler for this endpoint, otherwise you miss catching this subscription message. The httpRequest object that your handler is passed, needs to access it's SNSMsgTypeHdr header field. This value will be "SubscriptionConfirmation". You need to catch this particular message first and then get the subscription url. You can handle it in your app itself or maybe print it out, and then manually visit that url to make the subscription. I would ideally suggest to make a subscription to the same topic at the same with your mail id, so that everytime your app gets a messages pushed , your mail id also gets the message(albeit the tokens will be different) but at least you will be sure that the message was pushed to your endpoint. All you need to do is keep working your app to handle the messages at that endpoint as per your requirements then.
There are 3 types of messages with SNS. Subscribe, Unsubscribe, and Notification. You will not get any Notification messages until you have correctly handled the subscribe message. Which involves making an API request to AWS when you receive the Subscribe request.
The call in this case is ConfirmSubscription: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SNS.html#confirmSubscription-property
Once you do that, then you will start receiving notification messages and you can handle those as your code allows.
After you subscribe your endpoint, Amazon SNS will send a subscription confirmation message to the endpoint.
You should have code at the endpoint that retrieves the SubscribeURL value from the subscription confirmation message and either visit the location specified by SubscribeURL itself or make it available to you so that you can manually visit the SubscribeURL, for example, using a web browser.
Amazon SNS will not send messages to the endpoint until the subscription has been confirmed.
You can use the Amazon SNS console to verify that the subscription is confirmed: The Subscription ID will display the ARN for the subscription instead of the PendingConfirmation value that you saw when you first added the subscription.

Resources