読者です 読者をやめる 読者になる 読者になる

きょこみのーと

元六本木でGo書いてました。今はVRでGo書いてます。

GoConでGoで作った拡張しやすいSlack botについてLTしてきた

拡張しやすいはず・・・

最初はkyokomi/gomaの話にしようかなと思ったけど、LT駆動開発キメたろうと思って、slack bot作ることにした。

LTであまり時間無く、ざくっと概要とデモ見せる感じで終わったので、exampleとか詳細を書こうと思います。

実際に実行するbotのサンプル

tokenの取得

slack botのtokenは以下で取得してください。

f:id:kyokomi:20150621211458p:plain

f:id:kyokomi:20150621211555p:plain

exampleを実行する

<token>は、一つ前の節で取得したもの。環境変数SLACK_BOT_TOKENでも可。

$ go get github.com/kyokomi/slackbot
$ cd $GOPATH/src/github.com/kyokomi/slackbot/example
$ go run main.go -token <token>

これで実行終わり。起動時のログはこんな感じ。

f:id:kyokomi:20150621212112p:plain

Slackで発言してみる。(botが居る部屋で)

f:id:kyokomi:20150621212144p:plain

echoプラグインが動いていることがわかる。

pluginの仕組み

_ importするだけでpluginを追加できているのは、この辺をみればわかる。

init()でplugins.AddPluginを読んでいる箇所

slackbot/echo.go at v1.1 · kyokomi/slackbot · GitHub

func init() {
    plugins.AddPlugin(pluginKey("naruhodoMessage"), NaruhodoMessage{})
}

SlackのMessage受信時に呼び出される処理している箇所

pluginsに突っ込まれたものを順番に呼び出している。

slackbot/plugin.go at v1.1 · kyokomi/slackbot · GitHub

func ExecPlugins(ctx context.Context, message string) {
    for _, p := range plugins {
        ok, m := p.CheckMessage(ctx, message)
        if !ok {
            continue
        }

        next := p.DoAction(ctx, m)
        if !next {
            break
        }
    }
}

実は、とても原始的な実装。

割とウケた「なるほどですぞ」プラグインのソースは以下だけ。(ウケてよかった・・・)

package naruhodo

import (
    "math/rand"
    "strings"
    "time"

    "github.com/kyokomi/slackbot/plugins"
    "golang.org/x/net/context"
)

type pluginKey string

var naruhodoList = []string{
    "なるほどなるほどですぞ!",
    "なるほど!",
    "なるほど?",
    "なーるほど!",
    "それはなるほどですね",
    "なるほど!!",
    "なるほど!!!",
}

var rd = rand.New(rand.NewSource(time.Now().UnixNano()))

func init() {
    plugins.AddPlugin(pluginKey("naruhodoMessage"), NaruhodoMessage{})
}

type NaruhodoMessage struct {
}

func (r NaruhodoMessage) CheckMessage(ctx context.Context, message string) (bool, string) {
    return strings.Index(message, "なるほど") != -1, message
}

func (r NaruhodoMessage) DoAction(ctx context.Context, message string) bool {
    idx := int(rd.Int() % len(naruhodoList))
    plugins.SendMessage(ctx, naruhodoList[idx])
    return false // next ng
}

var _ plugins.BotMessagePlugin = (*NaruhodoMessage)(nil)

ほとんどgenerateしたコードのまま。

  • CheckMessageでなるほどという文字がmessageに含まれていたらtrueを返す
  • CheckMessagetrueを返した時にDoActionが実行される
  • DoActioncontextに突っ込んでいたSendMessageを使ったSlackへ返信する

たったこれだけ。簡単なのでぜひ使ってみてください! そしてプラグイン作ったら報告してくれると嬉しいです!! (特定の環境に依存してないプラグインじゃなければ、PullRequestでもOK)