🐙

AWSのLambdaでお問い合わせAPIを作る

2021/02/24に公開

構成要素

DynamoDB: お問い合わせデータ保存用のDB
Lambda: 処理を記載
APIGateway: Lambdaのエンドポイント

目標

お問い合わせをされた受けた時にDBにレコードをのこす
お問い合わせをした人にお問い合わせを承ったことをメールで知らせる
管理人にお問い合わせがあったことをメールで知らせる

Lambdaの記述

いったん全部

import json
import boto3
import os

# for date and time
import re, datetime
# for generate id
import random, string

"""    DynamoDB    """
from boto3.dynamodb.conditions import Key

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table(os.environ["DB_NAME"])

"""    Email    """
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

from_email = os.environ["FROM_MAIL"]

smtp_user = os.environ["SMTP_USER"]
password = os.environ["MAIL_PASS"]

host = os.environ["HOST"]
email_port = 587

subject_user = os.environ["SUB_USER"]
subject_owner = os.environ["SUB_OWNER"]

message_user = os.environ["MESS_USER"]
message_owner = os.environ["MESS_OWNER"]

my_usual_email = os.environ["USUAL_MAIL"]

#########################################
#                functions              #
#########################################
# generate id
def gen_id():
    random_ = [random.choice(string.ascii_letters + string.digits + '-' + '_') for i in range(4)]
    id_ = "".join(random_)
    return id_
    
# generate date and time with int
def gen_datetime():
    str_datetime = "{}".format(datetime.datetime.now())
    str_date, str_time = str_datetime.split(" ")
    lst = [
        int(str_date.replace("-", "")),
        int(re.sub(r'\.[\d]*', '', str_time.replace(":", "")))
    ]
    return lst
    
# send email
def sending_user(name, mail, content, category):
    content = re.sub('\r|\n\r\|\n', '<br>', content)
    message = message_user.format(from_=from_email, user_=name, content_=content, category_=category)
    
    msg = MIMEMultipart('alternative')
    #msg = MIMEText(message, "html")
    
    msg["Subject"] = subject_user
    msg["To"] = mail
    msg["From"] = from_email
    
    msg_bod = MIMEText(message, "html")
    msg.attach(msg_bod)
    
    
    message_ow = message_owner.format(mail_=mail, user_=name, content_=content, category_=category)
    
    msg_owner = MIMEMultipart('alternative')
    
    msg_owner["Subject"] = subject_owner
    msg_owner["To"] = from_email
    msg_owner["From"] = from_email
    
    msg_owner_bod = MIMEText(message_ow, "html")
    msg_owner.attach(msg_owner_bod)
    
    
    server = smtplib.SMTP(host, email_port)
    server.ehlo()
    server.starttls()
    server.ehlo()
    
    
    # AWS SES
    server.login(smtp_user, password)
    
    # for user
    server.sendmail(from_email, mail, msg.as_string())
    
    # for owner
    server.sendmail(from_email, my_usual_email, msg_owner.as_string())
    
    server.close()
    

#########################################
#               operations              #
#########################################

def operation_scan():
    scanData = table.scan()# get all
    items = scanData['Items']
    print(items)
    return scanData
    
    
def operation_put(id, name, mail, content, category):
    date = gen_datetime()[0]
    time = gen_datetime()[1]
    putResponse = table.put_item(
        Item = {
            'id': id,
            'name': name,
            'mail': mail,
            'content': content,
            'category': category,
            'date': date,
            'time': time,
        }
    )
    if putResponse['ResponseMetadata']['HTTPStatusCode'] != 200:
        print(putResponse)
    else:
        print(200, putResponse)
        sending_user(name, mail, content, category)
    return putResponse


#########################################
#                 handler               #
#########################################

def lambda_handler(event, context):
    OperationType = event['OperationType']
    try:
        if OperationType == "PUT":
            Id = gen_id()
            Name = event["Keys"]["name"]
            Mail = event["Keys"]["mail"]
            Content = event["Keys"]["content"]
            Category = event["Keys"]["category"]
            return operation_put(Id, Name, Mail, Content, Category)
        elif OperationType == "SCAN":
            return operation_scan()
    except Exception as e:
        print("Erro Exception.")
        print(e)

詳細

DynamoDB(問い合わせ情報の保存用)

"""    DynamoDB    """
from boto3.dynamodb.conditions import Key # 今回は使ってないが、キーでフィルターを書ける等に使う

dynamodb = boto3.resource('dynamodb') # dynamodbへのアクセス
table = dynamodb.Table(os.environ["DB_NAME"]) # tableへのアクセス

メール設定(問い合わせが来たら自動で返信 & 持ち主の普段使いのメールに通知)

"""    Email    """
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

from_email = os.environ["FROM_MAIL"] # 送り元のメールアドレス(今回はAWS SESを使用)

smtp_user = os.environ["SMTP_USER"] # AWS SESで確かめてそれを使用(gmailならメールアドレスでOK)
password = os.environ["MAIL_PASS"] #  AWS SESで確かめてそれを使用

host = os.environ["HOST"] # AWS SESので確かめてそれを使用(email-smtp.<region>.amazonaws.com)
email_port = 587

subject_user = os.environ["SUB_USER"] # ユーザーに送る件名
subject_owner = os.environ["SUB_OWNER"] # オーナーに送る件名

message_user = os.environ["MESS_USER"] # ユーザーに送る本文
message_owner = os.environ["MESS_OWNER"] # オーナーに送る本文

my_usual_email = os.environ["USUAL_MAIL"] # 普段使いのメールアドレス

実行する処理

下準備用関数

# generate id
# ランダム文字列を生成
# 今回はやってませんが、文字数intを引数にすると使い勝手が良いです
def gen_id():
    random_ = [random.choice(string.ascii_letters + string.digits + '-' + '_') for i in range(4)]
    id_ = "".join(random_)
    return id_


# generate date and time with int
# 現在時刻のdateとtimeのリストを生成
def gen_datetime():
    str_datetime = "{}".format(datetime.datetime.now())
    str_date, str_time = str_datetime.split(" ")
    lst = [
        int(str_date.replace("-", "")),
        int(re.sub(r'\.[\d]*', '', str_time.replace(":", "")))
    ]
    return lst

メール送信用の関数

# send email
def sending_user(name, mail, content, category):
    content = re.sub('\r|\n\r\|\n', '<br>', content) # 入力された内容の改行文字を置換する
    # ユーザーへのメール送信
    message = message_user.format(from_=from_email, user_=name, content_=content, category_=category) # 本文を入力内容に合わせてフォーマット
    
    msg = MIMEMultipart('alternative')
    
    msg["Subject"] = subject_user # 件名
    msg["To"] = mail # 宛先
    msg["From"] = from_email # 送信元
    
    msg_bod = MIMEText(message, "html")
    msg.attach(msg_bod)
    
    
    # 管理人へのメール送信 (上とほぼ同じ)
    message_ow = message_owner.format(mail_=mail, user_=name, content_=content, category_=category)
    
    msg_owner = MIMEMultipart('alternative')
    
    msg_owner["Subject"] = subject_owner
    msg_owner["To"] = from_email
    msg_owner["From"] = from_email
    
    msg_owner_bod = MIMEText(message_ow, "html")
    msg_owner.attach(msg_owner_bod)
    
    
    # smtpサーバーを起動
    server = smtplib.SMTP(host, email_port)
    server.ehlo()
    server.starttls()
    server.ehlo()
   
    
    # AWS SES
    server.login(smtp_user, password) # smtpサーバーにログイン
    
    # for user
    server.sendmail(from_email, mail, msg.as_string()) # ユーザーにメールを送る
    
    # for owner
    server.sendmail(from_email, my_usual_email, msg_owner.as_string()) # 管理人にメールを送る
    
    server.close() # smtpサーバーを閉じる

アクセスがあった際の処理

def operation_put(id, name, mail, content, category):
    date = gen_datetime()[0] # 現在の日付
    time = gen_datetime()[1] # 現在の時刻
    putResponse = table.put_item( # tableの作成
        Item = {
            'id': id, # 引数で受け取る
            'name': name, # 引数で受け取る
            'mail': mail, # 引数で受け取る
            'content': content, # 引数で受け取る
            'category': category, # 引数で受け取る
            'date': date,
            'time': time,
        }
    )
    if putResponse['ResponseMetadata']['HTTPStatusCode'] != 200:
        print(putResponse)
    else:
        print(200, putResponse)
        sending_user(name, mail, content, category) # 問題がなければメールを送信
    return putResponse

試す

curl -X POST '<API Gatewayのエンドポイント>' -d '{"OperationType": "PUT", "Keys": {"name": "userA", "mail": "<your email>", "content": "This is sample", "category": "sample"}}'

これでメールが送受信されてデータも追加されてるはずです

curl -X POST '<API Gatewayのエンドポイント>' -d '{"OperationType": "SCAN"}'

でレコードを全部見れます

余談

NoSQLであることを考えるとpartition keyをmailにした方がイケてる気がする

アドバイスや疑問点等ありましたらコメントよろしくお願いいたします。

Discussion