LoginSignup
1
2

More than 5 years have passed since last update.

Scalaの勉強兼ねて、ChatOpsの実践導入をしたみた

Last updated at Posted at 2016-07-06

最近Scalaをやることになって
勉強のためにと前から興味のあったChatOpsに挑戦してみたら、これが割りと良い題材だった。

Botにさせる処理によっては、
定型文をBotにしゃべらせるだけの簡単なものから
APIたたいて返ってきたjson解析したり、
sshのライブラリを使ってサーバに入って何か処理したりと
基礎的なことをいろいろできるので、新しい言語を勉強したいという方には良いかもしれません。

ソース

slack-scala-clientというライブラリがあったので、今回はこれを使用。

Main.scala
import slack.models._

object BotRunner extends App {
  SlackBot.client.onEvent {
    // 人からのメッセージがきたら処理
    case e: Message => SlackBot.hears(e)
    case _          =>
  }
}

フックになる、イベントが何種類か用意されている。
- 人からのメッセージがきたら
- Botからのメッセージがきたら
- チャンネルに人が追加されたら
などなど。詳しくはココ

現状は人が特定のコマンドを打ったら反応する機能しかないので、case でわけているものの処理は1つ。

SlackBot.scala
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は予め取得して、環境変数で設定しておいてください。

BotProcess.scala
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マスターへの道のりは長そう

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2