Go의 간단한 Slack 봇 - The Bot

해를 찬양하다



Real Dark Souls Slack 봇이 여기에서 시작됩니다.



내 친구 몇 명과 나는 일반적인 커뮤니케이션 및 기타 말도 안되는 여러 가지 용도로 사용하는 자체 Slack 인스턴스를 가지고 있습니다. 때때로 그것은 비디오 게임에 대한 토론으로 바뀝니다. 자주 등장하는 게임 중 하나는 Dark Souls입니다. 그래서 제가 Go에서 Slack 봇을 빌드하는 방법을 살펴보고 있을 때 "Solaire of Astora"와 일반적인 Dark Souls 후렴구인 "Praise the Astora"를 기반으로 봇을 만들기로 했습니다. 해!"

이것은 nlopes의 Slack Go 패키지를 사용하는 매우 간단한 프로젝트입니다. 대화 봇이 아니며 머신 러닝 또는 "AI"구성 요소를 사용하지 않습니다. 단순한 정규 표현식에 불과합니다. 계속 읽고 마법을보십시오.

모든 Go 코드의 핵심 - 패키지를 기본으로 선언합니다. 여기에서 표준 Go 라이브러리와 Slack 패키지에서 가져옵니다.

package main

import (
  "fmt"
  "os"
  "regexp"
  "strings"

  "github.com/nlopes/slack"
)

다음으로 getenv() 함수는 설정되었는지 확인하려는 환경 변수의 문자열을 가져와서 반환합니다. 환경 변수가 설정되어 있지 않으면 당황할 것입니다. 최신 버전의 봇이 사용하는 모듈 버전Github을 사용할 수 있지만 이 코드를 가능한 한 간단하게 유지하여 더 쉽게 다룰 수 있도록 하고 싶었습니다. 결과 봇이 Google Compute Engine에서 호스팅되고 Slack 토큰을 하드 코딩하고 싶지 않기 때문에 환경에서 가져오고 있습니다. 현재 구성 방법은 향후 게시물에서 다루겠습니다.

func getenv(name string) string {
  v := os.Getenv(name)
  if v == "" {
    panic("missing required environment variable " + name)
  }
  return v
}

봇이 너무 단순하고 테스트를 수행하지 않기 때문에 다른 기능을 설정하지 않고 바로 main() 로 이동합니다. 먼저 환경에서 Slack 봇의 토큰을 가져오고 이를 사용하여 봇을 인스턴스화하고 시작합니다.

func main() {
  token := getenv("SLACKTOKEN")
  api := slack.New(token)
  rtm := api.NewRTM()
  go rtm.ManageConnection()

우리 봇의 핵심은 다음 루프입니다. 일반적으로 하나의 큰 코드 블록에 표시하고 싶은 것보다 조금 더 큽니다. 그러나 분해하면 따라가기가 약간 어려울 수 있습니다. 중요한 부분을 조금 빼서 좀 더 자세히 살펴보겠습니다. 기본적으로 우리는 시작 시 잘못된 자격 증명, Control-C 또는 SIGHUP에 의해서만 중단되는 무한 루프를 시작하고 있습니다. 우리의 for 루프를 사용하면 들어오는 이벤트를 반복하고 주시할 수 있습니다. 하나를 보면 유형을 확인하고 slack.MessageEvent와 일치하는 경우 "무거운 리프팅"을 시작합니다. 우리가 주시하고 있는 다른 가능한 이벤트는 앞서 언급한 인증 오류 또는 실시간 메시징(RTM) 오류입니다.

Loop:
  for {
    select {
    case msg := <-rtm.IncomingEvents:
      fmt.Print("Event Received: ")
      switch ev := msg.Data.(type) {

      case *slack.MessageEvent:
        info := rtm.GetInfo()

        text := ev.Text
        text = strings.TrimSpace(text)
        text = strings.ToLower(text)

        matched, _ := regexp.MatchString("dark souls", text)

        if ev.User != info.User.ID && matched {
          rtm.SendMessage(rtm.NewOutgoingMessage("\\[T]/ Praise the Sun \\[T]/", ev.Channel))
        }

      case *slack.RTMError:
        fmt.Printf("Error: %s\n", ev.Error())

      case *slack.InvalidAuthEvent:
        fmt.Printf("Invalid credentials")
        break Loop

      default:
        // Take no action
      }
    }
  }
}

메시지 이벤트 사례를 자세히 살펴보겠습니다. 먼저 rtm.GetInfo()를 호출하여 봇 연결에 대한 정보를 가져옵니다. 실제로 필요하지는 않지만 이 경우 단순히 봇 ID가 메시지를 트리거하는 ID가 아닌지 확인하는 데 사용됩니다. 트리거 텍스트를 말하지 않기 때문에 어쨌든 트리거하지 않을 것입니다.

봇 정보가 있으면 ev.Test를 사용하여 이벤트의 텍스트 본문을 가져옵니다. 그런 다음 텍스트는 선행 및 후행 공백을 제거하기 위해 잘립니다(다시 말하지만 실제로 필요하지는 않지만 깔끔하게 유지하기 위해 수행됩니다). 그런 다음 텍스트가 소문자로 변경됩니다. 마지막으로 결과 문자열이 트리거 문구와 일치합니다. 따라서 "dark souls"또는 "Dark Souls"(또는 위아래의 다른 조합)라는 단어는 항상 일치해야 합니다.

일치하는 항목이 있으면 새 발신 메시지가 생성되고 트리거 텍스트가 발견된 채널로 설정됩니다.

      case *slack.MessageEvent:
        info := rtm.GetInfo()

        text := ev.Text
        text = strings.TrimSpace(text)
        text = strings.ToLower(text)

        matched, _ := regexp.MatchString("dark souls", text)

        if ev.User != info.User.ID && matched {
          rtm.SendMessage(rtm.NewOutgoingMessage("\\[T]/ Praise the Sun \\[T]/", ev.Channel))
        }

\[T]/ Praise the Sun \[T]/



더 많은 문구에 응답하고 응답을 혼합하도록 봇을 쉽게 확장할 수 있지만 가능한 한 단순하게 유지하는 아이디어가 마음에 듭니다.

봇을 실행하려면 go run를 호출하거나 명령에 Slack 토큰을 포함하도록 빌드하면 됩니다.

SLACKTOKEN=slacktoken go run main.go

전체 코드 목록

package main

import (
  "fmt"
  "os"
  "regexp"
  "strings"

  "github.com/nlopes/slack"
)

func getenv(name string) string {
  v := os.Getenv(name)
  if v == "" {
    panic("missing required environment variable " + name)
  }
  return v
}

func main() {
  token := getenv("SLACKTOKEN")
  api := slack.New(token)
  rtm := api.NewRTM()
  go rtm.ManageConnection()

Loop:
  for {
    select {
    case msg := <-rtm.IncomingEvents:
      fmt.Print("Event Received: ")
      switch ev := msg.Data.(type) {

      case *slack.MessageEvent:
        info := rtm.GetInfo()

        text := ev.Text
        text = strings.TrimSpace(text)
        text = strings.ToLower(text)

        matched, _ := regexp.MatchString("dark souls", text)

        if ev.User != info.User.ID && matched {
          rtm.SendMessage(rtm.NewOutgoingMessage("\\[T]/ Praise the Sun \\[T]/", ev.Channel))
        }

      case *slack.RTMError:
        fmt.Printf("Error: %s\n", ev.Error())

      case *slack.InvalidAuthEvent:
        fmt.Printf("Invalid credentials")
        break Loop

      default:
        // Take no action
      }
    }
  }
}


그리고 우리는 그것을 가지고 있습니다!



다음 시간에는 "더 나은"배포 방법을 위해 봇을 배포하고 준비하는 방법을 살펴보겠습니다. 그때까지...



이 게시물을 즐기십니까?


How about buying me a coffee?

좋은 웹페이지 즐겨찾기