Azure Static Web AppsのAPI作成でLINE BOTを作る


前に書いた記事でAzure Static Web Appsを簡単に試してみましたが、後半の手順でAPIの作成も出来ました。

> 参考: [たぶん10分で試せる。Azure Static Web AppsにWebサイトをデプロイして独自ドメイン設定とFunctionsでAPI公開まで](https://qiita.com/n0bisuke/items/1e12d8abb76d40a83fea)

今回はこれを使ってLINE BOTを作ってみようと思います。

ちなみにStatic Web AppsのAPIの中身はAzure Functionsになる模様です。

> 参考: [Azure Functions による Azure Static Web Apps プレビューでの API のサポート](https://docs.microsoft.com/ja-jp/azure/static-web-apps/apis)

## 環境

* macOS Catalina
* Node.js v12系

現状だとローカル実行やAzure FunctionsがNode.js v12までしか対応してなさそうでした。

## APIエンドポイントを作ってみる（おさらい）

こんな手順です。

* まずは[参考記事](https://qiita.com/n0bisuke/items/1e12d8abb76d40a83fea)をもとに、Azure Static Web Appで静的サイトを作成してみる
* `apiフォルダ`を作成し、さらに`linebot`フォルダを作成
* `linebot`フォルダ内に`index.js`と`function.json`を作成

```index.js
module.exports = async function (context, req) {
    context.res = {
       body: { 
         text: "Hello from the API" 
       }
    };
  };
```

```function.json
{
    "bindings": [
      {
        "authLevel": "anonymous",
        "type": "httpTrigger",
        "direction": "in",
        "name": "req",
        "methods": [
          "get"
        ],
        "route": "linebot"
      },
      {
        "type": "http",
        "direction": "out",
        "name": "res"
      }
    ]
  }
```

> ![](https://i.gyazo.com/313fd49fe97acf4f92700a5ee535883b.png)

この状態でプッシュしてしばらくするとAPIが有効になります。

`https://<APP ID>.azurestaticapps.net/api/linebot`にアクセスするとAPIが見えます。

> ![](https://i.gyazo.com/802f6efeb1cc5226ef97df1f2059992b.png)

## ローカル実行まで

まずはローカルで実行できる環境を整えます。

### Azure Functionsの拡張機能のインストールと設定

VScodeに[Azure Functionsの拡張機能](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions)をインストールします。

インストールが完了するとAzureマークがVSCodeのサイドバーに表示されます。選択するとサインインしましょう的なボタンがあるのでサインインします。

> ![](https://i.gyazo.com/39cbfdd0018c1f01650610545e8907e3.png)

サインインすると少し読み込み待ち

> ![](https://i.gyazo.com/9fe7ef94caa3fd083acaef79b538c072.png)

`Create New Project...`のボタンを選択し、`Browse`を選択。

> ![](https://i.gyazo.com/8cddddb0178b4fb10384e51924ba535c.png)

作成した`apiフォルダ`を選択

> ![](https://i.gyazo.com/d70e6d729b10bd0e121849d1c6819897.png)

言語選択で`JavaScript`を選択

> ![スクリーンショット 2020-08-17 1.27.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/53fd54c4-decb-aa08-8e20-413bb5a5ccfc.png "スクリーンショット 2020-08-17 1.27.33.png")

テンプレート選択で`HTTP trigger`を選択

> ![スクリーンショット 2020-08-17 1.27.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/6855f5fd-3900-5ac7-f78b-9c2e2855e027.png "スクリーンショット 2020-08-17 1.27.28.png")

関数名を`webhook`とし、承認レベルを`anonymous`にします。

> ![](https://i.gyazo.com/ce5f84edaf2bdf3fe5263049d3761e31.png)

こんな感じで`apiフォルダ`内に`webhookフォルダ`やその他ファイルが作成されます。

>  <img src="https://i.gyazo.com/b26ebe45030c0573021391e66af8339c.png" height="200px" >

Azure Functions拡張機能のタブに戻るとこのような表示になっています。

> ![](https://i.gyazo.com/9f8e45536c0b5e5d6fd36417655e2103.png)

### core toolsでローカル実行

[Azure Functions Core Tools](https://www.npmjs.com/package/azure-functions-core-tools)というツールを使ってローカル実行をする模様です。

VScodeのコマンドパレットを開き(macだとcommand+shift+p)、`core tools`と入力しインストールします。

> ![スクリーンショット 2020-08-17 1.43.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/61dd8bda-c863-4fa3-c56f-3bed951ec9cb.png "スクリーンショット 2020-08-17 1.43.35.png")

バージョン選択ですが、レコメンドされてるので`Azure Functions v3`を選択します。

> ![スクリーンショット 2020-08-17 1.43.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/a3fb4cae-7edf-1e90-80c5-3f49fda4a6f1.png "スクリーンショット 2020-08-17 1.43.44.png")

しばらくするとインストールが完了します。

VSCodeのメニューの`Run > Start Debugging`を選択するとローカルサーバーが起動します。

> ![スクリーンショット 2020-08-17 1.55.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/35387/a11c0611-50a2-94a4-b494-237ceabe594a.png "スクリーンショット 2020-08-17 1.55.28.png")

ちなみに、このときにNode.js v14を利用してたらエラーが出ました。執筆時点ではv12までの対応らしいのでv12に切り替えたらうまく行きました。

ターミナルではこんな感じで作成した`webhook`と`linebot`のエンドポイントが有効になったような表示になります。

> ![](https://i.gyazo.com/3dd2113c6a980aa161d39d2632f850ce.png)

ブラウザでアクセスしてみるとちゃんと表示されました。

> ![](https://i.gyazo.com/e3c8a00959a8949b0e39cda8bed3c124.png)


## LINE BOTプログラムの作成

現状だとルートディレクトリに`apiフォルダ`と`index.html`がありますが、`apiフォルダ`内に`package.json`があるのでそこで`npm install`を実行していきます。

* ルートからapiフォルダへ

```bash
$ ls
api        index.html
```

```bash
$ cd api
```

```bash
$ ls
host.json           local.settings.json package.json        webhook
linebot             package-lock.json   proxies.json
```

* apiフォルダでモジュールのインストール

```bash
npm i @line/bot-sdk express azure-function-express
```

通常だとAzure Functionsのお作法で書く必要がありますが、`azure-function-express`を利用することで、Azure Functionsっぽい書き方に依存せずに通常のexpressの利用っぽい雰囲気で書くことが出来ます。ひらりんさんの記事が参考になりました！

> 参考: [LINE Bot を Azure Functions (Node.js) で作る際のオウム返しテンプレ](https://himanago.hatenablog.com/entry/2020/04/23/205202)

* `api/webhook/index.js`にコードを記述

> ![](https://i.gyazo.com/ecc113a651e2ca15665da486edfdcfbc.png)



```js
'use strict';

const line = require('@line/bot-sdk');
const createHandler = require("azure-function-express").createHandler;
const express = require('express');

const config = {
    channelSecret: '作成したBOTのチャンネルシークレット',
    channelAccessToken: '作成したBOTのチャンネルアクセストークン'
};

const app = express();

app.get('/api/webhook', (req, res) => res.send('Hello LINE BOT!(GET)')); //ブラウザ確認用(無くても問題ない)
app.post('/api/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);

    //ここのif分はdeveloper consoleの"接続確認"用なので削除して問題ないです。
    if(req.body.events[0].replyToken === '00000000000000000000000000000000' && req.body.events[1].replyToken === 'ffffffffffffffffffffffffffffffff'){
        res.send('Hello LINE BOT!(POST)');
        console.log('疎通確認用');
        return;
    }

    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

const client = new line.Client(config);

async function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  return client.replyMessage(event.replyToken, {
    type: 'text',
    text: event.message.text //実際に返信の言葉を入れる箇所
  });
}

module.exports = createHandler(app);
```

大元はこちらの[1時間でLINE BOTを作るハンズオン](https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d)の記事のコードになっています。

参考記事をもとにLINE BOTのチャンネルシークレットとアクセストークンもコードに記載しておきます。

> 参考: [1時間でLINE BOTを作るハンズオン](https://qiita.com/n0bisuke/items/ceaa09ef8898bee8369d)

## おうむ返し

ngrokでトンネリングして試してみましょう。

コードが出来たら先ほどと同様に`Run > Start Debugging`でローカルサーバーを起動させます。

> ![](https://i.gyazo.com/8c3fede06514a675d9e4e02642ae4290.png)

今回(デフォ？)7071ポートで起動したのでngrokで7071ポートにトンネリングさせます。

```bash
$ npx ngrok http 7071
```

これは別のターミナルで実行しておいた方が良いと思います。

こんな感じで`https://35027c542caa.ngrok.io`というURLが発行されました。

> ![](https://i.gyazo.com/8651dc806841ab36a8fb14d6e1db9ace.png)

このURLを使ってLINE DevelopersでWebhook URLの設定を行います。

> ![](https://i.gyazo.com/d46598f4338ed46a7f10e3b4617dd706.png)

今回はエンドポイントが`/api/webhook`になっているので、この場合は`https://35027c542caa.ngrok.io/api/webhook`となります。

この状態（ローカルサーバー起動中 + ngrok起動中 + Webhook URL登録済）でLINE BOTに話しかけると無事におうむ返しをしてくれました。

> ![](https://i.gyazo.com/d6c9abd715d69d78ce13fed9791189bc.png)

ひとまずローカルで実行できて一安心。。

## デプロイして永続化する

デプロイは簡単で、もともとGitHub連携ありきでAzure Static Web Appsを作っているのでローカルで作ったものをプッシュすればOKです。

裏側でGitHub Actionsでビルドされる模様なので少し待ちましょう。

しばらくするとポータルの画面にもwebhook関数が表示されます。

> ![](https://i.gyazo.com/3d926dad972a18ff12e648c713352440.png)

`https://<APP ID>.azurestaticapps.net/api/webhook`にブラウザでアクセスすると`Hello LINE BOT!`の表示が見えるようになると思います。

> ![](https://i.gyazo.com/9c80e45f2094f0d157a2bbdb7e5e09f6.png)

最終的にこのアドレスをLINE BOTの管理画面からWebhook URLに登録します。

> ![](https://i.gyazo.com/28bfaca6c2235b47732c40b1fd5b98f7.png)

これでデプロイも完了し永続化まで出来ました。

念のため最後におうむ返しがちゃんと動くか確認しましょう。

## おまけ 環境変数の利用

ソースコードの`チャンネルシークレット`と`チャンネルアクセストークン`をコードに直書きしてましたが環境変数置き換えも出来ます。

```js
//省略

const config = {
    channelSecret: process.env.CHANNEL_SECRET,
    channelAccessToken: process.env.CHANNEL_ACCESS_TOKEN,
};

//省略
```

ポータルの構成の箇所から環境変数の追加ができます。

> ![](https://i.gyazo.com/b079ea0c9cee413d979ae753e0bb683c.png)

パスワードやトークンはここを活用すると良さそうです。

> ![](https://i.gyazo.com/e9e9499520fbea5514189a64c8f9aeb1.png)

最後にチェックをして保存を押すことで保存されるのですが、これを忘れると値が保存されないので注意です。

> ![](https://i.gyazo.com/69f252cf8ffc56d87c0db58b11ba2901.png)

## まとめと所感

Static Web AppsでLINE BOTを作成することが出来ました。

当たり前ですがMSエコシステムということでVSCodeを使わないと厳しいかもしれないですね。
この辺は趣味趣向ありそうです。

内部はAzure Functionsを使ってるっぽいですが、Azure Functionsのリソースは一個も作っていないので金額とかは特に発生せずに使えてるのかな......

Static Web Appsの管理画面がシンプルすぎて稼働状況もいまいちわからないですが、とりあえずしばらく稼働させてみます。