Incorporar o Microsoft Teams na sua aplicação

Este artigo descreve como incorporar a experiência do Microsoft Teams na sua aplicação. Quando incorporar o Teams na sua aplicação, os seus utilizadores podem ler e enviar mensagens do Teams diretamente a partir da sua aplicação, sem terem de alternar entre a sua aplicação e o Teams.

Para melhorar o tempo de resposta da sua aplicação e ajudar a reduzir os custos, deverá minimizar o número de vezes que uma mensagem é lida a partir do Microsoft Graph. Este artigo explica como obter mensagens uma vez e colocar as mensagens em cache e, em seguida, utilizar notificações de alteração para obter apenas as mensagens subsequentes.

Passo 1: Estruturar e configurar a arquitetura

O diagrama seguinte mostra a arquitetura de alto nível sugerida para uma aplicação que se integra com o Teams.

Diagrama a mostrar a integração do Teams com uma IU da aplicação

A arquitetura inclui três componentes:

  • Uma IU de chat que obtém entradas de utilizador e apresenta mensagens. A IU do chat faz pedidos de API (como POST/GET conversas, POST/GET mensagens) às APIs do Teams. Também recebe novas mensagens em tempo real a partir do componente do servidor.

  • Um componente de servidor que subscreve a alteração de notificações em tempo real para obter novas mensagens a partir das APIs do Teams. Quando as APIs do Teams enviam notificações de alteração, é necessário um URL de webhook para ouvir as notificações de alteração e a IU, como o telemóvel dos utilizadores, poderá não ter um URL de webhook. No entanto, o componente do servidor tem um URL de webhook estável. Em seguida, as novas mensagens são enviadas do componente do servidor para a IU do chat, através de métodos de comunicação como o ASP.NET SignalR.

    Observação

    Também pode optar por ter o componente do servidor, em vez da IU do chat, fazer todos os pedidos da API às APIs do Teams e colocar todas as mensagens em cache. Por exemplo, se tiver outro componente do sistema de back-end que também precisa de fazer pedidos de API, como para conformidade e auditoria, poderá optar por centralizar os pedidos da API e colocar em cache no componente do servidor.

  • Uma cache que persiste nas mensagens. Para melhorar o tempo de resposta da sua aplicação e reduzir potencialmente os custos por si, minimize a leitura da mesma mensagem várias vezes ao armazenar mensagens nesta cache. Não quer ficar surpreendido com os custos de consumo da API mais tarde. Para saber como configurar uma cache, veja Adicionar colocação em cache para melhorar o desempenho no Azure Gerenciamento de API.

Depois de configurar estes componentes, pode começar a utilizar as APIs do Teams.

Passo 2: criar uma nova conversa

Antes de enviar um novo chatMessage, tem de criar uma conversa ao atribuir membros. O exemplo seguinte mostra como criar uma conversa de grupo. Para obter mais exemplos que mostram como criar diferentes tipos de chat, consulte Criar chat.

Solicitação

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')"
        }
    ]
}

Resposta

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
}

Passo 3: enviar uma mensagem no chat

Os membros na conversa podem enviar mensagens entre si. O exemplo seguinte mostra como enviar uma mensagem simples. Para obter mais exemplos, incluindo o envio de outros suportes de dados, como anexos de ficheiros e cartões ajustáveis, consulte Enviar chatMessage.

Solicitação

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

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

Resposta

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": []
}

Passo 4: Obter mensagens

Utilize o GET método HTTP no recurso chatMessages para obter mensagens.

Para melhorar o tempo de resposta da sua aplicação, minimizar a limitação e reduzir potencialmente os custos por si, minimize a leitura da mesma mensagem várias vezes. Utilize o GET método HTTP como uma exportação única ou quando as notificações de alteração expirarem e pretender sincronizar as mensagens novamente. Caso contrário, confie na cache e altere as notificações.

O Microsoft Graph fornece várias formas de obter mensagens de chat:

Ao utilizar /getAllMessageso , pode obter mensagens em todas as conversas de um utilizador. Esta API foi concebida para aplicações de back-end, como aplicações de auditoria e conformidade, que muitas vezes recebem mensagens em todas as conversas ao mesmo tempo. Suporta apenas permissões de aplicação . Além disso, trata-se de uma API com tráfego limitado.

Ao utilizar /messageso , pode fazer chamadas à API a partir da IU com permissões delegadas , conforme descrito no Passo 1.

As APIs diferentes têm limites de limitação diferentes. Por exemplo, a API por chat /messages tem um limite de 30 pedidos por segundo (rps) por aplicação por inquilino. Se um inquilino tiver 50 utilizadores e cada utilizador tiver, em média, 15 conversas e quiser obter mensagens para todos os utilizadores e todas as conversas no início do seu sistema, precisará de, pelo menos, 50 utilizadores x 15 pedidos de chat/utilizador = 750 pedidos. Neste caso, é melhor distribuir os pedidos por, pelo menos, 750 pedidos/30 rps = 25 segundos. Uma vez que existe um limite (máximo $top=50) para o número de mensagens devolvidas numa resposta, poderá ter de fazer vários pedidos para obter todas as mensagens.

O exemplo seguinte mostra como utilizar a API por chat /messages . Por predefinição, a lista de mensagens devolvida é ordenada por lastModifiedDateTime. Este exemplo ordena por createdDateTime. A ordenação é especificada através do orderBy parâmetro de consulta no pedido.

As aplicações de mensagens interativas típicas apresentam apenas as mensagens mais recentes por predefinição e, em seguida, os utilizadores podem carregar mensagens mais antigas ao paginar, deslocar ou clicar. Para obter apenas as mensagens de que precisa, ambas as APIs acima também suportam a filtragem (por exemplo, $top=10, $filter=lastModifiedDateTime gt 2019-03-17T07:13:28.000z).

Solicitação

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

Resposta

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": []
        }
    ]
}

Neste exemplo, o contentType pode ser text ou html; certifique-se de que a sua aplicação pode apresentar ambos.

Para incorporar imagens na mensagem de chat, faça uma segunda chamada para obter o chatMessageHostedContent. Para obter detalhes, consulte Obter chatMessageHostedContent.

Recomendamos que a sua aplicação monitorize o campo chatMessage.policyViolation.dlpAction , que procure notificações de alteração para este campo e oculte ou sinaliza as mensagens de acordo com a prevenção de perda de dados (DLP) ou regras semelhantes definidas pela sua organização. Os valores válidos são None, NotifySendere BlockAccess. Atualmente, o BlockAccessExternalTeams ignora . Para obter detalhes sobre estes valores, veja chatMessagePolicyViolation resource type (Tipo de recurso chatMessagePolicyViolation).

Algumas mensagens são mensagens do sistema. Por exemplo, a seguinte mensagem de sistema mostra que um novo membro aderiu à conversa.

{
  "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"
      }
    }
  }
}

Passo 5: Colocar mensagens em cache

Uma vez que cada mensagem que recebe de getAllMessages ou notificação de alteração está sujeita a custos de consumo, vai querer minimizar a leitura da mesma mensagem várias vezes. Recomendamos que coloque as mensagens em cache durante, pelo menos, algumas horas para que um utilizador possa reabrir rapidamente uma conversa recente. Não coloque as mensagens em cache durante mais tempo do que o permitido pelas políticas de retenção da sua organização.

No Passo 6, decidirá se a cache é ou não por utilizador.

Passo 6: subscrever notificações de alteração

O Microsoft Graph oferece vários tipos de notificações de alteração para mensagens, conforme especificado pelas propriedades de recursos correspondentes:

  • Por chat: "resource": "/chats/{id}/messages"
  • Por utilizador, em todas as conversas: "resource": "/users/{id}/chats/getAllMessages"
  • Por inquilino, em todas as conversas: "resource": "/chats/getAllMessages"
  • Por aplicação, em todas as conversas num inquilino onde a aplicação está instalada: "resource": "/appCatalogs/teamsApps/{id}/installedToChats/getAllMessages"

Se quiser controlar apenas conversas específicas, /messages é uma opção, mas deve considerar o número de conversas diferentes que precisa de controlar. Existe um limite no número de notificações de alteração por chat. Em vez disso, considere subscrever uma das três /getAllMessages opções, que obtêm mensagens em todas as conversas de um utilizador, inquilino ou aplicação.

Todas as quatro opções são chamadas pelo componente do servidor back-end. Uma vez que todas suportam permissões de aplicação , preste atenção à lógica de controlo de acesso para mostrar e ocultar conversas em conformidade à medida que os utilizadores entram ou saem. A opção por utilizador, que também suporta permissões delegadas , poderá ser mais fácil de implementar, porque as notificações de alteração já são específicas do utilizador; No entanto, pode ser mais dispendioso a longo prazo porque a mesma mensagem acionaria várias notificações de alteração, uma para cada utilizador subscrito e poderá precisar de uma cache maior para armazenar as mensagens duplicadas. Para obter mais detalhes sobre permissões e requisitos de licenciamento para os diferentes recursos subscritos, veja Criar subscrição.

Ao criar a subscrição, certifique-se de que a propriedade includeResourceData está definida como truee que especificou as propriedades encryptionCertificate e encryptionCertificateId . Caso contrário, o conteúdo encriptado não será devolvido nas notificações de alteração. Para obter detalhes, veja Configurar notificações de alteração que incluem dados de recursos.

O exemplo seguinte mostra como obter todas as mensagens por utilizador. Antes de utilizar este exemplo, o ponto final de notificação de subscrição especificado na propriedade notificationUrl tem de conseguir responder a um pedido de validação, conforme descrito em Configurar notificações para alterações nos dados do utilizador. Se a validação falhar, o pedido para criar a subscrição devolve um 400 Bad Request erro.

Para obter mais detalhes sobre este exemplo, veja Criar subscrição.

Solicitação

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",
  "expirationDateTime": "2023-01-10T18:56:49.112603+00:00",
  "clientState": "ClientSecret",
  "includeResourceData": true,
  "encryptionCertificate": "MMMM/sMMMsssMsMMMsMMsMMMs4sMMsM4ssMsMsMMMss4ssMMMssss...s4sMMMMsM444ssM4MMsssMMMMsM4MMM4sMsM4MMsM44MMM4ssss4Ms4sMM4MMMMM4MMs+ss4MsMssMss4s==",
  "encryptionCertificateId": "44M4444M4444M4M44MM4444MM4444MMMM44MM4M4"
}

Resposta

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",
  "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
}

Passo 7: Receber e desencriptar notificações de alteração

Sempre que existir uma alteração ao recurso subscrito, é enviada uma notificação de alteração para o notificationUrl. Por motivos de segurança, o conteúdo é encriptado. Para desencriptar o conteúdo, veja Desencriptar dados de recursos a partir de notificações de alteração.

Quando criar a subscrição, certifique-se de que a propriedade includeResourceData está definida como truee que especificou as propriedades encryptionCertificate e encryptionCertificateId . Caso contrário, o conteúdo encriptado não será devolvido nas notificações de alteração. Para obter detalhes, confira Validação do ponto de extremidade da notificação.

Pedido (enviado pelo 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"
  ]
}

Conteúdo desencriptado

{
  "@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
}

Por vezes, as notificações de alteração são entregues fora de ordem, porque são assíncronas. Se a sua aplicação exigir que os recursos sejam ordenados por uma ordem específica, certifique-se de que ordena o conteúdo desencriptado pela propriedade adequada. Por exemplo, se as mensagens devem ser apresentadas por ordem cronológica na sua aplicação de chat, ordene as chatMessages desencriptadas por createdDateTime.

Quando uma mensagem de chat é editada, é enviada uma notificação de alteração para a edição, com uma atualização lastEditedDateTime. A sua aplicação de chat deve apresentar a mensagem editada em vez da mensagem original, se a intenção for apresentar a versão mais recente das mensagens.

As notas sobre contentType, imagens, prevenção de perda de dados (DLP) e políticas de retenção no Passo 4: Obter mensagens também se aplicam às mensagens desencriptadas.

Passo 8: Renovar subscrições de notificação de alteração

Por motivos de segurança, as subscrições do chatMessage expiram em 60 minutos. Recomendamos que renove a cada 30 minutos para permitir alguma memória intermédia. As notificações de ciclo de vida para subscrições prestes a expirar não estão atualmente disponíveis; Por conseguinte, tem de controlar as subscrições e renová-las antes de expirarem atualizando a propriedade expirationDateTime , conforme descrito em Atualizar subscrição. Uma vez que a renovação de milhares de subscrições demora algum tempo, esta é uma razão para evitar notificações de alteração por chat.

Se uma subscrição expirar antes de ser renovada, poderão ser perdidas algumas notificações de alteração. Ressincronize as mensagens ao repetir o Passo 4: Obter mensagens.

O exemplo seguinte mostra como renovar uma subscrição.

Solicitação

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

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

Resposta

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
}

Passo 9: Obter e definir pontos de vista

Um ponto de vista numa conversa marca o carimbo de data/hora em que a conversa foi lida pela última vez pelos utilizadores, para que os utilizadores possam ver que as mensagens sob o ponto de vista não são lidas.

Para obter o ponto de vista de uma conversa, utilize o GET método HTTP no recurso chats , conforme mostrado no exemplo seguinte.

Solicitação

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

Resposta

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"
    }
}

O ponto de vista de uma conversa para um utilizador é atualizado sempre que o utilizador marca a conversa como lida, marca a conversa como não lida, oculta a conversa ou desativaa conversa.

Estimativa de custos

Atualmente, a obtenção de mensagens por utilizador, por chat (Passo 4) não envolve custos de consumo (mas tem limites de limitação). Apenas as notificações de alteração têm custos de consumo de 0,00075 $ por mensagem.

Se a sua aplicação tiver 50 utilizadores e cada utilizador receber mensagens de 20 utilizadores e enviar 300 mensagens por mês, o custo aproximado será:

  • 50 destinatários x (20 remetentes x 300 mensagens/mês/remetente)/destinatário x $0,00075/mensagem = 300 000 mensagens/mês x $0,00075/mensagem = $225/mês.

Para obter as informações de preços mais atualizadas, veja Requisitos de licenciamento e pagamento da API do Microsoft Teams.