最近Scalaをやることになって
勉強のためにと前から興味のあったChatOpsに挑戦してみたら、これが割りと良い題材だった。
Botにさせる処理によっては、
定型文をBotにしゃべらせるだけの簡単なものから
APIたたいて返ってきたjson解析したり、
sshのライブラリを使ってサーバに入って何か処理したりと
基礎的なことをいろいろできるので、新しい言語を勉強したいという方には良いかもしれません。
ソース
slack-scala-clientというライブラリがあったので、今回はこれを使用。
import slack.models._
object BotRunner extends App {
SlackBot.client.onEvent {
// 人からのメッセージがきたら処理
case e: Message => SlackBot.hears(e)
case _ =>
}
}
フックになる、イベントが何種類か用意されている。
- 人からのメッセージがきたら
- Botからのメッセージがきたら
- チャンネルに人が追加されたら
などなど。詳しくはココ
現状は人が特定のコマンドを打ったら反応する機能しかないので、case でわけているものの処理は1つ。
import akka.actor.ActorSystem
import slack.rtm.SlackRtmClient
import slack.models.Message
object SlackBot {
implicit val system = ActorSystem("slack")
implicit val ec = system.dispatcher
private[this] val token = sys.env("SLACK_BOT_TOKEN") // 予め環境変数にSlackBotのTokenを設定しておく
val client = SlackRtmClient(token)
val selfId = client.state.self.id
// メッセージを受けた時に行う処理。優先順位(高い -> 低い(IdleTalk))
val processes = Seq(Deploy,
SparkAppList,
SparkAppSelect,
ZeppelinRestartor,
IdleTalk)
// 人からのメッセージを受けた時に行う処理
def hears(message: Message) = {
// キーワードに引っかかるものだけ実行(最初にヒットしたもののみ)
processes.filter(_.check(message)).headOption.map(_.run(message))
}
}
Botオブジェクトでは、コマンドの登録と
メッセージ内にキーワードが含まれているかチェックし、含まれていたらその処理を行うようになっている。
SLACK_BOT_TOKENは予め取得して、環境変数で設定しておいてください。
import slack.models.Message
// 基底クラス
abstract class BotProcess extends PermissionConfig {
protected[this] val matchPattern: String // 処理に入るためのコマンド(正規表現も可)
// メッセージが
def check(message: Message): Boolean = {
message.text matches matchPattern
}
// checkが通った時に行う処理
def run(message: Message)
}
// 派生クラス
object TestProcess extends BotProcess {
override val matchPattern = s"""^<@${SlackBot.selfId}>: test"""
override def run(message: Message) = {
// Botにしゃべらせる
SlackBot.client.sendMessage(message.channel, "テストだよ")
}
}
処理クラスの内容としては
- check: メッセージ内にキーワードが含まれているかチェック
- run: checkの結果がtrueだった時に行う処理
新しいコマンドを追加するときは、BotProcessを継承して、
どんなメッセージがきたら反応するのかを示す matchPattern と
どんな処理をさせるかの run をoverrideして変更する。
matchPatternの最初に<@${SlackBot.selfId}>
がついているのは
Botに対して、Mentionしてきた時にだけ反応させたいため。
こうしておかないと、誰かが何かメッセージを書く度に反応してしまう。
実際に動かしているものは、チャンネル毎に打てるコマンドの管理だったり
ヘルパー関連の処理などがあるものの、個人で作る分にはあまり必要ないので割愛します。
実装済みコマンド
デプロイ
CircleCIから最新のbuild済みjarをSparkクラスタの特定のパスに配置して、アプリケーションの再起動を行う。
sshライブラリを使って、Sparkのサーバへ入ってexec関数でコマンドを実行していたが
sudoコマンドを使いたいときは、exec ではなく execPTY にしないといけないことに気づかず結構悩んだ。
jarの管理(バージョンのリスト、バージョン切り替え)
デプロイで再起動した結果、万が一正常に動かなかった時用に前のバージョンにも戻れるように作った。
Zeppelinの再起動
一時期、Zeppelinサーバの調子が定期的に悪くなるという現象が起こっていたため
サーバに入らずとも再起動ができるようにした。
人工知能ボットAPIを使った雑談
User Localさんの人工知能ボットAPIを前に応募していて使えるようになったので組み込んでみた。
雑談ができたところで特に仕事上メリットはないけど、楽しい。
Gitのプルリク作成、イシューの作成
GitAPIも試してみたかったので入れてみた。
ヘルプ
Botに対して、どんなコマンドを打つとどんな処理をしてくれるのか、教えてくれるもの。
コマンドが増えてくるとこれがないと覚えてられないので重要。
まとめ
新しい言語を勉強するとき、ただただ参考書を読んでもなかなか身につかないし
かと言って何を作ればいいのか…という人はChatOpsおすすめです。
それにしてもScala難しい…今までがずっとPythonをやっていたからというのもあるのだろうけど。
Right型やLeft型という直感ではわからない型があったり
JArrayという型のものに対して、Arrayの時と同じようにforeachとか使うとそんな関数ないよってエラーになったり
JStringはいいけどStringはダメみたいな関数があったりと
やたら型が多い割に、型に厳しいから覚えることが多くて大変。
Scalaマスターへの道のりは長そう