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.
Codice di esempio
Se si vuole passare direttamente alla fine, è possibile scaricare questa guida introduttiva come esempio in GitHub.
Prerequisiti
- Un account Azure con una sottoscrizione attiva. Creare un account gratuitamente
- Un Mac che esegue Xcode, insieme a un certificato dello sviluppatore valido installato nel portachiavi.
- Una distribuzione di Teams.
- Un token di accesso utente per il servizio di comunicazione di Azure. È anche possibile usare l'interfaccia della riga di comando di Azure ed eseguire il comando con il stringa di connessione per creare un utente e un token di accesso.
az communication identity token issue --scope voip --connection-string "yourConnectionString"
Per informazioni dettagliate, vedere Usare l'interfaccia della riga di comando di Azure per creare e gestire i token di accesso.
Configurazione
Creazione del progetto Xcode
In Xcode creare un nuovo progetto iOS e selezionare il modello Single View Application. Questa esercitazione usa il framework SwiftUI, quindi è necessario impostare Il linguaggio su Swift e l'interfaccia utente su SwiftUI. In questo argomento di avvio rapido non verranno creati test. È possibile deselezionare Includi test.
Installazione di CocoaPods
Usare questa guida per installare CocoaPods nel Mac.
Installare il pacchetto e le dipendenze con CocoaPods
Per creare un
Podfile
oggetto per l'applicazione, aprire il terminale e passare alla cartella del progetto ed eseguire pod init.Aggiungere il codice seguente a
Podfile
sotto la destinazione e salvare.
target 'Chat Teams Interop' do
# Comment the next line if you don't want to use dynamic frameworks
use_frameworks!
# Pods for Chat Teams Interop
pod 'AzureCommunicationCalling'
pod 'AzureCommunicationChat'
end
Eseguire
pod install
.Aprire il
.xcworkspace
file con Xcode.
Richiedere l'accesso al microfono
Per accedere al microfono del dispositivo, è necessario aggiornare il file Information Property List dell'app con NSMicrophoneUsageDescription
. Il valore associato è stato impostato su un string
oggetto incluso nella finestra di dialogo utilizzata dal sistema per richiedere l'accesso all'utente.
Nella destinazione selezionare la Info
scheda e aggiungere una stringa per "Privacy - Descrizione utilizzo microfono"
Disabilitare la sandbox degli script utente
Alcuni degli script all'interno delle librerie collegate scrivono file durante il processo di compilazione. Per consentire questa operazione, disabilitare il sandboxing di script utente in Xcode.
Nelle impostazioni di compilazione cercare sandbox
e impostare su User Script Sandboxing
No
.
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. Una volta che un utente ha partecipato alla riunione di Teams, può inviare e ricevere messaggi con altri partecipanti alla riunione. L'utente non avrà accesso ai messaggi di chat inviati prima di partecipare, né sarà in grado di inviare o ricevere messaggi quando non si trovano nella riunione. Per partecipare alla riunione e avviare la chat, è possibile seguire i passaggi successivi.
Configurare il framework dell'app
Importare i pacchetti di comunicazione di Azure in ContentView.swift
aggiungendo il frammento di codice seguente:
import AVFoundation
import SwiftUI
import AzureCommunicationCalling
import AzureCommunicationChat
Aggiungere ContentView.swift
il frammento di codice seguente, appena sopra la struct ContentView: View
dichiarazione:
let endpoint = "<ADD_YOUR_ENDPOINT_URL_HERE>"
let token = "<ADD_YOUR_USER_TOKEN_HERE>"
let displayName: String = "Quickstart User"
Sostituire <ADD_YOUR_ENDPOINT_URL_HERE>
con l'endpoint per la risorsa di Servizi di comunicazione.
Sostituire <ADD_YOUR_USER_TOKEN_HERE>
con il token generato in precedenza, tramite la riga di comando del client di Azure.
Altre informazioni sui token di accesso utente: Token di accesso utente
Sostituire Quickstart User
con il nome visualizzato che si vuole usare nella chat.
Per mantenere lo stato, aggiungere le variabili seguenti allo ContentView
struct:
@State var message: String = ""
@State var meetingLink: String = ""
@State var chatThreadId: String = ""
// Calling state
@State var callClient: CallClient?
@State var callObserver: CallDelegate?
@State var callAgent: CallAgent?
@State var call: Call?
// Chat state
@State var chatClient: ChatClient?
@State var chatThreadClient: ChatThreadClient?
@State var chatMessage: String = ""
@State var meetingMessages: [MeetingMessage] = []
Aggiungere ora il corpo principale var per contenere gli elementi dell'interfaccia utente. In questa guida introduttiva viene associata la logica di business a questi controlli. Aggiungere il codice seguente allo ContentView
struct :
var body: some View {
NavigationView {
Form {
Section {
TextField("Teams Meeting URL", text: $meetingLink)
.onChange(of: self.meetingLink, perform: { value in
if let threadIdFromMeetingLink = getThreadId(from: value) {
self.chatThreadId = threadIdFromMeetingLink
}
})
TextField("Chat thread ID", text: $chatThreadId)
}
Section {
HStack {
Button(action: joinMeeting) {
Text("Join Meeting")
}.disabled(
chatThreadId.isEmpty || callAgent == nil || call != nil
)
Spacer()
Button(action: leaveMeeting) {
Text("Leave Meeting")
}.disabled(call == nil)
}
Text(message)
}
Section {
ForEach(meetingMessages, id: \.id) { message in
let currentUser: Bool = (message.displayName == displayName)
let foregroundColor = currentUser ? Color.white : Color.black
let background = currentUser ? Color.blue : Color(.systemGray6)
let alignment = currentUser ? HorizontalAlignment.trailing : .leading
HStack {
if currentUser {
Spacer()
}
VStack(alignment: alignment) {
Text(message.displayName).font(Font.system(size: 10))
Text(message.content)
.frame(maxWidth: 200)
}
.padding(8)
.foregroundColor(foregroundColor)
.background(background)
.cornerRadius(8)
if !currentUser {
Spacer()
}
}
}
.frame(maxWidth: .infinity)
}
TextField("Enter your message...", text: $chatMessage)
Button(action: sendMessage) {
Text("Send Message")
}.disabled(chatThreadClient == nil)
}
.navigationBarTitle("Teams Chat Interop")
}
.onAppear {
// Handle initialization of the call and chat clients
}
}
Inizializzare ChatClient
Creare un'istanza di e abilitare le notifiche dei ChatClient
messaggi. Si usano notifiche in tempo reale per la ricezione di messaggi di chat.
Dopo aver configurato il corpo principale, aggiungere le funzioni per gestire la configurazione dei client di chiamata e chat.
onAppear
Nella funzione aggiungere il codice seguente per inizializzare CallClient
e ChatClient
:
if let threadIdFromMeetingLink = getThreadId(from: self.meetingLink) {
self.chatThreadId = threadIdFromMeetingLink
}
// Authenticate
do {
let credentials = try CommunicationTokenCredential(token: token)
self.callClient = CallClient()
self.callClient?.createCallAgent(
userCredential: credentials
) { agent, error in
if let e = error {
self.message = "ERROR: It was not possible to create a call agent."
print(e)
return
} else {
self.callAgent = agent
}
}
// Start the chat client
self.chatClient = try ChatClient(
endpoint: endpoint,
credential: credentials,
withOptions: AzureCommunicationChatClientOptions()
)
// Register for real-time notifications
self.chatClient?.startRealTimeNotifications { result in
switch result {
case .success:
self.chatClient?.register(
event: .chatMessageReceived,
handler: receiveMessage
)
case let .failure(error):
self.message = "Could not register for message notifications: " + error.localizedDescription
print(error)
}
}
} catch {
print(error)
self.message = error.localizedDescription
}
Aggiungere la funzione di partecipazione alla riunione
Aggiungere la funzione seguente allo ContentView
struct per gestire la partecipazione alla riunione.
func joinMeeting() {
// Ask permissions
AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
if granted {
let teamsMeetingLink = TeamsMeetingLinkLocator(
meetingLink: self.meetingLink
)
self.callAgent?.join(
with: teamsMeetingLink,
joinCallOptions: JoinCallOptions()
) {(call, error) in
if let e = error {
self.message = "Failed to join call: " + e.localizedDescription
print(e.localizedDescription)
return
}
self.call = call
self.callObserver = CallObserver(self)
self.call?.delegate = self.callObserver
self.message = "Teams meeting joined successfully"
}
} else {
self.message = "Not authorized to use mic"
}
}
}
Inizializzare ChatThreadClient
Verrà inizializzato dopo ChatThreadClient
che l'utente ha partecipato alla riunione. È quindi necessario controllare lo stato della riunione dal delegato e quindi inizializzare con l'oggetto ChatThreadClient
threadId
quando si partecipa alla riunione.
Creare la connectChat()
funzione con il codice seguente:
func connectChat() {
do {
self.chatThreadClient = try chatClient?.createClient(
forThread: self.chatThreadId
)
self.message = "Joined meeting chat successfully"
} catch {
self.message = "Failed to join the chat thread: " + error.localizedDescription
}
}
Aggiungere la funzione helper seguente a ContentView
, usata per analizzare l'ID del thread chat dal collegamento alla riunione del team, se possibile. Nel caso in cui l'estrazione non riesca, l'utente dovrà immettere manualmente l'ID del thread di Chat usando le API Graph per recuperare l'ID del thread.
func getThreadId(from teamsMeetingLink: String) -> String? {
if let range = teamsMeetingLink.range(of: "meetup-join/") {
let thread = teamsMeetingLink[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: displayName,
type: .text
)
self.chatThreadClient?.send(message: message) { result, _ in
switch result {
case .success:
print("Chat message sent")
self.chatMessage = ""
case let .failure(error):
self.message = "Failed to send message: " + error.localizedDescription + "\n Has your token expired?"
}
}
}
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: Identifiable {
let id: String
let date: Date
let content: String
let displayName: String
static func fromTrouter(event: ChatMessageReceivedEvent) -> MeetingMessage {
let displayName: String = event.senderDisplayName ?? "Unknown User"
let content: String = event.message.replacingOccurrences(
of: "<[^>]+>", with: "",
options: String.CompareOptions.regularExpression
)
return MeetingMessage(
id: event.id,
date: event.createdOn?.value ?? Date(),
content: content,
displayName: displayName
)
}
}
Aggiungere quindi la receiveMessage()
funzione a ContentView
. Questo metodo viene chiamato quando si verifica un evento di messaggistica. Si noti che è necessario eseguire la registrazione per tutti gli eventi che si desidera gestire nell'istruzione switch
tramite il chatClient?.register()
metodo .
func receiveMessage(event: TrouterEvent) -> Void {
switch event {
case let .chatMessageReceivedEvent(messageEvent):
let message = MeetingMessage.fromTrouter(event: messageEvent)
self.meetingMessages.append(message)
/// OTHER EVENTS
// case .realTimeNotificationConnected:
// case .realTimeNotificationDisconnected:
// case .typingIndicatorReceived(_):
// case .readReceiptReceived(_):
// case .chatMessageEdited(_):
// case .chatMessageDeleted(_):
// case .chatThreadCreated(_):
// case .chatThreadPropertiesUpdated(_):
// case .chatThreadDeleted(_):
// case .participantsAdded(_):
// case .participantsRemoved(_):
default:
break
}
}
Infine, è necessario implementare il gestore delegato per il client di chiamata. Questo gestore viene usato per controllare lo stato della chiamata e inizializzare il client di chat quando l'utente partecipa alla riunione.
class CallObserver : NSObject, CallDelegate {
private var owner: ContentView
init(_ view: ContentView) {
owner = view
}
func call(
_ call: Call,
didChangeState args: PropertyChangedEventArgs
) {
owner.message = CallObserver.callStateToString(state: call.state)
if call.state == .disconnected {
owner.call = nil
owner.message = "Left Meeting"
} else if call.state == .inLobby {
owner.message = "Waiting in lobby (go let them in!)"
} else if call.state == .connected {
owner.message = "Connected"
owner.connectChat()
}
}
private static func callStateToString(state: CallState) -> String {
switch state {
case .connected: return "Connected"
case .connecting: return "Connecting"
case .disconnected: return "Disconnected"
case .disconnecting: return "Disconnecting"
case .earlyMedia: return "EarlyMedia"
case .none: return "None"
case .ringing: return "Ringing"
case .inLobby: return "InLobby"
default: return "Unknown"
}
}
}
Lasciare la chat
Quando l'utente lascia la riunione del team, si cancellano i messaggi di chat dall'interfaccia utente e si blocca la chiamata. Di seguito è riportato il codice completo.
func leaveMeeting() {
if let call = self.call {
self.chatClient?.unregister(event: .chatMessageReceived)
self.chatClient?.stopRealTimeNotifications()
call.hangUp(options: nil) { (error) in
if let e = error {
self.message = "Leaving Teams meeting failed: " + e.localizedDescription
} else {
self.message = "Leaving Teams meeting was successful"
}
}
self.meetingMessages.removeAll()
} else {
self.message = "No active call to hangup"
}
}
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
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.
Codice di esempio
Se si vuole passare direttamente alla fine, è possibile scaricare questa guida introduttiva come esempio in GitHub.
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