Sdílet prostřednictvím


Rychlý start: Připojení k chatovací aplikaci ke schůzce Teams

Začněte se službou Azure Communication Services tím, že připojíte chatovací řešení k Microsoft Teams.

Tento článek popisuje, jak chatovat ve schůzce Teams pomocí sady SDK chatu služby Azure Communication Services pro JavaScript.

Ukázka kódu

Stáhněte si tento kód na GitHubu v Azure Samples Propojte svou chatovací aplikaci se schůzkou Teams.

Požadavky

Připojení k chatu schůzky

Uživatel komunikačních služeb se může ke schůzce Teams připojit jako anonymní uživatel prostřednictvím sady SDK pro volání. Připojení ke schůzce je také přidá jako účastníka do chatu schůzky, kde můžou posílat a přijímat zprávy s ostatními uživateli schůzky. Uživatel nemá přístup ke zprávům chatu, které byly odeslány předtím, než se připojili ke schůzce, a po skončení schůzky nemůžou posílat ani přijímat zprávy. Pokud se chcete ke schůzce připojit a začít chatovat, můžete postupovat podle dalších kroků.

Vytvoření nové aplikace Node.js

Otevřete terminál nebo příkazové okno, vytvořte pro aplikaci nový adresář a přejděte na něj.

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

Spuštěním příkazu npm init -y vytvořte soubor package.json s výchozím nastavením.

npm init -y

Instalace chatovacích balíčků

npm install Pomocí příkazu nainstalujte potřebné sady SDK komunikačních služeb pro 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

Možnost --save uvádí knihovnu jako závislost v souboru package.json.

Nastavení architektury aplikace

Tato ukázka používá webpack k zabalení prostředků aplikace. Spuštěním následujícího příkazu nainstalujte balíčky webpack, webpack-cli a webpack-dev-server npm a uveďte je jako vývojové závislosti ve vašem package.json:

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

V kořenovém adresáři projektu vytvořte soubor index.html . Tento soubor používáme ke konfiguraci základního rozložení, které uživateli umožňuje připojit se ke schůzce a začít chatovat.

Přidání ovládacích prvků uživatelského rozhraní Teams

Nahraďte kód v index.html následujícím fragmentem kódu.

Pomocí textového pole v horní části stránky zadejte kontext schůzky Teams. Koncoví uživatelé se můžou ke zadané schůzce připojit kliknutím na tlačítko Připojit se ke schůzce Teams .

V dolní části stránky se zobrazí automaticky otevírané okno chatu. Koncoví uživatelé ho můžou použít k odesílání zpráv ve vlákně schůzky. Zobrazí se v reálném čase všechny zprávy odeslané ve vlákně, zatímco uživatel služby Communication Services je členem.

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

Povolení ovládacích prvků uživatelského rozhraní Teams

Obsah souboru client.js nahraďte následujícím fragmentem kódu.

V rámci fragmentu kódu nahraďte

  • SECRET_CONNECTION_STRINGs připojovacím řetězcem vaší komunikační služby
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 Teams nenastavuje zobrazované názvy účastníků vlákna chatu. Jména jsou vrácena jako null v rozhraní API pro výpis účastníků, v události participantsAdded a události participantsRemoved. Zobrazované jména účastníků chatu lze načíst z remoteParticipants pole objektu call . Při přijetí oznámení o změně seznamu můžete pomocí tohoto kódu načíst jméno uživatele, kterého jste přidali nebo odebrali:

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

Spuštění kódu

Použijte webpack-dev-server ke sestavení a spuštění vaší aplikace. Spuštěním následujícího příkazu zkompilujte hostitele aplikace na místním webovém serveru:

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

Otevřete prohlížeč a přejděte na http://localhost:8080/. Měla by se zobrazit spuštěná aplikace, jak je znázorněno na následujícím snímku obrazovky:

Snímek obrazovky dokončené javascriptové aplikace

Vložte odkaz na schůzku v Teams do textového pole. Uživatelé můžou kliknout na Připojit se ke schůzce Teams a připojit se ke schůzce Teams. Jakmile se uživatel Komunikační služby přihlásí ke schůzce, můžete chatovat z aplikace Communication Services. Přejděte do pole v dolní části stránky a začněte chatovat. Kvůli jednoduchosti aplikace zobrazuje pouze poslední dvě zprávy v chatu.

Poznámka:

Některé funkce se v současné době nepodporují ve scénářích interoperability s Teams. Další informace o podporovaných funkcích najdete v tématu Možnosti schůzek Teams pro externí uživatele Teams.

Tento článek popisuje, jak chatovat ve schůzce Teams pomocí sady SDK chatu služby Azure Communication Services pro iOS.

Ukázka kódu

Stáhněte si tento kód na GitHub Azure Samples přidejte vaši chatovací aplikaci ke schůzce Teams.

Požadavky

  • Účet Azure s aktivním předplatným. Vytvoření účtu zdarma
  • Mac se systémem Xcode spolu s platným certifikátem vývojáře nainstalovaným do klíčenky.
  • Nasazení systému Teams.
  • Přístupový token uživatele pro vaši službu Azure Communication Service. K vytvoření uživatele a přístupového tokenu můžete také použít Azure CLI a spustit příkaz se svým připojovacím řetězcem.
az communication user-identity token issue --scope voip chat --connection-string "yourConnectionString"

Podrobnosti najdete v tématu Použití Azure CLI k vytváření a správě přístupových tokenů.

Nastavení

Vytvoření projektu Xcode

V Xcode vytvořte nový projekt pro iOS a vyberte šablonu aplikace s jedním zobrazením. Tento kurz používá architekturu SwiftUI, takže byste měli nastavit jazyk na Swift a uživatelské rozhraní na SwiftUI. Během tohoto rychlého startu nebudete vytvářet testy. Nebojte se zrušit zaškrtnutí políčka Zahrnout testy.

Snímek obrazovky s oknem Nový projekt v Xcode

Instalace CocoaPods

Tento průvodce použijte k instalaci CocoaPods na Mac.

Instalace balíčku a závislostí pomocí CocoaPods

  1. Pokud chcete vytvořit Podfile aplikaci, otevřete terminál a přejděte do složky projektu a spusťte inicializaci podu.

  2. Do cíle přidejte následující kód Podfile a uložte ho.

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. Spusťte pod install.

  2. .xcworkspace Otevřete soubor pomocí Xcode.

Vyžádání přístupu k mikrofonu

Pokud chcete získat přístup k mikrofonu zařízení, musíte aktualizovat seznam vlastností informací aplikace pomocí funkce NSMicrophoneUsageDescription. Nastavíte přidruženou string hodnotu na hodnotu, která byla zahrnuta v dialogovém okně, který systém používá k vyžádání přístupu od uživatele.

Pod cílem vyberte Info kartu a přidejte řetězec pro Privacy - Microphone Usage Description.

Snímek obrazovky znázorňující přidání využití mikrofonu v Xcode

Zakázání sandboxu uživatelských skriptů

Některé skripty v propojených knihovnách zapisují soubory během procesu sestavení. Pokud chcete povolit zápis souborů, zakažte sandboxing uživatelských skriptů v Xcode.

V nastavení sestavení vyhledejte sandbox a nastavte User Script Sandboxing hodnotu No.

Snímek obrazovky znázorňující zakázání sandboxu uživatelských skriptů v Xcode

Připojení k chatu schůzky

Uživatel komunikačních služeb se může ke schůzce Teams připojit jako anonymní uživatel pomocí sady SDK pro volání. Jakmile se uživatel připojí ke schůzce Teams, může posílat a přijímat zprávy s ostatními účastníky schůzky. Uživatel nemá přístup k chatovým zprávům odeslaných před připojením ani nemůže posílat ani přijímat zprávy, když nejsou ve schůzce.

Pokud se chcete připojit ke schůzce a začít chatovat, postupujte podle dalších kroků.

Nastavení architektury aplikace

Importujte balíčky Azure Communication ve ContentView.swift přidáním následujícího úryvku kódu:

import AVFoundation
import SwiftUI

import AzureCommunicationCalling
import AzureCommunicationChat

V ContentView.swift přidejte následující fragment těsně nad deklaraci struct ContentView: View:

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

Nahraďte <ADD_YOUR_ENDPOINT_URL_HERE> koncovým bodem vašeho prostředku komunikačních služeb. Nahraďte <ADD_YOUR_USER_TOKEN_HERE> dříve vygenerovaným tokenem prostřednictvím příkazového řádku klienta Azure.

Další informace najdete v tématu Přístupový token uživatele.

Nahraďte Quickstart User zobrazovaným jménem, které chcete použít v chatu.

Pokud chcete tento stav uchovávat, přidejte do ContentView struktury následující proměnné:

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

Teď přidejte hlavní text var pro uložení prvků uživatelského rozhraní. K těmto ovládacím prvkům připojíme obchodní logiku. Do struktury přidejte následující kód ContentView :

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

Inicializujte ChatClient

Inicializujte ChatClient a povolte notifikace zpráv. Pro příjem chatových zpráv používáme oznámení v reálném čase.

S nastavením hlavního těla přidáme funkce pro zpracování nastavení klientů hovorů a chatu.

Ve funkci onAppear přidejte následující kód k inicializaci CallClient a 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
  }

Přidání funkce pro připojení ke schůzce

Přidejte následující funkci do ContentView struktury pro umožnění připojení ke schůzce.

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

Inicializovat ChatThreadClient

Inicializujeme ChatThreadClient poté, co se uživatel připojí ke schůzce. Pak musíme zkontrolovat stav schůzky podle informací od delegáta a pak inicializovat ChatThreadClient s threadId při připojení ke schůzce.

connectChat() Vytvořte funkci s následujícím kódem:

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

Pokud je to možné, přidejte následující pomocnou funkci k ContentView pro analýzu ID vlákna chatu z odkazu na schůzku týmu. V případě, že tato extrakce selže, musí uživatel ručně zadat ID vlákna chatu pomocí rozhraní Graph API k načtení ID vlákna.

 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
}

Povolení odesílání zpráv

sendMessage() Přidejte funkci do ContentView. Tato funkce používá ChatThreadClient k odesílání zpráv od uživatele.

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

Povolení příjmu zpráv

Abychom mohli přijímat zprávy, implementujeme obslužnou rutinu pro ChatMessageReceived události. Když se do vlákna odešlou nové zprávy, tato obslužná rutina přidá zprávy do meetingMessages proměnné, aby je bylo možné zobrazit v uživatelském rozhraní.

Nejprve přidejte následující strukturu do ContentView.swift. Uživatelské rozhraní používá data ve struktuře k zobrazení zpráv chatu.

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

Dále přidejte receiveMessage() funkci do ContentView. Když dojde k události zasílání zpráv, volá tuto funkci. Musíte zaregistrovat všechny události, které chcete zpracovat v switch příkazu prostřednictvím chatClient?.register() metody.

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

Nakonec musíme implementovat delegovaného správce pro volacího klienta. Pomocí této obslužné rutiny můžete zkontrolovat stav hovoru a inicializovat chatovacího klienta, když se uživatel připojí ke schůzce.

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štění chatu

Když uživatel opustí schůzku Teams, vymažeme zprávy chatu z uživatelského rozhraní a zavěsíme hovor. Podívejte se na následující úplný příklad kódu.

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

Získejte vlákno chatu ze schůzky na Teams pro uživatele Komunikačních služeb.

Podrobnosti o schůzce v Teams je možné načíst pomocí rozhraní Graph API, která jsou podrobně popsána v dokumentaci k Graphu. Sada SDK pro volání komunikačních služeb přijímá úplný odkaz na schůzku v Teams nebo ID schůzky. Vrací se jako součást onlineMeeting zdroje, který je přístupný prostřednictvím joinWebUrl vlastnosti.

Pomocí rozhraní Graph APIs můžete získat také threadID. Odpověď má chatInfo objekt, který obsahuje threadID.

Spuštění kódu

Aplikaci spusťte.

Pokud se chcete připojit ke schůzce Teams, zadejte do uživatelského rozhraní odkaz na schůzku vašeho týmu.

Po připojení ke schůzce týmu musíte uživateli povolit schůzku v klientovi vašeho týmu. Jakmile je uživatel přijat a připojí se k chatu, může posílat a přijímat zprávy.

Snímek obrazovky dokončené aplikace pro iOS

Poznámka:

Některé funkce se v současné době nepodporují ve scénářích interoperability s Teams. Další informace o podporovaných funkcích najdete v tématu Možnosti schůzek Teams pro externí uživatele Teams.

Tento článek popisuje, jak do aplikace přidat chat schůzky Teams pomocí sady SDK pro chat služby Azure Communication Services pro Android.

Ukázka kódu

Stáhněte si tento kód na GitHubu Azure Samples Propojte svou chatovací aplikaci se schůzkou v Teams.

Požadavky

Povolení interoperability pro Teams

Uživatel komunikačních služeb, který se připojí ke schůzce Teams jako uživatel typu host, má přístup k chatu schůzky až po připojení ke schůzce Teams. Další informace o tom, jak přidat uživatele Komunikační služby do hovoru na schůzku v Teams, najdete v tématu Spolupráce v Teams.

Abyste mohli tuto funkci používat, musíte být členem vlastnící organizace obou entit.

Připojení k chatu schůzky

Jakmile povolíte interoperabilitu Teams, může se uživatel komunikačních služeb připojit k volání Teams jako externí uživatel pomocí sady SDK pro volání. Připojení k hovoru je také přidá jako účastníka do chatu schůzky. Z chatu můžou posílat a přijímat zprávy s ostatními uživateli hovoru. Uživatel nemá přístup ke zprávům chatu, které byly odeslány předtím, než se připojil k hovoru. Pokud chcete koncovým uživatelům povolit, aby se připojili ke schůzkám Teams a mohli začít chatovat, proveďte následující kroky.

Přidání chatu do volající aplikace Teams

Na build.gradle úrovni modulu přidejte závislost na chatové knihovně SDK.

Důležité

Známý problém: Pokud používáte SDK pro chat a volání na Androidu společně v jedné aplikaci, funkce oznámení v reálném čase SDK pro chat nefunguje. Vygenerujete problém s řešením závislostí. Zatímco pracujeme na řešení, můžete funkci oznámení v reálném čase vypnout přidáním následujících vyloučení do závislosti sady Chat SDK v souboru aplikace build.gradle :

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

Přidání rozložení uživatelského rozhraní Teams

Nahraďte kód v activity_main.xml následujícím fragmentem kódu. Přidá vstupy pro ID vlákna a pro odesílání zpráv, tlačítko pro odeslání zadané zprávy a základní rozložení chatu.

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

Povolení ovládacích prvků uživatelského rozhraní Teams

Import balíčků a definování stavových proměnných

Do obsahu MainActivity.javapřidejte následující 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 Do třídy přidejte následující proměnné:

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

Nahraďte <USER_ID> ID uživatele, který chat iniciuje. Nahraďte <COMMUNICATION_SERVICES_RESOURCE_ENDPOINT> koncovým bodem vašeho prostředku komunikačních služeb.

Inicializujte ChatThreadClient

Po připojení ke schůzce vytvořte ChatThreadClient instanci a zviditelněte komponenty chatu.

Aktualizujte konec MainActivity.joinTeamsMeeting() metody následujícím kódem:

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

Povolení odesílání zpráv

Přidejte metodu sendMessage() do MainActivity. Používá ChatThreadClient k odesílání zpráv jménem uživatele.

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

Povolení dotazování na zprávy a jejich vykreslení v aplikaci

Důležité

Známý problém: Vzhledem k tomu, že funkce oznámení sady SDK chatu v reálném čase nefunguje společně s volajícími sadami SDK, musíte se na rozhraní API dotazovat GetMessages v předdefinovaných intervalech. V této ukázce používáme 3sekundové intervaly.

Ze seznamu zpráv vrácených rozhraním GetMessages API můžeme získat následující data:

  • Zprávy text a html zprávy ve vlákně od připojení
  • Změny seznamu vláken
  • Aktualizace tématu vlákna

MainActivity Do třídy přidejte obslužnou rutinu a spustitelnou úlohu, která se spouští v 3sekundových intervalech:

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

Úloha již byla zahájena na konci MainActivity.joinTeamsMeeting() metody aktualizované v inicializačním kroku.

Nakonec přidáme metodu pro dotazování všech přístupných zpráv ve vlákně, parsování html podle typu zprávy a zobrazení těchto text zpráv:

    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 Teams nenastavuje zobrazované názvy účastníků vlákna chatu. Názvy se vrátí jako 'null' v API rozhraní pro seznam účastníků, a to jak v události participantsAdded, tak v události participantsRemoved. Zobrazované jména účastníků chatu lze načíst z remoteParticipants pole objektu call .

Získejte vlákno chatu schůzky v Teams pro uživatele komunikačních služeb

Podrobnosti o schůzce v Teams je možné načíst pomocí rozhraní Graph API, která jsou podrobně popsána v dokumentaci k Graphu. Sada SDK pro volání komunikačních služeb přijímá úplný odkaz na schůzku v Teams nebo ID schůzky. Jsou vráceny jako součást prostředku onlineMeeting, který je přístupný v rámci vlastnosti joinWebUrl.

Pomocí rozhraní Graph API můžete také získat threadID. Odpověď má chatInfo objekt, který obsahuje threadID.

Spuštění kódu

Aplikaci teď můžete spustit z tlačítka Spustit aplikaci na panelu nástrojů (Shift+F10).

Pokud se chcete připojit ke schůzce a chatu Teams, zadejte odkaz na schůzku vašeho týmu a ID vlákna v uživatelském rozhraní.

Po připojení ke schůzce týmu musíte uživateli povolit schůzku v klientovi vašeho týmu. Jakmile je uživatel přijat a připojí se k chatu, může posílat a přijímat zprávy.

Snímek obrazovky dokončené aplikace pro Android

Poznámka:

Některé funkce se v současné době nepodporují ve scénářích interoperability s Teams. Další informace o podporovaných funkcích najdete v tématu Možnosti schůzek v Teams pro externí uživatele Teams.

Tento článek popisuje, jak chatovat ve schůzce Teams pomocí sady SDK chatu služby Azure Communication Services pro jazyk C#.

Ukázkový kód

Stáhněte si tento kód z Azure Samples na GitHubu: Připojte svou chatovací aplikaci ke schůzce na Teams.

Požadavky

Připojení k chatu schůzky

Uživatel komunikačních služeb se může k schůzce Teams připojit anonymně pomocí sady SDK pro volání. Připojení ke schůzce je také přidá jako účastníka do chatu schůzky. V chatu můžou posílat a přijímat zprávy s ostatními uživateli ve schůzce. Uživatel nemá přístup ke zprávům chatu, které byly odeslány předtím, než se ke schůzce připojil. Po skončení schůzky nemůžou odesílat ani přijímat zprávy. Pokud chcete uživatelům umožnit, aby se připojili ke schůzkám Teams a mohli začít chatovat, proveďte následující kroky.

Spuštění kódu

Kód můžete sestavit a spustit v programu Visual Studio. Podporované platformy řešení jsou: x64,x86 a ARM64.

  1. Otevřete instanci PowerShellu, Terminál Windows, příkazového řádku nebo ekvivalent a přejděte do adresáře, do kterého chcete ukázku naklonovat.

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

  3. Otevřete projekt ChatTeamsInteropQuickStart/ChatTeamsInteropQuickStart.csproj ve Visual Studiu.

  4. Nainstalujte následující verze balíčků NuGet (nebo vyšší):

    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. Po zakoupení prostředku Komunikační služby v rámci předpokladů, přidejte připojovací řetězec do souboru 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_ = "";
    

    Důležité

    • Před spuštěním kódu vyberte v rozevíracím seznamu Platformy řešení v sadě Visual Studio správnou platformu.x64
    • Ujistěte se, že ve Windows povolíte režim vývojáře(nastavení pro vývojáře).

    Další kroky nefungují, pokud není platforma správně nakonfigurovaná.

  6. Stisknutím klávesy F5 spusťte projekt v režimu ladění.

  7. Vložte platný odkaz na schůzku teams do pole Odkaz na schůzku Teams (viz další část).

  8. Koncoví uživatelé kliknou na Připojit se ke schůzce Teams a začnou chatovat.

Důležité

Jakmile sada SDK pro volání vytvoří spojení s týmovou schůzkou Komunikační služby pro volání aplikace Windows, jsou klíčové funkce pro zpracování operací chatu: StartPollingForChatMessages a SendMessageButton_Click. Oba fragmenty kódu jsou v ChatTeamsInteropQuickStart\MainPage.xaml.cs souboru.

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

Načtěte odkaz na schůzku Teams pomocí rozhraní Graph API, jak je popsáno v dokumentaci k Graphu. Tento odkaz se vrátí jako součást onlineMeeting prostředku, který je přístupný v rámci joinWebUrl vlastnosti.

Požadovaný odkaz na schůzku můžete získat také z adresy URL pro připojení ke schůzce v samotné pozvánce na schůzku v Teams.

Odkaz na schůzku v Teams vypadá jako následující adresa URL:

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

Pokud má odkaz Teams jiný formát, musíte načíst ID vlákna pomocí rozhraní Graph API.

Snímek obrazovky dokončené aplikace csharp

Poznámka:

Některé funkce se v současné době nepodporují ve scénářích interoperability s Teams. Další informace o podporovaných funkcích najdete v tématu Možnosti schůzek Teams pro externí uživatele Teams.

Vyčištění prostředků

Pokud chcete vyčistit a odebrat předplatné služby Communication Services, můžete odstranit prostředek nebo skupinu prostředků. Odstraněním skupiny prostředků se odstraní také všechny ostatní prostředky, které jsou k ní přidružené. Přečtěte si další informace o čištění prostředků.