Jakarta Mail(旧 Java Mail)でメールを取得します。
build.gradle
implementation("jakarta.mail:jakarta.mail-api:2.1.2") implementation("org.eclipse.angus:jakarta.mail:2.0.2")
imports
import java.net.URLEncoder import jakarta.mail.Authenticator import jakarta.mail.BodyPart import jakarta.mail.Folder import jakarta.mail.PasswordAuthentication import jakarta.mail.Session import jakarta.mail.internet.MimeMultipart import org.eclipse.angus.mail.imap.IMAPMessage import java.util.* import org.eclipse.angus.mail.util.BASE64DecoderStream
コード説明
Sessionを作成します。IMAP4を使用しています。
private val session = Session.getDefaultInstance( Properties().also { it.setProperty("mail.imap.ssl.enable", "true"); it.setProperty("mail.imap.host", "mail.host"); it.setProperty("mail.imap.port", "993"); }, object : Authenticator() { override fun getPasswordAuthentication(): PasswordAuthentication { return PasswordAuthentication( "user name", "password", ) } }, )
messageを取得します。Stringが入っている場合もあれば、HTMLや画像データが入っている事もあります。パース処理は後述。
fun main(args: Array<String>) { session.getStore("imap").also { it.connect() }.use { imap4 -> imap4.getFolder("INBOX").also { it.open(Folder.READ_ONLY) }.use { folder -> folder.messages .map { it as IMAPMessage } .mapIndexed { index, it -> println("======================message index=$index=========================") println("subject: ${it.subject}") when (val content = it.dataHandler.content) { is String -> { println("String: ${content.toString().toPreviewString()}") } is MimeMultipart -> { MultipartParser.parseMultipart(content, 0) } else -> { println("=============Unknown=============") println(it.dataHandler.content) } } } } } }
コンソールに出力すると意図しない表示になるので、encodeして、先頭だけ見ます。MimeMultipartがネストしている事もあります。
private fun String.toPreviewString() : String { val content = this .replace("\n", "") .replace("\r\n", "") return URLEncoder.encode(content, Charsets.UTF_8).take(100) }
パースします。contentTypeを確認してパースしていきます。
ネストしているMimeMultipartについては、入っているものは同じ様な内容でした。(text/plainとtext/htmlとか)
object MultipartParser { fun parseMultipart(multipart: MimeMultipart, indent: Int) { (0 until multipart.count).map { index -> multipart.getBodyPart(index) }.mapIndexed { index, bodyPart -> println("${indent(indent)}Miltipart: $indent-$index") parse(indent, bodyPart) } } private fun parse(indent: Int, bodyPart: BodyPart) { when { bodyPart.isMimeType("text/plain") -> { println( "${indent(indent)}${bodyPart.contentType}: ${bodyPart.content.toString().toPreviewString()}" ) } bodyPart.isMimeType("text/html") -> { println( "${indent(indent)}${bodyPart.contentType}: ${bodyPart.content.toString().toPreviewString()}" ) } bodyPart.isMimeType("image/jpeg") -> { when(val content = bodyPart.content) { is BASE64DecoderStream -> { println("${indent(indent)}${bodyPart.contentType}: ${content}") } else -> { println("${indent(indent)}${bodyPart.contentType}: Unknown Class: ${content::class.java}") } } } bodyPart.content is MimeMultipart -> { println("${indent(indent)}->MimeMultipart") parseMultipart(bodyPart.content as MimeMultipart, indent + 1) } else -> { println("${indent(indent)}->Unknown:${bodyPart.contentType} ${bodyPart.content.toString().toPreviewString()}") } } } private fun indent(level: Int) = "\t".repeat(level) }