Guida introduttiva: Aggiungere l'app di chat a una riunione di Teams
Iniziare a usare Servizi di comunicazione di Azure connettendo la soluzione di chat a Microsoft Teams.
In questa guida introduttiva si apprenderà come chattare in una riunione di Teams usando Servizi di comunicazione di Azure Chat SDK per JavaScript.
Codice di esempio
Trovare il codice finalizzato per questa guida introduttiva in GitHub.
Prerequisiti
- Una distribuzione di Teams.
- Un'app di chat funzionante.
Partecipare alla chat della riunione
Un utente di Servizi di comunicazione può partecipare a una riunione di Teams come utente anonimo usando l'SDK chiamante. Partecipare alla riunione li aggiunge anche come partecipante alla chat della riunione, in cui possono inviare e ricevere messaggi con altri utenti nella riunione. L'utente non avrà accesso ai messaggi di chat inviati prima della riunione e non sarà in grado di inviare o ricevere messaggi dopo la fine della riunione. Per partecipare alla riunione e avviare la chat, è possibile seguire i passaggi successivi.
Creare una nuova applicazione Node.js
Aprire la finestra del terminale o di comando, creare una nuova directory per l'app e passare a tale directory.
mkdir chat-interop-quickstart && cd chat-interop-quickstart
Eseguire npm init -y
per creare un file package.json con le impostazioni predefinite.
npm init -y
Installare i pacchetti di chat
Usare il npm install
comando per installare gli SDK di Servizi di comunicazione necessari per JavaScript.
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'opzione --save
elenca la libreria come dipendenza nel file package.json.
Configurare il framework dell'app
Questa guida introduttiva usa Webpack per aggregare gli asset dell'applicazione. Eseguire il comando seguente per installare i pacchetti webpack, webpack-cli e webpack-dev-server npm e elencarli come dipendenze di sviluppo nel package.json:
npm install webpack@5.89.0 webpack-cli@5.1.4 webpack-dev-server@4.15.1 --save-dev
Creare un file index.html nella directory radice del progetto. Questo file viene usato per configurare un layout di base che consentirà all'utente di partecipare a una riunione e di avviare la chat.
Aggiungere i controlli dell'interfaccia utente di Teams
Sostituire il codice in index.html con il frammento di codice seguente. La casella di testo nella parte superiore della pagina verrà usata per immettere il contesto della riunione di Teams. Il pulsante "Partecipa alla riunione di Teams" viene usato per partecipare alla riunione specificata. Viene visualizzato un popup di chat nella parte inferiore della pagina. Può essere usato per inviare messaggi nel thread della riunione e visualizza in tempo reale tutti i messaggi inviati sul thread mentre l'utente di Servizi di comunicazione è membro.
<!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>
Abilitare i controlli dell'interfaccia utente di Teams
Sostituire il contenuto del file client.js con il frammento di codice seguente.
All'interno del frammento di codice sostituire
SECRET_CONNECTION_STRING
con la stringa di connessione del servizio di comunicazione
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].replace("endpoint=", "");
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}`);
});
I nomi visualizzati dei partecipanti al thread di chat non vengono impostati dal client teams. I nomi vengono restituiti come Null nell'API per elencare i partecipanti, nell'evento participantsAdded
e nell'evento participantsRemoved
. I nomi visualizzati dei partecipanti alla chat possono essere recuperati dal remoteParticipants
campo dell'oggetto call
. Quando si riceve una notifica relativa a una modifica del roster, è possibile usare questo codice per recuperare il nome dell'utente aggiunto o rimosso:
var displayName = call.remoteParticipants.find(p => p.identifier.communicationUserId == '<REMOTE_USER_ID>').displayName;
Eseguire il codice
Gli utenti di Webpack possono usare webpack-dev-server
per compilare ed eseguire l'app. Eseguire il comando seguente per aggregare l'host dell'applicazione in un server Web locale:
npx webpack-dev-server --entry ./client.js --output bundle.js --debug --devtool inline-source-map
Aprire il browser e passare a http://localhost:8080/
. Verrà visualizzata l'app avviata come illustrato nello screenshot seguente:
Inserire il collegamento alla riunione di Teams nella casella di testo. Premere Partecipa alla riunione di Teams per partecipare alla riunione di Teams. Dopo che l'utente di Servizi di comunicazione è stato ammesso alla riunione, è possibile chattare dall'interno dell'applicazione servizi di comunicazione. Passare alla casella nella parte inferiore della pagina per iniziare a chattare. Per semplicità, l'applicazione mostra solo gli ultimi due messaggi nella chat.
Nota
Alcune funzionalità non sono attualmente supportate per gli scenari di interoperabilità con Teams. Altre informazioni sulle funzionalità supportate, vedere Funzionalità delle riunioni di Teams per gli utenti esterni di Teams
In questa guida introduttiva si apprenderà come chattare in una riunione di Teams usando Servizi di comunicazione di Azure Chat SDK per iOS.
Prerequisiti
- Una distribuzione di Teams.
- Un'app chiamante funzionante.
Partecipare alla chat della riunione
Un utente di Servizi di comunicazione può partecipare a una riunione di Teams come utente anonimo usando l'SDK chiamante. Partecipare alla riunione li aggiunge anche come partecipante alla chat della riunione, in cui possono inviare e ricevere messaggi con altri utenti nella riunione. L'utente non avrà accesso ai messaggi di chat inviati prima della riunione e non sarà in grado di inviare o ricevere messaggi dopo la fine della riunione. Per partecipare alla riunione e avviare la chat, è possibile seguire i passaggi successivi.
Aggiungere chat all'app per chiamate di Teams
Podfile.lock
Eliminare e sostituire il contenuto del Podfile con quanto segue:
platform :ios, '13.0'
use_frameworks!
target 'AzureCommunicationCallingSample' do
pod 'AzureCommunicationCalling', '~> 1.0.0-beta.12'
pod 'AzureCommunicationChat', '~> 1.0.0-beta.11'
end
Installare i pacchetti di chat
Eseguire pod install
per installare il pacchetto AzureCommunicationChat.
Dopo l'installazione, aprire il .xcworkspace
file.
Configurare il framework dell'app
Importare il pacchetto AzureCommunicationChat in ContentView.swift
aggiungendo il frammento di codice seguente:
import AzureCommunicationChat
Aggiungere i controlli dell'interfaccia utente di Teams
Aggiungere ContentView.swift
il frammento di codice seguente sotto le variabili di stato esistenti.
// Chat
@State var chatClient: ChatClient?
@State var chatThreadClient: ChatThreadClient?
@State var chatMessage: String = ""
@State var meetingMessages: [MeetingMessage] = []
let displayName: String = "<YOUR_DISPLAY_NAME_HERE>"
Sostituire <YOUR_DISPLAY_NAME_HERE>
con il nome visualizzato che si vuole usare nella chat.
Successivamente, modifichiamo il modulo Section
per visualizzare i messaggi di chat e aggiungere controlli dell'interfaccia utente per la chat.
Aggiungere il frammento di codice seguente al modulo esistente. Subito dopo la visualizzazione Text(recordingStatus)
Testo per lo stato di registrazione.
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)
Modificare quindi il titolo in Chat Teams Quickstart
. Modificare la riga seguente con questo titolo.
.navigationBarTitle("Chat Teams Quickstart")
Abilitare i controlli dell'interfaccia utente di Teams
Inizializzare ChatClient
Creare un'istanza di ChatClient
e abilitare le notifiche in tempo reale. Si usano notifiche in tempo reale per ricevere messaggi di chat.
All'interno di NavigationView
.onAppear
aggiungere il frammento di codice seguente, dopo il codice esistente che inizializza .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
}
Sostituire <COMMUNICATION_SERVICES_RESOURCE_ENDPOINT_HERE>
con l'endpoint per la risorsa di Servizi di comunicazione.
Sostituire <USER_ACCESS_TOKEN_HERE>
con un token di accesso con ambito chat.
Altre informazioni sui token di accesso utente: Token di accesso utente
Inizializzare ChatThreadClient
All'interno della funzione esistente joinTeamsMeeting()
, verrà inizializzato ChatThreadClient
dopo che l'utente ha partecipato alla riunione.
All'interno del gestore di completamento per la chiamata a self.callAgent?.join()
aggiungere il codice sotto il commento // Initialize the ChatThreadClient
. Di seguito è riportato il codice completo.
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")
}
}
Aggiungere la funzione helper seguente a ContentView
, che viene usata per recuperare l'ID del thread chat dal collegamento alla riunione del team.
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
}
Abilitare l'invio di messaggi
Aggiungere la sendMessage()
funzione a ContentView
. Questa funzione usa per ChatThreadClient
inviare messaggi dall'utente.
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 = ""
}
}
Abilitare la ricezione di messaggi
Per ricevere messaggi, implementiamo il gestore per ChatMessageReceived
gli eventi. Quando vengono inviati nuovi messaggi al thread, questo gestore aggiunge i messaggi alla meetingMessages
variabile in modo che possano essere visualizzati nell'interfaccia utente.
Aggiungere prima di tutto lo struct seguente a ContentView.swift
. L'interfaccia utente usa i dati nello struct per visualizzare i messaggi di Chat.
struct MeetingMessage {
let id: String
let content: String
let displayName: String
}
Aggiungere quindi la receiveMessage()
funzione a ContentView
. Si tratta del gestore chiamato ogni volta che si verifica un ChatMessageReceived
evento.
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
)
)
}
Lasciare la chat
Quando l'utente lascia la riunione del team, la chat viene cancellata dall'interfaccia utente. Di seguito è riportato il codice completo.
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"
}
}
Ottenere un thread di chat della riunione di Teams per un utente di Servizi di comunicazione
I dettagli delle riunioni di Teams possono essere recuperati usando le API Graph, descritte in dettaglio nella documentazione di Graph. Communication Services Calling SDK accetta un collegamento completo alla riunione di Teams o un ID riunione. Vengono restituiti come parte della onlineMeeting
risorsa, accessibile nella joinWebUrl
proprietà
Con le API Graph è anche possibile ottenere .threadID
La risposta ha un chatInfo
oggetto che contiene l'oggetto threadID
.
È anche possibile ottenere le informazioni necessarie sulla riunione e l'ID del thread dall'URL della riunione di Partecipare all'invito alla riunione di Teams stesso.
Un collegamento a una riunione di Teams ha un aspetto simile al seguente: https://teams.microsoft.com/l/meetup-join/meeting_chat_thread_id/1606337455313?context=some_context_here
. threadID
è la posizione in cui meeting_chat_thread_id
si trova il collegamento. Assicurarsi che l'oggetto meeting_chat_thread_id
sia senza caratteri di escape prima dell'uso. Deve avere il formato seguente: 19:meeting_ZWRhZDY4ZGUtYmRlNS00OWZaLTlkZTgtZWRiYjIxOWI2NTQ4@thread.v2
Eseguire il codice
Eseguire l'applicazione.
Per partecipare alla riunione di Teams, immettere il collegamento alla riunione del team nell'interfaccia utente.
Dopo aver eseguito la riunione del team, è necessario ammettere l'utente alla riunione nel client del team. Dopo che l'utente è stato ammesso e ha partecipato alla chat, è possibile inviare e ricevere messaggi.
Nota
Alcune funzionalità non sono attualmente supportate per gli scenari di interoperabilità con Teams. Altre informazioni sulle funzionalità supportate, vedere Funzionalità delle riunioni di Teams per gli utenti esterni di Teams
In questa guida introduttiva si apprenderà come chattare in una riunione di Teams usando Servizi di comunicazione di Azure Chat SDK per Android.
Prerequisiti
- Una distribuzione di Teams.
- Un'app chiamante funzionante.
Abilitare l'interoperabilità di Teams
Un utente di Servizi di comunicazione che partecipa a una riunione di Teams come utente guest può accedere alla chat della riunione solo dopo essere stato aggiunto alla chiamata della riunione di Teams. Per informazioni su come aggiungere un utente di Servizi di comunicazione a una chiamata di riunione di Teams, vedere la documentazione sull'interoperabilità di Teams.
Per usare questa funzionalità, è necessario essere un membro dell'organizzazione proprietaria di entrambe le entità.
Partecipare alla chat della riunione
Dopo aver abilitato l'interoperabilità di Teams, un utente di Servizi di comunicazione può partecipare alla chiamata di Teams come utente esterno usando l'SDK chiamante. Partecipare alla chiamata li aggiunge anche come partecipante alla chat della riunione, in cui possono inviare e ricevere messaggi con altri utenti nella chiamata. L'utente non ha accesso ai messaggi di chat inviati prima di essere aggiunti alla chiamata. Per partecipare alla riunione e avviare la chat, è possibile seguire i passaggi successivi.
Aggiungere chat all'app per chiamate di Teams
Nel livello build.gradle
del modulo aggiungere la dipendenza dall'SDK della chat.
Importante
Problema noto: quando si usa Android Chat e Calling SDK insieme nella stessa applicazione, la funzionalità di notifiche in tempo reale di Chat SDK non funzionerà. Si otterrà un problema di risoluzione delle dipendenze. Mentre si sta lavorando a una soluzione, è possibile disattivare la funzionalità delle notifiche in tempo reale aggiungendo le esclusioni seguenti alla dipendenza di Chat SDK nel file dell'app build.gradle
:
implementation ("com.azure.android:azure-communication-chat:2.0.3") {
exclude group: 'com.microsoft', module: 'trouter-client-android'
}
Aggiungere il layout dell'interfaccia utente di Teams
Sostituire il codice in activity_main.xml con il frammento di codice seguente. Aggiunge input per l'ID thread e per l'invio di messaggi, un pulsante per l'invio del messaggio tipizzato e un layout di chat di base.
<?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>
Abilitare i controlli dell'interfaccia utente di Teams
Importare pacchetti e definire le variabili di stato
Nel contenuto di MainActivity.java
aggiungere le importazioni seguenti:
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 java.util.List;
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;
Per la MainActivity
classe aggiungere le variabili seguenti:
// 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<>();
Sostituire <USER_ID>
con l'ID dell'utente che avvia la chat.
Sostituire <COMMUNICATION_SERVICES_RESOURCE_ENDPOINT>
con l'endpoint per la risorsa di Servizi di comunicazione.
Inizializzare ChatThreadClient
Dopo aver eseguito la riunione, creare un'istanza di ChatThreadClient
e rendere visibili i componenti della chat.
Aggiornare la fine del MainActivity.joinTeamsMeeting()
metodo con il codice seguente:
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);
}
Abilitare l'invio di messaggi
Aggiungere il sendMessage()
metodo a MainActivity
. Usa per ChatThreadClient
inviare messaggi per conto dell'utente.
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("");
}
Abilitare il polling per i messaggi e eseguirne il rendering nell'applicazione
Importante
Problema noto: poiché la funzionalità di notifiche in tempo reale di Chat SDK non funziona con l'SDK chiamante, è necessario eseguire il polling dell'API GetMessages
a intervalli predefiniti. Nell'esempio verranno usati intervalli di 3 secondi.
È possibile ottenere i dati seguenti dall'elenco dei messaggi restituiti dall'API GetMessages
:
- Messaggi
text
ehtml
nel thread dopo l'aggiunta - Modifiche all'elenco di thread
- Aggiornamenti all'argomento thread
MainActivity
Alla classe aggiungere un gestore e un'attività eseguibile che verrà eseguita a intervalli di 3 secondi:
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);
}
};
Si noti che l'attività è già stata avviata alla fine del MainActivity.joinTeamsMeeting()
metodo aggiornato nel passaggio di inizializzazione.
Infine, si aggiunge il metodo per eseguire query su tutti i messaggi accessibili nel thread, analizzandoli in base al tipo di messaggio e visualizzandoli html
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);
}
}
I nomi visualizzati dei partecipanti al thread di chat non vengono impostati dal client teams. I nomi vengono restituiti come Null nell'API per elencare i partecipanti, nell'evento participantsAdded
e nell'evento participantsRemoved
. I nomi visualizzati dei partecipanti alla chat possono essere recuperati dal remoteParticipants
campo dell'oggetto call
.
Ottenere un thread di chat della riunione di Teams per un utente di Servizi di comunicazione
I dettagli delle riunioni di Teams possono essere recuperati usando le API Graph, descritte in dettaglio nella documentazione di Graph. Communication Services Calling SDK accetta un collegamento completo alla riunione di Teams o un ID riunione. Vengono restituiti come parte della onlineMeeting
risorsa, accessibili nella joinWebUrl
proprietà
Con le API Graph è anche possibile ottenere .threadID
La risposta ha un chatInfo
oggetto che contiene l'oggetto threadID
.
Eseguire il codice
È ora possibile avviare l'app usando il pulsante "Run App" (Esegui app) sulla barra degli strumenti (MAIUSC+F10).
Per partecipare alla riunione e alla chat di Teams, immettere il collegamento alla riunione del team e l'ID del thread nell'interfaccia utente.
Dopo aver eseguito la riunione del team, è necessario ammettere l'utente alla riunione nel client del team. Dopo che l'utente è stato ammesso e ha partecipato alla chat, è possibile inviare e ricevere messaggi.
Nota
Alcune funzionalità non sono attualmente supportate per gli scenari di interoperabilità con Teams. Altre informazioni sulle funzionalità supportate, vedere Funzionalità delle riunioni di Teams per gli utenti esterni di Teams
Questa guida introduttiva illustra come chattare in una riunione di Teams usando Servizi di comunicazione di Azure Chat SDK per C#.
Codice di esempio
Trovare il codice per questa guida introduttiva in GitHub.
Prerequisiti
- Una distribuzione di Teams.
- Un account Azure con una sottoscrizione attiva. Creare un account gratuitamente.
- Installare Visual Studio 2019 con piattaforma UWP (Universal Windows Platform) carico di lavoro sviluppo.
- Una risorsa di Servizi di comunicazione distribuita. Creare una risorsa di Servizi di comunicazione.
- Collegamento alle riunioni di Teams.
Partecipare alla chat della riunione
Un utente di Servizi di comunicazione può partecipare a una riunione di Teams come utente anonimo usando l'SDK chiamante. Partecipare alla riunione li aggiunge anche come partecipante alla chat della riunione, in cui possono inviare e ricevere messaggi con altri utenti nella riunione. L'utente non avrà accesso ai messaggi di chat inviati prima della riunione e non potrà inviare o ricevere messaggi dopo la fine della riunione. Per partecipare alla riunione e avviare la chat, è possibile seguire i passaggi successivi.
Eseguire il codice
È possibile compilare ed eseguire il codice in Visual Studio. Si notino le piattaforme della soluzione supportate: x64
,x86
e ARM64
.
- Aprire un'istanza di PowerShell, Terminale Windows, prompt dei comandi o equivalente e passare alla directory in cui si vuole clonare l'esempio.
git clone https://github.com/Azure-Samples/Communication-Services-dotnet-quickstarts.git
- Aprire il progetto ChatTeamsInteropQuickStart/ChatTeamsInteropQuickStart.csproj in Visual Studio.
- Installare le versioni dei pacchetti NuGet seguenti (o versioni successive):
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
- Con la risorsa servizi di comunicazione ottenuta nei prerequisiti, aggiungere la stringa di connessione al file 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_ = "";
Importante
- Selezionare la piattaforma appropriata dall'elenco a discesa "Piattaforme soluzioni" in Visual Studio prima di eseguire il codice, ad esempio
x64
- Assicurarsi di avere la modalità sviluppatore in Windows 10 abilitata (Developer Impostazioni)
I passaggi successivi non funzioneranno se questa configurazione non è configurata correttamente
- Premere F5 per avviare il progetto in modalità di debug.
- Incollare un collegamento alla riunione di Teams valido nella casella "Collegamento riunione di Teams" (vedere la sezione successiva)
- Premere "Partecipa alla riunione di Teams" per avviare la chat.
Importante
Dopo che l'SDK chiamante stabilisce la connessione con la riunione dei team Vedere Servizi di comunicazione che chiama l'app Windows, le funzioni chiave per gestire le operazioni di chat sono: StartPollingForChatMessages e SendMessageButton_Click. Entrambi i frammenti di codice si trovano in 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;
}
Ottenere un collegamento alla riunione di Teams
Il collegamento alla riunione di Teams può essere recuperato usando le API Graph, descritto in dettaglio nella documentazione di Graph. Questo collegamento viene restituito come parte della onlineMeeting
risorsa, accessibile nella joinWebUrl
proprietà .
È anche possibile ottenere il collegamento alla riunione richiesto dall'URL di partecipazione alla riunione nell'invito alla riunione di Teams stesso.
Un collegamento a una riunione di Teams ha un aspetto simile al seguente: https://teams.microsoft.com/l/meetup-join/meeting_chat_thread_id/1606337455313?context=some_context_here
.
Se il collegamento ai team ha un formato diverso, è necessario recuperare l'ID thread usando l'API Graph.
Nota
Alcune funzionalità non sono attualmente supportate per gli scenari di interoperabilità con Teams. Altre informazioni sulle funzionalità supportate, vedere Funzionalità delle riunioni di Teams per gli utenti esterni di Teams
Pulire le risorse
Se si vuole pulire e rimuovere una sottoscrizione a Servizi di comunicazione, è possibile eliminare la risorsa o il gruppo di risorse. L'eliminazione del gruppo di risorse comporta anche l'eliminazione di tutte le altre risorse associate. Altre informazioni sulla pulizia delle risorse.
Passaggi successivi
Per altre informazioni, vedere gli articoli seguenti:
- Consultare l'esempio hero per le chat
- Altre informazioni sul funzionamento della chat