filter mails by property contact alias

This commit is contained in:
androidlover5842
2026-01-24 21:57:06 +05:30
parent 9300a85bd3
commit 0d3472c60e
12 changed files with 178 additions and 16 deletions

View File

@@ -49,6 +49,8 @@ class EmailIngestionService(
@Value("\${mail.imap.protocol:imaps}")
private val protocol: String
) {
@Value("\${storage.emails.publicBaseUrl}")
private val publicBaseUrl: String = ""
private val running = AtomicBoolean(false)
@Value("\${mail.imap.enabled:false}")
private val enabled: Boolean = false
@@ -86,9 +88,10 @@ class EmailIngestionService(
val subject = message.subject
val from = message.from?.firstOrNull()?.toString()
val recipients = extractRecipients(message)
val receivedAt = message.receivedDate?.toInstant()?.atOffset(OffsetDateTime.now().offset)
val body = extractText(message)
val property = matchProperty(subject, body)
val property = matchProperty(subject, body, recipients)
val inbound = InboundEmail(
property = property,
@@ -98,6 +101,10 @@ class EmailIngestionService(
receivedAt = receivedAt
)
val rawBytes = extractRawMessage(message)
if (rawBytes != null) {
inbound.rawEmlPath = emailStorage.storeEml(property?.id, messageId, rawBytes)
}
inbound.rawPdfPath = emailStorage.storePdf(property?.id, messageId, subject, body)
inboundEmailRepo.save(inbound)
@@ -153,7 +160,8 @@ class EmailIngestionService(
}
val guest = resolveGuest(property, extracted)
val booking = createBooking(property, guest, extracted, sourceBookingId)
val emailUrl = "${publicBaseUrl}/properties/${property.id}/inbound-emails/${inbound.id}/file"
val booking = createBooking(property, guest, extracted, sourceBookingId, emailUrl)
inbound.booking = booking
inbound.status = InboundEmailStatus.CREATED
inbound.processedAt = OffsetDateTime.now()
@@ -214,7 +222,8 @@ class EmailIngestionService(
property: Property,
guest: Guest,
extracted: Map<String, String>,
sourceBookingId: String
sourceBookingId: String,
emailAuditPdfUrl: String?
): Booking {
val zone = ZoneId.of(property.timezone)
val checkin = parsedDate(extracted["checkinDate"], zone)
@@ -226,7 +235,8 @@ class EmailIngestionService(
source = extracted["otaSource"]?.takeIf { !it.contains("NONE", true) } ?: "OTA",
sourceBookingId = sourceBookingId,
expectedCheckinAt = checkin,
expectedCheckoutAt = checkout
expectedCheckoutAt = checkout,
emailAuditPdfUrl = emailAuditPdfUrl
)
return bookingRepo.save(booking)
}
@@ -253,10 +263,20 @@ class EmailIngestionService(
}
}
private fun matchProperty(subject: String?, body: String): Property? {
private fun matchProperty(subject: String?, body: String, recipients: List<String>): Property? {
val haystack = "${subject ?: ""}\n$body".lowercase()
val properties = propertyRepo.findAll()
val matches = properties.filter { property ->
if (recipients.isNotEmpty()) {
val propertyEmails = property.emailAddresses.map { it.lowercase() }.toSet()
if (propertyEmails.isNotEmpty() && recipients.any { it.lowercase() in propertyEmails }) {
return@filter true
}
val orgEmails = property.org.emailAliases.map { it.lowercase() }.toSet()
if (orgEmails.isNotEmpty() && recipients.any { it.lowercase() in orgEmails }) {
return@filter true
}
}
val aliases = mutableSetOf<String>()
aliases.add(property.name)
aliases.add(property.code)
@@ -267,6 +287,15 @@ class EmailIngestionService(
return if (matches.size == 1) matches.first() else null
}
private fun extractRecipients(message: Message): List<String> {
val list = mutableListOf<String>()
val to = message.getRecipients(Message.RecipientType.TO)
val cc = message.getRecipients(Message.RecipientType.CC)
(to ?: emptyArray()).forEach { list.add(it.toString()) }
(cc ?: emptyArray()).forEach { list.add(it.toString()) }
return list
}
private fun extractText(message: Message): String {
return try {
val content = message.content
@@ -280,6 +309,16 @@ class EmailIngestionService(
}
}
private fun extractRawMessage(message: Message): ByteArray? {
return try {
val out = java.io.ByteArrayOutputStream()
message.writeTo(out)
out.toByteArray()
} catch (_: Exception) {
null
}
}
private fun extractFromMultipart(multipart: Multipart): String {
var text: String? = null
var html: String? = null