diff --git a/build.gradle b/build.gradle index 14a641c..9cfc458 100644 --- a/build.gradle +++ b/build.gradle @@ -39,7 +39,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' //email(javax) - implementation 'javax.mail:mail:1.4.7' + implementation 'com.sun.mail:javax.mail:1.6.2' //DB gradle runtimeOnly 'com.mysql:mysql-connector-j' diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 9f4197d..a80b22c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/com/Nunbody/domain/Mail/controller/GmailController.java b/src/main/java/com/Nunbody/domain/Mail/controller/GmailController.java new file mode 100644 index 0000000..b00b19d --- /dev/null +++ b/src/main/java/com/Nunbody/domain/Mail/controller/GmailController.java @@ -0,0 +1,192 @@ +package com.Nunbody.domain.Mail.controller; + +import com.Nunbody.domain.Mail.domain.MailBody; +import com.Nunbody.domain.Mail.domain.MailHeader; +import com.Nunbody.domain.Mail.domain.PlatformType; +import com.Nunbody.domain.Mail.repository.MailBodyRepository; +import com.Nunbody.domain.Mail.repository.MailRepository; +import com.Nunbody.domain.member.repository.MemberRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.mongodb.core.MongoTemplate; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.mail.*; +import javax.mail.internet.MimeMultipart; +import javax.mail.internet.MimeUtility; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@RequiredArgsConstructor +@RestController +public class GmailController { + private final MailRepository mailRepository; + private final MongoTemplate mongoTemplate; + private static Long userId = Long.valueOf(1); + private static PlatformType platformType = PlatformType.getEnumPlatformTypeFromStringPlatformType("GOOGLE"); + private static String platformHost ="imap.gmail.com"; + private final Pattern pattern = Pattern.compile("<(.*?)>"); + private final MemberRepository memberRepository; + private final MailBodyRepository mailBodyRepository; + public static List mailBodies = new ArrayList<>(); + @GetMapping("/gmail") + public ResponseEntity getGmailMessages() throws MessagingException, IOException { + +// MailList mailList = MailList.builder().memberId(userId).build(); + Properties props = new Properties(); + props.setProperty("mail.store.protocol", "imaps"); + props.setProperty("mail.imaps.host", "imap.gmail.com"); + props.setProperty("mail.imaps.port", "993"); + props.setProperty("mail.imaps.ssl.enable", "true"); + props.setProperty("mail.imap.ssl.protocols", "TLSv1.2"); + props.setProperty("mail.imap.ssl.ciphersuites", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"); + props.setProperty("mail.debug.ssl", "true"); + + Session session = Session.getInstance(props); + StringBuilder result = new StringBuilder(); + + + Store store = session.getStore("imaps"); + store.connect("imap.gmail.com", "qogustj50@gmail.com", "eqno csrt cmcm xnaa"); + + + Folder inbox = store.getFolder("INBOX"); + inbox.open(Folder.READ_ONLY); + + Message[] messages = inbox.getMessages(1, 9); // 최근 10개 메일 가져오기 + + MailHeader latestMail = mailRepository.findFirstByMemberIdAndPlatformTypeOrderByDateDesc(userId, platformType).orElse(null); + +// if (latestMail == null) { +// reset(messages, userId, platformType, platformHost); +// } else { + processNewMails(messages, userId, platformType, platformHost, latestMail, mailBodies); +// } + +// mongoTemplate.insertAll(mailBodies); +mailBodyRepository.saveAll(mailBodies); + + return ResponseEntity.ok(null); + } + @Transactional + public void reset(Message[] messages, Long userId, PlatformType platformType, String platformHost) + throws MessagingException, IOException { + List mailBodies = new ArrayList<>(); + int startIndex = Math.max(0, messages.length - 20); + + for (int i = startIndex; i < messages.length; i++) { + MailHeader mailHeader = createMailHeader(messages[i], userId, platformType); + mailRepository.save(mailHeader); + mailBodies.add(extractMailBody(platformHost, messages[i], mailHeader.getId())); + } + mongoTemplate.insertAll(mailBodies); + } + private void processNewMails(Message[] messages, Long userId, PlatformType platformType, String platformHost, + MailHeader latestMail, List mailBodies) throws MessagingException, IOException { +// LocalDateTime latestMailDate = LocalDateTime.parse(latestMail.getDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + ZoneId seoulZone = ZoneId.of("Asia/Seoul"); + + for (int i = messages.length - 1; i >= 0; i--) { + Message message = messages[i]; + LocalDateTime messageDate = LocalDateTime.ofInstant(message.getReceivedDate().toInstant(), seoulZone); + +// if (!messageDate.isAfter(latestMailDate)) { + MailHeader mailHeader = createMailHeader(message, userId, platformType); + mailRepository.save(mailHeader); + mailBodies.add(extractMailBody(platformHost, message, mailHeader.getId())); +// } else { +// break; // No need to check older messages +// } + } + } + private String handleMultipart(Multipart multipart) throws MessagingException, IOException { + StringBuilder result = new StringBuilder(); + int count = multipart.getCount(); + for (int i = 0; i < count; i++) { + BodyPart bodyPart = multipart.getBodyPart(i); + if (bodyPart.isMimeType("text/plain")) { + result.append(MimeUtility.decodeText((String) bodyPart.getContent())); + } else if (bodyPart.isMimeType("text/html")) { + result.append(MimeUtility.decodeText((String) bodyPart.getContent())); + } + } + return result.toString(); + } + public MailBody extractMailBody(String platformHost, Message message, Long mailId) { + try { +// log.debug("Extracting mail body for mailId: {} on platform: {}", mailId, platformHost); + + Object content = message.getContent(); + String decodedContent = ""; + + if (content instanceof Multipart) { + decodedContent = handleMultipart((Multipart) content); + } else if (content instanceof String) { + decodedContent = MimeUtility.decodeText((String) content); + } else if (content != null) { + decodedContent = content.toString(); + } + + return MailBody.builder() + .mailId(mailId) + .content(decodedContent.isEmpty() ? "No content available" : decodedContent) + .build(); + + } catch (MessagingException | IOException e) { +// log.error("Error extracting mail body for mailId: {} on platform: {}", mailId, platformHost, e); + return MailBody.builder() + .mailId(mailId) + .content("Error occurred while extracting mail content") + .build(); + } + } + private MailHeader createMailHeader(Message message, Long userId, PlatformType platformType) throws MessagingException { + Matcher matcher = pattern.matcher(message.getFrom()[0].toString()); + String fromPerson = matcher.find() ? matcher.group(1) : message.getFrom()[0].toString(); + + ZonedDateTime kstDateTime = ZonedDateTime.ofInstant(message.getReceivedDate().toInstant(), ZoneId.of("Asia/Seoul")); + String formattedDate = kstDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + + return MailHeader.builder() + .title(message.getSubject()) + .fromPerson(fromPerson) + .date(formattedDate) + .member(memberRepository.getReferenceById(userId)) + .platformType(platformType) + .build(); + } + private String getTextFromMessage(Message message) throws MessagingException, IOException { + if (message.isMimeType("text/plain")) { + return message.getContent().toString(); + } else if (message.isMimeType("multipart/*")) { + MimeMultipart mimeMultipart = (MimeMultipart) message.getContent(); + return getTextFromMimeMultipart(mimeMultipart); + } + return ""; + } + + private String getTextFromMimeMultipart(MimeMultipart mimeMultipart) throws MessagingException, IOException { + StringBuilder result = new StringBuilder(); + int count = mimeMultipart.getCount(); + for (int i = 0; i < count; i++) { + BodyPart bodyPart = mimeMultipart.getBodyPart(i); + if (bodyPart.isMimeType("text/plain")) { + result.append("\n").append(bodyPart.getContent()); + break; // 일반 텍스트 부분을 찾았으므로 중단 + } else if (bodyPart.getContent() instanceof MimeMultipart) { + result.append(getTextFromMimeMultipart((MimeMultipart) bodyPart.getContent())); + } + } + return result.toString(); + } +} diff --git a/src/main/java/com/Nunbody/domain/Windyflo/controller/WindyfloController.java b/src/main/java/com/Nunbody/domain/Windyflo/controller/WindyfloController.java new file mode 100644 index 0000000..f6f28f4 --- /dev/null +++ b/src/main/java/com/Nunbody/domain/Windyflo/controller/WindyfloController.java @@ -0,0 +1,21 @@ +package com.Nunbody.domain.Windyflo.controller; + +import com.Nunbody.domain.Windyflo.dto.req.WindyfloReq; +import com.Nunbody.domain.Windyflo.service.WindyfloService; +import com.Nunbody.external.ConversationQARes; +import com.Nunbody.global.common.SuccessResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/windyflo") +public class WindyfloController { + private final WindyfloService windyfloService; + @PostMapping + public ResponseEntity> createMail(@RequestBody WindyfloReq windyfloReq){ + ConversationQARes conversationQARes = windyfloService.createMail(windyfloReq); + return SuccessResponse.ok(conversationQARes); + } +} diff --git a/src/main/java/com/Nunbody/domain/Windyflo/dto/req/WindyfloReq.java b/src/main/java/com/Nunbody/domain/Windyflo/dto/req/WindyfloReq.java new file mode 100644 index 0000000..48a7de0 --- /dev/null +++ b/src/main/java/com/Nunbody/domain/Windyflo/dto/req/WindyfloReq.java @@ -0,0 +1,6 @@ +package com.Nunbody.domain.Windyflo.dto.req; + +public record WindyfloReq( + String prompt +) { +} diff --git a/src/main/java/com/Nunbody/domain/Windyflo/service/WindyfloService.java b/src/main/java/com/Nunbody/domain/Windyflo/service/WindyfloService.java new file mode 100644 index 0000000..13f71f5 --- /dev/null +++ b/src/main/java/com/Nunbody/domain/Windyflo/service/WindyfloService.java @@ -0,0 +1,20 @@ +package com.Nunbody.domain.Windyflo.service; + +import com.Nunbody.domain.Windyflo.dto.req.WindyfloReq; +import com.Nunbody.external.ConversationQARes; +import com.Nunbody.external.WindyfloMailClient; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class WindyfloService { + private final WindyfloMailClient windyfloMailClient; + public ConversationQARes createMail(WindyfloReq windyfloReq){ + ConversationQARes result = windyfloMailClient.findMailInVectorDB(windyfloReq.prompt()); + if(result.getText().equals("Hmm, I'm not sure.")){ + result = windyfloMailClient.createMail(windyfloReq.prompt()); + } + return result; + } +} diff --git a/src/main/java/com/Nunbody/domain/mail/controller/MailController.java b/src/main/java/com/Nunbody/domain/mail/controller/MailController.java index 2a81e5d..6c8311b 100644 --- a/src/main/java/com/Nunbody/domain/mail/controller/MailController.java +++ b/src/main/java/com/Nunbody/domain/mail/controller/MailController.java @@ -31,11 +31,7 @@ public class MailController { public ResponseEntity> getMail(@MemberId Long memberId, @RequestParam String type) { MailList mailList; - if (PlatformType.getEnumPlatformTypeFromStringPlatformType(type).equals(NAVER)) { - mailList = mailService.getNaverMail(memberId); - } else { - mailList = mailService.getGoogleMail(memberId); - } + mailList = mailService.getMail(memberId,type); return SuccessResponse.ok(mailList); } diff --git a/src/main/java/com/Nunbody/domain/mail/service/MailService.java b/src/main/java/com/Nunbody/domain/mail/service/MailService.java index 6e603aa..bbd624f 100644 --- a/src/main/java/com/Nunbody/domain/mail/service/MailService.java +++ b/src/main/java/com/Nunbody/domain/mail/service/MailService.java @@ -20,11 +20,13 @@ import javax.mail.*; import javax.mail.internet.MimeMultipart; +import javax.mail.internet.MimeUtility; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.time.Instant; +import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -34,9 +36,10 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static com.Nunbody.domain.Mail.domain.PlatformType.*; + @Slf4j @RequiredArgsConstructor -@Transactional @Service public class MailService { private final MongoTemplate mongoTemplate; @@ -44,255 +47,165 @@ public class MailService { private final MailRepository mailRepository; private final MemberRepository memberRepository; private final Pattern pattern = Pattern.compile("<(.*?)>"); - private Matcher matcher; - - - public MailList getNaverMail(Long memberId) { - - MailList naverMail = MailList.builder() - .memberId(memberId) - .build(); - - Member member = memberRepository.findById(memberId).orElse(null); - String id = member.getNaverId(); - String decode = EncoderDecoder.decodeFromBase64(member.getNaverPassword()); - - /** naver mail */ - final String naverHost = "imap.naver.com"; - - final String naverId = id; - - final String naverPassword = decode; - - naverMail = mailSetting(memberId, naverHost, naverId, naverPassword, naverMail, PlatformType.NAVER); - return naverMail; - - } - - public MailList getGoogleMail(Long memberId) { - - MailList googleMail = MailList.builder() - .memberId(memberId) - .build(); - Member member = memberRepository.findById(memberId).orElse(null); - String id = member.getGmailId(); - String decode = EncoderDecoder.decodeFromBase64(member.getGmailPassword()); - - /** google mail */ - final String googleHost = "imap.gmail.com"; - final String googleId = id; - - final String googlePassword = decode; - - googleMail = mailSetting(memberId, googleHost, googleId, googlePassword, googleMail, PlatformType.GOOGLE); - return googleMail; + public MailList getMail(Long memberId, String type) {; + PlatformType platformType =getEnumPlatformTypeFromStringPlatformType(type); + Member member = memberRepository.findById(memberId) + .orElseThrow(() -> new RuntimeException("Member not found")); + + String id, password, host; + switch (platformType) { + case NAVER: + id = member.getNaverId(); + password = EncoderDecoder.decodeFromBase64(member.getNaverPassword()); + host = "imap.naver.com"; + break; + case GOOGLE: + id = member.getGmailId(); + password = EncoderDecoder.decodeFromBase64(member.getGmailPassword()); + host = "imap.gmail.com"; + break; + default: + throw new IllegalArgumentException("Unsupported platform type"); + } + return mailSetting(memberId, host, id, password, platformType); } - public MailList mailSetting(Long userId, String platformHost, String platformId, String platformPassword, MailList mailList, - PlatformType platformType) { + @Transactional + public MailList mailSetting(Long userId, String platformHost, String platformId, String platformPassword, PlatformType platformType) { List mailBodies = new ArrayList<>(); + MailList mailList = MailList.builder().memberId(userId).build(); + + try (Store store = connectToMailStore(platformHost, platformId, platformPassword); + Folder folder = store.getFolder("inbox")) { - try { - Properties prop = new Properties(); - prop.put("mail.imap.host", platformHost); - prop.put("mail.imap.port", 993); - prop.put("mail.imap.ssl.enable", "true"); - prop.put("mail.imap.ssl.protocols", "TLSv1.2"); - prop.put("mail.store.protocol", "imap"); - - // Session 클래스 인스턴스 생성 - Session session = Session.getInstance(prop); - - // Store 클래스 - Store store = session.getStore("imap"); - store.connect(platformHost, platformId, platformPassword); - - // 받은 편지함 - Folder folder = store.getFolder("inbox"); folder.open(Folder.READ_ONLY); Message[] messages = folder.getMessages(); - MailHeader mailHeaderData; MailHeader latestMail = mailRepository.findFirstByMemberIdAndPlatformTypeOrderByDateDesc(userId, platformType).orElse(null); if (latestMail == null) { reset(messages, userId, platformType, platformHost); } else { - for (int i = messages.length - 2; i < messages.length; i++) { - matcher = pattern.matcher(messages[i].getFrom()[0].toString()); - Instant receivedInstant = messages[i].getReceivedDate().toInstant(); - ZonedDateTime kstDateTime = ZonedDateTime.ofInstant(receivedInstant, ZoneId.of("Asia/Seoul")); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - String formattedDate = kstDateTime.format(formatter); - int compare = formattedDate.compareTo(latestMail.getDate()); - if (compare > 0) { - if (matcher.find()) { - String fromPerson = matcher.group(1); - mailHeaderData = MailHeader.builder() - .title(messages[i].getSubject()) - .fromPerson(fromPerson) - .date(formattedDate) - .member(memberRepository.findById(userId).get()) - .platformType(platformType) - .build(); - } else { - mailHeaderData = MailHeader.builder() - .title(messages[i].getSubject()) - .fromPerson(messages[i].getFrom()[0].toString()) - .date(formattedDate) - .member(memberRepository.findById(userId).get()) - .platformType(platformType) - .build(); - } - mailRepository.save(mailHeaderData); - //mailList.addData(mailHeaderData); - - Long mailId = mailHeaderData.getId(); - mailBodies.add(extractMailBody(platformHost, messages[i], mailId)); - } - mongoTemplate.insertAll(mailBodies); - } + processNewMails(messages, userId, platformType, platformHost, latestMail, mailBodies); } - // 폴더와 스토어 닫기 - folder.close(false); - store.close(); + mongoTemplate.insertAll(mailBodies); } catch (Exception e) { - e.printStackTrace(); + log.error("Error in mailSetting", e); + throw new RuntimeException("Failed to process emails", e); } return mailList; } - public void reset(Message[] messages, Long userId, PlatformType platformType, String platformHost) - throws MessagingException, IOException { - MailHeader mailHeaderData; - List mailBodies = new ArrayList<>(); - - for (int i = messages.length-20; i < messages.length; i++) { - - matcher = pattern.matcher(messages[i].getFrom()[0].toString()); - Instant receivedInstant = messages[i].getReceivedDate().toInstant(); - ZonedDateTime kstDateTime = ZonedDateTime.ofInstant(receivedInstant, ZoneId.of("Asia/Seoul")); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - String formattedDate = kstDateTime.format(formatter); - if (matcher.find()) { - String fromPerson = matcher.group(1); - mailHeaderData = MailHeader.builder() - .title(messages[i].getSubject()) - .fromPerson(fromPerson) - .date(formattedDate) - .member(memberRepository.findById(userId).get()) - .platformType(platformType) - .build(); - } else { - mailHeaderData = MailHeader.builder() - .title(messages[i].getSubject()) - .fromPerson(messages[i].getFrom()[0].toString()) - .date(formattedDate) - .member(memberRepository.findById(userId).get()) - .platformType(platformType) - .build(); - } - mailRepository.save(mailHeaderData); - - - Long mailId = mailHeaderData.getId(); - mailBodies.add(extractMailBody(platformHost, messages[i], mailId)); - } - mongoTemplate.insertAll(mailBodies); + private Store connectToMailStore(String host, String username, String password) throws MessagingException { + Properties props = new Properties(); + props.setProperty("mail.store.protocol", "imaps"); + props.setProperty("mail.imaps.host", host); + props.setProperty("mail.imaps.port", "993"); + props.setProperty("mail.imaps.ssl.enable", "true"); + props.setProperty("mail.imap.ssl.protocols", "TLSv1.2"); + props.setProperty("mail.imap.ssl.ciphersuites", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"); + + Session session = Session.getInstance(props); + Store store = session.getStore("imaps"); + store.connect(host, username, password); + return store; } - public MailBody extractMailBody(String platformhost, Message messages, Long mailId) throws MessagingException, IOException { + private void processNewMails(Message[] messages, Long userId, PlatformType platformType, String platformHost, + MailHeader latestMail, List mailBodies) throws MessagingException, IOException { + LocalDateTime latestMailDate = LocalDateTime.parse(latestMail.getDate(), DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); + ZoneId seoulZone = ZoneId.of("Asia/Seoul"); - Object content = messages.getContent(); - byte[] contentBytes = null; - if (platformhost.equals("imap.naver.com")) { - if (content instanceof Multipart) { - List multipartContentBytes = parseMultipart(messages, (Multipart) content); - // 여러 BodyPart의 결과를 합쳐서 contentBytes로 설정 - contentBytes = combineMultipartContent(multipartContentBytes); - } else if (content instanceof Part) { - contentBytes = parseBody(messages, (BodyPart) content); - } - } - if (platformhost.equals("imap.google.com")) { - if (content instanceof Multipart) { - List multipartContentBytes = parseMultipart(messages, (Multipart) content); - // 여러 BodyPart의 결과를 합쳐서 contentBytes로 설정 - contentBytes = null; - } else if (content instanceof Part) { - contentBytes = parseBody(messages, (BodyPart) content); + for (int i = messages.length - 1; i >= 0; i--) { + Message message = messages[i]; + LocalDateTime messageDate = LocalDateTime.ofInstant(message.getReceivedDate().toInstant(), seoulZone); + + if (messageDate.isAfter(latestMailDate)) { + MailHeader mailHeader = createMailHeader(message, userId, platformType); + mailRepository.save(mailHeader); + mailBodies.add(extractMailBody(platformHost, message, mailHeader.getId())); + } else { + break; // No need to check older messages } } - MailBody mailBody = MailBody.builder() - .mailId(mailId) - .content(contentBytes != null ? new String(contentBytes, StandardCharsets.UTF_8) : " ") - .build(); - - return mailBody; } - private static List parseMultipart(Message message, Multipart mp) throws IOException, MessagingException { - MimeMultipart mm = (MimeMultipart) mp; + private MailHeader createMailHeader(Message message, Long userId, PlatformType platformType) throws MessagingException { + Matcher matcher = pattern.matcher(message.getFrom()[0].toString()); + String fromPerson = matcher.find() ? matcher.group(1) : message.getFrom()[0].toString(); - List multipartContentBytes = new ArrayList<>(); + ZonedDateTime kstDateTime = ZonedDateTime.ofInstant(message.getReceivedDate().toInstant(), ZoneId.of("Asia/Seoul")); + String formattedDate = kstDateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); - int bodyCount = mm.getCount(); - for (int i = 0; i < bodyCount; i++) { - BodyPart bodyPart = mm.getBodyPart(i); - Object partContent = bodyPart.getContent(); - - byte[] partContentBytes = parseBody(message, bodyPart); - multipartContentBytes.add(partContentBytes); - } - - return multipartContentBytes; + return MailHeader.builder() + .title(message.getSubject()) + .fromPerson(fromPerson) + .date(formattedDate) + .member(memberRepository.getReferenceById(userId)) + .platformType(platformType) + .build(); } - private static byte[] combineMultipartContent(List multipartContentBytes) { - // 여러 BodyPart의 결과를 합치는 로직을 구현 - // 예를 들어, 각 부분을 줄바꿈으로 구분하여 이어붙일 수 있습니다. - StringBuilder combinedContent = new StringBuilder(); - for (byte[] partContentBytes : multipartContentBytes) { - if (partContentBytes != null) { - combinedContent.append(new String(partContentBytes, StandardCharsets.UTF_8)); - combinedContent.append(System.lineSeparator()); // 각 부분을 줄바꿈으로 구분 - } - } + @Transactional + public void reset(Message[] messages, Long userId, PlatformType platformType, String platformHost) + throws MessagingException, IOException { + List mailBodies = new ArrayList<>(); + int startIndex = Math.max(0, messages.length - 20); - // 최종 결과를 byte 배열로 변환 - return combinedContent.toString().getBytes(StandardCharsets.UTF_8); + for (int i = startIndex; i < messages.length; i++) { + MailHeader mailHeader = createMailHeader(messages[i], userId, platformType); + mailRepository.save(mailHeader); + mailBodies.add(extractMailBody(platformHost, messages[i], mailHeader.getId())); + } + mongoTemplate.insertAll(mailBodies); } - private static byte[] parseBody(Message message, BodyPart bp) throws IOException, MessagingException { - Object obj = bp.getContent(); - byte[] contentBytes = null; - - if (obj instanceof BASE64DecoderStream) { - // 처리할 첨부 파일 - BASE64DecoderStream newObj = (BASE64DecoderStream) obj; - - contentBytes = readInputStream(newObj); + public MailBody extractMailBody(String platformHost, Message message, Long mailId) { + try { + log.debug("Extracting mail body for mailId: {} on platform: {}", mailId, platformHost); - newObj.close(); - } else if (obj instanceof String) { - // 텍스트 형식인 경우 - String contentType = message.getContentType().toLowerCase(); + Object content = message.getContent(); + String decodedContent = ""; - contentBytes = ((String) obj).getBytes(StandardCharsets.UTF_8); + if (content instanceof Multipart) { + decodedContent = handleMultipart((Multipart) content); + } else if (content instanceof String) { + decodedContent = MimeUtility.decodeText((String) content); + } else if (content != null) { + decodedContent = content.toString(); + } - } else if (obj instanceof Multipart) { - parseMultipart(message, (Multipart) obj); - } else { - // 기타 형식인 경우 - contentBytes = bp.getContentType().getBytes(StandardCharsets.UTF_8); + return MailBody.builder() + .mailId(mailId) + .content(decodedContent.isEmpty() ? "No content available" : decodedContent) + .build(); + + } catch (MessagingException | IOException e) { + log.error("Error extracting mail body for mailId: {} on platform: {}", mailId, platformHost, e); + return MailBody.builder() + .mailId(mailId) + .content("Error occurred while extracting mail content") + .build(); } + } - return contentBytes; + private String handleMultipart(Multipart multipart) throws MessagingException, IOException { + StringBuilder result = new StringBuilder(); + int count = multipart.getCount(); + for (int i = 0; i < count; i++) { + BodyPart bodyPart = multipart.getBodyPart(i); + if (bodyPart.isMimeType("text/plain")) { + result.append(MimeUtility.decodeText((String) bodyPart.getContent())); + } else if (bodyPart.isMimeType("text/html")) { + result.append(MimeUtility.decodeText((String) bodyPart.getContent())); + } + } + return result.toString(); } private static byte[] readInputStream(InputStream inputStream) throws IOException { @@ -306,18 +219,12 @@ private static byte[] readInputStream(InputStream inputStream) throws IOExceptio return buffer.toByteArray(); } + @Transactional(readOnly = true) public MailDetailResponseDto getMailBody(Long mailId) { MailBody mailBody = mailBodyRepository.findByMailId(mailId); - MailResponseDto mailResponseDto = createMailDetailResponseDto(mailId); + MailHeader mailHeader = mailRepository.findById(mailId) + .orElseThrow(() -> new RuntimeException("Mail header not found")); + MailResponseDto mailResponseDto = MailResponseDto.of(mailHeader); return MailDetailResponseDto.of(mailResponseDto, mailBody.getContent()); } - - private MailResponseDto createMailDetailResponseDto(Long mailId) { - MailHeader mailHeader = getMailHeader(mailId); - return MailResponseDto.of(mailHeader); - } - - private MailHeader getMailHeader(Long mailId) { - return mailRepository.findById(mailId).orElse(null); - } } \ No newline at end of file diff --git a/src/main/java/com/Nunbody/external/ConversationQARes.java b/src/main/java/com/Nunbody/external/ConversationQARes.java new file mode 100644 index 0000000..2b092f6 --- /dev/null +++ b/src/main/java/com/Nunbody/external/ConversationQARes.java @@ -0,0 +1,11 @@ +package com.Nunbody.external; + +import lombok.Getter; + +@Getter +public class ConversationQARes { + private String text; + private String question; + private String chatId; + private String chatMessageId; +} diff --git a/src/main/java/com/Nunbody/external/QuestionRequest.java b/src/main/java/com/Nunbody/external/QuestionRequest.java new file mode 100644 index 0000000..0406c95 --- /dev/null +++ b/src/main/java/com/Nunbody/external/QuestionRequest.java @@ -0,0 +1,14 @@ +package com.Nunbody.external; + +import lombok.Getter; + +@Getter +public class QuestionRequest { + private String question; + + public QuestionRequest(String question) { + this.question = question; + } + + // getter and setter +} diff --git a/src/main/java/com/Nunbody/external/WindyfloMailClient.java b/src/main/java/com/Nunbody/external/WindyfloMailClient.java new file mode 100644 index 0000000..cd358c9 --- /dev/null +++ b/src/main/java/com/Nunbody/external/WindyfloMailClient.java @@ -0,0 +1,37 @@ +package com.Nunbody.external; + +import com.Nunbody.global.config.RestTemplateConfig; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Service +@RequiredArgsConstructor +public class WindyfloMailClient { + private final String FIND_MAIL_URL = "https://windyflo.com/api/v1/prediction/a7800245-8f8e-4933-bab2-7776c22a5da8"; + private final String CREATE_MAIL_URL = "https://windyflo.com/api/v1/prediction/2b8a91d7-cdfe-434b-9672-d9bb3cb6faa1"; + private final RestTemplate restTemplate; + + public ConversationQARes findMailInVectorDB(String question) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + QuestionRequest requestBody = new QuestionRequest(question); + HttpEntity request = new HttpEntity<>(requestBody, headers); + + return restTemplate.postForObject(FIND_MAIL_URL, request, ConversationQARes.class); + } + public ConversationQARes createMail(String question) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + QuestionRequest requestBody = new QuestionRequest(question); + HttpEntity request = new HttpEntity<>(requestBody, headers); + + return restTemplate.postForObject(CREATE_MAIL_URL, request, ConversationQARes.class); + } +} diff --git a/src/main/java/com/Nunbody/global/config/SpringSecurityConfig.java b/src/main/java/com/Nunbody/global/config/SpringSecurityConfig.java index d5366f8..1cc3863 100644 --- a/src/main/java/com/Nunbody/global/config/SpringSecurityConfig.java +++ b/src/main/java/com/Nunbody/global/config/SpringSecurityConfig.java @@ -24,7 +24,7 @@ public class SpringSecurityConfig { private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; private final JwtTokenProvider tokenProvider; private final CorsConfig corsConfig; - private static final String[] whiteList = {"/api/member/signup", "/api/member/signin", "/api/member/validate", "/api/mail/validate"}; + private static final String[] whiteList = {"/api/member/signup", "/api/member/signin", "/api/member/validate", "/api/mail/validate","/gmail"}; @Bean public WebSecurityCustomizer webSecurityCustomizer() {