〇〇したらLINE WORKSへ通知する、それIFTTTで

# 今回のお題
年末に同僚とランチしていた時に、こんな相談を受けました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/68da6564-f1fb-4f42-aa67-6cd10b9f488e.png)

**「TwitterでLINE WORKSについて呟かれたら、LINE WORKSへ通知する」**を実現してみようと思います。**[IFTTT](https://ifttt.com/)**を使って！！！！

こんなTweetがあったら、
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/85e0c449-a42f-8949-3b1c-24fd5d249399.png)

こんな感じで、LINE WORKSへ通知したいと思います。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/861014ea-bc7a-c24a-6fa7-a0dfa241c506.png)

> 同様の手順で下記のような要件にも対応可能です！:smiley:
>
> **Google Calendar（Office365カレンダー）に新しい会議が作成されたら、LINE WORKSへ通知する
> TrelloにToDoが追加されたら、LINE WORKSへ通知する
> Slackに通知があったら、LINE WORKSへ通知する（おっと？！）
> Boxにファイルをアップロードされたら、LINE WORKSへ通知する
> etc**

# ところで、IFTTTって？
IFTTTは、If This Then Thatの略で、Google,Twitter,box,Evernoteなど様々なSaaSをノンコーディングで連携させることができるサービスです。
使い方などは、下記のサイト等で確認してみてください。

> [さまざまなサービスが連携できる「IFTTT（イフト）」の使い方](http://www.atmarkit.co.jp/ait/articles/1711/22/news031.html)
> [IFTTTとは？業務効率化から私生活に便利を提供するサービスの全容](http://rpa.ne.jp/post-57/)

# でも、LINE WORKSってIFTTT対応していませんよね？
はい、してません。
してなかったら、自分で作るんです。
IFTTTの「Webhook」を使うことで、LINE WORKSのBotへ通知することが可能です！！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/221042e4-8b7c-2813-8a59-b2e4fbe80eea.png)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/1b42ca59-0948-b1b9-1bb6-e469c292ccc0.png)

#　構成
毎回一緒ですが、、BotサーバーはHeroku上でNode.jsで構築します。
IFTTTからはWebhookを使って、BotサーバーへデータをPOSTします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/af02b9d4-4fea-919c-f351-15e7052cc999.png)

# 構築の流れ

構築の流れは下記のStepになります。
１、２、４、５は「LINE WORKSで初めてのBot開発！」という記事で詳しく紹介しているので、そちらでご確認ください。
リンクを貼っておきます。

1. [LINE WORKS　Developer Consoleの設定](https://qiita.com/tokotan/items/f615f4a62219d655436f#developer-console%E8%A8%AD%E5%AE%9A)
2. [Botサーバーの用意](https://qiita.com/tokotan/items/f615f4a62219d655436f#bot%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%AE%E7%94%A8%E6%84%8F)
3. Botプログラミング
4. [Herokuへデプロイ](https://qiita.com/tokotan/items/976d35ca56132e0bb5c1#heroku%E3%81%B8%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4)
5. [Botを公開](https://qiita.com/tokotan/items/976d35ca56132e0bb5c1#bot%E5%88%A9%E7%94%A8%E8%A8%AD%E5%AE%9A)
6. IFTTTでアプレット作成

ここからは、「3.Botプログラミング」と「6.IFTTTでアプレット作成」に付いて書きます。

# Botプログラミング
## モジュールのインストール
下記モジュールを追加でインストールします。

```
$ npm install express --save
$ npm install body-parser --save
$ npm install jsonwebtoken --save
$ npm install request --save
$ npm install querystring --save
$ npm install basic-auth-connect　--save
```

index.jsにて、モジュールをインポートしておきます。

```index.js
//モジュールインポート
const express = require("express");
const bodyParser = require("body-parser");
const jwt = require('jsonwebtoken');
const https = require("https");
const request = require("request");
const qs = require("querystring");
const basicAuth = require("basic-auth-connect");//基本認証
```

## 環境変数設定

Server TokenやConsumer Keyは、Herokuの環境変数へ設定します。
Herokuの環境変数への設定はHerokuのダッシュボードから設定することができます。手順は[こちら](https://devcenter.heroku.com/articles/config-vars#using-the-heroku-dashboard)を参照してください。
今回は、下記7つの値をHerokuの環境変数として設定しましょう。

|環境変数名|Developer Consoleの表示箇所|
|--------|-------------------------|
|APIID|[API]メニューのAPI IDに表示されている英数字|
|CONSUMERKEY|[API]メニューのServer API Consumer KeyのKey欄に表示されている英数字|
|SERVERID|[API]メニューのServer List(ID登録タイプ)のID欄に表示されている英数字|
|PRIVATEKEY|[API]メニューのServer List(ID登録タイプ)の[認証キー再発行]ボタンからダウンロードします。<br> ダウンロードしたファイルに表示されている英数字
|BOTNO|[Bot]メニューに表示されているBotをクリックし、<br>[Bot No.]に表示されている４桁の数字|
|BASICID|BotサーバーBasic認証のためのID|
|BASICPASS|BotサーバーBasic認証のためのPASSWORD<br>PASSWORDは推測されにくい複雑なものを設定しましょう|

全て設定するとこんな感じになります。

設定したら、プログラムから利用できるよう変数として定義しておきましょう。

```index.js
const APIID = process.env.APIID;
const SERVERID = process.env.SERVERID;
const CONSUMERKEY = process.env.CONSUMERKEY;
const PRIVATEKEY = process.env.PRIVATEKEY;
const BOTNO = process.env.BOTNO;
const BASICID = process.env.BASICID;
const BASICPASS = process.env.BASICPASS;
```

## サーバーをBasic認証対応する
Bot用のWebサーバーのEndpointはパブリックネットワークに公開する必要があります。
そのURLへ悪意を持った第三者から不正なデータが送信されることを防ぐため、最低限の対策としてBasic認証に対応したいと思います。

```index.js
const express = require("express");
const server = express();
const basicAuth = require("basic-auth-connect");
const BASICID = process.env.BASICID;
const BASICPASS = process.env.BASICPASS;
:
server.use(basicAuth(
    BASICID, BASICPASS
))
```


## 通知メッセージをユーザーへ送信するためのトークンを取得する

トークンは、下記の流れで取得します。

1.JWTを取得
2.JWTを使ってServer Tokenを取得

それぞれ実装のサンプルコードは下記となります。詳細な仕様についてはこちらを参照してください。

```index.js
    getJWT((jwttoken) => {
        getServerToken(jwttoken, (newtoken) => {
            //取得したnewtokenを使ってメッセージ送信
        });
    });

//1. JWTを取得 
function getJWT(callback){
    const iss = SERVER_ID;
    const iat = Math.floor(Date.now() / 1000);
    const exp = iat + (60 * 60);　//JWTの有効期間は1時間
    const cert = PRIVATEKEY;
    const token = [];
    const jwttoken = jwt.sign({"iss":iss, "iat":iat, "exp":exp}, cert, {algorithm:"RS256"}, (err, jwttoken) => {
        if (!err) {
            callback(jwttoken);
        } else {
            console.log(err);
        }
    });
}
//2.JWTを使ってServer Tokenを取得
function getServerToken(jwttoken, callback) {
    const postdata = {
        url: 'https://authapi.worksmobile.com/b/' + API_ID + '/server/token',
        headers : {
            'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8',
        },
        form: {
            "grant_type" : encodeURIComponent("urn:ietf:params:oauth:grant-type:jwt-bearer"),
            "assertion" : jwttoken
        }
    };
    request.post(postdata, (error, response, body) => {
        if (error) {
            console.log(error);
            callback(error);
        } else {
            const jsonobj = JSON.parse(body);
            const AccessToken = jsonobj.access_token;
            callback(AccessToken);
        }
    });
}
```

>**[注意]**
Server Token（access_token）は24時間以内に利用されない場合には自動的に失効します。access_token発行時に、発行時間とaccess_tokenを保存し、Server APIを実行する際には有効期限を確認するようにしてください。有効期限内であれば保存したaccess_tokenを利用することができます。

## メッセージを送信する
取得したServerTokenを使って、ユーザーへ通知メッセージを送信します。
メッセージ送信APIの詳細な仕様は[こちら](https://developers.worksmobile.com/jp/document/100500801?lang=ja)を参照してください。
実装サンプルコードは下記となります。

```index.js
    //サーバートークンを取得し、メッセージ送信関数を呼び出す
    getJWT((jwttoken) => {
        getServerToken(jwttoken, (newtoken) => {
            sendMessage(newtoken, accountId, message);
        });
    });

//メッセージ送信関数
function sendMessage(token, accountId, message) {
    const postdata = {
        url: 'https://apis.worksmobile.com/' + API_ID + '/message/sendMessage/v2',
        headers : {
          'Content-Type' : 'application/json;charset=UTF-8',
          'consumerKey' : CONSUMERKEY,
          'Authorization' : "Bearer " + token
        },
        json: {
            "botNo" : Number(BOTNO),
            "accountId" : accountId,
            "content" : {
                "type" : "text",
                "text" : message
            }
        }
    };
    request.post(postdata, (error, response, body) => {
        if (error) {
          console.log(error);
        }
        console.log(body);
    });
}
```

>**　[注意]**
Botから１人のユーザーに対しメッセージを送信する場合は、上記のようになります。
トークルームに対しメッセージを送信する場合は、下記のようにaccountIdをnullにして、その代わりにroomIdを送信してください。

サンプルのJSONデータ

```
{
            "botNo" : Number(BOTNO),
            "accountId" : null,
            "roomId" : roomId,
            "content" : {
                "type" : "text",
                "text" : message
}
```

##最終的なサンプルコード
完成したindex.jsのサンプルコードは下記のようになります。

> **注意**
> 今回は、わかりやすいようにindex.jsに全てのコードを記述しています。必要に応じて、モジュール化を検討してください。

```index.js
"use strict";

// モジュールインポート
const express = require("express");
const server = express();
const bodyParser = require("body-parser");
const jwt = require('jsonwebtoken');
const https = require("https");
const request = require("request");
const qs = require("querystring");
const basicAuth = require("basic-auth-connect");

//変数
const APIID = process.env.APIID;
const SERVERID = process.env.SERVERID;
const CONSUMERKEY = process.env.CONSUMERKEY;
const PRIVATEKEY = process.env.PRIVATEKEY;
const BOTNO = process.env.BOTNO;
const BASICID = process.env.BASICID;
const BASICPASS = process.env.BASICPASS;

//基本認証
server.use(basicAuth(
    BASICID, BASICPASS
))
server.use(bodyParser.json());

// Webアプリケーション起動
server.listen(process.env.PORT || 3000);

// サーバー起動確認
server.get('/', (req, res) => {
    res.send('Hello World!');
});

// IFTTTからメッセージを受け取る
server.post('/webhook', (req, res) => {
    res.sendStatus(200);

    const message = req.body.message;
    const roomId = req.body.roomId;
    const accountId = req.body.accountId;

    getJWT((jwttoken) => {
        getServerToken(jwttoken, (newtoken) => {
            sendMessage(newtoken, accountId, message);
        });
    });
});

//サーバーAPI用JWT取得
function getJWT(callback){
    const iss = SERVERID;
    const iat = Math.floor(Date.now() / 1000);
    const exp = iat + (60 * 60);　//JWTの有効期間は1時間
    const cert = PRIVATEKEY;
    const token = [];
    const jwttoken = jwt.sign({"iss":iss, "iat":iat, "exp":exp}, cert, {algorithm:"RS256"}, (err, jwttoken) => {
        if (!err) {
            callback(jwttoken);
        } else {
            console.log(err);
        }
    });
}

function getServerToken(jwttoken, callback) {
    const postdata = {
        url: 'https://authapi.worksmobile.com/b/' + APIID + '/server/token',
        headers : {
            'Content-Type' : 'application/x-www-form-urlencoded; charset=UTF-8',
        },
        form: {
            "grant_type" : encodeURIComponent("urn:ietf:params:oauth:grant-type:jwt-bearer"),
            "assertion" : jwttoken
        }
    };
    request.post(postdata, (error, response, body) => {
        if (error) {
            console.log(error);
            callback(error);
        } else {
            const jsonobj = JSON.parse(body);
            const AccessToken = jsonobj.access_token;
            callback(AccessToken);
        }
    });
}

function sendMessage(token, accountId, message) {
    const postdata = {
        url: 'https://apis.worksmobile.com/' + APIID + '/message/sendMessage/v2',
        headers : {
          'Content-Type' : 'application/json;charset=UTF-8',
          'consumerKey' : CONSUMERKEY,
          'Authorization' : "Bearer " + token
        },
        json: {
            "botNo" : Number(BOTNO),
            "accountId" : accountId,
            "content" : {
                "type" : "text",
                "text" : message
            }
        }
    };
    request.post(postdata, (error, response, body) => {
        if (error) {
          console.log(error);
        }
        console.log(body);
    });
}
```

通知用Botプログラミングはこれで完了です。

続きの手順は、下記となります。
4. [Herokuへデプロイ](https://qiita.com/tokotan/items/976d35ca56132e0bb5c1#heroku%E3%81%B8%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4)
5. [Botを公開](https://qiita.com/tokotan/items/976d35ca56132e0bb5c1#bot%E5%88%A9%E7%94%A8%E8%A8%AD%E5%AE%9A)

ここまでで、通知Botは完成です！
ここからは通知Botへメッセージを送るIFTTTのアプレットを作成しましょう。

# IFTTTでアプレット作成
Twitterで#lineworksハッシュタグの新しい投稿があったら、LINE WORKSへ通知するという動きにします。
①[+this]をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/0f0c3e4b-563c-7e59-37d2-555224d714df.png)
②[Choose a service]でTwitterを選びます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/93e7ba81-cc57-51fc-bc83-d638cd622dbb.png)
③[Choose trigger]で[New tweet from search]をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/04ca82c7-fa47-0d41-2841-7ae008d2ded9.png)
④[Complete trigger fields]でSearch for欄に「"#lineworks"」と入力し、[Create trigger]ボタンをクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/bce7087e-65c6-e787-2324-45619da8851b.png)
⑤[+that]をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/b09a6ddc-7137-da36-5ae1-1c6cecf5fed0.png)
⑥[Choose action service]から「Webhooks」を選びます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/c8e8c8af-efef-bc71-f109-22f0b681cd62.png)
⑦[Choose action]で[Make a web request]を選びます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/a3c60d01-f6f6-310f-baa1-f217fd8a98bb.png)
⑧[Complete action fields]では下記のように設定します。設定が完了したら、[Create action]をクリックします。
通知先のBotサーバーはBasic認証をかけてますので、URLの前にHerokuの環境変数として設定したIDとPASSWORDを付与します。

|項目名|設定値|
|--------|-------------------------|
|URL|https://＜Basic認証のID＞:＜Basic認証のPASSWORD＞@＜herokuアプリケーション名＞.herokuapp.com/webhook|
|Method|POST|
|Content Type|application/json|
|Body|{"message":"LINE WORKSに関連するツイートがあったようです。\n {{Text}}\n {{UserName}}\n {{LinkToTweet}}", "accountId":"＜通知先のユーザーのLINE WORKS　ID＞" }|

⑨[Review and finish]で内容を確認し、[Finish]をクリックします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/5e93ed7b-fc2d-9cc5-ad22-459f6b1de038.png)

# 動作確認

Twitterにアクセスし、#lineworksハッシュタグをつけて投稿してみましょう！
LINE WORKSでLINE WORKS通知BotからTwitterに投稿したメッセージを受信できたら完成です！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/135983/861014ea-bc7a-c24a-6fa7-a0dfa241c506.png)

#　応用編

今回は、IFTTTからBotサーバーへWebhookを投げましたが、IFTTT以外にも、Webhookさえ投げられるシステムであれば、Microsoft Flow、Zapier、Zabbixなどでも応用可能です。

色々な連携を試してみていただけると嬉しいです！
その時は、是非、Twitterで#lineworksハッシュタグをつけて投稿してください！:heart_eyes: