filter mails by property contact alias
This commit is contained in:
@@ -28,14 +28,16 @@ class EmailStorage(
|
|||||||
val safeName = (messageId ?: UUID.randomUUID().toString()).replace(Regex("[^A-Za-z0-9._-]"), "_")
|
val safeName = (messageId ?: UUID.randomUUID().toString()).replace(Regex("[^A-Za-z0-9._-]"), "_")
|
||||||
val fileName = "${safeName}_${OffsetDateTime.now().toEpochSecond()}.pdf"
|
val fileName = "${safeName}_${OffsetDateTime.now().toEpochSecond()}.pdf"
|
||||||
val path = dir.resolve(fileName).normalize()
|
val path = dir.resolve(fileName).normalize()
|
||||||
|
val tmp = dir.resolve("${fileName}.tmp").normalize()
|
||||||
|
|
||||||
val document = PDDocument()
|
val document = PDDocument()
|
||||||
val page = PDPage(PDRectangle.LETTER)
|
val page = PDPage(PDRectangle.LETTER)
|
||||||
document.addPage(page)
|
document.addPage(page)
|
||||||
val header = "Subject: ${subject ?: ""}\n\n"
|
val header = "Subject: ${subject ?: ""}\n\n"
|
||||||
writeText(document, page, header + body)
|
writeText(document, page, header + body)
|
||||||
document.save(path.toFile())
|
document.save(tmp.toFile())
|
||||||
document.close()
|
document.close()
|
||||||
|
atomicMove(tmp, path)
|
||||||
return path.toString()
|
return path.toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,4 +84,28 @@ class EmailStorage(
|
|||||||
content.endText()
|
content.endText()
|
||||||
content.close()
|
content.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun storeEml(propertyId: UUID?, messageId: String?, rawBytes: ByteArray): String {
|
||||||
|
val dir = if (propertyId != null) {
|
||||||
|
Paths.get(rootPath, propertyId.toString(), "raw")
|
||||||
|
} else {
|
||||||
|
Paths.get(rootPath, "unassigned", "raw")
|
||||||
|
}
|
||||||
|
Files.createDirectories(dir)
|
||||||
|
val safeName = (messageId ?: UUID.randomUUID().toString()).replace(Regex("[^A-Za-z0-9._-]"), "_")
|
||||||
|
val fileName = "${safeName}_${OffsetDateTime.now().toEpochSecond()}.eml"
|
||||||
|
val path = dir.resolve(fileName).normalize()
|
||||||
|
val tmp = dir.resolve("${fileName}.tmp").normalize()
|
||||||
|
Files.write(tmp, rawBytes)
|
||||||
|
atomicMove(tmp, path)
|
||||||
|
return path.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun atomicMove(tmp: Path, target: Path) {
|
||||||
|
try {
|
||||||
|
Files.move(tmp, target, java.nio.file.StandardCopyOption.ATOMIC_MOVE, java.nio.file.StandardCopyOption.REPLACE_EXISTING)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
Files.move(tmp, target, java.nio.file.StandardCopyOption.REPLACE_EXISTING)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package com.android.trisolarisserver.controller
|
||||||
|
|
||||||
|
import com.android.trisolarisserver.component.PropertyAccess
|
||||||
|
import com.android.trisolarisserver.db.repo.InboundEmailRepo
|
||||||
|
import com.android.trisolarisserver.models.property.Role
|
||||||
|
import com.android.trisolarisserver.security.MyPrincipal
|
||||||
|
import org.springframework.core.io.FileSystemResource
|
||||||
|
import org.springframework.http.HttpHeaders
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.http.MediaType
|
||||||
|
import org.springframework.http.ResponseEntity
|
||||||
|
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.PathVariable
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import org.springframework.web.server.ResponseStatusException
|
||||||
|
import java.nio.file.Files
|
||||||
|
import java.nio.file.Paths
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/properties/{propertyId}/inbound-emails")
|
||||||
|
class InboundEmails(
|
||||||
|
private val propertyAccess: PropertyAccess,
|
||||||
|
private val inboundEmailRepo: InboundEmailRepo
|
||||||
|
) {
|
||||||
|
|
||||||
|
@GetMapping("/{emailId}/file")
|
||||||
|
fun downloadEmailPdf(
|
||||||
|
@PathVariable propertyId: UUID,
|
||||||
|
@PathVariable emailId: UUID,
|
||||||
|
@AuthenticationPrincipal principal: MyPrincipal?
|
||||||
|
): ResponseEntity<FileSystemResource> {
|
||||||
|
requirePrincipal(principal)
|
||||||
|
propertyAccess.requireMember(propertyId, principal!!.userId)
|
||||||
|
propertyAccess.requireAnyRole(propertyId, principal.userId, Role.ADMIN, Role.MANAGER)
|
||||||
|
|
||||||
|
val email = inboundEmailRepo.findByIdAndPropertyId(emailId, propertyId)
|
||||||
|
?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Email not found")
|
||||||
|
val path = email.rawPdfPath ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Email PDF missing")
|
||||||
|
val file = Paths.get(path)
|
||||||
|
if (!Files.exists(file)) {
|
||||||
|
throw ResponseStatusException(HttpStatus.NOT_FOUND, "Email PDF missing")
|
||||||
|
}
|
||||||
|
val resource = FileSystemResource(file)
|
||||||
|
return ResponseEntity.ok()
|
||||||
|
.contentType(MediaType.APPLICATION_PDF)
|
||||||
|
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=\"email-${emailId}.pdf\"")
|
||||||
|
.contentLength(resource.contentLength())
|
||||||
|
.body(resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun requirePrincipal(principal: MyPrincipal?) {
|
||||||
|
if (principal == null) {
|
||||||
|
throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "Missing principal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,11 +41,13 @@ class Orgs(
|
|||||||
}
|
}
|
||||||
val org = Organization().apply {
|
val org = Organization().apply {
|
||||||
name = request.name
|
name = request.name
|
||||||
|
emailAliases = request.emailAliases?.toMutableSet() ?: mutableSetOf()
|
||||||
}
|
}
|
||||||
val saved = orgRepo.save(org)
|
val saved = orgRepo.save(org)
|
||||||
return OrgResponse(
|
return OrgResponse(
|
||||||
id = saved.id ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Org id missing"),
|
id = saved.id ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Org id missing"),
|
||||||
name = saved.name ?: ""
|
name = saved.name ?: "",
|
||||||
|
emailAliases = saved.emailAliases.toSet()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +65,8 @@ class Orgs(
|
|||||||
}
|
}
|
||||||
return OrgResponse(
|
return OrgResponse(
|
||||||
id = org.id ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Org id missing"),
|
id = org.id ?: throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Org id missing"),
|
||||||
name = org.name ?: ""
|
name = org.name ?: "",
|
||||||
|
emailAliases = org.emailAliases.toSet()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,8 @@ class Properties(
|
|||||||
timezone = request.timezone ?: "Asia/Kolkata",
|
timezone = request.timezone ?: "Asia/Kolkata",
|
||||||
currency = request.currency ?: "INR",
|
currency = request.currency ?: "INR",
|
||||||
active = request.active ?: true,
|
active = request.active ?: true,
|
||||||
otaAliases = request.otaAliases?.toMutableSet() ?: mutableSetOf()
|
otaAliases = request.otaAliases?.toMutableSet() ?: mutableSetOf(),
|
||||||
|
emailAddresses = request.emailAddresses?.toMutableSet() ?: mutableSetOf()
|
||||||
)
|
)
|
||||||
val saved = propertyRepo.save(property)
|
val saved = propertyRepo.save(property)
|
||||||
return saved.toResponse()
|
return saved.toResponse()
|
||||||
@@ -211,6 +212,9 @@ class Properties(
|
|||||||
if (request.otaAliases != null) {
|
if (request.otaAliases != null) {
|
||||||
property.otaAliases = request.otaAliases.toMutableSet()
|
property.otaAliases = request.otaAliases.toMutableSet()
|
||||||
}
|
}
|
||||||
|
if (request.emailAddresses != null) {
|
||||||
|
property.emailAddresses = request.emailAddresses.toMutableSet()
|
||||||
|
}
|
||||||
|
|
||||||
return propertyRepo.save(property).toResponse()
|
return propertyRepo.save(property).toResponse()
|
||||||
}
|
}
|
||||||
@@ -249,7 +253,8 @@ private fun Property.toResponse(): PropertyResponse {
|
|||||||
timezone = timezone,
|
timezone = timezone,
|
||||||
currency = currency,
|
currency = currency,
|
||||||
active = active,
|
active = active,
|
||||||
otaAliases = otaAliases.toSet()
|
otaAliases = otaAliases.toSet(),
|
||||||
|
emailAddresses = emailAddresses.toSet()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,14 @@ package com.android.trisolarisserver.controller.dto
|
|||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
data class OrgCreateRequest(
|
data class OrgCreateRequest(
|
||||||
val name: String
|
val name: String,
|
||||||
|
val emailAliases: Set<String>? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
data class OrgResponse(
|
data class OrgResponse(
|
||||||
val id: UUID,
|
val id: UUID,
|
||||||
val name: String
|
val name: String,
|
||||||
|
val emailAliases: Set<String>
|
||||||
)
|
)
|
||||||
|
|
||||||
data class PropertyCreateRequest(
|
data class PropertyCreateRequest(
|
||||||
@@ -18,7 +20,8 @@ data class PropertyCreateRequest(
|
|||||||
val timezone: String? = null,
|
val timezone: String? = null,
|
||||||
val currency: String? = null,
|
val currency: String? = null,
|
||||||
val active: Boolean? = null,
|
val active: Boolean? = null,
|
||||||
val otaAliases: Set<String>? = null
|
val otaAliases: Set<String>? = null,
|
||||||
|
val emailAddresses: Set<String>? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
data class PropertyUpdateRequest(
|
data class PropertyUpdateRequest(
|
||||||
@@ -28,7 +31,8 @@ data class PropertyUpdateRequest(
|
|||||||
val timezone: String? = null,
|
val timezone: String? = null,
|
||||||
val currency: String? = null,
|
val currency: String? = null,
|
||||||
val active: Boolean? = null,
|
val active: Boolean? = null,
|
||||||
val otaAliases: Set<String>? = null
|
val otaAliases: Set<String>? = null,
|
||||||
|
val emailAddresses: Set<String>? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
data class PropertyResponse(
|
data class PropertyResponse(
|
||||||
@@ -40,7 +44,8 @@ data class PropertyResponse(
|
|||||||
val timezone: String,
|
val timezone: String,
|
||||||
val currency: String,
|
val currency: String,
|
||||||
val active: Boolean,
|
val active: Boolean,
|
||||||
val otaAliases: Set<String>
|
val otaAliases: Set<String>,
|
||||||
|
val emailAddresses: Set<String>
|
||||||
)
|
)
|
||||||
|
|
||||||
data class UserResponse(
|
data class UserResponse(
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import java.util.UUID
|
|||||||
interface InboundEmailRepo : JpaRepository<InboundEmail, UUID> {
|
interface InboundEmailRepo : JpaRepository<InboundEmail, UUID> {
|
||||||
fun findByMessageId(messageId: String): InboundEmail?
|
fun findByMessageId(messageId: String): InboundEmail?
|
||||||
fun findByPropertyIdAndOtaBookingId(propertyId: UUID, otaBookingId: String): InboundEmail?
|
fun findByPropertyIdAndOtaBookingId(propertyId: UUID, otaBookingId: String): InboundEmail?
|
||||||
|
fun findByIdAndPropertyId(id: UUID, propertyId: UUID): InboundEmail?
|
||||||
fun existsByMessageId(messageId: String): Boolean
|
fun existsByMessageId(messageId: String): Boolean
|
||||||
fun existsByPropertyIdAndOtaBookingId(propertyId: UUID, otaBookingId: String): Boolean
|
fun existsByPropertyIdAndOtaBookingId(propertyId: UUID, otaBookingId: String): Boolean
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ class Booking(
|
|||||||
@Column(name = "expected_checkout_at", columnDefinition = "timestamptz")
|
@Column(name = "expected_checkout_at", columnDefinition = "timestamptz")
|
||||||
var expectedCheckoutAt: OffsetDateTime? = null,
|
var expectedCheckoutAt: OffsetDateTime? = null,
|
||||||
|
|
||||||
|
@Column(name = "email_audit_pdf_url")
|
||||||
|
var emailAuditPdfUrl: String? = null,
|
||||||
|
|
||||||
var notes: String? = null,
|
var notes: String? = null,
|
||||||
|
|
||||||
@ManyToOne(fetch = FetchType.LAZY)
|
@ManyToOne(fetch = FetchType.LAZY)
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ class InboundEmail(
|
|||||||
@Column(name = "raw_pdf_path")
|
@Column(name = "raw_pdf_path")
|
||||||
var rawPdfPath: String? = null,
|
var rawPdfPath: String? = null,
|
||||||
|
|
||||||
|
@Column(name = "raw_eml_path")
|
||||||
|
var rawEmlPath: String? = null,
|
||||||
|
|
||||||
@Column(name = "extracted_data", columnDefinition = "jsonb")
|
@Column(name = "extracted_data", columnDefinition = "jsonb")
|
||||||
var extractedData: String? = null,
|
var extractedData: String? = null,
|
||||||
|
|
||||||
|
|||||||
@@ -14,5 +14,14 @@ class Organization {
|
|||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
var name: String? = null
|
var name: String? = null
|
||||||
|
|
||||||
|
@ElementCollection(fetch = FetchType.EAGER)
|
||||||
|
@CollectionTable(
|
||||||
|
name = "org_email_alias",
|
||||||
|
joinColumns = [JoinColumn(name = "org_id")]
|
||||||
|
)
|
||||||
|
@Column(name = "email", nullable = false)
|
||||||
|
var emailAliases: MutableSet<String> = mutableSetOf()
|
||||||
|
|
||||||
@Column(name = "created_at", nullable = false, columnDefinition = "timestamptz")
|
@Column(name = "created_at", nullable = false, columnDefinition = "timestamptz")
|
||||||
val createdAt: OffsetDateTime = OffsetDateTime.now() }
|
val createdAt: OffsetDateTime = OffsetDateTime.now()
|
||||||
|
}
|
||||||
|
|||||||
@@ -37,6 +37,14 @@ class Property(
|
|||||||
@Column(name = "address_text")
|
@Column(name = "address_text")
|
||||||
var addressText: String? = null,
|
var addressText: String? = null,
|
||||||
|
|
||||||
|
@ElementCollection(fetch = FetchType.EAGER)
|
||||||
|
@CollectionTable(
|
||||||
|
name = "property_email_address",
|
||||||
|
joinColumns = [JoinColumn(name = "property_id")]
|
||||||
|
)
|
||||||
|
@Column(name = "email", nullable = false)
|
||||||
|
var emailAddresses: MutableSet<String> = mutableSetOf(),
|
||||||
|
|
||||||
@ElementCollection(fetch = FetchType.EAGER)
|
@ElementCollection(fetch = FetchType.EAGER)
|
||||||
@CollectionTable(
|
@CollectionTable(
|
||||||
name = "property_email_alias",
|
name = "property_email_alias",
|
||||||
|
|||||||
@@ -49,6 +49,8 @@ class EmailIngestionService(
|
|||||||
@Value("\${mail.imap.protocol:imaps}")
|
@Value("\${mail.imap.protocol:imaps}")
|
||||||
private val protocol: String
|
private val protocol: String
|
||||||
) {
|
) {
|
||||||
|
@Value("\${storage.emails.publicBaseUrl}")
|
||||||
|
private val publicBaseUrl: String = ""
|
||||||
private val running = AtomicBoolean(false)
|
private val running = AtomicBoolean(false)
|
||||||
@Value("\${mail.imap.enabled:false}")
|
@Value("\${mail.imap.enabled:false}")
|
||||||
private val enabled: Boolean = false
|
private val enabled: Boolean = false
|
||||||
@@ -86,9 +88,10 @@ class EmailIngestionService(
|
|||||||
|
|
||||||
val subject = message.subject
|
val subject = message.subject
|
||||||
val from = message.from?.firstOrNull()?.toString()
|
val from = message.from?.firstOrNull()?.toString()
|
||||||
|
val recipients = extractRecipients(message)
|
||||||
val receivedAt = message.receivedDate?.toInstant()?.atOffset(OffsetDateTime.now().offset)
|
val receivedAt = message.receivedDate?.toInstant()?.atOffset(OffsetDateTime.now().offset)
|
||||||
val body = extractText(message)
|
val body = extractText(message)
|
||||||
val property = matchProperty(subject, body)
|
val property = matchProperty(subject, body, recipients)
|
||||||
|
|
||||||
val inbound = InboundEmail(
|
val inbound = InboundEmail(
|
||||||
property = property,
|
property = property,
|
||||||
@@ -98,6 +101,10 @@ class EmailIngestionService(
|
|||||||
receivedAt = receivedAt
|
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)
|
inbound.rawPdfPath = emailStorage.storePdf(property?.id, messageId, subject, body)
|
||||||
inboundEmailRepo.save(inbound)
|
inboundEmailRepo.save(inbound)
|
||||||
|
|
||||||
@@ -153,7 +160,8 @@ class EmailIngestionService(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val guest = resolveGuest(property, extracted)
|
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.booking = booking
|
||||||
inbound.status = InboundEmailStatus.CREATED
|
inbound.status = InboundEmailStatus.CREATED
|
||||||
inbound.processedAt = OffsetDateTime.now()
|
inbound.processedAt = OffsetDateTime.now()
|
||||||
@@ -214,7 +222,8 @@ class EmailIngestionService(
|
|||||||
property: Property,
|
property: Property,
|
||||||
guest: Guest,
|
guest: Guest,
|
||||||
extracted: Map<String, String>,
|
extracted: Map<String, String>,
|
||||||
sourceBookingId: String
|
sourceBookingId: String,
|
||||||
|
emailAuditPdfUrl: String?
|
||||||
): Booking {
|
): Booking {
|
||||||
val zone = ZoneId.of(property.timezone)
|
val zone = ZoneId.of(property.timezone)
|
||||||
val checkin = parsedDate(extracted["checkinDate"], zone)
|
val checkin = parsedDate(extracted["checkinDate"], zone)
|
||||||
@@ -226,7 +235,8 @@ class EmailIngestionService(
|
|||||||
source = extracted["otaSource"]?.takeIf { !it.contains("NONE", true) } ?: "OTA",
|
source = extracted["otaSource"]?.takeIf { !it.contains("NONE", true) } ?: "OTA",
|
||||||
sourceBookingId = sourceBookingId,
|
sourceBookingId = sourceBookingId,
|
||||||
expectedCheckinAt = checkin,
|
expectedCheckinAt = checkin,
|
||||||
expectedCheckoutAt = checkout
|
expectedCheckoutAt = checkout,
|
||||||
|
emailAuditPdfUrl = emailAuditPdfUrl
|
||||||
)
|
)
|
||||||
return bookingRepo.save(booking)
|
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 haystack = "${subject ?: ""}\n$body".lowercase()
|
||||||
val properties = propertyRepo.findAll()
|
val properties = propertyRepo.findAll()
|
||||||
val matches = properties.filter { property ->
|
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>()
|
val aliases = mutableSetOf<String>()
|
||||||
aliases.add(property.name)
|
aliases.add(property.name)
|
||||||
aliases.add(property.code)
|
aliases.add(property.code)
|
||||||
@@ -267,6 +287,15 @@ class EmailIngestionService(
|
|||||||
return if (matches.size == 1) matches.first() else null
|
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 {
|
private fun extractText(message: Message): String {
|
||||||
return try {
|
return try {
|
||||||
val content = message.content
|
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 {
|
private fun extractFromMultipart(multipart: Multipart): String {
|
||||||
var text: String? = null
|
var text: String? = null
|
||||||
var html: String? = null
|
var html: String? = null
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ storage.documents.publicBaseUrl=https://api.hoteltrisolaris.in
|
|||||||
storage.documents.tokenSecret=change-me
|
storage.documents.tokenSecret=change-me
|
||||||
storage.documents.tokenTtlSeconds=300
|
storage.documents.tokenTtlSeconds=300
|
||||||
storage.emails.root=/home/androidlover5842/docs/emails
|
storage.emails.root=/home/androidlover5842/docs/emails
|
||||||
|
storage.emails.publicBaseUrl=https://api.hoteltrisolaris.in
|
||||||
mail.imap.host=localhost
|
mail.imap.host=localhost
|
||||||
mail.imap.port=993
|
mail.imap.port=993
|
||||||
mail.imap.protocol=imaps
|
mail.imap.protocol=imaps
|
||||||
|
|||||||
Reference in New Issue
Block a user