SlackクローンのMattermostを使ってみる - 外部連携編 -（WebHooks、Hubot）

[前回の記事](http://qiita.com/terukizm/items/4a4016d8ec5a21856e4f)では、導入と基本的な使い方を中心にご紹介しました。

ここでは応用編として、外部連携機能についてまとめておきます。

# Integrations (WebHooks / Slash Commands)

Mattermostには"Integrations"と呼ばれる機能があり、外部システムとの連携を行うことができます。

* [Incoming Webhooks — Mattermost 2.0 documentation](http://docs.mattermost.com/developer/webhooks-incoming.html)
* [Outgoing Webhooks — Mattermost 2.0 documentation](http://docs.mattermost.com/developer/webhooks-outgoing.html)
* [Slash Commands — Mattermost 2.0 documentation](http://docs.mattermost.com/developer/slash-commands.html)

Slackにも同様の機能があり、ドキュメントでは["Slack-compatible"](http://docs.mattermost.com/developer/webhooks-outgoing.html#slack-compatibility)ということなのですが、どこまで互換性があるのかはわかりませんでした。

Request/Response形式はたしかにそれっぽいのですが、試してみないと正直なんとも…という印象です。是非皆さま、人柱となって知見を…

* 参考: 
  * [Incoming Webhooks | Slack](https://api.slack.com/incoming-webhooks)
  * [Outgoing Webhooks | Slack](https://api.slack.com/outgoing-webhooks)
  * [Slash Commands | Slack](https://api.slack.com/slash-commands)

## 0. 事前準備

`「System Console」→「Service Settings」`で、各機能を有効化する必要があります。（デフォルトでは無効）

![ちちち.png](https://qiita-image-store.s3.amazonaws.com/0/50184/7a76522d-c83d-ab9c-9c60-b90543e9ae93.png)

各項目はこんな感じでしょうか。

**・Enable Incoming Webhooks:**
**・Enable Outgoing Webhooks:**
**・Enable Slash Commands:**
>   各機能をそれぞれ有効にする。

**・Enable Integrations for Admin Only:**
> System Admin / Team Admin 権限のユーザのみ、Integration機能を設定できるようにする。

**・Enable Overriding Usernames from Webhooks and Slash Commands:**
**・Enable Overriding Icon from Webhooks and Slash Commands:**
> Integration機能経由でユーザ名／ユーザアイコン画像を変更できるようにする。
>   例: Webhooksを利用したBotのレスポンスは別のアイコンに差し替えて表示させる、など。

あと、

**・Enable Insecure Outgoing Connections:** 

> https環境の場合にいわゆる「自己署名証明書」を許可するか？ (デフォルトはfalse)
> httpsでの動作を試していないのですが、自己署名証明書を利用する場合はtrueにしておく必要があるのかも？

というのもあるみたいです。httpsで利用する場合は気をつけてください。

## 1. Incoming WebHooks

その名の通り、「他システム」→「Mattermostサーバ」という方向で作用するWebHookです。
「他システムからMattermostに通知する」のに使います。

たとえば、

* HubotがMattermostに発言する
* Jenkinsのビルドが終わったら、Mattermostのタイムラインに通知する
* cron等を設定して定期的にお知らせを流す
* 監視ツールと連携して、アラートが上がったらMattermostのタイムラインに通知する

といった使い方ができます。

### 設定方法

`「Account Settings」→「Integrations」`から、`「Incoming Webhooks」`を選択。

発言させたいチャンネル（部屋）の名前を選択して「Add」を押下すると、Incoming Webhook が生成されます。

このときデフォルトで発言に利用されるのは、このWebhooksを作成したユーザのアカウントになるので注意してください。
（override機能を有効にすることでこの挙動を回避させることもできます）

<img src="https://qiita-image-store.s3.amazonaws.com/0/50184/f636e5ca-d395-eb58-c89b-609e7165c648.png" width="1000px">


### 動作確認

curlコマンドで動作確認できます。

```
$ curl -i -X POST -d 'payload={"text": "Hello, this  some text."}' http://standalone-mattermost.vagrant.local/hooks/t9cqza13btb5fxkcf66ciex97a
HTTP/1.1 200 OK
Server: nginx/1.8.1
Date: Thu, 03 Mar 2016 07:44:50 GMT
Content-Type: text/plain
Content-Length: 2
Connection: keep-alive
X-Ratelimit-Limit: 10
X-Ratelimit-Remaining: 9
X-Ratelimit-Reset: 1
X-Request-Id: c6o3o6kps38ttmaaam7errbtko
X-Version-Id: 2.0.0.1456990591
```
![response.png](https://qiita-image-store.s3.amazonaws.com/0/50184/9cac5ba1-cc1b-46ec-6f5a-87e1ec74cd7f.png)


## 2. Outgoing WebHooks

`「Account Settings」→「Integrations」`から、`「Outgoing Webhooks」`を選択。
こちらは逆に、「Mattermostサーバ」→「他システム」という方向のWebHookになります。

### 設定方法

<img src="https://qiita-image-store.s3.amazonaws.com/0/50184/f810f9ac-c218-f4c6-4c0d-98c6f1e16fd9.png" width="1000px">

**・Channel:**
>   後述の "Trigger Words" による監視対象とするチャンネル（部屋）名。入力しない場合は、全てのチャンネルが対象になります。

**・Trigger Words:**
>   Outgoing Webhooksを呼び出すキーワード。この例だと誰かが "Jenkins氏～" という文字列を含む発言をした場合に、以下のCallback URLsが呼び出されます。

**・Callback URLs:**
>   呼び出す他システムのURL。HubotだったらHubotの稼働するエンドポイントを指定。
>   POSTリクエストなので、設定例のようにJenkinsのビルド用URLを指定するとそのまま該当JOBのビルドが走ります。(認証が不要な場合に限る)

<img src="https://qiita-image-store.s3.amazonaws.com/0/50184/0eae9c15-a3a5-db87-8cb1-608e9a80272e.png" width="1000px">


### 動作確認


払い出されたTokenを連携先のシステム(ここでは後述のHubotを指定しています)に設定してください。

Mattermostで上記の"Trigger Words"で指定した文字列を発言し、指定したURLに対してリクエストが飛ぶことを確認してください。
疎通確認にはtcpdump等でパケットをキャプチャするのが良いと思います。

以下のようにキーワードは **「完全一致、スペースで句切られていないと反応しない」** ので注意してください。

![シンディー.png](https://qiita-image-store.s3.amazonaws.com/0/50184/c9e78f8d-c2dd-24d7-723d-cec65b1598d1.png)

なお、登録したWebHookを編集することはできないので(v2.0.0時点)、修正したい場合はWebHookを一旦削除して作りなおさないといけないようです。
tokenも再生成されるので、大変面倒ですが連携システム側のtokenの値も忘れずに更新しておきましょう… [^1]

## 3. Slash Commands

おそらく「拡張コマンドとして外部APIを叩いてその結果をMattermostで通知」みたいなことができるのだと思います。
個人的にあまり使ったことがないので便利さがわからず、試せておりません…

Slackで拡張コマンドとかを使いこなしていた方いらっしゃいましたら、是非試してみてください…


# Hubot連携

公式でも紹介されている [hubot-mattermost](https://www.npmjs.com/package/hubot-mattermost) というadapterを利用して、Hubotとの連携ができます。
ただ、いくつかハマった部分がありましたので、ご確認いただければと。

## 注意点

### :warning: Mattermost 2.0系に対応していない？

hubot-mattermostの最新版(1.0.7)が `2015/12/20` リリース、[Mattermost 2.0](https://www.mattermost.org/open-source-localizable-slack-alternative-mattermost-2-0/)のリリースが`2016/02/16`のため、最新のMattermostに対応できていない？可能性があります。
比較的新しい類似プロダクトに[hubot-matteruser](https://www.npmjs.com/package/hubot-matteruser)があるので、httpsの場合はこちらの利用も視野に入れてみてください。

私のHubot力不足、CoffeeScript力不足などもあると思うのですが、~~以下のサンプルでは`robot.hear`しか動作確認ができませんでした。(`robot.respond`が動かない)~~

```coffeescript
module.exports = (robot) ->
  robot.hear /badger/i, (res) ->
    res.send "Badgers? BADGERS? WE DON'T NEED NO STINKIN BADGERS"

  robot.respond /open the pod bay doors/i, (res) ->
    res.reply "I'm afraid I can't let you do that."

  robot.hear /I like pie/i, (res) ->
    res.emote "makes a freshly baked pie"

  robot.respond /PING$/i, (msg) ->
    msg.send "PONG!"
```


> 2016/03/03 23:00 補足:

「2.0でもrespond対応してるよ」というメッセージ頂いてちょこちょこいじってみました。

以下のように、Outgoint Webhookに「Trigger-Words="matter-bot"」として、「$MATTERMOST_HUBOT_USERNAME="matter-bot"」とすると、たしかにrespondに反応するのですが、

![bbbb.png](https://qiita-image-store.s3.amazonaws.com/0/50184/cfe5656a-22c3-d4c0-4a68-0093ff75b2cb.png)
![abcde.png](https://qiita-image-store.s3.amazonaws.com/0/50184/854b6092-903a-9480-0cc8-1809485ee82a.png)

`@matter-bot`で反応していません。多分こちらの形式で確認していたので、動かないと思い込んでしまったのだと思います。。。

Hubot側でtcpdumpをかけながら確認すると、どうも **「Outgoing WebhookのTrigger-Wordsは先頭文字列、かつ完全一致でないと反応しない」** という仕組みで、その後は入力された文字列がそのままの形で送り先(hubot)に渡され、処理されているのではないかと推測されます。

（`$MATTERMOST_HUBOT_USERNAME`もHubotからMattermostへのレスポンスの表示名に使われるだけかと思っていたのですが、`./bin/habot --name $MATTERMOST_HUBOT` として使われているような感じもあります…この辺Hubot詳しい方教えてください…`）

>> 2016/03/05 補足: コメントでご指摘いただいているのでそちらもあわせてご参照いただければと。

結論として、`matter-bot`および`@matter-bot`の両方でHubotに応答させるには、以下のようにする必要がありました。

1. Outgoint WebHook にキーワードとして`matter-bot` `@matter-bot`を両方登録
![111.png](https://qiita-image-store.s3.amazonaws.com/0/50184/6cafe246-eb0a-c410-3650-2320f8c356fb.png)

2. $MATTERMOST_HUBOT_USERNAME="matter-bot"として、","区切りで上記２つのtokenを併記

```~/.bash_profile
export MATTERMOST_TOKEN=coe6s91nkbgu7b63uqg7qqu3xo,9gha7oj8w3b1tf9r9zzwkihbcy
export MATTERMOST_HUBOT_USERNAME=matter-bot
```

ご参考までに…

## :warning: Mattermostと同じサーバ上でHubotを動かすのはNG

[ここにしっかり書いてあった](https://www.npmjs.com/package/hubot-mattermost#installation)のですが、HTTPとOutgoing Webhookの仕組み上、**リクエスト送信側(Mattermost)が利用するポート番号と、リクエスト受信側(Hubot)が利用するポート番号を同一にできない**ので、Mattermostと同じサーバ上でHubotを動かしても動作しません。[^2]

サンプルではVagrantを使ってHubot専用のインスタンスを作成していますが、実際にChatOps的な運用をする場合は、

* Mattermost + GitLabサーバ
* Hubot + Jenkinsサーバ

と分けるなどの配慮が必要になるのかな、と思います。

## インストール、導入

こちらもVagrantのスクリプトにまとめました。ご利用ください。
[terukizm/hubot-mattermost-vagrant: Hubot + Mattermost Sample for Vagrant (VirtualBox + CentOS 7.2.x)](https://github.com/terukizm/hubot-mattermost-vagrant)

比較的新しいnode.jsが要求されるみたいなのでnvmを使い、Hubotはポート18080で起動するようにしています。


## Mattermost に WebHookを設定

#### Incoming Webhook

![inc_1.png](https://qiita-image-store.s3.amazonaws.com/0/50184/f17d832b-17ca-af93-a5af-e98e00adb07f.png)

#### Outgoing Webhook [^3]

![out_2.png](https://qiita-image-store.s3.amazonaws.com/0/50184/84c5ac69-257b-ab08-32d8-30c53d3a388f.png) 

* Incoming URL
* Outgoing Token

この２つが必要になるので、控えておいてください。

## WebHook設定を反映

vagrant内の `~/.bash_profile` を編集し、先ほど発行した取得したURL, TOKENで置き換えます。[^4]

```diff
- export MATTERMOST_TOKEN={YOUR_OUTGOING_WEBHOOKS_TOKEN}
- export MATTERMOST_INCOME_URL={YOUR_INCOMING_WEBHOOKS_URL}
+ export MATTERMOST_TOKEN=655dh6hyity1fpc58daaw1wk9a
+ export MATTERMOST_INCOME_URL=http://192.168.56.125/hooks/t9cqza13btb5fxkcf66ciex97a
```

おまけで以下の設定も行っています。省略した場合はWebHookを設定したアカウントのユーザ名/アイコン画像になるのかな？と思います。

```shell
export MATTERMOST_HUBOT_USERNAME="シンディー" 
export MATTERMOST_ICON_URL=https://s3-eu-west-1.amazonaws.com/renanvicente/toy13.png
```

編集後は ```$ source ~/.bash_profile``` を忘れずに。[^5]

## 起動

```
[vagrant@hubot-mattermost matter-bot]$ cd matter-bot
[vagrant@hubot-mattermost matter-bot]$ bin/hubot -a mattermost
[Thu Mar 03 2016 10:16:42 GMT+0000 (UTC)] ERROR hubot-heroku-alive included, but missing HUBOT_HEROKU_KEEPALIVE_URL. `heroku config:set HUBOT_HEROKU_KEEPALIVE_URL=$(heroku apps:info -s  | grep web-url | cut -d= -f2)`
[Thu Mar 03 2016 10:16:42 GMT+0000 (UTC)] INFO hubot-redis-brain: Using default redis on localhost:6379
```

Heroku, redisのエラーが出ますが支障はありません。気になるようでしたら[この辺を見て](http://stackoverflow.com/questions/30657596/remove-heroku-setting-from-hubot)対応したりしてください。
foreverでdaemon化したりするのもよいとおもいます。

## 動作確認

あとは `"Trigger Word"` に指定した単語を使い、Hubotに呼びかけてみてください。

`~/matter-bot/hubot/scripts/sample.coffee`に記述されているものを利用したサンプルです。
![badgers.png](https://qiita-image-store.s3.amazonaws.com/0/50184/da025057-a813-a5d4-33d9-e00b296b7a01.png)

非常に力技なのですが、Jenkinsのビルドをkickするサンプルもありますので、適宜いろいろ試していただければと思います。
 
# まとめ

外部連携部分はメイン機能と比べるとまだ荒削りで、試行錯誤が必要な感じですが、いわゆる「ChatOps」に必要な機能はひととおり用意されているようでした。
「SlackもHipChatも使えないから今流行のChatOpsなんて…」と諦めていた方は、選択肢の一つとして加えていただければと思います。

このあたりの情報は非常にすくなく、実際に運用するのは正直かなり茨の道かなと思いますが、是非チャレンジしていただければと思います。
そしてBK情報をまとめてみんなで幸せになりましょう…

よろしく願い申し上げます。


[^1]: 何回もやってると、もうテーブル調べて直接RDBMSにUPDATE文投げるほうが早い気がしてきました…
[^2]: 同一サーバ上でのポートフォワーディングみたいなことがもしできるなら、それを使えばできるのかもしれないですが…もしあれば教えて下さい…
[^3]: Mattermost → Hubot にネットワーク的にアクセスできる必要があり、名前解決がめんどくさかったのでIPアドレスを直接指定してしまっていますが、本当は適宜/etc/hostsなどに設定してあげるのが良いかと思います。。。
[^4]: こちらはHubot → Mattermostにネットワーク的にアクセスできる必要があるため、IPアドレスに置き換えています。
[^5]: 再読み込みを忘れ「なんで設定効かないんだ…？」ってなることが多かったので、試行錯誤するときは`bin/hubot`に直接export文を書いてしまうのがいいかもしれません…