I want to create a simple system like this
Using SES to receive email (no need sending email)
Once new email coming, get email body and POST to another Api.
Anyone know what is the technical to do something like that?
I found the answer now.
Config SES to accept receiving emails
Using S3 as email server to store emails.
On Email Receiving configuration (AWS console), Create rules set when new email coming and do 2 things
Store email to s3 bucket
Trigger Lambda function. Lambda function can read messageId but could not read email body, so need to use messageId as a Bucket Key and get body from s3 Bucket.
Do something you want with email body.
Code for lambda function to read email body. Other steps are no need any code, just setting on AWS console
import * as AWS from "#aws-sdk/client-s3";
const bucket = "email-storing-bucket-name";
async function getS3Object (bucketKey) {
return new Promise(async (resolve, reject) => {
const getObjectCommand = new AWS.GetObjectCommand({ Bucket: bucket, Key: bucketKey });
try {
const s3 = new AWS.S3({});
const response = await s3.send(getObjectCommand);
let responseDataChunks = [];
response.Body.once('error', err => reject(err));
response.Body.on('data', chunk => responseDataChunks.push(chunk));
response.Body.once('end', () => resolve(responseDataChunks.join('')));
} catch (err) {
return reject(err);
}
});
}
export const handler = async(event, context) => {
console.log(JSON.stringify(event));
const bucketKey = event.Records[0].ses.mail.messageId
console.log("email bucketKey == ", bucketKey);
await getS3Object(bucketKey).then(data => {
console.log("data ==", data);
// TODO: Call webhook or do anything with email body here
});
};
Hope this help someone else.
Related
Basically I'm trying to get a pre-signed URL for the putObject method in S3. I send this link back to my frontend, which then uses it to upload the file directly from the client.
Here's my code :
const AWS = require("aws-sdk");
const s3 = new AWS.S3({
accessKeyId: process.env.AWS_IAM_ACCESS,
secretAccessKey: process.env.AWS_IAM_SECRET,
region: 'ap-southeast-1',
});
const getPreSignedUrlS3 = async (data) => {
try {
//DO SOMETHING HERE TO GENERATE KEY
const options = {
Bucket: process.env.AWS_USER_CDN,
ContentType: data.type,
Key: key,
Expires: 5 * 60
};
return new Promise((resolve, reject) => {
s3.getSignedUrl(
"putObject", options,
(err, url) => {
if (err) {
reject(err);
}
else resolve({ url, key });
}
);
});
} catch (err) {
console.log(err)
return {
status: 500,
msg: 'Failed to sync with CDN, Please try again later!',
}
}
}
I'm getting the following error from the aws sdk : The security token included in the request is invalid.
Things I have tried :
Double check the permissions from my IAM user. Even made bucket access public for testing. My IAM user is given full s3 access policy.
Tried using my root user security key and access details. Still got the same error.
Regenerated new security credentials for my IAM user. I don't have any MFA turned on.
I'm following this documentation.
SDK Version : 2.756.0
I've been stuck on this for a while now. Any help is appreciated. Thank you.
Pre-signed URLs are created locally in the SDK so there's no need to use the asynchronous calls.
Instead, use a synchronous call to simplify your code, something like this:
const getPreSignedUrlS3 = (Bucket, Key, ContentType, Expires = 5 * 60) => {
const params = {
Bucket,
ContentType,
Key,
Expires
};
return s3.getSignedUrl("putObject", params);
}
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();
}
In my Android app, I want my users to be able to change their email addresses (that they use to connect to their accounts), without getting any verification code by email.
So far, I manage to change the email address, and thanks to a lambda, set email_verified to true automatically. But unfortunately, an email is still sent with a verification code...
Here is what I did in my Android app:
public void onClickChangeEmail(View view)
{
CognitoUserAttributes attributes = new CognitoUserAttributes();
attributes.getAttributes().put("email", "second#mail.com");
CognitoSettings
.getCognitoUserPool(MainActivity.this)
.getCurrentUser()
.updateAttributesInBackground(attributes, new UpdateAttributesHandler()
{
#Override
public void onSuccess(List<CognitoUserCodeDeliveryDetails> attributesVerificationList)
{
Log.i("tag", "Email updated!");
}
#Override
public void onFailure(Exception e)
{
e.printStackTrace();
}
});
}
And in my AWS console, I added a trigger in Cognito on Custom message, and here is my lambda function, which is triggered everytime a user updates his email:
const AWS = require('aws-sdk')
AWS.config.update({region: 'eu-central-1'});
exports.handler = (event, context, callback) => {
if (event.triggerSource === 'CustomMessage_UpdateUserAttribute')
{
const params = {
UserAttributes: [
{
Name: 'email_verified',
Value: 'true',
},
],
UserPoolId: event.userPoolId,
Username: event.userName,
};
var cognitoIdServiceProvider = new AWS.CognitoIdentityServiceProvider();
cognitoIdServiceProvider.adminUpdateUserAttributes(params, function(err, data) {
if (err) context.done(err, event); // an error occurred
else context.done(null, event); // successful response
});
}
else
{
context.done(null, event);
}
};
The only workaround I found is to throw an error instead of context.done(null, event);, but it doesn't look like a clean solution.
Is there a better and cleaner way to prevent Cognito from sending a verification email?
Thanks for your help.
I am calling the Cognito API in my Springboot Service and I am able to update a user's email without getting a verification code. In my adminUpdateUserAttributes() method, I am passing in:
Name: 'email_verified',
Value: 'true'
together with the email field that needs to be updated and it updates successfully without sending an email. Maybe the labda doesn't work correctly or they have fixed the bug since this is an old question.
I have a simple AWS Lambda function which makes an S3.getObject() call as follows:
const AWS = require('aws-sdk');
AWS.config.logger = console;
const s3 = new AWS.S3();
exports.handler = async (event) => {
return await getObject({
Bucket: "<MY-BUCKET>",
Key: "<MY-KEY>"
}).then( (res) => {
console.log('Retrieved object from S3');
console.log(res);
return res.Body.toString('ascii');
})
};
async function getObject(params){
return await s3.getObject(params).promise();
}
I've enabled logging SDK calls as per this document.
How do I get response headers for the s3.getObject() SDK call that was made? I am basically trying to retrieve the S3 request ID and extended request ID.
The in-built logger added via the "AWS.config.logger = console;" line does not seem to log response headers. How else do I get response headers for AWS JavaScript SDK calls?
P.S: Bonus points if you can let me know whether or not I need two await keywords in the code above.
Listen to httpHeaders event.
var requestObject = s3.getObject(params);
requestObject.on('httpHeaders', (statusCode, headers, response, statusMessage) => {
// your code here.
});
requestObject.promise()
.then(response => { ... })
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.