Cisco WebexでQuizを出題する仕組みを作ってみた

この記事はシスコの有志による Cisco Systems Japan Advent Calendar 2020 1枚目の 17日目として投稿しています。

2020年版(1枚目): https://qiita.com/advent-calendar/2020/cisco
2020年版(2枚目): https://qiita.com/advent-calendar/2020/cisco2

以下、昨年までのものです。

2019年版: https://qiita.com/advent-calendar/2019/cisco
2018年版: https://qiita.com/advent-calendar/2018/cisco
2017年版: https://qiita.com/advent-calendar/2017/cisco

## はじめに

`Cisco Webex`で以下のようにQuizを出題するBotアプリケーションを作ってみました。トレーニングやセミナーの中で参加者の理解度を計ったり、レクリエーションに使えるのではないかと思っています:yum:

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176968/0e71c5f2-6fa2-9c74-94d5-a9de12de1cc0.png)

Botアプリケーションのデプロイ先は`Vercel Serverless Functions`にします。

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

Cisco Webex Botの作り方とBotアプリケーションを`Vercel Serverless Functions`にデプロイする方法は以下にまとめたので先にご一読ください。

**[Qiita] Vercel Serverless FunctionsでCisco Webex Botを作る**
https://qiita.com/tetsusat/items/6cd2234fecd5af9441c4

Quizは以下のようなYAML形式のファイルを作成してアプリケーションのビルド時に読み込ませています。

```config/quiz.yaml
---
title: CiscoカルトQ
quiz:
  - question: かつてCiscoのCDO（最高開発責任者）を務めたMario MazzolaがCiscoに買収される以前に社長兼CEO務めていた会社は以下のいずれか？
    choices:
      - Kalpana
      - Grand Junction
      - Crescendo Communications
      - Granite Systems
      - StrataCom
    answers: c
...
```

## Botアプリケーションの全体像

Botアプリケーションの全体像を前半と後半に分けてみていきます。

前半はBotにメンションで呼びかけてQuizを出題してくれるまでで、下図の赤枠で囲んだ部分になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176968/8b659784-a58b-ec95-929d-4cd0d3bc4cb2.png)

このメンションを受けるために、あらかじめ用意しておいたBotのAccess Tokenでメッセージが作成されたときに呼び出されるWebhookを作成します。パラメータは以下のような感じです。

* name: Webhook for messages（任意）
* targetUrl: https://{your_app}.vercel.app/api/webhook1
* resourace: messages
* event: created
* filter: mentionedPeople=me

実際にBotを呼びかけるとこのWebhookが呼ばれます。コンテンツの具体例は以下のようになります。

```
{
  id: 'Y2lzY29zcGFyazovL3VzL1dFQkhPT0svNzYzZTk5ZjAtYzliOS00ZjNhLWIwNjQtMWU2ZTdjOWI2Mjhh',
  name: 'Cult Q message webhook',
  targetUrl: 'https://{your_app}.vercel.app/api/webhook1',
  resource: 'messages',
  event: 'created',
  filter: 'mentionedPeople=Y2lzY29zcGFyazovL3VzL1BFT1BMRS9hYjIwNzljNS00YTUyLTQyMDctODhmNi1iODdmNTAzYzNiYWU',
  orgId: 'Y2lzY29zcGFyazovL3VzL09SR0FOSVpBVElPTi8xZWI2NWZkZi05NjQzLTQxN2YtOTk3NC1hZDcyY2FlMGUxMGY',
  createdBy: 'Y2lzY29zcGFyazovL3VzL1BFT1BMRS9hYjIwNzljNS00YTUyLTQyMDctODhmNi1iODdmNTAzYzNiYWU',
  appId: 'Y2lzY29zcGFyazovL3VzL0FQUExJQ0FUSU9OL0MzMmM4MDc3NDBjNmU3ZGYxMWRhZjE2ZjIyOGRmNjI4YmJjYTQ5YmE1MmZlY2JiMmM3ZDUxNWNiNGEwY2M5MWFh',
  ownedBy: 'creator',
  status: 'active',
  created: '2020-12-01T09:47:12.257Z',
  actorId: 'Y2lzY29zcGFyazovL3VzL1BFT1BMRS82NTc2Njg1YS1hNGE1LTQzMjktYjMwOS1hODEwMjNiNzdmMzk',
  data: {
    id: 'Y2lzY29zcGFyazovL3VzL01FU1NBR0UvNDc3ODYzZTAtM2RkNi0xMWViLTk2NDUtZGZmMzhkNDRlNTli',
    roomId: 'Y2lzY29zcGFyazovL3VzL1JPT00vNmNlMzZiODAtYTFhOC0xMWU2LWFkZDYtMzU1NmUzN2QyNzJj',
    roomType: 'group',
    personId: 'Y2lzY29zcGFyazovL3VzL1BFT1BMRS82NTc2Njg1YS1hNGE1LTQzMjktYjMwOS1hODEwMjNiNzdmMzk',
    personEmail: '<removed>',
    mentionedPeople: [
      'Y2lzY29zcGFyazovL3VzL1BFT1BMRS9hYjIwNzljNS00YTUyLTQyMDctODhmNi1iODdmNTAzYzNiYWU'
    ],
    created: '2020-12-14T06:33:29.630Z'
  }
}
```

コンテンツの中の`data.id`を取り出して、`Cisco Webex`のAPIを通じてメッセージの詳細を取得します。

https://developer.webex.com/docs/api/v1/messages/get-message-details

メッセージの具体例は以下のようになります。

```
{
  id: 'Y2lzY29zcGFyazovL3VzL01FU1NBR0UvNDc3ODYzZTAtM2RkNi0xMWViLTk2NDUtZGZmMzhkNDRlNTli',
  roomId: 'Y2lzY29zcGFyazovL3VzL1JPT00vNmNlMzZiODAtYTFhOC0xMWU2LWFkZDYtMzU1NmUzN2QyNzJj',
  roomType: 'group',
  text: 'cultq quiz 1',
  personId: 'Y2lzY29zcGFyazovL3VzL1BFT1BMRS82NTc2Njg1YS1hNGE1LTQzMjktYjMwOS1hODEwMjNiNzdmMzk',
  personEmail: '<removed>',
  html: '<p><spark-mention data-object-type="person" data-object-id="Y2lzY29zcGFyazovL3VzL1BFT1BMRS9hYjIwNzljNS00YTUyLTQyMDctODhmNi1iODdmNTAzYzNiYWU">cultq</spark-mention> quiz 1</p>',
  mentionedPeople: [
    'Y2lzY29zcGFyazovL3VzL1BFT1BMRS9hYjIwNzljNS00YTUyLTQyMDctODhmNi1iODdmNTAzYzNiYWU'
  ],
  created: '2020-12-14T06:33:29.630Z'
}
```

コンテンツの中の`text`の`cultq quiz 1`からクイズIDが分かるので、前述の`config/quiz.yaml`からこれに対応するクイズを見つけます。ちなみに、`cultq`はBot名なので、登録したBot名に応じてここは変わります。

応答するフォーム形式のメッセージはMessages APIのattachmentsを付与する形で実現します。attachmentsコンテンツは`Adaptive Cards`仕様を使ってを定義します。

https://developer.webex.com/docs/api/v1/messages/create-a-message

**[Cisco WebEx for Developer] Buttons and Cards**
https://developer.webex.com/docs/api/guides/cards

`Cisco Webex`のAPIを通じてメッセージを送信します。送信するルームは前述のWebhookのあったコンテンツの中の`roomId`から取得できます。

https://developer.webex.com/docs/api/v1/attachment-actions/create-an-attachment-action

前半は以上までとなります。

後半はQuizにフォームから回答を選択してSubmitすると回答の正誤に応じてスレッドに返事してくれるまでで、下図の赤枠で囲んだ部分になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176968/b838de46-93c8-4e1c-f5a9-90f003583368.png)

ユーザーがフォームをSubmitすることを`Attachment Action`と呼びます。クイズの回答内容を取得できるように`Attachment Action`が作成されたときに呼び出されるWebhookを作成します。パラメータは以下のような感じです。

* name: Webhook for attachment actions（任意）
* targetUrl: https://{your_app}.vercel.app/api/webhook2
* resourace: attachmentActions
* event: created

ユーザーがフォームをSubmitするとこのWebhookが呼ばれます。コンテンツの具体例は以下のようになります。

```
{
  id: 'Y2lzY29zcGFyazovL3VzL1dFQkhPT0svM2U3Y2YxM2QtZDFlOC00YmEyLTg5MjEtNjg0ZDVhYTAyZjgz',
  name: 'Cult Q attachment webhook',
  targetUrl: 'https://{your_app}.vercel.app/api/webhook2',
  resource: 'attachmentActions',
  event: 'created',
  orgId: 'Y2lzY29zcGFyazovL3VzL09SR0FOSVpBVElPTi8xZWI2NWZkZi05NjQzLTQxN2YtOTk3NC1hZDcyY2FlMGUxMGY',
  createdBy: 'Y2lzY29zcGFyazovL3VzL1BFT1BMRS9hYjIwNzljNS00YTUyLTQyMDctODhmNi1iODdmNTAzYzNiYWU',
  appId: 'Y2lzY29zcGFyazovL3VzL0FQUExJQ0FUSU9OL0MzMmM4MDc3NDBjNmU3ZGYxMWRhZjE2ZjIyOGRmNjI4YmJjYTQ5YmE1MmZlY2JiMmM3ZDUxNWNiNGEwY2M5MWFh',
  ownedBy: 'creator',
  status: 'active',
  created: '2020-12-02T12:05:01.672Z',
  actorId: 'Y2lzY29zcGFyazovL3VzL1BFT1BMRS82NTc2Njg1YS1hNGE1LTQzMjktYjMwOS1hODEwMjNiNzdmMzk',
  data: {
    id: 'Y2lzY29zcGFyazovL3VzL0FUVEFDSE1FTlRfQUNUSU9OLzhhYzc4ZTIwLTNkZGUtMTFlYi05M2FiLWE1Nzk0M2Q3MzRkZg',
    type: 'submit',
    messageId: 'Y2lzY29zcGFyazovL3VzL01FU1NBR0UvNDg4YTIyMDAtM2RkNi0xMWViLTgzOWMtYWZjYjI0YTJiMmZk',
    personId: 'Y2lzY29zcGFyazovL3VzL1BFT1BMRS82NTc2Njg1YS1hNGE1LTQzMjktYjMwOS1hODEwMjNiNzdmMzk',
    roomId: 'Y2lzY29zcGFyazovL3VzL1JPT00vNmNlMzZiODAtYTFhOC0xMWU2LWFkZDYtMzU1NmUzN2QyNzJj',
    created: '2020-12-14T07:32:38.530Z'
  }
}
```

コンテンツの中の`data.id`を取り出して、`Cisco Webex`のAPIを通じて`Attachment Action`の詳細を取得します。

`Attachment Action`の具体例は以下のようになります。

```
{
  id: 'Y2lzY29zcGFyazovL3VzL0FUVEFDSE1FTlRfQUNUSU9OLzhhYzc4ZTIwLTNkZGUtMTFlYi05M2FiLWE1Nzk0M2Q3MzRkZg',
  type: 'submit',
  messageId: 'Y2lzY29zcGFyazovL3VzL01FU1NBR0UvNDg4YTIyMDAtM2RkNi0xMWViLTgzOWMtYWZjYjI0YTJiMmZk',
  inputs: { '1': 'c' },
  personId: 'Y2lzY29zcGFyazovL3VzL1BFT1BMRS82NTc2Njg1YS1hNGE1LTQzMjktYjMwOS1hODEwMjNiNzdmMzk',
  roomId: 'Y2lzY29zcGFyazovL3VzL1JPT00vNmNlMzZiODAtYTFhOC0xMWU2LWFkZDYtMzU1NmUzN2QyNzJj',
  created: '2020-12-14T07:32:38.530Z'
}
```

コンテンツの中の`inputs`からユーザーがSubmitしたクイズの番号と回答が分かるので、前述の`config/quiz.yaml`と比較して回答が正誤を確認します。

ちなみ、複数選択可にしておくと、`inputs: { '2': 'b,c' }`みたいにカンマ区切りになります。

最後に、正誤に応じて`Cisco Webex`のAPIを通じてスレッドとしてメッセージを送信します。直前に取得したAttachment Actionのコンテンツの中の`messageId`のから親コンテンツのidが分かるので`parentId`にこれを指定することでスレッドとしてメッセージを送れます。

https://developer.webex.com/docs/api/v1/messages/create-a-message

## 成果物

実際のアプリケーションは以下に公開してます。とりあえず動作するプロトタイプ位なイメージでおおらかな気持ちでみていただけると幸いです:sweat_smile:

**[GitHub] tetsusat/webex-bot-quiz**
https://github.com/tetsusat/webex-bot-quiz

おしまい