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
- Un déploiement de Teams
- Application de conversation opérationnelle.
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 :
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
- Un déploiement de Teams
- Une application d’appel opérationnelle.
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.
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
- Un déploiement de Teams
- Une application d’appel opérationnelle.
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
ethtml
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.
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
- Un déploiement de Teams
- Compte Azure avec un abonnement actif. Créez un compte gratuitement.
- Installez Visual Studio 2019 avec la charge de travail de développement pour la plateforme Windows universelle.
- Une ressource Communication Services déployée. Créez une ressource Communication Services.
- Un lien de réunion Teams.
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
.
- 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.
git clone https://github.com/Azure-Samples/Communication-Services-dotnet-quickstarts.git
- Ouvrez le projet ChatTeamsInteropQuickStart/ChatTeamsInteropQuickStart.csproj dans Visual Studio.
- 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
- À 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.
- Appuyez sur la touche F5 pour démarrer le projet en mode débogage.
- Collez un lien de réunion Teams valide dans la zone « Lien de réunion Teams » (voir la section suivante).
- 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;
}
Obtenir un lien de réunion Teams
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
.
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 :
- Consultez notre exemple de bannière de conversation.
- Apprenez-en davantage sur le fonctionnement de la conversation.