Introduction to AWS Lambda, SES and Google Recaptcha

I wanted to get my feet wet with AWS Lambda and had the perfect opportunity today when setting up my freelance and consulting website http://x-qa.com. I went with something simple, managing e-mail from our contact form via an API without having to build out an actually API and configuring Apache/Nginx etc. So pretty much I wrote a Lambda function in Python using the SES boto3 interface to send e-mail and also confirm captcha’s as shown below.

The code

from botocore.exceptions import ClientError
from boto3.session import Session
import boto3, json, urllib, urllib2

def format_text(name, number, project, description):
    """
    Text E-mail Format
    """
    return """Hello!

A new form has been submitted on x-qa.com!

Name: {}

Number: {}

Project Type: {}

Description:
{}

Have a great day!""".format(name, number, project, description)

def format_html(name, number, project, description):
    """
    Format HTML E-mail for sending
    Restricted via Lambda to one file so e-mail goes here
    """
    css = 'p { margin-top:10px; margin-bottom:20px;}'
    return '''<html>
    <head>
      <style>{}</style>
    </head>
    <p>Hello!</p>

<p>A new form has been submitted on x-qa.com!</p>

<p>Name: {}</p>

<p>Number: {}</p>
<p>{}</p>

<p>Have a great day!</p>'''.format(css, name, number, project, description)

def confirm_captcha(captcha):
    """
    Handle Google Recaptcha
    Confirmation
    """
    verify_url = 'https://www.google.com/recaptcha/api/siteverify'
    payload = {
        'secret': '',
        'response': captcha,
    }
    response = urllib2.urlopen(verify_url, urllib.urlencode(payload))
    data = json.load(response)
    if data['success'] is True:
        return True

def send_mail(session, text, html):
    """
    Send SES Mail or raise Error
    """
    ses = session.client('ses')
    try:
        ses.send_email(
            Source='[email protected]',
            Destination={
                'ToAddresses': [
                    '[email protected]',
                ]
            },
            Message={
                'Subject': {
                    'Data': 'New message from X-QA.com',
                },
                'Body': {
                    'Text': {
                        'Data': text,
                    },
                    'Html': {
                        'Data': html,
                    },
                }
            },
        )
    except ClientError:
        raise ValueError("Unable to Send E-mail via SES")

def lambda_handler(event, context):
    """
    Handle POST request form X-QA
    """
    operation = event['operation']
    if operation == 'send_email':
        name = event['name']
        number = event['number']
        project = event['project']
        description = event['description']
        captcha = event['g-recaptcha-response']
        if confirm_captcha(captcha):
            text = format_text(name, number, project, description)
            html = format_html(name, number, project, description)
            session = Session(
              aws_access_key_id='',
              aws_secret_access_key='',
            )
            if session:
                send_mail(session, text, html)
                return True
            else:
                raise ValueError("Unable to create AWS Session")
        else:
            raise ValueError("Captcha was not confirmed")

Aftermath

I’m now wondering why I don’t just write full applications using Lambda and API gateway… maybe because it would be slower but I think the service will be great for new developers to get their feet wet as well as more advanced users to handle event automation.

I’m also really excited to see what amazing things I will do with AWS Lambda in the future. Feel free to use this in your own mail implementations. See below to see how it is implemented on the frontend side.

Frontend integration

I use a simple Angular App to integrate my form with both Google Recaptcha and the AWS Lambda function above. Here is a simple overview of how I am doing it.

app.controller('landingCtrl', function($scope, $http, vcRecaptchaService) {

   $scope.publicKey = "6LfqKxkTAAAAAIrGZB_zgrW_mBUtjV2teyYXcYko";


   /*
    * Object To Capture Contact Information
    */
   $scope.contact = {};

   /*
    * Callback Functions for submit
    */
   var successMail = function(response) {
       if ( response.data == true ) {
           $scope.contact = {};
           $scope.successMessage = "Contact Sent!";
           $("input, select, textarea").removeClass('ng-touched')
       } else {
           console.log(response);
           $scope.errorMessage = "Unable to send mail right now";
       }
   };

   var errorMail = function(response) {
       console.log('something went wrong');
       console.log(response);
   };

   /*
    * Send API Call to SES
    * To Handle Contact Form
    */
   $scope.submit = function(contact) {
       var name = contact.name;
       var number = contact.number;
       var project = contact.project;
       var description = contact.description;
       if ( vcRecaptchaService.getResponse() === "" ) {
         $scope.errorMessage = "Please resolve the captcha before submitting";
       } else {
         $scope.errorMessage = null;
         if ( name && number && project && description ) {
           var html = description;
           var api_url = 'https://9e87218e09.execute-api.us-west-2.amazonaws.com/prod/xqaMail';
           var data = {
             "operation": "send_email",
             "name": name,
             "number": number,
             "project": project,
             "description": description,
             "g-recaptcha-response": vcRecaptchaService.getResponse()
           };
           $http.post(api_url, data).then(successMail, errorMail);
         } else {
           $scope.errorMessage = "Missing required fields";
         }
       }
   };


 });

Write a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.