WebexのBotでカードやボタン付きメッセージを投稿するときに、これはやっておこう！

# 1. この記事でわかること

## 1.1. 最初の補足事項など

**この記事の内容はWebexの有償版と無償版両方に対応**しています。

Webexのメッセージング(グループチャット的なやつです)のREST APIでは、
Botを作って、グループチャットのスペース(チャネル、チャットルーム)にメッセージを投稿できます。
メッセージはカードUIやボタンのようなある程度奇麗な見た目にすることもできます。

> リンク先のガイドなどが英語のものが多いですが、
ブラウザ上で右クリック->[日本語に翻訳]などで、ブラウザについている翻訳機能でほぼ問題ない日本語で読めるはずです。

## 1.1. ここを読めばわかること

カードUIやボタン付きのメッセージを投稿する際のポイントなどは、
開発ガイドなどに書かれています。
そのなかで特に見落としがちなことで、私の経験上、
「これはやっとくべし」ということを**１つだけ**最後の章に記載します。

つまり、最後の章以外はおまけなので、
WebexのBotでカードやボタン付きのメッセージの投稿方法を既によく知っている方は、
最後の４章のみ読んでいただければと思います🎉
４章の内容もすでに熟知されていたって方は素晴らしいです✨

> 最後の章だけ読むなら３分もかからないと思います。

## 1.2. ここを読んでも詳細まではわからないこと

Botの実装方法詳細や、カードやボタン付きメッセージの投稿方法の詳細などは解説していません。
この辺りは概要のみざっくりと説明しています。

また、チェックボックスやテキストボックスなどを使ってユーザから入力を受け取るようなメッセージUIも作ることもできますが、
入力内容の受け取り方に関しては触れていません。

> まったく触れないのもアレなんで、軽くここで触れておくと、
入力された通知はWebhookで受け取って、入力内容の取得はREST APIで取得します。
WebexのWebhookの特徴は通知自体には、
意味をもたないidは含まれていますが、内容の中身詳細はWebhook通知には含まれていません。
セキュリティ・プライバシ上、正当なアクセストークンを利用して中身や詳細は再度取得しに行く必要があります。


# 2. 前提とする環境や基本知識(知っている人は読み飛ばす章)

以下に関して概要だけ記載しています。
知っている人は読み飛ばしましょう。

* Webexのアカウントの準備(無償版で可)
* Botアカウントのつくり方の概要
* Botアカウントでメッセージ投稿の例

## 2.1. Webexアカウントの準備

Botアカウントを作るためにはWebexのアカウントが必要です。
有償版でも無償版でも構いません。
無償版でもREST API使ったメッセージ投稿数に制限はありません。

無償アカウントであれば、以下のサイトなどで、メールアドレスを入力して、アカウントを作成できます。
**どちらのサイトで作ってもアカウントは共通**なので、**どちらかで作れば、どちらも利用できます**。
無償版を希望の場合はクレジットカード情報の入力などもないので、課金されることはありません。
メールアドレスはフリーのアドレスなどを利用しても作成できます(本記事の内容はGmailで作った無償アカウントで確認しています)。

* Webexの開発者向けのサイト: [Webex for Developers(https://developer.webex.com/)](https://developer.webex.com/)
* Web版のWebexサイト: https://web.webex.com/

## 2.2. Botアカウントのつくり方の概要

以下の手順でBot用のアカウントが作成できます。
Botアカウントはあるアプリがメッセージを投稿する際の、そのアプリ専用アカウントのイメージです。

1. [Webex for Developers(https://developer.webex.com/)](https://developer.webex.com/)へログイン。
1. [アプリの新規作成(https://developer.webex.com/my-apps/new)](https://developer.webex.com/my-apps/new)へアクセス([画面右上アイコン] -> [My Webex Apps] -> [Create a New App])。
1. [Creat a Bot]を選んで、必要項目を入力して、[Add Bot]を押す。
1. [Bot access token]という項目がAPI実行用のアクセストークンです。

## 2.2. Botアカウントでメッセージ投稿の例

REST APIを使って投稿できます。
HTTPS上に流れるメッセージをざっくり書くと以下のような感じです。
関係ない部分は、かなり大胆に省略しています。

``` http:Botによるメッセージ投稿のリクエスト例
POST /v1/messages HTTP/1.1
Host: webexapis.com:443
Authorization: Bearer <Botのアクセストークン>
Content-Type: application/json; charset=utf-8
Accept: application/json

{
    "toPersonEmail": "メールアドレス形式のWebexのアカウントID(実験の時は自分のとか)",
    "markdown": "**やっほー🎉**"
}
```

ざっくり解説すると、
`https://webexapis.com/v1/messages`に対して、POSTメソッドで、
`Authorization: Bearer <Botのアクセストークン>`というヘッダーを指定して、
以下のJSONのボディーを送っています。
`<Botのアクセストークン>`は、2.2項の手順などで作成したBotのアクセストークンです。

``` json:BodyのJSONの例
{
    "toPersonEmail": "メールアドレス形式のWebexのアカウントID(実験の時は自分のとか)",
    "markdown": "**やっほー🎉**"
}
```

これで、Botから自分宛にメッセージが届きます(自分のメールアドレス形式のWebexのアカウントIDを指定しているため)。
メッセージの受信の確認はWebexのクライアントアプリを利用するか、
[Web版のWebex(https://web.webex.com/)](https://web.webex.com/)で確認できます。
いずれも無償アカウント利用の場合は、無償で利用できます。

Botはグループチャットのスペース(チャネル、チャットルーム)にもメッセージを投稿できます。
その場合は、該当のBotをスペース(チャネル、チャットルーム)にメンバーとして追加しておく必要があります。
Botにもメールアドレス形式のWebexのアカウントID("`自分で決めたBotUser名@webex.bot`"形式)があるので、これを使ってスペースにメンバーとして追加できます。
Botアカウント作った画面の[Bot username]に表示されているやつです。

スペースにメッセージを投稿する場合は、BodyのJSONは以下のような感じになります。

``` json:スペースにメッセージを投稿する場合のJSONの例
{
    "roomId": "スペースのID",
    "markdown": "**やっほー🎉**"
}
```

アプリがスペースのIDを知っている必要がありますが、知る方法はだいたい以下になります。

* Bot自身がAPIを使って作成したりメンバー管理したスペースであれば、スペース作成時にスペースのIDが取得できる。
* Botが人間のユーザから話しかけられたりした際に反応するなどのパターンでは、話しかけられた際のアプリへの通知メッセージ(Webhook)でスペースのIDが取得できる。
* Botがスペースに追加された瞬間のアプリへの通知メッセージ(Webhook)からスペースのIDが取得できる。
* スペースを管理するAPIなどで取得する(例えば、[List Rooms API](https://developer.webex.com/docs/api/v1/rooms/list-rooms))。

# 3. カードやボタン付きメッセージの投稿方法概要(知っている人は読み飛ばす章)

Webexのメッセージング機能ではAPI経由でカードやボタン付きメッセージを投稿できます。
これによってユーザのメッセージの視認性や操作性の大幅な向上が期待できます。

REST APIによるメッセージ投稿はBot以外からも可能ですが、
Botによるメッセージ投稿がアプリ連携では圧倒的に多いと思うので、とりあえずBot前提で説明します。
※ 誰としてメッセージを投稿するかはAPI的にはアクセストークンの違いだけで、どうアクセストークンを作成するかの違いくらいの差です。

## 3.1. Webexのカードやボタン付きメッセージはAdaptive Cardsベース

Webexのメッセージング機能では、**Adaptive Cards**というUIを構成する仕組みを利用します。
ざっくり言うと、JSONを利用してシンプルなUIを作るイメージです。

Adaptive Cardsの公式サイトにサンプルがたくさんあるので、それを見るとイメージしやすいと思います。
[Adaptive Cardsのサンプル(https://adaptivecards.io/samples/)](https://adaptivecards.io/samples/)

Adaptive Cardsは、それほど複雑な仕組みではないですが、
完全に極めるにはおそらく１時間から４時間くらいかかると思います。
[Adaptive Cardsの公式サイト(https://adaptivecards.io/)](https://adaptivecards.io/)

実際の開発では、デザインツールなどを活用して、デザインすることになると思います。

## 3.2. WebexのAdaptive Cardsデザインツール

WebexのAdaptive Cardsデザインツールが公開されているので、
ツールを使ってベースのデザインを作るのがやりやすいと思います。
その後、動的に変化する部分はテンプレート化して、アプリ側で動的に変える処理などは必要かと思います。

[WebexのAdaptive Cardsデザインツール(https://developer.webex.com/buttons-and-cards-designer)](https://developer.webex.com/buttons-and-cards-designer)

とりあえず、シンプルなカードっぽいものをデザインしてみました。

![WebexのAdaptive Cardsデザインツール](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211666/c798fe93-f9cc-31bf-afa7-80ff7297f14d.png)

デザイナーでデザインしたら、上の方に[Copy card payload]ボタンがあるので、
ここを押すと対応するJSONが取得できます。
以下は、上のスクショに対応するAdaptive CardsのJSONです。
インデント付になっていますが、実際にメッセージ投稿する際のリクエスト時には、
インデントは無駄になるので、インデントや改行なしでリクエストするのがかなりお勧めです。

``` json:AdaptiveCardsのJSON例
{
    "type": "AdaptiveCard",
    "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
    "version": "1.2",
    "body": [
        {
            "type": "TextBlock",
            "text": "🎉Qiitaに記事を投稿しました🎉",
            "wrap": true,
            "size": "Large",
            "weight": "Bolder",
            "color": "Accent"
        },
        {
            "type": "ColumnSet",
            "columns": [
                {
                    "type": "Column",
                    "width": "auto",
                    "items": [
                        {
                            "type": "TextBlock",
                            "text": "⏰",
                            "wrap": true,
                            "size": "ExtraLarge"
                        }
                    ]
                },
                {
                    "type": "Column",
                    "width": "stretch",
                    "items": [
                        {
                            "type": "TextBlock",
                            "text": "公開日時:",
                            "wrap": true,
                            "weight": "Bolder",
                            "color": "Good"
                        },
                        {
                            "type": "TextBlock",
                            "text": "2021年12月04日(土曜日)",
                            "wrap": true,
                            "color": "Attention",
                            "size": "Large",
                            "weight": "Bolder",
                            "spacing": "None"
                        }
                    ]
                }
            ],
            "separator": true
        },
        {
            "type": "ActionSet",
            "actions": [
                {
                    "type": "Action.OpenUrl",
                    "title": "記事はこちら",
                    "url": "https://qiita.com/thrzn41/items/9507ba84576ae8b5c43a"
                }
            ],
            "separator": true
        }
    ]
}
```

## 3.3. WebexのAdaptive Cardsを投稿する方法の概要

基本的には通常のメッセージ投稿と同じです。
BodyのJSONにAdaptive CardsのJSONを`attachments`として入れ込みます。

``` json:AdaptiveCards付きでメッセージを投稿する例
{
    "toPersonEmail": "メールアドレス形式のWebexのアカウントID(実験の時は自分のとか)",
    "markdown": "**やっほー🎉**",
    "attachments": [
        {
            "contentType": "application/vnd.microsoft.card.adaptive",
            "content": 

#### この部分にAdaptive Cards のJSONを入れる ####

        
        }
    ]
}
```

こんな感じでメッセージを投稿すると、カードUIとしてメッセージが投稿されます。

![投稿されたメッセージの例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211666/7db0f8c8-40de-84bd-5b93-3afcd2f12dc5.png)


# 4. WebexでAdaptive Cards投稿時に、これはやるべし

注意すべき点などは、WebexのボタンやカードUI開発ガイドの項目に基本的には書かれていますが、
見落とされがちな記述もあります。

[WebexのボタンやカードUIのガイド(https://developer.webex.com/docs/buttons-and-cards)](https://developer.webex.com/docs/buttons-and-cards)

## 4.1. あまり見落とされないポイント

> すべて執筆時点でのポイントで今後変わる可能性はあります。

* 投稿するメッセージのBodyのJSON全体のサイズは22740バイト(約22KB)までなので、インデントなど無駄は極力排除する。
* 画像の利用は控えめにする。BodyのJSON全体サイズと参照先の画像サイズの合計はおおむね80KB程度までに抑えるのがベストプラクティス。
* Adaptive Cardsで規定されている機能でWebexのAdaptive Cardsでは利用できない機能もある。

## 4.2. 見落とされがちなポイント

投稿されたメッセージを閲覧するWebexのクライアントアプリ側で、
なんらかの理由でAdaptive Cardsがレンダリングできない場合は、
代替として、`text`や`markdown`で指定したメッセージが表示されます。
またメッセージ受信の通知画面にも`text`や`markdown`で指定した内容が表示されます。

これがかなり重要で、Adaptive Cardsが必ずしも表示されるとは限らないという意味になります。
`markdown`などで意味のあるメッセージを指定しておく必要があります。

失敗する主な理由は以下くらいかと思いますが、ほかにもあるかもしれません。

* 古いWebexアプリを利用している(現在ではほとんどないケース)。
* なんらかの理由でAdaptive Cardsメッセージのキャッシュが壊れた。

以下はダメな例で、「カードを投稿しました」というメッセージにはユーザにとって意味はありません。


``` json:AdaptiveCards付きでメッセージを投稿するダメな例
{
    "toPersonEmail": "メールアドレス形式のWebexのアカウントID(実験の時は自分のとか)",
    "markdown": "**カードを投稿しました🎉**",
    "attachments": [
        {
            "contentType": "application/vnd.microsoft.card.adaptive",
            "content": 

#### この部分にAdaptive Cards のJSONを入れる ####

        
        }
    ]
}
```

## 4.3. 見落とされがちなポイントへの対策方法

Adaptive Cardsをデザインする場合は、それと同じ意味の`markdown`もデザインして、
先に`markdown`からテストするのがお勧めです。

以下のAdaptive Cardsを投稿する例で同じ意味の`markdown`を考えてみます。

![投稿されたメッセージの例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211666/7db0f8c8-40de-84bd-5b93-3afcd2f12dc5.png)

たとえばこんな感じになると思います。

``` json:スペースにmarkdownメッセージを投稿する場合のJSONの例
{
    "toPersonEmail": "メールアドレス形式のWebexのアカウントID(実験の時は自分のとか)",
    "markdown": "**🎉Qiitaに記事を投稿しました🎉**  \n  \n⏰公開日時: **2021年12月04日(土曜日)**  \n[記事はこちら](https://qiita.com/thrzn41/items/9507ba84576ae8b5c43a)"
}
```

こんな感じで表示されます。

![markdownの表示例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211666/19cfb914-0c11-4897-a920-d0552a123f53.png)

Adaptive Cardsで投稿したほうが視認性と操作性は向上するの、
このメッセージにAdaptive Cardsを入れておけば、
通常はAdaptive Cardsが表示され、
何らかの理由でAdaptive Cardsが表示できない場合はmarkdownが表示されるようになります。

以下が対策版の例です。実際にはインデントや改行はなしでメッセージ投稿のAPIを実行するのを推奨します。

``` json:AdaptiveCards利用時の理想的な投稿の例
{
    "toPersonEmail": "メールアドレス形式のWebexのアカウントID(実験の時は自分のとか)",
    "markdown": "**🎉Qiitaに記事を投稿しました🎉**  \n  \n⏰公開日時: **2021年12月04日(土曜日)**  \n[記事はこちら](https://qiita.com/thrzn41/items/9507ba84576ae8b5c43a)",
    "attachments": [
        {
            "contentType": "application/vnd.microsoft.card.adaptive",
            "content": 
            {
                "type": "AdaptiveCard",
                "$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
                "version": "1.2",
                "body": [
                    {
                        "type": "TextBlock",
                        "text": "🎉Qiitaに記事を投稿しました🎉",
                        "wrap": true,
                        "size": "Large",
                        "weight": "Bolder",
                        "color": "Accent"
                    },
                    {
                        "type": "ColumnSet",
                        "columns": [
                            {
                                "type": "Column",
                                "width": "auto",
                                "items": [
                                    {
                                        "type": "TextBlock",
                                        "text": "⏰",
                                        "wrap": true,
                                        "size": "ExtraLarge"
                                    }
                                ]
                            },
                            {
                                "type": "Column",
                                "width": "stretch",
                                "items": [
                                    {
                                        "type": "TextBlock",
                                        "text": "公開日時:",
                                        "wrap": true,
                                        "weight": "Bolder",
                                        "color": "Good"
                                    },
                                    {
                                        "type": "TextBlock",
                                        "text": "2021年12月04日(土曜日)",
                                        "wrap": true,
                                        "color": "Attention",
                                        "size": "Large",
                                        "weight": "Bolder",
                                        "spacing": "None"
                                    }
                                ]
                            }
                        ],
                        "separator": true
                    },
                    {
                        "type": "ActionSet",
                        "actions": [
                            {
                                "type": "Action.OpenUrl",
                                "title": "記事はこちら",
                                "url": "https://qiita.com/thrzn41/items/9507ba84576ae8b5c43a"
                            }
                        ],
                        "separator": true
                    }
                ]
            }
        }
    ]
}
```

## 4.4. ユーザ入力を受け取る場合などの対策方法

Adaptive Cardsではユーザからの入力を受け取るメッセージを構成することもできます。
例えば以下のような感じです。

![入力のあるAdaptive Cardsの例](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211666/c764e728-5572-4cc1-84df-11c066c25bb3.png)

この場合は、同じ機能性を持つ`markdown`は実装できません。
対策としては、別途入力フォームはWebページとしても準備しておき、
チャット上のAdaptive Cardsから入力できる人は、そのまま入力してもらう、
Adaptive Cardsがなんらかの理由で表示できない場合に備えて、
Webページの入力フォームに誘導する`markdown`を表示する方法が考えられます。

上記のやり方が適切だとは思っていますが、ややいいかげんなやり方としては、
カードUIが表示されない場合は、
[Web版のWebex(https://web.webex.com/)](https://web.webex.com/)で見てくださいと
誘導する`markdown`を表示する方法も考えられます。
Web版のWebexでは、Adaptive Cardsが表示できる可能性が結構高いので、
この方法もありといえばありですが、お勧めではありません。