I want to send SMS to Indian numbers using AWS SNS service. I am using node.js code to trigger SNS APIs to send SMS. Success rate of SMS delivery is too low.
Not sure what can be done.
I checked post AWS SNS is not sending SMS anymore and Amazon SNS -- Change Sender ID
Also, raised query on AWS Forum - https://forums.aws.amazon.com/thread.jspa?threadID=303941
but could not get any pointers. Few more things done:
increased SMS maxPrice to $5 USD
Cloudwatch too does not show real time logs.
SMS API does not give any error in response
This is my code:
exports.sendSMS = function(msg, phoneNumber){
phoneNumber = "+91" + phoneNumber;
setMessageAttributes();
createSubscription(phoneNumber);
var params = {
Message: msg,
PhoneNumber: phoneNumber
};
// Create promise and SNS service object
var publishTextPromise = sns.publish(params).promise();
// Handle promise's fulfilled/rejected states
publishTextPromise.then(
function(data) {
console.log("MessageID is , SMS sent ------" + data.MessageId);
console.log("JSON.stringify ------" + JSON.stringify(data));
}).catch(
function(err) {
console.error(err, err.stack);
});
}
function setMessageAttributes(){
var messageParams = {
attributes: {
'DefaultSMSType': 'Transactional',//Also tried setting this to OTP, no use
'DefaultSenderID': 'DevCare',
}
};
function createSubscription(endpoint){
// Create subscribe/email parameters
var subscribeParams = {
Protocol: 'SMS',
TopicArn: 'arn:aws:sns:us-east-1:xxxxxxx:US-xxxx',
Endpoint: endpoint
};
This is how response looks like for failed SMS:
Subscription ARN is arn:aws:sns:us-east-1:351564314200:US-sendSMS:0725db58-xxxc-4764-af31-6825xxxxxx
MessageID is ------d5212bf1-9183-5da4-826e-7e96a48xxxxx
JSON.stringify ------{"ResponseMetadata":{"RequestId":"5d9bxxx9-319d-52b4-8979-xxxxxxxx"},"MessageId":"d521xxxxf1-91xx-5da4-826e-xxxxxxxx"}
Any help is highly appreciated. So that I can make a choice to go ahead or not with AWS SNS API.
Related
I have been trying for weeks now to figure out how to invoke a call to sendEmail from an Alexa-hosted node.js skill. It is a very simple skill where the user makes a selection. when the selection is made I want to send a email to myself with contents of the selection. I have been trying to call sendEmail from my index.js, which contains the logic for my skill. I created a IAM role with the proper .json file as indicated on AWS, and was able to run a basic node file from the aws command line interface that sends me a email. What kind of steps will I have to take to get my Alexa skill to send the email? Can I just invoke the lambda function that is already working from my Alexa skill?
I have been trying to put the code below, and code similar to it without nodemailer doing the basic ses send email. I started with the aws ses webpage. I cannot find a single tutorial that actually walks you through step by step of calling this ses send email function in a Alexa skill and I would be so grateful to be pointed in the right direction.
'''
const Alexa = require('ask-sdk-core');
const AWS = require("aws-sdk");
let nodemailer = require("nodemailer");
let aws = require("#aws-sdk/client-ses");
// configure AWS SDK
process.env.AWS_ACCESS_KEY_ID = "xxxx";
process.env.AWS_SECRET_ACCESS_KEY = "xxxx";
const ses = new aws.SES({
apiVersion: "2010-12-01",
region: "us-east-1",
});
// create Nodemailer SES transporter
let transporter = nodemailer.createTransport({
SES: { ses, aws },
});
// send some mail
transporter.sendMail(
{
from: "xxxx#gmail.com",
to: "xxxx#gmail.com",
subject: "Message",
text: "I hope this message gets sent!",
ses: {
// optional extra arguments for SendRawEmail
Tags: [
{
Name: "tag_name",
Value: "tag_value",
},
],
},
},
(err, info) => {
console.log(info.envelope);
console.log(info.messageId);
}
);
'''
edit: thanks for the responses already guys! The only notable error I am getting is there is a problem with the requested skills response. So my Alexa skill is amazon hosted. Do I have to change this to use SES,sendEmail()? In my lambda page on amazon I have a file in a folder called sendEmail() but that gives me a error about the line AWS = require(etc.. in the debugger with the output "errorMessage": "2021-03-14T00:33:00.315Z e19e74c6-4c00-47dc-9872-77c0a602541a Task timed out after 3.00 seconds" the code I have in the lambda function titled sendEmail is actually the below code. the line sendEmail() also gives there is a problem with the requested skills response from the Alexa. I do not see my Alexa skill in my lambda functions. Do I have to add it in? Sorry, I really am a noob for AWS programming. Thank you!
`AWS = require('aws-sdk');
// Set the region
AWS.config.update({region: 'us-east-1'});
// Create sendEmail params
var params = {
Destination: { /* required */
ToAddresses: [
'x#gmail.com',
/* more items */
]
},
Message: { /* required */
Body: { /* required */
Html: {
Charset: "UTF-8",
Data: "HTML_FORMAT_BODY"
},
Text: {
Charset: "UTF-8",
Data: "TEXT_FORMAT_BODY"
}
},
Subject: {
Charset: 'UTF-8',
Data: 'Test email'
}
},
Source: 'x#gmail.com', /* required */
ReplyToAddresses: [
'pamphl3t#gmail.com',
/* more items */
],
};
// Create the promise and SES service object
var sendPromise = new AWS.SES({apiVersion: '2010-12-01'}).sendEmail(params).promise();
// Handle promise's fulfilled/rejected states
sendPromise.then(
function(data) {
console.log(data.MessageId);
}).catch(
function(err) {
console.error(err, err.stack);
});`
I believe this part of the documentation is what you're looking for. after you put your custom skill on Lambda function you need to send the content of the body to your email:
const querystring = require('querystring');
let post_data = null;
exports.handler = function (event, context) {
post_data = querystring.stringify(event.body);
}
// your emailing code here
Check this repo too
Where are you defining process.env? If you're running in Alexa Hosted, you'll get environment variables for the Alexa-owned IAM role which will have no access to SES.
Have you tried hardcoding the key and secret from your personal AWS account? If that solves it, look at adding the dotenv package in your package.json, and follow its documentation to set the values in a .env file you can add to your .gitignore.
const AWS = require( 'aws-sdk' );
var SES = new AWS.SES( { apiVersion: '2010-12-01', region: 'us-east-1' } );
//when we are sending our email
//we have to return a promise
//this is because the sendEmail funx
//from SES
//is an ASYNCHRONOUS CALL to the simple email server
//we have to wait for this call to complete before RET
//this is the vital line that resolves my issue
if (typeof Promise === 'undefined' ) {
AWS.config.setPromisesDependency( require( 'bluebird' ) );
}
sendEmail("SOME TEXT");
function sendEmail ( text,event, context, callback ){
var link = "";
var params = {
Destination: {
ToAddresses: [ "address#gmail.com" ]
},
Message: {
Body: {
Text: { Data: link
}
},
Subject: { Data: " text + " has arrived!" }
},
Source: "verifiedaddress#gmail.com"
};
return SES.sendEmail( params ).promise();
}
I'm using nodemailer with gmail API to send mails.
Following code :-
var nodemailer = require('nodemailer');
var transporter = await nodemailer.createTransport({
service: 'Gmail',
auth: {
user: 'xx#usr.com',
pass: 'xxx'
}
});
console.log("Starting");
await transporter.sendMail({
from: 'xx#google.com',
to: 'xx#google.com',
subject: 'Hello !',
text: "Hello"
}, function(data, info){
});
Code is working perfectly on local & sending mails.
But, when used inside lambda nothing is happening. Function getting executed successfully.
The error would probably be in the (1) code, (2) IAM permissions (for SES, see Nodemailer SES doc, Connecting to the Amazon SES SMTP Endpoint and SES FAQ) or
(3) networking (if lambda runs from private VPC and networking isn't fully configured).
Or maybe its working fine (see #TheProgrammer's comment in this question)
To view and understand the error, try the following:
Adding logging to the error callback of sendMail method (see this):
transporter.sendMail({
from: 'sender#example.com',
to: 'recipient#example.com',
subject: 'Message',
text: 'I hope this message gets delivered!'
}, (err, info) => {
console.log(info.envelope);
console.log(info.messageId);
console.log(err);
});
Viewing the error in CloudWatch logs (accessed directly from View logs in CloudWatch, docs)
Usecase
I want to subscribe email to SNS topic and i need to confirm it programmatically rather than manually clicking at the confirmation link.
What i had did
This is usually for confirmation i am using SNS confirmSubscription method which will take token as a parameter. I can able to achieve the token by calling the SNS subscribe method by passing protocol as email-json. For this i received the following subscription message at the endpoint.
{
"Type" : "SubscriptionConfirmation",
"MessageId" : "xxx-9160-49a3-960f-xx",
"Token" : "2336412f37fb687adfaf",
"TopicArn" : "arn:aws:sns:ap-south-1:xx-xx",
"Message" : "You have chosen to subscribe to the topic arn:aws:sns:ap-south-1:xx-xx.\nTo confirm the subscription, visit the SubscribeURL included in this message.",
"SubscribeURL" : "https://sns.ap-south-1.amazonaws.com/?Action=xx&TopicArn=arn:aws:sns:ap-south-1:477530015769:xx-xx&Token=2336412f37fb6872",
"Timestamp" : "2019-03-21T08:34:26.597Z",
"SignatureVersion" : "1",
"Signature" : "bIwq8Q92Yvrn8bBrIDQJ2chc5y/5IPAZPPR4roZ6WfK7kaE0QTbJpqAmHMH0sHL4aoyTrapLwX4e6HaEd6nGny8EAWj5wA2JvFCrO9BFQu",
"SigningCertURL" : "https://sns.ap-south-1.amazonaws.com/SimpleNotificationService-xx.pem"
}
What i want
How can able to extract the token value from the above json. This is the below lambda i am using to programmatically confirm the sns subscriber.
const AWS = require('aws-sdk');
var sns = new AWS.SNS();
var snsconfirm= {
Token: 'xx', /* required */ -->How can i able to retrieve the token value from the endpoint json.
TopicArn: 'arn:aws:sns:ap-south-1:xxx:xx-xxx', /* required */
AuthenticateOnUnsubscribe: 'true'
};
var snsparams = {
Protocol: 'email-json', /* required */
TopicArn: 'arn:aws:sns:ap-south-1:xx:xx-xx', /* required */
Endpoint: 'abc#xyz.com'
};
exports.handler = async (event) => {
// TODO implement
try {
const snsrespone = await sns.subscribe(snsparams).promise();
const confirmsub = await sns.confirmSubscription(snsconfirm).promise();
} catch (err) {
console.log(err.message);
throw err;
}
};
Any help is appreciated
Thanks
You can only programmatically confirm the subscription if you have the token, as you have already found out. This is most likely to make sure you can't subscribe someone unwillingly.
Your only option, assuming you have access to the endpoint, is to setup a filter of some sort that triggers a Lambda that can subscribe off the reply. Possibly by setting an email forward rule in your email to forward to an SES email, that in turn triggers a Lambda that confirms the subscription.
I have and preordered AWS snowball that has been ordered with all its events raised.
I need to know what the event is that is running (it will send a lambda when its shipped and when it arrives back to amazon, how can i get different operations for this?)
The Examples are too simple
my lambda is
var http = require('http');
exports.handler = function(event, context) {
var httpOptions = {
host: "myip",
port: '80',
path: "/createFirs",
method: "GET"
};
if (false) {
http.get(httpOptions, function(response) {
response.on('data', function (chunk) {
console.log(chunk);
//context.done(null, chunk);
context.succeed(event);
});
response.on('error', function(err) {
console.log(err);
context.done(err, null);
});
});
} else {
context.done(new Error("snowball lambda isn't complete yet"));
}
};
Information about AWS Import/Export Snowball jobs is available via the DescribeJob API.
There are also notifications generated by Snowball that are sent via Amazon SNS. Notifications include:
In transit
Delivered
Importing
Completed
You can subscribe an AWS Lambda function to an Amazon SNS topic, so that the Lambda function is called when the Snowball notification is received.
I have been searching all over the web and nothing gives a clear answer to confirm the subscription request from amazon SNS. I already send the subscription from the amazon console to my website, but what's next? I am using amazon EC2 as my server with PHP.
Before you even configure the HTTP/HTTPS endpoint subscription through AWS management console, you need to make sure that the HTTP or HTTPS endpoint of your PHP web site has the capability to handle the HTTP POST requests that Amazon SNS generates. There are several types of SNS messages: SubscriptionConfirmation, Notification and UnsubscribeConfirmation. Your PHP code needs to get the header x-amz-sns-message-type from request and process it based on the message type. For SubscriptionConfirmation message, your PHP application needs to process the POST message body, which is a JSON document. In order to subscribe the topic, your PHP code needs to visit the "SubscriberURL" specified in the JSON body. Optionally, you should verify the signature to make sure the authenticity of message before subscribing the topic.
You can find more details on AWS documentation: http://docs.aws.amazon.com/sns/latest/dg/SendMessageToHttp.html
Here is an express application (Node.js) which confirms the SNS subscription:
const express = require('express')
const request = require('request')
// parse urlencoded request bodies into req.body
const bodyParser = require('body-parser')
const app = express()
const port = 8080
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
app.post('/', (req, res) => {
let body = ''
req.on('data', (chunk) => {
body += chunk.toString()
})
req.on('end', () => {
let payload = JSON.parse(body)
if (payload.Type === 'SubscriptionConfirmation') {
const promise = new Promise((resolve, reject) => {
const url = payload.SubscribeURL
request(url, (error, response) => {
if (!error && response.statusCode == 200) {
console.log('Yess! We have accepted the confirmation from AWS')
return resolve()
} else {
return reject()
}
})
})
promise.then(() => {
res.end("ok")
})
}
})
})
app.listen(port, () => console.log('Example app listening on port ' + port + '!'))
To use it one needs to install required packages:
yarn add express request body-parser
Once you confirm the subscription AWS will send a POST request to the server with the following content:
{
"Type": "SubscriptionConfirmation",
"MessageId": "XXXXXXXX-1ee3-4de3-9c69-XXXXXXXXXXXX",
"Token": "SECRET_TOKEN",
"TopicArn": "arn:aws:sns:us-west-2:XXXXXXXXXXXX:ses-test",
"Message": "You have chosen to subscribe to the topic arn:aws:sns:us-west-2:XXXXXXXXXXXX:ses-test. To confirm the subscription, visit the SubscribeURL included in this message.",
"SubscribeURL": "https://sns.us-west-2.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-west-2:XXXXXXXXXXXX:ses-test&Token=SECRET_TOKEN",
"Timestamp": "2018-11-21T19:48:08.170Z",
"SignatureVersion": "1",
"Signature": "SECRET",
"SigningCertURL": "https://sns.us-west-2.amazonaws.com/SimpleNotificationService-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.pem"
}
The payload contains SubscribeURL which is requested by the server.
The end point you have specified will get data from AWS SNS endpoint verification service, The same end point will be used to verify the end point and to get notifications from aws,
Simply dump the input sent by AWS SNS into one text file like,
$json_write_to_text = json_decode(file_get_contents("php://input"));
You will find all data sent by AWS SNS, but just find SubscriptionUrl (which will be specific for endpoint having valid token), Open this in browser you will have SubscriptionConfirmation status. That's it
Enjoy.
Spring cloud SNS subscription with annotation
spring cloud AWS has support for auto confirmation of subscriber, you just need to put this annotation "#NotificationSubscriptionMapping"
#Controller
#RequestMapping("/topicName")
public class NotificationTestController {
#NotificationSubscriptionMapping
public void handleSubscriptionMessage(NotificationStatus status) throws IOException {
//We subscribe to start receive the message
status.confirmSubscription();
}
#NotificationMessageMapping
public void handleNotificationMessage(#NotificationSubject String subject, #NotificationMessage String message) {
// ...
}
#NotificationUnsubscribeConfirmationMapping
public void handleUnsubscribeMessage(NotificationStatus status) {
//e.g. the client has been unsubscribed and we want to "re-subscribe"
status.confirmSubscription();
}
}
http://cloud.spring.io/spring-cloud-aws/spring-cloud-aws.html#_sns_support
I solved this using NodeJS backend. Lets say you have an API like this in HapiJS (Well it doesnt matter you can have another tech)
{
method: 'POST',
path: '/hello',
handler: ( request, reply ) => {
reply( Hello.print(request.payload) );
},
config: {
tags: ['api']
}
}
Just pass the payload you receive, on to your business logic.
In the business logic process it like this
'use strict';
const request = require('request');
exports.print = (payload) => {
payload = JSON.parse(payload);
if(payload.Type === 'SubscriptionConfirmation'){
return new Promise((resolve, reject) => {
const url = payload.SubscribeURL;
request(url, (error, response) => {
if (!error && response.statusCode == 200) {
console.log('Yess! We have accepted the confirmation from AWS');
return resolve();
}
else
return reject();
});
});
}
I am using request module from NPM to automatically accept such requests.
Another way would be to print the contents of payload and then click on the URL given in payload.SubscribeURL.
Once AWS accepts it you check the confirmation on the Subscriptions page where Subscription ARN would be changed from Pending Confirmation to a complex name-cum-SHA having your Topic name.