はじめてのGo - すぎゃーんメモ の続き?的な。
入門で終わらずにもうちょっとマトモに何か作れるようになりたい、ということで、以前にRubyで書いた「げんきだしてbot」をGoで書いてみた。
成果物のリポジトリはこちら。
「げんきだしてbot」とは
まぁUser streamからネガティヴな発言を拾ってきて「げんきだして!」とリプライを送るだけのTwitter botです。
ファイル分割
すべて同一のmainパッケージ内で書くけど、機能ごとにファイルを分けてみた。
twitter_client.go
: Twitter APIを叩いて認証したりStreamに接続したりなどconfig.go
: 認証して得たaccess tokenを保存したり読み込んだりgenki-bot.go
: 実際にStreamからTweetを読み取り返すべき発言を判定したりなどのロジックmain.go
: main関数。コマンド引数を読んでgenki-botを呼ぶだけ
typeやfuncの定義が色んなところに分散していても、どうせgodef
使って定義元にジャンプできるので問題ない。
設定値の保存/読取
前回は go-pit を使って設定値を読み書きしていたけど、簡単に自分で書いてみよう、と自前で用意してみた。
~/.config/
以下に適当にディレクトリ掘ってini形式ファイルでaccess tokenを保存。JSONやYAMLなどの構造化データもいいけど、Goで扱う場合 それほど複雑なデータ構造を必要としないならiniファイルくらいがお手軽で良いなーという印象を持った。
HOMEディレクトリの取得の際に最初はos/user
パッケージを使っていたけど、これがどうもクロスコンパイルするときの障壁になるようなので避けることにした。
Stream API
普通にOAuth認証でのリクエストまでは問題ないとして、Stream APIに接続したときのレスポンスを継続して受け続けるために。
などを参考に。
bufio.NewScanner(resp.Body)
で作った*bufio.Scanner
に対してScan
をかけることで 新たなデータが流れてきたときにbyte列を取得できるようだ。
ただこのときclient側から接続を切ろうとすると接続時のConnectionを維持している必要がありそうで、そのへんは面倒そうなのでここでは省略した。
正規表現
Ruby版では「疲れた」に対しては「疲れてるの?げんきだして!」と返信するが「お疲れさま」などには反応しないように/疲(?!れ(?:様|さ(?:ま|ん)))/
のような正規表現を使って判定している。
が、これをこのままGoで使おうとするとinvalid or unsupported Perl syntax: `(?!`
とエラーを吐かれてしまう。(?!pattern)
、"negative look-ahead"否定先読みというのかな、これはGoではサポートされていないらしい。注意。
テスト
いちおうどんな発言に対してどんな返信をすべきか、または返信すべきでないか、を確かめるためにテストも書いておいた。
あまり書きやすいかんじはしないけど、とりあえず書いておけばgo test
コマンドで簡単にテストを実行できるので安心できる。
Goroutineで簡単非同期
リプライを送る際にちょっとsleepで間を空けたり、またそのPOSTの送信で処理がブロックされる。Streamの受け取りは止めずにリプライ処理をするために、ここで初めてGoroutineを使用。
if mention != nil { go func() { time.Sleep(time.Second * time.Duration(rand.Int31n(5)+5)) tweet, err := bot.client.Mention(mention) if err != nil { log.Println(err) } else { log.Printf("tweeted: %s", tweet.Text) } }() }
Mention
メソッド自体は普通に返り値、エラーを戻す関数として定義しておいて、それを呼ぶ側をfunc(){...}()
で囲んでGoroutineとして呼んでやればいいかな、と。
ここでは並行処理同士での協調や調整は必要ないはずなのでChannelとか難しいことは考えない。
クロスコンパイル
Goは簡単に他の環境用のバイナリを生成できる(前述の通りos/userのようなcgoを使うパッケージを含んでいるとハマったりするけれど)、ということで試してみた。
Mac OS上で作ったバイナリがさくらVPSで動かしているCentOS上で動くことが確認できた。感動。
drone.ioやwerckerを使ってバイナリ配布とかも後でチャレンジしてみよう。
Windowsについては試せる環境もっていないので無視で…。
まとめ
まだまだ慣れないけれど 少しずつGoを書く楽しさは感じはじめてる。
やっぱりツールが充実していてすぐに文法エラーを検出できたり定義を調べたり出来るのは強いなーと思う。
もうちょい色んなパッケージやライブラリのソース読んだりして良い書き方とか身に付けたい。
次は何を作ってみようか