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

Slackを一句BOTで風流に

おはようございます。プログラマーのhakatashiです。

普段はpixivコミックpixivノベルの開発を手伝っています。が、今回はそれとは全く関係ないSlackの話をします。

一句BOTとは

みなさんSlackは使っているでしょうか。普段から業務にプライベートにと幅広くSlackを使っていると、メンバーの何気ない一言に“一句”を感じることがあります。

f:id:hakatashi:20160705180600p:plain

風流ですね。

pixivにはこのような日常に潜む和の心を大切にする風雅なエンジニアが多いので、平安貴族よろしく日常会話や業務連絡に5・7・5の形の川柳を混ぜて会話します。とても優雅ですね。

ですが、上の画像のような完全に日常に溶け込んだ野生のステルス一句は、誰にも気づかれずにログの彼方へ流れていってしまうことも多いようです。そこで、Slackのメッセージから自動で一句を検出してReactionをつけるBOT、slack-ikkuを(1時間で)作りました。

github.com

f:id:hakatashi:20160705170036p:plain

このBOTを社内Slackに導入したことにより、人間がメッセージを読んで一句判定をする必要がなくなり、社員の負担が軽減され、QoLが上昇、業務成績は右肩上がりとなり、社内から笑顔の絶えない明るい職場となりました。みなさんもslack-ikkuを社内に導入して優雅な一句ライフをエンジョイしましょう。

使用した技術

というのはもちろん冗談ですが、これで終わるのも情けないので、今回のBOT制作に使用した技術を書き連ねていきます。

言語(処理系): Node.js

筆者はJavaScript大好きマンなので迷わずNode.jsを選択しました。

形態素解析: kuromojin

与えられた文章が5・7・5に分割できるかを判定するためには、文章の読みと単語の境界を解析しなくてはいけません。これを行う形態素解析器にはChaSenMeCabなどの実装が存在しますが、今回はkuromojinを使用しました。

kuromojinは、形態素解析器kuromojiの純JavaScript実装であるkuromoji.jsを平易なAPIでラップしたライブラリです。このライブラリを使用すると、以下のように書くだけで文字列を形態素に分割することができます。

const tokenize = require('kuromojin').tokenize;
 
tokenize('「スラック」と「一句」で韻を踏んでいる').then((tokens) => {
    console.log(tokens);
});

kuromojinはtextlintの日本語プラグインを作る際にもよく用いられるライブラリです。覚えておいて損はないでしょう。

一句判定

形態素解析した文章が一句であるか判定するには、与えられた形態素を適当なスパンで分割して5音・7音・5音の三句に分けられるかどうかを判定すればよいでしょう。ですが、それだけの条件だとあまり和の心を感じない文章も一句判定されてしまい、一部の風流人に怒られてしまいます。

f:id:hakatashi:20160705172052p:plain

これはよくありません。このような一句は文節の途中で句切れが入ってしまっているため、あまりよい一句とは言えません。このような文章を除外するために、形態素列から文節の切れ目を適当に検出します。この手法は私が過去執筆した機械学習で石川啄木の未完の短歌を完成させるに書いた方法を応用したものなので、詳しくはそちらを参照してください。

Unicode正規化: unorm

一句の文化がSlackに根付くにつれて、だんだんと過激派の一句も登場します。

f:id:hakatashi:20160705183634p:plain

もはや風流を通り越してシュールですが、このような一句にもUnicode正規化を行うことで対応できます。

const unorm = require('unorm');

unorm.nfkc('㌠ ㌢㍍ ㌕'); //=> 'サンチーム センチメートル キログラム'

正規化で多少の表記ゆれにも対応できるので、事前処理としてこのような処理を入れておいて損はないでしょう。ライブラリはNFKCを正しく実装しているならなんでも良いですが、一番DL数の多いunormを使用しました。

機能テスト: mocha, mockery, nock, mock-socket

正直これだけのアプリケーションなのでテスト書かなくてもよいと思ったのですが、仮にも業務用Slackで動かすものだというのと、HTTP通信のモックを書いてみたかったのがあったので、簡単に機能テストを書きました。Travis-CIで動いています。

  • mocha: テストフレームワーク。
  • mockery: npmモジュールをモックするライブラリ。今回は設定ファイルとWebSocketをモックするのに使った。同名のPHPライブラリとは別物だと思われる。
  • nock: HTTP通信をモックするライブラリ。Slack API への通信をモックした。
  • mock-socket: WebSocket通信をモックするライブラリ。Slack Real Time Messaging への通信をモックした。

軽い気持ちで書き始めたら想像以上にたくさんのモジュールをモックする必要があって大変でした。

メンテナンス: GreenKeeper

こんな一発芸的なBOTは作ったあと誰にもメンテされないのは目に見えているので、あとの管理作業は機械に任せてしまいましょう。テストを書いたあと、GreenKeeperを導入しました。

GreenKeeperはリポジトリ上のpackage.jsonを監視し、依存しているライブラリにアップデートがあったら自動でテストを走らせて、ビルドが壊れていないかどうか確かめてくれます。npmの依存モジュールのバージョン解決はデフォルトでメジャーバージョンの最新に追従するようになっているので、新しいコードが浸透しやすい代わりにこれがビルドが壊す原因にもなっています。Node.jsアプリケーションを作ったら、ちゃんとテストを書いてこういったサービスを導入することを検討したほうがいいかもしれません。

また、GreenKeeperは依存バージョンにpackage.jsonでカバーされてないアップデートがあった場合に、package.jsonを更新するプルリクエストを自動で発行してくれます。

f:id:hakatashi:20160705175639p:plain

プルリクを受け取ったら、テストが通っているのを確認してGitHub上のマージボタンを押すだけでpackage.jsonの更新が完了します。スマホからでも余裕です。テストが落ちてたら不健全なのでコードを修正しましょう。

次回予告

slack-ikkuの開発により社内Slackに一句文化を根付かせることに成功したhakatashi。実装が落ち着いたと思ったのも束の間、pixivのリードエンジニアから新たな難題が……。

f:id:hakatashi:20160705184956p:plain

果たしてhakatashiはこの無茶ぶり難問に立ち向かうことができるのか。次回へ続く?

おわりに

pixivでは和の心を大切にするクリエイティブで風流なエンジニアを募集しています。一緒にSlackで一句バトルを繰り広げましょう。アルバイトインターンもあるんだよ。