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

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_STRINGcon 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:

Screenshot dell'applicazione JavaScript completata.

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

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.onAppearaggiungere 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.

Screenshot dell'applicazione iOS completata.

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

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.gradledel 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.javaaggiungere 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 e html 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 htmltext :

    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.

Screenshot dell'applicazione Android completata.

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

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.

  1. Aprire un'istanza di PowerShell, Terminale Windows, prompt dei comandi o equivalente e passare alla directory in cui si vuole clonare l'esempio.
  2. git clone https://github.com/Azure-Samples/Communication-Services-dotnet-quickstarts.git
  3. Aprire il progetto ChatTeamsInteropQuickStart/ChatTeamsInteropQuickStart.csproj in Visual Studio.
  4. 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

  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

  1. Premere F5 per avviare il progetto in modalità di debug.
  2. Incollare un collegamento alla riunione di Teams valido nella casella "Collegamento riunione di Teams" (vedere la sezione successiva)
  3. 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;
        }

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.

Screenshot dell'applicazione csharp completata.

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: