Chatwork から Slackへの Webhook通知 

### 前置き

社内は Slackを多用していて参加チャンネルも多かったのですが、
最近案件の都合でChatWorkのチャンネルにも入ったことで
通知のチェックが大変になってきました。

そこで、ChatWorkの通知をSlackでも受け取りたいと思い
アプリを検索してみたのですが...

以外とないので、Lambdaで適当に作ってみることに。


### 連携イメージ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200155/23cad49a-c963-7337-658d-fc691aabdaa4.png)


こんな流れにで、Webhookの フォーマット形式を良い感じ変換できれば
以外と簡単に実現できそう！ （と、思ってました）。


### 作成

データのフローとは逆の形で、エンドポイント側（Slack側）から
対応していく必要があります。


#### 1. Slack - IncomingWebhook

IncomingWebhook のインストールをし、新規WebhookURLを発行します。

アプリURL:
　https://slack.com/apps/A0F7XDUAZ--incoming-webhook-

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200155/86287a15-2114-929c-6ff4-3b4eb6b00434.png)


#### 2. Lambda

##### 1. 実装

今回は Nodejsにて実装。
-> index.js に以下のように作成

```javascript:index.js
const https = require('https');

// Slack の IncomingWebhook のURLを設定
const slack_host = "hooks.slack.com";
const slack_path = "/services/*********/*********/*****************";

exports.handler = async (event) => {

  if(!event["webhook_event"]){
    return "no event data.";
  }

  let chatevent = event["webhook_event"];
  
  let postData = {
    username : "chatwork-bot / " + (chatevent["from_account_id"] || chatevent["account_id"]),
    text : chatevent["body"] + (event["webhook_event_type"] == "message_updated" ? " (edited)": ""),
    icon_emoji : ":ghost:"
  };

  let data = "payload="+JSON.stringify(postData)+"";
  let options = {
    host : slack_host,
    path : slack_path,
    method : "post",
    headers: {
     "Content-type": "application/x-www-form-urlencoded",
     "Content-Length" : Buffer.byteLength(data),
    }
  };
  
  return new Promise((resolve, reject) => {
    let buff = [];
    let req = https.request(options, (res)=> {
      res.on('data', (chunk) => {
        buff.push(chunk);
      });
      res.on('end', () => {
        if (res.statusCode == 200) {
          resolve(Buffer.concat(buff).toString());
        } else {
          reject("request failed: " + res.statusCode + " - " + res.statusMessage + " " + Buffer.concat(buff).toString());
        }
      });
    });
    req.on('error', (e) => {
      console.error(`request error: ${e.message}`);
    });
    req.write(data);
    req.end();
  });

  
};
```

##### 2. テスト

この段階で IncomingWebhookとLambdaの実装が正しくできていれば
Slackへのテスト通知が可能です。

テストは、Chatworkの仕様に従い２種用意します。
- test1: 通常メッセージ
- test2: 自分へのメンションメッセージ

※ webhook_event_type は message_created, message_updated, mention_to_me の3種あり、
　message_created と message_updatedはフォーマット形式が同じ。
　（公式のサンプルだと、message_idが被ってるので、微調整）

```json:テスト1　(webhook_event_type...message_created,message_updated)
{
  "webhook_setting_id": "12345",
  "webhook_event_type": "message_created",
  "webhook_event_time": 1498028152,
  "webhook_event": {
    "message_id": "789012345",
    "room_id": 567890123,
    "account_id": 1484814,
    "body": "お客様とのランチミーティング用のお弁当、発注完了しました。",
    "send_time": 1498028120,
    "update_time": 0
  }
}
```

```json:テスト2　(webhook_event_type...mention_to_me)
{
  "webhook_setting_id": "12345",
  "webhook_event_type": "mention_to_me",
  "webhook_event_time": 1498028130,
  "webhook_event": {
    "from_account_id": 123456,
    "to_account_id": 1484814,
    "room_id": 567890123,
    "message_id": "789012346",
    "body": "[To:1484814]おかずはなんですか？",
    "send_time": 1498028125,
    "update_time": 0
  }
}
```
　　　　　⬇︎　テスト1 実行例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200155/c20b8a63-dac3-c091-2d0f-e93a46c998f6.png)
　　　　　⬇︎　テスト2 実行例
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200155/b6a15bd1-7c7b-5b1f-e2de-7da2c813c723.png)

といった具合にslackへ通知が飛べばここまで成功です。

※ この段階で気づいたのですが、
　通知された情報に投稿者名がなく、アカウントIDしかなかったんですよね。
　-> 取り急ぎ連携することを優先するので、ここでは見ないことにします。


#### 3. APIGateway

API-Gateway にて上で定義したLambda関数の呼び出すエンドポイントを作成します。

1. リソース作成(任意)
2. メソッド作成
-> 上で作成したLambda を指定
3. ステージの作成
-> 公開URLの発行

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200155/f3c263fc-8440-3a34-8582-a2deaedde8a3.png)


<small> ※ リソースを追加した際は、リソースのパスの追加を忘れずに </small>

#### 4. Chatwork - Webhook

1. API設定画面の起動
　画面右上の `API設定` を選択
　https://www.chatwork.com/service/packages/chatwork/subpackages/webhook/list.php

2. 新規作成にて 3.で作ったAPIGatewayのURLをWebhookURLへ登録

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200155/06107912-0d45-fa82-f830-4a969b41332e.png)

　
上記で設定完了です。
　
　

#### 5. 実装結果

<small>**Chatwork**</small>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200155/b119d98a-4169-190e-ac51-8cc7f5493f8b.png)
　　　　　　⬇︎
<small>**Slack**</small>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200155/ce28950d-63b0-5864-c507-5cc021b19ee6.png)


ご覧の通り、良い感じに連携できました。  (※投稿者以外は)
　

も、目的の、通知が使えれば良いんじゃないかな。
一応他も試して見る。

<small>**Chatwork**</small>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200155/f97a9559-25a8-a7a2-b3cd-95afdc329183.png)
　　　　　　⬇︎
<small>**Slack**</small>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200155/1b3a8753-d2a0-b0b0-d9ae-9acf23ea5017.png)


と少々残念なことに。

他、引用や返信を試してみたところ

<small>**Chatwork**</small>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200155/4284237f-1816-93fa-f851-c7280ef64dfc.png)
　　　　　　⬇︎
<small>**Slack**</small>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/200155/8c06fdb5-22bd-0612-220d-4b4cfe212c67.png)
　
ぱっと見、よくわからない。

<small> ※ 引用元の発言者の名前が載ってるので誤認しそう </small>


　
他：
- リアクションは、何も通知されませんでした。
- 投稿済の記事の編集は、編集後の内容でSlackへ通知されました。
（IncomingWebhookの特性上記事の更新は難しいので、今回は末尾に `(edited)` を直接付与して誤魔化し）



### 6 最後に

適当に作ってみたのですが、課題が山積みでした。
Webhookの対応のみでは、完全な通知の連携とまでいかないので、
何らかの通知があったことを知ることに使おうかなと思います。
　

改善に向けた課題:

- 投稿者の accout_id を名前へ変換
- メンションの変換
- 発言者のサムネイル表示
- 発言内容の絵文字も変換
- 引用元がわかるように
- 添付ファイルも直リンクできるように
- Webhookの接続元の検証の実施　(`X-ChatWorkWebhookSignature`)
- 記事の編集通知

　
APIの仕様上、どこまで対応できるかわかりませんが、　
今後時間を見てブラッシュアップしていきたいと思います。
　


　
　
参考:
- http://developer.chatwork.com/ja/webhook.html
- https://api.slack.com/messaging/webhooks