From 4be84ed8148c00a15cac060ac3875dbd1944ca98 Mon Sep 17 00:00:00 2001 From: Junmo Date: Mon, 17 Feb 2025 15:23:36 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat:=20User=20domain=20=EC=9A=94=EC=86=8C?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service/build.gradle | 3 +++ .../main/java/com/whalewatch/domain/User.java | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/service/build.gradle b/service/build.gradle index ad1455f..36c9dcd 100644 --- a/service/build.gradle +++ b/service/build.gradle @@ -20,4 +20,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' testImplementation 'org.springframework.boot:spring-boot-starter-test' + + //telegramBot + implementation 'org.telegram:telegrambots-spring-boot-starter:6.9.7.1' } \ No newline at end of file diff --git a/service/src/main/java/com/whalewatch/domain/User.java b/service/src/main/java/com/whalewatch/domain/User.java index e2ef8ae..6102b9b 100644 --- a/service/src/main/java/com/whalewatch/domain/User.java +++ b/service/src/main/java/com/whalewatch/domain/User.java @@ -13,6 +13,25 @@ public class User { private String username; private String password; + private Long telegramChatId; + private String otpHash; + + public Long getTelegramChatId() { + return telegramChatId; + } + + public void setTelegramChatId(Long telegramChatId) { + this.telegramChatId = telegramChatId; + } + + public String getOtpHash() { + return otpHash; + } + + public void setOtpHash(String otpHash) { + this.otpHash = otpHash; + } + protected User() {} public User(String email, String username, String password) { From c47e987c0dc8a8363dded5db4118ceb344e199f3 Mon Sep 17 00:00:00 2001 From: Junmo Date: Mon, 17 Feb 2025 15:26:46 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20UserService=20OTP=20login=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/whalewatch/service/UserService.java | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/service/src/main/java/com/whalewatch/service/UserService.java b/service/src/main/java/com/whalewatch/service/UserService.java index d7c05fd..3ef4be1 100644 --- a/service/src/main/java/com/whalewatch/service/UserService.java +++ b/service/src/main/java/com/whalewatch/service/UserService.java @@ -9,21 +9,55 @@ public class UserService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; + private final TelegramUserBot telegramUserBot; public UserService(UserRepository userRepository, - PasswordEncoder passwordEncoder) { + PasswordEncoder passwordEncoder, + TelegramUserBot telegramUserBot) { this.userRepository = userRepository; this.passwordEncoder = passwordEncoder; + this.telegramUserBot = telegramUserBot; } public User registerUser(User user) { String hashed = passwordEncoder.encode(user.getPassword()); user.setPassword(hashed); - return userRepository.save(user); } public User getUserInfo(int id) { return userRepository.findById(id).orElseThrow(() -> new RuntimeException("Not found")); } + + // 이메일을 받아 새로운 OTP 생성 후, 해당 사용자의 otpHash를 업데이트하고 텔레그램으로 전송 + public void requestLoginOtp(String email) { + User user = userRepository.findByEmail(email) + .orElseThrow(() -> new RuntimeException("User not found")); + // OTP 생성 + String otp = String.valueOf((int) ((Math.random() * 900000) + 100000)); + String otpHash = passwordEncoder.encode(otp); + user.setOtpHash(otpHash); + userRepository.save(user); + + // 사용자의 telegramChatId가 존재하면 텔레그램으로 OTP 전송 + if (user.getTelegramChatId() != null) { + telegramUserBot.sendTextMessage(user.getTelegramChatId(), "로그인 OTP: " + otp); + } else { + throw new RuntimeException("User is not registered with Telegram"); + } + } + + // 입력받은 OTP가 저장된 otpHash와 일치하면 로그인 성공 처리 + public User loginWithOtp(String email, String otp) { + User user = userRepository.findByEmail(email) + .orElseThrow(() -> new RuntimeException("User not found")); + if (user.getOtpHash() != null && passwordEncoder.matches(otp, user.getOtpHash())) { + // OTP는 한 번 사용 후 삭제 + user.setOtpHash(null); + userRepository.save(user); + return user; + } else { + throw new RuntimeException("Invalid OTP"); + } + } } From 72a06ceb5dde46476385ffda5da32e06d53372c6 Mon Sep 17 00:00:00 2001 From: Junmo Date: Mon, 17 Feb 2025 15:34:01 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat:=20JWTService=20Login=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/whalewatch/service/JwtService.java | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/service/src/main/java/com/whalewatch/service/JwtService.java b/service/src/main/java/com/whalewatch/service/JwtService.java index 5ce8f24..22ea08d 100644 --- a/service/src/main/java/com/whalewatch/service/JwtService.java +++ b/service/src/main/java/com/whalewatch/service/JwtService.java @@ -17,43 +17,34 @@ public class JwtService { private final JwtTokenRepository jwtTokenRepository; private final JwtTokenProvider tokenProvider; private final PasswordEncoder passwordEncoder; + private final UserService userService; public JwtService(UserRepository userRepository, - JwtTokenRepository jwtTokenRepository, - JwtTokenProvider tokenProvider, - PasswordEncoder passwordEncoder) { + JwtTokenRepository jwtTokenRepository, + JwtTokenProvider tokenProvider, + PasswordEncoder passwordEncoder, + UserService userService) { this.userRepository = userRepository; this.jwtTokenRepository = jwtTokenRepository; this.tokenProvider = tokenProvider; this.passwordEncoder = passwordEncoder; + this.userService = userService; } - public TokenResponseDto login(String email, String Password) { - User user = userRepository.findByEmail(email) - .orElseThrow(() -> new RuntimeException("Invalid email or password")); - - if (!passwordEncoder.matches(Password, user.getPassword())) { - throw new RuntimeException("Invalid password"); - } - + public TokenResponseDto login(String email, String otp) { + User user = userService.loginWithOtp(email, otp); String accessToken = tokenProvider.generateAccessToken(user.getEmail()); String refreshToken = tokenProvider.generateRefreshToken(user.getEmail()); - - return new TokenResponseDto(accessToken, refreshToken); } public TokenResponseDto refreshAccessToken(String refreshToken) { - - // RefreshToken 자체가 유효한지 + // RefreshToken 유효성 검사 if (!tokenProvider.validateToken(refreshToken)) { throw new RuntimeException("Invalid or expired refresh token."); } - - // 새 Access Token 발급 String email = tokenProvider.getEmailFromToken(refreshToken); String newAccessToken = tokenProvider.generateAccessToken(email); - return new TokenResponseDto(newAccessToken, refreshToken); } } From 2c5f272748081067e7460330c185a22901dfb009 Mon Sep 17 00:00:00 2001 From: Junmo Date: Mon, 17 Feb 2025 16:18:36 +0900 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20domain=20Password=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EB=B0=8F=20TelegramBot=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../whalewatch/controller/UserController.java | 23 +++-- application.yml | 5 ++ .../config/TelegramBotProperties.java | 24 +++++ .../main/java/com/whalewatch/domain/User.java | 11 +-- .../whalewatch/service/TelegramUserBot.java | 89 +++++++++++++++++++ .../com/whalewatch/service/UserService.java | 4 +- 6 files changed, 134 insertions(+), 22 deletions(-) create mode 100644 common/src/main/java/com/whalewatch/config/TelegramBotProperties.java create mode 100644 service/src/main/java/com/whalewatch/service/TelegramUserBot.java diff --git a/api/src/main/java/com/whalewatch/controller/UserController.java b/api/src/main/java/com/whalewatch/controller/UserController.java index 6b1a7a4..b9ed324 100644 --- a/api/src/main/java/com/whalewatch/controller/UserController.java +++ b/api/src/main/java/com/whalewatch/controller/UserController.java @@ -8,6 +8,8 @@ import com.whalewatch.service.UserService; import org.springframework.web.bind.annotation.*; +import java.util.Map; + @RestController @RequestMapping("/api/users") public class UserController { @@ -23,17 +25,20 @@ public UserController(UserService userService, this.jwtService = jwtService; } - @PostMapping - public UserDto registerUser(@RequestBody UserDto userDto) { - User entity = userMapper.toEntity(userDto); - User saved = userService.registerUser(entity); - return userMapper.toDto(saved); - } @PostMapping("/login") - public TokenResponseDto loginUser(@RequestBody UserDto userDto) { - // JwtService로 로그인 + 토큰 발급 - return jwtService.login(userDto.getEmail(),userDto.getPassword()); + public TokenResponseDto loginUser(@RequestBody Map request) { + String email = request.get("email"); + String otp = request.get("otp"); + return jwtService.login(email, otp); + } + + // 사용자의 이메일을 받아 OTP를 생성 후 텔레그램으로 전송 + @PostMapping("/request-otp") + public String requestOtp(@RequestBody Map request) { + String email = request.get("email"); + userService.requestLoginOtp(email); + return "OTP has been sent to Telegram."; } @PostMapping("/refresh") diff --git a/application.yml b/application.yml index c81ca5e..0cc548d 100644 --- a/application.yml +++ b/application.yml @@ -71,3 +71,8 @@ exchanges: BTC: 0.8 ETH: 22.0 SOL: 300.0 + +telegram: + bot: + username: "${TELEGRAM_USERNAME}" + token: "${TELEGRAM_TOKEN}" \ No newline at end of file diff --git a/common/src/main/java/com/whalewatch/config/TelegramBotProperties.java b/common/src/main/java/com/whalewatch/config/TelegramBotProperties.java new file mode 100644 index 0000000..df80a8a --- /dev/null +++ b/common/src/main/java/com/whalewatch/config/TelegramBotProperties.java @@ -0,0 +1,24 @@ +package com.whalewatch.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "telegram.bot") +public class TelegramBotProperties { + private String username; + private String token; + + public String getUsername() { + return username; + } + public void setUsername(String username) { + this.username = username; + } + public String getToken() { + return token; + } + public void setToken(String token) { + this.token = token; + } +} diff --git a/service/src/main/java/com/whalewatch/domain/User.java b/service/src/main/java/com/whalewatch/domain/User.java index 6102b9b..b28da40 100644 --- a/service/src/main/java/com/whalewatch/domain/User.java +++ b/service/src/main/java/com/whalewatch/domain/User.java @@ -11,7 +11,6 @@ public class User { private String email; private String username; - private String password; private Long telegramChatId; private String otpHash; @@ -34,10 +33,9 @@ public void setOtpHash(String otpHash) { protected User() {} - public User(String email, String username, String password) { + public User(String email, String username) { this.email = email; this.username = username; - this.password = password; } public int getId() { @@ -52,10 +50,6 @@ public String getUsername() { return username; } - public String getPassword() { - return password; - } - public void setEmail(String email) { this.email = email; } @@ -64,7 +58,4 @@ public void setUsername(String username) { this.username = username; } - public void setPassword(String password) { - this.password = password; - } } diff --git a/service/src/main/java/com/whalewatch/service/TelegramUserBot.java b/service/src/main/java/com/whalewatch/service/TelegramUserBot.java new file mode 100644 index 0000000..1984cae --- /dev/null +++ b/service/src/main/java/com/whalewatch/service/TelegramUserBot.java @@ -0,0 +1,89 @@ +package com.whalewatch.service; + +import com.whalewatch.config.TelegramBotProperties; +import com.whalewatch.domain.User; +import org.springframework.stereotype.Component; +import org.telegram.telegrambots.bots.TelegramLongPollingBot; +import org.telegram.telegrambots.meta.api.methods.send.SendMessage; +import org.telegram.telegrambots.meta.api.objects.Update; +import org.telegram.telegrambots.meta.exceptions.TelegramApiException; + +import java.util.HashMap; +import java.util.Map; + +@Component +public class TelegramUserBot extends TelegramLongPollingBot { + + private final UserService userService; + private final TelegramBotProperties telegramBotProperties; + + private Map registrationDataMap = new HashMap<>(); + + public TelegramUserBot(UserService userService, TelegramBotProperties telegramBotProperties) { + this.userService = userService; + this.telegramBotProperties = telegramBotProperties; + } + + @Override + public String getBotUsername() { + return telegramBotProperties.getUsername(); + } + + @Override + public String getBotToken() { + return telegramBotProperties.getToken(); + } + + @Override + public void onUpdateReceived(Update update) { + if (update.hasMessage() && update.getMessage().hasText()) { + String messageText = update.getMessage().getText(); + Long chatId = update.getMessage().getChatId(); + + if (messageText.equalsIgnoreCase("/start")) { + registrationDataMap.put(chatId, new RegistrationData()); + sendTextMessage(chatId, "Welcome! Please enter your email to sign up."); + return; + } + + RegistrationData data = registrationDataMap.get(chatId); + if (data != null) { + if (data.getEmail() == null) { + data.setEmail(messageText.trim()); + sendTextMessage(chatId, "Email received. Now, please enter your username."); + } else if (data.getUsername() == null) { + data.setUsername(messageText.trim()); + User newUser = new User(data.getEmail(), data.getUsername()); + newUser.setTelegramChatId(chatId); + userService.registerUser(newUser); + sendTextMessage(chatId, "Registration completed! You can now request an OTP to log in."); + registrationDataMap.remove(chatId); + } + return; + } + sendTextMessage(chatId, "Unrecognized command. Please type /start to begin registration."); + } + } + + public void sendTextMessage(Long chatId, String text) { + SendMessage message = new SendMessage(); + message.setChatId(chatId.toString()); + message.setText(text); + try { + execute(message); + } catch (TelegramApiException e) { + e.printStackTrace(); + } + } + + // 내부 대화 상태 저장 클래스 + private static class RegistrationData { + private String email; + private String username; + + public String getEmail() { return email; } + public void setEmail(String email) { this.email = email; } + public String getUsername() { return username; } + public void setUsername(String username) { this.username = username; } + } +} diff --git a/service/src/main/java/com/whalewatch/service/UserService.java b/service/src/main/java/com/whalewatch/service/UserService.java index 3ef4be1..a23cb87 100644 --- a/service/src/main/java/com/whalewatch/service/UserService.java +++ b/service/src/main/java/com/whalewatch/service/UserService.java @@ -20,8 +20,6 @@ public UserService(UserRepository userRepository, } public User registerUser(User user) { - String hashed = passwordEncoder.encode(user.getPassword()); - user.setPassword(hashed); return userRepository.save(user); } @@ -41,7 +39,7 @@ public void requestLoginOtp(String email) { // 사용자의 telegramChatId가 존재하면 텔레그램으로 OTP 전송 if (user.getTelegramChatId() != null) { - telegramUserBot.sendTextMessage(user.getTelegramChatId(), "로그인 OTP: " + otp); + telegramUserBot.sendTextMessage(user.getTelegramChatId(), "Your login OTP: " + otp); } else { throw new RuntimeException("User is not registered with Telegram"); } From 7dcf968967d81010319ff1cbb0ee7e2e8ba2f558 Mon Sep 17 00:00:00 2001 From: Junmo Date: Mon, 17 Feb 2025 17:27:14 +0900 Subject: [PATCH 5/5] =?UTF-8?q?feat:=20=EC=88=9C=ED=99=98=EC=B0=B8?= =?UTF-8?q?=EC=A1=B0=20=EC=82=AD=EC=A0=9C,=20=ED=85=94=EB=A0=88=EA=B7=B8?= =?UTF-8?q?=EB=9E=A8=20=EB=B4=87=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85,?= =?UTF-8?q?=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/whalewatch/WhaleWatchApplication.java | 1 + .../whalewatch/controller/UserController.java | 2 +- .../com/whalewatch/config/SecurityConfig.java | 2 +- .../main/java/com/whalewatch/dto/UserDto.java | 11 +--------- service/build.gradle | 4 ++++ .../com/whalewatch/mapper/UserMapper.java | 2 -- .../whalewatch/repository/UserRepository.java | 2 -- .../com/whalewatch/service/UserService.java | 19 ++++++++++-------- .../telegram/TelegramBotConfig.java | 17 ++++++++++++++++ .../telegram/TelegramMessageEvent.java | 20 +++++++++++++++++++ .../TelegramUserBot.java | 11 ++++++++-- 11 files changed, 65 insertions(+), 26 deletions(-) create mode 100644 service/src/main/java/com/whalewatch/telegram/TelegramBotConfig.java create mode 100644 service/src/main/java/com/whalewatch/telegram/TelegramMessageEvent.java rename service/src/main/java/com/whalewatch/{service => telegram}/TelegramUserBot.java (90%) diff --git a/api/src/main/java/com/whalewatch/WhaleWatchApplication.java b/api/src/main/java/com/whalewatch/WhaleWatchApplication.java index 54004d2..2122ded 100644 --- a/api/src/main/java/com/whalewatch/WhaleWatchApplication.java +++ b/api/src/main/java/com/whalewatch/WhaleWatchApplication.java @@ -18,4 +18,5 @@ public CommandLineRunner run(WebSocketManager webSocketManager) { webSocketManager.startAll(); }; } + } diff --git a/api/src/main/java/com/whalewatch/controller/UserController.java b/api/src/main/java/com/whalewatch/controller/UserController.java index b9ed324..0c57b99 100644 --- a/api/src/main/java/com/whalewatch/controller/UserController.java +++ b/api/src/main/java/com/whalewatch/controller/UserController.java @@ -46,7 +46,7 @@ public TokenResponseDto refreshToken(@RequestBody TokenResponseDto tokenDto){ return jwtService.refreshAccessToken(tokenDto.getRefreshToken()); } - @GetMapping("{id}") + @GetMapping("/info/{id}") public UserDto getUserInfo(@PathVariable int id) { User user = userService.getUserInfo(id); return userMapper.toDto(user); diff --git a/common/src/main/java/com/whalewatch/config/SecurityConfig.java b/common/src/main/java/com/whalewatch/config/SecurityConfig.java index 50ddd98..16536d0 100644 --- a/common/src/main/java/com/whalewatch/config/SecurityConfig.java +++ b/common/src/main/java/com/whalewatch/config/SecurityConfig.java @@ -39,7 +39,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // URL별 권한 설정 .authorizeHttpRequests(auth -> auth .requestMatchers("/h2-console/**").permitAll() - .requestMatchers("/api/users", "/api/users/login").permitAll() // 회원가입 및 로그인 허용 + .requestMatchers("/api/users/request-otp", "/api/users/login").permitAll() // 회원가입 및 로그인 허용 .requestMatchers("/api/users/**").authenticated() .anyRequest().authenticated() ) diff --git a/common/src/main/java/com/whalewatch/dto/UserDto.java b/common/src/main/java/com/whalewatch/dto/UserDto.java index 914e3a7..106c475 100644 --- a/common/src/main/java/com/whalewatch/dto/UserDto.java +++ b/common/src/main/java/com/whalewatch/dto/UserDto.java @@ -4,13 +4,11 @@ public class UserDto { private int id; private String email; private String username; - private String password; - public UserDto(int id,String email, String username, String password) { + public UserDto(int id,String email, String username) { this.id = id; this.email = email; this.username = username; - this.password = password; } public void setId(int id) { @@ -25,10 +23,6 @@ public String getEmail() { return email; } - public String getPassword() { - return password; - } - public void setEmail(String email) { this.email = email; } @@ -41,7 +35,4 @@ public void setUsername(String username) { this.username = username; } - public void setPassword(String password) { - this.password = password; - } } diff --git a/service/build.gradle b/service/build.gradle index 36c9dcd..14e5e5a 100644 --- a/service/build.gradle +++ b/service/build.gradle @@ -23,4 +23,8 @@ dependencies { //telegramBot implementation 'org.telegram:telegrambots-spring-boot-starter:6.9.7.1' + + //JAXB + implementation 'javax.xml.bind:jaxb-api:2.3.1' + implementation 'org.glassfish.jaxb:jaxb-runtime:2.3.1' } \ No newline at end of file diff --git a/service/src/main/java/com/whalewatch/mapper/UserMapper.java b/service/src/main/java/com/whalewatch/mapper/UserMapper.java index cfcb9ba..253f9dc 100644 --- a/service/src/main/java/com/whalewatch/mapper/UserMapper.java +++ b/service/src/main/java/com/whalewatch/mapper/UserMapper.java @@ -11,13 +11,11 @@ public interface UserMapper { @Mapping(target = "id", source = "id") @Mapping(target = "email", source = "email") @Mapping(target = "username", source = "username") - @Mapping(target = "password", source = "password") UserDto toDto(User entity); // DTO -> Entity @Mapping(target = "id", ignore = true) @Mapping(target = "email", source = "email") @Mapping(target = "username", source = "username") - @Mapping(target = "password", source = "password") User toEntity(UserDto dto); } diff --git a/service/src/main/java/com/whalewatch/repository/UserRepository.java b/service/src/main/java/com/whalewatch/repository/UserRepository.java index 2b8ca0e..ab2da03 100644 --- a/service/src/main/java/com/whalewatch/repository/UserRepository.java +++ b/service/src/main/java/com/whalewatch/repository/UserRepository.java @@ -7,6 +7,4 @@ public interface UserRepository extends JpaRepository { Optional findByEmail(String email); - - Optional findByEmailAndPassword(String email, String password); } diff --git a/service/src/main/java/com/whalewatch/service/UserService.java b/service/src/main/java/com/whalewatch/service/UserService.java index a23cb87..b362bd7 100644 --- a/service/src/main/java/com/whalewatch/service/UserService.java +++ b/service/src/main/java/com/whalewatch/service/UserService.java @@ -2,6 +2,10 @@ import com.whalewatch.domain.User; import com.whalewatch.repository.UserRepository; +import com.whalewatch.telegram.TelegramMessageEvent; +import com.whalewatch.telegram.TelegramUserBot; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Lazy; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; @@ -9,14 +13,14 @@ public class UserService { private final UserRepository userRepository; private final PasswordEncoder passwordEncoder; - private final TelegramUserBot telegramUserBot; + private final ApplicationEventPublisher eventPublisher; public UserService(UserRepository userRepository, PasswordEncoder passwordEncoder, - TelegramUserBot telegramUserBot) { + ApplicationEventPublisher eventPublisher) { this.userRepository = userRepository; this.passwordEncoder = passwordEncoder; - this.telegramUserBot = telegramUserBot; + this.eventPublisher = eventPublisher; } public User registerUser(User user) { @@ -24,22 +28,21 @@ public User registerUser(User user) { } public User getUserInfo(int id) { - return userRepository.findById(id).orElseThrow(() -> new RuntimeException("Not found")); + return userRepository.findById(id) + .orElseThrow(() -> new RuntimeException("Not found")); } - // 이메일을 받아 새로운 OTP 생성 후, 해당 사용자의 otpHash를 업데이트하고 텔레그램으로 전송 + // 이메일을 받아 OTP 생성 후, 해당 사용자의 otpHash 업데이트 및 텔레그램 메시지 전송 이벤트 발행 public void requestLoginOtp(String email) { User user = userRepository.findByEmail(email) .orElseThrow(() -> new RuntimeException("User not found")); - // OTP 생성 String otp = String.valueOf((int) ((Math.random() * 900000) + 100000)); String otpHash = passwordEncoder.encode(otp); user.setOtpHash(otpHash); userRepository.save(user); - // 사용자의 telegramChatId가 존재하면 텔레그램으로 OTP 전송 if (user.getTelegramChatId() != null) { - telegramUserBot.sendTextMessage(user.getTelegramChatId(), "Your login OTP: " + otp); + eventPublisher.publishEvent(new TelegramMessageEvent(user.getTelegramChatId(), "Your login OTP: " + otp)); } else { throw new RuntimeException("User is not registered with Telegram"); } diff --git a/service/src/main/java/com/whalewatch/telegram/TelegramBotConfig.java b/service/src/main/java/com/whalewatch/telegram/TelegramBotConfig.java new file mode 100644 index 0000000..47d1d55 --- /dev/null +++ b/service/src/main/java/com/whalewatch/telegram/TelegramBotConfig.java @@ -0,0 +1,17 @@ +package com.whalewatch.telegram; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.telegram.telegrambots.meta.TelegramBotsApi; +import org.telegram.telegrambots.meta.exceptions.TelegramApiException; +import org.telegram.telegrambots.updatesreceivers.DefaultBotSession; + +@Configuration +public class TelegramBotConfig { + @Bean + public TelegramBotsApi telegramBotsApi(TelegramUserBot telegramUserBot) throws TelegramApiException { + TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class); + botsApi.registerBot(telegramUserBot); + return botsApi; + } +} diff --git a/service/src/main/java/com/whalewatch/telegram/TelegramMessageEvent.java b/service/src/main/java/com/whalewatch/telegram/TelegramMessageEvent.java new file mode 100644 index 0000000..3fd1d4c --- /dev/null +++ b/service/src/main/java/com/whalewatch/telegram/TelegramMessageEvent.java @@ -0,0 +1,20 @@ +package com.whalewatch.telegram; + +public class TelegramMessageEvent { + private final Long chatId; + private final String message; + + public TelegramMessageEvent(Long chatId, String message) { + this.chatId = chatId; + this.message = message; + } + + public Long getChatId() { + return chatId; + } + + public String getMessage() { + return message; + } +} + diff --git a/service/src/main/java/com/whalewatch/service/TelegramUserBot.java b/service/src/main/java/com/whalewatch/telegram/TelegramUserBot.java similarity index 90% rename from service/src/main/java/com/whalewatch/service/TelegramUserBot.java rename to service/src/main/java/com/whalewatch/telegram/TelegramUserBot.java index 1984cae..fa45b0a 100644 --- a/service/src/main/java/com/whalewatch/service/TelegramUserBot.java +++ b/service/src/main/java/com/whalewatch/telegram/TelegramUserBot.java @@ -1,7 +1,9 @@ -package com.whalewatch.service; +package com.whalewatch.telegram; import com.whalewatch.config.TelegramBotProperties; import com.whalewatch.domain.User; +import com.whalewatch.service.UserService; +import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; import org.telegram.telegrambots.bots.TelegramLongPollingBot; import org.telegram.telegrambots.meta.api.methods.send.SendMessage; @@ -56,7 +58,7 @@ public void onUpdateReceived(Update update) { User newUser = new User(data.getEmail(), data.getUsername()); newUser.setTelegramChatId(chatId); userService.registerUser(newUser); - sendTextMessage(chatId, "Registration completed! You can now request an OTP to log in."); + sendTextMessage(chatId, "Registration completed! You can request an OTP to log in."); registrationDataMap.remove(chatId); } return; @@ -76,6 +78,11 @@ public void sendTextMessage(Long chatId, String text) { } } + @EventListener + public void handleTelegramMessageEvent(TelegramMessageEvent event) { + sendTextMessage(event.getChatId(), event.getMessage()); + } + // 내부 대화 상태 저장 클래스 private static class RegistrationData { private String email;