サポートアプリにEメールを使用する新しい方法:AWSチュートリアル

公開: 2022-03-11

電子メールは他のコミュニケーションプラットフォームほどクールではないかもしれませんが、それでも作業は楽しいものです。 私は最近、モバイルアプリにメッセージングを実装するという任務を負いました。 唯一の落とし穴は、実際のコミュニケーションは電子メールで行う必要があるということでした。 アプリユーザーは、テキストメッセージを送信するのと同じように、サポートチームと通信できるようにしたいと考えていました。 サポートチームのメンバーは、これらのメッセージを電子メールで受信する必要があり、また、発信元のユーザーに応答できる必要がありました。 エンドユーザーにとって、他の最新のメッセージングアプリと同じように見え、機能するために必要なものはすべてあります。

この記事では、JavaといくつかのAmazonのWebサービスを使用して、上記のようなサービスを実装する方法を見ていきます。 有効なAWSアカウント、ドメイン名、およびお気に入りのJavaIDEへのアクセスが必要です。

インフラストラクチャ

コードを作成する前に、メールのルーティングと消費に必要なAWSサービスを設定します。 メールの送受信にはSESを使用し、着信メッセージのルーティングにはSNS+SQSを使用します。

AWSを使用してプログラムでEメールを消費する

AmazonSESを使用したサポートアプリケーションで電子メールを活性化します。
つぶやき

それはすべてここでSESから始まります。 AWSアカウントにログインし、SESコンソールに移動することから始めます。

始める前に、メールを送信できる確認済みのドメイン名が必要です。

これは、ユーザーがメールメッセージを送信し、サポートメンバーが返信するドメインアプリになります。 SESを使用したドメインの確認は簡単なプロセスであり、詳細についてはこちらをご覧ください。

SESを初めて使用する場合、または送信制限をリクエストしていない場合、アカウントはサンドボックス化されます。 これは、AWSで確認されていないアドレスにメールを送信できないことを意味します。 これにより、チュートリアルの後半で架空のヘルプデスクに電子メールを送信するときにエラーが発生する可能性があります。 これを回避するには、SESコンソールの[電子メールアドレス]タブで、ヘルプデスクとして使用する予定の電子メールアドレスを確認できます。

確認済みのドメインを取得したら、ルールセットを作成できます。 SESコンソールの[ルールセット]タブに移動し、新しい受信ルールを作成します。

受信ルールを作成するときの最初のステップは、受信者を定義することです。

受信者フィルターを使用すると、SESが消費する電子メールと、各受信メッセージの処理方法を定義できます。 ここで定義する受信者は、アプリのユーザーメッセージが電子メールで送信されるドメインとアドレスパターンに一致する必要があります。 ここでの最も簡単なケースは、以前に確認したドメインの受信者を追加することです。この場合はexample.comです。 これにより、 example.comに送信されるすべての電子メールにルールを適用するようにSESが構成されます。 (例:foo @ example.com、bar @ example.com)。

ドメイン全体のルールを作成するには、 example.comの受信者を追加します。

アドレスパターンを一致させることも可能です。 これは、着信メッセージを異なるSQSキューにルーティングする場合に役立ちます。

キューAとキューBがあるとします[email protected][email protected]2つの受信者を追加できます。 キューAにメッセージを挿入する場合は、a [email protected]に電子メールを送信します。 この一部は、a @example.comの受信者と一致します。 +と@の間はすべて任意のユーザーデータであり、SESのアドレスマッチングには影響しません。 キューBに挿入するには、aをbに置き換えるだけです。

受信者を定義したら、次のステップは、新しい電子メールを消費した後にSESが実行するアクションを構成することです。 最終的にはこれらをSQSにまとめたいと考えていますが、現在、SESからSQSに直接移動することはできません。 ギャップを埋めるには、SNSを使う必要があります。 SNSアクションを選択し、新しいトピックを作成します。 最終的には、メッセージをSQSに挿入するようにこのトピックを設定します。

[SNSトピックの作成]を選択して名前を付けます。

トピックを作成したら、メッセージエンコーディングを選択する必要があります。 特殊文字を保持するためにBase64を使用します。 選択したエンコーディングは、サービスでメッセージを使用するときにメッセージがデコードされる方法に影響します。

ルールを設定したら、名前を付けるだけです。

次のステップは、SQSとSNSを設定することです。そのために、SQSコンソールに移動して、新しいキューを作成する必要があります。

わかりやすくするために、SNSトピックと同じ名前を使用しています。

キューを定義したら、アクセスポリシーを調整する必要があります。 SNSトピックに挿入する権限のみを付与します。 これは、SNSトピックarnに一致する条件を追加することで実現できます。

値フィールドには、SESが通知しているSNSトピックのARNを入力する必要があります。

SQSがセットアップされたら、SNSコンソールに戻って、光沢のある新しいSQSキューに通知を挿入するようにトピックを設定します。

SNSコンソールで、SESが通知しているトピックを選択します。 そこから、新しいサブスクリプションを作成します。 サブスクリプションプロトコルはAmazonSQSである必要があり、宛先は先ほど生成したSQSキューのARNである必要があります。

その後、方程式のAWS側をすべて設定する必要があります。 私たちは自分自身に電子メールを送ることによって私たちの仕事をテストすることができます。 SESで設定されたドメインにメールを送信してから、SQSコンソールに移動してキューを選択します。 あなたはあなたの電子メールを含むペイロードを見ることができるはずです。

メールを処理するJavaサービス

さあ、楽しい部分に移りましょう! このセクションでは、メッセージの送信と受信メールの処理が可能なシンプルなマイクロサービスを作成します。 最初のステップは、ユーザーに代わってサポートデスクにメールを送信するAPIを定義することです。

簡単なメモ。 このサービスのビジネスロジックコンポーネントに焦点を当て、RESTエンドポイントや永続性レイヤーを定義することはしません。

Springサービスを構築するには、SpringBootとMavenを使用します。 Spring Initializerを使用して、プロジェクトstart.spring.ioを生成できます。

まず、pom.xmlは次のようになります。

 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.toptal.tutorials</groupId> <artifactId>email-processor</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>email-processor</name> <description>A simple "micro-service" for emailing support on behalf of a user and processing replies</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

ユーザーに代わってサポートにメールを送信する

まず、ユーザーに代わってサポートデスクにメールを送信するためのBeanを定義しましょう。 このBeanの役割は、ユーザーIDからの着信メッセージを処理し、そのメッセージを事前定義されたサポートデスクの電子メールアドレスに電子メールで送信することです。

インターフェイスを定義することから始めましょう。

 public interface SupportBean { /** * Send a message to the application support desk on behalf of a user * @param fromUserId The ID of the originating user * @param message The message to send */ void messageSupport(long fromUserId, String message); }

そして空の実装:

 @Component public class SupportBeanSesImpl implements SupportBean { /** * Email address for our application help-desk * This is the destination address user support emails will be sent to */ private static final String SUPPORT_EMAIL_ADDRESS = "[email protected]"; @Override public void messageSupport(long fromUserId, String message) { //todo: send an email to our support address } }

また、AWS SDKをpomに追加しましょう。SESクライアントを使用して、メールを送信します。

 <dependency> <groupId>com.amazonaws</groupId> <artifactId>aws-java-sdk</artifactId> <version>1.11.5</version> </dependency>

最初に行う必要があるのは、ユーザーのメッセージを送信するための電子メールアドレスを生成することです。 私たちが生成するアドレスは、私たちのサービスの消費側で重要な役割を果たします。 ヘルプデスクの返信を元のユーザーにルーティングするのに十分な情報が含まれている必要があります。

これを実現するために、生成された電子メールアドレスに元のユーザーIDを含めます。 物事をきれいに保つために、ユーザーIDを含むオブジェクトを作成し、そのオブジェクトのBase64エンコードされたJSON文字列を電子メールアドレスとして使用します。

ユーザーIDをメールアドレスに変換する新しいBeanを作成しましょう。

 public interface UserEmailBean { /** * Returns a unique per user email address * @param userID Input user ID * @return An email address unique for the input userID */ String emailAddressForUserID(long userID); }

必要な同意と、JSONのシリアル化に役立つ単純な内部クラスを追加することから実装を始めましょう。

 @Component public class UserEmailBeanJSONImpl implements UserEmailBean { /** * The TLD for all our generated email addresses */ private static final String EMAIL_DOMAIN = "example.com"; /** * com.fasterxml.jackson.databind.ObjectMapper used to create a JSON object including our user ID */ private final ObjectMapper objectMapper = new ObjectMapper(); @Override public String emailAddressForUserID(long userID) { //todo: create the email address return null; } /** * Simple helper class we will serialize. * The JSON representation of this class will become our user email address */ private static class UserDetails{ private Long userID; public Long getUserID() { return userID; } public void setUserID(Long userID) { this.userID = userID; } } }

メールアドレスの生成は簡単です。必要なのは、UserDetailsオブジェクトを作成し、Base64でJSON表現をエンコードすることだけです。 createAddressForUserIDメソッドの完成版は、次のようになります。

 @Override public String emailAddressForUserID(long userID) { UserDetails userDetails = new UserDetails(); userDetails.setUserID(userID); //create a JSON representation. String jsonString = objectMapper.writeValueAsString(userDetails); //Base64 encode it String base64String = Base64.getEncoder().encodeToString(jsonString.getBytes()); //create an email address out of it String emailAddress = base64String + "@" + EMAIL_DOMAIN; return emailAddress; }

これで、SupportBeanSesImplに戻り、作成したばかりの新しい電子メールBeanを使用するように更新できます。

 private final UserEmailBean userEmailBean; @Autowired public SupportBeanSesImpl(UserEmailBean userEmailBean) { this.userEmailBean = userEmailBean; } @Override public void messageSupport(long fromUserId, String message) throws JsonProcessingException { //user specific email String fromEmail = userEmailBean.emailAddressForUserID(fromUserId); }

メールを送信するには、AWSSDKに含まれているAWSSESクライアントを使用します。

 /** * SES client */ private final AmazonSimpleEmailService amazonSimpleEmailService = new AmazonSimpleEmailServiceClient( new DefaultAWSCredentialsProviderChain() //see http://docs.aws.amazon.com/AWSJavaSDK/latest/javadoc/com/amazonaws/auth/DefaultAWSCredentialsProviderChain.html );

DefaultAWSCredentialsProviderChainを利用してクレデンシャルを管理しています。このクラスは、ここで定義されているAWSクレデンシャルを検索します。

SES、最終的にはSQSへのアクセスでプロビジョニングされたAWSアクセスキーを使用します。 詳細については、Amazonのドキュメントをご覧ください。

次のステップは、 AWSSDKを使用してメールサポートにmessageSupportメソッドを更新することです。 SES SDKは、これを簡単なプロセスにします。 完成したメソッドは次のようになります。

 @Override public void messageSupport(long fromUserId, String message) throws JsonProcessingException { //User specific email String fromEmail = userEmailBean.emailAddressForUserID(fromUserId); //create the email Message supportMessage = new Message( new Content("New support request from userID " + fromUserId), //Email subject new Body().withText(new Content(message)) //Email body, this contains the user's message ); //create the send request SendEmailRequest supportEmailRequest = new SendEmailRequest( fromEmail, //From address, our user's generated email new Destination(Collections.singletonList(SUPPORT_EMAIL_ADDRESS)), //to address, our support email address supportMessage //Email body defined above ); //Send it off amazonSimpleEmailService.sendEmail(supportEmailRequest); }

試してみるには、テストクラスを作成し、SupportBeanを注入します。 SupportBeanSesImplで定義されているSUPPORT_EMAIL_ADDRESSが、所有している電子メールアドレスを指していることを確認してください。 SESアカウントがサンドボックス化されている場合は、このアドレスも確認する必要があります。 電子メールアドレスは、SESコンソールの[電子メールアドレス]セクションで確認できます。

 @Test public void emailSupport() throws JsonProcessingException { supportBean.messageSupport(1, "Hello World!"); }

これを実行すると、受信トレイにメッセージが表示されます。 さらに良いことに、メッセージに返信して、前に設定したSQSキューを確認してください。 応答を含むペイロードが表示されます。

SQSからの返信の消費

最後のステップは、SQSからのメールを読み込み、メールメッセージを解析し、返信が転送されるユーザーIDがどのユーザーIDであるかを把握することです。

Amazon SQSのようなメッセージキューイングサービスは、速度、信頼性、またはスケーラビリティを損なうことなくサービスが相互に通信できるようにすることで、サービス指向アーキテクチャで重要な役割を果たします。

新しいSQSメッセージをリッスンするには、SpringCloudAWSメッセージングSDKを使用します。 これにより、アノテーションを介してSQSメッセージリスナーを設定できるようになり、ボイラープレートコードをかなり回避できます。

まず、必要な依存関係。

SpringCloudメッセージングの依存関係を追加します。

 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-aws-messaging</artifactId> </dependency>

そして、SpringCloudAWSをpom依存関係管理に追加します。

 <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-aws</artifactId> <version>1.1.0.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>

現在、Spring Cloud AWSはアノテーション駆動型の設定をサポートしていないため、XMLBeanを定義する必要があります。 幸い、多くの構成は必要ないので、Beanの定義はかなり軽くなります。 このファイルの主なポイントは、アノテーション駆動型キューリスナーを有効にすることです。これにより、メソッドにSqsListenerとしてアノテーションを付けることができます。

リソースフォルダーにaws-config.xmlという名前の新しいXMLファイルを作成します。 私たちの定義は次のようになります。

 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aws-context="http://www.springframework.org/schema/cloud/aws/context" xmlns:aws-messaging="http://www.springframework.org/schema/cloud/aws/messaging" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/cloud/aws/context http://www.springframework.org/schema/cloud/aws/context/spring-cloud-aws-context.xsd http://www.springframework.org/schema/cloud/aws/messaging http://www.springframework.org/schema/cloud/aws/messaging/spring-cloud-aws-messaging.xsd"> <!--enable annotation driven queue listeners --> <aws-messaging:annotation-driven-queue-listener /> <!--define our region, this lets us reference queues by name instead of by URL. --> <aws-context:context-region region="us-east-1" /> </beans>

このファイルの重要な部分は、 <aws-messaging:annotation-driven-queue-listener />です。 デフォルトのリージョンも定義しています。 これは必須ではありませんが、そうすることで、URLではなく名前でSQSキューを参照できるようになります。 AWSクレデンシャルを定義していません。これらを省略すると、SpringはデフォルトでDefaultAWSCredentialsProviderChainになります。これは、以前にSESBeanで使用したのと同じプロバイダーです。 詳細については、SpringCloudAWSのドキュメントをご覧ください。

Spring BootアプリでこのXML構成を使用するには、明示的にインポートする必要があります。 @SpringBootApplicationクラスに移動し、インポートします。

 @SpringBootApplication @ImportResource("classpath:aws-config.xml") //Explicit import for our AWS XML bean definition public class EmailProcessorApplication { public static void main(String[] args) { SpringApplication.run(EmailProcessorApplication.class, args); } }

次に、着信SQSメッセージを処理するBeanを定義しましょう。 Spring Cloud AWSでは、単一のアノテーションでこれを実現できます。

 /** * Bean reasonable for polling SQS and processing new emails */ @Component public class EmailSqsListener { @SuppressWarnings("unused") //IntelliJ isn't quite smart enough to recognize methods marked with @SqsListener yet @SqsListener(value = "com-example-ses", deletionPolicy = SqsMessageDeletionPolicy.ON_SUCCESS) //Mark this method as a SQS listener //Since we already set up our region we can use the logical queue name here //Spring will automatically delete messages if this method executes successfully public void consumeSqsMessage(@Headers Map<String, String> headers, //Map of headers returned when requesting a message from SQS //This map will include things like the relieved time, count and message ID @NotificationMessage String rawJsonMessage //JSON string representation of our payload //Spring Cloud AWS supports marshaling here as well //For the sake of simplicity we will work with incoming messages as a JSON object ) throws Exception{ //com.amazonaws.util.json.JSONObject included with the AWS SDK JSONObject jsonSqsMessage = new JSONObject(rawJsonMessage); } }

ここでの魔法は@SqsListenerアノテーションにあります。 これにより、Springはエグゼキューターをセットアップし、SQSのポーリングを開始します。 新しいメッセージが見つかるたびに、注釈付きのメソッドがメッセージの内容とともに呼び出されます。 オプションで、着信メッセージをマーシャリングするようにSpring Cloudを構成して、キューリスナー内で強い型のオブジェクトを操作できるようにすることができます。 さらに、基になるAWS呼び出しから返された単一のヘッダーまたはすべてのヘッダーのマップを挿入することができます。

以前にaws-config.xmlでリージョンを定義したので、ここで論理キュー名を使用できます。省略したい場合は、値を完全修飾SQSURLに置き換えることができます。 削除ポリシーも定義しています。これにより、条件が満たされた場合にSQSから着信メッセージを削除するようにSpringが設定されます。 SqsMessageDeletionPolicyには複数のポリシーが定義されており、consumeSqsMessageメソッドが正常に実行された場合にメッセージを削除するようにSpringを構成しています。

また、@ Headersを使用して返されたSQSヘッダーをメソッドに挿入しています。挿入されたマップには、受信したキューとペイロードに関連するメタデータが含まれます。 メッセージ本文は@NotificationMessageを使用して挿入されます。 Springは、Jacksonを利用するか、カスタムメッセージ本文コンバーターを介したマーシャリングをサポートします。 便宜上、生のJSON文字列を挿入し、AWSSDKに含まれているJSONObjectクラスを使用して処理します。

SQSから取得したペイロードには、大量のデータが含まれます。 JSONObjectを見て、返されるペイロードをよく理解してください。 ペイロードには、通過したすべてのAWSサービス、SES、SNS、そして最後にSQSからのデータが含まれています。 このチュートリアルでは、送信先の電子メールアドレスのリストと電子メールの本文の2つだけを気にします。 メールを解析することから始めましょう。

 //Pull out the array containing all email addresses this was sent to JSONArray emailAddressArray = jsonSqsMessage.getJSONObject("mail").getJSONArray("destination"); for(int i = 0 ; i < emailAddressArray.length() ; i++){ String emailAddress = emailAddressArray.getString(i); }

現実の世界では、ヘルプデスクの返信に元の送信者以外のものが含まれている可能性があるため、ユーザーIDを解析する前にアドレスを確認する必要があります。 これにより、サポートデスクは、複数のユーザーに同時にメッセージを送信する機能と、アプリ以外のユーザーを含める機能の両方を利用できるようになります。

UserEmailBeanインターフェースに戻って、別のメソッドを追加しましょう。

 /** * Returns true if the input email address matches our template * @param emailAddress Email to check * @return true if it matches */ boolean emailMatchesUserFormat(String emailAddress);

UserEmailBeanJSONImplでは、このメソッドを実装するために2つのことを実行します。 まず、アドレスがEMAIL_DOMAINで終わっているかどうかを確認してから、マーシャリングできるかどうかを確認します。

 @Override public boolean emailMatchesUserFormat(String emailAddress) { //not our address, return right away if(!emailAddress.endsWith("@" + EMAIL_DOMAIN)){ return false; } //We just care about the email part, not the domain part String emailPart = splitEmail(emailAddress); try { //Attempt to decode our email UserDetails userDetails = objectMapper.readValue(Base64.getDecoder().decode(emailPart), UserDetails.class); //We assume this email matches if the address is successfully decoded and marshaled return userDetails != null && userDetails.getUserID() != null; } catch (IllegalArgumentException | IOException e) { //The Base64 decoder will throw an IllegalArgumentException it the input string is not Base64 formatted //Jackson will throw an IOException if it can't read the string into the UserDetails class return false; } } /** * Splits an email address on @ * Returns everything before the @ * @param emailAddress Address to split * @return all parts before @. If no @ is found, the entire address will be returned */ private static String splitEmail(String emailAddress){ if(!emailAddress.contains("@")){ return emailAddress; } return emailAddress.substring(0, emailAddress.indexOf("@")); }

インターフェイスに追加したばかりのemailMatchesUserFormatと、@でメールアドレスを分割するためのシンプルなユーティリティメソッドの2つの新しいメソッドを定義しました。 emailMatchesUserFormatの実装は、Base64でデコードし、アドレス部分をUserDetailsヘルパークラスにマーシャルして戻すことで機能します。 これが成功した場合は、必要なuserIDが入力されていることを確認します。 これがすべてうまくいけば、安全に一致を想定できます。

EmailSqsListenerに戻り、更新されたばかりのUserEmailBeanを挿入します。

 private final UserEmailBean userEmailBean; @Autowired public EmailSqsListener(UserEmailBean userEmailBean) { this.userEmailBean = userEmailBean; }

次に、consumeSqsMethodを更新します。 まず、メール本文を解析してみましょう。

 //Pull our content, remember the content will be Base64 encoded as per our SES settings String encodedContent = jsonSqsMessage.getString("content"); //Create a new String after decoding our body String decodedBody = new String( Base64.getDecoder().decode(encodedContent.getBytes()) ); for(int i = 0 ; i < emailAddressArray.length() ; i++){ String emailAddress = emailAddressArray.getString(i); }

次に、メールアドレスとメール本文を処理する新しいメソッドを作成しましょう。

 private void processEmail(String emailAddress, String emailBody){ }

最後に、一致するものが見つかった場合にこのメソッドを呼び出すように電子メールループを更新します。

 //Loop over all sent to addresses for(int i = 0 ; i < emailAddressArray.length() ; i++){ String emailAddress = emailAddressArray.getString(i); //If we find a match, process the email and method if(userEmailBean.emailMatchesUserFormat(emailAddress)){ processEmail(emailAddress, decodedBody); } }

processEmailを実装する前に、UserEmailBeanにもう1つのメソッドを追加する必要があります。 メールからuserIDを返すメソッドが必要です。 UserEmailBeanインターフェースに戻って、最後のメソッドを追加します。

 /** * Returns the userID from a formatted email address. * Returns null if no userID is found. * @param emailAddress Formatted email address, this address should be verified using {@link #emailMatchesUserFormat(String)} * @return The originating userID if found, null if not */ Long userIDFromEmail(String emailAddress);

このメソッドの目的は、フォーマットされたアドレスからuserIDを返すことです。 実装は、検証方法と同様になります。 UserEmailBeanJSONImplに移動して、このメソッドに入力してみましょう。

 @Override public Long userIDFromEmail(String emailAddress) { String emailPart = splitEmail(emailAddress); try { //Attempt to decode our email UserDetails userDetails = objectMapper.readValue(Base64.getDecoder().decode(emailPart), UserDetails.class); if(userDetails == null || userDetails.getUserID() == null){ //We couldn't find a userID return null; } //ID found, return it return userDetails.getUserID(); } catch (IllegalArgumentException | IOException e) { //The Base64 decoder will throw an IllegalArgumentException it the input string is not Base64 formatted //Jackson will throw an IOException if it can't read the string into the UserDetails class //Return null since we didn't find a userID return null; } }

次に、EmailSqsListenerに戻り、processEmailを更新してこの新しいメソッドを使用します。

 private void processEmail(String emailAddress, String emailBody){ //Parse out the email address Long userID = userEmailBean.userIDFromEmail(emailAddress); if(userID == null){ //Whoops, we couldn't find a userID. Abort! return; } }

すごい! これで、必要なものはほぼすべて揃っています。 最後に行う必要があるのは、生のメッセージからの応答を解析することです。

電子メールクライアントは、数年前のWebブラウザと同様に、実装の不整合に悩まされています。

電子メールからの返信を解析することは、実際にはかなり複雑な作業です。 電子メールメッセージの形式は標準化されておらず、さまざまな電子メールクライアント間で大きな違いが生じる可能性があります。 生の応答には、応答と署名だけではありません。 ほとんどの場合、元のメッセージも含まれます。 Mailgunの賢い人々は、いくつかの課題を説明するすばらしいブログ投稿をまとめました。 また、メールを解析するための機械学習ベースのアプローチをオープンソース化しています。こちらをご覧ください。

MailgunライブラリはPythonで記述されているため、このチュートリアルでは、より単純なJavaベースのソリューションを使用します。 GitHubユーザーedlioは、GitHubのライブラリの1つに基づいて、JavaでMITライセンスの電子メールパーサーをまとめました。 この素晴らしいライブラリを使用します。

まず、pomを更新しましょう。https://jitpack.ioを使用してEmailReplyParserを取得します。

 <repositories> <repository> <id>jitpack.io</id> <url>https://jitpack.io</url> </repository> </repositories>

次に、GitHub依存関係を追加します。

 <dependency> <groupId>com.github.edlio</groupId> <artifactId>EmailReplyParser</artifactId> <version>v1.0</version> </dependency>

また、ApacheCommonsEメールを使用します。 EmailReplyParserに渡す前に、生の電子メールを解析してjavax.mailMimeMessageにする必要があります。 コモンズの依存関係を追加します。

 <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-email</artifactId> <version>1.4</version> </dependency>

これで、EmailSqsListenerに戻って、processEmailを終了できます。 この時点で、発信元のuserIDと生の電子メール本文があります。 残された唯一のことは、応答を解析することです。

これを実現するために、javax.mailとedlioのEmailReplyParserを組み合わせて使用​​します。

 private void processEmail(String emailAddress, String emailBody) throws Exception { //Parse out the email address Long userID = userEmailBean.userIDFromEmail(emailAddress); if(userID == null){ //Whoops, we couldn't find a userID. Abort! return; } //Default javax.mail session Session session = Session.getDefaultInstance(new Properties()); //Create a new mimeMessage out of the raw email body MimeMessage mimeMessage = MimeMessageUtils.createMimeMessage( session, emailBody ); MimeMessageParser mimeMessageParser = new MimeMessageParser(mimeMessage); //Parse the message mimeMessageParser.parse(); //Parse out the reply for our message String replyText = EmailReplyParser.parseReply(mimeMessageParser.getPlainContent()); //Now we're done! //We have both the userID and the response! System.out.println("Processed reply for userID: " + userID + ". Reply: " + replyText); }

要約

以上です! これで、元のユーザーに応答を提供するために必要なすべてが揃いました。

見る? 私はあなたに電子メールが楽しいことができると言いました!

この記事では、AmazonWebServicesを使用して複雑なパイプラインを調整する方法について説明しました。 この記事では、パイプラインは電子メールを中心に設計されています。 これらの同じツールを利用して、インフラストラクチャの保守について心配する必要がなく、代わりにソフトウェアエンジニアリングの楽しい側面に集中できる、さらに複雑なシステムを設計できます。

関連:アマゾンウェブサービスで生産性を高める