- 
                Notifications
    You must be signed in to change notification settings 
- Fork 0
refactor: 예약 메세지 기능 수정 #37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
          
     Merged
      
      
    The head ref may contain hidden characters: "36-refactor-\uC608\uC57D-\uBA54\uC138\uC9C0-\uAE30\uB2A5-\uC218\uC815"
  
     Merged
                    Changes from all commits
      Commits
    
    
            Show all changes
          
          
            14 commits
          
        
        Select commit
          Hold shift + click to select a range
      
      0671d46
              
                refactor: commandName 수정
              
              
                haeyoon1 853bcad
              
                feat: 채널 입력 -> commandListener에서 관리
              
              
                haeyoon1 80f134b
              
                feat: 시간, 텍스트 입력 -> ModalListener에서 관리
              
              
                haeyoon1 4968b4d
              
                feat: 모달, 드롭다운 선택하는 리스너 생성
              
              
                haeyoon1 321f48a
              
                feat: 멤버 언급 기능 추가
              
              
                haeyoon1 41099ee
              
                feat: 멤버 언급 기능 추가로 인한 파싱 수정
              
              
                haeyoon1 41241c3
              
                feat: 예약 메세지 전송 시 백틱 처리되어있던 이름 -> 백틱 삭제
              
              
                haeyoon1 0c6bfbc
              
                feat: Jda 설정에 ListenerAdapter 등록
              
              
                haeyoon1 34671ba
              
                refactor: 변수 명 변경
              
              
                haeyoon1 c2882e5
              
                refactor: 변수 명 변경
              
              
                haeyoon1 173b516
              
                refactor: listener 이름 변경
              
              
                haeyoon1 0295dd8
              
                refactor: split 된 array 변수로 표현
              
              
                haeyoon1 2f7eb99
              
                refactor: AutoCompleteInteractionListener -> SlashCommandListener로 변경
              
              
                haeyoon1 ffa096d
              
                refactor: 빈 등록 방식 클래스단위로 변경
              
              
                haeyoon1 File filter
Filter by extension
Conversations
          Failed to load comments.   
        
        
          
      Loading
        
  Jump to
        
          Jump to file
        
      
      
          Failed to load files.   
        
        
          
      Loading
        
  Diff view
Diff view
There are no files selected for viewing
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              
        
          
          
            143 changes: 0 additions & 143 deletions
          
          143 
        
  src/main/java/greedy/greedybot/presentation/jda/listener/ScheduledCommandListener.java
  
  
      
      
   
        
      
      
    This file was deleted.
      
      Oops, something went wrong.
      
    
  
        
          
          
            93 changes: 93 additions & 0 deletions
          
          93 
        
  ...main/java/greedy/greedybot/presentation/jda/listener/ScheduledMessageCommandListener.java
  
  
      
      
   
        
      
      
    
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| package greedy.greedybot.presentation.jda.listener; | ||
|  | ||
| import greedy.greedybot.common.exception.GreedyBotException; | ||
| import greedy.greedybot.presentation.jda.role.DiscordRole; | ||
| import greedy.greedybot.presentation.jda.role.ScheduledMessageChannel; | ||
| import java.util.Map; | ||
| import java.util.Set; | ||
| import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; | ||
| import net.dv8tion.jda.api.interactions.commands.OptionMapping; | ||
| import net.dv8tion.jda.api.interactions.commands.OptionType; | ||
| import net.dv8tion.jda.api.interactions.commands.build.Commands; | ||
| import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; | ||
| import net.dv8tion.jda.api.interactions.components.selections.StringSelectMenu; | ||
| import org.jetbrains.annotations.NotNull; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
| import org.springframework.beans.factory.annotation.Value; | ||
| import org.springframework.stereotype.Component; | ||
|  | ||
| @Component | ||
| public class ScheduledMessageCommandListener implements SlashCommandListener { | ||
|  | ||
| private static final Logger log = LoggerFactory.getLogger(ScheduledMessageCommandListener.class); | ||
|  | ||
| private static final Map<String, ScheduledMessageChannel> CHANNEL_NAME_TO_ENUM = Map.of( | ||
| "🚀공통-자유", ScheduledMessageChannel.NOTICE, | ||
| "🍃백엔드-스터디", ScheduledMessageChannel.BACKEND, | ||
| "\uD83E\uDD8B프론트엔드-스터디", ScheduledMessageChannel.FRONT, | ||
| "\uD83E\uDEE7리드-대화", ScheduledMessageChannel.LEAD_CONVERSATION, | ||
| "tf-discord-test-ground", ScheduledMessageChannel.TEST | ||
| ); | ||
|  | ||
| @Value("${discord.message_writing_channel}") | ||
| private Long allowedChannelId; | ||
|  | ||
| @Override | ||
| public String getCommandName() { | ||
| return "scheduled-message"; | ||
| } | ||
|  | ||
| @Override | ||
| public SlashCommandData getCommandData() { | ||
| return Commands.slash("scheduled-message", "예약 메세지 등록") | ||
| .addOption(OptionType.STRING, "member", "멘션할 멤버나 그룹이 있다면, 엔터 대신 Space(띄어쓰기)로 구분해 입력하세요. \n" | ||
| + "사용자는 @이름 형태로 입력하세요.", false); | ||
| } | ||
|  | ||
| @Override | ||
| public void onAction(@NotNull SlashCommandInteractionEvent event) { | ||
| try { | ||
| validateAllowedChannel(event); | ||
|  | ||
| String members = null; | ||
| OptionMapping memberOption = event.getOption("member"); | ||
| if (memberOption != null) { | ||
| members = memberOption.getAsString(); | ||
| } | ||
|  | ||
| String selectMenuId = "scheduled-channel-select"; | ||
| if (members != null && !members.isEmpty()) { | ||
| selectMenuId += ":" + members; | ||
| } | ||
|  | ||
| // 채널 선택 드롭다운 생성 | ||
| StringSelectMenu.Builder channelMenu = StringSelectMenu.create(selectMenuId) | ||
| .setPlaceholder("메시지를 보낼 채널을 선택하세요"); | ||
|  | ||
| CHANNEL_NAME_TO_ENUM.forEach((name, enumValue) -> { | ||
| channelMenu.addOption(name, enumValue.name()); | ||
| }); | ||
|  | ||
| event.reply("📌 메시지를 보낼 채널을 선택하세요:") | ||
| .addActionRow(channelMenu.build()) | ||
| .setEphemeral(true) | ||
| .queue(); | ||
| } catch (GreedyBotException e) { | ||
| log.error(e.getMessage()); | ||
| event.reply(e.getMessage()).setEphemeral(true).queue(); | ||
| } | ||
| } | ||
|  | ||
| private void validateAllowedChannel(final @NotNull SlashCommandInteractionEvent event) { | ||
| if (event.getChannel().getIdLong() != allowedChannelId) { | ||
| log.warn("[NOT ALLOWED CHANNEL COMMAND]: {}", event.getUser().getEffectiveName()); | ||
| throw new GreedyBotException("예약 메세지는 현재 채널에서 작성할 수 없습니다"); | ||
| } | ||
| } | ||
|  | ||
| @Override | ||
| public Set<DiscordRole> allowedRoles() { | ||
| return Set.of(DiscordRole.LEAD); | ||
| } | ||
| } | 
        
          
          
            54 changes: 54 additions & 0 deletions
          
          54 
        
  src/main/java/greedy/greedybot/presentation/jda/listener/ScheduledMessageModalLauncher.java
  
  
      
      
   
        
      
      
    
  
    
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      Learn more about bidirectional Unicode characters
    
  
  
    
              | Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| package greedy.greedybot.presentation.jda.listener; | ||
|  | ||
| import java.time.LocalDateTime; | ||
| import java.time.format.DateTimeFormatter; | ||
| import java.util.Locale; | ||
| import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; | ||
| import net.dv8tion.jda.api.hooks.ListenerAdapter; | ||
| import net.dv8tion.jda.api.interactions.components.text.TextInput; | ||
| import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; | ||
| import net.dv8tion.jda.api.interactions.modals.Modal; | ||
| import org.jetbrains.annotations.NotNull; | ||
| import org.springframework.stereotype.Component; | ||
|  | ||
| @Component | ||
| public class ScheduledMessageModalLauncher extends ListenerAdapter { | ||
|  | ||
| private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm", | ||
| Locale.ENGLISH); | ||
|  | ||
| @Override | ||
| public void onStringSelectInteraction(@NotNull StringSelectInteractionEvent event) { | ||
| String[] parts = event.getComponentId().split(":", 2); | ||
| String componentId = parts[0]; | ||
| String members = (parts.length > 1) ? parts[1] : null; | ||
|  | ||
| if (!componentId.equals("scheduled-channel-select")) { | ||
| return; | ||
| } | ||
|  | ||
| TextInput messageInput = TextInput.create("message", "메시지", TextInputStyle.PARAGRAPH) | ||
| .setRequired(true) | ||
| .build(); | ||
|  | ||
| final String defaultTime = LocalDateTime.now().format(DATE_TIME_FORMATTER); | ||
| TextInput timeInput = TextInput.create("time", "예약 시간", TextInputStyle.SHORT) | ||
| .setValue(defaultTime) | ||
| .setRequired(true) | ||
| .build(); | ||
|  | ||
| final String channelId = event.getValues().get(0); | ||
|  | ||
| String modalId = "scheduled-message-modal:" + channelId; | ||
| if (members != null && !members.isEmpty()) { | ||
| modalId += ":" + members; | ||
| } | ||
|  | ||
| Modal modal = Modal.create(modalId, "예약 메시지 등록") | ||
| .addActionRow(messageInput) | ||
| .addActionRow(timeInput) | ||
| .build(); | ||
|  | ||
| event.replyModal(modal).queue(); | ||
| } | ||
| } | 
      
      Oops, something went wrong.
        
    
  
  Add this suggestion to a batch that can be applied as a single commit.
  This suggestion is invalid because no changes were made to the code.
  Suggestions cannot be applied while the pull request is closed.
  Suggestions cannot be applied while viewing a subset of changes.
  Only one suggestion per line can be applied in a batch.
  Add this suggestion to a batch that can be applied as a single commit.
  Applying suggestions on deleted lines is not supported.
  You must change the existing code in this line in order to create a valid suggestion.
  Outdated suggestions cannot be applied.
  This suggestion has been applied or marked resolved.
  Suggestions cannot be applied from pending reviews.
  Suggestions cannot be applied on multi-line comments.
  Suggestions cannot be applied while the pull request is queued to merge.
  Suggestion cannot be applied right now. Please check back later.
  
    
  
    
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
빈 등록 하나가 누락 되었는데, 이것만 추가 하고 머지하시죠!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 부분 관련해서 질문이 있습니다🤔
ScheduledMessageModalLauncher를 Listener에 등록할 경우 모달 제출 시 다음과 같은 에러가 발생했습니다:이 에러는 하나의 Interaction을 두 개의 Listener(
ScheduledMessageModalLauncher,ScheduledMessageSubmitListener)가 동시에 수신하면서 중복 응답이 발생했기 때문입니다.그래서 실제로 이벤트를 처리해야 하는
SlashCommandListenerMapper(=명령어 등록)와ScheduledMessageSubmitListener(=모달 제출) 두 개 Bean만 등록하도록 수정했습니다.(이렇게 수정했을 시 정상 작동은 하면서 위의 오류가 발생하지 않습니다!)
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
많은 리스너들 중 왜 하필 저 두 리스너만 충돌이 일어날까?에 대해 찾아보았으나 충분한 이해가 되지 않습니다....🤯
일단 플로우를 찾아보았을 때 아래와 같다고 하네요. 🚨 표시된 곳이 문제가 일어나는 부분들인 것 같은데 알맞게 이해한 것인지 태연님 생각이 궁금합니다!
① 드롭다운 선택 → 모달 띄우기 (
ScheduledMessageModalLauncher)JDA가 Discord에 첫 번째 응답 전송
② 사용자가 모달에 메시지/시간을 입력하고 제출함
ScheduledMessageSubmitListener에서ModalInteractionEvent발생→ 🚨🚨🚨 새로운 Interaction(
ModalInteractionEvent)을 모든 ListenerAdapter에게 broadcast③
ScheduledMessageSubmitListener가 이벤트를 받아 처리④⚠️  하지만 
ScheduledMessageModalLauncher도 ListenerAdapter 이기 때문에🚨🚨🚨 JDA는 이
ModalInteractionEvent를ScheduledMessageModalLauncher에게도 전달-> 내부적으로
⑤ Discord 입장에서는 중복 응답
ScheduledMessageSubmitListener→event.reply("✅ 메시지가...")ScheduledMessageModalLauncher→ 내부적으로 이미event.replyModal()로 응답한 적 있음→ Discord API는 해당 이벤트가 이미 응답된 Interaction임을 감지
→ 에러 발생