SalesforceとAmazon SESを使ったメール配信システム

はじめに

みなさんこんにちは。AWSも好きなSalesforceエンジニアの吉澤です。
今日はSalesforceとAmazon SESを使ったメール配信システムについてお話したいと思います。

メール送信におけるSalesforceの制限

1組織あたり1日に最大5000個の外部メールアドレスに対し一括メール送信ができる。
Salesforceの一括メール送信は取引先責任者、個人取引先、リード、および内部ユーザにのみ。
(内部ユーザへのメール送信は制限はありません。)
※Salesforceヘルプより引用

今回の仮想シナリオ

今回は、以下のような場合に、どうやったら簡単にメール配信ができるかを考えてみました。
1.Salesforceの外部ユーザ(5000人以上)に対し、ダイレクトメールを送付したい。
2.迷惑メール業者に認定されたくないので、不達管理は行う!

メール配信ができそうなツールを考えてみた。

メール配信を考えたときにまずはじめに思いついたのが、Amazon SESです。
Amazon SESはAmazon Web ServicesのEメールプラットフォームです。
メールの送信機能には以下のような特徴があります。
・SPFやDKIMなどの認証に対応している。
・メールが不達だった場合、Amazon SNSを使って結果を返してくれる。
・弊社連携ツールである、SkyOnDemandのメールアダプタを使うと簡単に送信できる。

こうした特長から、Amazon SES&SkyOnDemandであれば、今回の条件でも簡単にメール配信&不達管理ができそうです。

全体の構成

全体のシステム構成は以下のように設定しました。
システム構成図.png

1.配信者はメール対象オブジェクトに対象となるメールアドレスを登録します。
2.配信者がメール配信処理を行うと、SkyOnDemandがメール配信対象から対象メールアドレスを抽出し、Amazon SESを使ってメール配信される
3.不達のメールはAmazon SNSを使ってSalesforceに返され、Salesforceの処理で不達フラグを立てる。

メール送信設定

メール配信の事前準備として、メール送信サーバーのグローバルリソースの設定を行います。
こちらはリージョンごとに固定です。

メール送信設定.png

メール配信スクリプトはメールデータ取得→送信対象→宛先セット→メール送付と、これだけです。

スクリプト例.png

メール送信アイコンでは認証キーが必要ですので、以下のように設定します。
必須設定タブ
 送信元メールアドレス:AWS側で認証済のメールアドレス
認証タブ
 ユーザ名 :AWSで発行したメールユーザのアクセスキー
 パスワード:AWSで発行したメールユーザのシークレットキー

メール送信アイコン.png

メール送信はたったこれだけの作業でできました!

不達管理設定

不達になったメールはAmazon SNSで通知されます。
まずはAmazon SNSの仕様を調べてみました。

Amazon SNS

Amazon SNSの通知方法は6種類があります。
・HTTPS(JSON)
・HTTP(JSON)
・SQS
・Email
・Email-JSON
・Application
今回はSalesforceのメールサービスを使い、不達メールアドレスを受け取るようにしました。

メールサービス設定

メールサービス設定はこのような形で設定します

メールサービス.png

MailServiceClass

このメールサービスで呼び出すクラスでは、メール本文の解析、バウンスメールレコードを作成、メール本文内からステータス、メールアドレスを抽出し、メール配信対象オブジェクトを検索。該当するメールアドレスのメール配信対象に対し、不達のチェックフラグを立てる。
といった処理を行います。

global class BounceMailHandler implements Messaging.InboundEmailHandler {
    global Messaging.InboundEmailResult handleInboundEmail(Messaging.InboundEmail email, Messaging.InboundEnvelope envelope) {
        Messaging.InboundEmailResult result = new Messaging.InboundEmailresult();

        try {
            // バウンスメールを作成
            Bouncemail__c bmail = new Bouncemail__c();
            bmail.Detail__c = email.plainTextBody;

            // ステータス検索
            String statuscode = getObjectFromBody(bmail.Detail__c,'status\\\\":\\\\"','\\\\','"Message" : ','status');
            bmail.Status__c = statusCode;

            // メールアドレス検索
            String bounceMailAdderess = getObjectFromBody(bmail.Detail__c,'destination\\\\":\\[\\\\"','\\\\','"Message" : ','destination');
            bmail.Email__c = bounceMailAdderess;

            // タイムスタンプ検索
            Datetime bounceDatetime;
            String bounceTimeStamp = getObjectFromBody(bmail.Detail__c,'Timestamp" : "','"','"Timestamp" : ','Timestamp');
            if(!String.isEmpty(bounceTimeStamp)){
                System.debug(bounceTimeStamp);
                // 年取得
                Integer bYear = Integer.valueOf(bounceTimeStamp.subString(0,4));
                // 月取得
                Integer bMonth = Integer.valueOf(bounceTimeStamp.subString(5,7));
                // 日取得
                Integer bDay = Integer.valueOf(bounceTimeStamp.subString(8,10));
                // 時取得
                Integer bHour = Integer.valueOf(bounceTimeStamp.subString(11,13));
                // 分取得
                Integer bMin = Integer.valueOf(bounceTimeStamp.subString(14,16));
                // 秒取得
                Integer bSec = Integer.valueOf(bounceTimeStamp.subString(17,19));
                System.debug(bYear);
                System.debug(bMonth);
                System.debug(bDay);
                System.debug(bHour);
                System.debug(bMin);
                System.debug(bSec);
                // 日時型へ変換
                Datetime tmpDate = datetime.newInstance(bYear,bMonth,bDay,bHour,bMin,bSec);
                // 9時間加算
                Datetime bDate =  tmpDate.addHours(9);
                // 項目セット
                bmail.SendDateTime__c = bDate;
            }

            if((statuscode=='5.1.1' || statuscode=='5.3.0') && !String.isEmpty(bounceMailAdderess)){
                // 該当メールアドレスが設定されている取引先を検索
                List<SendMailTo__C> bounceMailToList = getMailToFromEmail(bounceMailAdderess);
                // メール配信対象更新
                if(bounceMailToList != null && bounceMailToList.size() > 0){
                    for(SendMailTo__C cont : bounceMailToList){
                        cont.MailBounceFlg__c = true;
                        cont.MailBounceDate__c = SYstem.now();
                    }
                    update bounceMailToList;

                    // メール配信対象と紐づけ
                    bmail.mailToUpdateFlg__c = true;
                }

            }
            Insert bmail;

            

        }catch(Exception e){
            String errMsg = '◆◆◆バウンスメール処理エラー:' + e.getMessage(); 
            system.debug(errMsg);

            // 本文のみセットし登録
            Bouncemail__c errorMail = new Bouncemail__c();
            errorMail.Detail__c = email.plainTextBody;
            Insert errorMail;

            // throw new EstimateWSMailHandlerException(errMsg);
        }
        return result;
    }
}

※処理の基幹部分のみを抜粋

これで不達となった対象ユーザを把握できるようになりました。

まとめ


より充実した機能を求めるとApp Exchangeでメール配信サービスなどの利用を考えますが、今回は単純なメール配信サービスであれば、Salesforceと他のサービスをつなぐ事で比較的簡単にできました。
SalesforceやAWSはどんどんと新しいサービスが発表されておりますので、より新しいサービスを触ってみたいと思います。