Hubot を Amazon EC2 にセットアップして Yammer に投稿する

# <i class="fa fa-map-marker"></i> 目的
![image](https://qiita-image-store.s3.amazonaws.com/0/130874/82af24ea-e8de-91f8-c12a-c4a775fc071f.png)

　Hubot + Heroku + Slack な組み合わせはよくあるけれど、うちの社内ではチャットに Yammer を使用しているのと、AWS アカウントがあるので AWS/EC2 + Hubot + Yammer の組み合わせを試します。
　Slack でやってもいいんですが周りの人にわざわざ Slack アカウント作ってもらうのが面倒なのと、誰も Yammer-Adapter 使ってなさそうなので人柱です。

　Hubot-Yammer の README を読みながらすすめました。
[athieriot/hubot-yammer: This is the Yammer adapter for hubot that allows you to send a message to him with Yammer and he will happily reply the same way.](https://github.com/athieriot/hubot-yammer)

# <i class="fa fa-plane"></i> 結果
　できました。
> ![image](https://qiita-image-store.s3.amazonaws.com/0/130874/10011a2f-1534-2fca-654d-c0e546cd88b7.png)

　社内で ChatOps 的なことができそうです。

　Hubot を生かす為だけに EC2 使うのもいまいちなので最終的にはこれを Lambda でサーバレスにしようと思っていたのですが、Yammer には Outgoing Webhooks の機能が無いので、Yammer に投稿があったタイミングでイベントを発行したい場合は [Zapier](https://zapier.com/zapbook/yammer/) などで Yammer -> Webhook なレシピ(zapbook)を作らないとだめみたいです。

# <i class="fa fa-location-arrow"></i> 構築手順
　EC2 インスタンス作成するのと、Hubot インストールまではざっくりした流れだけ記載します。

## 必要なもの

|必要なもの|今回作成した名称|備考|
|:--|:--|:--|
|AWS の Linux インスタンス|-|無料枠(t2.micro）のAmazon Linux を作成|
|Yammer 内 Public グループ|BOTTESTGROUP|テスト用のグループ、日本語でもいけましたがスペース入れるとだめ。|
|BOTアカウント|YAMMERBOTACCOUNT|APIトークンを取得する、実際には人のアカウントを借りました。|
|テスト用アカウント|TESTYOUACCOUNT|BOTに話しかける用、自分のアカウントを使いました。|

## AWS/EC2 上に Linux インスタンスを作成
　無料枠（t2.micro）で Linux インスタンスを作成、EIP を割当てました。セキュリティグループは構築のための SSH/MyIP と、Yammer API 呼び出しのための HTTP/AnyIP で許可が必要です。後者は Yammer API の IP範囲で絞っても良いとおもいます。

## nodejs, npm のインストール
　Hubot、Yoneman に必要な node.js, npm をインストールします。n はグローバルにインストールした node.js のバージョン管理するものみたい。

```bash

[ec2-user@HOST ~]$ sudo yum install nodejs npm --enablerepo=epel
[ec2-user@HOST ~]$ npm -v
1.3.6
[ec2-user@HOST ~]$ sudo npm install -g n
[ec2-user@HOST ~]$ sudo n stable
     install : node-v6.8.1
       mkdir : /usr/local/n/versions/node/6.8.1
       fetch : https://nodejs.org/dist/v6.8.1/node-v6.8.1-linux-x64.tar.gz
######################################################################## 100.0%
   installed : v0.10.46
[ec2-user@HOST ~]$ node -v
v0.10.46
```

## Yeoman、generator-hubot のインストール
　Yeoman (yo) と Hubot の Yeoman ジェネレーター (generator-hubot) をインストールします。

```bash
[ec2-user@HOST ~]$ sudo npm install -g yo generator-hubot
（略）
[ec2-user@HOST ~]$ sudo npm list -g generator-hubot yo
npm WARN unmet dependency /usr/lib/node_modules/block-stream requires inherits@'~2.0.0' but will load
npm WARN unmet dependency undefined,
npm WARN unmet dependency which is version undefined
npm WARN unmet dependency /usr/lib/node_modules/fstream requires inherits@'~2.0.0' but will load
npm WARN unmet dependency undefined,
npm WARN unmet dependency which is version undefined
npm WARN unmet dependency /usr/lib/node_modules/fstream-ignore requires inherits@'2' but will load
npm WARN unmet dependency undefined,
npm WARN unmet dependency which is version undefined
npm WARN unmet dependency /usr/lib/node_modules/fstream-npm requires inherits@'2' but will load
npm WARN unmet dependency undefined,
npm WARN unmet dependency which is version undefined
npm WARN unmet dependency /usr/lib/node_modules/glob requires inherits@'2' but will load
npm WARN unmet dependency undefined,
npm WARN unmet dependency which is version undefined
npm WARN unmet dependency /usr/lib/node_modules/npmconf requires inherits@'~2.0.0' but will load
npm WARN unmet dependency undefined,
npm WARN unmet dependency which is version undefined
npm WARN unmet dependency /usr/lib/node_modules/tar requires inherits@'2' but will load
npm WARN unmet dependency undefined,
npm WARN unmet dependency which is version undefined
/usr/lib
├── generator-hubot@0.4.0 
└── yo@1.8.5 
```

###（補足）npm WARN unmet dependency 対応
　何故かライブラリ inherits が不足しているようなので、追加でグローバルにインストールし、エラーが出なくなることを確認しました。

```bash
[ec2-user@HOST ~]$ sudo npm install inherits -g
npm http GET https://registry.npmjs.org/inherits
npm http 304 https://registry.npmjs.org/inherits
inherits@2.0.3 /usr/lib/node_modules/inherits
[ec2-user@HOST ~]$  sudo npm list -g generator-hubot yo
/usr/lib
├── generator-hubot@0.4.0 
└── yo@1.8.5
```

### Hubot 作成、初期設定
　適当な名前でディレクトリを作成して、yo hubot で作成。Bot adapter には yammer を指定します。コンソールがかわいい。

```bash
[ec2-user@HOST ~]$ mkdir YAMMERBOT
[ec2-user@HOST ~]$ cd YAMMERBOT
[ec2-user@HOST YAMMERBOT]$ yo hubot
（略）
? Owner MYEMAILADRESS@example.com
? Bot name YAMMERBOT
? Description YammerBotDesu
? Bot adapter yammer
   create bin/hubot
   create bin/hubot.cmd
（略）
```

![image](https://qiita-image-store.s3.amazonaws.com/0/130874/05c59bad-0247-6700-e2ce-9e5fd491c4ab.png)

　作成後の Procfile は web の Yammer になっています。

```bash
[ec2-user@HOST ~]$ cat Procfile 
web: bin/hubot -a yammer
```

### Hubot をローカルで動作確認
　まだ Yammer には接続していません。HUBOT_HEROKU_KEEPALIVE_URL 設定せよと WARNING が出ていますが今は無視で。

```bash
[ec2-user@HOST YAMMERBOT]$ hubot
YAMMERBOT> [Thu Oct 20 2016 02:59:28 GMT+0000 (UTC)] WARNING Loading scripts from hubot-scripts.json is deprecated and will be removed in 3.0 (https://github.com/github/hubot-scripts/issues/1113) in favor of packages for each script.

Your hubot-scripts.json is empty, so you just need to remove it.
[Thu Oct 20 2016 02:59:28 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 Oct 20 2016 02:59:28 GMT+0000 (UTC)] INFO hubot-redis-brain: Using default redis on localhost:6379
YAMMERBOT> YAMMERBOT ping
YAMMERBOT> PONG
YAMMERBOT> YAMMERBOT time
YAMMERBOT> Server time is: Thu Oct 20 2016 03:01:21 GMT+0000 (UTC)
```

## Yammer から API アクセスキー取得
　API のアクセスキートークンを取得します。BOT アカウントで Yammer にログインした状態で下記 URL にアクセスします。

- ログインユーザ：YAMMERBOTACCOUNT
- https://www.yammer.com/client_applications
- Input
    - アプリケーション名：HogeFugaApp
    - 組織：HOGE
    - サポート用メールアドレス：hoge@example.com
    - ウェブサイト：https://example.com/
    - リダイレクト先の URI：https://example.com/

　上記のように超適当に入力すると、API キーを取得できます。

- Output
    - クライアントID：XxXxXxXxXxXxXxXxXxXxXx
    - クライアントシークレット：XxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx
    - リダイレクト先：https://example.com/
    - Generate a developer token for this application
        - Here is your personal token for testing. Please copy it somewhere safe as this will disappear when you change page:
        - 00000-XxXxXxXxXxXxXxXxXxXx

　一番下の developer token というのを使用します。

## 投稿用グループの作成
　Yammer 上に投稿用のグループを作成、BOT アカウントとテスト用アカウントの2つをグループに追加しておきます。グループ名は環境変数に設定するので、英名のほうがよさそうです。

- グループ作成
    - グループ名：BOTTESTGROUP
    - 公開範囲：Public
- グループメンバ追加
    - BOT アカウント：YAMMERBOTACCOUNT
    - テスト用アカウント：TESTYOUACCOUNT 

　BOT アカウントのアカウント名は必ずしも BOT 名（YAMMERBOT）と合わせる必要はありません。

## API キー設定
　先ほど取得した API キーと、作成したグループ名を環境変数に追加します。HUBOT_HEROKU_KEEPALIVE_URL は何を指定すればいいのかよくわからないけど、KeepAlive のために 5 分ごとに Get しているようなので、401 が帰ってくる URL を適当に指定するとよさそうでした。Heroku じゃないけど使ってるみたいです。

```bash
[ec2-user@HOST ~]$ export HUBOT_YAMMER_ACCESS_TOKEN="00000-XxXxXxXxXxXxXxXxXxXx"
[ec2-user@HOST ~]$ export HUBOT_YAMMER_GROUPS="BOTTESTGROUP"
[ec2-user@HOST ~]$ export HUBOT_HEROKU_KEEPALIVE_URL="https://www.yammer.com/api/v1/users.json"
[ec2-user@HOST ~]$ printenv
HUBOT_YAMMER_GROUPS=BOTTESTGROUP
HUBOT_YAMMER_ACCESS_TOKEN=00000-XxXxXxXxXxXxXxXxXxXx
HUBOT_HEROKU_KEEPALIVE_URL=https://www.yammer.com/api/v1/users.json
```

## EC2 から Yamme API に繋がること確認
　ネットワーク設定確認のため、一応 wget します。

```bash
[ec2-user@HOST ~]$ wget -O -  https://www.yammer.com/
（略）
-                               100%[======================================================>]  15.97K  --.-KB/s    in 0.002s  

2016-10-20 05:11:13 (10.2 MB/s) - written to stdout [16354/16354]
```


## BOT 起動と動作確認
　満を持して起動します。groups list に設定したグループ名、groups_ids list にグループ名から取得したグループIDが表示されます。5 分毎に keepalive が正常に動いたログも出ています。

```
[ec2-user@HOST ~]$ hubot -a yammer -n "YAMMERBOT"
[Thu Oct 20 2016 05:21:16 GMT+0000 (UTC)] WARNING Loading scripts from hubot-scripts.json is deprecated and will be removed in 3.0 (https://github.com/github/hubot-scripts/issues/1113) in favor of packages for each script.

Your hubot-scripts.json is empty, so you just need to remove it.
[Thu Oct 20 2016 05:21:16 GMT+0000 (UTC)] INFO hubot-redis-brain: Using default redis on localhost:6379
groups list : BOTTESTGROUP
groups_ids list : 0000000
[Thu Oct 20 2016 05:26:15 GMT+0000 (UTC)] INFO keepalive ping
[Thu Oct 20 2016 05:26:16 GMT+0000 (UTC)] INFO keepalive pong: 401  
[Thu Oct 20 2016 05:31:15 GMT+0000 (UTC)] INFO keepalive ping
[Thu Oct 20 2016 05:31:16 GMT+0000 (UTC)] INFO keepalive pong: 401  
```

　Yammer のテスト用アカウントから「YAMMERBOT PING」の形式でコマンドが送れました。コンソール側にもログが出力されます。

```bash
received YAMMERBOT PING from TESTYOUACCOUNT (thread_id: 000000000, sender_id: 0000000)
reply message to TESTYOUACCOUNT with text PONG
Message creation status 201
```

> ![image](https://qiita-image-store.s3.amazonaws.com/0/130874/10011a2f-1534-2fca-654d-c0e546cd88b7.png)

# <i class="fa fa-pencil-square-o"></i> おまけのエラー集
## BOT アカウント からテストできない。
　BOT アカウントで BOT アカウントに話しかけても無視される仕様でした。

```bash
groups list : BOTTESTGROUP
groups_ids list : 0000000
received YAMMERBOT PING from YAMMERBOTACCOUNT (thread_id: 000000000, sender_id: 0000000)
YAMMERBOT does not reply himself, YAMMERBOT not crazy nor desperate
```

## 認証エラー（Authentication failure）
　HUBOT_YAMMER_ACCESS_TOKEN を間違えていると起きるようです。Yammer が落ちててネットワークエラーもこれになりました。

```bash
[Thu Oct 20 2016 04:06:52 GMT+0000 (UTC)] ERROR TypeError: Cannot use 'in' operator to search for 'data' in undefined
  at /home/ec2-user/node_modules/hubot-yammer/src/yammer.coffee:80:6, <js>:146:23
  at /home/ec2-user/node_modules/yammer/main.js:13:19
  at Request._callback (/home/ec2-user/node_modules/yammer/main.js:132:14)
  at Request.self.callback (/home/ec2-user/node_modules/yammer/node_modules/request/request.js:121:22)
  at emitTwo (events.js:106:13)
  at Request.emit (events.js:191:7)
  at Request.<anonymous> (/home/ec2-user/node_modules/yammer/node_modules/request/request.js:978:14)
  at emitOne (events.js:101:20)
  at Request.emit (events.js:188:7)
  at IncomingMessage.<anonymous> (/home/ec2-user/node_modules/yammer/node_modules/request/request.js:929:12)
  at emitNone (events.js:91:20)
  at IncomingMessage.emit (events.js:185:7)
  at endReadableNT (_stream_readable.js:974:12)
  at _combinedTickCallback (internal/process/next_tick.js:74:11)
  at process._tickCallback (internal/process/next_tick.js:98:9)

yammer groups error: Error: Error status 401
 Authentication failure.
```

　Yammer のステータスはこちら、よく落ちます。ChatOps もそうだけどサービスフルにすると SaaS が止まると業務が止まりますね。

- https://status.yammer.com/
- https://twitter.com/yammerops

## groups_id 取得できないエラー
「groups_ids list : （ブランク）」になる場合は、グループ ID が取得できていません、グループが存在しなかったり、変数 HUBOT_YAMMER_GROUPS の指定を間違えるとなるようです。正常に取得できた場合の ID は Yammer グループ URL の feedId が取得されます。
https://www.yammer.com/COMPANY.com/#/threads/inGroup?type=in_group&feedId=0000000&view=all

```bash
[Thu Oct 20 2016 04:07:24 GMT+0000 (UTC)] INFO hubot-redis-brain: Using default redis on localhost:6379
groups list : WRONG_GROUP_NAME
groups_ids list : 
[Thu Oct 20 2016 04:07:24 GMT+0000 (UTC)] ERROR Error: No group registered or an error occured to resolve IDs.
  at /home/ec2-user/node_modules/hubot-yammer/src/yammer.coffee:146:20, <js>:225:17
  at Request._callback (/home/ec2-user/node_modules/yammer/main.js:137:12)
  at Request.self.callback (/home/ec2-user/node_modules/yammer/node_modules/request/request.js:121:22)
  at emitTwo (events.js:106:13)
  at Request.emit (events.js:191:7)
  at Request.<anonymous> (/home/ec2-user/node_modules/yammer/node_modules/request/request.js:978:14)
  at emitOne (events.js:101:20)
  at Request.emit (events.js:188:7)
  at IncomingMessage.<anonymous> (/home/ec2-user/node_modules/yammer/node_modules/request/request.js:929:12)
  at emitNone (events.js:91:20)
  at IncomingMessage.emit (events.js:185:7)
  at endReadableNT (_stream_readable.js:974:12)
  at _combinedTickCallback (internal/process/next_tick.js:74:11)
  at process._tickCallback (internal/process/next_tick.js:98:9)
```

### Yammer と通信できないときのエラー
　HTTP/Any を許可していないときは次のようなエラーになりました。グループ ID は取れてるけど。

```bash
[Thu Oct 20 2016 08:24:22 GMT+0000 (UTC)] INFO hubot-redis-brain: Using default redis on localhost:6379
groups list : BOTTESTGROUP
groups_ids list : 0000000
[Thu Oct 20 2016 08:24:25 GMT+0000 (UTC)] ERROR TypeError: Cannot use 'in' operator to search for 'data' in undefined
  at /home/ec2-user/node_modules/hubot-yammer/src/yammer.coffee:80:6, <js>:146:23
  at Request._callback (/home/ec2-user/node_modules/hubot-yammer/node_modules/yammer/main.js:33:43)
  at Request.self.callback (/home/ec2-user/node_modules/hubot-yammer/node_modules/yammer/node_modules/request/request.js:121:22)
  at emitTwo (events.js:106:13)
  at Request.emit (events.js:191:7)
  at Request.<anonymous> (/home/ec2-user/node_modules/hubot-yammer/node_modules/yammer/node_modules/request/request.js:978:14)
  at emitOne (events.js:101:20)
  at Request.emit (events.js:188:7)
  at IncomingMessage.<anonymous> (/home/ec2-user/node_modules/hubot-yammer/node_modules/yammer/node_modules/request/request.js:929:12)
  at emitNone (events.js:91:20)
  at IncomingMessage.emit (events.js:185:7)
  at endReadableNT (_stream_readable.js:974:12)
  at _combinedTickCallback (internal/process/next_tick.js:74:11)
  at process._tickCallback (internal/process/next_tick.js:98:9)
```

# <i class="fa fa-external-link"></i> 参考リンク
（お世話になりました）
[Hubot / ChatOps とは - Qiita](http://qiita.com/bouzuya/items/c7d0ad80c357aab6b696)
[Hubot を Heroku で動かして Slack から話す - Qiita](http://qiita.com/bouzuya/items/2a200c9e8a45e2478bc2)
[Hubot のインストール - Qiita](http://qiita.com/bouzuya/items/11c0c6da2b3ad54b827f)
[CentOSにEPELリポジトリを追加する - Qiita](http://qiita.com/muniere/items/6c4923a070cbbd824f39)
[CentOS 7 に Node.js をインストールする - Qiita](http://qiita.com/moris/items/4850d4e19392186e34f0)
[CentOSにEPELリポジトリを追加する - Qiita](http://qiita.com/muniere/items/6c4923a070cbbd824f39)
[npm WARN unmet dependency発生したら。 by デビット ジョナサン](https://www.wantedly.com/techniques/2700)
[Hubot を Amazon EC2 にセットアップし、Slack と連携する！ - Qiita](http://qiita.com/hkusu/items/3e3695450f8a4f9389b3)
[yammer App の Programming – Tsmatz](https://blogs.msdn.microsoft.com/tsmatsuz/2014/03/26/yammer-app-programming/)
[Webhookとは？ | SendGridブログ](https://sendgrid.kke.co.jp/blog/?p=1851)
[WebHookの調査と実装](http://www.slideshare.net/moaikids/webhook)
[Office 365 Connectors: Where’s Yammer? | Turn the page](https://thehouseofbeck.com/2016/03/31/office-365-connectors-wheres-yammer/)

（以上）