From 9aaa2f630c385ce6eb3a196f54327372253f097b Mon Sep 17 00:00:00 2001 From: dankito Date: Thu, 21 Nov 2024 20:30:20 +0100 Subject: [PATCH] Reading message body --- .../net/codinux/invoicing/mail/MailReader.kt | 29 +++++++++++++++++++ .../codinux/invoicing/mail/MailWithInvoice.kt | 4 +++ .../codinux/invoicing/mail/MailReaderTest.kt | 3 ++ 3 files changed, 36 insertions(+) diff --git a/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/mail/MailReader.kt b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/mail/MailReader.kt index 377e371..f5b5933 100644 --- a/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/mail/MailReader.kt +++ b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/mail/MailReader.kt @@ -112,6 +112,7 @@ class MailReader( return MailWithInvoice( message.from.joinToString(), message.subject, message.sentDate?.let { map(it) }, map(message.receivedDate), message.messageNumber, + getPlainTextBody(parts), getHtmlBody(parts), attachmentsWithEInvoice ) } @@ -194,6 +195,34 @@ class MailReader( } } + private fun getPlainTextBody(parts: Collection) = getBodyWithMediaType(parts, "text/plain") + + private fun getHtmlBody(parts: Collection) = getBodyWithMediaType(parts, "text/html") + + private fun getBodyWithMediaType(parts: Collection, mediaType: String): String? = try { + val partsForMediaType = parts.filter { it.mediaType == mediaType } + + if (partsForMediaType.size == 1) { + partsForMediaType.first().part.content as? String + } else if (partsForMediaType.isEmpty()) { + null + } else { + val partsForMediaTypeWithoutFilename = partsForMediaType.filter { it.part.fileName == null } + if (partsForMediaTypeWithoutFilename.size == 1) { + partsForMediaTypeWithoutFilename.first().part.content as? String + } else if (partsForMediaTypeWithoutFilename.isEmpty()) { + log.warn { "Multiple message parts with media type '$mediaType' found, but all have a filename" } + null + } else { + log.warn { "Multiple message parts with media type '$mediaType' found, but more than one does not have a filename" } + partsForMediaTypeWithoutFilename.first().part.content as? String + } + } + } catch (e: Throwable) { + log.error(e) { "Could not get message body for media type '$mediaType'" } + null + } + private fun map(date: Date): Instant = date.toInstant() diff --git a/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/mail/MailWithInvoice.kt b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/mail/MailWithInvoice.kt index 7052649..3f56b39 100644 --- a/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/mail/MailWithInvoice.kt +++ b/e-invoicing-domain/src/main/kotlin/net/codinux/invoicing/mail/MailWithInvoice.kt @@ -9,7 +9,11 @@ class MailWithInvoice( val sent: Instant?, val received: Instant, val messageNumber: Int, + val plainTextBody: String?, + val htmlBody: String?, val attachmentsWithEInvoice: List ) { + val plainTextOrHtmlBody: String? by lazy { plainTextBody ?: htmlBody } + override fun toString() = "${(sent ?: received).atZone(ZoneId.systemDefault()).toLocalDate()} $sender: $subject, ${attachmentsWithEInvoice.size} invoice(s)" } \ No newline at end of file diff --git a/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/mail/MailReaderTest.kt b/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/mail/MailReaderTest.kt index 6ef1250..2e3a441 100644 --- a/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/mail/MailReaderTest.kt +++ b/e-invoicing-domain/src/test/kotlin/net/codinux/invoicing/mail/MailReaderTest.kt @@ -27,6 +27,9 @@ class MailReaderTest { val result = underTest.listAllMessagesWithEInvoice(mailAccount) assertThat(result).isNotEmpty() + + val messagesWithoutBody = result.filter { it.plainTextOrHtmlBody == null } + assertThat(messagesWithoutBody).isEmpty() } } \ No newline at end of file