Vercel Serverless FunctionsでCisco Webex Botを作る

## はじめに

今回は`Vercel Serverless Functions`を使って`Cisco Webex Bot`を作ってみます。
メンションによる呼びかけに応答する対話型のBotを作るには`Cisco Webex`クラウドからのWebhookを受信できる必要があるため、Botアプリケーションを動作させる環境を用意するのが若干敷居が高いのですが、`Vercel Serverless Functions`を使うことで簡単にできます。

## Vercel Serverless Functionsとは

`Vercel`は`Next.js`や`Nuxt.js`などのスタティックアプリケーションをホストするためのクラウドプラットフォームですが、`Serverless Functions`というNode.jsやGo等をサポートするFaaSも提供していて、今回はBotのWebhookを受けるのにこれを活用します。

**[Vercel] Serverless Functions**
https://vercel.com/docs/serverless-functions/introduction

2020年12月現在、クレジットカードの登録なしで無償（Hobby）アカウントが作れて、無償枠でもBotを運用するには十分な機能とリソースが使えます。

**[Vercel] Price**
https://vercel.com/pricing

**[Vercel] Limit**
https://vercel.com/docs/platform/limits

## 対話型Botアプリケーションの流れ

Botにメンションしてメッセージ（もしくは1対1メッセージ）を送るとあらかじめ登録してあるWebhookが呼ばれます。このWebhookのBodyにメッセージのIDが含まれるので、このIDを用いてBotアプリケーションからWebex APIを通じてメッセージの詳細を取得します。最後に、このメッセージの内容に応じて、BotアプリケーションからWebex APIを通じてメッセージを返信する流れとなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176968/dbe85998-5b40-bdfd-2d2f-71497f48e162.png)

## 作成するBotアプリケーション

VercelでBotアプリケーションを動作させることが記事の一番の目的なので、Bot自体はとてもシンプルに以下のようなメンションで呼びかけられたら同じメッセージをオウム返しするいわゆるエコーBotを作ってみたいと思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176968/7cef4c8b-886c-d56a-484d-a3292647793e.png)

## Botアカウントを作る

`Cisco Webex for Developer`ポータル（`developer.webex.com`）からBotアカウントを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176968/403a0826-1dea-ce27-6be2-457a2a163356.png)

https://developer.webex.com/my-apps/new/bot

適宜、以下のパラメータを設定してからページ下部にある`Add bot`ボタンをクリックします。

- Bot name
- Bot username
- Icon
- Description

`Bot name`はメンションでBotを呼びかけるときに使います。`Bot name`は他のBotと重複した名前であっても付けることもできます。
`Bot username`はルームに追加するときに使います。`Bot username`は一意である必要があり、すでにあるBotと重複した名前を付けることができません。

`Bot access token`が生成されるので、後に使うためにこれをコピーして保存しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176968/3bfdaaf2-e2d6-b1c1-9ba3-4c04dddc42b4.png)

## Webhookを作る

同様に`Cisco Webex for Developer`ポータル（`developer.webex.com`）からWebhookを作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176968/9f49c5ef-523a-7669-d085-bcc5eb1631fd.png)

https://developer.webex.com/docs/api/v1/webhooks/create-a-webhook

以下のようにパラメータを設定してページ下部にある`Run`ボタンをクリックします。

**Header**

- Authorization: Bot Access Tokenを入力

**Body** 

- name: 任意
- tergetUrl: https://\<your_app>\.vercel.app/api/webhook
- resource: messages
- event: created
- filter: mentionedPeople=me

`Authorization`には直前に作った`Bot Access Token`を入力してください。最初は個人のAccess Tokenがセットされているので明示的にBotのAccess Tokenを設定するように注意してください。

Botはメンションされたメッセージ（1対1のメッセージを含む）の他に、自分のメッセージにも反応するため、メンションされたメッセージにのみ反応するようにfilterとして`mentionedPeople=me`を設定します。

## Botアプリケーションを作る

`Vercel Serverless Funcitons`は以下の言語を公式サポートしますが、`Cisco Webex`の公式SDKもあって開発しやすいNode.jsを使うことにします。

- Node.js
- Go
- Python
- Ruby

前述のとおりBot作成にあたっては、Cisco Webex公式のNode.js SDKを利用します。 

**[developer.webex.com] Node.js SDK**
https://developer.webex.com/docs/sdks/node

**[GitHub] webex/webex-js-sdk**
https://github.com/webex/webex-js-sdk

Vercelにアプリケーションをデプロイする方法は、Vercek CLIによる方法とGitHubなどのGitプロジェクトと連携させる方法がありますが、今回はVercel CLIを使っていきます。

まず、Vercel CLIをインストールします。

```
$ npm i -g vercel
```

ログインします。Vercelのアカウントはあらかじめ作っておいてください。

```
$ vercel login hoge@example.com
```

Vercelのアカウントと紐づいているメールアドレスを指定します。
登録しているメールアドレスに確認メールが届くので下部にある`Verify`ボタンをクリックする（リンクが貼られてるURLを叩く）ことでログインできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176968/c24f2ed3-f52b-8504-2ec3-d1b2fcd54a8f.png)

アプリケーションのディレクトリを作成して、移動します。

```
$ mkdir yamabiko-bot
```

```
$ cd yamabiko-bot
```

Node.jsプロジェクトを初期化します。

```
$ npm init -y
```

前述の`Cisco Webex`公式のNode.js SDKをインストールします。

```
$ npm install webex
```

apiディレクトリを作成します。

```
$ mkdir api
```

apiディレクトリに以下のようなプログラムを用意します。
~~面倒くさいので~~分かりやすさを優先してエラー処理は割愛してます。

```js:api/webhook.js
const webex = require('webex/env');

module.exports = async (req, res) => {
  const { body } = req
  console.log(body)

  const message = await webex.messages.get(body.data.id);

  const result = await webex.messages.create({
    text: message.text.replace(process.env.BOT_NAME, '').trim(),
    roomId: body.data.roomId
  });

  console.log(result)

  res.send('')
}
```

このままでも暗黙のルーティングルールによって動作するのですが、呼び出せるHTTPメソッドをPOSTに限定するためにも、プロジェクトのトップディレクトリにルーティングルールの設定をします。

```vercel.json
{
  "routes": [
    {
      "src": "/api/webhook",
      "methods": ["POST"],
      "dest": "/api/webhook.js"
    }
  ]
}
```

Vercelのためにアプリケーションを初期セットアップします。
対話形式でいろいろ聞かれますが、とりあえずすべてデフォルトのものを受け入れてOKです。

```
$ vercel
```

環境変数を設定します。
環境変数は、VercelのWebダッシュボード（`https://vercel.com/dashboard`）から、対象のプロジェクトを選択し、`Project Settings`にある`Environment Variables`で設定できます。

Bot名はプレインテキスト環境変数として設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176968/aeac2786-275f-8776-c934-9289bad5cbfb.png)

`Bot Access Token`はシークレット環境変数として設定します。`Add Secret`でSecretを作成すると@hogehogeみたいな名前がつくので、それを選択します。

ちなみに、`WEBEX_ACCESS_TOKEN`という環境変数はプログラムの中に見当たりませんが、Cisco Webex公式のNode.js SDKが内部で参照するものになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176968/03ba8497-3bef-2d78-c4eb-269a0b836186.png)

環境変数を反映するために、もう一度デプロイします。

本来であればもう一度`vercel`コマンドを実行していったんステージ環境で動くことを試してから`vercel --prod`コマンドを実行してプロダクション環境にデプロイするところですが、Webhookを作成した際にプロダクションのURLにリンクしてしまっているので一気にプロダクション環境にデプロイします。

```
$ vercel --prod
```

`Cisco Webex`のクライアントでBotを使いたいスペースに追加してあげます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176968/1bcf73f7-52b7-f8d5-b2aa-a0ce4abed5e4.png)

これで、冒頭で紹介したような呼びかけたメッセージをそのままオウム返しするエコーBotが使えるようになります。

おしまい