From 91ae1cafc14b3d7c0e75ac4081a7f3ffa5b120ed Mon Sep 17 00:00:00 2001 From: qogustj Date: Thu, 4 Jul 2024 10:33:40 +0900 Subject: [PATCH 01/13] =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/Nunbody/domain/mail/service/MailManageService.java | 5 ++++- src/main/java/com/Nunbody/global/error/ErrorCode.java | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/Nunbody/domain/mail/service/MailManageService.java b/src/main/java/com/Nunbody/domain/mail/service/MailManageService.java index d399779..4ef54de 100644 --- a/src/main/java/com/Nunbody/domain/mail/service/MailManageService.java +++ b/src/main/java/com/Nunbody/domain/mail/service/MailManageService.java @@ -68,7 +68,7 @@ private String validateImap(ValidateRequestDto validateRequestDto) throws Messag Store store = createStore(prop); store.connect(host, id, password); return "성공"; - } catch (AuthenticationFailedException e) { + } catch (Exception e) { String errorMessage = e.getMessage(); if (errorMessage.contains("Please check your username, password")) { // Handle username or password error @@ -86,6 +86,9 @@ private String validateImap(ValidateRequestDto validateRequestDto) throws Messag if (errorMessage.contains("IMAP access")) { throw new InvalidValueException(IMAP_ERROR); } + if (errorMessage.contains("Couldn't connect to host")) { + throw new InvalidValueException(IMAP_ERROR); + } } return null; } diff --git a/src/main/java/com/Nunbody/global/error/ErrorCode.java b/src/main/java/com/Nunbody/global/error/ErrorCode.java index 5d0a65c..74da579 100644 --- a/src/main/java/com/Nunbody/global/error/ErrorCode.java +++ b/src/main/java/com/Nunbody/global/error/ErrorCode.java @@ -9,7 +9,7 @@ @RequiredArgsConstructor(access = AccessLevel.PRIVATE) public enum ErrorCode { BAD_REQUEST(HttpStatus.BAD_REQUEST, "잘못된 요청입니다."), - INTERNAL_SERVER_ERROR(HttpStatus.BAD_REQUEST, "서버 내부에서 문제가 발생했습니다."), + INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버 내부에서 문제가 발생했습니다."), NOT_FOUND(HttpStatus.BAD_REQUEST, "해당 로그인 정보는 존재하지 않습니다."), UNAUTHORIZED(HttpStatus.BAD_REQUEST, "권한이 없습니다."), // Success From b2046463106f404d0acbfee3f025e486ba444f0a Mon Sep 17 00:00:00 2001 From: qogustj Date: Tue, 9 Jul 2024 14:38:47 +0900 Subject: [PATCH 02/13] =?UTF-8?q?[chore]:=20windyflo=20url=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/Windyflo/service/WindyfloService.java | 14 +++++++------- .../com/Nunbody/external/WindyfloMailClient.java | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/Nunbody/domain/Windyflo/service/WindyfloService.java b/src/main/java/com/Nunbody/domain/Windyflo/service/WindyfloService.java index ce4f3b4..e1aec48 100644 --- a/src/main/java/com/Nunbody/domain/Windyflo/service/WindyfloService.java +++ b/src/main/java/com/Nunbody/domain/Windyflo/service/WindyfloService.java @@ -23,14 +23,14 @@ public class WindyfloService { public EmailResDto createMail(WindyfloReq windyfloReq) throws IOException { EmailResDto emailResDto = new EmailResDto("", "", ""); while (emailResDto.template().isEmpty() || emailResDto.header().isEmpty() || emailResDto.body().isEmpty()) { -// ConversationQARes result = windyfloMailClient.findMailInVectorDB(windyfloReq.prompt()); -//// emailResDto = extractEmailTemplate(result.getText()); -// if (result.getText().equals("Hmm, I'm not sure")) { + ConversationQARes result = windyfloMailClient.findMailInVectorDB(windyfloReq.prompt()); +// emailResDto = extractEmailTemplate(result.getText()); + if (result.getText().equals("Hmm, I'm not sure")) { emailResDto = extractEmailTemplate(windyfloMailClient.createMail(windyfloReq.prompt())); -//// } -// else -// emailResDto = extractEmailTemplate(result.getText()); -//// System.out.println(result.getText()); + } + else + emailResDto = extractEmailTemplate(result.getText()); + } return emailResDto; } diff --git a/src/main/java/com/Nunbody/external/WindyfloMailClient.java b/src/main/java/com/Nunbody/external/WindyfloMailClient.java index baf87b7..18edc8b 100644 --- a/src/main/java/com/Nunbody/external/WindyfloMailClient.java +++ b/src/main/java/com/Nunbody/external/WindyfloMailClient.java @@ -13,8 +13,8 @@ @Service @RequiredArgsConstructor public class WindyfloMailClient { - private final String FIND_MAIL_URL = "https://windyflo.com/api/v1/prediction/2eb15033-681a-410b-8577-2b69d8e309d4"; - private final String CREATE_MAIL_URL = "https://windyflo.com/api/v1/prediction/f66b8ade-2393-4a1a-ae48-a23c258b460a"; + private final String FIND_MAIL_URL = "https://windyflo.com/api/v1/prediction/05d769b1-0a34-49c5-83d3-6b92c1d650f8"; + private final String CREATE_MAIL_URL = "https://windyflo.com/api/v1/prediction/fd835f04-0889-4f19-8d97-508901854a76"; private final RestTemplate restTemplate; public ConversationQARes findMailInVectorDB(String question) { From 14ca36ef80f800d7611306ec418d75562db637b8 Mon Sep 17 00:00:00 2001 From: qogustj Date: Tue, 9 Jul 2024 14:53:23 +0900 Subject: [PATCH 03/13] =?UTF-8?q?[chore]:=20windyflo=20url=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/Windyflo/service/WindyfloService.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/Nunbody/domain/Windyflo/service/WindyfloService.java b/src/main/java/com/Nunbody/domain/Windyflo/service/WindyfloService.java index e1aec48..440e47f 100644 --- a/src/main/java/com/Nunbody/domain/Windyflo/service/WindyfloService.java +++ b/src/main/java/com/Nunbody/domain/Windyflo/service/WindyfloService.java @@ -23,13 +23,13 @@ public class WindyfloService { public EmailResDto createMail(WindyfloReq windyfloReq) throws IOException { EmailResDto emailResDto = new EmailResDto("", "", ""); while (emailResDto.template().isEmpty() || emailResDto.header().isEmpty() || emailResDto.body().isEmpty()) { - ConversationQARes result = windyfloMailClient.findMailInVectorDB(windyfloReq.prompt()); +// ConversationQARes result = windyfloMailClient.findMailInVectorDB(windyfloReq.prompt()); // emailResDto = extractEmailTemplate(result.getText()); - if (result.getText().equals("Hmm, I'm not sure")) { +// if (result.getText().equals("Hmm, I'm not sure")) { emailResDto = extractEmailTemplate(windyfloMailClient.createMail(windyfloReq.prompt())); - } - else - emailResDto = extractEmailTemplate(result.getText()); +// } +// else +// emailResDto = extractEmailTemplate(result.getText()); } return emailResDto; From 7e1a809341952b262bf0b212c391803f18e92ca0 Mon Sep 17 00:00:00 2001 From: qogustj Date: Tue, 9 Jul 2024 16:11:14 +0900 Subject: [PATCH 04/13] =?UTF-8?q?[feat]=20=EB=A9=A4=EB=B2=84=20=EB=A9=94?= =?UTF-8?q?=EC=9D=BC=20id=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/member/controller/MemberController.java | 14 ++++++++++---- .../domain/member/dto/res/MemberMailIdResDto.java | 10 ++++++++++ .../domain/member/service/MemberService.java | 6 ++++++ 3 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/Nunbody/domain/member/dto/res/MemberMailIdResDto.java diff --git a/src/main/java/com/Nunbody/domain/member/controller/MemberController.java b/src/main/java/com/Nunbody/domain/member/controller/MemberController.java index e6a980d..7a53c70 100644 --- a/src/main/java/com/Nunbody/domain/member/controller/MemberController.java +++ b/src/main/java/com/Nunbody/domain/member/controller/MemberController.java @@ -5,14 +5,15 @@ import com.Nunbody.domain.member.dto.SignInRequestDto; import com.Nunbody.domain.member.dto.SignInResponseDto; import com.Nunbody.domain.member.dto.ValidateRequestDto; +import com.Nunbody.domain.member.dto.res.MemberMailIdResDto; import com.Nunbody.domain.member.service.MemberService; import com.Nunbody.global.common.SuccessResponse; +import com.Nunbody.global.config.auth.MemberId; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @RestController @RequestMapping("/api/member") @@ -21,6 +22,11 @@ public class MemberController { private final MemberService memberService; + @GetMapping("/mailId") + public ResponseEntity> getMemberMailId(@MemberId Long memberId) { + MemberMailIdResDto memberMailId = memberService.getMemberMailId(memberId); + return SuccessResponse.ok(memberMailId); + } @PostMapping("/signup") public ResponseEntity> create(@RequestBody MemberRegisterRequestDto dto) { diff --git a/src/main/java/com/Nunbody/domain/member/dto/res/MemberMailIdResDto.java b/src/main/java/com/Nunbody/domain/member/dto/res/MemberMailIdResDto.java new file mode 100644 index 0000000..6c4c14c --- /dev/null +++ b/src/main/java/com/Nunbody/domain/member/dto/res/MemberMailIdResDto.java @@ -0,0 +1,10 @@ +package com.Nunbody.domain.member.dto.res; + +public record MemberMailIdResDto( + String GOOGLE, + String NAVER +) { + public static MemberMailIdResDto of(String GOOGLE, String NAVER){ + return new MemberMailIdResDto(GOOGLE, NAVER); + } +} diff --git a/src/main/java/com/Nunbody/domain/member/service/MemberService.java b/src/main/java/com/Nunbody/domain/member/service/MemberService.java index 2a0e476..7140c08 100644 --- a/src/main/java/com/Nunbody/domain/member/service/MemberService.java +++ b/src/main/java/com/Nunbody/domain/member/service/MemberService.java @@ -4,6 +4,7 @@ import com.Nunbody.domain.member.domain.Member; import com.Nunbody.domain.member.dto.MemberRegisterRequestDto; import com.Nunbody.domain.member.dto.SignInResponseDto; +import com.Nunbody.domain.member.dto.res.MemberMailIdResDto; import com.Nunbody.domain.member.repository.KeywordRepository; import com.Nunbody.domain.member.repository.MemberRepository; import com.Nunbody.global.common.EncoderDecoder; @@ -35,8 +36,13 @@ public class MemberService { private final JwtTokenProvider jwtTokenProvider; private final PasswordEncoder passwordEncoder; private final OAuthService oAuthService; + private final MemberReader memberReader; + public MemberMailIdResDto getMemberMailId(Long memberId){ + Member member = memberReader.getMemberById(memberId); + return MemberMailIdResDto.of(member.getNaverId(), member.getGmailId()); + } public void register(MemberRegisterRequestDto resource) { Member member; From e0ecd881165b10e6fa62448715c12d32672df79e Mon Sep 17 00:00:00 2001 From: qogustj Date: Tue, 9 Jul 2024 16:19:24 +0900 Subject: [PATCH 05/13] =?UTF-8?q?[fix]=20=EB=A9=A4=EB=B2=84=20=EB=A9=94?= =?UTF-8?q?=EC=9D=BC=20id=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/Nunbody/domain/member/service/MemberService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/Nunbody/domain/member/service/MemberService.java b/src/main/java/com/Nunbody/domain/member/service/MemberService.java index 7140c08..0cda54f 100644 --- a/src/main/java/com/Nunbody/domain/member/service/MemberService.java +++ b/src/main/java/com/Nunbody/domain/member/service/MemberService.java @@ -41,7 +41,7 @@ public class MemberService { public MemberMailIdResDto getMemberMailId(Long memberId){ Member member = memberReader.getMemberById(memberId); - return MemberMailIdResDto.of(member.getNaverId(), member.getGmailId()); + return MemberMailIdResDto.of(member.getGmailId(), member.getNaverId()); } public void register(MemberRegisterRequestDto resource) { From 084af85e47eab895b37abb730aded0999b112e82 Mon Sep 17 00:00:00 2001 From: qogustj Date: Thu, 11 Jul 2024 16:41:22 +0900 Subject: [PATCH 06/13] =?UTF-8?q?[chore]:=20=EC=98=A4=ED=83=80=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/Nunbody/external/WindyfloMailClient.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/Nunbody/external/WindyfloMailClient.java b/src/main/java/com/Nunbody/external/WindyfloMailClient.java index 18edc8b..f83eaa4 100644 --- a/src/main/java/com/Nunbody/external/WindyfloMailClient.java +++ b/src/main/java/com/Nunbody/external/WindyfloMailClient.java @@ -14,7 +14,7 @@ @RequiredArgsConstructor public class WindyfloMailClient { private final String FIND_MAIL_URL = "https://windyflo.com/api/v1/prediction/05d769b1-0a34-49c5-83d3-6b92c1d650f8"; - private final String CREATE_MAIL_URL = "https://windyflo.com/api/v1/prediction/fd835f04-0889-4f19-8d97-508901854a76"; + private final String CREATE_MAIL_URL = "https://windyflo.com/api/v1/prediction/12434af5-c955-4d7e-a613-1d28b0b4f169"; private final RestTemplate restTemplate; public ConversationQARes findMailInVectorDB(String question) { From 4038a35e68cd36f274774b96dbb24d29c554bc16 Mon Sep 17 00:00:00 2001 From: qogustj Date: Wed, 31 Jul 2024 11:01:04 +0900 Subject: [PATCH 07/13] url --- build.gradle | 2 + .../java/com/Nunbody/NunbodyApplication.java | 2 + .../Kakao/controller/KakaoController.java | 31 +++ .../domain/Kakao/service/KakaoService.java | 210 ++++++++++++++++++ .../Kakao/service/MailScheduleService.java | 128 +++++++++++ .../domain/Kakao/service/MessageService.java | 32 +++ .../Kakao/service/dto/DefaultMessageDto.java | 13 ++ .../domain/Kakao/service/dto/Friend.java | 14 ++ .../domain/mail/service/MailService.java | 2 +- .../domain/member/service/MemberReader.java | 5 + .../external/KakaoFriendsResponse.java | 14 ++ .../com/Nunbody/external/KakaoMessage.java | 59 +++++ .../Nunbody/external/WindyfloMailClient.java | 2 +- .../common/HealthCheckApiController.java | 50 +++++ .../global/config/SpringSecurityConfig.java | 2 +- 15 files changed, 563 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/Nunbody/domain/Kakao/controller/KakaoController.java create mode 100644 src/main/java/com/Nunbody/domain/Kakao/service/KakaoService.java create mode 100644 src/main/java/com/Nunbody/domain/Kakao/service/MailScheduleService.java create mode 100644 src/main/java/com/Nunbody/domain/Kakao/service/MessageService.java create mode 100644 src/main/java/com/Nunbody/domain/Kakao/service/dto/DefaultMessageDto.java create mode 100644 src/main/java/com/Nunbody/domain/Kakao/service/dto/Friend.java create mode 100644 src/main/java/com/Nunbody/external/KakaoFriendsResponse.java create mode 100644 src/main/java/com/Nunbody/external/KakaoMessage.java diff --git a/build.gradle b/build.gradle index 884b726..7776910 100644 --- a/build.gradle +++ b/build.gradle @@ -54,6 +54,8 @@ dependencies { //email implementation 'org.springframework.boot:spring-boot-starter-mail' + //json + implementation group: 'org.json', name: 'json', version: '20210307' } tasks.named('test') { diff --git a/src/main/java/com/Nunbody/NunbodyApplication.java b/src/main/java/com/Nunbody/NunbodyApplication.java index f4b00cc..3b3955d 100644 --- a/src/main/java/com/Nunbody/NunbodyApplication.java +++ b/src/main/java/com/Nunbody/NunbodyApplication.java @@ -2,8 +2,10 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; @SpringBootApplication +@EnableScheduling public class NunbodyApplication { public static void main(String[] args) { diff --git a/src/main/java/com/Nunbody/domain/Kakao/controller/KakaoController.java b/src/main/java/com/Nunbody/domain/Kakao/controller/KakaoController.java new file mode 100644 index 0000000..6e7d2ad --- /dev/null +++ b/src/main/java/com/Nunbody/domain/Kakao/controller/KakaoController.java @@ -0,0 +1,31 @@ +package com.Nunbody.domain.Kakao.controller; + +import com.Nunbody.domain.Kakao.service.MessageService; +import com.Nunbody.global.common.SuccessResponse; +import com.Nunbody.global.config.auth.MemberId; +import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RequiredArgsConstructor +@RestController +@RequestMapping("/api/kakao") +public class KakaoController { + private final MessageService messageService; + @GetMapping("/send") + public ResponseEntity> sendMe(@RequestParam( name = "code") String code) throws JsonProcessingException { + + messageService.sendMyMessage(code); + return SuccessResponse.ok(null); + } + @GetMapping("/send/friend") + public ResponseEntity> sendFriend(@RequestParam( name = "code") String code) throws JsonProcessingException { + + messageService.sendFriend(code); + return SuccessResponse.ok(null); + } +} diff --git a/src/main/java/com/Nunbody/domain/Kakao/service/KakaoService.java b/src/main/java/com/Nunbody/domain/Kakao/service/KakaoService.java new file mode 100644 index 0000000..4efcbc7 --- /dev/null +++ b/src/main/java/com/Nunbody/domain/Kakao/service/KakaoService.java @@ -0,0 +1,210 @@ +package com.Nunbody.domain.Kakao.service; + +import com.Nunbody.domain.Kakao.service.dto.DefaultMessageDto; +import com.Nunbody.domain.Kakao.service.dto.Friend; +import com.Nunbody.external.KakaoFriendsResponse; +import com.Nunbody.external.KakaoMessage; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import lombok.RequiredArgsConstructor; +import org.json.JSONArray; +import org.json.JSONObject; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.logging.Logger; + + + +@Service +@RequiredArgsConstructor +public class KakaoService { +// private Logger logger = LoggerFactory.getLogger(this.getClass()); +private static final String APP_TYPE_URL_ENCODED = "application/x-www-form-urlencoded;charset=UTF-8"; + private static final String APP_TYPE_JSON = "application/json;charset=UTF-8"; + private static final String KAKAO_TOKEN_ENDPOINT = "https://kauth.kakao.com/oauth/token"; + + public static String authToken; + @Value("${app.kakao.client.id}") + private String KAKAO_CLIENT_ID; + + @Value("${app.kakao.client.secret}") + private String KAKAO_CLIENT_SECRET; + + @Value("${app.kakao.callback.url}") + private String KAKAO_REDIRECT_URI; + private final RestTemplate restTemplate; + private final KakaoMessage kakaoMessage; + + public String getAuthToken(String code) throws JsonProcessingException { + String jsonResponse = restTemplate.postForObject(KAKAO_TOKEN_ENDPOINT + + "?grant_type=authorization_code" + + "&client_id=" + KAKAO_CLIENT_ID + + "&client_secret=" + KAKAO_CLIENT_SECRET + + "&redirect_uri=" + KAKAO_REDIRECT_URI + + "&code=" + code, null, String.class); + + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode rootNode = objectMapper.readTree(jsonResponse); + return rootNode.get("access_token").asText(); + } +// private Logger logger = (Logger) LoggerFactory.getLogger(this.getClass()); + private static final String MSG_SEND_SERVICE_URL = "https://kapi.kakao.com/v2/api/talk/memo/default/send"; + private static final String SEND_SUCCESS_MSG = "메시지 전송에 성공했습니다."; + private static final String SEND_FAIL_MSG = "메시지 전송에 실패했습니다."; + + private static final String SUCCESS_CODE = "0"; //kakao api에서 return해주는 success code 값 + + public boolean sendMessage(String accessToken, DefaultMessageDto msgDto) { + try { + JSONObject linkObj = new JSONObject(); + linkObj.put("web_url", msgDto.webUrl()); + linkObj.put("mobile_web_url", msgDto.mobileUrl()); + + JSONObject templateObj = new JSONObject(); + templateObj.put("object_type", msgDto.objType()); + templateObj.put("text", msgDto.text()); + templateObj.put("link", linkObj); + templateObj.put("button_title", msgDto.btnTitle()); + + HttpHeaders header = new HttpHeaders(); + header.set("Content-Type", APP_TYPE_URL_ENCODED); + header.set("Authorization", "Bearer " + accessToken); + + MultiValueMap parameters = new LinkedMultiValueMap<>(); + parameters.add("template_object", templateObj.toString()); + + HttpEntity messageRequestEntity = kakaoMessage.httpClientEntity(header, parameters); + + ResponseEntity response = kakaoMessage.httpRequest(MSG_SEND_SERVICE_URL, HttpMethod.POST, messageRequestEntity); + + + String resultCode = ""; + JSONObject jsonData = new JSONObject(response.getBody()); + resultCode = jsonData.get("result_code").toString(); + + return successCheck(resultCode); + } catch (Exception e) { + // 예외 처리 + e.printStackTrace(); + return false; + } + } + + public boolean successCheck(String resultCode) { + if(resultCode.equals(SUCCESS_CODE)) { +// logger.info(SEND_SUCCESS_MSG); + return true; + }else { +// logger.debug(SEND_FAIL_MSG); + return false; + } + + } + + private static final String FRIEND_MESSAGE_SEND_URL = "https://kapi.kakao.com/v1/api/talk/friends/message/default/send"; + + public boolean sendMessageToFriend(String accessToken, DefaultMessageDto msgDto, String receiverUuid) { + try { + JSONObject templateObj = createTemplateObject(msgDto); + + HttpHeaders header = new HttpHeaders(); + header.set("Content-Type", APP_TYPE_URL_ENCODED); + header.set("Authorization", "Bearer " + accessToken); + + MultiValueMap parameters = new LinkedMultiValueMap<>(); + parameters.add("receiver_uuids", "[\"" + receiverUuid + "\"]"); + parameters.add("template_object", templateObj.toString()); + + HttpEntity messageRequestEntity = kakaoMessage.httpClientEntity(header, parameters); + + ResponseEntity response = kakaoMessage.httpRequest(FRIEND_MESSAGE_SEND_URL, HttpMethod.POST, messageRequestEntity); + + JSONObject jsonData = new JSONObject(response.getBody()); + + if (jsonData.has("successful_receiver_uuids")) { + JSONArray successfulReceivers = jsonData.getJSONArray("successful_receiver_uuids"); + System.out.println("메시지 전송 성공한 사용자: " + successfulReceivers.toString()); + } + + if (jsonData.has("failure_info")) { + JSONArray failureInfo = jsonData.getJSONArray("failure_info"); + System.out.println("메시지 전송 실패 정보: " + failureInfo.toString()); + } + + return !jsonData.has("failure_info") || jsonData.getJSONArray("failure_info").length() == 0; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + } + + private JSONObject createTemplateObject(DefaultMessageDto msgDto) { + JSONObject linkObj = new JSONObject(); + linkObj.put("web_url", msgDto.webUrl()); + linkObj.put("mobile_web_url", msgDto.mobileUrl()); + + JSONObject templateObj = new JSONObject(); + templateObj.put("object_type", msgDto.objType()); + templateObj.put("text", msgDto.text()); + templateObj.put("link", linkObj); + templateObj.put("button_title", msgDto.btnTitle()); + + return templateObj; + } + private static final String FRIEND_LIST_URL = "https://kapi.kakao.com/v1/api/talk/friends"; + + public Optional getFriendByNickname(String accessToken, String nickname) { + List allFriends = getAllFriends(accessToken); + return allFriends.stream() + .filter(friend -> friend.getProfile_nickname().equals(nickname)) + .findFirst(); + } + + public List getAllFriends(String accessToken) { + List allFriends = new ArrayList<>(); + String nextUrl = "https://kapi.kakao.com/v1/api/talk/friends"; + + while (nextUrl != null) { + ResponseEntity response = makeApiCall(accessToken, nextUrl); + KakaoFriendsResponse friendsResponse = response.getBody(); + + if (friendsResponse != null) { + allFriends.addAll(friendsResponse.getElements()); + nextUrl = friendsResponse.getAfter_url(); + } else { + nextUrl = null; + } + } + + return allFriends; + } + + private ResponseEntity makeApiCall(String accessToken, String url) { + HttpHeaders headers = new HttpHeaders(); + headers.set("Authorization", "Bearer " + accessToken); + headers.setContentType(MediaType.APPLICATION_JSON); + HttpEntity entity = new HttpEntity<>(headers); + + return restTemplate.exchange( + url, + HttpMethod.GET, + entity, + KakaoFriendsResponse.class + ); + } + + // Friend 클래스 정의 + +} \ No newline at end of file diff --git a/src/main/java/com/Nunbody/domain/Kakao/service/MailScheduleService.java b/src/main/java/com/Nunbody/domain/Kakao/service/MailScheduleService.java new file mode 100644 index 0000000..d1ebee6 --- /dev/null +++ b/src/main/java/com/Nunbody/domain/Kakao/service/MailScheduleService.java @@ -0,0 +1,128 @@ +package com.Nunbody.domain.Kakao.service; + +import com.Nunbody.domain.Mail.domain.MailHeader; +import com.Nunbody.domain.Mail.domain.PlatformType; +import com.Nunbody.domain.Mail.repository.MailRepository; +import com.Nunbody.domain.Mail.service.MailService; +import com.Nunbody.domain.member.domain.Member; +import com.Nunbody.domain.member.service.MemberReader; +import jakarta.mail.*; +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.jsoup.Jsoup; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import static com.Nunbody.domain.Mail.domain.PlatformType.NAVER; + +@Service +@RequiredArgsConstructor +public class MailScheduleService { + + private final MailService mailService; + private final MemberReader memberReader; + private final MailRepository mailRepository; + + + public List checkNewMailsAndGetContent(Long userId, String platformHost, String platformId, String platformPassword, PlatformType platformType) { + List newMailContents = new ArrayList<>(); + + try (Store store = mailService.connectToMailStore(platformHost, platformId, platformPassword); + Folder folder = store.getFolder("inbox")) { + + folder.open(Folder.READ_ONLY); + + // 최근 3개의 메일만 가져옵니다. + int messageCount = folder.getMessageCount(); + int startIndex = Math.max(1, messageCount - 2); + Message[] messages = folder.getMessages(startIndex, messageCount); + + // 가장 최근에 저장된 메일의 날짜를 가져옵니다. + String latestStoredDate = mailRepository.findFirstByMemberIdAndPlatformTypeOrderByDateDesc(userId, platformType) + .map(MailHeader::getDate) + .orElse(String.valueOf(new Date(0))); // 저장된 메일이 없으면 1970년 1월 1일로 설정 + + for (Message message : messages) { + Date messageDate = message.getReceivedDate(); + if (messageDate.after(parseDate(latestStoredDate))) { + String content = getMessageContent(message); + newMailContents.add(content); + } + } + + } catch (Exception e) { +// log.error("Error in checkNewMailsAndGetContent for user: " + userId, e); + } + + return newMailContents; + } + private Date parseDate(String dateStr) { + try { + SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + return formatter.parse(dateStr); + } catch (ParseException e) { +// log.error("Error parsing date: " + dateStr, e); + return new Date(0); // 파싱 실패 시 1970년 1월 1일 반환 + } + } + private String getMessageContent(Message message) throws MessagingException, IOException { + Object content = message.getContent(); + if (content instanceof String) { + return (String) content; + } else if (content instanceof Multipart) { + return getTextFromMultipart((Multipart) content); + } + return "Unable to extract content"; + } + + private String getTextFromMultipart(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(bodyPart.getContent()); + } else if (bodyPart.isMimeType("text/html")) { + String html = (String) bodyPart.getContent(); + result.append(Jsoup.parse(html).text()); + } else if (bodyPart.getContent() instanceof Multipart) { + result.append(getTextFromMultipart((Multipart) bodyPart.getContent())); + } + } + return result.toString(); + } + + @Scheduled(fixedDelay = 10000) // 10000 milliseconds = 10 seconds + public void scheduleMailCheck() { + List users = memberReader.findAll(); + for (Member member : users) { + try { + List newMailContents = checkNewMailsAndGetContent( + member.getId(), + "imap.naver.com", + member.getNaverId(), + member.getNaverPassword(), + PlatformType.NAVER + ); + String body = newMailContents.get(0); + + if (!newMailContents.isEmpty()) { +// log.info("New mails for user {}: {}", member.getId(), newMailContents.size()); + // 여기서 새 메일 내용을 처리할 수 있습니다. + for (String content : newMailContents) { +// log.info("New mail content: {}", content.substring(0, Math.min(content.length(), 100)) + "..."); + } + } + } catch (Exception e) { +// log.error("Failed to check mails for user: " + member.getId(), e); + } + } + } +} diff --git a/src/main/java/com/Nunbody/domain/Kakao/service/MessageService.java b/src/main/java/com/Nunbody/domain/Kakao/service/MessageService.java new file mode 100644 index 0000000..2c1171a --- /dev/null +++ b/src/main/java/com/Nunbody/domain/Kakao/service/MessageService.java @@ -0,0 +1,32 @@ +package com.Nunbody.domain.Kakao.service; + +import com.Nunbody.domain.Kakao.service.dto.DefaultMessageDto; +import com.Nunbody.domain.Kakao.service.dto.Friend; +import com.fasterxml.jackson.core.JsonProcessingException; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; + +@Service +@RequiredArgsConstructor +public class MessageService { + private final KakaoService kakaoService; + + public boolean sendMyMessage(String code) throws JsonProcessingException { + String accessToken = kakaoService.getAuthToken(code); + + DefaultMessageDto myMsg = DefaultMessageDto.of("text", "emap 테스트","https://www.richam.site/login","https://www.richam.site/","자세히보기"); + + return kakaoService.sendMessage(accessToken, myMsg); + } + + public boolean sendFriend(String code) throws JsonProcessingException { + String accessToken = kakaoService.getAuthToken(code); + + DefaultMessageDto myMsg = DefaultMessageDto.of("text", "emap 테스트","https://www.richam.site/login","https://www.richam.site/","자세히보기"); + Optional users = kakaoService.getFriendByNickname(accessToken,"성태현"); + return kakaoService.sendMessageToFriend(accessToken, myMsg, users.get().getUuid()); + } +} diff --git a/src/main/java/com/Nunbody/domain/Kakao/service/dto/DefaultMessageDto.java b/src/main/java/com/Nunbody/domain/Kakao/service/dto/DefaultMessageDto.java new file mode 100644 index 0000000..b9d3d7a --- /dev/null +++ b/src/main/java/com/Nunbody/domain/Kakao/service/dto/DefaultMessageDto.java @@ -0,0 +1,13 @@ +package com.Nunbody.domain.Kakao.service.dto; + +public record DefaultMessageDto( + String objType, + String text, + String webUrl, + String mobileUrl, + String btnTitle +) { + public static DefaultMessageDto of(String objType, String text, String webUrl, String mobileUrl, String btnTitle) { + return new DefaultMessageDto(objType, text, webUrl, mobileUrl, btnTitle); + } +} diff --git a/src/main/java/com/Nunbody/domain/Kakao/service/dto/Friend.java b/src/main/java/com/Nunbody/domain/Kakao/service/dto/Friend.java new file mode 100644 index 0000000..b747a07 --- /dev/null +++ b/src/main/java/com/Nunbody/domain/Kakao/service/dto/Friend.java @@ -0,0 +1,14 @@ +package com.Nunbody.domain.Kakao.service.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class Friend { + private Long id; + private String uuid; + private boolean favorite; + private String profile_nickname; + private String profile_thumbnail_image; +} \ No newline at end of file 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 eb7f86f..ad1980c 100644 --- a/src/main/java/com/Nunbody/domain/mail/service/MailService.java +++ b/src/main/java/com/Nunbody/domain/mail/service/MailService.java @@ -100,7 +100,7 @@ public MailList mailSetting(Long userId, String platformHost, String platformId, return mailList; } - private Store connectToMailStore(String host, String username, String password) throws MessagingException { + public 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); diff --git a/src/main/java/com/Nunbody/domain/member/service/MemberReader.java b/src/main/java/com/Nunbody/domain/member/service/MemberReader.java index c34065b..94d36ad 100644 --- a/src/main/java/com/Nunbody/domain/member/service/MemberReader.java +++ b/src/main/java/com/Nunbody/domain/member/service/MemberReader.java @@ -7,6 +7,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.List; + @Service @RequiredArgsConstructor public class MemberReader { @@ -14,4 +16,7 @@ public class MemberReader { public Member getMemberById(Long memberId){ return memberRepository.findById(memberId).orElseThrow(() -> new EntityNotFoundException(ErrorCode.ENTITY_NOT_FOUND)); } + public List findAll(){ + return memberRepository.findAll(); + } } diff --git a/src/main/java/com/Nunbody/external/KakaoFriendsResponse.java b/src/main/java/com/Nunbody/external/KakaoFriendsResponse.java new file mode 100644 index 0000000..f76f9b4 --- /dev/null +++ b/src/main/java/com/Nunbody/external/KakaoFriendsResponse.java @@ -0,0 +1,14 @@ +package com.Nunbody.external; + +import com.Nunbody.domain.Kakao.service.dto.Friend; +import lombok.Data; + +import java.util.List; + +@Data +public class KakaoFriendsResponse { + private String after_url; + private List elements; + private int total_count; + private int favorite_count; +} diff --git a/src/main/java/com/Nunbody/external/KakaoMessage.java b/src/main/java/com/Nunbody/external/KakaoMessage.java new file mode 100644 index 0000000..81d26e1 --- /dev/null +++ b/src/main/java/com/Nunbody/external/KakaoMessage.java @@ -0,0 +1,59 @@ +package com.Nunbody.external; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +import java.net.URI; +@Service +@RequiredArgsConstructor +public class KakaoMessage { + + private final RestTemplate restTemplate; + /** + * Http 요청 클라이언트 객체 생성 method + * + * @ param Map header HttpHeader 정보 + * @ param Object params HttpBody 정보 + * @ return HttpEntity 생성된 HttpClient객체 정보 반환 + * @ exception 예외사항 + */ + public HttpEntity httpClientEntity(HttpHeaders header, Object params) { + HttpHeaders requestHeaders = header; + + if (params == null || "".equals(params)) + return new HttpEntity<>(requestHeaders); + else + return new HttpEntity<>(params, requestHeaders); + } + + /** + * Http 요청 method + * + * @ param String url 요청 URL 정보 + * @ param HttpMethod method 요청 Method 정보 + * @ param HttpEntity entity 요청 EntityClient 객체 정보 + * @ return HttpEntity 생성된 HttpClient객체 정보 반환 + */ + public ResponseEntity httpRequest(String url, HttpMethod method, HttpEntity entity){ + + return restTemplate.exchange(url, method, entity,String.class); + } + + /** + * Http 요청 method + * + * @ param URI url 요청 URL 정보 + * @ param HttpMethod method 요청 Method 정보 + * @ param HttpEntity entity 요청 EntityClient 객체 정보 + * @ return HttpEntity 생성된 HttpClient객체 정보 반환 + */ + public ResponseEntity httpRequest(URI url, HttpMethod method, HttpEntity entity){ + + return restTemplate.exchange(url, method, entity,String.class); + } +} diff --git a/src/main/java/com/Nunbody/external/WindyfloMailClient.java b/src/main/java/com/Nunbody/external/WindyfloMailClient.java index f83eaa4..1ce230d 100644 --- a/src/main/java/com/Nunbody/external/WindyfloMailClient.java +++ b/src/main/java/com/Nunbody/external/WindyfloMailClient.java @@ -14,7 +14,7 @@ @RequiredArgsConstructor public class WindyfloMailClient { private final String FIND_MAIL_URL = "https://windyflo.com/api/v1/prediction/05d769b1-0a34-49c5-83d3-6b92c1d650f8"; - private final String CREATE_MAIL_URL = "https://windyflo.com/api/v1/prediction/12434af5-c955-4d7e-a613-1d28b0b4f169"; + private final String CREATE_MAIL_URL = "https://windyflo.com/api/v1/prediction/85b9eb4a-e94c-4e98-ab4f-ed805bba1220"; private final RestTemplate restTemplate; public ConversationQARes findMailInVectorDB(String question) { diff --git a/src/main/java/com/Nunbody/global/common/HealthCheckApiController.java b/src/main/java/com/Nunbody/global/common/HealthCheckApiController.java index c91de0e..4d052e3 100644 --- a/src/main/java/com/Nunbody/global/common/HealthCheckApiController.java +++ b/src/main/java/com/Nunbody/global/common/HealthCheckApiController.java @@ -1,12 +1,62 @@ package com.Nunbody.global.common; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + @RestController public class HealthCheckApiController { @RequestMapping("/") public String MeetUpServer() { return "스팸보다 리챔"; } + @Value("${app.kakao.client.id}") + private String KAKAO_CLIENT_ID; + + @Value("${app.kakao.client.secret}") + private String KAKAO_CLIENT_SECRET; + + @Value("${app.kakao.callback.url}") + private String KAKAO_REDIRECT_URI; + private static final String KAKAO_AUTH_ENDPOINT = "https://kauth.kakao.com/oauth/authorize"; + + @GetMapping("/oauth/kakao") + public void kakaoOauth(HttpServletResponse response) throws IOException { + String scope = "talk_message,friends"; // 카카오톡 메시지 전송과 친구 목록 접근을 위한 scope + + String authUrl = KAKAO_AUTH_ENDPOINT + + "?client_id=" + KAKAO_CLIENT_ID + + "&redirect_uri=" + KAKAO_REDIRECT_URI + + "&response_type=code" + + "&scope=" + scope; + + response.sendRedirect(authUrl); + } + @GetMapping("/kakao-login") + public ResponseEntity> initiateKakaoLogin() { + String state = generateRandomState(); // 보안을 위한 랜덤 상태 생성 + String scope = "talk_message,friends"; + String authUrl = KAKAO_AUTH_ENDPOINT + + "?client_id=" + KAKAO_CLIENT_ID + + "&redirect_uri=" + KAKAO_REDIRECT_URI + + "&response_type=code" + + "&scope=" + scope; + + Map response = new HashMap<>(); + response.put("loginUrl", authUrl); + response.put("state", state); + + return ResponseEntity.ok(response); + } + private String generateRandomState() { + return UUID.randomUUID().toString(); + } } diff --git a/src/main/java/com/Nunbody/global/config/SpringSecurityConfig.java b/src/main/java/com/Nunbody/global/config/SpringSecurityConfig.java index 1cc3863..68606c8 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","/gmail"}; + private static final String[] whiteList = {"/api/member/signup", "/api/member/signin", "/api/member/validate", "/api/mail/validate","/gmail", "/oauth/kakao","/api/kakao/send/**","/kakao-login"}; @Bean public WebSecurityCustomizer webSecurityCustomizer() { From 56fee40023edc0adf2f209d25a9ac168bdaf1597 Mon Sep 17 00:00:00 2001 From: qogustj Date: Sun, 4 Aug 2024 11:35:05 +0900 Subject: [PATCH 08/13] =?UTF-8?q?[chore]=20=EC=84=A4=EC=A0=95=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/Nunbody/global/common/HealthCheckApiController.java | 3 ++- .../java/com/Nunbody/global/config/SpringSecurityConfig.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/Nunbody/global/common/HealthCheckApiController.java b/src/main/java/com/Nunbody/global/common/HealthCheckApiController.java index 4d052e3..aac8ca7 100644 --- a/src/main/java/com/Nunbody/global/common/HealthCheckApiController.java +++ b/src/main/java/com/Nunbody/global/common/HealthCheckApiController.java @@ -29,7 +29,7 @@ public String MeetUpServer() { private static final String KAKAO_AUTH_ENDPOINT = "https://kauth.kakao.com/oauth/authorize"; @GetMapping("/oauth/kakao") - public void kakaoOauth(HttpServletResponse response) throws IOException { + public String kakaoOauth(HttpServletResponse response) throws IOException { String scope = "talk_message,friends"; // 카카오톡 메시지 전송과 친구 목록 접근을 위한 scope String authUrl = KAKAO_AUTH_ENDPOINT + @@ -39,6 +39,7 @@ public void kakaoOauth(HttpServletResponse response) throws IOException { "&scope=" + scope; response.sendRedirect(authUrl); + return response.encodeRedirectURL(authUrl); } @GetMapping("/kakao-login") public ResponseEntity> initiateKakaoLogin() { diff --git a/src/main/java/com/Nunbody/global/config/SpringSecurityConfig.java b/src/main/java/com/Nunbody/global/config/SpringSecurityConfig.java index 68606c8..e187003 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","/gmail", "/oauth/kakao","/api/kakao/send/**","/kakao-login"}; + private static final String[] whiteList = {"/api/member/signup", "/api/member/signin", "/api/member/validate", "/api/mail/validate","/gmail", "/oauth/kakao","/kakao-login"}; @Bean public WebSecurityCustomizer webSecurityCustomizer() { From de926c75a3f9f9e04d5a5d39336de1944a7891e9 Mon Sep 17 00:00:00 2001 From: qogustj Date: Sun, 4 Aug 2024 11:35:20 +0900 Subject: [PATCH 09/13] =?UTF-8?q?[feat]=20=EB=82=98=EC=97=90=EA=B2=8C=20?= =?UTF-8?q?=EB=B3=B4=EB=82=B4=EA=B8=B0=20api=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Kakao/controller/KakaoController.java | 7 ++-- .../Kakao/service/MailScheduleService.java | 2 +- .../domain/Kakao/service/MessageService.java | 36 +++++++++++++++++-- .../Nunbody/external/WindyfloMailClient.java | 12 +++++++ 4 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/Nunbody/domain/Kakao/controller/KakaoController.java b/src/main/java/com/Nunbody/domain/Kakao/controller/KakaoController.java index 6e7d2ad..a91309a 100644 --- a/src/main/java/com/Nunbody/domain/Kakao/controller/KakaoController.java +++ b/src/main/java/com/Nunbody/domain/Kakao/controller/KakaoController.java @@ -4,6 +4,7 @@ import com.Nunbody.global.common.SuccessResponse; import com.Nunbody.global.config.auth.MemberId; import com.fasterxml.jackson.core.JsonProcessingException; +import jakarta.mail.MessagingException; import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; @@ -11,15 +12,17 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.io.IOException; + @RequiredArgsConstructor @RestController @RequestMapping("/api/kakao") public class KakaoController { private final MessageService messageService; @GetMapping("/send") - public ResponseEntity> sendMe(@RequestParam( name = "code") String code) throws JsonProcessingException { + public ResponseEntity> sendMe(@MemberId Long memberId, @RequestParam( name = "code") String code) throws IOException, MessagingException { - messageService.sendMyMessage(code); + messageService.sendMyMessage(memberId, code); return SuccessResponse.ok(null); } @GetMapping("/send/friend") diff --git a/src/main/java/com/Nunbody/domain/Kakao/service/MailScheduleService.java b/src/main/java/com/Nunbody/domain/Kakao/service/MailScheduleService.java index d1ebee6..9f3f7be 100644 --- a/src/main/java/com/Nunbody/domain/Kakao/service/MailScheduleService.java +++ b/src/main/java/com/Nunbody/domain/Kakao/service/MailScheduleService.java @@ -82,7 +82,7 @@ private String getMessageContent(Message message) throws MessagingException, IOE return "Unable to extract content"; } - private String getTextFromMultipart(Multipart multipart) throws MessagingException, IOException { + public String getTextFromMultipart(Multipart multipart) throws MessagingException, IOException { StringBuilder result = new StringBuilder(); int count = multipart.getCount(); for (int i = 0; i < count; i++) { diff --git a/src/main/java/com/Nunbody/domain/Kakao/service/MessageService.java b/src/main/java/com/Nunbody/domain/Kakao/service/MessageService.java index 2c1171a..eca2b46 100644 --- a/src/main/java/com/Nunbody/domain/Kakao/service/MessageService.java +++ b/src/main/java/com/Nunbody/domain/Kakao/service/MessageService.java @@ -2,10 +2,18 @@ import com.Nunbody.domain.Kakao.service.dto.DefaultMessageDto; import com.Nunbody.domain.Kakao.service.dto.Friend; +import com.Nunbody.domain.Mail.service.MailService; +import com.Nunbody.domain.member.domain.Member; +import com.Nunbody.domain.member.service.MemberReader; +import com.Nunbody.external.WindyfloMailClient; +import com.Nunbody.global.common.EncoderDecoder; import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import jakarta.mail.*; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.io.IOException; import java.util.List; import java.util.Optional; @@ -13,11 +21,33 @@ @RequiredArgsConstructor public class MessageService { private final KakaoService kakaoService; - - public boolean sendMyMessage(String code) throws JsonProcessingException { + private final WindyfloMailClient windyfloMailClient; + private final MemberReader memberReader; + private final MailService mailService; + private final MailScheduleService mailScheduleService; + public boolean sendMyMessage(Long memberId, String code) throws IOException, MessagingException { String accessToken = kakaoService.getAuthToken(code); + Member member = memberReader.getMemberById(memberId); + Store store = mailService.connectToMailStore("imap.naver.com", member.getNaverId(), EncoderDecoder.decodeFromBase64(member.getNaverPassword())); - DefaultMessageDto myMsg = DefaultMessageDto.of("text", "emap 테스트","https://www.richam.site/login","https://www.richam.site/","자세히보기"); + Folder folder = store.getFolder("inbox"); + + folder.open(Folder.READ_ONLY); + + int lastMessageIndex = folder.getMessageCount(); // 가장 최근 메일의 인덱스 + Message message = folder.getMessage(lastMessageIndex); + Object object = message.getContent(); + + String st = ""; + if (object instanceof String) { + st = (String) object; + } else if (object instanceof Multipart) { + st = mailScheduleService.getTextFromMultipart((Multipart) message.getContent()); + } + + + JsonNode jsonNode = windyfloMailClient.summaryMail(st); + DefaultMessageDto myMsg = DefaultMessageDto.of("text", jsonNode.get("text").asText() +" 이메일이 도착했어요 확인해주세요","https://www.richam.site/login","https://www.richam.site/","메일 확인하러가기"); return kakaoService.sendMessage(accessToken, myMsg); } diff --git a/src/main/java/com/Nunbody/external/WindyfloMailClient.java b/src/main/java/com/Nunbody/external/WindyfloMailClient.java index 1ce230d..14e56d3 100644 --- a/src/main/java/com/Nunbody/external/WindyfloMailClient.java +++ b/src/main/java/com/Nunbody/external/WindyfloMailClient.java @@ -15,6 +15,8 @@ public class WindyfloMailClient { private final String FIND_MAIL_URL = "https://windyflo.com/api/v1/prediction/05d769b1-0a34-49c5-83d3-6b92c1d650f8"; private final String CREATE_MAIL_URL = "https://windyflo.com/api/v1/prediction/85b9eb4a-e94c-4e98-ab4f-ed805bba1220"; + private final String SUMMARY_MAIL_URL = "https://windyflo.com/api/v1/prediction/2f26c1c0-e096-4b47-975c-850e1483ae4b"; + private final RestTemplate restTemplate; public ConversationQARes findMailInVectorDB(String question) { @@ -36,4 +38,14 @@ public JsonNode createMail(String question) { JsonNode ss = restTemplate.postForObject(CREATE_MAIL_URL, request, JsonNode.class); return ss; } + public JsonNode summaryMail(String question) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + QuestionRequest requestBody = new QuestionRequest(question); + HttpEntity request = new HttpEntity<>(requestBody, headers); + + JsonNode ss = restTemplate.postForObject(SUMMARY_MAIL_URL, request, JsonNode.class); + return ss; + } } From 22ad05796875470e33b596b6bd4074fe0e05584d Mon Sep 17 00:00:00 2001 From: qogustj Date: Sun, 4 Aug 2024 16:18:38 +0900 Subject: [PATCH 10/13] =?UTF-8?q?[feat]=20=EB=82=98=EC=97=90=EA=B2=8C=20?= =?UTF-8?q?=EB=B3=B4=EB=82=B4=EA=B8=B0=20api=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Kakao/service/MailScheduleService.java | 43 ++++++++++++------- .../domain/Kakao/service/MessageService.java | 4 ++ .../Nunbody/domain/member/domain/Member.java | 6 ++- 3 files changed, 35 insertions(+), 18 deletions(-) diff --git a/src/main/java/com/Nunbody/domain/Kakao/service/MailScheduleService.java b/src/main/java/com/Nunbody/domain/Kakao/service/MailScheduleService.java index 9f3f7be..a89667e 100644 --- a/src/main/java/com/Nunbody/domain/Kakao/service/MailScheduleService.java +++ b/src/main/java/com/Nunbody/domain/Kakao/service/MailScheduleService.java @@ -1,11 +1,14 @@ package com.Nunbody.domain.Kakao.service; +import com.Nunbody.domain.Kakao.service.dto.DefaultMessageDto; import com.Nunbody.domain.Mail.domain.MailHeader; import com.Nunbody.domain.Mail.domain.PlatformType; import com.Nunbody.domain.Mail.repository.MailRepository; import com.Nunbody.domain.Mail.service.MailService; import com.Nunbody.domain.member.domain.Member; import com.Nunbody.domain.member.service.MemberReader; +import com.Nunbody.external.WindyfloMailClient; +import com.fasterxml.jackson.databind.JsonNode; import jakarta.mail.*; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; @@ -29,33 +32,41 @@ public class MailScheduleService { private final MailService mailService; private final MemberReader memberReader; private final MailRepository mailRepository; + private final WindyfloMailClient windyfloMailClient; + private final KakaoService kakaoService; - - public List checkNewMailsAndGetContent(Long userId, String platformHost, String platformId, String platformPassword, PlatformType platformType) { + public List checkNewMailsAndGetContent(Member member, String platformHost, String platformId, String platformPassword, PlatformType platformType) { List newMailContents = new ArrayList<>(); try (Store store = mailService.connectToMailStore(platformHost, platformId, platformPassword); Folder folder = store.getFolder("inbox")) { folder.open(Folder.READ_ONLY); - - // 최근 3개의 메일만 가져옵니다. - int messageCount = folder.getMessageCount(); - int startIndex = Math.max(1, messageCount - 2); - Message[] messages = folder.getMessages(startIndex, messageCount); - - // 가장 최근에 저장된 메일의 날짜를 가져옵니다. - String latestStoredDate = mailRepository.findFirstByMemberIdAndPlatformTypeOrderByDateDesc(userId, platformType) + int lastMessageIndex = folder.getMessageCount(); // 가장 최근 메일의 인덱스 + Message message = folder.getMessage(lastMessageIndex); + Object object = message.getContent(); + + String st = ""; + if (object instanceof String) { + st = (String) object; + } else if (object instanceof Multipart) { + st = getTextFromMultipart((Multipart) message.getContent()); + } + String latestStoredDate = mailRepository.findFirstByMemberIdAndPlatformTypeOrderByDateDesc(member.getId(), platformType) .map(MailHeader::getDate) .orElse(String.valueOf(new Date(0))); // 저장된 메일이 없으면 1970년 1월 1일로 설정 - for (Message message : messages) { - Date messageDate = message.getReceivedDate(); - if (messageDate.after(parseDate(latestStoredDate))) { - String content = getMessageContent(message); - newMailContents.add(content); + Date messageDate = message.getReceivedDate(); + if (messageDate.after(parseDate(latestStoredDate))) { + st = getMessageContent(message); } + else{ + return null; } + JsonNode jsonNode = windyfloMailClient.summaryMail(st); + DefaultMessageDto myMsg = DefaultMessageDto.of("text", jsonNode.get("text").asText() +" 이메일이 도착했어요 확인해주세요","https://www.richam.site/login","https://www.richam.site/","메일 확인하러가기"); + + kakaoService.sendMessage(member.getAccessToken(), myMsg); } catch (Exception e) { // log.error("Error in checkNewMailsAndGetContent for user: " + userId, e); @@ -105,7 +116,7 @@ public void scheduleMailCheck() { for (Member member : users) { try { List newMailContents = checkNewMailsAndGetContent( - member.getId(), + member, "imap.naver.com", member.getNaverId(), member.getNaverPassword(), diff --git a/src/main/java/com/Nunbody/domain/Kakao/service/MessageService.java b/src/main/java/com/Nunbody/domain/Kakao/service/MessageService.java index eca2b46..7f97f60 100644 --- a/src/main/java/com/Nunbody/domain/Kakao/service/MessageService.java +++ b/src/main/java/com/Nunbody/domain/Kakao/service/MessageService.java @@ -12,6 +12,7 @@ import jakarta.mail.*; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.io.IOException; import java.util.List; @@ -25,9 +26,12 @@ public class MessageService { private final MemberReader memberReader; private final MailService mailService; private final MailScheduleService mailScheduleService; + @Transactional public boolean sendMyMessage(Long memberId, String code) throws IOException, MessagingException { String accessToken = kakaoService.getAuthToken(code); + Member member = memberReader.getMemberById(memberId); + member.updateAccessToken(accessToken); Store store = mailService.connectToMailStore("imap.naver.com", member.getNaverId(), EncoderDecoder.decodeFromBase64(member.getNaverPassword())); Folder folder = store.getFolder("inbox"); diff --git a/src/main/java/com/Nunbody/domain/member/domain/Member.java b/src/main/java/com/Nunbody/domain/member/domain/Member.java index 8f8b6d1..9c3e4ff 100644 --- a/src/main/java/com/Nunbody/domain/member/domain/Member.java +++ b/src/main/java/com/Nunbody/domain/member/domain/Member.java @@ -27,7 +27,7 @@ public class Member extends BaseTimeEntity { private String naverPassword; private String gmailId; private String gmailPassword; - + private String accessToken; private String refreshToken; @OneToMany(mappedBy = "member") @Builder.Default @@ -36,5 +36,7 @@ public class Member extends BaseTimeEntity { public void updateRefreshToken(String newRefreshToken) { this.refreshToken = newRefreshToken; } - + public void updateAccessToken(String accessToken) { + this.accessToken = accessToken; + } } From 3381c8c378f06159e829984f6fbee112e956510c Mon Sep 17 00:00:00 2001 From: qogustj Date: Sun, 4 Aug 2024 17:31:13 +0900 Subject: [PATCH 11/13] =?UTF-8?q?[feat]=20=EB=82=98=EC=97=90=EA=B2=8C=20?= =?UTF-8?q?=EB=B3=B4=EB=82=B4=EA=B8=B0=20api=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Kakao/service/MailScheduleService.java | 64 ++++++++++++------- .../Nunbody/domain/member/domain/Member.java | 7 ++ 2 files changed, 47 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/Nunbody/domain/Kakao/service/MailScheduleService.java b/src/main/java/com/Nunbody/domain/Kakao/service/MailScheduleService.java index a89667e..3aced01 100644 --- a/src/main/java/com/Nunbody/domain/Kakao/service/MailScheduleService.java +++ b/src/main/java/com/Nunbody/domain/Kakao/service/MailScheduleService.java @@ -6,15 +6,18 @@ import com.Nunbody.domain.Mail.repository.MailRepository; import com.Nunbody.domain.Mail.service.MailService; import com.Nunbody.domain.member.domain.Member; +import com.Nunbody.domain.member.repository.MemberRepository; import com.Nunbody.domain.member.service.MemberReader; import com.Nunbody.external.WindyfloMailClient; +import com.Nunbody.global.common.EncoderDecoder; import com.fasterxml.jackson.databind.JsonNode; import jakarta.mail.*; -import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; +import org.joda.time.DateTime; import org.jsoup.Jsoup; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import java.io.IOException; import java.text.ParseException; @@ -34,7 +37,9 @@ public class MailScheduleService { private final MailRepository mailRepository; private final WindyfloMailClient windyfloMailClient; private final KakaoService kakaoService; + private final MemberRepository memberRepository; + @Transactional public List checkNewMailsAndGetContent(Member member, String platformHost, String platformId, String platformPassword, PlatformType platformType) { List newMailContents = new ArrayList<>(); @@ -52,21 +57,28 @@ public List checkNewMailsAndGetContent(Member member, String platformHos } else if (object instanceof Multipart) { st = getTextFromMultipart((Multipart) message.getContent()); } - String latestStoredDate = mailRepository.findFirstByMemberIdAndPlatformTypeOrderByDateDesc(member.getId(), platformType) - .map(MailHeader::getDate) - .orElse(String.valueOf(new Date(0))); // 저장된 메일이 없으면 1970년 1월 1일로 설정 + Date latestStoredDate = member.getLastTime(); +// mailRepository.findFirstByMemberIdAndPlatformTypeOrderByDateDesc(member.getId(), platformType) +// .map(MailHeader::getDate) +// .orElse(String.valueOf(new Date(0))); // 저장된 메일이 없으면 1970년 1월 1일로 설정 Date messageDate = message.getReceivedDate(); - if (messageDate.after(parseDate(latestStoredDate))) { + if ((messageDate.compareTo(latestStoredDate) > 0)) { st = getMessageContent(message); + member.updateLastTime(messageDate); + memberRepository.save(member); + JsonNode jsonNode = windyfloMailClient.summaryMail(st); + DefaultMessageDto myMsg = DefaultMessageDto.of("text", jsonNode.get("text").asText() +" 이메일이 도착했어요 확인해주세요","https://www.richam.site/login","https://www.richam.site/","메일 확인하러가기"); + + kakaoService.sendMessage(member.getAccessToken(), myMsg); } else{ return null; } - JsonNode jsonNode = windyfloMailClient.summaryMail(st); - DefaultMessageDto myMsg = DefaultMessageDto.of("text", jsonNode.get("text").asText() +" 이메일이 도착했어요 확인해주세요","https://www.richam.site/login","https://www.richam.site/","메일 확인하러가기"); - - kakaoService.sendMessage(member.getAccessToken(), myMsg); +// JsonNode jsonNode = windyfloMailClient.summaryMail(st); +// DefaultMessageDto myMsg = DefaultMessageDto.of("text", jsonNode.get("text").asText() +" 이메일이 도착했어요 확인해주세요","https://www.richam.site/login","https://www.richam.site/","메일 확인하러가기"); +// +// kakaoService.sendMessage(member.getAccessToken(), myMsg); } catch (Exception e) { // log.error("Error in checkNewMailsAndGetContent for user: " + userId, e); @@ -114,25 +126,29 @@ public String getTextFromMultipart(Multipart multipart) throws MessagingExceptio public void scheduleMailCheck() { List users = memberReader.findAll(); for (Member member : users) { - try { - List newMailContents = checkNewMailsAndGetContent( - member, - "imap.naver.com", - member.getNaverId(), - member.getNaverPassword(), - PlatformType.NAVER - ); - String body = newMailContents.get(0); - - if (!newMailContents.isEmpty()) { +// Member member = memberReader.getMemberById(8L); + if (member.getAccessToken()!=null) { + try { + + List newMailContents = checkNewMailsAndGetContent( + member, + "imap.naver.com", + member.getNaverId(), + EncoderDecoder.decodeFromBase64(member.getNaverPassword()), + PlatformType.NAVER + ); + String body = newMailContents.get(0); + + if (!newMailContents.isEmpty()) { // log.info("New mails for user {}: {}", member.getId(), newMailContents.size()); - // 여기서 새 메일 내용을 처리할 수 있습니다. - for (String content : newMailContents) { + // 여기서 새 메일 내용을 처리할 수 있습니다. + for (String content : newMailContents) { // log.info("New mail content: {}", content.substring(0, Math.min(content.length(), 100)) + "..."); + } } - } - } catch (Exception e) { + } catch (Exception e) { // log.error("Failed to check mails for user: " + member.getId(), e); + } } } } diff --git a/src/main/java/com/Nunbody/domain/member/domain/Member.java b/src/main/java/com/Nunbody/domain/member/domain/Member.java index 9c3e4ff..a608457 100644 --- a/src/main/java/com/Nunbody/domain/member/domain/Member.java +++ b/src/main/java/com/Nunbody/domain/member/domain/Member.java @@ -6,7 +6,10 @@ import lombok.*; import jakarta.persistence.*; +import org.joda.time.DateTime; + import java.util.ArrayList; +import java.util.Date; import java.util.List; @NoArgsConstructor(access = AccessLevel.PROTECTED) @@ -29,6 +32,7 @@ public class Member extends BaseTimeEntity { private String gmailPassword; private String accessToken; private String refreshToken; + private Date lastTime; @OneToMany(mappedBy = "member") @Builder.Default private List mail = new ArrayList<>(); @@ -39,4 +43,7 @@ public void updateRefreshToken(String newRefreshToken) { public void updateAccessToken(String accessToken) { this.accessToken = accessToken; } + public void updateLastTime(Date lastTime) { + this.lastTime = lastTime; + } } From 52d587425e2c799bbbbe7d8264487cdd23a04186 Mon Sep 17 00:00:00 2001 From: Bae Hyeonseo Date: Tue, 13 Aug 2024 00:47:12 +0900 Subject: [PATCH 12/13] =?UTF-8?q?[chore]=20url=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/Nunbody/external/WindyfloMailClient.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/Nunbody/external/WindyfloMailClient.java b/src/main/java/com/Nunbody/external/WindyfloMailClient.java index 14e56d3..5676a27 100644 --- a/src/main/java/com/Nunbody/external/WindyfloMailClient.java +++ b/src/main/java/com/Nunbody/external/WindyfloMailClient.java @@ -14,8 +14,8 @@ @RequiredArgsConstructor public class WindyfloMailClient { private final String FIND_MAIL_URL = "https://windyflo.com/api/v1/prediction/05d769b1-0a34-49c5-83d3-6b92c1d650f8"; - private final String CREATE_MAIL_URL = "https://windyflo.com/api/v1/prediction/85b9eb4a-e94c-4e98-ab4f-ed805bba1220"; - private final String SUMMARY_MAIL_URL = "https://windyflo.com/api/v1/prediction/2f26c1c0-e096-4b47-975c-850e1483ae4b"; + private final String CREATE_MAIL_URL = "https://windyflo.com/api/v1/prediction/c26a6a36-a0cd-41a2-8452-06fd002a99b6"; + private final String SUMMARY_MAIL_URL = "https://windyflo.com/api/v1/prediction/b085498f-e6da-477f-bbaf-b61992e1dc6b"; private final RestTemplate restTemplate; From 36e403f13ba7437428801ece1845a51e7ef2afd1 Mon Sep 17 00:00:00 2001 From: Bae Hyeonseo Date: Tue, 13 Aug 2024 03:15:42 +0900 Subject: [PATCH 13/13] =?UTF-8?q?[chore]=20url=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/Nunbody/external/WindyfloMailClient.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/Nunbody/external/WindyfloMailClient.java b/src/main/java/com/Nunbody/external/WindyfloMailClient.java index 5676a27..df80cc2 100644 --- a/src/main/java/com/Nunbody/external/WindyfloMailClient.java +++ b/src/main/java/com/Nunbody/external/WindyfloMailClient.java @@ -14,8 +14,8 @@ @RequiredArgsConstructor public class WindyfloMailClient { private final String FIND_MAIL_URL = "https://windyflo.com/api/v1/prediction/05d769b1-0a34-49c5-83d3-6b92c1d650f8"; - private final String CREATE_MAIL_URL = "https://windyflo.com/api/v1/prediction/c26a6a36-a0cd-41a2-8452-06fd002a99b6"; - private final String SUMMARY_MAIL_URL = "https://windyflo.com/api/v1/prediction/b085498f-e6da-477f-bbaf-b61992e1dc6b"; + private final String CREATE_MAIL_URL = "https://flowise-1-6z96.onrender.com/api/v1/prediction/187dedc3-a56f-4619-a4e8-6fe17dc82d4d"; + private final String SUMMARY_MAIL_URL = "https://flowise-1-6z96.onrender.com/api/v1/prediction/01554e84-e710-4424-8aef-085531ddd0f4"; private final RestTemplate restTemplate;