프로젝트

[WeatherBot][Spring] 2. Slack bot 언급하면 대답하기

HEY__ 2023. 8. 21. 11:21
728x90

✅ Slack bot과 상호작용할 수 있는 방법이 뭐가 있을까?

슬랙에서 사용자들이 Slack bot을 사용하려면 Slack bot과 상호작용을 할 수 있어야 한다. 즉, 사용자가 Slack 채널에 특정 메세지를 올리면 그에 따라 Slack bot이 적절한 응답을 할 수 있어야 한다.

 

Slack bot이 메세지에 응답할 수 있는 방법은 두 가지가 있다.

1. Slash command

2. Event subscription

 

Slash command/날씨 와 같이 명령어에 대해 반응을 하는 것이고, Event subscription은 멘션부터 채널 생성, 퇴장과 같이 다양한 이벤트에 대해 반응한다.

 

그럼 둘 중 무엇을 쓰는 것이 더 좋을까?

일단 Slack App 설정 페이지에서 Slash command와 Event subscription을 어떻게 사용하는지 보자.

 

먼저 Slash command는 api.slack.com - Features - Slash commands 탭에서

명령어와, 명령어 입력 시 API를 요청할 Request URL을 설정할 수 있다.

Event subscriptions에 비해서는 비교적 간단하지만, 문제는 Command를 생성할 때마다 Request URL을 작성해주어야 한다는 것이다.

 

 

 

 

Event subscription은 api.slack.com - Features - Event Subscriptions 탭에서

기능을 On하면 Request URL을 설정할 수 있다.

Subscribe to bot events를 통해 어떤 이벤트가 발생했을 때 Request URL로 요청을 할 것인지 설정할 수 있다.

 

 

우리는 /날씨같은 명령어 형식보다는 @나무늘봇 날씨 알려줘와 같이 좀 더 챗봇스럽게 만들고싶기 때문에 Event subscription을 이용해서 Slack bot이 메세지에 응답하도록 해보자.

 

 


✅  ngrok: 로컬 환경 테스트 시, 외부에서 접근할 수 있는 임시 URL

본격적으로 코드를 작성하기 전에 해주어야 할 일이 있는데, Features - Event subscriptions에 가면 Request URL을 작성해야 한다. AWS에 올려서 할당받은 주소를 넣으면 제일 좋겠지만, 개발 초기에 간단하게 테스트 하는 용도로는 부담스러운 것이 사실이다.

이 때, ngrok를 통해 외부에서 접근할 수 있는 임시 주소를 부여받을 수 있다.

 

1. brew를 통해 ngrok 설치

$ brew install --cask ngrok

 

2. ngrok 실행

실행은 굉장히 간단하다. iterm과 같은 콘솔창을 연 다음, 밑과 같은 명령어를 작성하면 된다. port 번호에는 연결하고자 하는 서버 포트번호를 작성하면 된다.

예를 들어 spring에서 실행하면 기본 포트가 8080이므로, $ ngrok http 8080을 통해 https://975c-어쩌구저쩌구.ngrok-free.app 형태의 포트포워딩 된 주소를 얻을 수 있다.

$ ngrok http {port 번호}

2-1. ngrok session expire 해결

기본적으로 ngrok http 명령어를 통해 실행하면 session expire 항목이 있다. 기본으로 2시간이 주어지는데, 다음과 같은 과정을 통해 Expire가 없도록 실행할 수 있다.

1) https://dashboard.ngrok.com/ 으로 접속 후, 로그인(Github, Google 가능)

2) Your Authtoken으로 들어가서 token 복사 (https://dashboard.ngrok.com/get-started/your-authtoken)

3) $ ngrok http 8080 --authtoken={Auth-Token} 으로 ngrok 실행

auth token과 함께 실행을 하면 Session Expired 항목이 사라진 것을 확인할 수 있다.

Session Status                online
Account                       HEY (Plan: Free)
Update                        update available (version 3.3.4, Ctrl-U to update)
Version                       3.3.3
Region                        Japan (jp)
Latency                       40ms
Web Interface                 http://127.0.0.1:4040
Forwarding                    https://975c-어쩌구저쩌구.ngrok-free.app -> http://localhost:8080

Connections                   ttl     opn     rt1     rt5     p50     p90
                              39      0       0.00    0.00    2.88    61.82
이제 Forwarding 항목의 https://975c-어쩌구저쩌구.ngrok-free.app 주소를 통해 접근할 수 있다.

 


✅  Event subscriptions을 통해 Slack bot의 응답 설정

Event subscription을 이용해서 Slack bot을 mention(언급)한 경우에 '안녕하세요!'라는 말을 출력하도록 해보자. 지금은 인사 밖에 못하는 인사성이 바른 Slack bot에 불과하지만, 이후에 Service 코드를 변경시키면 다양한 기능을 가진 Slack bot으로 탈바꿈 할 수 있을 것이다.

 

1. Request URL 설정하기

Event subscriptions에서 subscribe 해놓은 event가 발생하면 Request URL로 요청을 하게 된다. 그럼 Service단에서 event에 알맞는 행동을 취하면 된다.

Slack에서 Request URL로 등록을 하려면 handshake를 통해 url_verification하는 과정이 필요하다.

URL을 작성하면 해당 URL로 challenge 파라미터와 함께 POST 요청을 하는데, 그에 대한 응답으로 challenge 파라미터를 그대로 돌려주면 된다.

https://api.slack.com/events/url_verification 를 보면 좀 더 자세히 설명이 되어 있다.

POST로 request 할 때 밑처럼 token, challenge, type 세 가지 값을 전달한다. 요청을 받아서 Request body 안의 challenge만 그대로 돌려주면 된다.

{
    "token": "Jhj5dZrVaK7ZwHHjRyZWjbDl",
    "challenge": "3eZbrw1aBm2rZgRNFdxV2595E9CY3gmdALWMmHkvFXO7tYXAYM8P",
    "type": "url_verification"
}
// Slack Request URL 검증용 DTO
data class ValidDto(val type: String,
                    val token: String,
                    val challenge: String)
// Slack Request URL 검증용
@PostMapping("/api/slack/event")
fun validateURL(@RequestBody req: ValidDto) : String{
    return req.challenge;
}

 

2. Controller, Service 코드 짜기

이전 포스팅에서 webhook을 통해 메세지를 전달하는 짧은 코드를 작성했었다. 그것을 이용해서 메세지를 전송해보자.

runCatching을 통해 만일 메세지 전송에 실패했을 경우 예외 처리를 하도록 했다.

@RestController
@RequestMapping("/api/slack")
class SlackController(private val slackService: SlackService) {
    // Bot event 발생 시 실행
    @PostMapping("/event")
    fun event() {
        slackService.sendMessageByWebhook();
    }
}
@Service
@Transactional(readOnly = true)
class SlackService {
    @Value("\${slack.webhook-url}")
    lateinit var webhookUrl:String;

    // slack bot의 message 전송
    fun sendMessageByWebhook(){
    	// Slack의 인스턴스를 얻음
        val slackInst = Slack.getInstance();

        runCatching {
        	// webhook을 통한 메세지 전송
            slackInst.send(webhookUrl, "{\"text\":\"안녕하세요!\"}");
        }
        	// 실패 시 예외처리
            .onFailure {
                err -> Logger.log.error(err.message);
                throw BusinessException(ErrorCode.SLACK_MESSAGE_DONT_SEND);
            }
    }
}

다음 포스팅에서는 언급 or 언급 + 인삿말을 한 경우에, {닉네임}님, 안녕하세요!라는 메세지를 보내게 하여 조금 더 챗봇스럽게 발전시켜보자.

 

 

참고 링크

https://techblog.lotteon.com/slack-bot-%EC%83%81%ED%98%B8-%EC%9E%91%EC%9A%A9-66596c262616

728x90