Share via


Schnellstart: Beitreten mit einer Chat-App zu einer Teams-Besprechung

Steigen Sie in die Nutzung von Azure Communication Services ein, indem Sie Ihre Chatlösung mit Microsoft Teams verbinden.

In dieser Schnellstartanleitung wird beschrieben, wie Sie während einer Teams-Besprechung chatten, indem Sie das Chat SDK für JavaScript von Azure Communication Services verwenden.

Beispielcode

Den fertigen Code für diese Schnellstartanleitung finden Sie auf GitHub.

Voraussetzungen

Beitritt zum Besprechungschat

Ein Communication Services-Benutzer kann mithilfe des Calling SDK einer Teams-Besprechung als anonymer Benutzer beitreten. Durch das Beitreten zur Besprechung werden die Benutzer*innen dem Besprechungschat als Teilnehmer*innen hinzugefügt und können Nachrichten mit anderen Benutzer*innen in der Besprechung austauschen. Die Benutzer*innen haben keinen Zugriff auf Chatnachrichten, die vor ihrer Teilnahme an der Besprechung gesendet wurden, und sie können nach dem Ende der Besprechung keine Nachrichten senden oder empfangen. Führen Sie die folgenden Schritte aus, um an der Besprechung teilzunehmen und mit dem Chatten zu beginnen.

Erstellen einer neuen Node.js-Anwendung

Öffnen Sie Ihr Terminal- oder Befehlsfenster, erstellen Sie ein neues Verzeichnis für Ihre App, und navigieren Sie zu diesem Verzeichnis.

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

Führen Sie npm init -y aus, um die Datei package.json mit den Standardeinstellungen zu erstellen.

npm init -y

Installieren der Chatpakete

Verwenden Sie den Befehl npm install, um die notwendigen Communication Services-SDKs für JavaScript zu installieren.

npm install @azure/communication-common --save

npm install @azure/communication-identity --save

npm install @azure/communication-chat --save

npm install @azure/communication-calling --save

Mit der Option --save wird die Bibliothek als Abhängigkeit in Ihrer Datei package.json aufgelistet.

Einrichten des App-Frameworks

In dieser Schnellstartanleitung wird Webpack verwendet, um die Anwendungsressourcen zu bündeln. Führen Sie den folgenden Befehl aus, um die npm-Pakete „webpack“, „webpack-cli“ und „webpack-dev-server“ zu installieren und als Entwicklungsabhängigkeiten in Ihrer Datei package.json aufzulisten:

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

Erstellen Sie im Stammverzeichnis Ihres Projekts die Datei index.html. Diese Datei wird zum Konfigurieren eines grundlegenden Layouts verwendet, das es Benutzer*innen ermöglicht, einer Besprechung beizutreten und einen Chat zu beginnen.

Hinzufügen der Steuerelemente der Teams-Benutzeroberfläche

Ersetzen Sie den Code in „index.html“ durch den folgenden Codeausschnitt. Das Textfeld oben auf der Seite wird verwendet, um den Teams-Besprechungskontext einzugeben. Über die Schaltfläche „An Teams-Besprechung teilnehmen“ nehmen Sie an der angegebenen Besprechung teil. Unten auf der Seite wird ein Chatpopup angezeigt. Über dieses Popup können Nachrichten im Besprechungsthread gesendet werden. Sofern die Communication Services-Benutzer*innen Mitglieder sind, werden alle im Thread gesendeten Nachrichten in Echtzeit angezeigt.

<!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>

Aktivieren der Steuerelemente der Teams-Benutzeroberfläche

Ersetzen Sie den Inhalt der Datei „client.js“ durch den folgenden Codeausschnitt.

Ersetzen Sie in diesem Codeausschnitt

  • SECRET_CONNECTION_STRING durch die Verbindungszeichenfolge für Ihre Communication Services-Instanz
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}`);
});

Die Anzeigenamen der Chatthread-Teilnehmer*innen werden nicht vom Teams-Client festgelegt. Beim Ereignis participantsAdded und participantsRemoved werden die Namen in der API für die Auflistung von Teilnehmer*innen als NULL zurückgegeben. Die Anzeigenamen der Chatteilnehmer können aus dem remoteParticipants-Feld des call-Objekts abgerufen werden. Wenn Sie eine Benachrichtigung über eine Listenänderung erhalten, können Sie den folgenden Code verwenden, um den Namen des Benutzers abzurufen, der hinzugefügt oder entfernt wurde:

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

Ausführen des Codes

Webpack-Benutzer können webpack-dev-server verwenden, um Ihre App zu erstellen und auszuführen. Führen Sie den folgenden Befehl aus, um Ihren Anwendungshost auf einem lokalen Webserver zu bündeln:

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

Navigieren Sie in Ihrem Browser zu http://localhost:8080/. Die App sollte nun gestartet werden, wie im folgenden Screenshot gezeigt:

Screenshot der fertigen JavaScript-Anwendung

Fügen Sie den Teams-Besprechungslink in das Textfeld ein. Klicken Sie auf An Teams-Besprechung teilnehmen, um an der Teams-Besprechung teilzunehmen. Nachdem der Communication Services-Benutzer zur Besprechung zugelassen wurde, können Sie über Ihre Communication Services-Anwendung chatten. Navigieren Sie zu dem Feld unten auf der Seite, um mit dem Chatten zu beginnen. Der Einfachheit halber zeigt die Anwendung nur die letzten beiden Nachrichten im Chat an.

Hinweis

Bestimmte Funktionen werden derzeit für Szenarien zur Interoperabilität mit Teams nicht unterstützt. Weitere Informationen zu den unterstützten Features finden Sie unter Besprechungsfunktionen für externe Teams-Benutzer*innen.

In diesem Schnellstart erfahren Sie, wie Sie mit dem Chat SDK für iOS für Azure Communication Services an einer Teams-Besprechung teilnehmen.

Beispielcode

Wenn Sie direkt zum Ende springen möchten, können Sie diese Schnellstartanleitung als Beispiel auf GitHub herunterladen.

Voraussetzungen

  • Ein Azure-Konto mit einem aktiven Abonnement. Kostenlos ein Konto erstellen
  • Einen Mac mit Xcode zusammen mit einem gültigen in Ihrer Keychain installierten Entwicklerzertifikat.
  • Eine Teams-Bereitstellung
  • Ein Benutzerzugriffstoken für Ihren Azure Communication Service Sie können auch die Azure CLI verwenden und den Befehl mit Ihrer Verbindungszeichenfolge ausführen, um einen Benutzer und ein Zugriffs-Token zu erstellen.
az communication identity token issue --scope voip --connection-string "yourConnectionString"

Ausführliche Informationen finden Sie unter Verwenden der Azure CLI zum Erstellen und Verwalten von Zugriffstoken.

Einrichten

Erstellen des Xcode-Projekts

Erstellen Sie in Xcode ein neues iOS-Projekt, und wählen Sie die Vorlage Single View App (Einzelansicht-App) aus. In diesem Tutorial wird das SwiftUI-Framework verwendet – legen Sie daher „Sprache“ auf „Swift“ und „Benutzeroberfläche“ auf „SwiftUI“ fest. Im Rahmen dieses Schnellstarts werden keine Tests erstellt. Sie können die Option Tests einschließen daher deaktivieren.

Screenshot des Fensters „Neues Projekt“ in Xcode

Installieren von CocoaPods

Verwenden Sie diese Anleitung, um CocoaPods auf Ihrem Mac zu installieren.

Installieren des Pakets und der Abhängigkeiten mit CocoaPods

  1. Zum Erstellen einer Podfile-Datei für Ihre Anwendung öffnen Sie das Terminal, navigieren Sie zum Projektordner, und führen Sie „pod init“ aus.

  2. Fügen Sie Podfile unter dem Ziel den folgenden Code hinzu, und speichern Sie.

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
  1. Führen Sie pod install aus.

  2. Öffnen Sie die .xcworkspace-Datei mit Xcode.

Anfordern des Zugriffs auf das Mikrofon

Damit Sie auf das Mikrofon des Geräts zugreifen zu können, müssen Sie die Liste der Informationseigenschaften Ihrer App mit NSMicrophoneUsageDescription aktualisieren. Legen Sie den zugehörigen Wert auf eine Zeichenfolge (string) fest, die in den Dialog aufgenommen wurde, mit dem das System den Zugriff beim Benutzer anfordert.

Wählen Sie unter dem Ziel die Registerkarte „Info“ aus, und fügen Sie eine Zeichenfolge für „Datenschutz – Beschreibung der Mikrofonnutzung“ hinzu

Screenshot, der das Hinzufügens der Mikrofonnutzung innerhalb von Xcode zeigt.

Deaktivieren der Verwendung einer Benutzerskript-Sandbox

Einige der Skripte in den verknüpften Bibliotheken schreiben Dateien während des Buildprozesses. Um dies zuzulassen, deaktivieren Sie die Verwendung der Benutzerskript-Sandbox in Xcode. Suchen Sie unter den Buildeinstellungen nach sandbox, und legen Sie User Script Sandboxing auf No fest.

Screenshot, der das Deaktivieren der Verwendung der Benutzerskript-Sandbox in Xcode zeigt.

Beitritt zum Besprechungschat

Ein Communication Services-Benutzer kann mithilfe des Calling SDK einer Teams-Besprechung als anonymer Benutzer beitreten. Sobald ein Benutzer der Teams-Besprechung beigetreten ist, kann er Nachrichten an andere Besprechungsteilnehmer senden und von diesen empfangen. Der Benutzer hat keinen Zugriff auf Chatnachrichten, die vor dem Beitritt gesendet wurden, und kann auch keine Nachrichten senden oder empfangen, wenn er sich nicht in der Besprechung befindet. Führen Sie die folgenden Schritte aus, um an der Besprechung teilzunehmen und mit dem Chatten zu beginnen.

Einrichten des App-Frameworks

Importieren Sie die Azure Communication-Pakete in ContentView.swift, indem Sie den folgenden Codeschnipsel hinzufügen:

import AVFoundation
import SwiftUI

import AzureCommunicationCalling
import AzureCommunicationChat

Fügen Sie in ContentView.swift den folgenden Codeschnipsel direkt oberhalb der struct ContentView: View-Deklaration hinzu:

let endpoint = "<ADD_YOUR_ENDPOINT_URL_HERE>"
let token = "<ADD_YOUR_USER_TOKEN_HERE>"
let displayName: String = "Quickstart User"

Ersetzen Sie <ADD_YOUR_ENDPOINT_URL_HERE> durch den Endpunkt Ihrer Communication Services-Ressource. Ersetzen Sie <ADD_YOUR_USER_TOKEN_HERE> durch das oben generierte Token über die Azure-Client-Befehlszeile. Weitere Informationen zu Benutzerzugriffstoken: Benutzerzugriffstoken

Ersetzen Quickstart User Sie durch den im Chat gewünschten Anzeigenamen.

Um den Zustand zu speichern, fügen Sie der ContentView-Struktur die folgenden Variablen hinzu:

  @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] = []

Nun fügen wir die Variable des Haupttexts hinzu, um die UI-Elemente zu speichern. In dieser Schnellstartanleitung fügen wir Geschäftslogik an diese Steuerelemente an. Fügen Sie der ContentView-Struktur den folgenden Code hinzu:

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
    }
  }

Initialisieren von ChatClient

Instanziieren Sie ChatClient, und aktivieren Sie Benachrichtigungen für den Empfang von Nachrichten. Wir verwenden Echtzeitbenachrichtigungen für den Empfang von Chatnachrichten.

Nachdem der Haupttext eingerichtet wurde, fügen wir die Funktionen zum Bearbeiten der Einrichtung der Anruf- und Chatclients hinzu.

Fügen Sie in der onAppear-Funktion den folgenden Code hinzu, um CallClient und ChatClient zu initialisieren:

  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
  }

Funktion für Beitritt zur Besprechung hinzufügen

Fügen Sie der ContentView-Struktur die folgende Funktion hinzu, um den Beitritt zur Besprechung zu bearbeiten.

  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"
      }
    }
  }

Initialisieren von ChatThreadClient

Wir initialisieren ChatThreadClient, nachdem der Benutzer der Besprechung beigetreten ist. Dies erfordert, dass wir den Besprechungsstatus des Delegaten überprüfen und dann ChatThreadClient mit threadId initialisieren, wenn der Besprechung beigetreten wurde.

Erstellen Sie die connectChat()-Funktion mit dem folgenden Code:

  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
    }
  }

Fügen Sie nach Möglichkeit ContentView die folgende Hilfsfunktion hinzu, die zum Analysieren der Thread-ID des Chats aus dem Besprechungslink des Teams dient. Falls diese Extraktion fehlschlagen sollte, muss der Benutzer die Thread-ID des Chats manuell mithilfe von Graph-APIs eingeben, um die Thread-ID abzurufen.

 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
}

Aktivieren des Sendens von Nachrichten

Fügen Sie die sendMessage()-Funktion ContentView hinzu. Diese Funktion verwendet ChatThreadClient, um Nachrichten der Benutzer*innen zu senden.

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?"
    }
  }
}

Aktivieren des Empfangens von Nachrichten

Um Nachrichten zu empfangen, implementieren wir den Handler für ChatMessageReceived-Ereignisse. Wenn neue Nachrichten an den Thread gesendet werden, fügt dieser Handler die Nachrichten der Variablen meetingMessages hinzu, damit sie auf der Benutzeroberfläche angezeigt werden können.

Fügen Sie zunächst ContentView.swift die folgende Struktur hinzu. Die Benutzeroberfläche verwendet die Daten in der Struktur, um unsere Chatnachrichten anzuzeigen.

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
    )
  }
}

Fügen Sie als Nächstes die receiveMessage()-Funktion ContentView hinzu. Dies wird aufgerufen, wenn ein Nachrichtenereignis auftritt. Beachten Sie, dass Sie sich für alle Ereignisse registrieren müssen, die Sie in der switch-Anweisung über die chatClient?.register()-Methode bearbeiten möchten.

  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
    }
  }

Schließlich müssen wir den Delegatenhandler für den Anrufclient implementieren. Dieser Handler wird verwendet, um den Anrufstatus zu überprüfen und den Chatclient zu initialisieren, wenn der Benutzer der Besprechung beitritt.

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"
    }
  }
}

Verlassen des Chats

Wenn der Benutzer die Besprechung des Teams verlässt, löschen wir die Chatnachrichten auf der Benutzeroberfläche und beenden den Anruf. Der vollständige Code ist unten dargestellt.

  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"
    }
  }

Abrufen eines Teams-Besprechungschatthreads für einen Communication Services-Benutzer

Die Details für die Teams-Besprechung können über Graph-APIs abgerufen werden, wie in der Graph-Dokumentation ausführlich erläutert. Das Communication Services Calling SDK akzeptiert einen vollständigen Teams-Besprechungslink oder eine Besprechungs-ID. Beide werden als Teil der onlineMeeting-Ressource zurückgegeben, auf die unter der joinWebUrl-Eigenschaft zugegriffen werden kann.

Sie können die threadID auch mit den Graph-APIs abrufen. Die Antwort umfasst ein chatInfo-Objekt, das die threadID enthält.

Ausführen des Codes

Führen Sie die Anwendung aus.

Um an der Teams-Besprechung teilzunehmen, geben Sie auf der Benutzeroberfläche den Besprechungslink Ihres Teams ein.

Nachdem Sie der Teams-Besprechung beigetreten sind, müssen Sie die Benutzer*innen in Ihrem Teams-Client zur Besprechung zulassen. Sobald der Benutzer zugelassen wurde und dem Chat beigetreten ist, können Sie Nachrichten senden und empfangen.

Screenshot der fertigen iOS-Anwendung

Hinweis

Bestimmte Funktionen werden derzeit für Szenarien zur Interoperabilität mit Teams nicht unterstützt. Weitere Informationen zu den unterstützten Features finden Sie unter Besprechungsfunktionen für externe Teams-Benutzer*innen.

In diesem Schnellstart erfahren Sie, wie Sie mit dem Chat SDK für Android von Azure Communication Services an einer Teams-Besprechung teilnehmen.

Beispielcode

Wenn Sie direkt zum Ende springen möchten, können Sie diese Schnellstartanleitung als Beispiel auf GitHub herunterladen.

Voraussetzungen

Aktivieren der Teams-Interoperabilität

Ein Communication Services-Benutzer, der einer Teams-Besprechung als Gastbenutzer hinzugefügt wird, kann nur auf den Chat der Besprechung zugreifen, wenn er der Teams-Telefonkonferenz beitritt. Weitere Informationen zum Hinzufügen eines Communication Services-Benutzers zu einer Teams-Telefonkonferenz finden Sie in der Dokumentation zur Teams-Interoperabilität.

Sie müssen Mitglied der besitzenden Organisation beider Entitäten sein, um dieses Feature verwenden zu können.

Beitritt zum Besprechungschat

Sobald die Teams-Interoperabilität aktiviert ist, können Communication Services-Benutzer über die Callin SDK als externe Benutzer am Teams-Anruf teilnehmen. Durch das Beitreten zur Anrufkonferenz wird sie dem Besprechungschat als Teilnehmer*innen hinzugefügt und können Nachrichten mit anderen Benutzer*innen der Anrufkonferenz austauschen. Der Benutzer besitzt keinen Zugriff auf Chatnachrichten, die gesandt wurden, bevor er dem Anruf beigetreten ist. Führen Sie die folgenden Schritte aus, um an der Besprechung teilzunehmen und mit dem Chatten zu beginnen.

Hinzufügen von Chat zur Anruf-App von Teams

Fügen Sie in build.gradle auf Modulebene die Abhängigkeit vom Chat SDK hinzu.

Wichtig

Bekanntes Problem: Bei gemeinsamer Verwendung von Android Chat SDK und Calling SDK in der gleichen Anwendung funktioniert das Echtzeitbenachrichtigungsfeature des Chat SDK nicht. Es wird ein Problem mit der Abhängigkeitsauflösung angezeigt. Während wir an einer Lösung arbeiten, können Sie das Feature für Echtzeitbenachrichtigungen deaktivieren, indem Sie der Chat SDK-Abhängigkeit in der build.gradle-Datei der App die folgenden Ausschlüsse hinzufügen:

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

Hinzufügen des Layouts der Teams-Benutzeroberfläche

Ersetzen Sie den Code in „activity_main.xml“ durch den folgenden Codeausschnitt. Dadurch werden Eingaben für die Thread-ID und für das Senden von Nachrichten, eine Schaltfläche zum Senden der eingegebenen Nachricht und ein einfaches Chatlayout hinzugefügt.

<?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>

Aktivieren der Steuerelemente der Teams-Benutzeroberfläche

Importieren von Paketen und Definieren von Zustandsvariablen

Fügen Sie dem Inhalt von MainActivity.java die folgenden Importe hinzu:

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;

Fügen Sie der Klasse MainActivity die folgenden Variablen hinzu:

    // 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<>();

Ersetzen Sie <USER_ID> durch die ID des Benutzers, der den Chat initiiert. Ersetzen Sie <COMMUNICATION_SERVICES_RESOURCE_ENDPOINT> durch den Endpunkt Ihrer Communication Services-Ressource.

Initialisieren von ChatThreadClient

Instanziieren Sie nach dem Beitritt zur Besprechung den ChatThreadClient, und machen Sie die Chatkomponenten sichtbar.

Aktualisieren Sie das Ende der MainActivity.joinTeamsMeeting()-Methode mit dem folgenden Code:

    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);
    }

Aktivieren des Sendens von Nachrichten

Fügen Sie die sendMessage()-Methode zu MainActivity hinzu. Diese Funktion verwendet ChatThreadClient, um Nachrichten im Namen der Benutzer*innen zu senden.

    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("");
    }

Aktivieren des Abrufs für Nachrichten und Rendern von Nachrichten in der Anwendung

Wichtig

Bekanntes Problem: Da das Echtzeitbenachrichtigungsfeature des Chat SDK nicht mit den Calling SDKs funktioniert, muss die GetMessages-API in vordefinierten Intervallen abgefragt werden. In unserem Beispiel verwenden wir Intervalle von 3 Sekunden.

Wir können die folgenden Daten aus der von der GetMessages-API zurückgegebenen Nachrichtenliste abrufen:

  • Die text- und html-Nachrichten im Thread seit dem Beitritt
  • Änderungen an der Threadliste
  • Aktualisierungen des Threadthemas

Fügen Sie der MainActivity-Klasse einen Handler und eine ausführbare Aufgabe hinzu, die in Intervallen von 3 Sekunden ausgeführt wird:

    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);
        }
    };

Beachten Sie, dass die Aufgabe bereits am Ende der MainActivity.joinTeamsMeeting()-Methode gestartet wurde, die im Initialisierungsschritt aktualisiert wurde.

Abschließend fügen wir die Methode hinzu, um alle zugänglichen Nachrichten im Thread abzufragen, sie anhand des Nachrichtentyps zu analysieren und die html- und text-Nachrichten anzuzeigen:

    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);
        }
    }

Die Anzeigenamen der Chatthread-Teilnehmer*innen werden nicht vom Teams-Client festgelegt. Beim Ereignis participantsAdded und participantsRemoved werden die Namen in der API für die Auflistung von Teilnehmer*innen als NULL zurückgegeben. Die Anzeigenamen der Chatteilnehmer können aus dem remoteParticipants-Feld des call-Objekts abgerufen werden.

Abrufen eines Teams-Besprechungschatthreads für einen Communication Services-Benutzer

Die Details für die Teams-Besprechung können über Graph-APIs abgerufen werden, wie in der Graph-Dokumentation ausführlich erläutert. Das Communication Services Calling SDK akzeptiert einen vollständigen Teams-Besprechungslink oder eine Besprechungs-ID. Beide werden als Teil der onlineMeeting-Ressource zurückgegeben, auf die unter der joinWebUrl-Eigenschaft zugegriffen werden kann.

Sie können die threadID auch mit den Graph-APIs abrufen. Die Antwort umfasst ein chatInfo-Objekt, das die threadID enthält.

Ausführen des Codes

Die App kann jetzt mithilfe der Schaltfläche „Run app“ (App ausführen) auf der Symbolleiste gestartet werden (UMSCHALT + F10).

Um an der Teams-Besprechung und am Chat teilzunehmen, geben Sie den Besprechungslink aus Teams und die Thread-ID ein.

Nach dem Beitritt zur Teams-Besprechung müssen Sie die Benutzer*innen in Ihrem Teams-Client zur Besprechung zulassen. Sobald der Benutzer zugelassen wurde und dem Chat beigetreten ist, können Sie Nachrichten senden und empfangen.

Screenshot der fertigen Android-Anwendung.

Hinweis

Bestimmte Funktionen werden derzeit für Szenarien zur Interoperabilität mit Teams nicht unterstützt. Weitere Informationen zu den unterstützten Features finden Sie unter Besprechungsfunktionen für externe Teams-Benutzer*innen.

In dieser Schnellstartanleitung erfahren Sie, wie Sie mit dem Azure Communication Services Chat SDK für C# in einer Teams-Besprechung chatten.

Beispielcode

Den Code für diese Schnellstartanleitung finden Sie auf GitHub.

Voraussetzungen

Beitritt zum Besprechungschat

Ein Communication Services-Benutzer kann mithilfe des Calling SDK einer Teams-Besprechung als anonymer Benutzer beitreten. Durch das Beitreten zur Besprechung werden die Benutzer*innen dem Besprechungschat als Teilnehmer*innen hinzugefügt und können Nachrichten mit anderen Benutzer*innen in der Besprechung austauschen. Der Benutzer hat keinen Zugriff auf Chatnachrichten, die vor seiner Teilnahme an der Besprechung gesandt wurden, und er kann nach dem Ende der Besprechung keine Nachrichten senden oder empfangen. Führen Sie die folgenden Schritte aus, um an der Besprechung teilzunehmen und mit dem Chatten zu beginnen.

Ausführen des Codes

Sie können den Build in Visual Studio erstellen und den Code ausführen. Beachten Sie die von uns unterstützen Projektmappenplattformen: x64, x86 und ARM64.

  1. Öffnen Sie eine Instanz von PowerShell, des Windows-Terminals, einer Eingabeaufforderung oder ein Äquivalent dazu, und navigieren Sie zu dem Verzeichnis, in dem Sie das Beispiel klonen möchten.
  2. git clone https://github.com/Azure-Samples/Communication-Services-dotnet-quickstarts.git
  3. Öffnen Sie das Projekt „ChatTeamsInteropQuickStart/ChatTeamsInteropQuickStart.csproj“ in Visual Studio.
  4. Installieren Sie die folgenden NuGet-Paketversionen (oder höher):
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. Fügen Sie der Datei ChatTeamsInteropQuickStart/MainPage.xaml.cs die Verbindungszeichenfolge mit der in den Voraussetzungen vermittelten Communication Services-Ressource hinzu.
//Azure Communication Services resource connection string, i.e., = "endpoint=https://your-resource.communication.azure.net/;accesskey=your-access-key";
private const string connectionString_ = "";

Wichtig

  • Wählen Sie die richtige Plattform aus der Dropdownliste „Projektmappenplattformen“ in Visual Studio aus, bevor Sie den Code ausführen, d. h. x64
  • Stellen Sie sicher, dass der Entwicklermodus in Windows 10 aktiviert ist (Entwicklereinstellungen).

Die nächsten Schritte funktionieren nicht, wenn dies nicht ordnungsgemäß konfiguriert ist.

  1. Drücken Sie F5, um das Projekt im Debugmodus zu starten.
  2. Fügen Sie einen gültigen Teams-Besprechungslink in das Feld „Teams-Besprechungslink“ ein (siehe nächster Abschnitt).
  3. Klicken Sie auf „Join Teams meeting“ (Teams-Besprechung beitreten), um mit dem Chatten zu beginnen.

Wichtig

Sobald das aufrufende SDK die Verbindung mit der Teamsitzung hergestellt hat siehe „Communication Services, die die Windows App aufrufen“, sind die Schlüsselfunktionen zur Handhabung von Chatvorgängen: StartPollingForChatMessages und SendMessageButton_Click. Beide Codeausschnitte befinden sich 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;
        }

Der Teams-Besprechungslink kann über Graph-APIs abgerufen werden, wie in der Graph-Dokumentation ausführlich erläutert. Dieser Link wird als Teil der onlineMeeting-Ressource zurückgegeben, auf die unter der joinWebUrl-Eigenschaft zugegriffen werden kann.

Der erforderliche Besprechungslink kann auch der URL für den Besprechungsbeitritt aus der Teams-Besprechungseinladung entnommen werden. Ein Teams-Besprechungslink sieht wie folgt aus: https://teams.microsoft.com/l/meetup-join/meeting_chat_thread_id/1606337455313?context=some_context_here. Wenn Ihr Teams-Link ein anderes Format als dieses aufweist, müssen Sie die Thread-ID mithilfe der Graph-API abrufen.

Screenshot der fertigen csharp-Anwendung

Hinweis

Bestimmte Funktionen werden derzeit für Szenarien zur Interoperabilität mit Teams nicht unterstützt. Weitere Informationen zu den unterstützten Features finden Sie unter Besprechungsfunktionen für externe Teams-Benutzer*innen.

Bereinigen von Ressourcen

Wenn Sie ein Communication Services-Abonnement bereinigen und entfernen möchten, können Sie die Ressource oder die Ressourcengruppe löschen. Wenn Sie die Ressourcengruppe löschen, werden auch alle anderen Ressourcen gelöscht, die ihr zugeordnet sind. Weitere Informationen zum Bereinigen von Ressourcen finden Sie hier.

Nächste Schritte

Weitere Informationen finden Sie in den folgenden Artikeln: