Elixir - Hedwig Responderを書く

この記事は [リクルートライフスタイル Advent Calendar 2016](http://qiita.com/advent-calendar/2016/recruitlifestyle) の3日目の記事です。

# はじめに

ビューティ開発Tの@shotatです。
今回は「Elixir製botフレームワークのHedwigのResponder作成」について書きます。
bot作成は普通にHubot使うのが一番楽なんですが、Elixirを書きたいがためにHedwigを使っていきます。

正直Elixir力は全然高くないので、間違った箇所があればご指摘いただけると幸いです。

# Hedwigについて

https://github.com/hedwig-im/hedwig

冒頭にも書きましたがElixir製のbotフレームワークです。

> Hedwig is a chat bot, highly inspired by GitHub's Hubot.

と、READMEに書いてある通り、書き方は非常にHubotに似ています。
Hubotを使ったことがあり、最低限のElixirの知識があれば基本的な対話の実装はできます。

Slackで動かすまでの日本語のチュートリアル的なものは以下の記事にまとめています。

[Elixir製Hubot風BotフレームワークのHedwigでSlack Botを作る - Memento memo.](http://shotat.hateblo.jp/entry/2016/10/16/005608)

# Responderを作る

Responderは「pingと呟くとPONGを返す」みたいなscriptのことです。
ドキュメントを読むとResponderはbot appの `lib/responders/xxxxx.ex` に直接書けばいいんですが、どうせなら外部モジュールに切り出してプラグインっぽくしたいので、今回はそれをやりました。

以下のHedwigのawesomeリポジトリを参考にしました。

https://github.com/enilsen16/awesome-hedwig

先月くらいまではResponders欄に2件いた気がするんですが、現時点で再確認したら半減していました。Hedwig界のエコシステムはまだまだ黎明期と言えるので、自作のモジュールを作成するチャンスです。

## 作ったもの

https://github.com/shotat/hedwig_github

![hoge.png](https://qiita-image-store.s3.amazonaws.com/0/99635/c5f5e6dd-1942-7012-e638-41d941c0d0cc.png)

`<bot_name>: github <repository_name>` とreplyを送るとマッチしたGitHubのリポジトリ + スター数を送ってくれます。極稀に役に立っています。

昨今においてはコンプライアンス遵守の重要性が叫ばれているため、なんとなくモザイク処理をしておきました。

## 作り方

とりあえず `mix new` でプロジェクトを作成します。

次に `mix.exs` にhedwigと必要なライブラリ系をつっこみます。 `tentacat` はGitHubのAPI wrapperです。

```mix.exs
  def application do
    [applications: [
      :hedwig,
      :tentacat,
    ],
  mod: {HedwigGithub, []}]
  end

  ...

  defp deps do
    [
      {:tentacat, "~> 0.5"},
      {:hedwig, "~> 1.0.0-rc.4"}
    ]
  end
```

`lib/<app_name>/responders/<responder_name>.ex` にResponderを実装していきます。

```github.ex
defmodule HedwigGithub.Responders.Github do
  use Hedwig.Responder

  @usage """
  hedwig: github <repository name> - Respond with GitHub url with stars count.
  """
  respond ~r/github(?: +)(.+)/i, msg do
    %{matches: %{1 => q}} = msg
    reply msg, search_repository(q)
  end

  def search_repository(repository_name) do
    client = Tentacat.Client.new
    params = %{
      q: repository_name,
      per_page: 1,
    }
    options = [ pagination: :none ]

    response = Tentacat.get "search/repositories", client, params, options
    case response do
    %{"incomplete_results" => false, "items" => [item | _]} ->
      parse_item(item)
    _ ->
      "?"
    end
  end

  defp parse_item(item) do
    %{"html_url" => url, "stargazers_count" => stars} = item
    "#{url} :star: #{stars}"
  end
end
```

あまり本筋とは関係ないですが、実装当時Tentacatのsearchモジュールが怪しい挙動をしていたので直接getを呼んでいます。Tentacat使った意味の7割くらいが消えています。

## Responderを使う側の実装

https://github.com/shotat/hedwig_github のREADMEに書きました。

とりあえず `mix.exs` で依存定義してReponderのリストに追加すれば動きます。

# まとめ

結構端折ってしまいましたが、Repositoryを覗いて頂ければ同じように作成できるかと思います。
Elixir自体書き味の良い言語なので、botあたりの簡単なところから初めつつ、こっそりプロダクトコードに突っ込んでいきたいです。

# おまけ: deploy周り

素のElixirだと対応してるPaaSが存在しないので、Dockerを使います。
`Dockerfile` の例は以下です。

```Dockerfile
FROM elixir

RUN mkdir /app
COPY . /app/
WORKDIR /app
RUN mix local.hex --force
RUN mix deps.get && mix compile

CMD ["mix", "run", "--no-halt"]
```

こんな感じでDockerコンテナで動くようにしておけばGAE等で簡単に運用できます。便利な世の中ですね。