Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
En este artículo se describe cómo insertar la experiencia de Microsoft Teams en la aplicación. Al insertar Teams en la aplicación, los usuarios pueden leer y enviar mensajes de Teams directamente desde la aplicación, sin tener que cambiar entre la aplicación y Teams.
Para mejorar el tiempo de respuesta de la aplicación y ayudar a reducir los costos, querrá minimizar el número de veces que se lee un mensaje de Microsoft Graph. En este artículo se explica cómo recuperar mensajes una vez y almacenarlos en caché y, a continuación, usar notificaciones de cambio para obtener solo los mensajes posteriores.
Paso 1: Diseño y configuración de la arquitectura
En el diagrama siguiente se muestra la arquitectura de alto nivel sugerida para una aplicación que se integra con Teams.
La arquitectura incluye tres componentes:
Interfaz de usuario de chat que obtiene entradas de usuario y muestra mensajes. La interfaz de usuario de chat realiza solicitudes de API (como
POST
/GET
chats,POST
/GET
mensajes) a las API de Teams. También obtiene nuevos mensajes en tiempo real del componente de servidor.Componente de servidor que se suscribe a notificaciones de cambio en tiempo real para obtener nuevos mensajes de las API de Teams. Cuando las API de Teams envían notificaciones de cambio, se requiere una dirección URL de webhook para escuchar las notificaciones de cambio y es posible que la interfaz de usuario, como el teléfono móvil de los usuarios, no tenga una dirección URL de webhook. Sin embargo, el componente de servidor tiene una dirección URL de webhook estable. A continuación, los nuevos mensajes se insertan desde el componente de servidor a la interfaz de usuario de chat, mediante métodos de comunicación como ASP.NET SignalR.
Nota:
También puede optar por tener el componente de servidor, en lugar de la interfaz de usuario de chat, realizar todas las solicitudes de API a las API de Teams y almacenar en caché todos los mensajes. Por ejemplo, si tiene otro componente del sistema back-end que también necesita realizar solicitudes de API, como para cumplimiento y auditoría, puede optar por centralizar las solicitudes de API y el almacenamiento en caché en el componente de servidor en su lugar.
Memoria caché que conserva los mensajes. Para mejorar el tiempo de respuesta de la aplicación y reducir potencialmente los costos, minimice la lectura del mismo mensaje varias veces almacenando mensajes en esta memoria caché. No quiere sorprenderse con los cargos de consumo de API más adelante. Para obtener información sobre cómo configurar una caché, consulte Incorporación de almacenamiento en caché para mejorar el rendimiento en Azure API Management.
Algunas API de Teams tienen requisitos de licencia y pago. Para obtener más información, consulte Modelos de pago y requisitos de licencia para obtener más información.
Después de configurar estos componentes, puede empezar a usar las API de Teams.
Paso 2: Crear un nuevo chat
Antes de enviar un nuevo chatMessage, debe crear un chat mediante la asignación de miembros. En el ejemplo siguiente se muestra cómo crear un chat de grupo. Para obtener más ejemplos que muestran cómo crear diferentes tipos de chat, vea Crear chat.
Solicitud
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')"
}
]
}
Respuesta
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
}
Paso 3: Enviar un mensaje en el chat
Los miembros del chat pueden enviar mensajes entre sí. En el ejemplo siguiente se muestra cómo enviar un mensaje simple. Para obtener más ejemplos, incluido el envío de otros medios, como archivos adjuntos y tarjetas adaptables, consulte Envío de chatMessage.
Solicitud
POST https://graph.microsoft.com/v1.0/chats/19:b1234aaa12345a123aa12aa12aaaa1a9@thread.v2/messages
Content-type: application/json
{
"body": {
"content": "Hello World"
}
}
Respuesta
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": []
}
Paso 4: Recuperar mensajes
Use el GET
método HTTP en el recurso chatMessages para recuperar mensajes.
Para mejorar el tiempo de respuesta de la aplicación, minimizar la limitación y reducir potencialmente los costos, minimice la lectura del mismo mensaje varias veces. Use el GET
método HTTP como exportación única o cuando las notificaciones de cambio hayan expirado y quiera volver a sincronizar los mensajes. De lo contrario, confíe en la memoria caché y cambie las notificaciones.
Microsoft Graph proporciona varias maneras de recuperar mensajes de chat:
-
Obtener todos los mensajes de todos los chats (en todos los chats):
GET /users/{user-id | user-principal-name}/chats/getAllMessages
-
Enumerar mensajes en un chat (por chat):
GET /chats/{chat-id}/messages
/getAllMessages
Mediante , puede obtener mensajes en todos los chats de un usuario. Esta API está diseñada para aplicaciones back-end, como aplicaciones de auditoría y cumplimiento, que a menudo obtienen mensajes en todos los chats a la vez. Solo admite permisos de aplicación . Además, se trata de una API de uso medido.
/messages
Mediante , puede realizar llamadas API desde la interfaz de usuario mediante permisos delegados, como se describe en el paso 1.
Las distintas API tienen límites de limitación diferentes. Por ejemplo, la API por chat /messages
tiene un límite de 30 solicitudes por segundo (rps) por aplicación por inquilino. Si un inquilino tiene 50 usuarios y cada usuario tiene 15 chats en promedio, y desea recuperar mensajes para todos los usuarios y todos los chats al principio del sistema, necesitaría al menos 50 usuarios x 15 solicitudes de chat/usuario = 750 solicitudes. En este caso, es mejor distribuir las solicitudes entre al menos 750 solicitudes / 30 rps = 25 segundos. Dado que hay un límite (máximo $top=50
) al número de mensajes que se devuelven en una respuesta, es posible que tenga que realizar varias solicitudes para obtener todos los mensajes.
En el ejemplo siguiente se muestra cómo usar la API por chat /messages
. De forma predeterminada, la lista devuelta de mensajes se ordena por lastModifiedDateTime
. En este ejemplo se ordena por createdDateTime. La ordenación se especifica mediante el parámetro de orderBy
consulta en la solicitud.
Las aplicaciones de mensajería interactiva típicas muestran solo los mensajes más recientes de forma predeterminada y los usuarios pueden cargar mensajes más antiguos paginando, desplazando o haciendo clic. Para recuperar solo los mensajes que necesita, ambas API anteriores también admiten el filtrado (por ejemplo, $top=10
, $filter=lastModifiedDateTime gt 2019-03-17T07:13:28.000z
).
Solicitud
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
Respuesta
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": []
}
]
}
En este ejemplo, contentType puede ser text
o html
; asegúrese de que la aplicación puede mostrar ambos.
Para obtener imágenes incrustadas en el mensaje de chat, realice una segunda llamada para recuperar chatMessageHostedContent. Para obtener más información, vea Obtener chatMessageHostedContent.
Se recomienda que la aplicación supervise el campo chatMessage.policyViolation.dlpAction , observe las notificaciones de cambio en este campo y oculte o marca los mensajes según la prevención de pérdida de datos (DLP) o reglas similares definidas por su organización. Los valores válidos son None
, NotifySender
y BlockAccess
. Actualmente, Teams omite BlockAccessExternal
. Para obtener más información sobre estos valores, vea chatMessagePolicyViolation resource type (Tipo de recurso chatMessagePolicyViolation).
Algunos mensajes son mensajes del sistema. Por ejemplo, el siguiente mensaje del sistema muestra que un nuevo miembro se unió al chat.
{
"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"
}
}
}
}
Paso 5: Almacenar en caché los mensajes
Dado que cada mensaje que recibe de getAllMessages o notificación de cambios está sujeto a cargos de consumo, querrá minimizar la lectura del mismo mensaje varias veces. Se recomienda almacenar en caché los mensajes durante al menos unas horas para que un usuario pueda volver a abrir rápidamente un chat reciente. No almacene en caché los mensajes durante más tiempo del permitido según las directivas de retención de la organización.
En el paso 6, decidirá si la memoria caché es por usuario o no.
Paso 6: Suscribirse a notificaciones de cambio
Microsoft Graph ofrece varios tipos de notificaciones de cambio para los mensajes, según lo especificado por las propiedades del recurso correspondientes:
- Por chat:
"resource": "/chats/{id}/messages"
- Por usuario, en todos los chats:
"resource": "/users/{id}/chats/getAllMessages"
- Por inquilino, en todos los chats:
"resource": "/chats/getAllMessages"
- Por aplicación, en todos los chats de un inquilino donde está instalada la aplicación:
"resource": "/appCatalogs/teamsApps/{id}/installedToChats/getAllMessages"
Si solo quieres realizar un seguimiento de chats específicos, /messages
es una opción, pero debes considerar cuántos chats diferentes tendrás que realizar. Hay un límite (por ejemplo, 10 000) en el número de notificaciones de cambio por chat; para obtener más información, consulte suscripciones. En su lugar, considere la posibilidad de suscribirse a una de las tres /getAllMessages
opciones, que obtienen mensajes en todos los chats de un usuario, inquilino o aplicación.
El componente de servidor back-end llama a las cuatro opciones. Dado que todos admiten permisos de aplicación , preste atención a la lógica de control de acceso para mostrar y ocultar los chats en consecuencia a medida que los usuarios se unan o se vayan. La opción por usuario, que también admite permisos delegados , podría ser más fácil de implementar, ya que las notificaciones de cambio ya son específicas del usuario; sin embargo, esto podría ser más costoso a largo plazo porque el mismo mensaje desencadenaría varias notificaciones de cambio, una para cada usuario suscrito, y es posible que necesite una memoria caché más grande para almacenar los mensajes duplicados. Para obtener más información sobre los permisos y los requisitos de licencia para los diferentes recursos suscritos, consulte Creación de una suscripción.
Las suscripciones de notificación de cambios tienen cargos de consumo. Especifique el model
parámetro en la propiedad de recurso , como se muestra en el ejemplo siguiente.
Al crear la suscripción, asegúrese de que la propiedad includeResourceData está establecida en true
y de que ha especificado las propiedades encryptionCertificate y encryptionCertificateId . De lo contrario, el contenido cifrado no se devolverá en las notificaciones de cambio. Para obtener más información, consulte Configuración de notificaciones de cambio que incluyen datos de recursos.
En el ejemplo siguiente se muestra cómo obtener todos los mensajes por usuario. Antes de usar este ejemplo, el punto de conexión de notificación de suscripción (especificado en la propiedad notificationUrl ) debe ser capaz de responder a una solicitud de validación, como se describe en Configuración de notificaciones para cambios en los datos de usuario. Si se produce un error en la validación, la solicitud para crear la suscripción devuelve un 400 Bad Request
error.
Para obtener más información sobre este ejemplo, consulte Creación de una suscripción.
Solicitud
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"
}
Respuesta
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/sMMMsssMsMMMsMMsMMMs4sMMsM4ssMsMsMMMss4ssMMMssssssM4s4MMMsMMMMMMMMsMMMMMMMssMMsMMMMMMMMM4MMMMMsMMMMMMMssMMsMMMMMMMMMM4MMMssMsMMMMMMMs4MMMMsMM4sssMsM4MsMMMsMssMMsMsMMM4MMssMMMsMssMMsMsMMMsMMssMMMsMsMsMMssMsMMMMMMMsM4MMMss4ssMMMsMMssM4MsMsM4Ms4sM4MssMssMsMssMMMMMMsMMMMMsMMsssMMMMMMMMMssMMMMMMMMsMssMMMMM4ssMMs4sMsM/+MM4444s4M/+4sss4MMMMMsMsMsss/s/sMMsMss4sMsMMMss4M4Ms44M4M4MsssssM4M4MMMM444Mss4+s4M44MsssMMMs4Ms4MsMMsMMsMsMMM4sMMMMsssMssssMMss44MMs+MMssMsMsM4sMMs4MsMsM4ssM4MMMsMMs4sMMM4MsM+MsMss+sMsMM4sMM4sMMM4ss4ssssMMMsssM4MMssM+MsM/sMMss4MsMMM44+/MMMsMs4s44M++ssssssMMs/MsMMMMsMMssMsssssMMss4MMMsM4s4MssMsMssMsMMMMMMs4sMMssMsMMMM/ss4sMMsMMsMMMsMMMMMsssM4MMsMMMsMMMMMsssMMsMsMMssMsMMMsMMMMMMMsMsMsMMMsMMMMMMMsMsMMMMMsMMMMMMMsMMMMMsMsMsMsMMMMMMMsMMssMsMMMMsMsM4Ms+sMssMs4sMsMsssM4M4Ms4MMMMMMMMMssssMMMsssMsMMMMsMMMMMMs4sssM4MMMMMMsMMMMMMsMMsssssMMsMs4sM4MsMs4sM4Mss44ssM4ss44ssMsssM4sssMsM4MssMMsM44sMMsMMM4MM4MsMM4MMMMsM4MMM4MMMMMsMMssMsMsMMMsM4MsMsMsMM4sssMsMsMMMsMMMMMMMMMMM4s4sMM4Ms4sssssMsMsMM4sMsssMMssM4MMMMMMMMsMMMMMMMMsMM4MMssMMM4MMMMsMsMMssMsMMMsMMMMMMMsMMMsM4M4MMMMMMsMMMMMMsMMsssssMMsMs4sM4MsMs4ssMMsM4MsM4MsMM4MMsMMM4sMMsMMMMMsMsMMMM4MMsssMM4MMMMsMM4sssMsMsMMMMMsMMM4MsMssMMMMsssMsMMMMssMsMMsMM4sMssM4MssMMsMM4sMssssM4ssMMsM44sMMsMMM4MM4MsMM4MMMMsM4MMM4MMMMMsMMssMsMsMMMsM4MsMsMsMM4sssMsMsMMMsMMMMMMMMMMM4s4sMM4Ms4ssss4MsMsMM4sMsssMMssM4MMMMMMMMsMMMMMMMMsMM4MMssMMM4MMMMsMsMMssMsMMMsMMMMMMMsMMMsM4M4MM4MM4MsMsMMMMMsM4M4ssMMMssssMMMMMsM/s4MsMMMMsMMMM4MMs4MMMMMMsMsMsMMMM4MMMMsMsMssMMssMMsssMssM4ss4MssM4ssMMssssssMMsss4ss44sssMsMsMMMM4MssMsMMMMMMMMMMMsssMMsMMMMMM/sMM4sMssM4MssM4ssMMss4MsMsMsM44sM4MssMssMsMsM4MMMM4MMMMsMsMMssMsMMMsMMMMMMMsMMMsM4MsssMssMMsMs4sM4MsMM4ssMMsM4MsM4MssM4MMMMsMsMMssMsMMMsMMMMMMMsMMMsM4MsssMssMMsMs4sM4MsMs4ssMMsM4MsM4MssM4MMMMsMsMMssMsMMMsMMMMMMMsMMMsM4MsssMssMMsMs4sM4MsMs4ssMMsM4MsM4MssM4MMMMsMsMMssMsMMMsMMMMMMMsMMMsM4MsssMssMMsMs4sM4MsMM4ssMMsM4MsM4MssM4MMMMsMsMMssMsMMMsMMMMMMMsMMMsM4MsMM4MM4MsMsMMMMsMMMsMMMMssMssss4s+MMM44MMMsMsMM4MM4MsMMMMMMMMMMsMMMMMMsMMMsssMsMMMMsMMsMMMssssssM4s4MMMsMMMMMMMMMMMM4MMMMssss444MsMsMMM44MM/444sMMMs4sMsMM4sMMMssMM4+M4sssMs+MsMMMMM/M/s4MMssM4ssss/4MMMsssMsMMss44sMsss4++ss/4s+s4sMs+4sM4MsM/4/MssMMMsMssMs4MsMss4MMsMsMssssssMMM4MsMM4s+MMM4M4sMMMMs4s4sMMMMsM444ssM4MMsssMMMMsM4MsMsMMM4sMsMs4sMsMMMMMs4MsMsMsMsM4sMs4sMMMMMsssMssMsMsMMss4MMM4sMsM4sMMssMMsM44MM4ss4s4Ms44sMMM4ssss4Ms4sMM4MMMMM4MMs+ss4MsMssMss4s==",
"encryptionCertificateId": "44M4444M4444M4M44MM4444MM4444MMMM44MM4M4",
"notificationUrlAppId": null
}
Paso 7: Recepción y descifrado de notificaciones de cambio
Cada vez que se produce un cambio en el recurso suscrito, se envía una notificación de cambio a notificationUrl. Por motivos de seguridad, el contenido se cifra. Para descifrar el contenido, consulte Descifrado de datos de recursos a partir de notificaciones de cambios.
Al crear la suscripción, asegúrese de que la propiedad includeResourceData está establecida en true
y de que ha especificado las propiedades encryptionCertificate y encryptionCertificateId . De lo contrario, el contenido cifrado no se devolverá en las notificaciones de cambio. Para obtener más información, consulte Validación de la notificación del punto de conexión.
Solicitud (enviada por 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"
]
}
Contenido descifrado
{
"@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
}
A veces, las notificaciones de cambio se entregan sin orden, porque son asincrónicas. Si la aplicación requiere que los recursos se ordenen en un orden determinado, asegúrese de ordenar el contenido descifrado por la propiedad adecuada. Por ejemplo, si los mensajes deben mostrarse en orden cronológico en la aplicación de chat, ordene los chatMessages descifrados por createdDateTime.
Cuando se edita un mensaje de chat, se envía una notificación de cambio para la edición, con un objeto actualizado lastEditedDateTime
. La aplicación de chat debe mostrar el mensaje editado en lugar del mensaje original, si la intención es mostrar la versión más reciente de los mensajes.
Las notas sobre contentType, imágenes, prevención de pérdida de datos (DLP) y directivas de retención del paso 4: Recuperar mensajes también se aplican a los mensajes descifrados.
Paso 8: Renovación de suscripciones de notificación de cambios
Por motivos de seguridad, las suscripciones para chatMessage expiran en 60 minutos. Se recomienda renovar cada 30 minutos para permitir algún búfer. Las notificaciones de ciclo de vida de las suscripciones que expiran no están disponibles actualmente; por lo tanto, debe realizar un seguimiento de las suscripciones y renovarlas antes de que expiren actualizando la propiedad expirationDateTime , tal como se describe en Actualizar suscripción. Dado que la renovación de miles de suscripciones tarda tiempo, esta es una razón para evitar las notificaciones de cambio por chat.
Si una suscripción expira antes de que se renueve, es posible que se pierdan algunas notificaciones de cambio. Vuelva a sincronizar los mensajes repitiendo Paso 4: Recuperar mensajes.
En el ejemplo siguiente se muestra cómo renovar una suscripción.
Solicitud
PATCH https://graph.microsoft.com/v1.0/subscriptions/88aa8a88-88a8-88a8-8888-88a8aa88a88a
Content-type: application/json
{
"expirationDateTime":"2023-01-12T18:23:45.9356913Z"
}
Respuesta
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
}
Paso 9: Obtener y establecer puntos de vista
Un punto de vista de un chat marca la marca de tiempo en la que los usuarios leyeron por última vez el chat, de modo que los usuarios puedan ver que los mensajes bajo el punto de vista no están leídos.
Para obtener el punto de vista de un chat, use el GET
método HTTP en el recurso chats , como se muestra en el ejemplo siguiente.
Solicitud
GET https://graph.microsoft.com/v1.0/users/87d349ed-44d7-43e1-9a83-5f2406dee5bd/chats/19:b1234aaa12345a123aa12aa12aaaa1a9@thread.v2
Respuesta
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"
}
}
El punto de vista de un chat para un usuario se actualiza cada vez que el usuario marca el chat como leído, marca el chat como no leído, oculta el chat o desencuencael chat.
Estimación de costos
Actualmente, la recuperación de mensajes por usuario y por chat (paso 4) no implica cargos por consumo (pero tiene límites de limitación). Solo las notificaciones de cambio tienen cargos de consumo de 0,00075 USD por mensaje.
Si la aplicación tiene 50 usuarios y cada usuario recibe mensajes de 20 usuarios y envía 300 mensajes al mes, el costo aproximado sería:
- 50 destinatarios x (20 remitentes x 300 mensajes/mes/remitente)/destinatario x $0,00075/message = 300 000 mensajes/mes x $0,00075/message = $225/mes.
Para obtener la información de precios más actualizada, consulte Requisitos de pago y licencias de la API de Microsoft Teams.