Teamsアプリの全体像を把握しよう【機能紹介から実装概要まで】
この記事では、謎に複雑でリッチな機能を持たせることができるTeamsアプリについて、正確性はいったん括弧に入れ、わかりやすさに95%ぐらいを振った上で、その全体像を紹介できればと思っています。
想定する読み手は、(幸運なことに)社内サービスのTeamsアプリ化を任されたIT担当、もしくはエンジニアです。公式ドキュメントでは不可能であろう適当解説を試みることで、直感的な理解の助けになればと思います。
Teamsアプリを理解する上で重要なのは、以下の4点だと考えています。
- 実装できる機能の種類
- 各機能をTeams内で表示できる箇所
- 各機能の実装の概要
- 各機能における認証の概要
上から順に把握をしていくことで、Teamsアプリの謎を解き明かすことができるはずです。では、さっそく機能の種類について見ていきましょう。
この記事はパーソルホールディングス株式会社に所属するエンジニアが、業務時間を頂いた上で書いています。もし会社に興味を持った方がいたら、dodaなどからご応募いただけると嬉しいです。
実装できる機能の種類
Teamsアプリでは大きく4つの機能をもたせることができます。
- タブ機能
- ボット機能
- メッセージ拡張機能
- コネクタ機能
重要なのは1つのアプリに複数の機能をもたせることができる点です。例えば「2つのタブ機能と1つのボット機能、1つのメッセージ拡張機能」を1つのTeamsアプリとしてまとめて提供することができます。
ただし、ボット機能とメッセージ拡張機能については1アプリにそれぞれ1つしか持たせられない制限があります。(将来的にこの制限が解除される可能性があるようなニュアンスを公式ドキュメントからは感じます)
それぞれの機能について、画像と共に紹介します。
タブ機能
タブ機能は任意のWebページをTeams内で表示できる機能です。
主に、既存のWebサービスをTeams内で表示する用途に使われます。
後ほど少しだけ詳しく紹介しますが、タブ機能は会議中画面でも表示させることが可能です。以下のようなイメージですが、こちらも基本的には何かしらのWebページを表示しているだけです。
とりあえず、ざっくりとタブ機能 = Webページを表示する機能でOKです。
ボット機能
名前の通りです。ボットとの1:1の会話や、ボットを含んだグループチャットが可能になる機能です。大規模言語モデルの登場により、今後より活躍が期待される機能ですね。
任意のチャンネルから@SomeBot
などのメンションで呼び出したり、ボットに対してのコマンド(/help
で使い方を説明する回答を返す)などを定義することができます。
メッセージ拡張機能
メッセージ拡張機能は、痒いところに手を届かせる機能だと思ってください。大きく3つの機能をまとめて「メッセージ拡張機能」と呼ばれています。それぞれ紹介します。
1. メッセージにコンテンツを挿入する機能 (Search Command)
この機能を使うことで、Teams上で外部サービスのデータを調べ、それをメッセージ内のコンテンツとして挿入することができます。
例えば「Qiita Teamsアプリ」があったとすると、Qiitaの記事をTeamsのメッセージ入力エリアから検索し、そのままメッセージ内にカード形式のコンテンツとして挿入させることができる、みたいな機能です。
2. メッセージへのアクションを追加する機能 (Action Command)
メッセージに対してハートマークや顔文字でリアクションする機能は、皆さんよく使われていると思います。このAction Command機能は、メッセージに対してのアクションを増やしたい場合に追加います。
以下の例では「メッセージに対して対応期限のリマインダーを設定する」という機能が紹介されています。
3. URLのプレビュー機能 (Link unfurling)
最後はOGPの拡張版みたいなやつです。特定のURLが入力された際に、それをカード形式のコンテンツでプレビューを表示させる機能になります。
以下の画像だとちょっと分かりづらいですが、メッセージ入力欄にhttps://some-app.com/contents/xxx
のようなURLを入力すると、メッセージ入力欄に以下のような対応するプレビューを表示させることができるようになります。
コネクタ機能
コネクタは、外部から情報を定期的に受け取って、その内容を特定のチャンネルに投稿できるようにする仕組みです。
以下の例は、TODOアプリの例で、TODOアプリでタスクが作成されたらそれをGチャンネルに通知するコネクタになっています。
まとめとWebhookについての補足
いかがだったでしょうか。まずは4つの機能の概要を把握できたと思います。
さて、もしかするとここまで読んでWebhookはどうなっているのだ、と思われる方がいらっしゃるかもしれません。TeamsにもWebhook機能が用意されていますが、それらはボット機能とコネクト機能の簡易版としてイメージしていただければOKです。
簡単にWebhookを紹介しておくと、一般的なメッセージアプリと同様に、TeamsにもOutgoing Webhook(送信Webhook)とIncoming Webhook(受信Webhook)が用意されています。
送信Webhookは@SomeHook
とすることで指定された外部URLにメッセージを送信する機能です。つまり、ボット機能の簡易版になります。
受信Webhookは、Teamsが用意した受信用のURL宛にメッセージを送信することで、受信した内容を特定のチャンネルに投稿する機能です。つまり、コネクタ機能の簡易版になります。
各Webhookを利用するべきか、Teamsアプリとして機能を提供すべきか、の判断基準は、とにかくWebhookで済ませられるならWebhookで済ませておくのが良いと思います。しかし、Webhookは最低限の機能しか提供しないため、より細かい部分のカスタマイズが必要であったり、ボット機能とタブ機能の組み合わせの機能提供が必要であったりする場合は、Teamsアプリとして提供するという判断になるかと思います。
各機能をTeams内で表示できる箇所
機能について把握したところで、各機能をTeams内のどこに表示できるのか、を説明できればと思います。まず結論から書くと、各機能と表示できる箇所との対応は以下のようにまとめることができます。
タブ機能 | ボット機能 | メッセージ拡張機能 | コネクタ機能 | |
---|---|---|---|---|
チャット画面 | - | ◯ メッセージのやりとり | - | ◯ メッセージの送信 |
メッセージ入力エリア | - | ◯ ボット用に設定したコマンドを呼び出せる | ◯ 独自のコンテンツを挿入可能 | - |
メッセージに対するアクション | - | - | ◯ 独自のアクションがとれようになる | - |
パーソナルタブ | ◯ | ◯ 1対1のチャット画面として表示される | - | - |
チャンネルタブ | ◯ | - | - | - |
会議タブ | ◯ | - | - | - |
会議中画面 | ◯ | - | - | - |
それぞれの詳細について簡単に紹介していければと思います。
チャット画面
チャットが行える場所は、細かく見ると、個人/グループチャット、チーム内のチャンネル、会議中などに分けられますが、ここではまとめてすべてをチャット画面と呼んでみます。
ボット機能はすべての箇所で利用可能ですが、コネクタ機能が利用できるのはチーム内のチャンネルのみです。コネクタを使った個人へのメッセージ送信は行えません。
メッセージ入力エリア
メッセージの入力エリアと呼んでいるのは、以下の画像で赤で囲んだ部分です。ボットに対するコマンドは、ボットとの1対1のチャット画面で表示されます。そして、メッセージ拡張機能によるコンテンツの入力支援は、小さなアイコンボタンとして追加することができます。
メッセージに対するアクション
メッセージに対するアクションは、メッセージにカーソルを合わせることで表示されるメニューです。メッセージ拡張機能で独自のアクションを追加した場合は、以下のように表示されます。
パーソナルタブ
パーソナルタブは、個人用アプリと呼ばれたりプラベートタブと呼ばれたりもしています。以下の赤枠で囲んだ部分のことを指します。このエリアはユーザーごとにカスタマイズができ、自分がよく利用するTeamsアプリをピンドメしておくことができます。
画像では左パネル上部に「チャット」「タスク」「詳細情報」という3つのメニューが並んでいますが「チャット」はTeamsアプリがボット機能を含む場合に表示されるタブで、通常のチャット画面とは別にこちらからもボットとの1:1のチャットが行なえます。
その他の「タスク」「詳細情報」はTeamsアプリに含まれるタブ機能(正確には「静的タブ」と呼ばれているやつ。後ほど少しだけ補足します)です。それぞれのメニューをクリクすることで、指定されたタブ(Webページ)が表示されます。タブの順番は「チャット」を含めて自由に設定が行なえます。
チャンネルタブ
チャンネルタブは、各チャンネルに設定しておけるタブのことです。チャンネルに参加しているユーザー全員に共通のタブになります。Teamsアプリのタブ機能(正確には「構成タブ」呼ばれているやつ。後ほど少しだけ補足します)を表示したいチャンネルに表示することができます。
会議タブ
会議タブは、各会議に設定できるタブのことです。
あまり利用されているのを見たことはないですが、会議ごとにタブを設定することができます。
会議中画面
ミーティング中に表示できるタブには2種類あります。1つは「会議中タブ」と呼ばれる、ミーティング画面の右側に表示されるエリアです。ミーティングに参加しているメンバー全員に共通のものが表示されます。
もう1つは「会議ステージ」と呼ばれる部分で、通常は画面共有をした際に表示されるエリアです。こちらには、同時編集などの機能を持ったタブ(Webページ)が表示されることを期待されています。以下はホワイトボードアプリの例になります。
まとめ
いかがでしょうか。機能ごとの表示箇所を把握した現在、Teamsアプリをほぼ理解したと言っても過言ではありません。とはいっても、より細かい部分を正確に説明すれば、もう少し細かい区分が存在しています。
しかし仕様の細部にとらわれることなく、これぐらいの粒度で俯瞰しておけば、あとは詳細を検討する部分でキャッチアップできると思います。
次は、実装の概要を簡単に見ることで各機能でできることを理解していきましょう。
各機能の実装の概要
機能ごとの説明に入る前にTeamsアプリ全体の概要を説明できればと思います。Teamsアプリの全体構成は「manifest.json」と呼ばれるJSONで定義します。
こちらにJSONのスキーマが公開されているので、JSONスキーマに詳しい方は、直接スキーマを見たほうが理解が進む部分もあると思います。
以下は、社内で開発したAIチャットサービス用のTeamsアプリのmanifest.jsonです。(必要な部分は伏せ字にしています)。少し長いJSONなので、JSONに慣れていない方は慌てるかもしれませんが、大丈夫です。
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.16/MicrosoftTeams.schema.json",
"manifestVersion": "1.16",
"version": "1.0.0",
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"packageName": "xxx.com",
"webApplicationInfo": {
"id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"resource": "api://xxx.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
},
"developer": {
"name": "PERSOL HOLDINGS CO., LTD.",
"websiteUrl": "https://xxx.com",
"privacyUrl": "https://xxx.com/privacy",
"termsOfUseUrl": "https://xxx.com/termsOfUse"
},
"icons": {
"color": "color.png",
"outline": "outline.png"
},
"name": {
"short": "AI Chat Assistant",
"full": "AI Chat Assistant"
},
"description": {
"short": "AI Chat AssistantをTeams内から利用可能にするTeamsアプリです",
"full": "AI Chat AssistantをTeams内から利用可能にするTeamsアプリです"
},
"staticTabs": [
{
"contentUrl": "https://xxx.com/auth/signin",
"entityId": "aichat",
"name": "AIチャット",
"scopes": [
"personal"
]
}
],
"bots": [
{
"botId": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
"scopes": [
"personal",
"groupchat",
"team"
],
"isNotificationOnly": false,
"supportsFiles": false
}
],
"permissions": [
"identity",
"messageTeamMembers"
],
"validDomains": [
"xxx.com"
]
}
まず注目していただきたいのは「staticTabs」と「bots」の部分です。このmanifest.json内では、1つのタブ機能と1つのボット機能が定義されていることがわかると思います。
このmanifest.jsonと「icons」の部分で指定されているアイコン画像をまとめてzipファイルにして、アプリを登録することで、Teams上からアプリを利用できるようになります。
Teamsアプリとmanifest.jsonが1:1で対応しているんだ、というぐらいの理解をした上で、各機能の実装の概要を見ていきましょう。
タブ機能
先程のサンプルmanifest.jsonの「staticTabs」部分を抜粋したものが以下になります。
AIチャット
という名前のタブをhttps://xxx.com/auth/signin
というURLで定義しています。
"staticTabs": [
{
"contentUrl": "https://xxx.com/auth/signin",
"entityId": "aichat",
"name": "AIチャット",
"scopes": [
"personal"
]
}
]
Teams内でこのタブを開くと、定義されたhttps://xxx.com/auth/signi
というURLのWebページがiframe(スマホ版のTeamsアプリの場合はWebView)を使って表示されます。ここではiframe(とWebView)の詳細については触れませんが、とにかくWebページをTeams内でそのまま表示できるもの、という認識でOKです。
タブ機能については以上、という感じなのですが、もう1つの登場人物としてteams-jsもしくはTeams JavaScript client library
と呼ばれるものがあります。これはMicrosoftが提供するTeamsアプリ用のライブラリで、WebページがTeams内で表示された場合にのみ動作し、Teams内の情報取得などに利用できます。
例えば、表示しているユーザーが誰なのかや、どこで表示されているのか(どのチャンネルで、どのミーティングで)などの情報を取得することができ、その情報をWebページ側に渡すことで、Teams内の情報を使ってWebページ側の動きをコントロールすることが可能になります。
また後ほど別途紹介しますが、teams-jsは認証時にも利用します。
ボット機能
ボット機能は、もう少し複雑になります。まずは登場人物を挙げてみます。
- Azureから登録するAzure Bot Service
- Azure Bot Serviceからのリクエストを受け取って返答を作成するAPIサーバー
- APIサーバーの内で利用するbotbuilderと呼ばれるライブラリ
中心人物はAzure Bot Service
というAzure上で利用可能なボットをつくるためのリソースです。このサービスはTeamsボット専用のサービスではなく、例えばSlackやLINE用のボットの作成にも利用することができます。
その上で、先程のmanifest.jsonからボット機能の部分を抜粋して見てみましょう。
"botId": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy"
となっている部分の"yyy..."に設定されているのがAzure Bot Service
のIDだと思ってください(わかりやすさを優先して不正確な内容、もしくは嘘を書いています。認証の章でもう少し正しく解説します)。
"bots": [
{
"botId": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
"scopes": [
"personal",
"groupchat",
"team"
],
"isNotificationOnly": false,
"supportsFiles": false
}
]
ユーザーがTeams内でボットにメッセージを送信し回答を受け取るまでの流れを書くと以下のようになります。
- ユーザーがTeams内でボットにメッセージを送る
- Teamsがmanifest.jsonを見て、対応するAzure Bot Serviceにユーザーが送ったメッセージを転送する
- Azure Bot Serviceが転送されたメッセージを、返答を作成するAPIサーバーに再転送する
- APIサーバーで返答を作成し、Azure Bot Serviceに返す (この部分の実装でbotbuilderを利用する)
- Azure Bot ServiceがTeamsに返答し、ユーザーが返答を受け取る
APIサーバーはAzure上に配置されている必要はなく、AWSやGCPを利用して問題ありません。またbotbuilderを使わずに実装を行うことも可能ですが(botbuilderも細部の仕様がかなり込み入っているため把握が大変)、使ったほうが楽になるかなと思います。
メッセージ拡張機能
次にメッセージ拡張機能です。以外かもしれませんが、登場人物はボット機能と全く同じです。
メッセージに対して備忘のリマインダーを設定可能にするメッセージ拡張機能の実装サンプルにおけるmanifest.jsonから、メッセージ拡張機能を定義している部分を抜粋してみます。
"composeExtensions": [
{
"botId": "<<BotId>>",
"commands": [
{
"id": "create-reminder",
"description": "Fetch create-reminder task module",
"title": "Create-reminder",
"type": "action",
"fetchTask": true,
"context": [ "message" ]
}
]
}
]
ボット機能と同様にbotId
の設定が必要なことが分かると思います。
つまり、ボット機能ではテキストのメッセージをやりとりしていたのが、メッセージ拡張機能では「このメッセージに対してcreate-reminder
アクションが呼ばれたよ」のような、もう少しリッチな内容がAPIサーバーに飛んでくるので、それらのリクエストをいい感じにハンドリングできるようにAPIサーバーを実装すれば良い、という感じです。
ボット機能が実装できれば、メッセージ拡張機能も実装できたも当然、と思っておいてだいたい良いのではないかと思います。
コネクタ機能
コネクタ機能における登場人物は、以下の4名です。
- Connector Developer Portalから登録するコネクター定義
- Teamsのチャンネルにコネクタを登録する際に表示する「コネクタ設定ページ」
- コネクタ設定ページから送信される「コネクタの登録情報」を受け取って管理するAPIサーバー
- 「コネクタの登録情報」を元にイベント時に登録情報に従ってメッセージを送信する(API)サーバー
外部のTODOアプリに関するイベントを通知するコネクタ機能の実装サンプルにおけるmanifest.jsonから、コネクタ機能部分の抜粋です。
"connectors": [
{
"connectorId": "<<ConnectorId>>",
"configurationUrl": "https://<<ConfigurationPageUrl>>",
"scopes": [
"team"
]
}
]
ConnectorId
の部分にはConnector Developer Portalでの登録時に払い出されるコネクタのIDを設定し、ConfigurationPageUrl
には「コネクタ設定ページ」のURLを設定します。
ユーザーがあるチャンネルにコネクタを設定し、TODOが登録された際にメッセージが届くまでの処理の流れは以下になります。
- ユーザーがTeams内であるチャンネルにコネクタを設定を試みる
- manifest.jsonに従って
configurationUrl
に設定されているページが「コネクタ設定ページ」として表示される- コネクタ設定ページはタグ機能と同じく、かなり自由にカスタマイズできます
- この設定ページ内で
teams-js
を使って(6)で必要になるIncomingWebhookURL
を取得することができます
- 設定ページで受け取るイベントをユーザーが選択し(例えばTODOの新規作成と削除時に通知を受け取る、などを選択する)「設定」ボタンを押す
- 設定ページから「コネクタの登録情報」を管理するAPIサーバーにリクエストが飛び「コネクタの登録情報」がDBなどに保持される
- TODOの新規登録時に、DB内の「コネクタの登録情報」が参照され、その中の
IncomingWebhookURL
宛に(API)サーバーがメッセージをTeamsに投げる - 投げられたメッセージがチャンネルに表示される
いかがでしょうか。
「コネクタ設定ページ」が挟まるため少しだけ手順が長いですが、割とシンプルだと思います。
まとめ
実装編はいかがだったでしょうか。もし上から通して読んでくれた方がいたら、流石に疲れてきたと思います。書き手の私も大変疲れておりますが、実際の利用には欠かせない認証部分の紹介を最後にできればと思います。
各機能における認証の概要
Teamsアプリでの認証(権限制御)には3つの選択肢があります。
- Teamsアカウントと紐づいているAzureADでのシングルサインオン
- GoogleやFacebookなどのアカウントを使って認証させる(外部アカウントでのOAuth認証)
- 認証しない
3が選べるのであれば嬉しいのですが、ユーザーが誰なのかを特定するためにも認証が求められる場合が多いです。今回は、社内向けサービスのTeamsアプリ化という目的を持った読者を想定しており、Teamsを採用している=AzureADを利用していると考えて問題ないはずなので、主に1のシングルサインオンについて紹介できればと思います。
なお(1)と(2)の違いは、どのIDaaSで認証するかだけで、基本的にはOAuth認証の流れに乗ったものであり、大きな流れにそれほど差異はありません。
タブ機能、コネクタ機能における認証
タブ機能とコネクタ機能をひとまとめにしていますが、どちらもteams-js
を使って認証を呼び出す流れであり、基本的なフローが共通のためです。タブ機能ではタブに表示するWebページ自身に、コネクタ機能の場合は、コネクタを登録する際の設定ページにてteams-js
を使います。
公式ドキュメントにあるシングルサインオン(SSO)のフロー図を元に流れを理解してみましょう。
図のTab App Client
が、タグ機能ではタグに表示するWebページ、コネクタ機能の場合はコネクタ設定ページに当たります。Tab App Server
がWebページやコネクタ設定ページから呼び出すAPIサーバーです。
フローの1にあるように、teams-js
にはgetAuthToken()
と呼ばれるメソッドが用意されています。シングルサインオンの場合は、このメソッドを呼び出すことで、Teamsへのログイン情報を元にTeamsクライアントがAzureADにアクセストークンを取得してくれます。
Teamsクライアントを太字にした理由は、ユーザー自身がAzureADにアクセストークンを要求するのではなく、Teamsがユーザーの代わりにアクセストークンを要求する部分を抑えてほしいためです。ここさえ抑えておけば、躓きなくタブ機能、コネクタ機能における認証の実装が行えるはずです。
そのことを理解しておけば、公式ドキュメントのこの辺りで行われている複雑な設定は、Teamsがユーザーの代わりにアクセストークンを要求することを許可するための設定であることが、よく分かると思います。
ボット機能、メッセージ拡張機能における認証
先程紹介した通り、ボット機能とメッセージ機能は兄弟みたいなもので、認証方式もまったく同様です。こちらも公式ドキュメントのフロー図を元に紹介します。
まず、この図におけるBot Framework Token Service
に当たるのは、ボット機能の実装概要で書いた「Azure Bot Serviceからのリクエストを受け取って返答を作成するAPIサーバー」になります。Bot Framework Token Service
と書かれるとAzure Bot Service
自身にトークンを管理してくれる機能があるような気がしますが、そうではありません。あくまでトークンを管理する機能をAPIサーバー内に自前で用意しないといけません。
各ステップごと、理解の助けになるように解説できればと思います。
- ステップ2と3の間にある
Check if token exists
の意味は、既に認証済みであればAPIサーバー(図ではBot Framework Token Service)に対象ユーザーのトークンが保持されているはずなので、そうであれば認証済みであると判断する、ということです。この処理をAPIサーバー側で用意しないといけません。 - ステップ3の
Request Sign-in Link
、Foward Sign-in Link (as OAuth Card)
という部分も解説が必要です。Teamsではテキスト以外のリッチなコンテンツを軽量にやりとりするためにAdaptive Cardと呼ばれる仕様をサポートしています(MS謹製)。このAdaptive Cardの特殊な形態の一つにOAuth Card
とよばれるものがあり、このカードを受け取るとTeamsがユーザーに代わって暗黙的にAzure ADにトークンを要求してくれます(シングルサインオンの場合。その他のOAuth認証の場合はログイン画面はホップアップで表示されるなど挙動が変わります)。その暗黙的に呼び出させるトークンの要求がステップ4です。 - ステップ5は「Teamsが貴方に代わってトークン要求してるけど大丈夫?」という確認です。OKすると先に進みます。
- ステップ6の手前の
Send access token
がもともとの要求元のTeams Clientではなく、Bot Serviceになっている点に違和感をおぼえる方も多いかもしれません。これは単に認証完了時のコールバック先をBot Serviceにしておくことで、Teams Client -> Bot Serviceの余計なリクエストを減らすことが目的です。ですので、公式ドキュメントのこの辺りで行われているコールバックURLの追加設定は、Bot Serviceをコールバック先に設定するためのものです。
以上の解説を頼りに、なんとかボット機能、メッセージ拡張機能における認証のフローのイメージを掴んでいただけたら幸いです。
終わりに
Teamsアプリに造詣の深い方からすると「あれ?ここの説明が抜けてない?」と思う部分が多々あると思いますが、まずは多機能なTeamsアプリを80%くらいの正確度で解説することで、その全体像をおおまかに理解しているユーザーを増やすことに寄与できていたら嬉しいです。
本記事の中で使っている画像は公式ドキュメントから拝借しています。
またTeamsアプリには大量のサンプルが提供されています。時々動かないサンプルもあり、涙がこぼれることもあったりしますが、実際のコードがあるのは大変助けになります。必要に応じて、こちらもご参照ください。
ではでは、ここまで読んで頂きありがとうございました。
よいTeamsアプリライフを!
Discussion