Udostępnij za pośrednictwem


Szybki start: dołączanie aplikacji do czatu do spotkania Teams

Rozpocznij pracę z usługami Azure Communication Services, łącząc rozwiązanie czatu z usługą Microsoft Teams.

W tym artykule opisano sposób czatu na spotkaniu w usłudze Teams przy użyciu zestawu SDK czatu usług Azure Communication Services dla języka JavaScript.

Przykładowy kod

Pobierz ten kod z GitHub Azure Samples Dołącz aplikację czatu do spotkania Teams.

Wymagania wstępne

Dołączanie do czatu na spotkaniu

Użytkownik usługi Communication Services może dołączyć do spotkania Teams jako anonimowy użytkownik za pośrednictwem Calling SDK. Dołączenie do spotkania powoduje również dodanie ich jako uczestnika do czatu spotkania, w którym mogą wysyłać i odbierać wiadomości innym użytkownikom na spotkaniu. Użytkownik nie ma dostępu do wiadomości czatu, które zostały wysłane przed dołączeniem do spotkania i nie może wysyłać ani odbierać wiadomości po zakończeniu spotkania. Aby dołączyć do spotkania i rozpocząć rozmowę, możesz wykonać następne kroki.

Tworzenie nowej aplikacji Node.js

Otwórz terminal lub okno polecenia, utwórz nowy katalog dla aplikacji i przejdź do niego.

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

Uruchom polecenie npm init -y , aby utworzyć plik package.json z ustawieniami domyślnymi.

npm init -y

Instalowanie pakietów czatów

Użyj polecenia , npm install aby zainstalować niezbędne zestawy SDK usług Komunikacyjnych dla języka 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

Opcja --save wyświetla bibliotekę jako zależność w pliku package.json .

Konfigurowanie struktury aplikacji

W tym przykładzie użyto pakietu webpack do tworzenia pakietów zawartości aplikacji. Uruchom następujące polecenie, aby zainstalować pakiety webpack, webpack-cli i webpack-dev-server npm i wyświetlić je jako zależności programistyczne w package.json:

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

Utwórz plik index.html w katalogu głównym projektu. Ten plik służy do konfigurowania podstawowego układu, który umożliwia użytkownikowi dołączenie do spotkania i rozpoczęcie rozmowy.

Dodawanie kontrolek interfejsu użytkownika usługi Teams

Zastąp kod w index.html poniższym fragmentem kodu.

Użyj pola tekstowego w górnej części strony, aby wprowadzić kontekst spotkania w Teams. Użytkownicy końcowi mogą dołączyć do określonego spotkania, klikając przycisk Dołącz do spotkania w aplikacji Teams .

W dolnej części strony zostanie wyświetlone wyskakujące okienko czatu. Użytkownicy końcowi mogą używać go do wysyłania wiadomości w wątku spotkania. Wyświetla on w czasie rzeczywistym wszystkie komunikaty wysyłane w wątku, gdy użytkownik usług komunikacyjnych jest członkiem.

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

Włącz kontrolki interfejsu użytkownika Teams

Zastąp zawartość pliku client.js poniższym fragmentem kodu.

W fragmencie kodu zastąp

  • SECRET_CONNECTION_STRINGz ciągiem połączeniowym usługi komunikacyjnej
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}`);
});

Klient usługi Teams nie ustawia nazw wyświetlanych uczestników wątku czatu. Nazwy są zwracane jako wartość null w API listy uczestników, w przypadku zdarzeń participantsAdded i participantsRemoved. Nazwy wyświetlane uczestników czatu można pobrać z remoteParticipants pola call obiektu. Po otrzymaniu powiadomienia o zmianie listy możesz użyć tego kodu, aby pobrać nazwę użytkownika, który został dodany lub usunięty:

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

Uruchamianie kodu

Użyj polecenia , webpack-dev-server aby skompilować i uruchomić aplikację. Uruchom następujące polecenie, aby utworzyć pakiet hosta aplikacji na lokalnym serwerze internetowym:

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

Otwórz przeglądarkę i przejdź do http://localhost:8080/. Aplikacja powinna zostać uruchomiona, jak pokazano na poniższym zrzucie ekranu:

Zrzut ekranu przedstawiający ukończoną aplikację JavaScript.

Wstaw link do spotkania w Teams w polu tekstowym. Użytkownicy mogą kliknąć pozycję Dołącz do spotkania usługi Teams , aby dołączyć do spotkania usługi Teams. Gdy użytkownik usług komunikacyjnych zostanie przyjęty na spotkanie, możesz porozmawiać z poziomu aplikacji usług komunikacyjnych. Przejdź do pola w dolnej części strony, aby rozpocząć rozmowę. Dla uproszczenia aplikacja wyświetla tylko dwa ostatnie komunikaty na czacie.

Uwaga

Niektóre funkcje nie są obecnie obsługiwane w scenariuszach współdziałania z usługą Teams. Aby uzyskać więcej informacji na temat obsługiwanych funkcji, zobacz Możliwości spotkań usługi Teams dla użytkowników zewnętrznych usługi Teams.

W tym artykule opisano sposób czatu na spotkaniu w aplikacji Teams przy użyciu zestawu SDK czatu usług Azure Communication Services dla systemu iOS.

Przykładowy kod

Pobierz ten kod w ramach GitHub Azure Samples Dołącz aplikację czatu do spotkania Teams.

Wymagania wstępne

  • Konto platformy Azure z aktywną subskrypcją. Bezpłatne tworzenie konta
  • Komputer Mac z uruchomionym programem Xcode wraz z prawidłowym certyfikatem dewelopera zainstalowanym w Twoim pęku kluczy.
  • Wdrożenie usługi Teams.
  • Token dostępu użytkownika dla usługi Azure Communication Service. Możesz również użyć Azure CLI i uruchomić polecenie z użyciem ciągu połączenia, aby utworzyć użytkownika i token dostępu.
az communication user-identity token issue --scope voip chat --connection-string "yourConnectionString"

Aby uzyskać szczegółowe informacje, zobacz Tworzenie tokenów dostępu za pomocą interfejsu wiersza polecenia platformy Azure i zarządzanie nimi.

Konfigurowanie

Tworzenie projektu Xcode

W programie Xcode utwórz nowy projekt systemu iOS i wybierz szablon Aplikacja z jednym widokiem. W tym samouczku jest używany framework SwiftUI, dlatego należy ustawić język na Swift i interfejs użytkownika na SwiftUI. Podczas tego szybkiego startu nie utworzysz testów. Możesz usunąć zaznaczenie pola wyboru Uwzględnij testy.

Zrzut ekranu przedstawiający okno Nowy projekt w programie Xcode.

Instalowanie platformy CocoaPods

Skorzystaj z tego przewodnika, aby zainstalować narzędzie CocoaPods na komputerze Mac.

Instalowanie pakietu i zależności za pomocą narzędzia CocoaPods

  1. Aby utworzyć Podfile dla swojej aplikacji, otwórz terminal, przejdź do folderu projektu i uruchom pod init.

  2. Dodaj następujący kod do obiektu Podfile docelowego i zapisz.

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. Uruchom program pod install.

  2. Otwórz plik .xcworkspace w programie Xcode.

Żądanie dostępu do mikrofonu

Aby uzyskać dostęp do mikrofonu urządzenia, należy zaktualizować listę właściwości informacji aplikacji za pomocą elementu NSMicrophoneUsageDescription. Skojarzona wartość została ustawiona na string, która została uwzględniona w dialogu używanym przez system do żądania dostępu od użytkownika.

W obszarze docelowym wybierz kartę Info i dodaj ciąg dla elementu Privacy - Microphone Usage Description.

Zrzut ekranu przedstawiający dodawanie użycia mikrofonu w środowisku Xcode.

Wyłącz piaskownicowanie skryptów użytkownika

Niektóre skrypty w połączonych bibliotekach zapisują pliki podczas procesu kompilacji. Aby umożliwić zapisywanie plików, wyłącz piaskownicę skryptów użytkownika w Xcode.

W obszarze ustawień kompilacji wyszukaj sandbox i ustaw User Script Sandboxing na No.

Zrzut ekranu przedstawiający wyłączanie piaskownicy skryptów użytkowych w środowisku Xcode.

Dołączanie do czatu na spotkaniu

Użytkownik usług komunikacyjnych może dołączyć do spotkania Microsoft Teams jako anonimowy użytkownik, używając SDK połączeń. Gdy użytkownik dołączy do spotkania usługi Teams, będzie mógł wysyłać i odbierać wiadomości z innymi uczestnikami spotkania. Użytkownik nie ma dostępu do wiadomości czatu wysyłanych przed dołączeniem ani nie może wysyłać ani odbierać wiadomości, gdy nie są w spotkaniu.

Aby dołączyć do spotkania i rozpocząć rozmowę, wykonaj następne kroki.

Konfigurowanie struktury aplikacji

Zaimportuj pakiety usługi Azure Communication w programie ContentView.swift , dodając następujący fragment kodu:

import AVFoundation
import SwiftUI

import AzureCommunicationCalling
import AzureCommunicationChat

W ContentView.swift pliku dodaj następujący fragment kodu tuż nad deklaracją struct ContentView: View :

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

Zastąp <ADD_YOUR_ENDPOINT_URL_HERE> punktem końcowym zasobu Usług Komunikacyjnych. Zastąp <ADD_YOUR_USER_TOKEN_HERE> wcześniej wygenerowanym tokenem za pomocą wiersza poleceń klienta Azure.

Aby uzyskać więcej informacji, zobacz Token dostępu użytkowników

Zastąp Quickstart User nazwą wyświetlaną, której chcesz użyć w Czacie.

Aby zachować stan, dodaj następujące zmienne do ContentView struktury:

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

Teraz dodaj główną treść var do przechowywania elementów interfejsu użytkownika. Dołączamy logikę biznesową do tych kontrolek. Dodaj następujący kod do ContentView struktury:

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

Inicjowanie obiektu ChatClient

Utwórz wystąpienie ChatClient i włącz powiadomienia o wiadomościach. Do odbierania wiadomości na czacie używamy powiadomień w czasie rzeczywistym.

Po skonfigurowaniu głównej struktury dodajmy funkcje do obsługi konfiguracji klientów do połączeń i czatów.

W funkcji onAppear dodaj następujący kod, aby zainicjować CallClient i ChatClient:

  if let threadIdFromMeetingLink = getThreadId(from: self.meetingLink) {
    self.chatThreadId = threadIdFromMeetingLink
  }
  // Authenticate
  do {
    let credentials = try CommunicationTokenCredential(token: token)
    self.callClient = CallClient()
    self.callClient?.createCallAgent(
      userCredential: credentials
    ) { agent, error in
      if let e = error {
        self.message = "ERROR: It was not possible to create a call agent."
        print(e)
        return
      } else {
        self.callAgent = agent
      }
    }
  
    // Start the chat client
    self.chatClient = try ChatClient(
      endpoint: endpoint,
      credential: credentials,
      withOptions: AzureCommunicationChatClientOptions()
    )
    // Register for real-time notifications
    self.chatClient?.startRealTimeNotifications { result in
      switch result {
      case .success:
        self.chatClient?.register(
          event: .chatMessageReceived,
          handler: receiveMessage
      )
      case let .failure(error):
        self.message = "Could not register for message notifications: " + error.localizedDescription
        print(error)
      }
    }
  } catch {
    print(error)
    self.message = error.localizedDescription
  }

Dodaj funkcję dołączania do spotkania

Dodaj następującą funkcję do struktury ContentView, aby obsłużyć uczestnictwo w spotkaniu.

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

Inicjowanie elementu ChatThreadClient

Inicjujemy ChatThreadClient po tym, jak użytkownik dołączy do spotkania. Następnie musimy sprawdzić status spotkania od delegata, a następnie zainicjować ChatThreadClient za pomocą threadId przy dołączeniu do spotkania.

connectChat() Utwórz funkcję przy użyciu następującego kodu:

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

Dodaj następującą funkcję pomocniczą do ContentView, używaną do analizowania identyfikatora wątku czatu z linku ze spotkania zespołu, jeśli jest to możliwe. W przypadku niepowodzenia wyodrębniania użytkownik musi ręcznie wprowadzić identyfikator wątku czatu przy użyciu interfejsów API programu Graph w celu pobrania identyfikatora wątku.

 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
}

Włączanie wysyłania komunikatów

Dodaj funkcję sendMessage() do ContentView. Ta funkcja używa elementu ChatThreadClient do wysyłania komunikatów od użytkownika.

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

Włączanie odbierania komunikatów

Aby odbierać komunikaty, implementujemy procedurę obsługi zdarzeń ChatMessageReceived . Po wysłaniu nowych komunikatów do wątku ta procedura obsługi dodaje komunikaty do meetingMessages zmiennej, aby mogły być wyświetlane w interfejsie użytkownika.

Najpierw dodaj następującą strukturę do ContentView.swift. Interfejs użytkownika używa danych w strukturze do wyświetlania naszych wiadomości na czacie.

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

Następnie dodaj receiveMessage() funkcję do ContentView. Gdy wystąpi zdarzenie komunikacyjne, wywołuje tę funkcję. Musisz zarejestrować się na wszystkie zdarzenia, które chcesz obsługiwać w instrukcji switch za pomocą metody chatClient?.register().

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

Na koniec musimy zaimplementować handler delegata dla klienta połączeń. Użyj tej obsługi, aby sprawdzić stan połączenia i zainicjować klienta czatu, gdy użytkownik dołącza do spotkania.

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

Opuść czat

Gdy użytkownik opuści spotkanie usługi Teams, wyczyścimy wiadomości czatu z interfejsu użytkownika i zawiesimy połączenie. Zapoznaj się z poniższym pełnym przykładem kodu.

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

Uzyskaj wątek czatu spotkania Teams dla użytkownika usługi Communication Services

Szczegóły spotkania usługi Teams można pobrać przy użyciu interfejsów API programu Graph, szczegółowo opisanych w dokumentacji programu Graph. SDK dla usług komunikacyjnych akceptuje pełny link do spotkania Teams lub identyfikator spotkania. Są one zwracane jako część onlineMeeting zasobu dostępnego w joinWebUrl ramach właściwości

Za pomocą interfejsów API Graph można również uzyskać threadID. Odpowiedź ma obiekt chatInfo zawierający threadID element.

Uruchamianie kodu

Uruchom aplikację.

Aby dołączyć do spotkania Teams, wprowadź link do spotkania zespołu w interfejsie użytkownika.

Po dołączeniu do spotkania zespołu musisz przyznać użytkownika do spotkania w aplikacji Teams. Gdy użytkownik zostanie przyjęty i dołączy do czatu, może wysyłać i odbierać wiadomości.

Zrzut ekranu przedstawiający ukończoną aplikację systemu iOS.

Uwaga

Niektóre funkcje nie są obecnie obsługiwane w scenariuszach współdziałania z usługą Teams. Aby uzyskać więcej informacji na temat obsługiwanych funkcji, zobacz Możliwości spotkań usługi Teams dla użytkowników zewnętrznych usługi Teams.

W tym artykule opisano sposób dodawania czatu spotkania usługi Teams do aplikacji przy użyciu zestawu SDK czatu usług Azure Communication Services dla systemu Android.

Przykładowy kod

Pobierz ten kod na stronie GitHub Azure Samples dołącz swoją aplikację czatu do spotkania Teams.

Wymagania wstępne

Włącz współdziałanie usługi Teams

Użytkownik usług komunikacyjnych, który dołącza do spotkania w Teams jako użytkownik-gość, może uzyskać dostęp do czatu spotkania dopiero po dołączeniu do rozmowy w ramach spotkania w Teams. Aby uzyskać więcej informacji na temat dodawania użytkownika Usług Komunikacyjnych do spotkania w usłudze Teams, zobacz Teams interop.

Aby korzystać z tej funkcji, musisz być członkiem organizacji, która jest właścicielem obu jednostek.

Dołączanie do czatu na spotkaniu

Po włączeniu interoperacyjności z Teams, użytkownik usług komunikacyjnych może dołączyć do wywołania w Teams jako użytkownik zewnętrzny przy użyciu SDK połączeń. Dołączenie do połączenia powoduje również dodanie ich jako uczestnika do czatu na spotkaniu. Z czatu mogą wysyłać i odbierać wiadomości z innymi użytkownikami podczas rozmowy. Użytkownik nie ma dostępu do wiadomości czatu, które zostały wysłane przed dołączeniem do połączenia. Aby umożliwić użytkownikom końcowym dołączanie do spotkań usługi Teams i rozpoczynanie rozmowy, wykonaj następujące kroki.

Dodaj czat do aplikacji Teams do rozmów

Na poziomie build.gradle modułu dodaj zależność od zestawu SDK czatu.

Ważne

Znany problem: Gdy używasz zestawu Android Chat i Calling SDK razem w tej samej aplikacji, funkcja powiadomień w czasie rzeczywistym zestawu SDK czatu nie działa. Generujesz problem z rozwiązaniem zależności. Podczas pracy nad rozwiązaniem możesz wyłączyć funkcję powiadomień w czasie rzeczywistym, dodając następujące wykluczenia do zależności zestawu Chat SDK w pliku aplikacji build.gradle :

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

Dodawanie układu interfejsu użytkownika usługi Teams

Zastąp kod w activity_main.xml poniższym fragmentem kodu. Dodaje dane wejściowe dla identyfikatora wątku i wysyłania wiadomości, przycisk wysyłania wpisanej wiadomości i podstawowy układ czatu.

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

Włącz kontrolki interfejsu użytkownika Teams

Importowanie pakietów i definiowanie zmiennych stanu

Do zawartości MainActivity.javapliku dodaj następujące importy:

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;

MainActivity W klasie dodaj następujące zmienne:

    // 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 corresponding to messages which have already been processed
    ArrayList<String> chatMessages = new ArrayList<>();

Zastąp <USER_ID> element identyfikatorem użytkownika inicjującego czat. Zastąp <COMMUNICATION_SERVICES_RESOURCE_ENDPOINT> punktem końcowym zasobu usługi komunikacyjnej.

Inicjalizacja ChatThreadClient

Po dołączeniu do spotkania utwórz instancję ChatThreadClient i wyświetl komponenty czatu.

Zaktualizuj koniec MainActivity.joinTeamsMeeting() metody przy użyciu następującego kodu:

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

Włączanie wysyłania komunikatów

Dodaj metodę sendMessage() do MainActivity. Używa ChatThreadClient do wysyłania wiadomości w imieniu użytkownika.

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

Włączanie sondowania komunikatów i renderowanie ich w aplikacji

Ważne

Znany problem: Funkcja powiadomień w czasie rzeczywistym w zestawie SDK czatu nie współdziała z zestawami SDK do połączeń, dlatego musisz odpytywać interfejs API w zdefiniowanych odstępach czasu. W tym przykładzie używamy 3-sekundowych interwałów.

Możemy uzyskać następujące dane z listy komunikatów zwróconych przez GetMessages interfejs API:

  • Komunikaty text i html w wątku od momentu dołączenia
  • Zmiany w składzie wątku
  • Aktualizacje tematu wątku

MainActivity Do klasy dodaj program obsługi i zadanie, które można uruchomić w 3-sekundowych interwałach:

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

Zadanie zostało już uruchomione na końcu metody MainActivity.joinTeamsMeeting(), zaktualizowanej w kroku inicjowania.

Na koniec dodamy metodę zapytań dotyczących wszystkich dostępnych wiadomości w wątku, analizowania ich według typu wiadomości i wyświetlania tych, które są oznaczone jako html i text.

    private void retrieveMessages() throws InterruptedException {
        // Initialize the list of messages not yet processed
        ArrayList<ChatMessage> newChatMessages = new ArrayList<>();
        
        // Retrieve all messages accessible to the user
        PagedAsyncStream<ChatMessage> messagePagedAsyncStream
                = this.chatThreadAsyncClient.listMessages(new ListChatMessagesOptions(), null);
        // Set up a lock to wait until all returned messages have been inspected
        CountDownLatch latch = new CountDownLatch(1);
        // Traverse the returned messages
        messagePagedAsyncStream.forEach(new AsyncStreamHandler<ChatMessage>() {
            @Override
            public void onNext(ChatMessage message) {
                // Messages that should be displayed in the chat
                if ((message.getType().equals(ChatMessageType.TEXT)
                    || message.getType().equals(ChatMessageType.HTML))
                    && !chatMessages.contains(message.getId())) {
                    newChatMessages.add(message);
                    chatMessages.add(message.getId());
                }
                if (message.getType().equals(ChatMessageType.PARTICIPANT_ADDED)) {
                    // Handle participants added to chat operation
                    List<ChatParticipant> participantsAdded = message.getContent().getParticipants();
                    CommunicationIdentifier participantsAddedBy = message.getContent().getInitiatorCommunicationIdentifier();
                }
                if (message.getType().equals(ChatMessageType.PARTICIPANT_REMOVED)) {
                    // Handle participants removed from chat operation
                    List<ChatParticipant> participantsRemoved = message.getContent().getParticipants();
                    CommunicationIdentifier participantsRemovedBy = message.getContent().getInitiatorCommunicationIdentifier();
                }
                if (message.getType().equals(ChatMessageType.TOPIC_UPDATED)) {
                    // Handle topic updated
                    String newTopic = message.getContent().getTopic();
                    CommunicationIdentifier topicUpdatedBy = message.getContent().getInitiatorCommunicationIdentifier();
                }
            }
            @Override
            public void onError(Throwable throwable) {
                latch.countDown();
            }
            @Override
            public void onComplete() {
                latch.countDown();
            }
        });
        // Wait until the operation completes
        latch.await(1, TimeUnit.MINUTES);
        // Returned messages should be ordered by the createdOn field to be guaranteed a proper chronological order
        // For the purpose of this demo we 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);
        }
    }

Klient usługi Teams nie ustawia nazw wyświetlanych uczestników wątku czatu. Nazwy są zwracane jako null w interfejsie API do listy uczestników, w participantsAdded zdarzeniu i w participantsRemoved zdarzeniu. Nazwy wyświetlane uczestników czatu można pobrać z remoteParticipants pola call obiektu.

Uzyskaj wątek czatu spotkania Teams dla użytkownika Communication Services

Szczegóły spotkania usługi Teams można pobrać przy użyciu interfejsów API programu Graph, szczegółowo opisanych w dokumentacji programu Graph. Zestaw SDK do wywoływania usług Communication Services akceptuje pełny link do spotkania Teams lub identyfikator spotkania. Są one zwracane jako część onlineMeeting zasobu dostępnego pod joinWebUrl właściwością

Za pomocą interfejsów API Graph można również uzyskać threadID. Odpowiedź ma obiekt chatInfo, który zawiera element threadID.

Uruchamianie kodu

Teraz możesz uruchomić aplikację z przycisku Uruchom aplikację na pasku narzędzi (Shift+F10).

Aby dołączyć do spotkania Teams i czatu, wprowadź link do spotkania zespołu i identyfikator wątku w interfejsie użytkownika.

Po dołączeniu do spotkania zespołu musisz zaakceptować użytkownika w aplikacji zespołu. Gdy użytkownik zostanie przyjęty i dołączy do czatu, może wysyłać i odbierać wiadomości.

Zrzut ekranu przedstawiający ukończoną aplikację systemu Android.

Uwaga

Niektóre funkcje nie są obecnie obsługiwane w scenariuszach współdziałania z usługą Teams. Aby uzyskać więcej informacji na temat obsługiwanych funkcji, zobacz Możliwości spotkań usługi Teams dla użytkowników zewnętrznych usługi Teams

W tym artykule opisano sposób czatu na spotkaniu w aplikacji Teams przy użyciu zestawu SDK czatu usług Azure Communication Services dla języka C#.

Przykładowy kod

Pobierz ten kod na stronie GitHub Azure Samples Join your chat app to a Teams meeting (Dołącz aplikację czatu do spotkania usługi Teams).

Wymagania wstępne

Dołączanie do czatu na spotkaniu

Użytkownik Communication Services może anonimowo dołączyć do spotkania w Teams przy użyciu Calling SDK. Dołączenie do spotkania powoduje również dodanie ich jako uczestnika do czatu spotkania. Na czacie mogą wysyłać i odbierać wiadomości z innymi użytkownikami na spotkaniu. Użytkownik nie ma dostępu do wiadomości czatu, które zostały wysłane przed dołączeniem do spotkania. Nie mogą wysyłać ani odbierać wiadomości po zakończeniu spotkania. Aby umożliwić użytkownikom dołączanie do spotkań usługi Teams i rozpoczynanie rozmowy, wykonaj następujące kroki.

Uruchamianie kodu

Możesz skompilować i uruchomić kod w programie Visual Studio. Obsługiwane platformy rozwiązań to: x64,x86 i ARM64.

  1. Otwórz wystąpienie programu PowerShell, programu Terminal Windows, wiersza polecenia lub równoważnego i przejdź do pożądanego katalogu, do którego chcesz skopiować przykład.

  2. git clone https://github.com/Azure-Samples/Communication-Services-dotnet-quickstarts.git

  3. Otwórz projekt ChatTeamsInteropQuickStart/ChatTeamsInteropQuickStart.csproj w programie Visual Studio.

  4. Zainstaluj następujące wersje pakietów NuGet (lub nowsze):

    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
    
    
  5. W przypadku zasobu usług komunikacyjnych pozyskanych w wymaganiach wstępnych dodaj parametry połączenia do pliku 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_ = "";
    

    Ważne

    • Wybierz odpowiednią platformę z listy rozwijanej Platformy rozwiązań w programie Visual Studio przed uruchomieniem kodu, na przykład x64
    • Upewnij się, że tryb dewelopera jest włączony w systemie Windows (ustawienia dewelopera)

    Następne kroki nie działają, jeśli platforma nie jest poprawnie skonfigurowana.

  6. Naciśnij F5 , aby uruchomić projekt w trybie debugowania.

  7. Wklej prawidłowy link do spotkania Microsoft Teams w polu Link do spotkania Teams (zobacz następną sekcję).

  8. Użytkownicy końcowi klikają pozycję Dołącz do spotkania w aplikacji Teams , aby rozpocząć rozmowę.

Ważne

Po nawiązaniu połączenia z zestawem SDK wywołującego spotkanie zespołów Zobacz Usługi komunikacyjne wywołujące aplikację systemu Windows kluczowe funkcje do obsługi operacji czatu to: StartPollingForChatMessages i SendMessageButton_Click. Oba fragmenty kodu znajdują się w ChatTeamsInteropQuickStart\MainPage.xaml.cs pliku

        /// <summary>
        /// Background task that keeps polling for chat messages while the call connection is established
        /// </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;
        }

Pobierz link do spotkania Teams przy użyciu Graph API, zgodnie z opisem w dokumentacji Graph. Ten link jest zwracany jako część zasobu onlineMeeting, dostępnego w ramach właściwości joinWebUrl.

Możesz również uzyskać link potrzebny do spotkania z URL 'Dołącz do spotkania' w zaproszeniu na spotkanie Teams.

Link do spotkania w Teams wygląda jak następujący adres URL:

https://teams.microsoft.com/l/meetup-join/meeting_chat_thread_id/1606337455313?context=some_context_here`.

Jeśli link do Teams ma inny format, musisz pobrać identyfikator wątku za pomocą Graph API.

Zrzut ekranu przedstawiający ukończoną aplikację csharp.

Uwaga

Niektóre funkcje nie są obecnie obsługiwane w scenariuszach współdziałania z usługą Teams. Aby uzyskać więcej informacji na temat obsługiwanych funkcji, zobacz Możliwości spotkań usługi Teams dla użytkowników zewnętrznych usługi Teams.

Czyszczenie zasobów

Jeśli chcesz wyczyścić i usunąć subskrypcję usług Komunikacyjnych, możesz usunąć zasób lub grupę zasobów. Usunięcie grupy zasobów powoduje również usunięcie wszelkich innych skojarzonych z nią zasobów. Dowiedz się więcej o czyszczeniu zasobów.