次の方法で共有


アプリにMicrosoft Teamsを埋め込む

この記事では、アプリケーション内に Microsoft Teams エクスペリエンスを埋め込む方法について説明します。 アプリに Teams を埋め込むと、ユーザーはアプリと Teams を切り替えることなく、アプリから直接 Teams メッセージを読み取って送信できます。

アプリの応答時間を短縮し、コストを削減するには、Microsoft Graph からメッセージを読み取る回数を最小限に抑える必要があります。 この記事では、メッセージを 1 回取得してキャッシュしてから、変更通知を使用して後続のメッセージのみを取得する方法について説明します。

手順 1: アーキテクチャの設計と設定

次の図は、Teams と統合されるアプリに推奨される高レベルのアーキテクチャを示しています。

Teams とアプリケーション UI の統合を示す図

アーキテクチャには、次の 3 つのコンポーネントが含まれています。

  • ユーザー入力を取得し、メッセージを表示する チャット UI 。 チャット UI は、API 要求 ( POST/GET チャット、 POST/GET メッセージなど) を Teams API に送信します。 また、サーバー コンポーネントから新しいメッセージをリアルタイムで取得します。

  • Teams API から新しいメッセージを取得するために、変更通知をリアルタイムでサブスクライブする サーバー コンポーネント 。 Teams API が 変更通知を送信する場合、変更通知をリッスンするために Webhook URL が必要であり、ユーザーの携帯電話などの UI に Webhook URL がない可能性があります。 ただし、サーバー コンポーネントには安定した Webhook URL があります。 新しいメッセージは、 ASP.NET SignalR などの通信方法を使用して、サーバー コンポーネントからチャット UI にプッシュされます。

    注:

    また、チャット UI ではなくサーバー コンポーネントを用意し、Teams API に対するすべての API 要求を行い、すべてのメッセージをキャッシュすることもできます。 たとえば、コンプライアンスや監査など、API 要求を行う必要がある別のバックエンド システム コンポーネントがある場合は、代わりに API 要求とキャッシュをサーバー コンポーネントに一元化することを選択できます。

  • メッセージを保持する キャッシュ 。 アプリケーションの応答時間を短縮し、コストを削減するには、このキャッシュにメッセージを格納することで、同じメッセージの複数回の読み取りを最小限に抑えます。 後で API の使用量に驚いたくありません。 キャッシュを設定する方法については、「キャッシュを追加して Azure API Managementのパフォーマンスを向上させる」を参照してください。

    一部の Teams API には、ライセンスと支払いの要件があります。 詳細については、「 支払いモデルとライセンス要件 」を参照してください。

これらのコンポーネントを設定したら、Teams API の使用を開始できます。

手順 2: 新しいチャットを作成する

新しい chatMessage を送信する前に、メンバーを割り当ててチャットを作成する必要があります。 次の例は、グループ チャットを作成する方法を示しています。 さまざまな種類のチャットを作成する方法を示すその他の例については、「 チャットの作成」を参照してください。

要求

POST https://graph.microsoft.com/v1.0/chats
Content-Type: application/json

{
    "chatType": "group",
    "members": [
        {
            "@odata.type": "#microsoft.graph.aadUserConversationMember",
            "roles": [
                "owner"
            ],
            "user@odata.bind": "https://graph.microsoft.com/v1.0/users('adams@contoso.com')"
        },
        {
            "@odata.type": "#microsoft.graph.aadUserConversationMember",
            "roles": [
                "owner"
            ],
            "user@odata.bind": "https://graph.microsoft.com/v1.0/users('gradyA@contoso.com')"
        },
        {
            "@odata.type": "#microsoft.graph.aadUserConversationMember",
            "roles": [
                "owner"
            ],
            "user@odata.bind": "https://graph.microsoft.com/v1.0/users('4562bcc8-c436-4f95-b7c0-4f8ce89dca5e')"
        }
    ]
}

応答

HTTP/1.1 201 Created
Content-Type: application/json

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#chats/$entity",
    "id": "19:b1234aaa12345a123aa12aa12aaaa1a9@thread.v2",
    "topic": null,
    "createdDateTime": "2023-01-11T01:34:18.929Z",
    "lastUpdatedDateTime": "2023-01-11T01:34:18.929Z",
    "chatType": "group",
    "webUrl": "https://teams.microsoft.com/l/chat/19%3Ab1234aaa12345a123aa12aa12aaaa1a9%40thread.v2/0?tenantId=4dc1fe35-8ac6-4f0d-904a-7ebcd364bea1",
    "tenantId": "4dc1fe35-8ac6-4f0d-904a-7ebcd364bea1",
    "viewpoint": null,
    "onlineMeetingInfo": null
}

手順 3: チャットでメッセージを送信する

チャット内のメンバーは、互いにメッセージを送信できます。 次の例は、単純なメッセージを送信する方法を示しています。 添付ファイルやアダプティブ カードなどの他のメディアの送信など、その他の例については、「 chatMessage の送信」を参照してください。

要求

POST https://graph.microsoft.com/v1.0/chats/19:b1234aaa12345a123aa12aa12aaaa1a9@thread.v2/messages
Content-type: application/json

{
  "body": {
    "content": "Hello World"
  }
}

応答

HTTP/1.1 201 Created
Content-type: application/json

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#chats('19:b1234aaa12345a123aa12aa12aaaa1a9%40thread.v2')/messages/$entity",
    "id": "1673482643198",
    "replyToId": null,
    "etag": "1673482643198",
    "messageType": "message",
    "createdDateTime": "2023-01-12T00:17:23.198Z",
    "lastModifiedDateTime": "2023-01-12T00:17:23.198Z",
    "lastEditedDateTime": null,
    "deletedDateTime": null,
    "subject": null,
    "summary": null,
    "chatId": "19:b1234aaa12345a123aa12aa12aaaa1a@thread.v2",
    "importance": "normal",
    "locale": "en-us",
    "webUrl": null,
    "channelIdentity": null,
    "policyViolation": null,
    "eventDetail": null,
    "from": {
        "application": null,
        "device": null,
        "user": {
            "id": "87d349ed-44d7-43e1-9a83-5f2406dee5bd",
            "displayName": "John Smith",
            "userIdentityType": "aadUser"
        }
    },
    "body": {
        "contentType": "text",
        "content": "Hello world"
    },
    "attachments": [],
    "mentions": [],
    "reactions": []
}

手順 4: メッセージを取得する

メッセージを取得するには、chatMessages リソースの GET HTTP メソッドを使用します。

アプリケーションの応答時間を短縮し、調整を最小限に抑え、コストを削減するには、同じメッセージの複数回の読み取りを最小限に抑えます。 GET HTTP メソッドを 1 回限りのエクスポートとして使用するか、変更通知の有効期限が切れてメッセージをもう一度同期する場合に使用します。 それ以外の場合は、キャッシュに依存し、通知を変更します。

Microsoft Graph には、チャット メッセージを取得するいくつかの方法が用意されています。

/getAllMessagesを使用すると、ユーザーのすべてのチャットでメッセージを取得できます。 この API は、監査アプリケーションやコンプライアンス アプリケーションなどのバックエンド アプリケーション向けに設計されており、多くの場合、すべてのチャットで一度にメッセージを取得します。 アプリケーションのアクセス許可のみがサポートされます。 また、これは 従量制課金 API です

/messagesを使用すると、手順 1 で説明されているように、委任されたアクセス許可を使用して UI から API 呼び出しを行うことができます。

API によって 調整制限が異なります。 たとえば、チャットごとの /messages API には、テナントごとにアプリごとに 1 秒あたり 30 要求 (rps) の制限があります。 テナントに 50 人のユーザーがあり、各ユーザーに平均 15 件のチャットがあり、システムの開始時にすべてのユーザーとすべてのチャットのメッセージを取得する場合は、少なくとも 50 ユーザー x 15 チャット要求/ユーザー = 750 要求が必要です。 この場合、要求を少なくとも 750 要求/30 rps = 25 秒に分散することをお勧めします。 応答で返されるメッセージの数に制限 (最大 $top=50) があるため、すべてのメッセージを取得するために複数の要求を行う必要がある場合があります。

次の例は、チャットごとの /messages API を使用する方法を示しています。 既定では、返されたメッセージの一覧は lastModifiedDateTime順に並べ替えられます。 次の使用例は、 createdDateTime で並べ替えます。 並べ替えは、要求の orderBy クエリ パラメーターを使用して指定されます。

一般的な対話型メッセージング アプリでは、既定では最新のメッセージのみが表示され、ユーザーはページング、スクロール、またはクリックして古いメッセージを読み込むことができます。 必要なメッセージのみを取得するために、上記の両方の API でもフィルター処理 (たとえば、 $top=10$filter=lastModifiedDateTime gt 2019-03-17T07:13:28.000z) がサポートされます。

要求

GET https://graph.microsoft.com/v1.0/users/87d349ed-44d7-43e1-9a83-5f2406dee5bd/chats/19:b1234aaa12345a123aa12aa12aaaa1a9@thread.v2/messages?$top=2&$filter=lastModifiedDateTime gt 2021-03-17T07:13:28.000z&$orderby=createdDateTime desc

応答

HTTP/1.1 200 OK
Content-type: application/json

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('87d349ed-44d7-43e1-9a83-5f2406dee5bd')/chats('19%3Ab1234aaa12345a123aa12aa12aaaa1a9%40thread.v2')/messages",
    "@odata.count": 2,
    "@odata.nextLink": "https://graph.microsoft.com/v1.0/users/87d349ed-44d7-43e1-9a83-5f2406dee5bd/chats/19:b1234aaa12345a123aa12aa12aaaa1a9@thread.v2/messages?$top=2&$filter=lastModifiedDateTime+gt+2021-03-17T07%3a13%3a28.000z&$orderby=createdDateTime+desc&$skiptoken=A111wwAwAA1ww1AwA1wwA1Aww111AA1wAwAAwAAwAAAwA1w1AAAwAAwww1Aww1AwAAwwAAA1AA1wAwAAw111wA11AAAww11Aw1wwww1wAwwwAAwwAwAwAAw1",
    "value": [
        {
            "id": "1673543687527",
            "replyToId": null,
            "etag": "1673543687527",
            "messageType": "message",
            "createdDateTime": "2023-01-12T17:14:47.527Z",
            "lastModifiedDateTime": "2023-01-12T17:14:47.527Z",
            "lastEditedDateTime": null,
            "deletedDateTime": null,
            "subject": null,
            "summary": null,
            "chatId": "19:b1234aaa12345a123aa12aa12aaaa1a9@thread.v2",
            "importance": "normal",
            "locale": "en-us",
            "webUrl": null,
            "channelIdentity": null,
            "policyViolation": null,
            "eventDetail": null,
            "from": {
                "application": null,
                "device": null,
                "user": {
                    "id": "6789f158-72b1-4a63-9959-1f006381132b",
                    "displayName": "Adele Vance",
                    "userIdentityType": "aadUser",
                    "tenantId": "4dc1fe35-8ac6-4f0d-904a-7ebcd364bea1"
                }
            },
            "body": {
                "contentType": "html",
                "content": "<p>Good morning, world!</p>"
            },
            "attachments": [],
            "mentions": [],
            "reactions": []
        },
        {
            "id": "1673482643198",
            "replyToId": null,
            "etag": "1673482643198",
            "messageType": "message",
            "createdDateTime": "2023-01-12T00:17:23.198Z",
            "lastModifiedDateTime": "2023-01-12T00:17:23.198Z",
            "lastEditedDateTime": null,
            "deletedDateTime": null,
            "subject": null,
            "summary": null,
            "chatId": "19:b1234aaa12345a123aa12aa12aaaa1a9@thread.v2",
            "importance": "normal",
            "locale": "en-us",
            "webUrl": null,
            "channelIdentity": null,
            "policyViolation": null,
            "eventDetail": null,
            "from": {
                "application": null,
                "device": null,
                "user": {
                    "id": "87d349ed-44d7-43e1-9a83-5f2406dee5bd",
                    "displayName": "John Smith",
                    "userIdentityType": "aadUser",
                    "tenantId": "4dc1fe35-8ac6-4f0d-904a-7ebcd364bea1"
                }
            },
            "body": {
                "contentType": "text",
                "content": "Hello world"
            },
            "attachments": [],
            "mentions": [],
            "reactions": []
        }
    ]
}

この例では、 contentTypetext または htmlできます。アプリケーションで両方を表示できることを確認します。

チャット メッセージに埋め込まれたイメージを取得するには、2 回目の呼び出しを行って chatMessageHostedContent を取得します。 詳細については、「 Get chatMessageHostedContent」を参照してください。

アプリでは、chatMessage.policyViolation.dlpAction フィールドを監視し、このフィールドへの変更通知を監視し、organizationで定義されているデータ損失防止 (DLP) または同様のルールに従ってメッセージを非表示またはフラグを設定することをお勧めします。 有効な値は、 NoneNotifySender、および BlockAccessです。 現時点では、Teams は BlockAccessExternalを無視します。 これらの値の詳細については、「 chatMessagePolicyViolation リソースの種類」を参照してください。

一部のメッセージは システム メッセージです。 たとえば、次のシステム メッセージは、新しいメンバーがチャットに参加したことを示しています。

{
  "id": "1616883610266",
  "replyToId": null,
  "etag": "1616883610266",
  "messageType": "systemEventMessage",
  "createdDateTime": "2021-03-28T03:50:10.266Z",
  "lastModifiedDateTime": "2021-03-28T03:50:10.266Z",
  "lastEditedDateTime": null,
  "deletedDateTime": null,
  "subject": null,
  "summary": null,
  "chatId": null,
  "importance": "normal",
  "locale": "en-us",
  "webUrl": "https://teams.microsoft.com/l/message/19%3A4a95f7d8db4c4e7fae857bcebe0623e6%40thread.tacv2/1616883610266?groupId=fbe2bf47-16c8-47cf-b4a5-4b9b187c508b&tenantId=2432b57b-0abd-43db-aa7b-16eadd115d34&createdTime=1616883610266&parentMessageId=1616883610266",
  "policyViolation": null,
  "from": null,
  "body": {
    "contentType": "html",
    "content": "<systemEventMessage/>"
  },
  "channelIdentity": {
    "teamId": "fbe2bf47-16c8-47cf-b4a5-4b9b187c508b",
    "channelId": "19:4a95f7d8db4c4e7fae857bcebe0623e6@thread.tacv2"
  },
  "onBehalfOf": null,
  "attachments": [],
  "mentions": [],
  "reactions": [],
  "eventDetail": {
    "@odata.type": "#microsoft.graph.membersAddedEventMessageDetail",
    "visibleHistoryStartDateTime": "0001-01-01T00:00:00Z",
    "members": [{
        "id": "06a5b888-ad96-455e-88ef-c059ec4e4cf0",
        "displayName": null,
        "userIdentityType": "aadUser"
      },
      {
        "id": "1fb8890f-423e-4154-8fbf-db6809bc8756",
        "displayName": null,
        "userIdentityType": "aadUser"
      }
    ],
    "initiator": {
      "application": null,
      "device": null,
      "user": {
        "id": "9ee3dc1b-6a70-4582-8bc5-5dd35336b6c3",
        "displayName": null,
        "userIdentityType": "aadUser"
      }
    }
  }
}

手順 5: メッセージをキャッシュする

getAllMessages または変更通知から受け取る各メッセージは従量課金の対象となるため、同じメッセージの読み取りを複数回最小限に抑える必要があります。 ユーザーが最近のチャットをすばやく再度開くことができるように、メッセージを少なくとも数時間キャッシュすることをお勧めします。 organizationのアイテム保持ポリシーに従って許可されるメッセージよりも長くメッセージをキャッシュしないでください。

手順 6 では、キャッシュがユーザーごとのかどうかを決定します。

手順 6: 変更通知をサブスクライブする

Microsoft Graph では、対応するリソース プロパティで指定されたメッセージに対して、いくつかの種類の変更通知が提供されます。

  • チャットごと: "resource": "/chats/{id}/messages"
  • ユーザーごとに、すべてのチャットで次の手順を実行します。 "resource": "/users/{id}/chats/getAllMessages"
  • テナントごとに、すべてのチャットで次の手順を実行します。 "resource": "/chats/getAllMessages"
  • アプリごとに、アプリがインストールされているテナント内のすべてのチャットで、次の手順を実行します。 "resource": "/appCatalogs/teamsApps/{id}/installedToChats/getAllMessages"

特定のチャットのみを追跡する場合は、 /messages オプションですが、追跡する必要があるさまざまなチャットの数を検討する必要があります。チャットごとの変更通知の数には 制限 があります。 代わりに、ユーザー、テナント、またはアプリのすべてのチャットでメッセージを取得する 3 つの /getAllMessages オプションのいずれかにサブスクライブすることを検討してください。

4 つのオプションはすべて、バックエンド サーバー コンポーネントによって呼び出されます。 これらはすべて アプリケーション のアクセス許可をサポートしているため、アクセス制御ロジックに注意して、ユーザーの参加または退出に応じてチャットの表示と非表示を切り替えます。 変更通知は既にユーザー固有であるため、 委任された アクセス許可もサポートするユーザーごとのオプションを実装する方が簡単な場合があります。ただし、同じメッセージが複数の変更通知をトリガーし、サブスクライブしているユーザーごとに 1 つ、重複したメッセージを格納するためにより大きなキャッシュが必要になる場合があるため、長期的にはコストが高くなる可能性があります。 さまざまなサブスクライブされたリソースのアクセス許可とライセンス要件の詳細については、「 サブスクリプションの作成」を参照してください。

変更通知サブスクリプションには従量課金があります。 次の例に示すように、リソース プロパティに model パラメーターを指定します。

サブスクリプションを作成するときは、includeResourceData プロパティが true に設定されていること、および encryptionCertificate プロパティと encryptionCertificateId プロパティを指定していることを確認します。 それ以外の場合、暗号化されたコンテンツは変更通知で返されません。 詳細については、「 リソース データを含む変更通知を設定する」を参照してください。

次の例は、ユーザーごとにすべてのメッセージを取得する方法を示しています。 この例を使用する前に、「ユーザー データの変更に対する通知を設定する」で説明されているように、notificationUrl プロパティで指定されたサブスクリプション通知エンドポイントが検証要求に応答できる必要があります。 検証が失敗した場合、サブスクリプションを作成する要求から 400 Bad Request エラーが返されます。

この例の詳細については、「サブスクリプションの 作成」を参照してください。

要求

POST https://graph.microsoft.com/v1.0/subscriptions
Content-type: application/json

{
  "changeType": "created,updated,deleted",
  "notificationUrl": "https://webhook.azurewebsites.net/api/send/myNotifyClient",
  "resource": "/users/87d349ed-44d7-43e1-9a83-5f2406dee5bd/chats/getAllMessages?model=B",
  "expirationDateTime": "2023-01-10T18:56:49.112603+00:00",
  "clientState": "ClientSecret",
  "includeResourceData": true,
  "encryptionCertificate": "MMMM/sMMMsssMsMMMsMMsMMMs4sMMsM4ssMsMsMMMss4ssMMMssss...s4sMMMMsM444ssM4MMsssMMMMsM4MMM4sMsM4MMsM44MMM4ssss4Ms4sMM4MMMMM4MMs+ss4MsMssMss4s==",
  "encryptionCertificateId": "44M4444M4444M4M44MM4444MM4444MMMM44MM4M4"
}

応答

HTTP/1.1 201 Created
Content-type: application/json

{
  "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity",
  "id": "88aa8a88-88a8-88a8-8888-88a8aa88a88a",
  "resource": "/users/87d349ed-44d7-43e1-9a83-5f2406dee5bd/chats/getAllMessages?model=B",
  "applicationId": "aa8aaaa8-8aa8-88a8-888a-aaaa8a8aa88a",
  "changeType": "created,updated,deleted",
  "clientState": "ClientSecret",
  "notificationUrl": "https://webhook.azurewebsites.net/api/send/myNotifyClient",
  "notificationQueryOptions": null,
  "lifecycleNotificationUrl": null,
  "expirationDateTime": "2023-01-10T18:56:49.112603Z",
  "creatorId": "8888a8a8-8a88-888a-88aa-8a888a88888a",
  "includeResourceData": true,
  "latestSupportedTlsVersion": "v1_2",
  "encryptionCertificate": "MMMM/sMMMsssMsMMMsMMsMMMs4sMMsMMs...sssMssMsMsMMss4MMM4sMsM4sMss4s==",
  "encryptionCertificateId": "44M4444M4444M4M44MM4444MM4444MMMM44MM4M4",
  "notificationUrlAppId": null
}

手順 7: 変更通知を受信して復号化する

サブスクライブされたリソースに変更がある場合は常に、 変更通知notificationUrl に送信されます。 セキュリティ上の理由から、コンテンツは暗号化されます。 コンテンツの暗号化を解除するには、「 変更通知からのリソース データの暗号化解除」を参照してください。

サブスクリプションを作成するときは、includeResourceData プロパティが true に設定されていること、および encryptionCertificate プロパティと encryptionCertificateId プロパティを指定していることを確認します。 それ以外の場合、暗号化されたコンテンツは変更通知で返されません。 詳細については、「通知エンドポイントの検証」を参照してください。

要求 (Microsoft Graph によって送信)

POST https://webhook.azurewebsites.net/api/send/myNotifyClient
Content-type: application/json

{
  "value": [
    {
      "subscriptionId": "88aa8a88-88a8-88a8-8888-88a8aa88a88a",
      "changeType": "created",
      "clientState": "ClientSecret",
      "subscriptionExpirationDateTime": "2023-01-10T11:03:37.0068432-08:00",
      "resource": "chats('19:b1234aaa12345a123aa12aa12aaaa1a9@thread.v2')/messages('1677774058888')",
      "resourceData": {
        "id": "1677774058888",
        "@odata.type": "#Microsoft.Graph.chatMessage",
        "@odata.id": "chats('19:b1234aaa12345a123aa12aa12aaaa1a9@thread.v2')/messages('1677774058888')"
      },
      "encryptedContent": {
        "data": "sMMsMsMM+3MMMs8MMsMMsss5M+M0+8sMMsM96MM/8MMMsM4MM12sMsssMMsssMsMMssMs8Mss6sssMMsM2ssssssssMss7sssMs4s35ssMs+0ss1sssMsMssMMMMsssss5MsMssssss+sMsMMMM8s4M3MMsMssM54s1ssssMs4ssMsss3MMM8M4sM+3MMss7MM8sMsMMMs3sssss5MssMss6s+Ms7sMssMMssMsMMss1sMs2sM6sss6sMssssMss7s1MMs7/Msssss5M9M7sMsMMMsMs+MMs+MsMMsMsMMMMsMMMss1M2ssMM8M3sMMMsMss2MMMMsM+ss0M+sssMM4M+sMsM69sMs+sMsssM+MMsMsMM/ssssMMMMMss/s6/47Ms0s5Ms6MsssM2sss4MMMMMMsMsMMM+s8MssMsMMssMMs+MMMM56ss0sMM+sssMsss1ssMsMs21s3MssM9ssMsss9M2+MM3sMMMMMM7MM770MMM2MMsssM11MsssMssMsMsMM2sM1s+84MMs6sss8MsMMsMMsMM3MMssMss1MssMsMsMMMMsMsssMMsssM1sssssM9MMM6s4MMss524sMMMssMs4ss3/+ssssss8MMs2ssMMs2MsMsMMssM8MMMMsMM0sss4MMs/sMsMMs0sMMsssss135sssss9+sssMsMMsMsssMsMsMsMsMM7Ms+MssMsMM1sMssss5s64sMss6sMs6sM0MMs3s29MMssM62ssMsMMssMsM0ssssssss+sM1MsM3sMM9sssssMMMsssMMsMsMsssMssssssMsMssMMMsM8Ms5MsssMM9ss/4MssMs3s5M81sMMssssMMMssMMs7Ms2M9M+7MsssMss6sM0sssM7M0ssMssssMMsMMs9s4MsMsMM6MMsMMssMMssM+Mss6MM8MMM6MM1s75MsssMMsM+MMMs2s9M1MMMsMMs1MssMsssssssMs8MsMsMMMMMM7sMsss0MssMsMMssMMM/sM0M01s2M7MsssssMs37MMs140sMMM0ssMMM/ssMMs3sMsM+Ms+sMMsM3MMssMssMsssss6MMssMMMs1MMMMMsssMs0sM9sMMMss+sssss2sssMssMsMMM1MssMMMs8MMMssssMM99ssMsssMssss2Ms5sMs1/5MMssssMsMM3MMMMM1MsMsMsMMsMMssMsMMsssssMs9Mss6Mss+sM+73Msss0ssMsss8sMssMssssssssssMM9MMMMsMMMMMMMM5MMMM27sM+ssMG",
        "dataSignature": "sMM+sss2sMssMMsMMMMMMMM6ssMs93MssMMM8sMMMMM=",
        "dataKey": "MMsMMMMMss7sssM34sMMsMMsMssMss7MssMssss+MM+4sMsssMss6Msss9sMssMssMsssMM0+MMsss0sMs8MMsMssss2MMMMsMsssMMsMsM3MssMs9ss5sssMMsMssMsMMM6MMssMsM1M+MMMMsMss3MMsssMs9s0ssMs/1sM6ssMMssM+Ms9MsssMMM8MMssssMMs2s94MsMssMMM92/MMMs4Ms8/ssssssMs5+0s+Ms2M7sMMMMsMMsMsMs+5MMM3sssMsMMMsM8sMss+MssssMsMs/MMsMM5ssssM8M0s0MM06sssMMsMM4MsssMMsMssMM9M9MsMMMMM7sMsMM==",
        "encryptionCertificateId": "44M4444M4444M4M44MM4444MM4444MMMM44MM4M4",
        "encryptionCertificateThumbprint": "07M3411M4904M3M78MM8211MM4589MMMM47MM7M6"
      },
      "tenantId": "4dc1fe35-8ac6-4f0d-904a-7ebcd364bea1"
    }
  ],
  "validationTokens": [
    "ssM0sMMsMsMMM1MsMMMssMssMsMMMsM1MsMsMss1sMM6Ms1MMMMMMM5MMsssMs9ssM1sMs9MsMMMMsssssMsMsssMMM6Ms1MMMMMMM5MMsssMs9ssM1sMs9MsMMMMsssssM9.ssMssMMsMsMsMsssMMMsMs04M2M2MMM1MMMsMMs4MM1sM2MsMMM5MsM3MsMsMMMss3MsMsMssMMsssssM3M0ss53sM5ss3ssMs5ssM8sMMMsMsM3Ms0sMMMsMMMsMMMsMMM3Ms0sMsMsMMMsMMMsMsMsMssssMM0MsssMsssMsssMsMsMMMsMsMsMsM2MsMsMsM3MMMsMsM4sMM6MMM3MsM2MMM1MMssMMssMsssMMMsM1sMssM5MMs4ssMsMMssMMMMM2MsMMMsMMsMMM0sMMMssMMsMMM6MsMsMsMsMsMsMMMsMMMsMMssMs05MMssMMMsMMssMMM0MMM4MsMsMsMssMssMMMsMsssMsMsMssssMM6Mss0sMMsMs8ss3MsMsssssMss3MsssM0MsM0MsMsMMssMMMsMsMsMMMsMs1sMMssMMM2MMMsMMMsMMMsMM8sMMMssMMsMsMsMsMsMsMsMM1sMsssMMMsMMMsMsssMM1sMMMsMMM0M2M2MMssMMMssMM6MsMsMMMsMMM3MMsMMMMMMsMMsMM4MsMsMsMsMssMsMMsMMMsM05MsMssMMssMMM5sMsMMMMMMsMsMsM1MsM6MsMsMsMsMsM1MMM3MMMsMMM5Ms1sM2M1MMMsMMM0MMMsMsM1MsMsMsMsMMM6MsM0MsMsMMssMMMsMsMsMMMsMs1sMMssMMM2MMMsMMMsMMMsMMMsMsM0sMM6MssMMs1sMMMssMsMMMMMssMMss16MMMsMMM2MMMsMsMsMsMssM.s16ssMMM97sM_MMs_ss8s8s3MMs95ssMMM8M6ss4M4Ms3sMMMs-M_7ss80MMMsss6ssM0sMM20MsMMs15sMM_ssMsssMMMs9ssM0M_sss5sMssMsMss4s-M-8Ms1ssM8sMsMMss9sMsMsMMMMMMMsMs6MMss2MMMsMMss0MMssMMssMssMMMMMMMMsMs817ssssssMss8MMMssMMMMsss0sMs1ssM0sM1ssMMMs6MMMMss6ss_sMMss3M4MM3sMss45s4s8MMss6s75ssMsM5sssMM0MMMMMM_1ssMMMMsMMMssMs44sMs4MssM5s-__ss5MMs6sMM_MMss5MsMMMM"
  ]
}

復号化されたコンテンツ

{
  "@odata.context": "https://graph.microsoft.com/$metadata#chats('19%3Ab1234aaa12345a123aa12aa12aaaa1a9%40thread.v2')/messages/$entity",
  "id": "1677774058888",
  "replyToId": null,
  "etag": "1677774058888",
  "messageType": "message",
  "createdDateTime": "2023-01-10T18:07:30.302Z",
  "lastModifiedDateTime": "2023-01-10T18:07:30.302Z",
  "lastEditedDateTime": null,
  "deletedDateTime": null,
  "subject": "",
  "summary": null,
  "chatId": "19:b1234aaa12345a123aa12aa12aaaa1a9@thread.v2",
  "importance": "normal",
  "locale": "en-us",
  "webUrl": null,
  "from": {
    "application": null,
    "device": null,
    "user": {
      "userIdentityType": "aadUser",
      "id": "87d349ed-44d7-43e1-9a83-5f2406dee5bd",
      "displayName": "John Smith",
      "tenantId": "4dc1fe35-8ac6-4f0d-904a-7ebcd364bea1"
    }
  },
  "body": {
    "contentType": "html",
    "content": "<p>Hello world</p>"
  },
  "channelIdentity": null,
  "attachments": [
    
  ],
  "mentions": [
    
  ],
  "onBehalfOf": null,
  "policyViolation": null,
  "reactions": [
    
  ],
  "messageHistory": [
    
  ],
  "replies": [
    
  ],
  "hostedContents": [
    
  ],
  "eventDetail": null
}

変更通知は非同期であるため、順序が変わる場合があります。 アプリケーションでリソースを特定の順序で並べ替える必要がある場合は、暗号化解除されたコンテンツを適切なプロパティで並べ替える必要があります。 たとえば、チャット アプリケーションでメッセージを時系列で表示する必要がある場合は、復号化された chatMessagescreatedDateTime で並べ替えます。

チャット メッセージが編集されると、変更通知が編集用に送信され、 lastEditedDateTimeが更新されます。 最新バージョンのメッセージを表示することを意図している場合は、チャット アプリケーションで元のメッセージではなく編集されたメッセージを表示する必要があります。

「手順 4: メッセージの取得」contentType、イメージ、データ損失防止 (DLP)、保持ポリシーに関する注意事項は、復号化されたメッセージにも適用されます。

手順 8: 変更通知サブスクリプションを更新する

セキュリティ上の理由から、 chatMessage のサブスクリプションの有効期限は 60 分です。 バッファーを使用できるように、30 分ごとに更新することをお勧めします。 期限切れのサブスクリプションのライフサイクル通知は現在使用できません。そのため、「サブスクリプションの更新」の説明に従って expirationDateTime プロパティを更新することで、サブスクリプションを追跡し、有効期限が切れる前に サブスクリプションを更新する必要があります。 何千ものサブスクリプションを更新するには時間がかかるため、これはチャットごとの変更通知を回避する理由です。

サブスクリプションが更新される前に有効期限が切れると、一部の変更通知が見逃される可能性があります。 手順 4: メッセージの取得を繰り返して、メッセージを再同期します。

次の例は、サブスクリプションを更新する方法を示しています。

要求

PATCH https://graph.microsoft.com/v1.0/subscriptions/88aa8a88-88a8-88a8-8888-88a8aa88a88a
Content-type: application/json

{
   "expirationDateTime":"2023-01-12T18:23:45.9356913Z"
}

応答

HTTP/1.1 200 OK
Content-type: application/json

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#subscriptions/$entity",
    "id": "88aa8a88-88a8-88a8-8888-88a8aa88a88a",
    "resource": "/users/87d349ed-44d7-43e1-9a83-5f2406dee5bd/chats/getAllMessages",
    "applicationId": "aa8aaaa8-8aa8-88a8-888a-aaaa8a8aa88a",
    "changeType": "created",
    "clientState": null,
    "notificationUrl": "https://function-ms-teams-subscription-webhook-z2a2ig2bfq-uc.a.run.app",
    "notificationQueryOptions": null,
    "lifecycleNotificationUrl": null,
    "expirationDateTime": "2023-01-12T18:23:45.9356913Z",
    "creatorId": "8888a8a8-8a88-888a-88aa-8a888a88888a",
    "includeResourceData": null,
    "latestSupportedTlsVersion": "v1_2",
    "encryptionCertificate": null,
    "encryptionCertificateId": null,
    "notificationUrlAppId": null
}

手順 9: ビューポイントを取得して設定する

チャットの ビューポイント は、ユーザーがチャットを最後に読んだタイムスタンプをマークし、ビューポイントの下のメッセージが未読であることをユーザーが確認できるようにします。

チャットの視点を取得するには、次の例に示すように、チャット リソースで GET HTTP メソッドを使用します。

要求

GET https://graph.microsoft.com/v1.0/users/87d349ed-44d7-43e1-9a83-5f2406dee5bd/chats/19:b1234aaa12345a123aa12aa12aaaa1a9@thread.v2

応答

HTTP/1.1 200 OK
Content-type: application/json

{
    "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#chats/$entity",
    "id": "19:b1234aaa12345a123aa12aa12aaaa1a9@thread.v2",
    "topic": null,
    "createdDateTime": "2023-01-11T01:34:18.929Z",
    "lastUpdatedDateTime": "2023-01-11T01:34:18.929Z",
    "chatType": "group",
    "webUrl": "https://teams.microsoft.com/l/chat/19%3Ab1234aaa12345a123aa12aa12aaaa1a9%40thread.v2/0?tenantId=4dc1fe35-8ac6-4f0d-904a-7ebcd364bea1",
    "tenantId": "4dc1fe35-8ac6-4f0d-904a-7ebcd364bea1",
    "onlineMeetingInfo": null,
    "viewpoint": {
        "isHidden": false,
        "lastMessageReadDateTime": "2021-05-27T22:13:01.577Z"
    }
}

ユーザーのチャットの観点は、ユーザーがチャットを開封済みとしてマークしたり、チャットを未読としてマークしたり、チャットを非表示にしたり、チャットを再表示したりするたびに更新されます。

コスト見積もり

現時点では、ユーザーごと、チャットごとのメッセージの取得 (手順 4) では、従量課金は必要ありません (ただし、調整制限があります)。 変更通知にのみ、メッセージあたり 0.00075 ドルの従量課金があります。

アプリに 50 人のユーザーがいて、各ユーザーが 20 人のユーザーからメッセージを受信し、1 か月あたり 300 件のメッセージを送信する場合、おおよそのコストは次のようになります。

  • 50 人の受信者 x (20 人の送信者 x 300 メッセージ/月/送信者)/受信者 x $0.00075/message = 300,000 メッセージ/月 x $0.00075/message = $225/month。

最新の価格情報については、「 Microsoft Teams API のライセンスと支払いの要件」を参照してください。