Mulai Cepat: Bergabung menggunakan aplikasi obrolan Anda ke rapat Teams

Mulai menggunakan Azure Communication Services dengan menyambungkan solusi obrolan Anda ke Microsoft Teams.

Dalam mulai cepat ini, Anda akan mempelajari cara melakukan obrolan dalam rapat Teams menggunakan Azure Communication Services Chat SDK untuk JavaScript.

Kode Sampel

Menemukan kode final untuk mulai cepat ini di GitHub.

Prasyarat

Bergabung dalam obrolan rapat

Pengguna Communication Services dapat bergabung dengan rapat tim sebagai pengguna anonim menggunakan SDK Panggilan. Bergabung dalam rapat menambahkan mereka sebagai peserta ke obrolan rapat juga, di mana mereka bisa mengirim dan menerima pesan dengan pengguna lain dalam rapat. Pengguna tidak akan memiliki akses ke pesan obrolan yang dikirim sebelum mereka bergabung dalam rapat dan mereka tidak akan dapat mengirim atau menerima pesan setelah rapat berakhir. Anda dapat mengikuti langkah-langkah berikut untuk bergabung dalam rapat dan memulai obrolan.

Membuat aplikasi Node.js baru

Buka terminal atau jendela perintah, buat direktori baru untuk aplikasi Anda, dan navigasi ke sana.

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

Jalankan npm init -y untuk membuat file package.json dengan pengaturan default.

npm init -y

Memasang paket obrolan

Gunakan perintah npm install untuk menginstal Communication Services SDK yang diperlukan 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

Opsi --save mencantumkan pustaka sebagai dependensi di file package.json Anda.

Menyiapkan kerangka kerja aplikasi

Mulai cepat ini menggunakan Webpack untuk memaketkan aset aplikasi. Jalankan perintah berikut untuk menginstal paket npm Webpack, webpack-cli, dan webpack-dev-server dan cantumkan sebagai dependensi pengembangan di package.json Anda:

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

Buat file index.html di direktori akar proyek Anda. Kami menggunakan file ini untuk mengonfigurasi tata letak dasar yang akan memungkinkan pengguna bergabung dalam rapat dan mulai mengobrol.

Menambahkan kontrol UI Teams

Ganti kode di index.html dengan cuplikan berikut. Kotak teks di bagian atas halaman akan digunakan untuk memasukkan konteks rapat Teams. Tombol 'Gabung Rapat Teams' digunakan untuk bergabung dalam rapat yang ditentukan. Pop-up obrolan muncul di bagian bawah halaman. Ini dapat digunakan untuk mengirim pesan di utas rapat, dan ditampilkan secara real time setiap pesan yang dikirim di utas saat pengguna Communication Services adalah anggota.

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

Mengaktifkan kontrol Teams UI

Ganti isi file client.js dengan cuplikan berikut.

Dalam cuplikan kode, ganti

  • SECRET_CONNECTION_STRING dengan string koneksi Communication Service Anda
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}`);
});

Nama tampilan peserta utas obrolan tidak diatur oleh klien Teams. Nama dikembalikan sebagai null di API untuk mencantumkan peserta, dalam participantsAdded peristiwa dan dalam participantsRemoved peristiwa. Nama tampilan peserta obrolan dapat diambil dari bidang remoteParticipants dari objek call. Saat menerima pemberitahuan tentang perubahan daftar, Anda dapat menggunakan kode ini untuk mengambil nama pengguna yang telah ditambahkan atau dihapus:

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

Menjalankan kode

Pengguna Webpack dapat menggunakan webpack-dev-server untuk membuat dan menjalankan aplikasi Anda. Jalankan perintah berikut untuk memaketkan host aplikasi di webserver lokal:

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

Buka browser Anda dan buka http://localhost:8080/. Anda akan melihat aplikasi diluncurkan seperti yang ditunjukkan pada cuplikan layar berikut:

Cuplikan layar Aplikasi JavaScript yang telah selesai.

Sisipkan tautan rapat Teams ke dalam kotak teks. Tekan Ikut Rapat Teams untuk bergabung dalam rapat Teams. Setelah pengguna Communication Services diterima dalam rapat, Anda bisa melakukan obrolan dari aplikasi Communication Services Anda. Navigasi ke kotak di bagian bawah halaman untuk memulai obrolan. Untuk kesederhanaan, aplikasi hanya menunjukkan dua pesan terakhir dalam obrolan.

Catatan

Fitur tertentu saat ini tidak didukung untuk skenario interoperabilitas dengan Teams. Pelajari selengkapnya tentang fitur yang didukung, silakan lihat Kemampuan rapat Teams untuk pengguna eksternal Teams

Dalam mulai cepat ini, Anda akan mempelajari cara mengobrol dalam rapat Teams menggunakan SDK Azure Communication Services Chat untuk iOS.

Kode Sampel

Jika Anda ingin melompati ke bagian akhir, Anda dapat mengunduh mulai cepat ini sebagai sampel di GitHub.

Prasyarat

  • Akun Azure dengan langganan aktif. Membuat akun secara gratis
  • Mac yang menjalankan Xcode, bersama dengan sertifikat pengembang valid yang diinstal ke rantai kunci Anda.
  • Penyebaran Teams.
  • Token Akses Pengguna untuk Azure Communication Service. Anda juga dapat menggunakan Azure CLI dan menjalankan perintah dengan string koneksi Anda untuk membuat pengguna dan token akses.
az communication identity token issue --scope voip --connection-string "yourConnectionString"

Untuk detailnya, lihat Menggunakan Azure CLI untuk Membuat dan Mengelola Token Akses.

Menyiapkan

Membuat proyek Xcode

Di Xcode, buat proyek iOS baru dan pilih templat Aplikasi Tampilan Tunggal. Tutorial ini menggunakan kerangka kerja SwiftUI, jadi Anda harus mengatur Bahasa ke Swift dan Antarmuka Pengguna ke SwiftUI. Anda tidak akan membuat tes selama mulai cepat ini. Jangan ragu untuk menghapus centang Sertakan Pengujian.

Cuplikan layar yang menunjukkan jendela Proyek Baru dalam Xcode.

Menginstal CocoaPods

Gunakan panduan ini untuk menginstal CocoaPods di Mac Anda.

Memasang paket dan dependensi dengan CocoaPods

  1. Untuk membuat Podfile untuk aplikasi Anda, buka terminal dan navigasikan ke folder proyek dan jalankan init pod.

  2. Tambahkan kode berikut ke Podfile di bawah target, dan simpan.

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. Jalankan pod install.

  2. .xcworkspace Buka file dengan Xcode.

Minta akses ke mikrofon

Untuk mengakses mikrofon perangkat, Anda perlu memperbarui Daftar Properti Informasi aplikasi Anda dengan NSMicrophoneUsageDescription. Anda mengatur nilai terkait ke string yang disertakan dalam dialog yang digunakan sistem untuk meminta akses dari pengguna.

Di bawah target, pilih tab Info dan tambahkan string untuk 'Privasi - Deskripsi Penggunaan Mikrofon'

Cuplikan layar memperlihatkan penambahan penggunaan mikrofon dalam Xcode.

Menonaktifkan Sandboxing Skrip Pengguna

Beberapa skrip dalam pustaka tertaut menulis file selama proses build. Untuk mengizinkan ini, nonaktifkan Sandboxing Skrip Pengguna di Xcode. Di bawah pengaturan build, cari sandbox dan atur User Script Sandboxing ke No.

Cuplikan layar memperlihatkan menonaktifkan sandboxing skrip pengguna dalam Xcode.

Bergabung dalam obrolan rapat

Pengguna Communication Services dapat bergabung dengan rapat tim sebagai pengguna anonim menggunakan SDK Panggilan. Setelah pengguna bergabung dalam rapat Teams, mereka dapat mengirim dan menerima pesan dengan peserta rapat lainnya. Pengguna tidak akan memiliki akses ke pesan obrolan yang dikirim sebelum bergabung, mereka juga tidak akan dapat mengirim atau menerima pesan saat mereka tidak berada dalam rapat. Anda dapat mengikuti langkah-langkah berikut untuk bergabung dalam rapat dan memulai obrolan.

Menyiapkan kerangka kerja aplikasi

Impor paket Azure Communication dengan ContentView.swift menambahkan cuplikan berikut:

import AVFoundation
import SwiftUI

import AzureCommunicationCalling
import AzureCommunicationChat

Dalam ContentView.swift tambahkan cuplikan berikut, tepat di struct ContentView: View atas deklarasi:

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

Ganti <ADD_YOUR_ENDPOINT_URL_HERE> dengan titik akhir untuk sumber daya Communication Services Anda. Ganti <ADD_YOUR_USER_TOKEN_HERE> dengan token yang dihasilkan di atas, melalui baris perintah klien Azure. Baca selengkapnya tentang token akses pengguna: Token Akses Pengguna

Ganti Quickstart User dengan nama tampilan yang ingin Anda gunakan di Chat.

Untuk menahan status, tambahkan variabel berikut ke ContentView struct:

  @State var message: String = ""
  @State var meetingLink: String = ""
  @State var chatThreadId: String = ""

  // Calling state
  @State var callClient: CallClient?
  @State var callObserver: CallDelegate?
  @State var callAgent: CallAgent?
  @State var call: Call?

  // Chat state
  @State var chatClient: ChatClient?
  @State var chatThreadClient: ChatThreadClient?
  @State var chatMessage: String = ""
  @State var meetingMessages: [MeetingMessage] = []

Sekarang mari kita tambahkan var tubuh utama untuk menahan elemen UI. Kami melampirkan logika bisnis ke kontrol ini dalam mulai cepat ini. Tambahkan kode berikut ke ContentView struct:

var body: some View {
    NavigationView {
      Form {
        Section {
          TextField("Teams Meeting URL", text: $meetingLink)
            .onChange(of: self.meetingLink, perform: { value in
              if let threadIdFromMeetingLink = getThreadId(from: value) {
                self.chatThreadId = threadIdFromMeetingLink
              }
            })
          TextField("Chat thread ID", text: $chatThreadId)
        }
        Section {
          HStack {
            Button(action: joinMeeting) {
              Text("Join Meeting")
            }.disabled(
              chatThreadId.isEmpty || callAgent == nil || call != nil
            )
            Spacer()
            Button(action: leaveMeeting) {
              Text("Leave Meeting")
            }.disabled(call == nil)
          }
          Text(message)
        }
        Section {
          ForEach(meetingMessages, id: \.id) { message in
            let currentUser: Bool = (message.displayName == displayName)
            let foregroundColor = currentUser ? Color.white : Color.black
            let background = currentUser ? Color.blue : Color(.systemGray6)
            let alignment = currentUser ? HorizontalAlignment.trailing : .leading
            
            HStack {
              if currentUser {
                Spacer()
              }
              VStack(alignment: alignment) {
                Text(message.displayName).font(Font.system(size: 10))
                Text(message.content)
                  .frame(maxWidth: 200)
              }

              .padding(8)
              .foregroundColor(foregroundColor)
              .background(background)
              .cornerRadius(8)

              if !currentUser {
                Spacer()
              }
            }
          }
          .frame(maxWidth: .infinity)
        }

        TextField("Enter your message...", text: $chatMessage)
        Button(action: sendMessage) {
          Text("Send Message")
        }.disabled(chatThreadClient == nil)
      }

      .navigationBarTitle("Teams Chat Interop")
    }

    .onAppear {
      // Handle initialization of the call and chat clients
    }
  }

Inisialisasi ChatClient

Buat instans dan aktifkan ChatClient pemberitahuan pesan. Kami menggunakan pemberitahuan real-time untuk menerima pesan obrolan.

Dengan penyiapan isi utama, mari kita tambahkan fungsi untuk menangani penyiapan klien panggilan dan obrolan.

onAppear Dalam fungsi , tambahkan kode berikut untuk menginisialisasi CallClient dan 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
  }

Menambahkan fungsi gabungan rapat

Tambahkan fungsi berikut ke ContentView struct untuk menangani bergabung dalam rapat.

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

Menginisialisasi ChatThreadClient

Kami akan menginisialisasi ChatThreadClient setelah pengguna bergabung dalam rapat. Ini mengharuskan kita untuk memeriksa status rapat dari delegasi dan kemudian menginisialisasi ChatThreadClient dengan threadId ketika bergabung ke rapat.

connectChat() Buat fungsi dengan kode berikut:

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

Tambahkan fungsi pembantu berikut ke ContentView, yang digunakan untuk mengurai ID utas Obrolan dari tautan rapat Tim, jika memungkinkan. Jika ekstraksi ini gagal, pengguna harus memasukkan ID utas Obrolan secara manual menggunakan API Graph untuk mengambil ID utas.

 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
}

Aktifkan pengiriman pesan

Tambahkan fungsi sendMessage() ke ContentView. Fungsi ini menggunakan ChatThreadClient untuk mengirim pesan dari pengguna.

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

Aktifkan penerimaan pesan

Untuk menerima pesan, kami menerapkan handler untuk ChatMessageReceived peristiwa. Ketika pesan baru dikirim ke utas, handler ini menambahkan pesan ke meetingMessages variabel sehingga dapat ditampilkan di UI.

Pertama tambahkan struktur berikut ke ContentView.swift. UI menggunakan data dalam struktur untuk menampilkan pesan Obrolan kami.

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

Selanjutnya tambahkan fungsi receiveMessage() ke ContentView. Ini disebut ketika peristiwa olahpesan terjadi. Perhatikan bahwa Anda perlu mendaftar untuk semua peristiwa yang ingin Anda tangani dalam switch pernyataan melalui chatClient?.register() metode .

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

Terakhir, kita perlu mengimplementasikan penangan delegasi untuk klien panggilan. Handler ini digunakan untuk memeriksa status panggilan dan menginisialisasi klien obrolan saat pengguna bergabung dalam rapat.

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

Tinggalkan obrolan

Saat pengguna keluar dari rapat Tim, kami menghapus pesan Obrolan dari UI dan menutup panggilan. Kode lengkap ditampilkan di bawah ini.

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

Mendapatkan utas obrolan rapat Teams untuk pengguna Communication Services

Detail rapat Teams dapat diambil menggunakan Graph API, yang diperinci dalam dokumentasi Graph. SDK Panggilan Communication Services menerima tautan rapat Teams lengkap atau ID rapat. Mereka dikembalikan sebagai bagian onlineMeeting dari sumber daya, dapat diakses di joinWebUrl bawah properti

Dengan Graph API, Anda juga bisa mendapatkan threadID. Respons memiliki chatInfo objek yang berisi threadID.

Menjalankan kode

Jalankan aplikasi lagi.

Untuk bergabung dalam rapat Teams, masukkan tautan rapat Tim Anda di UI.

Setelah bergabung dalam rapat Tim, Anda perlu memasukkan pengguna ke rapat di klien Tim Anda. Setelah pengguna diterima dan telah bergabung dengan obrolan, Anda dapat mengirim dan menerima pesan.

Cuplikan layar Aplikasi iOS yang telah selesai.

Catatan

Fitur tertentu saat ini tidak didukung untuk skenario interoperabilitas dengan Teams. Pelajari selengkapnya tentang fitur yang didukung, silakan lihat Kemampuan rapat Teams untuk pengguna eksternal Teams

Dalam mulai cepat ini, Anda akan mempelajari cara mengobrol dalam rapat Teams menggunakan SDK Azure Communication Services Chat untuk Android.

Kode Sampel

Jika Anda ingin melompati ke bagian akhir, Anda dapat mengunduh mulai cepat ini sebagai sampel di GitHub.

Prasyarat

Aktifkan interoperabilitas Teams

Pengguna Communication Services yang bergabung dalam rapat Teams sebagai pengguna tamu hanya dapat mengakses obrolan rapat saat mereka telah bergabung dalam panggilan rapat Teams. Lihat dokumentasi interoperabilitas Teams untuk mempelajari cara menambahkan pengguna Communication Services ke panggilan rapat Teams.

Anda harus menjadi anggota organisasi pemilik dari kedua entitas untuk menggunakan fitur ini.

Bergabung dalam obrolan rapat

Setelah interoperabilitas Teams diaktifkan, pengguna Communication Services dapat bergabung dengan panggilan Teams sebagai pengguna eksternal menggunakan Calling SDK. Bergabung dengan panggilan menambahkan mereka sebagai peserta ke obrolan rapat juga, di mana mereka bisa mengirim dan menerima pesan dengan pengguna lain pada panggilan. Pengguna tidak memiliki akses ke pesan obrolan yang dikirim sebelum mereka bergabung dalam panggilan. Anda dapat mengikuti langkah-langkah berikut untuk bergabung dalam rapat dan memulai obrolan.

Menambahkan Obrolan ke dalam aplikasi panggilan Teams

Di tingkat build.gradlemodul Anda , tambahkan dependensi pada SDK obrolan.

Penting

Masalah yang diketahui: Saat menggunakan Android Chat dan Calling SDK secara bersamaan dalam aplikasi yang sama, fitur notifikasi waktu nyata Chat SDK tidak berfungsi. Anda akan mendapatkan masalah resolusi dependensi. Saat sedang mengerjakan solusi, Anda dapat menonaktifkan fitur notifikasi secara real-time dengan menambahkan pengecualian berikut ke dependensi Chat SDK dalam file build.gradle aplikasi:

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

Menambahkan tata letak antarmuka pengguna Teams

Ganti kode di activity_main.xml dengan cuplikan kode berikut. Ini menambahkan input untuk ID utas dan untuk mengirim pesan, tombol untuk mengirim pesan yang diketik dan tata letak obrolan dasar.

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

Mengaktifkan kontrol Teams UI

Mengimpor paket dan menentukan variabel status

Ke konten MainActivity.java, tambahkan impor berikut:

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;

Tambahkan variabel berikut ke kelas MainActivity:

    // InitiatorId is used to differentiate incoming messages from outgoing messages
    private static final String InitiatorId = "<USER_ID>";
    private static final String ResourceUrl = "<COMMUNICATION_SERVICES_RESOURCE_ENDPOINT>";
    private String threadId;
    private ChatThreadAsyncClient chatThreadAsyncClient;
    
    // The list of ids corresponsding to messages which have already been processed
    ArrayList<String> chatMessages = new ArrayList<>();

Ganti <USER_ID> dengan ID pengguna yang memulai obrolan. Ganti <COMMUNICATION_SERVICES_RESOURCE_ENDPOINT> dengan titik akhir untuk sumber daya Communication Services Anda.

Menginisialisasi ChatThreadClient

Setelah bergabung dalam rapat, buatlah instans ChatThreadClient dan jadikan komponen obrolan terlihat.

Perbarui akhir metode MainActivity.joinTeamsMeeting() dengan kode di bawah ini:

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

Aktifkan pengiriman pesan

Tambahkan metode sendMessage() ke MainActivity. Ini menggunakan ChatThreadClient untuk mengirim pesan atas nama pengguna.

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

Aktifkan polling untuk pesan dan merender pesan dalam aplikasi

Penting

Masalah yang diketahui: Karena fitur notifikasi real-time Chat SDK tidak berfungsi bersama dengan SDK Panggilan, kita harus melakukan polling API GetMessages pada interval yang telah ditentukan. Dalam sampel kami, kami akan menggunakan interval 3 detik.

Kami dapat memperoleh data berikut dari daftar pesan yang dikembalikan oleh API GetMessages:

  • Pesan text dan html berada pada utas sejak bergabung
  • Perubahan pada daftar utas
  • Pembaruan pada topik utas

MainActivity Ke kelas , tambahkan handler dan tugas yang dapat dijalankan yang akan dijalankan pada interval 3 detik:

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

Perhatikan bahwa tugas telah dimulai di akhir metode MainActivity.joinTeamsMeeting() yang diperbarui dalam langkah inisialisasi.

Terakhir, kami menambahkan metode untuk mengkueri semua pesan yang dapat diakses di utas, mengurainya berdasarkan jenis pesan dan menampilkan html pesan dan text :

    private void retrieveMessages() throws InterruptedException {
        // Initialize the list of messages not yet processed
        ArrayList<ChatMessage> newChatMessages = new ArrayList<>();
        
        // Retrieve all messages accessible to the user
        PagedAsyncStream<ChatMessage> messagePagedAsyncStream
                = this.chatThreadAsyncClient.listMessages(new ListChatMessagesOptions(), null);
        // Set up a lock to wait until all returned messages have been inspected
        CountDownLatch latch = new CountDownLatch(1);
        // Traverse the returned messages
        messagePagedAsyncStream.forEach(new AsyncStreamHandler<ChatMessage>() {
            @Override
            public void onNext(ChatMessage message) {
                // Messages that should be displayed in the chat
                if ((message.getType().equals(ChatMessageType.TEXT)
                    || message.getType().equals(ChatMessageType.HTML))
                    && !chatMessages.contains(message.getId())) {
                    newChatMessages.add(message);
                    chatMessages.add(message.getId());
                }
                if (message.getType().equals(ChatMessageType.PARTICIPANT_ADDED)) {
                    // Handle participants added to chat operation
                    List<ChatParticipant> participantsAdded = message.getContent().getParticipants();
                    CommunicationIdentifier participantsAddedBy = message.getContent().getInitiatorCommunicationIdentifier();
                }
                if (message.getType().equals(ChatMessageType.PARTICIPANT_REMOVED)) {
                    // Handle participants removed from chat operation
                    List<ChatParticipant> participantsRemoved = message.getContent().getParticipants();
                    CommunicationIdentifier participantsRemovedBy = message.getContent().getInitiatorCommunicationIdentifier();
                }
                if (message.getType().equals(ChatMessageType.TOPIC_UPDATED)) {
                    // Handle topic updated
                    String newTopic = message.getContent().getTopic();
                    CommunicationIdentifier topicUpdatedBy = message.getContent().getInitiatorCommunicationIdentifier();
                }
            }
            @Override
            public void onError(Throwable throwable) {
                latch.countDown();
            }
            @Override
            public void onComplete() {
                latch.countDown();
            }
        });
        // Wait until the operation completes
        latch.await(1, TimeUnit.MINUTES);
        // Returned messages should be ordered by the createdOn field to be guaranteed a proper chronological order
        // For the purpose of this demo we will just reverse the list of returned messages
        Collections.reverse(newChatMessages);
        for (ChatMessage chatMessage : newChatMessages)
        {
            LinearLayout chatBoxLayout = findViewById(R.id.chat_box_layout);
            // For the purpose of this demo UI, we don't need to use HTML formatting for displaying messages
            // The Teams client always sends html messages in meeting chats 
            String message = Html.fromHtml(chatMessage.getContent().getMessage(), Html.FROM_HTML_MODE_LEGACY).toString().trim();
            TextView messageView = new TextView(this);
            messageView.setText(message);
            // Compare with sender identifier and align LEFT/RIGHT accordingly
            // Azure Communication Services users are of type CommunicationUserIdentifier
            CommunicationIdentifier senderId = chatMessage.getSenderCommunicationIdentifier();
            if (senderId instanceof CommunicationUserIdentifier
                && InitiatorId.equals(((CommunicationUserIdentifier) senderId).getId())) {
                messageView.setTextColor(Color.GREEN);
                messageView.setGravity(Gravity.RIGHT);
            } else {
                messageView.setTextColor(Color.BLUE);
                messageView.setGravity(Gravity.LEFT);
            }
            // Note: messages with the deletedOn property set to a timestamp, should be marked as deleted
            // Note: messages with the editedOn property set to a timestamp, should be marked as edited
            messageView.setTypeface(Typeface.SANS_SERIF, Typeface.BOLD);
            chatBoxLayout.addView(messageView);
        }
    }

Nama tampilan peserta utas obrolan tidak diatur oleh klien Teams. Nama dikembalikan sebagai null di API untuk mencantumkan peserta, dalam participantsAdded peristiwa dan dalam participantsRemoved peristiwa. Nama tampilan peserta obrolan dapat diambil dari bidang remoteParticipants dari objek call.

Mendapatkan utas obrolan rapat Teams untuk pengguna Communication Services

Detail rapat Teams dapat diambil menggunakan Graph API, yang diperinci dalam dokumentasi Graph. SDK Panggilan Communication Services menerima tautan rapat Teams lengkap atau ID rapat. Mereka dikembalikan sebagai bagian onlineMeeting dari sumber daya, dapat diakses di joinWebUrl bawah properti

Dengan Graph API, Anda juga bisa mendapatkan threadID. Respons memiliki chatInfo objek yang berisi threadID.

Menjalankan kode

Aplikasi kini dapat diluncurkan menggunakan tombol "Jalankan Aplikasi" pada toolbar (Shift+F10).

Untuk bergabung dalam rapat Teams, masukkan tautan rapat Teams Anda dan ID utas di antarmuka pengguna.

Setelah bergabung dalam rapat Tim, Anda perlu memasukkan pengguna ke rapat di klien Tim Anda. Setelah pengguna diterima dan telah bergabung dengan obrolan, Anda dapat mengirim dan menerima pesan.

Cuplikan layar Aplikasi Android yang telah selesai.

Catatan

Fitur tertentu saat ini tidak didukung untuk skenario interoperabilitas dengan Teams. Pelajari selengkapnya tentang fitur yang didukung, silakan lihat Kemampuan rapat Teams untuk pengguna eksternal Teams

Dalam mulai cepat ini, Anda mempelajari cara mengobrol dalam rapat Teams menggunakan Azure Communication Services Chat SDK untuk C#.

Kode Sampel

Temukan kode untuk mulai cepat ini di GitHub.

Prasyarat

Bergabung dalam obrolan rapat

Pengguna Communication Services dapat bergabung dengan rapat tim sebagai pengguna anonim menggunakan SDK Panggilan. Bergabung dalam rapat menambahkan mereka sebagai peserta ke obrolan rapat juga, di mana mereka bisa mengirim dan menerima pesan dengan pengguna lain dalam rapat. Pengguna tidak akan memiliki akses ke pesan obrolan yang dikirim sebelum mereka bergabung dalam rapat, dan mereka tidak akan dapat mengirim atau menerima pesan setelah rapat berakhir. Anda dapat mengikuti langkah-langkah berikut untuk bergabung dalam rapat dan memulai obrolan.

Menjalankan kode

Anda dapat membangun dan menjalankan kode di Visual Studio. Perhatikan platform solusi yang kami dukung: x64,x86 , dan ARM64.

  1. Buka instans PowerShell, Terminal Windows, Prompt Perintah, atau yang setara dan navigasikan ke direktori yang ingin Anda kloning sampelnya.
  2. git clone https://github.com/Azure-Samples/Communication-Services-dotnet-quickstarts.git
  3. Buka proyek ChatTeamsInteropQuickStart/ChatTeamsInteropQuickStart.csproj di Visual Studio.
  4. Pasang versi paket NuGet berikut (atau lebih tinggi):
Install-Package Azure.Communication.Calling -Version 1.0.0-beta.29
Install-Package Azure.Communication.Chat -Version 1.1.0
Install-Package Azure.Communication.Common -Version 1.0.1
Install-Package Azure.Communication.Identity -Version 1.0.1

  1. Dengan sumber daya Communication Services yang diakui dalam prasyarat, tambahkan connectionstring ke file ChatTeamsInteropQuickStart/MainPage.xaml.cs .
//Azure Communication Services resource connection string, i.e., = "endpoint=https://your-resource.communication.azure.net/;accesskey=your-access-key";
private const string connectionString_ = "";

Penting

  • Pilih platform yang tepat dari daftar dropdown 'Platform Solusi' di Visual Studio sebelum menjalankan kode, yaitu, x64
  • Pastikan Anda mengaktifkan 'Mode Pengembang' di Windows 10 diaktifkan (Pengaturan Pengembang)

Langkah selanjutnya tidak akan berfungsi jika ini tidak dikonfigurasi dengan benar

  1. Tekan F5 untuk memulai proyek dalam mode debugging.
  2. Menempelkan tautan rapat teams yang valid pada kotak 'Tautan Rapat Teams' (lihat bagian berikutnya)
  3. Tekan 'Bergabung dengan rapat Teams' untuk mulai mengobrol.

Penting

Setelah SDK panggilan membuat koneksi dengan rapat teams Lihat aplikasi Windows yang memanggil Azure Communication Services, fungsi utama untuk menangani operasi obrolan adalah: StartPollingForChatMessages dan SendMessageButton_Click. Kedua cuplikan kode berada di ChatTeamsInteropQuickStart\MainPage.xaml.cs

        /// <summary>
        /// Background task that keeps polling for chat messages while the call connection is stablished
        /// </summary>
        private async Task StartPollingForChatMessages()
        {
            CommunicationTokenCredential communicationTokenCredential = new(user_token_);
            chatClient_ = new ChatClient(EndPointFromConnectionString(), communicationTokenCredential);
            await Task.Run(async () =>
            {
                keepPolling_ = true;

                ChatThreadClient chatThreadClient = chatClient_.GetChatThreadClient(thread_Id_);
                int previousTextMessages = 0;
                while (keepPolling_)
                {
                    try
                    {
                        CommunicationUserIdentifier currentUser = new(user_Id_);
                        AsyncPageable<ChatMessage> allMessages = chatThreadClient.GetMessagesAsync();
                        SortedDictionary<long, string> messageList = new();
                        int textMessages = 0;
                        string userPrefix;
                        await foreach (ChatMessage message in allMessages)
                        {
                            if (message.Type == ChatMessageType.Html || message.Type == ChatMessageType.Text)
                            {
                                textMessages++;
                                userPrefix = message.Sender.Equals(currentUser) ? "[you]:" : "";
                                messageList.Add(long.Parse(message.SequenceId), $"{userPrefix}{StripHtml(message.Content.Message)}");
                            }
                        }

                        //Update UI just when there are new messages
                        if (textMessages > previousTextMessages)
                        {
                            previousTextMessages = textMessages;
                            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                            {
                                TxtChat.Text = string.Join(Environment.NewLine, messageList.Values.ToList());
                            });

                        }
                        if (!keepPolling_)
                        {
                            return;
                        }

                        await SetInCallState(true);
                        await Task.Delay(3000);
                    }
                    catch (Exception e)
                    {
                        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
                        {
                            _ = new MessageDialog($"An error occurred while fetching messages in PollingChatMessagesAsync(). The application will shutdown. Details : {e.Message}").ShowAsync();
                            throw e;
                        });
                        await SetInCallState(false);
                    }
                }
            });
        }
        private async void SendMessageButton_Click(object sender, RoutedEventArgs e)
        {
            SendMessageButton.IsEnabled = false;
            ChatThreadClient chatThreadClient = chatClient_.GetChatThreadClient(thread_Id_);
            _ = await chatThreadClient.SendMessageAsync(TxtMessage.Text);
            
            TxtMessage.Text = "";
            SendMessageButton.IsEnabled = true;
        }

Tautan rapat Teams dapat diambil menggunakan Graph API, yang diperinci dalam dokumentasi Graph. Tautan ini dikembalikan sebagai bagian dari sumber daya onlineMeeting, yang dapat diakses di dalam properti joinWebUrl.

Anda juga dapat memperoleh tautan rapat yang diperlukan dari URL Bergabung ke Rapat di undangan rapat Teams itu sendiri. Link rapat Teams terlihat seperti ini: https://teams.microsoft.com/l/meetup-join/meeting_chat_thread_id/1606337455313?context=some_context_here. Jika tautan tim Anda memiliki format yang berbeda dengan ini, Anda perlu mengambil ID utas menggunakan Graph API.

Cuplikan layar Aplikasi csharp yang telah selesai.

Catatan

Fitur tertentu saat ini tidak didukung untuk skenario interoperabilitas dengan Teams. Pelajari selengkapnya tentang fitur yang didukung, silakan lihat Kemampuan rapat Teams untuk pengguna eksternal Teams

Membersihkan sumber daya

Jika ingin membersihkan dan menghapus langganan Azure Communication Services, Anda bisa menghapus sumber daya atau grup sumber daya. Menghapus grup sumber daya juga menghapus sumber daya apa pun yang terkait dengannya. Pelajari selengkapnya tentang membersihkan sumber daya.

Langkah berikutnya

Untuk informasi lebih lanjut, baca artikel berikut: