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.
V tomto rychlém startu se dozvíte, jak chatovat na schůzce Teams pomocí sady SDK chatu služby Azure Communication Services pro JavaScript.
Ukázka kódu
Najděte finalizovaný kód pro tento rychlý start na GitHubu.
Požadavky
- Nasazení Teams.
- Pracovní chatovací aplikace
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í. Když se ke schůzce připojíte, přidáte je také 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 nebude mít přístup ke zprávům chatu odeslaných před připojením ke schůzce a po skončení schůzky nebude moct 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
Tato --save
možnost vypíše knihovnu jako závislost v souboru package.json .
Nastavení architektury aplikace
V tomto rychlém startu se ke sbalení prostředků aplikace používá webpack. 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žní připojit se ke schůzce a zahájit chatování.
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. Textové pole v horní části stránky se použije k zadání kontextu schůzky Teams. Tlačítko Připojit se ke schůzce v Teams slouží k připojení k zadané schůzce. V dolní části stránky se zobrazí automaticky otevírané okno chatu. Dá se použít k odesílání zpráv ve vlákně schůzky a zobrazuje se v reálném čase všechny zprávy odeslané ve vlákně, zatímco uživatel komunikační služby 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_STRING
s připojovací řetězec 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. Názvy se vrátí jako null v rozhraní API pro výpis účastníků v participantsAdded
události a v participantsRemoved
události. 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 k webpack-dev-server
sestavení a spuštění 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:
Vložte odkaz na schůzku v Teams do textového pole. Stisknutím klávesy Připojit se ke schůzce Teams se připojíte 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 v Teams pro externí uživatele Teams.
V tomto rychlém startu se dozvíte, jak chatovat na schůzce Teams pomocí sady SDK chatu služby Azure Communication Services pro iOS.
Ukázka kódu
Pokud chcete přeskočit na konec, můžete si tento rychlý start stáhnout jako ukázku na GitHubu.
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í 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í řetězec.
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.
Instalace CocoaPods
Tento průvodce použijte k instalaci CocoaPods na Mac.
Instalace balíčku a závislostí pomocí CocoaPods
Pokud chcete vytvořit
Podfile
aplikaci, otevřete terminál a přejděte do složky projektu a spusťte inicializaci podu.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
Spusťte
pod install
..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 "Privacy - Microphone Usage Description" (Ochrana osobních údajů – Popis použití mikrofonu).
Zakázání sandboxu uživatelských skriptů
Některé skripty v propojených knihovnách zapisují soubory během procesu sestavení. Pokud to chcete povolit, zakažte sandboxing uživatelského skriptu v Xcode.
V nastavení sestavení vyhledejte sandbox
a nastavte User Script Sandboxing
hodnotu No
.
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 nebude mít přístup k chatovým zprávům odeslaných před připojením ani nebude moct posílat ani přijímat zprávy, když nejsou ve schůzce. Pokud se chcete ke schůzce připojit a začít chatovat, můžete postupovat podle dalších kroků.
Nastavení architektury aplikace
Importujte balíčky ContentView.swift
Azure Communication přidáním následujícího fragmentu kódu:
import AVFoundation
import SwiftUI
import AzureCommunicationCalling
import AzureCommunicationChat
V ContentView.swift
přidání následujícího fragmentu struct ContentView: View
kódu těsně nad deklaraci:
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 služby Communication Services.
Nahraďte <ADD_YOUR_USER_TOKEN_HERE>
výše vygenerovaným tokenem prostřednictvím příkazového řádku klienta Azure.
Další informace o přístupových tokenech uživatele: 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řidáme hlavní text var pro uložení prvků uživatelského rozhraní. K těmto ovládacím prvkům připojíme obchodní logiku v tomto rychlém startu. 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
}
}
Inicializace ChatClient
Vytvořte instanci oznámení zpráv a povolte ChatClient
je. 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.
onAppear
Do funkce přidejte následující kód, který inicializuje CallClient
ChatClient
a:
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 zpracování připojování 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"
}
}
}
Inicializace ChatThreadClient
Inicializujeme ChatThreadClient
, jakmile se uživatel připojí ke schůzce. To vyžaduje, abychom zkontrolovali stav schůzky od delegáta a potom inicializovali ChatThreadClient
stav schůzky 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
analýze ID vlákna chatu z odkazu na schůzku týmu. V případě, že tato extrakce selže, bude muset 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
. To se volá, když dojde k události zasílání zpráv. Všimněte si, že je potřeba 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 obslužnou rutinu delegáta pro klienta volání. Tato obslužná rutina slouží ke kontrole stavu hovoru a inicializaci 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 týmu, vymažeme zprávy chatu z uživatelského rozhraní a zavěsíme hovor. Celý kód je uvedený níže.
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ískání vlákna 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. Vrátí se jako součást onlineMeeting
prostředku, který je přístupný v rámci joinWebUrl
vlastnosti.
Pomocí rozhraní Graph API můžete také získat threadID
rozhraní . 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ůžete odesílat a přijímat zprávy.
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.
V tomto rychlém startu se dozvíte, jak chatovat ve schůzce Teams pomocí sady SDK chatu služby Azure Communication Services pro Android.
Ukázka kódu
Pokud chcete přeskočit na konec, můžete si tento rychlý start stáhnout jako ukázku na GitHubu.
Požadavky
- Nasazení Teams.
- Funkční volající aplikace.
Povolení interoperability 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 jenom v případech, kdy se připojil ke schůzce Teams. V dokumentaci k interoperabilitě Teams se dozvíte, jak přidat uživatele komunikačních služeb do hovoru na schůzku 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 je povolena interoperabilita 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í. Když se k hovoru připojíte, přidáte je také jako účastníka do chatu schůzky, kde 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 se chcete ke schůzce připojit a začít chatovat, můžete postupovat podle dalších kroků.
Přidání chatu do volající aplikace Teams
Na úrovni build.gradle
modulu přidejte závislost na chatovací sadě SDK.
Důležité
Známý problém: Při společném používání chatu a volání sady SDK pro Android ve stejné aplikaci nebude funkce oznámení v reálném čase sady SDK chatu fungovat. Získáte 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.java
př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 corresponsding 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 služby Communication Services.
Inicializace ChatThreadClient
Po připojení ke schůzce vytvořte ChatThreadClient
instanci a zviditelnit 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
souboru . 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í sadou SDK, budeme muset rozhraní API dotazovat GetMessages
v předdefinovaných intervalech. V naší ukázce použijeme 3sekundové intervaly.
Ze seznamu zpráv vrácených rozhraním GetMessages
API můžeme získat následující data:
- Zprávy
text
ahtml
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 spustí 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);
}
};
Všimněte si, že úkol již byl zahájen 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 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);
}
}
Klient Teams nenastavuje zobrazované názvy účastníků vlákna chatu. Názvy se vrátí jako null v rozhraní API pro výpis účastníků v participantsAdded
události a v participantsRemoved
události. Zobrazované jména účastníků chatu lze načíst z remoteParticipants
pole objektu call
.
Získání vlákna 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. Vrátí se jako součást onlineMeeting
prostředku, který je přístupný v rámci joinWebUrl
vlastnosti.
Pomocí rozhraní Graph API můžete také získat threadID
rozhraní . Odpověď má chatInfo
objekt, který obsahuje threadID
.
Spuštění kódu
Aplikaci teď můžete spustit pomocí 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ůžete odesílat a přijímat zprávy.
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.
V tomto rychlém startu se dozvíte, jak chatovat na schůzce Teams pomocí sady SDK chatu služby Azure Communication Services pro jazyk C#.
Ukázkový kód
Najděte kód pro tento rychlý start na GitHubu.
Požadavky
- Nasazení Teams.
- Účet Azure s aktivním předplatným. Vytvoření účtu zdarma
- Nainstalujte Visual Studio 2019 s Univerzální platforma Windows vývojovou úlohou.
- Nasazený prostředek komunikační služby. Vytvořte prostředek komunikační služby.
- Odkaz na schůzku v Teams
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í. Když se ke schůzce připojíte, přidáte je také 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 nebude mít 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 nebudou moct odesí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ů.
Spuštění kódu
Kód můžete sestavit a spustit v sadě Visual Studio. Všimněte si platforem řešení, které podporujeme: x64
,x86
a ARM64
.
- 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.
git clone https://github.com/Azure-Samples/Communication-Services-dotnet-quickstarts.git
- Otevřete projekt ChatTeamsInteropQuickStart/ChatTeamsInteropQuickStart.csproj v sadě Visual Studio.
- 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
- S prostředkem Komunikační služby, který je v požadavcích, 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, tj.
x64
- Ujistěte se, že máte ve Windows 10 povolený režim pro vývojáře (nastavení pro vývojáře).
Další kroky nebudou fungovat, pokud není správně nakonfigurovaný.
- Stisknutím klávesy F5 spusťte projekt v režimu ladění.
- Vložení platného odkazu na schůzku teams do pole Odkaz na schůzku v Teams (viz další část)
- Stisknutím klávesy Join Teams meeting (Připojit se ke schůzce Teams) začněte chatovat.
Důležité
Jakmile volající sada SDK vytvoří připojení ke schůzce týmů viz Komunikační služby volající aplikaci pro Windows, klíčové funkce pro zpracování operací chatu jsou: StartPollingForChatMessages a SendMessageButton_Click. Oba fragmenty kódu jsou v ChatTeamsInteropQuickStart\MainPage.xaml.cs
/// <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;
}
Získání odkazu na schůzku v Teams
Odkaz na schůzku v Teams je možné načíst pomocí rozhraní Graph API, která jsou podrobně popsána 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á takto: https://teams.microsoft.com/l/meetup-join/meeting_chat_thread_id/1606337455313?context=some_context_here
.
Pokud má odkaz na týmy jiný formát, musíte načíst ID vlákna pomocí rozhraní Graph API.
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.
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ů.
Další kroky
Další informace najdete v následujících článcích:
- Podívejte se na ukázku našeho chatovacího hrdiny
- Další informace o fungování chatu