티스토리 뷰
1. 슬랙봇 DM 및 채널 메시지 연동하여 운영 자동화하기
[ 필요한 권한 세팅 ]
OAuth & Permissions에서 다음의 최소 OAuth Scope가 필요하다. 아래는 DM 연동을 위해 필요한 scopes이며, 필요에 맞게 추가해주도록 하자.
봇에게 보낸 메시지를 이벤트 형태로 받기 위해 Event Subscriptions에서 다음의 최소 권한이 필요하다.
채널에서 봇이 멘션되었을 경우에도 이벤트를 구독하려면 app_mention, private 채널에서 얘기된 메시지를 받으려면 message.groups, public 채널에서 얘기된 메시지를 받으려면 message.channels를 설정해주면 된다. 필요한 상황에 맞게 이벤트 구독 권한을 신청해주도록 하자.
RequestURL에는 봇에게 보낸 DM을 받아서 처리하기 위한 서버의 요청 주소를 넣어주면 된다.
[ 봇의 DM 활성화 ]
봇과의 DM을 활성화하기 위해서는 App Home의 Show Tabs 부분에서 다음의 체크 박스를 체크해주어야 한다.
위의 내용을 체크해주지 않으면 다음과 같이 “Sending messages to this app has been turned off.”가 뜨면서 DM이 막히므로 반드시 설정해주도록 하자.
[ 봇의 DM 활성화 ]
슬랙봇과 소켓 실시간 연동 방식도 가능하지만, 이번에는 DM이 보내졌을 때 이벤트를 AP로 구독하고, 이를 바탕으로 응답하는 형태로 개발하고자 한다. 먼저 다음과 같이 의존성을 추가해준다.
// slack 필수 의존성
implementation("com.slack.api:slack-api-client:1.40.0")
implementation("com.slack.api:slack-app-backend:1.40.0")
// slack kotlin 편의 의존성
implementation("com.slack.api:slack-api-model-kotlin-extension:1.40.0")
implementation("com.slack.api:slack-api-client-kotlin-extension:1.40.0")
이벤트를 구독하기 위한 API 스펙은 공식 문서에 정리되어 있다. 이를 바탕으로 먼저 우리가 위의 설정에서 RequestURL로 설정해둔, 요청을 받을 컨트롤러를 작성한다.
@RestController
@RequestMapping("/api/v1/slack")
class SlackController(
private val processSlackEventService: ProcessSlackEventService,
) {
@PostMapping("/subscript-event")
fun subscriptSlackEvent(
@RequestBody request: SlackEventRequest,
): ResponseEntity<Unit> {
// 봇이 보낸 메시지를 응답할 경우 `봇의 답변 -> 봇의 답변 -> 봇의 답변` 무한 루프 생길 수 있으므로 막아준다.
if (request.isMessageFromBotSelf()) {
return ResponseEntity.ok(Unit)
}
if (request.event != null) {
processSlackEventService.process(ProcessSlackEventService.Input(
eventChannel = request.event.channel,
eventText = request.event.text,
messageTs = request.event.eventTs,
))
}
return ResponseEntity.ok(Unit)
}
}
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class SlackEventRequest(
val token: String,
val teamId: String?,
val apiAppId: String?,
val event: Event?,
val type: String?,
val eventId: String?,
val eventTime: Long?,
val authedUsers: List<String>?,
) {
fun isMessageFromBotSelf(): Boolean {
return event?.user == BOT_MEMBER_ID
}
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
data class Event(
val type: String,
val user: String?,
val text: String = "",
val ts: String,
val channel: String,
val eventTs: String,
)
companion object {
const val BOT_MEMBER_ID = "U07ZH1WMK25"
}
}
여기서 BOT_MEMBER_ID는 봇의 프로필에서 확인할 수 있다.
이후에는 다음과 같이 후처리할 로직을 상황에 맞게 작성해주도록 하자.
@Service
class ProcessSlackEventService {
data class Input(
val eventChannel: String,
val eventText: String,
val messageTs: String,
)
private val slack: Slack by lazy {
Slack.getInstance()
}
fun process(input: Input) {
// 이벤트 처리
process()
// 메시지에 대한 스레드로 답변하기
sendMessageToThread(input, "처리완료")
}
fun process() {
}
private fun sendMessageToThread(input: Input, text: String): ChatPostMessageResponse {
return slack.methods().chatPostMessage { chat ->
chat.token(TOKEN)
.channel(input.eventChannel)
.threadTs(input.messageTs)
.text(text)
}
}
companion object {
const val TOKEN = "token"
}
}
이러한 방식으로 슬랙봇을 통한 자동화로 많은 운영 공수를 줄일 수 있을 것이다. 필요에 따라 DM 외에도 슬랙봇이 멘션되었을 경우, private 채널에서 메시지가 온 경우, public 채널에서 메시지가 온 경우 등 다양하게 활용할 수 있으니 적극 활용하도록 하자.
또한 슬랙봇을 통한 자동화를 많이 하다 보면 로직이 복잡해질 수 있는데, 이러한 경우에는 디자인 패턴을 적용하여 새로운 기능 추가를 용이하게 할 수 있으니 참고하도록 하자.
'나의 공부방' 카테고리의 다른 글
[개발서적] 구글 엔지니어는 이렇게 일한다 핵심 내용 정리 및 요약 (5) | 2024.12.10 |
---|---|
[Tool] 유용한 MacOS 앱 및 크롬 익스텐션(Chrome Extensions) 정리 (7) | 2024.10.29 |
[개발서적] 단위 테스트(Unit Testing) 핵심 내용 정리 및 요약 (16) | 2024.10.15 |
[Architecture] 헥사고날 아키텍처를 통한 의미 수준과 구현 수준에 대한 이해(semantic and implementation level with hexagonal architecture) (24) | 2024.09.24 |
[개발서적] 스트리트 코더(Street Coder) 핵심 내용 정리 및 요약 (10) | 2024.08.27 |