Démarrage rapide : Joindre votre application de conversation à une réunion Teams

Démarrez avec Azure Communication Services en connectant votre solution de conversation à Microsoft Teams.

Dans ce guide de démarrage rapide, vous allez découvrir comment rejoindre une conversation dans une réunion Teams à l’aide du kit SDK Conversation Azure Communication Services pour JavaScript.

Exemple de code

Vous trouverez le code finalisé pour ce guide de démarrage rapide sur GitHub.

Prérequis

Participation à la conversation d’une réunion

Un utilisateur de Communication Services peut participer à une réunion Teams en tant qu’utilisateur anonyme à l’aide du kit de développement logiciel (SDK) Appel. En rejoignant la réunion, il est également ajouté comme participant à la conversation de la réunion, où il peut échanger des messages avec d’autres utilisateurs connectés. L’utilisateur n’aura pas accès aux messages de la conversation envoyés avant qu’il rejoigne la réunion. Il ne pourra pas non plus envoyer ni recevoir des messages une fois la réunion terminée. Pour participer à la réunion et commencer à converser, vous pouvez suivre les étapes suivantes.

Création d’une application Node.js

Ouvrez votre fenêtre de terminal ou de commande, créez un répertoire pour votre application, puis accédez-y.

mkdir chat-interop-quickstart && cd chat-interop-quickstart

Exécutez npm init -y pour créer un fichier package.json avec les paramètres par défaut.

npm init -y

Installer les packages de chat

Utilisez la commande npm install pour installer les kits de développement logiciel (SDK) Communication Services pour JavaScript nécessaires.

npm install @azure/communication-common --save

npm install @azure/communication-identity --save

npm install @azure/communication-chat --save

npm install @azure/communication-calling --save

L’option --save liste la bibliothèque comme dépendance dans votre fichier package.json.

Configurer le framework d’application

Ce guide de démarrage rapide utilise Webpack pour regrouper les ressources de l’application. Exécutez la commande suivante pour installer les packages npm webpack, webpack-cli et webpack-dev-server, puis listez-les comme dépendances de développement dans votre fichier package.json :

npm install webpack@5.89.0 webpack-cli@5.1.4 webpack-dev-server@4.15.1 --save-dev

Créez un fichier index.html dans le répertoire racine de votre projet. Nous utilisons ce fichier pour configurer une disposition de base qui permettra à l’utilisateur de participer à une réunion et de démarrer une conversation.

Ajouter les contrôles d’interface utilisateur de Teams

Remplacez le code dans index.html par l’extrait de code suivant. La zone de texte en haut de la page sera utilisée pour entrer le contexte de la réunion Teams. Le bouton « Participer à la réunion Teams » est utilisé pour rejoindre la réunion spécifiée. Une fenêtre de conversation contextuelle s’affiche au bas de la page. Elle permet d’envoyer des messages sur le thread de la réunion et affiche en temps réel tous les messages envoyés sur ce thread tant que l’utilisateur Communication Services en est membre.

<!DOCTYPE html>
<html>
   <head>
      <title>Communication Client - Calling and Chat Sample</title>
      <style>
         body {box-sizing: border-box;}
         /* The popup chat - hidden by default */
         .chat-popup {
         display: none;
         position: fixed;
         bottom: 0;
         left: 15px;
         border: 3px solid #f1f1f1;
         z-index: 9;
         }
         .message-box {
         display: none;
         position: fixed;
         bottom: 0;
         left: 15px;
         border: 3px solid #FFFACD;
         z-index: 9;
         }
         .form-container {
         max-width: 300px;
         padding: 10px;
         background-color: white;
         }
         .form-container textarea {
         width: 90%;
         padding: 15px;
         margin: 5px 0 22px 0;
         border: none;
         background: #e1e1e1;
         resize: none;
         min-height: 50px;
         }
         .form-container .btn {
         background-color: #4CAF40;
         color: white;
         padding: 14px 18px;
         margin-bottom:10px;
         opacity: 0.6;
         border: none;
         cursor: pointer;
         width: 100%;
         }
         .container {
         border: 1px solid #dedede;
         background-color: #F1F1F1;
         border-radius: 3px;
         padding: 8px;
         margin: 8px 0;
         }
         .darker {
         border-color: #ccc;
         background-color: #ffdab9;
         margin-left: 25px;
         margin-right: 3px;
         }
         .lighter {
         margin-right: 20px;
         margin-left: 3px;
         }
         .container::after {
         content: "";
         clear: both;
         display: table;
         }
      </style>
   </head>
   <body>
      <h4>Azure Communication Services</h4>
      <h1>Calling and Chat Quickstart</h1>
          <input id="teams-link-input" type="text" placeholder="Teams meeting link"
        style="margin-bottom:1em; width: 400px;" />
        <p>Call state <span style="font-weight: bold" id="call-state">-</span></p>
      <div>
        <button id="join-meeting-button" type="button">
            Join Teams Meeting
        </button>
        <button id="hang-up-button" type="button" disabled="true">
            Hang Up
        </button>
      </div>
      <div class="chat-popup" id="chat-box">
         <div id="messages-container"></div>
         <form class="form-container">
            <textarea placeholder="Type message.." name="msg" id="message-box" required></textarea>
            <button type="button" class="btn" id="send-message">Send</button>
         </form>
      </div>
      <script src="./bundle.js"></script>
   </body>
</html>

Activer les contrôles d’interface utilisateur de Teams

Remplacez le contenu du fichier client.js par l’extrait de code suivant.

Dans l’extrait de code, remplacez :

  • SECRET_CONNECTION_STRING par la chaîne de connexion de votre service de communication ;
import { CallClient } from "@azure/communication-calling";
import { AzureCommunicationTokenCredential } from "@azure/communication-common";
import { CommunicationIdentityClient } from "@azure/communication-identity";
import { ChatClient } from "@azure/communication-chat";

let call;
let callAgent;
let chatClient;
let chatThreadClient;

const meetingLinkInput = document.getElementById("teams-link-input");
const callButton = document.getElementById("join-meeting-button");
const hangUpButton = document.getElementById("hang-up-button");
const callStateElement = document.getElementById("call-state");

const messagesContainer = document.getElementById("messages-container");
const chatBox = document.getElementById("chat-box");
const sendMessageButton = document.getElementById("send-message");
const messageBox = document.getElementById("message-box");

var userId = "";
var messages = "";
var chatThreadId = "";

async function init() {
  const connectionString = "<SECRET_CONNECTION_STRING>";
  const endpointUrl = connectionString.split(";")[0];

  const identityClient = new CommunicationIdentityClient(connectionString);

  let identityResponse = await identityClient.createUser();
  userId = identityResponse.communicationUserId;
  console.log(`\nCreated an identity with ID: ${identityResponse.communicationUserId}`);

  let tokenResponse = await identityClient.getToken(identityResponse, ["voip", "chat"]);

  const { token, expiresOn } = tokenResponse;
  console.log(`\nIssued an access token that expires at: ${expiresOn}`);
  console.log(token);

  const callClient = new CallClient();
  const tokenCredential = new AzureCommunicationTokenCredential(token);

  callAgent = await callClient.createCallAgent(tokenCredential);
  callButton.disabled = false;
  chatClient = new ChatClient(endpointUrl, new AzureCommunicationTokenCredential(token));

  console.log("Azure Communication Chat client created!");
}

init();

const joinCall = (urlString, callAgent) => {
  const url = new URL(urlString);
  console.log(url);
  if (url.pathname.startsWith("/meet")) {
    // Short teams URL, so for now call meetingID and pass code API
    return callAgent.join({
      meetingId: url.pathname.split("/").pop(),
      passcode: url.searchParams.get("p"),
    });
  } else {
    return callAgent.join({ meetingLink: urlString }, {});
  }
};

callButton.addEventListener("click", async () => {
  // join with meeting link
  try {
    call = joinCall(meetingLinkInput.value, callAgent);
  } catch {
    throw new Error("Could not join meeting - have you set your connection string?");
  }

  // Chat thread ID is provided from the call info, after connection.
  call.on("stateChanged", async () => {
    callStateElement.innerText = call.state;

    if (call.state === "Connected" && !chatThreadClient) {
      chatThreadId = call.info?.threadId;
      chatThreadClient = chatClient.getChatThreadClient(chatThreadId);

      chatBox.style.display = "block";
      messagesContainer.innerHTML = messages;

      // open notifications channel
      await chatClient.startRealtimeNotifications();

      // subscribe to new message notifications
      chatClient.on("chatMessageReceived", (e) => {
        console.log("Notification chatMessageReceived!");

        // check whether the notification is intended for the current thread
        if (chatThreadId != e.threadId) {
          return;
        }

        if (e.sender.communicationUserId != userId) {
          renderReceivedMessage(e.message);
        } else {
          renderSentMessage(e.message);
        }
      });
    }
  });

  // toggle button and chat box states
  hangUpButton.disabled = false;
  callButton.disabled = true;

  console.log(call);
});

async function renderReceivedMessage(message) {
  messages += '<div class="container lighter">' + message + "</div>";
  messagesContainer.innerHTML = messages;
}

async function renderSentMessage(message) {
  messages += '<div class="container darker">' + message + "</div>";
  messagesContainer.innerHTML = messages;
}

hangUpButton.addEventListener("click", async () => {
  // end the current call
  await call.hangUp();
  // Stop notifications
  chatClient.stopRealtimeNotifications();

  // toggle button states
  hangUpButton.disabled = true;
  callButton.disabled = false;
  callStateElement.innerText = "-";

  // toggle chat states
  chatBox.style.display = "none";
  messages = "";
  // Remove local ref
  chatThreadClient = undefined;
});

sendMessageButton.addEventListener("click", async () => {
  let message = messageBox.value;

  let sendMessageRequest = { content: message };
  let sendMessageOptions = { senderDisplayName: "Jack" };
  let sendChatMessageResult = await chatThreadClient.sendMessage(
    sendMessageRequest,
    sendMessageOptions
  );
  let messageId = sendChatMessageResult.id;

  messageBox.value = "";
  console.log(`Message sent!, message id:${messageId}`);
});

Les noms d’affichage des participants aux conversations ne sont pas définis par le client Teams. La valeur Null est renvoyée dans l’API pour la liste des participants, dans l’événement participantsAdded et dans l’événement participantsRemoved. Les noms d’affichage des participants à la conversation peuvent être récupérés dans le champ remoteParticipants de l’objet call. Lorsque vous recevez une notification concernant une modification de la liste, vous pouvez utiliser ce code pour récupérer le nom de l’utilisateur qui a été ajouté ou supprimé :

var displayName = call.remoteParticipants.find(p => p.identifier.communicationUserId == '<REMOTE_USER_ID>').displayName;

Exécuter le code

Les utilisateurs de Webpack peuvent utiliser webpack-dev-server pour générer et exécuter votre application. Exécutez la commande suivante pour créer un bundle de votre application hôte sur un serveur web local :

npx webpack-dev-server --entry ./client.js --output bundle.js --debug --devtool inline-source-map

Ouvrez votre navigateur et accédez à http://localhost:8080/. Vous devriez voir l’application lancée, comme illustré dans la capture d’écran suivante :

Capture d’écran de l’application JavaScript terminée.

Insérez le lien de la réunion Teams dans la zone de texte. Appuyez sur Participer à une réunion Teams pour participer à la réunion Teams. Une fois que l’utilisateur Communication Services est admis à la réunion, vous pouvez converser à partir de votre application Azure Communication Services. Accédez à la zone en bas de la page pour commencer la conversation. Par souci de simplicité, l’application n’affiche que les deux derniers messages de la conversation.

Notes

Certaines fonctionnalités ne sont actuellement pas prises en charge pour les scénarios d’interopérabilité avec Teams. Pour en savoir plus sur les fonctionnalités prises en charge, consultez Fonctionnalités de réunion Teams pour les utilisateurs Teams externes.

Dans ce guide de démarrage rapide, vous allez découvrir comment rejoindre une conversation dans une réunion Teams à l’aide du kit SDK Conversation Azure Communication Services pour iOS.

Prérequis

Participation à la conversation d’une réunion

Un utilisateur de Communication Services peut participer à une réunion Teams en tant qu’utilisateur anonyme à l’aide du kit de développement logiciel (SDK) Appel. En rejoignant la réunion, il est également ajouté comme participant à la conversation de la réunion, où il peut échanger des messages avec d’autres utilisateurs connectés. L’utilisateur n’aura pas accès aux messages de la conversation envoyés avant qu’il rejoigne la réunion. Il ne pourra pas non plus envoyer ni recevoir des messages une fois la réunion terminée. Pour participer à la réunion et commencer à converser, vous pouvez suivre les étapes suivantes.

Ajouter la conversation à l’application d’appel Teams

Supprimez Podfile.lock et remplacez le contenu du Podfile par le code suivant :

platform :ios, '13.0'
use_frameworks!

target 'AzureCommunicationCallingSample' do
  pod 'AzureCommunicationCalling', '~> 1.0.0-beta.12'
  pod 'AzureCommunicationChat', '~> 1.0.0-beta.11'
end

Installer les packages de chat

Exécutez pod install pour installer le package AzureCommunicationChat. Après l’installation, ouvrez le fichier .xcworkspace.

Configurer le framework d’application

Importez le package AzureCommunicationChat dans ContentView.swift en ajoutant l’extrait de code suivant :

import AzureCommunicationChat

Ajouter les contrôles d’interface utilisateur de Teams

Dans ContentView.swift, ajoutez l’extrait de code suivant sous les variables d’état existantes.

    // Chat
    @State var chatClient: ChatClient?
    @State var chatThreadClient: ChatThreadClient?
    @State var chatMessage: String = ""
    @State var meetingMessages: [MeetingMessage] = []

    let displayName: String = "<YOUR_DISPLAY_NAME_HERE>"

Remplacez <YOUR_DISPLAY_NAME_HERE> par le nom complet à utiliser dans la conversation.

Ensuite, nous devons modifier le formulaire Section pour afficher nos messages de conversation et ajouter des contrôles d’interface utilisateur pour la conversation.

Ajoutez l’extrait de code suivant au formulaire existant, juste après l’affichage de texte Text(recordingStatus), pour l’état de l’enregistrement.

VStack(alignment: .leading) {
    ForEach(meetingMessages, id: \.id) { message in
        let currentUser: Bool = (message.displayName == self.displayName)
        let foregroundColor = currentUser ? Color.white : Color.black
        let background = currentUser ? Color.blue : Color(.systemGray6)
        let alignment = currentUser ? HorizontalAlignment.trailing : HorizontalAlignment.leading
        VStack {
            Text(message.displayName).font(Font.system(size: 10))
            Text(message.content)
        }
        .alignmentGuide(.leading) { d in d[alignment] }
        .padding(10)
        .foregroundColor(foregroundColor)
        .background(background)
        .cornerRadius(10)
    }
}.frame(minWidth: 0, maxWidth: .infinity)
TextField("Enter your message...", text: $chatMessage)
Button(action: sendMessage) {
    Text("Send Message")
}.disabled(chatThreadClient == nil)

Ensuite, remplacez le titre par Chat Teams Quickstart. Modifiez la ligne suivante avec ce titre.

.navigationBarTitle("Chat Teams Quickstart")

Activer les contrôles d’interface utilisateur de Teams

Initialiser le ChatClient

Instanciez le ChatClient et activez les notifications en temps réel. Nous utilisons des notifications en temps réel pour la réception des messages de conversation.

Dans NavigationView.onAppear, ajoutez l’extrait de code ci-dessous, après le code existant qui initialise le CallAgent.

// Initialize the ChatClient
do {
    let endpoint = "COMMUNICATION_SERVICES_RESOURCE_ENDPOINT_HERE>"
    let credential = try CommunicationTokenCredential(token: "<USER_ACCESS_TOKEN_HERE>")

    self.chatClient = try ChatClient(
        endpoint: endpoint,
        credential: credential,
        withOptions: AzureCommunicationChatClientOptions()
    )

    self.message = "ChatClient successfully created"

    // Start real-time notifications
    self.chatClient?.startRealTimeNotifications() { result in
        switch result {
        case .success:
            print("Real-time notifications started")
            // Receive chat messages
            self.chatClient?.register(event: ChatEventId.chatMessageReceived, handler: receiveMessage)
        case .failure:
            print("Failed to start real-time notifications")
            self.message = "Failed to enable chat notifications"
        }
    }
} catch {
    print("Unable to create ChatClient")
    self.message = "Please enter a valid endpoint and Chat token in source code"
    return
}

Remplacez <COMMUNICATION_SERVICES_RESOURCE_ENDPOINT_HERE> par le point de terminaison de votre ressource Communication Services. Remplacez <USER_ACCESS_TOKEN_HERE> par un jeton d’accès qui a l’étendue de conversation.

En découvrir plus sur les jetons d’accès utilisateur : Jeton d’accès utilisateur

Initialiser le ChatThreadClient

Dans la fonction joinTeamsMeeting() existante, nous allons initialiser le ChatThreadClient après que l’utilisateur a rejoint la réunion.

Dans le gestionnaire d’achèvement de l’appel à self.callAgent?.join(), ajouter le code sous le commentaire // Initialize the ChatThreadClient. Le code complet est indiqué ci-dessous.

self.callAgent?.join(with: teamsMeetingLinkLocator, joinCallOptions: joinCallOptions) { (call, error) in
    if (error == nil) {
        self.call = call
        self.callObserver = CallObserver(self)
        self.call!.delegate = self.callObserver
        self.message = "Teams meeting joined successfully"

        // Initialize the ChatThreadClient
        do {
            guard let threadId = getThreadId(from: self.meetingLink) else {
                self.message = "Failed to join meeting chat"
                return
            }
            self.chatThreadClient = try chatClient?.createClient(forThread: threadId)
            self.message = "Joined meeting chat successfully"
        } catch {
            print("Failed to create ChatThreadClient")
            self.message = "Failed to join meeting chat"
            return
        }
    } else {
        print("Failed to join Teams meeting")
    }
}

Ajoutez la fonction d’assistance suivante au ContentView, qui est utilisée pour récupérer l’ID de thread de conversation à partir du lien de réunion de l’équipe.

func getThreadId(from meetingLink: String) -> String? {
    if let range = self.meetingLink.range(of: "meetup-join/") {
        let thread = self.meetingLink[range.upperBound...]
        if let endRange = thread.range(of: "/")?.lowerBound {
            return String(thread.prefix(upTo: endRange))
        }
    }
    return nil
}

Activer l’envoi des messages

Ajoutez la fonction sendMessage() à ContentView. Cette fonction utilise ChatThreadClient pour envoyer les messages de l’utilisateur.

func sendMessage() {
    let message = SendChatMessageRequest(
        content: self.chatMessage,
        senderDisplayName: self.displayName
    )
    
    self.chatThreadClient?.send(message: message) { result, _ in
        switch result {
        case .success:
            print("Chat message sent")
        case .failure:
            print("Failed to send chat message")
        }

        self.chatMessage = ""
    }
}

Activer la réception des messages

Pour recevoir des messages, nous devons implémenter le gestionnaire des événements ChatMessageReceived. Quand de nouveaux messages sont envoyés au thread, ce gestionnaire ajoute les messages à la variable meetingMessages afin qu’ils puissent être affichés dans l’interface utilisateur.

Tout d’abord, ajoutez le struct suivant à ContentView.swift. L’interface utilisateur utilise les données du struct pour afficher les messages de conversation.

struct MeetingMessage {
    let id: String
    let content: String
    let displayName: String
}

Ensuite, ajoutez la fonction receiveMessage() à ContentView. Il s’agit du gestionnaire qui est appelé chaque fois qu’un événement ChatMessageReceived se produit.

func receiveMessage(response: Any, eventId: ChatEventId) {
    let chatEvent: ChatMessageReceivedEvent = response as! ChatMessageReceivedEvent

    let displayName: String = chatEvent.senderDisplayName ?? "Unknown User"
    let content: String = chatEvent.message.replacingOccurrences(of: "<[^>]+>", with: "", options: String.CompareOptions.regularExpression)

    self.meetingMessages.append(
        MeetingMessage(
            id: chatEvent.id,
            content: content,
            displayName: displayName
        )
    )
}

Quitter la conversation

Quand l’utilisateur quitte la réunion de l’équipe, nous effaçons la conversation de l’interface utilisateur. Le code complet est indiqué ci-dessous.

func leaveMeeting() {
    if let call = call {
        call.hangUp(options: nil, completionHandler: { (error) in
            if error == nil {
                self.message = "Leaving Teams meeting was successful"
                // Clear the chat
                self.meetingMessages.removeAll()
            } else {
                self.message = "Leaving Teams meeting failed"
            }
        })
    } else {
        self.message = "No active call to hanup"
    }
}

Obtenir le fil de conversation d’une réunion Teams pour un utilisateur Communication Services

Il est possible de récupérer les détails de la réunion Teams à l’aide d’API Graph décrites dans la Documentation Graph. Le kit SDK Communication Services Calling accepte un lien de réunion Teams complet ou un ID de réunion. Ces informations sont retournées dans le cadre de la ressource onlineMeeting, accessible sous la propriété joinWebUrl

Avec les API Graph, vous pouvez également obtenir la valeur threadID. La réponse a un objet chatInfo qui contient le threadID.

Vous pouvez également récupérer les informations de réunion et l’ID de thread requis à partir de l’URL Rejoindre la réunion disponible dans l’invitation à la réunion Teams proprement dite. Un lien de réunion Teams ressemble à ceci : https://teams.microsoft.com/l/meetup-join/meeting_chat_thread_id/1606337455313?context=some_context_here. Le threadID est là où le meeting_chat_thread_id se trouve dans le lien. Assurez-vous que le meeting_chat_thread_id n’est pas échappé avant de l’utiliser. Il doit être au format suivant : 19:meeting_ZWRhZDY4ZGUtYmRlNS00OWZaLTlkZTgtZWRiYjIxOWI2NTQ4@thread.v2

Exécuter le code

Exécutez l'application.

Pour rejoindre la réunion Teams, saisissez le lien de réunion de votre équipe dans l’interface utilisateur.

Une fois que vous avez rejoint la réunion de l’équipe, vous devez autoriser l’utilisateur à participer à la réunion dans le client de votre équipe. Une fois que l’utilisateur est admis et a rejoint la conversation, vous pouvez envoyer et recevoir des messages.

Capture d’écran de l’application iOS terminée.

Notes

Certaines fonctionnalités ne sont actuellement pas prises en charge pour les scénarios d’interopérabilité avec Teams. Pour en savoir plus sur les fonctionnalités prises en charge, consultez Fonctionnalités de réunion Teams pour les utilisateurs Teams externes.

Dans ce démarrage rapide, vous allez découvrir comment rejoindre une conversation dans une réunion Teams à l’aide du Kit de développement logiciel (SDK) Conversation Azure Communication Services pour Android.

Prérequis

Activer l’interopérabilité de Teams

Un utilisateur Communication Services qui rejoint une réunion Teams en tant qu’utilisateur invité ne peut accéder à la conversation de la réunion qu’après avoir rejoint l’appel de réunion Teams. Consultez la documentation Interopérabilité de Teams pour savoir comment ajouter un utilisateur Communication Services à un appel de réunion Teams.

Vous devez être membre de l’organisation propriétaire des deux entités pour pouvoir utiliser cette fonctionnalité.

Participation à la conversation d’une réunion

Une fois l’interopérabilité de Teams activée, un utilisateur de Communication Services peut rejoindre l’appel Teams en tant qu’utilisateur externe à l’aide du kit de développement logiciel (SDK) Calling. En rejoignant l’appel, il est également ajouté comme participant à la conversation de la réunion, où il peut échanger des messages avec d’autres utilisateurs lors de l’appel. L’utilisateur n’aura pas accès aux messages de conversation qui ont été envoyés avant qu’il ne rejoigne l’appel. Pour participer à la réunion et commencer à converser, vous pouvez suivre les étapes suivantes.

Ajouter la conversation à l’application d’appel Teams

Dans votre fichier build.gradle au niveau du module, ajoutez la dépendance au kit de développement logiciel (SDK) de conversation.

Important

Problème connu : lors de l’utilisation conjointe du chat Android et du Kit de développement logiciel (SDK) d’appel dans la même application, la fonctionnalité de notifications en temps réel du SDK de conversation ne fonctionne pas. Vous obtiendrez un problème de résolution de dépendance. Pendant que nous travaillons sur une solution, vous pouvez désactiver la fonctionnalité de notifications en temps réel en ajoutant les exclusions suivantes à la dépendance du Kit de développement logiciel (SDK) de conversation dans le fichier build.gradle de l’application :

implementation ("com.azure.android:azure-communication-chat:1.0.0") {
    exclude group: 'com.microsoft', module: 'trouter-client-android'
}

Ajouter la disposition de l’interface utilisateur Teams

Remplacez le code dans activity_main.xml par l’extrait de code suivant. Il ajoute des entrées pour l’ID de thread et pour l’envoi de messages, un bouton pour envoyer le message saisi et une disposition de base pour le chat.

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/teams_meeting_thread_id"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="20dp"
        android:layout_marginTop="128dp"
        android:ems="10"
        android:hint="Meeting Thread Id"
        android:inputType="textUri"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/teams_meeting_link"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="20dp"
        android:layout_marginTop="64dp"
        android:ems="10"
        android:hint="Teams meeting link"
        android:inputType="textUri"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <LinearLayout
        android:id="@+id/button_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:gravity="center"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/teams_meeting_thread_id">

        <Button
            android:id="@+id/join_meeting_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Join Meeting" />

        <Button
            android:id="@+id/hangup_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hangup" />

    </LinearLayout>

    <TextView
        android:id="@+id/call_status_bar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="40dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/recording_status_bar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="20dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <ScrollView
        android:id="@+id/chat_box"
        android:layout_width="374dp"
        android:layout_height="294dp"
        android:layout_marginTop="40dp"
        android:layout_marginBottom="20dp"
        app:layout_constraintBottom_toTopOf="@+id/send_message_button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/button_layout"
        android:orientation="vertical"
        android:gravity="bottom"
        android:layout_gravity="bottom"
        android:fillViewport="true">

        <LinearLayout
            android:id="@+id/chat_box_layout"
            android:orientation="vertical"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:gravity="bottom"
            android:layout_gravity="top"
            android:layout_alignParentBottom="true"/>
    </ScrollView>

    <EditText
        android:id="@+id/message_body"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginHorizontal="20dp"
        android:layout_marginTop="588dp"
        android:ems="10"
        android:inputType="textUri"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="Type your message here..."
        tools:visibility="invisible" />

    <Button
        android:id="@+id/send_message_button"
        android:layout_width="138dp"
        android:layout_height="45dp"
        android:layout_marginStart="133dp"
        android:layout_marginTop="48dp"
        android:layout_marginEnd="133dp"
        android:text="Send Message"
        android:visibility="invisible"
        app:layout_constraintBottom_toTopOf="@+id/recording_status_bar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.428"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/chat_box" />

</androidx.constraintlayout.widget.ConstraintLayout>

Activer les contrôles d’interface utilisateur de Teams

Importer des packages et définir des variables d’état

Ajoutez les importations suivantes au contenu de MainActivity.java :

import android.graphics.Typeface;
import android.graphics.Color;
import android.text.Html;
import android.os.Handler;
import android.view.Gravity;
import android.view.View;
import android.widget.LinearLayout;
import java.util.Collections;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import com.azure.android.communication.chat.ChatThreadAsyncClient;
import com.azure.android.communication.chat.ChatThreadClientBuilder;
import com.azure.android.communication.chat.models.ChatMessage;
import com.azure.android.communication.chat.models.ChatMessageType;
import com.azure.android.communication.chat.models.ChatParticipant;
import com.azure.android.communication.chat.models.ListChatMessagesOptions;
import com.azure.android.communication.chat.models.SendChatMessageOptions;
import com.azure.android.communication.common.CommunicationIdentifier;
import com.azure.android.communication.common.CommunicationUserIdentifier;
import com.azure.android.core.rest.util.paging.PagedAsyncStream;
import com.azure.android.core.util.AsyncStreamHandler;

Ajoutez les variables suivantes à la classe MainActivity :

    // InitiatorId is used to differentiate incoming messages from outgoing messages
    private static final String InitiatorId = "<USER_ID>";
    private static final String ResourceUrl = "<COMMUNICATION_SERVICES_RESOURCE_ENDPOINT>";
    private String threadId;
    private ChatThreadAsyncClient chatThreadAsyncClient;
    
    // The list of ids corresponsding to messages which have already been processed
    ArrayList<String> chatMessages = new ArrayList<>();

Remplacez <USER_ID> par l’ID de l’utilisateur qui lance la conversation. Remplacez <COMMUNICATION_SERVICES_RESOURCE_ENDPOINT> par le point de terminaison de votre ressource Communication Services.

Initialiser le ChatThreadClient

Après avoir rejoint la réunion, instanciez le ChatThreadClient et rendez visibles les composants du chat.

Mettez à jour la fin de la méthode MainActivity.joinTeamsMeeting() avec le code ci-dessous :

    private void joinTeamsMeeting() {
        ...
        EditText threadIdView = findViewById(R.id.teams_meeting_thread_id);
        threadId = threadIdView.getText().toString();
        // Initialize Chat Thread Client
        chatThreadAsyncClient = new ChatThreadClientBuilder()
                .endpoint(ResourceUrl)
                .credential(new CommunicationTokenCredential(UserToken))
                .chatThreadId(threadId)
                .buildAsyncClient();
        Button sendMessageButton = findViewById(R.id.send_message_button);
        EditText messageBody = findViewById(R.id.message_body);
        // Register the method for sending messages and toggle the visibility of chat components
        sendMessageButton.setOnClickListener(l -> sendMessage());
        sendMessageButton.setVisibility(View.VISIBLE);
        messageBody.setVisibility(View.VISIBLE);
        
        // Start the polling for chat messages immediately
        handler.post(runnable);
    }

Activer l’envoi des messages

Ajoutez la méthode sendMessage() à MainActivity. Elle utilise le ChatThreadClient pour envoyer des messages au nom de l’utilisateur.

    private void sendMessage() {
        // Retrieve the typed message content
        EditText messageBody = findViewById(R.id.message_body);
        // Set request options and send message
        SendChatMessageOptions options = new SendChatMessageOptions();
        options.setContent(messageBody.getText().toString());
        options.setSenderDisplayName("Test User");
        chatThreadAsyncClient.sendMessage(options);
        // Clear the text box
        messageBody.setText("");
    }

Activer l’interrogation des messages et leur rendu dans l’application

Important

Problème connu : Dans la mesure où la fonctionnalité de notifications en temps réel du Kit de développement logiciel (SDK) de conversation ne fonctionne pas avec le SDK d’appel, nous devons interroger l’API GetMessages à des intervalles prédéfinis. Dans notre exemple, nous allons utiliser des intervalles de trois secondes.

Nous pouvons obtenir les données suivantes à partir de la liste des messages renvoyée par l’API GetMessages :

  • Les messages text et html sur la conversation depuis qu’elle a été rejointe
  • Les modifications apportées à la liste des participants à la conversation
  • Les mises à jour du sujet de la conversation

Ajoutez à la classe MainActivity un gestionnaire et une tâche exécutable qui seront exécutés à intervalles de trois secondes :

    private Handler handler = new Handler();
    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            try {
                retrieveMessages();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // Repeat every 3 seconds
            handler.postDelayed(runnable, 3000);
        }
    };

Notez que la tâche a déjà été démarrée à la fin de la méthode MainActivity.joinTeamsMeeting() mise à jour dans l’étape d’initialisation.

Enfin, nous devons ajouter la méthode permettant d’interroger tous les messages accessibles sur la conversation, de les analyser par type de message et d’afficher les messages html et text :

    private void retrieveMessages() throws InterruptedException {
        // Initialize the list of messages not yet processed
        ArrayList<ChatMessage> newChatMessages = new ArrayList<>();
        
        // Retrieve all messages accessible to the user
        PagedAsyncStream<ChatMessage> messagePagedAsyncStream
                = this.chatThreadAsyncClient.listMessages(new ListChatMessagesOptions(), null);
        // Set up a lock to wait until all returned messages have been inspected
        CountDownLatch latch = new CountDownLatch(1);
        // Traverse the returned messages
        messagePagedAsyncStream.forEach(new AsyncStreamHandler<ChatMessage>() {
            @Override
            public void onNext(ChatMessage message) {
                // Messages that should be displayed in the chat
                if ((message.getType().equals(ChatMessageType.TEXT)
                    || message.getType().equals(ChatMessageType.HTML))
                    && !chatMessages.contains(message.getId())) {
                    newChatMessages.add(message);
                    chatMessages.add(message.getId());
                }
                if (message.getType().equals(ChatMessageType.PARTICIPANT_ADDED)) {
                    // Handle participants added to chat operation
                    List<ChatParticipant> participantsAdded = message.getContent().getParticipants();
                    CommunicationIdentifier participantsAddedBy = message.getContent().getInitiatorCommunicationIdentifier();
                }
                if (message.getType().equals(ChatMessageType.PARTICIPANT_REMOVED)) {
                    // Handle participants removed from chat operation
                    List<ChatParticipant> participantsRemoved = message.getContent().getParticipants();
                    CommunicationIdentifier participantsRemovedBy = message.getContent().getInitiatorCommunicationIdentifier();
                }
                if (message.getType().equals(ChatMessageType.TOPIC_UPDATED)) {
                    // Handle topic updated
                    String newTopic = message.getContent().getTopic();
                    CommunicationIdentifier topicUpdatedBy = message.getContent().getInitiatorCommunicationIdentifier();
                }
            }
            @Override
            public void onError(Throwable throwable) {
                latch.countDown();
            }
            @Override
            public void onComplete() {
                latch.countDown();
            }
        });
        // Wait until the operation completes
        latch.await(1, TimeUnit.MINUTES);
        // Returned messages should be ordered by the createdOn field to be guaranteed a proper chronological order
        // For the purpose of this demo we will just reverse the list of returned messages
        Collections.reverse(newChatMessages);
        for (ChatMessage chatMessage : newChatMessages)
        {
            LinearLayout chatBoxLayout = findViewById(R.id.chat_box_layout);
            // For the purpose of this demo UI, we don't need to use HTML formatting for displaying messages
            // The Teams client always sends html messages in meeting chats 
            String message = Html.fromHtml(chatMessage.getContent().getMessage(), Html.FROM_HTML_MODE_LEGACY).toString().trim();
            TextView messageView = new TextView(this);
            messageView.setText(message);
            // Compare with sender identifier and align LEFT/RIGHT accordingly
            // Azure Communication Services users are of type CommunicationUserIdentifier
            CommunicationIdentifier senderId = chatMessage.getSenderCommunicationIdentifier();
            if (senderId instanceof CommunicationUserIdentifier
                && InitiatorId.equals(((CommunicationUserIdentifier) senderId).getId())) {
                messageView.setTextColor(Color.GREEN);
                messageView.setGravity(Gravity.RIGHT);
            } else {
                messageView.setTextColor(Color.BLUE);
                messageView.setGravity(Gravity.LEFT);
            }
            // Note: messages with the deletedOn property set to a timestamp, should be marked as deleted
            // Note: messages with the editedOn property set to a timestamp, should be marked as edited
            messageView.setTypeface(Typeface.SANS_SERIF, Typeface.BOLD);
            chatBoxLayout.addView(messageView);
        }
    }

Les noms d’affichage des participants aux conversations ne sont pas définis par le client Teams. La valeur Null est renvoyée dans l’API pour la liste des participants, dans l’événement participantsAdded et dans l’événement participantsRemoved. Les noms d’affichage des participants à la conversation peuvent être récupérés dans le champ remoteParticipants de l’objet call.

Obtenir le fil de conversation d’une réunion Teams pour un utilisateur Communication Services

Il est possible de récupérer les détails de la réunion Teams à l’aide d’API Graph décrites dans la Documentation Graph. Le kit SDK Communication Services Calling accepte un lien de réunion Teams complet ou un ID de réunion. Ces informations sont retournées dans le cadre de la ressource onlineMeeting, accessible sous la propriété joinWebUrl

Avec les API Graph, vous pouvez également obtenir la valeur threadID. La réponse a un objet chatInfo qui contient le threadID.

Vous pouvez également récupérer les informations de réunion et l’ID de thread requis à partir de l’URL Rejoindre la réunion disponible dans l’invitation à la réunion Teams proprement dite. Un lien de réunion Teams ressemble à ceci : https://teams.microsoft.com/l/meetup-join/meeting_chat_thread_id/1606337455313?context=some_context_here. Le threadId est là où le meeting_chat_thread_id se trouve dans le lien. Assurez-vous que le meeting_chat_thread_id n’est pas échappé avant de l’utiliser. Il doit être au format suivant : 19:meeting_ZWRhZDY4ZGUtYmRlNS00OWZaLTlkZTgtZWRiYjIxOWI2NTQ4@thread.v2

Exécuter le code

L’application peut maintenant être lancée à l’aide du bouton « Run App » de la barre d’outils (Maj+F10).

Pour rejoindre la conversation et la réunion Teams, entrez le lien de réunion et l’ID de conversation de votre équipe dans l’interface utilisateur.

Une fois que vous avez rejoint la réunion de l’équipe, vous devez autoriser l’utilisateur à participer à la réunion dans le client de votre équipe. Une fois que l’utilisateur est admis et a rejoint la conversation, vous pouvez envoyer et recevoir des messages.

Capture d’écran de l’application Android terminée.

Notes

Certaines fonctionnalités ne sont actuellement pas prises en charge pour les scénarios d’interopérabilité avec Teams. Pour en savoir plus sur les fonctionnalités prises en charge, consultez Fonctionnalités de réunion Teams pour les utilisateurs Teams externes.

Dans ce démarrage rapide, vous allez découvrir comment rejoindre une conversation dans une réunion Teams à l’aide du Kit de développement logiciel (SDK) Conversation d’Azure Communication Services pour C#.

Exemple de code

Retrouvez le code ce démarrage rapide sur GitHub.

Prérequis

Participation à la conversation d’une réunion

Un utilisateur de Communication Services peut participer à une réunion Teams en tant qu’utilisateur anonyme à l’aide du kit de développement logiciel (SDK) Appel. En rejoignant la réunion, il est également ajouté comme participant à la conversation de la réunion, où il peut échanger des messages avec d’autres utilisateurs lors de la réunion. L’utilisateur n’aura pas accès aux messages de la conversation envoyés avant qu’il rejoigne la réunion. Il ne pourra pas non plus envoyer ni recevoir des messages une fois la réunion terminée. Pour participer à la réunion et commencer à converser, vous pouvez suivre les étapes suivantes.

Exécuter le code

Vous pouvez générer et exécuter le code sur Visual Studio. Notez que les plateformes de solution que nous prenons en charge sont x64, x86 et ARM64.

  1. Ouvrez une instance de PowerShell, de Terminal Windows, une invite de commandes ou équivalent, puis accédez au répertoire dans lequel vous souhaitez cloner l’exemple.
  2. git clone https://github.com/Azure-Samples/Communication-Services-dotnet-quickstarts.git
  3. Ouvrez le projet ChatTeamsInteropQuickStart/ChatTeamsInteropQuickStart.csproj dans Visual Studio.
  4. Installez les versions de packages NuGet suivantes (ou ultérieures) :
Install-Package Azure.Communication.Calling -Version 1.0.0-beta.29
Install-Package Azure.Communication.Chat -Version 1.1.0
Install-Package Azure.Communication.Common -Version 1.0.1
Install-Package Azure.Communication.Identity -Version 1.0.1

  1. À l’aide de la ressource Communication Services obtenue dans les prérequis, ajoutez la chaîne de connexion au fichier ChatTeamsInteropQuickStart/MainPage.xaml.cs.
//Azure Communication Services resource connection string i.e = "endpoint=https://your-resource.communication.azure.net/;accesskey=your-access-key";
private const string connectionString_ = "";

Important

  • Sélectionnez la plateforme appropriée dans la liste déroulante « Plateformes de solution » dans Visual Studio avant d’exécuter le code. C’est-à-dire x64.
  • assurez-vous que le « mode développeur » est activé dans Windows 10 (Paramètres du développeur)

Les étapes suivantes ne fonctionnent pas si cette configuration n’est pas correcte.

  1. Appuyez sur la touche F5 pour démarrer le projet en mode débogage.
  2. Collez un lien de réunion Teams valide dans la zone « Lien de réunion Teams » (voir la section suivante).
  3. Appuyez sur « Rejoindre la réunion Teams » pour commencer la conversation instantanée.

Important

Une fois que le Kit de développement logiciel (SDK) appelant a établi la connexion avec la réunion Teams (voir Communication Services appelant une application Windows ), les fonctions clés pour gérer les opérations de conversation sont : StartPollingForChatMessages et SendMessageButton_Click. Les deux extraits de code se trouvent sous ChatTeamsInteropQuickStart\MainPage.xaml.cs.

        /// <summary>
        /// Background task that keeps polling for chat messages while the call connection is stablished
        /// </summary>
        private async Task StartPollingForChatMessages()
        {
            CommunicationTokenCredential communicationTokenCredential = new(user_token_);
            chatClient_ = new ChatClient(EndPointFromConnectionString(), communicationTokenCredential);
            await Task.Run(async () =>
            {
                keepPolling_ = true;

                ChatThreadClient chatThreadClient = chatClient_.GetChatThreadClient(thread_Id_);
                int previousTextMessages = 0;
                while (keepPolling_)
                {
                    try
                    {
                        CommunicationUserIdentifier currentUser = new(user_Id_);
                        AsyncPageable<ChatMessage> allMessages = chatThreadClient.GetMessagesAsync();
                        SortedDictionary<long, string> messageList = new();
                        int textMessages = 0;
                        string userPrefix;
                        await foreach (ChatMessage message in allMessages)
                        {
                            if (message.Type == ChatMessageType.Html || message.Type == ChatMessageType.Text)
                            {
                                textMessages++;
                                userPrefix = message.Sender.Equals(currentUser) ? "[you]:" : "";
                                messageList.Add(long.Parse(message.SequenceId), $"{userPrefix}{StripHtml(message.Content.Message)}");
                            }
                        }

                        //Update UI just when there are new messages
                        if (textMessages > previousTextMessages)
                        {
                            previousTextMessages = textMessages;
                            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                            {
                                TxtChat.Text = string.Join(Environment.NewLine, messageList.Values.ToList());
                            });

                        }
                        if (!keepPolling_)
                        {
                            return;
                        }

                        await SetInCallState(true);
                        await Task.Delay(3000);
                    }
                    catch (Exception e)
                    {
                        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                        {
                            _ = new MessageDialog($"An error occurred while fetching messages in PollingChatMessagesAsync(). The application will shutdown. Details : {e.Message}").ShowAsync();
                            throw e;
                        });
                        await SetInCallState(false);
                    }
                }
            });
        }
        private async void SendMessageButton_Click(object sender, RoutedEventArgs e)
        {
            SendMessageButton.IsEnabled = false;
            ChatThreadClient chatThreadClient = chatClient_.GetChatThreadClient(thread_Id_);
            _ = await chatThreadClient.SendMessageAsync(TxtMessage.Text);
            
            TxtMessage.Text = "";
            SendMessageButton.IsEnabled = true;
        }

Il est possible de récupérer le lien de réunion Teams à l’aide d’API Graph décrites dans la documentation de Graph. Ce lien est retourné dans le cadre de la ressource onlineMeeting, accessible sous la propriété joinWebUrl.

Vous pouvez également récupérer le lien de réunion requis à partir de l’URL Rejoindre la réunion disponible dans l’invitation à la réunion Teams elle-même. Un lien de réunion Teams ressemble à ceci : https://teams.microsoft.com/l/meetup-join/meeting_chat_thread_id/1606337455313?context=some_context_here.

Capture d’écran de l’application csharp terminée.

Notes

Certaines fonctionnalités ne sont actuellement pas prises en charge pour les scénarios d’interopérabilité avec Teams. Pour en savoir plus sur les fonctionnalités prises en charge, consultez Fonctionnalités de réunion Teams pour les utilisateurs Teams externes.

Nettoyer les ressources

Si vous voulez nettoyer et supprimer un abonnement Communication Services, vous pouvez supprimer la ressource ou le groupe de ressources. La suppression du groupe de ressources efface également les autres ressources qui y sont associées. Apprenez-en davantage sur le nettoyage des ressources.

Étapes suivantes

Pour plus d’informations, consultez les articles suivants :