Sdílet prostřednictvím


Rychlý start: Připojení volající aplikace k frontě volání Teams

V tomto rychlém startu se dozvíte, jak zahájit volání od uživatele Azure Communication Services do fronty volání Teams. Dosáhnete toho pomocí následujících kroků:

  1. Povolte federaci prostředku Azure Communication Services s tenantem Teams.
  2. Vyberte nebo vytvořte frontu volání teams prostřednictvím Centra pro správu Teams.
  3. Získejte e-mailovou adresu fronty volání prostřednictvím Centra pro správu Teams.
  4. Získejte ID objektu fronty volání prostřednictvím rozhraní Graph API.
  5. Začněte volat pomocí sady SDK pro volání služeb Azure Communication Services.

Pokud chcete přeskočit na konec, můžete si tento rychlý start stáhnout jako ukázku na GitHubu.

Povolení interoperability v tenantovi Teams

Uživatel Microsoft Entra s rolí správce Teams může spustit rutinu PowerShellu s modulem MicrosoftTeams a povolit prostředek Komunikační služby v tenantovi.

1. Příprava modulu Microsoft Teams

Nejprve otevřete PowerShell a pomocí následujícího příkazu ověřte existenci modulu Teams:

Get-module *teams* 

Pokud modul nevidíte MicrosoftTeams , nejdřív ho nainstalujte. Pokud chcete nainstalovat modul, musíte spustit PowerShell jako správce. Pak spusťte následující příkaz:

	Install-Module -Name MicrosoftTeams

Budete informováni o nainstalovaných modulech, které můžete potvrdit pomocí Y odpovědi.A Pokud je modul nainstalovaný, ale je zastaralý, můžete spuštěním následujícího příkazu modul aktualizovat:

	Update-Module MicrosoftTeams

2. Připojení k modulu Microsoft Teams

Po instalaci a připravenosti modulu se můžete připojit k modulu MicrosoftTeams pomocí následujícího příkazu. Zobrazí se výzva k přihlášení pomocí interaktivního okna. Uživatelský účet, který budete používat, musí mít oprávnění správce Teams. V opačném případě můžete získat access denied odpověď v dalších krocích.

Connect-MicrosoftTeams

3. Povolení konfigurace tenanta

Interoperabilita s prostředky komunikačních služeb se řídí prostřednictvím konfigurace tenanta a přiřazených zásad. Tenant Teams má jednu konfiguraci tenanta a uživatelé Teams přiřadili globální zásady nebo vlastní zásady. Další informace najdete v tématu Přiřazení zásad v Teams.

Po úspěšném přihlášení můžete spuštěním rutiny Set-CsTeamsAcsFederationConfiguration povolit prostředek Komunikační služby ve vašem tenantovi. Nahraďte text IMMUTABLE_RESOURCE_ID neměnným ID prostředku v komunikačním prostředku. Další podrobnosti o tom, jak tyto informace získat, najdete tady.

$allowlist = @('IMMUTABLE_RESOURCE_ID')
Set-CsTeamsAcsFederationConfiguration -EnableAcsUsers $True -AllowedAcsResources $allowlist

4. Povolení zásad tenanta

Každý uživatel Teams přiřadil, External Access Policy který určuje, jestli uživatelé komunikačních služeb mohou volat tohoto uživatele Teams. Pomocí rutiny Set-CsExternalAccessPolicy zajistěte, aby zásady přiřazené uživateli Teams byly nastaveny EnableAcsFederationAccess na $true

Set-CsExternalAccessPolicy -Identity Global -EnableAcsFederationAccess $true

Vytvoření nebo výběr fronty volání v Teams

Fronta hovorů v Teams je funkce v Microsoft Teams, která efektivně distribuuje příchozí hovory mezi skupinu určených uživatelů nebo agentů. Je užitečné pro scénáře zákaznické podpory nebo call centra. Volání se umístí do fronty a přiřadí se k dalšímu dostupnému agentovi na základě předem určené metody směrování. Agenti přijímají oznámení a můžou zpracovávat hovory pomocí ovládacích prvků volání v Teams. Tato funkce nabízí vytváření sestav a analýzy pro sledování výkonu. Zjednodušuje zpracování hovorů, zajišťuje konzistentní uživatelské prostředí a optimalizuje produktivitu agentů. Můžete vybrat existující nebo vytvořit novou frontu volání prostřednictvím Centra pro správu Teams.

Přečtěte si další informace o tom, jak vytvořit frontu volání pomocí Centra pro správu Teams.

Vyhledání ID objektu pro frontu volání

Po vytvoření fronty volání potřebujeme najít korelované ID objektu, abychom ho mohli později použít pro volání. ID objektu je připojené k účtu prostředku připojenému k frontě volání – otevřete kartu Účty zdrojů v Aplikaci Teams Admin a vyhledejte e-mail. Snímek obrazovky s účty prostředků na portálu pro správu Teams Všechny požadované informace o účtu prostředků najdete v Microsoft Graph Exploreru pomocí tohoto e-mailu ve vyhledávání.

https://graph.microsoft.com/v1.0/users/lab-test2-cq-@contoso.com

Ve výsledcích budeme moct najít pole ID.

    "userPrincipalName": "lab-test2-cq@contoso.com",
    "id": "31a011c2-2672-4dd0-b6f9-9334ef4999db"

Požadavky

  • Získejte účet Azure s aktivním předplatným. Vytvoření účtu zdarma
  • Node.js verze LTS a údržba ACTIVE LTS (8.11.1 a 10.14.1)
  • Vytvořte aktivní prostředek komunikační služby. Vytvořte prostředek komunikační služby.

Nastavení

Vytvoření nové aplikace Node.js

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

mkdir calling-quickstart && cd calling-quickstart

Nainstalujte balíček .

npm install Pomocí příkazu nainstalujte sadu SDK pro volání služeb Azure Communication Services pro JavaScript.

Důležité

V tomto rychlém startu se používá verze nextsady SDK pro volání služeb Azure Communication Services .

npm install @azure/communication-common@next --save
npm install @azure/communication-calling@next --save

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 webpackbalíčky a webpack-cli npm a webpack-dev-server uveďte je jako vývojové závislosti ve vaší package.json:

npm install copy-webpack-plugin@^11.0.0 webpack@^5.88.2 webpack-cli@^5.1.4 webpack-dev-server@^4.15.1 --save-dev

Vytvořte index.html soubor v kořenovém adresáři projektu. Tento soubor použijeme ke konfiguraci základního rozložení, které uživateli umožní umístit videohovor 1:1.

Tady je kód:

<!-- index.html -->
<!DOCTYPE html>
<html>
    <head>
        <title>Azure Communication Services - Calling Web SDK</title>
    </head>
    <body>
        <h4>Azure Communication Services - Calling Web SDK</h4>
        <input id="user-access-token"
            type="text"
            placeholder="User access token"
            style="margin-bottom:1em; width: 500px;"/>
        <button id="initialize-teams-call-agent" type="button">Initialize Call Agent</button>
        <br>
        <br>
        <input id="application-object-id"
            type="text"
            placeholder="Enter callee's Teams user identity in format: 'APP_GUID'"
            style="margin-bottom:1em; width: 500px; display: block;"/>
        <button id="start-call-button" type="button" disabled="true">Start Call</button>
        <button id="hangup-call-button" type="button" disabled="true">Hang up Call</button>
        <button id="accept-call-button" type="button" disabled="true">Accept Call</button>
        <button id="start-video-button" type="button" disabled="true">Start Video</button>
        <button id="stop-video-button" type="button" disabled="true">Stop Video</button>
        <br>
        <br>
        <div id="connectedLabel" style="color: #13bb13;" hidden>Call is connected!</div>
        <br>
        <div id="remoteVideoContainer" style="width: 40%;" hidden>Remote participants' video streams:</div>
        <br>
        <div id="localVideoContainer" style="width: 30%;" hidden>Local video stream:</div>
        <!-- points to the bundle generated from client.js -->
        <script src="./main.js"></script>
    </body>
</html>

Objektový model webové sady SDK volání služeb Azure Communication Services

Následující třídy a rozhraní zpracovávají některé z hlavních funkcí sady SDK pro volání služeb Azure Communication Services:

Název Popis
CallClient Hlavní vstupní bod do volající sady SDK.
CallAgent Používá se ke spouštění a správě hovorů.
DeviceManager Slouží ke správě mediálních zařízení.
Call Používá se pro reprezentaci hovoru.
LocalVideoStream Používá se k vytvoření místního videostreamu pro zařízení fotoaparátu v místním systému.
RemoteParticipant Slouží k reprezentaci vzdáleného účastníka hovoru.
RemoteVideoStream Používá se pro reprezentaci vzdáleného video streamu ze vzdáleného účastníka.

Vytvořte soubor v kořenovém adresáři projektu, který bude client.js obsahovat logiku aplikace pro účely tohoto rychlého startu. Do client.js přidejte následující kód:

// Make sure to install the necessary dependencies
const { CallClient, VideoStreamRenderer, LocalVideoStream } = require('@azure/communication-calling');
const { AzureCommunicationTokenCredential } = require('@azure/communication-common');
const { AzureLogger, setLogLevel } = require("@azure/logger");
// Set the log level and output
setLogLevel('verbose');
AzureLogger.log = (...args) => {
    console.log(...args);
};
// Calling web sdk objects
let callAgent;
let deviceManager;
let call;
let incomingCall;
let localVideoStream;
let localVideoStreamRenderer;
// UI widgets
let userAccessToken = document.getElementById('user-access-token');
let callQueueId = document.getElementById('application-object-id');
let initializeCallAgentButton = document.getElementById('initialize-teams-call-agent');
let startCallButton = document.getElementById('start-call-button');
let hangUpCallButton = document.getElementById('hangup-call-button');
let acceptCallButton = document.getElementById('accept-call-button');
let startVideoButton = document.getElementById('start-video-button');
let stopVideoButton = document.getElementById('stop-video-button');
let connectedLabel = document.getElementById('connectedLabel');
let remoteVideoContainer = document.getElementById('remoteVideoContainer');
let localVideoContainer = document.getElementById('localVideoContainer');
/**
 * Create an instance of CallClient. Initialize a CallAgent instance with a AzureCommunicationTokenCredential via created CallClient. CallAgent enables us to make outgoing calls and receive incoming calls. 
 * You can then use the CallClient.getDeviceManager() API instance to get the DeviceManager.
 */
initializeCallAgentButton.onclick = async () => {
    try {
        const callClient = new CallClient(); 
        tokenCredential = new AzureCommunicationTokenCredential(userAccessToken.value.trim());
        callAgent = await callClient.createCallAgent(tokenCredential)
        // Set up a camera device to use.
        deviceManager = await callClient.getDeviceManager();
        await deviceManager.askDevicePermission({ video: true });
        await deviceManager.askDevicePermission({ audio: true });
        // Listen for an incoming call to accept.
        callAgent.on('incomingCall', async (args) => {
            try {
                incomingCall = args.incomingCall;
                acceptCallButton.disabled = false;
                startCallButton.disabled = true;
            } catch (error) {
                console.error(error);
            }
        });
        startCallButton.disabled = false;
        initializeCallAgentButton.disabled = true;
    } catch(error) {
        console.error(error);
    }
}
/**
 * Place a 1:1 outgoing video call to a Teams Call Queue
 * Add an event listener to initiate a call when the `startCallButton` is selected.
 * Enumerate local cameras using the deviceManager `getCameraList` API.
 * In this quickstart, we're using the first camera in the collection. Once the desired camera is selected, a
 * LocalVideoStream instance will be constructed and passed within `videoOptions` as an item within the
 * localVideoStream array to the call method. When the call connects, your application will be sending a video stream to the other participant. 
 */
startCallButton.onclick = async () => {
    try {
        const localVideoStream = await createLocalVideoStream();
        const videoOptions = localVideoStream ? { localVideoStreams: [localVideoStream] } : undefined;
        call = callAgent.startCall([{ teamsAppId: callQueueId.value.trim(), cloud:"public" }], { videoOptions: videoOptions });
        // Subscribe to the call's properties and events.
        subscribeToCall(call);
    } catch (error) {
        console.error(error);
    }
}
/**
 * Accepting an incoming call with a video
 * Add an event listener to accept a call when the `acceptCallButton` is selected.
 * You can accept incoming calls after subscribing to the `CallAgent.on('incomingCall')` event.
 * You can pass the local video stream to accept the call with the following code.
 */
acceptCallButton.onclick = async () => {
    try {
        const localVideoStream = await createLocalVideoStream();
        const videoOptions = localVideoStream ? { localVideoStreams: [localVideoStream] } : undefined;
        call = await incomingCall.accept({ videoOptions });
        // Subscribe to the call's properties and events.
        subscribeToCall(call);
    } catch (error) {
        console.error(error);
    }
}
// Subscribe to a call obj.
// Listen for property changes and collection updates.
subscribeToCall = (call) => {
    try {
        // Inspect the initial call.id value.
        console.log(`Call Id: ${call.id}`);
        //Subscribe to call's 'idChanged' event for value changes.
        call.on('idChanged', () => {
            console.log(`Call ID changed: ${call.id}`); 
        });
        // Inspect the initial call.state value.
        console.log(`Call state: ${call.state}`);
        // Subscribe to call's 'stateChanged' event for value changes.
        call.on('stateChanged', async () => {
            console.log(`Call state changed: ${call.state}`);
            if(call.state === 'Connected') {
                connectedLabel.hidden = false;
                acceptCallButton.disabled = true;
                startCallButton.disabled = true;
                hangUpCallButton.disabled = false;
                startVideoButton.disabled = false;
                stopVideoButton.disabled = false;
            } else if (call.state === 'Disconnected') {
                connectedLabel.hidden = true;
                startCallButton.disabled = false;
                hangUpCallButton.disabled = true;
                startVideoButton.disabled = true;
                stopVideoButton.disabled = true;
                console.log(`Call ended, call end reason={code=${call.callEndReason.code}, subCode=${call.callEndReason.subCode}}`);
            }   
        });
        call.localVideoStreams.forEach(async (lvs) => {
            localVideoStream = lvs;
            await displayLocalVideoStream();
        });
        call.on('localVideoStreamsUpdated', e => {
            e.added.forEach(async (lvs) => {
                localVideoStream = lvs;
                await displayLocalVideoStream();
            });
            e.removed.forEach(lvs => {
               removeLocalVideoStream();
            });
        });
        
        call.on('isLocalVideoStartedChanged', () => {
            console.log(`isLocalVideoStarted changed: ${call.isLocalVideoStarted}`);
        });
        console.log(`isLocalVideoStarted: ${call.isLocalVideoStarted}`);
        // Inspect the call's current remote participants and subscribe to them.
        call.remoteParticipants.forEach(remoteParticipant => {
            subscribeToRemoteParticipant(remoteParticipant);
        });
        // Subscribe to the call's 'remoteParticipantsUpdated' event to be
        // notified when new participants are added to the call or removed from the call.
        call.on('remoteParticipantsUpdated', e => {
            // Subscribe to new remote participants that are added to the call.
            e.added.forEach(remoteParticipant => {
                subscribeToRemoteParticipant(remoteParticipant)
            });
            // Unsubscribe from participants that are removed from the call
            e.removed.forEach(remoteParticipant => {
                console.log('Remote participant removed from the call.');
            });
        });
    } catch (error) {
        console.error(error);
    }
}
// Subscribe to a remote participant obj.
// Listen for property changes and collection updates.
subscribeToRemoteParticipant = (remoteParticipant) => {
    try {
        // Inspect the initial remoteParticipant.state value.
        console.log(`Remote participant state: ${remoteParticipant.state}`);
        // Subscribe to remoteParticipant's 'stateChanged' event for value changes.
        remoteParticipant.on('stateChanged', () => {
            console.log(`Remote participant state changed: ${remoteParticipant.state}`);
        });
        // Inspect the remoteParticipants's current videoStreams and subscribe to them.
        remoteParticipant.videoStreams.forEach(remoteVideoStream => {
            subscribeToRemoteVideoStream(remoteVideoStream)
        });
        // Subscribe to the remoteParticipant's 'videoStreamsUpdated' event to be
        // notified when the remoteParticipant adds new videoStreams and removes video streams.
        remoteParticipant.on('videoStreamsUpdated', e => {
            // Subscribe to newly added remote participant's video streams.
            e.added.forEach(remoteVideoStream => {
                subscribeToRemoteVideoStream(remoteVideoStream)
            });
            // Unsubscribe from newly removed remote participants' video streams.
            e.removed.forEach(remoteVideoStream => {
                console.log('Remote participant video stream was removed.');
            })
        });
    } catch (error) {
        console.error(error);
    }
}
/**
 * Subscribe to a remote participant's remote video stream obj.
 * You have to subscribe to the 'isAvailableChanged' event to render the remoteVideoStream. If the 'isAvailable' property
 * changes to 'true' a remote participant is sending a stream. Whenever the availability of a remote stream changes
 * you can choose to destroy the whole 'Renderer' a specific 'RendererView' or keep them. Displaying RendererView without a video stream will result in a blank video frame. 
 */
subscribeToRemoteVideoStream = async (remoteVideoStream) => {
    // Create a video stream renderer for the remote video stream.
    let videoStreamRenderer = new VideoStreamRenderer(remoteVideoStream);
    let view;
    const renderVideo = async () => {
        try {
            // Create a renderer view for the remote video stream.
            view = await videoStreamRenderer.createView();
            // Attach the renderer view to the UI.
            remoteVideoContainer.hidden = false;
            remoteVideoContainer.appendChild(view.target);
        } catch (e) {
            console.warn(`Failed to createView, reason=${e.message}, code=${e.code}`);
        }	
    }
    
    remoteVideoStream.on('isAvailableChanged', async () => {
        // Participant has switched video on.
        if (remoteVideoStream.isAvailable) {
            await renderVideo();
        // Participant has switched video off.
        } else {
            if (view) {
                view.dispose();
                view = undefined;
            }
        }
    });
    // Participant has video on initially.
    if (remoteVideoStream.isAvailable) {
        await renderVideo();
    }
}
// Start your local video stream.
// This will send your local video stream to remote participants so they can view it.
startVideoButton.onclick = async () => {
    try {
        const localVideoStream = await createLocalVideoStream();
        await call.startVideo(localVideoStream);
    } catch (error) {
        console.error(error);
    }
}
// Stop your local video stream.
// This will stop your local video stream from being sent to remote participants.
stopVideoButton.onclick = async () => {
    try {
        await call.stopVideo(localVideoStream);
    } catch (error) {
        console.error(error);
    }
}
/**
 * To render a LocalVideoStream, you need to create a new instance of VideoStreamRenderer, and then
 * create a new VideoStreamRendererView instance using the asynchronous createView() method.
 * You may then attach view.target to any UI element. 
 */
// Create a local video stream for your camera device
createLocalVideoStream = async () => {
    const camera = (await deviceManager.getCameras())[0];
    if (camera) {
        return new LocalVideoStream(camera);
    } else {
        console.error(`No camera device found on the system`);
    }
}
// Display your local video stream preview in your UI
displayLocalVideoStream = async () => {
    try {
        localVideoStreamRenderer = new VideoStreamRenderer(localVideoStream);
        const view = await localVideoStreamRenderer.createView();
        localVideoContainer.hidden = false;
        localVideoContainer.appendChild(view.target);
    } catch (error) {
        console.error(error);
    } 
}
// Remove your local video stream preview from your UI
removeLocalVideoStream = async() => {
    try {
        localVideoStreamRenderer.dispose();
        localVideoContainer.hidden = true;
    } catch (error) {
        console.error(error);
    } 
}
// End the current call
hangUpCallButton.addEventListener("click", async () => {
    // end the current call
    await call.hangUp();
});

Přidání kódu místního serveru webpacku

V kořenovém adresáři projektu vytvořte soubor s názvem webpack.config.js , který bude obsahovat logiku místního serveru pro účely tohoto rychlého startu. Do webpack.config.js přidejte následující kód:

const path = require('path');
const CopyPlugin = require("copy-webpack-plugin");

module.exports = {
    mode: 'development',
    entry: './client.js',
    output: {
        filename: 'main.js',
        path: path.resolve(__dirname, 'dist'),
    },
    devServer: {
        static: {
            directory: path.join(__dirname, './')
        },
    },
    plugins: [
        new CopyPlugin({
            patterns: [
                './index.html'
            ]
        }),
    ]
};

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 sbalte hostitele aplikace v místním webovém serveru:

npx webpack serve --config webpack.config.js

Ruční postup nastavení hovoru:

  1. Otevřete prohlížeč a přejděte na http://localhost:8080/.
  2. Zadejte platný přístupový token uživatele. Pokud ještě nemáte k dispozici přístupové tokeny, projděte si dokumentaci k přístupovým tokenům uživatele.
  3. Klikněte na tlačítka Inicializovat agenta volání.
  4. Zadejte ID objektu fronty volání a vyberte tlačítko Zahájit hovor. Aplikace spustí odchozí volání do fronty volání s daným ID objektu.
  5. Volání je připojené k frontě volání.
  6. Uživatel komunikačních služeb je směrován prostřednictvím fronty volání na základě své konfigurace.

V tomto rychlém startu se dozvíte, jak zahájit volání od uživatele Azure Communication Services do fronty volání Teams. Dosáhnete toho pomocí následujících kroků:

  1. Povolte federaci prostředku Azure Communication Services s tenantem Teams.
  2. Vyberte nebo vytvořte frontu volání teams prostřednictvím Centra pro správu Teams.
  3. Získejte e-mailovou adresu fronty volání prostřednictvím Centra pro správu Teams.
  4. Získejte ID objektu fronty volání prostřednictvím rozhraní Graph API.
  5. Začněte volat pomocí sady SDK pro volání služeb Azure Communication Services.

Pokud chcete přeskočit na konec, můžete si tento rychlý start stáhnout jako ukázku na GitHubu.

Povolení interoperability v tenantovi Teams

Uživatel Microsoft Entra s rolí správce Teams může spustit rutinu PowerShellu s modulem MicrosoftTeams a povolit prostředek Komunikační služby v tenantovi.

1. Příprava modulu Microsoft Teams

Nejprve otevřete PowerShell a pomocí následujícího příkazu ověřte existenci modulu Teams:

Get-module *teams* 

Pokud modul nevidíte MicrosoftTeams , nejdřív ho nainstalujte. Pokud chcete nainstalovat modul, musíte spustit PowerShell jako správce. Pak spusťte následující příkaz:

	Install-Module -Name MicrosoftTeams

Budete informováni o nainstalovaných modulech, které můžete potvrdit pomocí Y odpovědi.A Pokud je modul nainstalovaný, ale je zastaralý, můžete spuštěním následujícího příkazu modul aktualizovat:

	Update-Module MicrosoftTeams

2. Připojení k modulu Microsoft Teams

Po instalaci a připravenosti modulu se můžete připojit k modulu MicrosoftTeams pomocí následujícího příkazu. Zobrazí se výzva k přihlášení pomocí interaktivního okna. Uživatelský účet, který budete používat, musí mít oprávnění správce Teams. V opačném případě můžete získat access denied odpověď v dalších krocích.

Connect-MicrosoftTeams

3. Povolení konfigurace tenanta

Interoperabilita s prostředky komunikačních služeb se řídí prostřednictvím konfigurace tenanta a přiřazených zásad. Tenant Teams má jednu konfiguraci tenanta a uživatelé Teams přiřadili globální zásady nebo vlastní zásady. Další informace najdete v tématu Přiřazení zásad v Teams.

Po úspěšném přihlášení můžete spuštěním rutiny Set-CsTeamsAcsFederationConfiguration povolit prostředek Komunikační služby ve vašem tenantovi. Nahraďte text IMMUTABLE_RESOURCE_ID neměnným ID prostředku v komunikačním prostředku. Další podrobnosti o tom, jak tyto informace získat, najdete tady.

$allowlist = @('IMMUTABLE_RESOURCE_ID')
Set-CsTeamsAcsFederationConfiguration -EnableAcsUsers $True -AllowedAcsResources $allowlist

4. Povolení zásad tenanta

Každý uživatel Teams přiřadil, External Access Policy který určuje, jestli uživatelé komunikačních služeb mohou volat tohoto uživatele Teams. Pomocí rutiny Set-CsExternalAccessPolicy zajistěte, aby zásady přiřazené uživateli Teams byly nastaveny EnableAcsFederationAccess na $true

Set-CsExternalAccessPolicy -Identity Global -EnableAcsFederationAccess $true

Vytvoření nebo výběr fronty volání v Teams

Fronta hovorů v Teams je funkce v Microsoft Teams, která efektivně distribuuje příchozí hovory mezi skupinu určených uživatelů nebo agentů. Je užitečné pro scénáře zákaznické podpory nebo call centra. Volání se umístí do fronty a přiřadí se k dalšímu dostupnému agentovi na základě předem určené metody směrování. Agenti přijímají oznámení a můžou zpracovávat hovory pomocí ovládacích prvků volání v Teams. Tato funkce nabízí vytváření sestav a analýzy pro sledování výkonu. Zjednodušuje zpracování hovorů, zajišťuje konzistentní uživatelské prostředí a optimalizuje produktivitu agentů. Můžete vybrat existující nebo vytvořit novou frontu volání prostřednictvím Centra pro správu Teams.

Přečtěte si další informace o tom, jak vytvořit frontu volání pomocí Centra pro správu Teams.

Vyhledání ID objektu pro frontu volání

Po vytvoření fronty volání potřebujeme najít korelované ID objektu, abychom ho mohli později použít pro volání. ID objektu je připojené k účtu prostředku připojenému k frontě volání – otevřete kartu Účty zdrojů v Aplikaci Teams Admin a vyhledejte e-mail. Snímek obrazovky s účty prostředků na portálu pro správu Teams Všechny požadované informace o účtu prostředků najdete v Microsoft Graph Exploreru pomocí tohoto e-mailu ve vyhledávání.

https://graph.microsoft.com/v1.0/users/lab-test2-cq-@contoso.com

Ve výsledcích budeme moct najít pole ID.

    "userPrincipalName": "lab-test2-cq@contoso.com",
    "id": "31a011c2-2672-4dd0-b6f9-9334ef4999db"

Abychom mohli v volající aplikaci používat, musíme k tomuto ID přidat předponu. V současné době jsou podporovány následující položky:

  • Fronta volání veřejného cloudu: 28:orgid:<id>
  • Fronta volání cloudu pro státní správu: 28:gcch:<id>

Požadavky

Nastavení

Vytvoření aplikace pro Android s prázdnou aktivitou

V Android Studiu vyberte Spustit nový projekt Android Studio.

Snímek obrazovky s tlačítkem Start a new Android Studio Project (Spustit nový projekt Android Studio) vybraným v android Studiu

V části Telefon a tablet vyberte šablonu projektu Prázdná zobrazení.

Snímek obrazovky znázorňující možnost Prázdná aktivita vybraná na obrazovce Šablony projektu

Vyberte minimální sadu SDK rozhraní API 26: Android 8.0 (Oreo) nebo vyšší.

Snímek obrazovky znázorňující možnost Prázdná aktivita vybraná na obrazovce Šablona projektu 2

Nainstalujte balíček .

Vyhledejte projekt settings.gradle.kts a nezapomeňte se podívat mavenCentral() na seznam úložišť v části pluginManagement a dependencyResolutionManagement

pluginManagement {
    repositories {
    ...
        mavenCentral()
    ...
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
    ...
        mavenCentral()
    }
}

Potom na úrovni modulu build.gradle přidejte následující řádky do oddílů závislostí a androidu.

android {
    ...
    
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    ...
    implementation ("com.azure.android:azure-communication-calling:2.+")
    ...
}

Přidání oprávnění k manifestu aplikace

Aby bylo možné požádat o oprávnění požadovaná k volání, musí být deklarována v manifestu aplikace (app/src/main/AndroidManifest.xml). Obsah souboru nahraďte následujícím kódem:

    <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.contoso.acsquickstart">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <!--Our Calling SDK depends on the Apache HTTP SDK.
When targeting Android SDK 28+, this library needs to be explicitly referenced.
See https://developer.android.com/about/versions/pie/android-9.0-changes-28#apache-p-->
        <uses-library android:name="org.apache.http.legacy" android:required="false"/>
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
    

Nastavení rozložení aplikace

Jsou potřeba dva vstupy: textový vstup pro volané ID a tlačítko pro umístění hovoru. Tyto vstupy lze přidat prostřednictvím návrháře nebo úpravou xml rozložení. Vytvořte tlačítko s ID call_button a textovým vstupem callee_id. Přejděte na (app/src/main/res/layout/activity_main.xml) a nahraďte obsah souboru následujícím kódem:

<?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="${launchApp}">

    <EditText
        android:id="@+id/callee_id"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="Callee Id"
        android:inputType="textPersonName"
        android:layout_marginTop="100dp"
        android:layout_marginHorizontal="20dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="46dp"
        android:gravity="center"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent">

        <Button
            android:id="@+id/call_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Call" />

        <Button
            android:id="@+id/hangup_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Hangup" />

    </LinearLayout>

    <TextView
        android:id="@+id/status_bar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

Vytvoření generování uživatelského rozhraní a vazeb hlavní aktivity

S rozložením vytvořeným vazbami je možné přidat i základní generování aktivity. Aktivita zpracovává žádosti o oprávnění modulu runtime, vytvoření agenta volání a umístění hovoru při stisknutí tlačítka. Metoda onCreate je přepsána k vyvolání getAllPermissions a createAgent přidání vazeb pro tlačítko volání. K této události dochází pouze jednou při vytvoření aktivity. Další informace onCreatenajdete v příručce Principy životního cyklu aktivity.

Přejděte na MainActivity.java a nahraďte obsah následujícím kódem:

package com.contoso.acsquickstart;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import android.Manifest;
import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.os.Bundle;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.concurrent.ExecutionException;

import com.azure.android.communication.common.CommunicationIdentifier;
import com.azure.android.communication.common.CommunicationUserIdentifier;
import com.azure.android.communication.calling.Call;
import com.azure.android.communication.calling.CallAgent;
import com.azure.android.communication.calling.CallClient;
import com.azure.android.communication.calling.HangUpOptions;
import com.azure.android.communication.common.CommunicationTokenCredential;
import com.azure.android.communication.calling.StartCallOptions;

public class MainActivity extends AppCompatActivity {
    private static final String[] allPermissions = new String[] { Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_PHONE_STATE };
    private static final String UserToken = "<User_Access_Token>";

    TextView statusBar;

    private CallAgent agent;
    private Call call;
    private Button callButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        callButton = findViewById(R.id.call_button);

        getAllPermissions();
        createAgent();
        callButton.setOnClickListener(l -> startCall());

        Button hangupButton = findViewById(R.id.hangup_button);
        hangupButton.setOnClickListener(l -> endCall());

        statusBar = findViewById(R.id.status_bar);
        
        setVolumeControlStream(AudioManager.STREAM_VOICE_CALL);
    }

    /**
     * Start a call
     */
    private void startCall() {
        if (UserToken.startsWith("<")) {
            Toast.makeText(this, "Please enter token in source code", Toast.LENGTH_SHORT).show();
            return;
        }

        EditText calleeIdView = findViewById(R.id.callee_id);
        String calleeId = calleeIdView.getText().toString();
        if (calleeId.isEmpty()) {
            Toast.makeText(this, "Please enter callee", Toast.LENGTH_SHORT).show();
            return;
        }
        List<CommunicationIdentifier> participants = new ArrayList<>();
        participants.add(new MicrosoftTeamsAppIdentifier(calleeId));
        StartCallOptions options = new StartCallOptions();
        call = agent.startCall(
                getApplicationContext(),
                participants,
                options);
        call.addOnStateChangedListener(p -> setStatus(call.getState().toString()));
    }

    /**
     * Ends the call previously started
     */
    private void endCall() {
        try {
            call.hangUp(new HangUpOptions()).get();
        } catch (ExecutionException | InterruptedException e) {
            Toast.makeText(this, "Unable to hang up call", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Create the call agent
     */
    private void createAgent() {
        try {
            CommunicationTokenCredential credential = new CommunicationTokenCredential(UserToken);
            agent = new CallClient().createCallAgent(getApplicationContext(), credential).get();
        } catch (Exception ex) {
            Toast.makeText(getApplicationContext(), "Failed to create call agent.", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Ensure all permissions were granted, otherwise inform the user permissions are missing.
     */
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, int[] grantResults) {
        boolean allPermissionsGranted = true;
        for (int result : grantResults) {
            allPermissionsGranted &= (result == PackageManager.PERMISSION_GRANTED);
        }
        if (!allPermissionsGranted) {
            Toast.makeText(this, "All permissions are needed to make the call.", Toast.LENGTH_LONG).show();
            finish();
        }
    }

    /**
     * Shows message in the status bar
     */
    private void setStatus(String status) {
        runOnUiThread(() -> statusBar.setText(status));
    }
}

Vyžádání oprávnění za běhu

Pro Android 6.0 a vyšší (úroveň rozhraní API 23) a targetSdkVersion 23 nebo vyšší jsou oprávnění udělena za běhu místo instalace aplikace. Aby bylo možné ho podporovat, getAllPermissions je možné implementovat volání ActivityCompat.checkSelfPermission a ActivityCompat.requestPermissions pro každé požadované oprávnění.

/**
 * Request each required permission if the app doesn't already have it.
 */
private void getAllPermissions() {
    ArrayList<String> permissionsToAskFor = new ArrayList<>();
    for (String permission : allPermissions) {
        if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
            permissionsToAskFor.add(permission);
        }
    }
    if (!permissionsToAskFor.isEmpty()) {
        ActivityCompat.requestPermissions(this, permissionsToAskFor.toArray(new String[0]), 1);
    }
}

Poznámka:

Při návrhu aplikace zvažte, kdy by se tato oprávnění měla požadovat. Oprávnění by se měla vyžadovat podle potřeby, a ne předem. Další informace najdete v průvodci oprávněními androidu .

Objektový model

Následující třídy a rozhraní zpracovávají některé z hlavních funkcí sady SDK pro volání služeb Azure Communication Services:

Název Popis
CallClient Jedná se CallClient o hlavní vstupní bod volající sady SDK.
CallAgent Slouží CallAgent ke spouštění a správě hovorů.
CommunicationTokenCredential Slouží CommunicationTokenCredential jako přihlašovací údaje tokenu k vytvoření instance CallAgent.
CommunicationIdentifier Používá CommunicationIdentifier se jako jiný typ účastníka, který může být součástí hovoru.

Vytvoření agenta z přístupového tokenu uživatele

Pomocí tokenu uživatele je možné vytvořit instanci ověřeného agenta volání. Obecně platí, že tento token se generuje ze služby s ověřováním specifickým pro aplikaci. Další informace o přístupových tokenech uživatelů najdete v průvodci uživatelskými přístupovými tokeny .

V tomto rychlém startu nahraďte <User_Access_Token> přístupovým tokenem uživatele vygenerovaným pro prostředek služby Azure Communication Service.


/**
 * Create the call agent for placing calls
 */
private void createAgent() {
    String userToken = "<User_Access_Token>";

    try {
            CommunicationTokenCredential credential = new CommunicationTokenCredential(userToken);
            callAgent = new CallClient().createCallAgent(getApplicationContext(), credential).get();
    } catch (Exception ex) {
        Toast.makeText(getApplicationContext(), "Failed to create call agent.", Toast.LENGTH_SHORT).show();
    }
}

Spuštění kódu

Aplikaci teď můžete spustit pomocí tlačítka Spustit aplikaci na panelu nástrojů.

Ruční postup nastavení hovoru:

  1. Spusťte aplikaci pomocí Android Studia.
  2. Zadejte ID objektu fronty volání (s předponou) a vyberte tlačítko Zahájit hovor. Aplikace spustí odchozí volání do fronty volání s daným ID objektu.
  3. Volání je připojené k frontě volání.
  4. Uživatel komunikačních služeb je směrován prostřednictvím fronty volání na základě své konfigurace.

V tomto rychlém startu se dozvíte, jak zahájit volání od uživatele Azure Communication Services do fronty volání Teams. Dosáhnete toho pomocí následujících kroků:

  1. Povolte federaci prostředku Azure Communication Services s tenantem Teams.
  2. Vyberte nebo vytvořte frontu volání teams prostřednictvím Centra pro správu Teams.
  3. Získejte e-mailovou adresu fronty volání prostřednictvím Centra pro správu Teams.
  4. Získejte ID objektu fronty volání prostřednictvím rozhraní Graph API.
  5. Začněte volat pomocí sady SDK pro volání služeb Azure Communication Services.

Pokud chcete přeskočit na konec, můžete si tento rychlý start stáhnout jako ukázku na GitHubu.

Povolení interoperability v tenantovi Teams

Uživatel Microsoft Entra s rolí správce Teams může spustit rutinu PowerShellu s modulem MicrosoftTeams a povolit prostředek Komunikační služby v tenantovi.

1. Příprava modulu Microsoft Teams

Nejprve otevřete PowerShell a pomocí následujícího příkazu ověřte existenci modulu Teams:

Get-module *teams* 

Pokud modul nevidíte MicrosoftTeams , nejdřív ho nainstalujte. Pokud chcete nainstalovat modul, musíte spustit PowerShell jako správce. Pak spusťte následující příkaz:

	Install-Module -Name MicrosoftTeams

Budete informováni o nainstalovaných modulech, které můžete potvrdit pomocí Y odpovědi.A Pokud je modul nainstalovaný, ale je zastaralý, můžete spuštěním následujícího příkazu modul aktualizovat:

	Update-Module MicrosoftTeams

2. Připojení k modulu Microsoft Teams

Po instalaci a připravenosti modulu se můžete připojit k modulu MicrosoftTeams pomocí následujícího příkazu. Zobrazí se výzva k přihlášení pomocí interaktivního okna. Uživatelský účet, který budete používat, musí mít oprávnění správce Teams. V opačném případě můžete získat access denied odpověď v dalších krocích.

Connect-MicrosoftTeams

3. Povolení konfigurace tenanta

Interoperabilita s prostředky komunikačních služeb se řídí prostřednictvím konfigurace tenanta a přiřazených zásad. Tenant Teams má jednu konfiguraci tenanta a uživatelé Teams přiřadili globální zásady nebo vlastní zásady. Další informace najdete v tématu Přiřazení zásad v Teams.

Po úspěšném přihlášení můžete spuštěním rutiny Set-CsTeamsAcsFederationConfiguration povolit prostředek Komunikační služby ve vašem tenantovi. Nahraďte text IMMUTABLE_RESOURCE_ID neměnným ID prostředku v komunikačním prostředku. Další podrobnosti o tom, jak tyto informace získat, najdete tady.

$allowlist = @('IMMUTABLE_RESOURCE_ID')
Set-CsTeamsAcsFederationConfiguration -EnableAcsUsers $True -AllowedAcsResources $allowlist

4. Povolení zásad tenanta

Každý uživatel Teams přiřadil, External Access Policy který určuje, jestli uživatelé komunikačních služeb mohou volat tohoto uživatele Teams. Pomocí rutiny Set-CsExternalAccessPolicy zajistěte, aby zásady přiřazené uživateli Teams byly nastaveny EnableAcsFederationAccess na $true

Set-CsExternalAccessPolicy -Identity Global -EnableAcsFederationAccess $true

Vytvoření nebo výběr fronty volání v Teams

Fronta hovorů v Teams je funkce v Microsoft Teams, která efektivně distribuuje příchozí hovory mezi skupinu určených uživatelů nebo agentů. Je užitečné pro scénáře zákaznické podpory nebo call centra. Volání se umístí do fronty a přiřadí se k dalšímu dostupnému agentovi na základě předem určené metody směrování. Agenti přijímají oznámení a můžou zpracovávat hovory pomocí ovládacích prvků volání v Teams. Tato funkce nabízí vytváření sestav a analýzy pro sledování výkonu. Zjednodušuje zpracování hovorů, zajišťuje konzistentní uživatelské prostředí a optimalizuje produktivitu agentů. Můžete vybrat existující nebo vytvořit novou frontu volání prostřednictvím Centra pro správu Teams.

Přečtěte si další informace o tom, jak vytvořit frontu volání pomocí Centra pro správu Teams.

Vyhledání ID objektu pro frontu volání

Po vytvoření fronty volání potřebujeme najít korelované ID objektu, abychom ho mohli později použít pro volání. ID objektu je připojené k účtu prostředku připojenému k frontě volání – otevřete kartu Účty zdrojů v Aplikaci Teams Admin a vyhledejte e-mail. Snímek obrazovky s účty prostředků na portálu pro správu Teams Všechny požadované informace o účtu prostředků najdete v Microsoft Graph Exploreru pomocí tohoto e-mailu ve vyhledávání.

https://graph.microsoft.com/v1.0/users/lab-test2-cq-@contoso.com

Ve výsledcích budeme moct najít pole ID.

    "userPrincipalName": "lab-test2-cq@contoso.com",
    "id": "31a011c2-2672-4dd0-b6f9-9334ef4999db"

Abychom mohli v volající aplikaci používat, musíme k tomuto ID přidat předponu. V současné době jsou podporovány následující položky:

  • Fronta volání veřejného cloudu: 28:orgid:<id>
  • Fronta volání cloudu pro státní správu: 28:gcch:<id>

Požadavky

  • Získejte úč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ý prostředek komunikační služby. Vytvořte prostředek komunikační služby. Pro účely tohoto rychlého startu musíte zaznamenat připojovací řetězec.

  • 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 identity token issue --scope voip --connection-string "yourConnectionString"
    

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

  • Minimální podpora pro aplikace teams volající: 2.15.0

Nastavení

Vytvoření projektu Xcode

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

Snímek obrazovky s oknem Nový projekt v Xcode

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

  1. Chcete-li vytvořit podfile pro vaši aplikaci, otevřete terminál a přejděte do složky projektu a spusťte:

    pod init

  2. Do souboru Podfile přidejte následující kód a uložte ho (ujistěte se, že cíl odpovídá názvu projektu):

    platform :ios, '13.0'
    use_frameworks!
    
    target 'AzureCommunicationCallingSample' do
      pod 'AzureCommunicationCalling', '~> 2.15.0'
    end
    
  3. Spusťte pod install.

  4. Otevřete soubor .xcworkspace 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.

Klikněte pravým tlačítkem myši na Info.plist položku stromu projektu a vyberte Otevřít jako>zdrojový kód. Přidejte následující řádky oddílu nejvyšší úrovně <dict> a pak soubor uložte.

<key>NSMicrophoneUsageDescription</key>
<string>Need microphone access for VOIP calling.</string>

Nastavení architektury aplikace

Otevřete soubor ContentView.swift projektu a přidejte import do horní části souboru deklaraci pro import souboru AzureCommunicationCalling library. Kromě toho AVFoundationpotřebujeme tento kód pro požadavek na zvukové oprávnění v kódu.

import AzureCommunicationCalling
import AVFoundation

Nahraďte implementaci ContentView struktury některými jednoduchými ovládacími prvky uživatelského rozhraní, které uživateli umožňují zahájit a ukončit volání. K těmto ovládacím prvkům připojíme obchodní logiku v tomto rychlém startu.

struct ContentView: View {
    @State var callee: String = ""
    @State var callClient: CallClient?
    @State var callAgent: CallAgent?
    @State var call: Call?

    var body: some View {
        NavigationView {
            Form {
                Section {
                    TextField("Who would you like to call?", text: $callee)
                    Button(action: startCall) {
                        Text("Start Call")
                    }.disabled(callAgent == nil)
                    Button(action: endCall) {
                        Text("End Call")
                    }.disabled(call == nil)
                }
            }
            .navigationBarTitle("Calling Quickstart")
        }.onAppear {
            // Initialize call agent
        }
    }

    func startCall() {
        // Ask permissions
        AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
            if granted {
                // Add start call logic
            }
        }
    }

    func endCall() {
        // Add end call logic
    }
}

Objektový model

Následující třídy a rozhraní zpracovávají některé z hlavních funkcí sady SDK pro volání služeb Azure Communication Services:

Název Popis
CallClient Jedná se CallClient o hlavní vstupní bod volající sady SDK.
CallAgent Slouží CallAgent ke spouštění a správě hovorů.
CommunicationTokenCredential Slouží CommunicationTokenCredential jako přihlašovací údaje tokenu k vytvoření instance CallAgent.
CommunicationUserIdentifier Slouží CommunicationUserIdentifier k reprezentaci identity uživatele, což může být jedna z následujících možností: CommunicationUserIdentifierneboPhoneNumberIdentifierCallingApplication.

Ověření klienta

Inicializace CallAgent instance pomocí přístupového tokenu uživatele, což nám umožňuje provádět a přijímat volání.

V následujícím kódu je nutné nahradit <USER ACCESS TOKEN> platným přístupovým tokenem uživatele pro váš prostředek. Pokud ještě token nemáte k dispozici, projděte si dokumentaci k přístupovým tokenům uživatele.

Do zpětného onAppear volání v ContentView.swift přidejte následující kód:

var userCredential: CommunicationTokenCredential?
do {
    userCredential = try CommunicationTokenCredential(token: "<USER ACCESS TOKEN>")
} catch {
    print("ERROR: It was not possible to create user credential.")
    return
}

self.callClient = CallClient()

// Creates the call agent
self.callClient?.createCallAgent(userCredential: userCredential!) { (agent, error) in
    if error != nil {
        print("ERROR: It was not possible to create a call agent.")
        return
    }
    else {
        self.callAgent = agent
        print("Call agent successfully created.")
    }
}

Zahájení hovoru

Metoda startCall je nastavena jako akce, která se provádí při klepnutí na tlačítko Zahájit volání . Aktualizujte implementaci tak, aby se spustilo volání pomocí ASACallAgentpříkazu :

func startCall()
{
    // Ask permissions
    AVAudioSession.sharedInstance().requestRecordPermission { (granted) in
        if granted {
            // start call logic
            let callees:[CommunicationIdentifier] = [MicrosoftTeamsAppIdentifier(self.callee)]
            self.callAgent?.startCall(participants: callees, options: StartCallOptions()) { (call, error) in
                if (error == nil) {
                    self.call = call
                } else {
                    print("Failed to get call object")
                }
            }
        }
    }
}

Pomocí vlastností StartCallOptions můžete také nastavit počáteční možnosti volání (to znamená, že umožňuje zahájit hovor s ztlumeným mikrofonem).

Ukončení hovoru

Implementujte metodu endCall pro ukončení aktuálního volání při klepnutí na tlačítko Ukončit hovor .

func endCall()
{    
    self.call!.hangUp(options: HangUpOptions()) { (error) in
        if (error != nil) {
            print("ERROR: It was not possible to hangup the call.")
        }
    }
}

Spuštění kódu

Aplikaci můžete sestavit a spustit v simulátoru iOS tak, že vyberete Spuštění produktu>nebo pomocí klávesové zkratky (⌘-R).

Poznámka:

Při prvním volání vás systém vyzve k přístupu k mikrofonu. V produkční aplikaci byste měli použít AVAudioSession rozhraní API ke kontrole stavu oprávnění a řádné aktualizaci chování aplikace v případě, že oprávnění není uděleno.

Ruční postup nastavení hovoru:

  1. Spuštění aplikace pomocí Xcode
  2. Zadejte ID objektu fronty volání (s předponou) a vyberte tlačítko Zahájit hovor. Aplikace spustí odchozí volání do fronty volání s daným ID objektu.
  3. Volání je připojené k frontě volání.
  4. Uživatel komunikačních služeb je směrován prostřednictvím fronty volání na základě své konfigurace.

V tomto rychlém startu se dozvíte, jak zahájit volání od uživatele Azure Communication Services do fronty volání Teams. Dosáhnete toho pomocí následujících kroků:

  1. Povolte federaci prostředku Azure Communication Services s tenantem Teams.
  2. Vyberte nebo vytvořte frontu volání teams prostřednictvím Centra pro správu Teams.
  3. Získejte e-mailovou adresu fronty volání prostřednictvím Centra pro správu Teams.
  4. Získejte ID objektu fronty volání prostřednictvím rozhraní Graph API.
  5. Začněte volat pomocí sady SDK pro volání služeb Azure Communication Services.

Pokud chcete přeskočit na konec, můžete si tento rychlý start stáhnout jako ukázku na GitHubu.

Povolení interoperability v tenantovi Teams

Uživatel Microsoft Entra s rolí správce Teams může spustit rutinu PowerShellu s modulem MicrosoftTeams a povolit prostředek Komunikační služby v tenantovi.

1. Příprava modulu Microsoft Teams

Nejprve otevřete PowerShell a pomocí následujícího příkazu ověřte existenci modulu Teams:

Get-module *teams* 

Pokud modul nevidíte MicrosoftTeams , nejdřív ho nainstalujte. Pokud chcete nainstalovat modul, musíte spustit PowerShell jako správce. Pak spusťte následující příkaz:

	Install-Module -Name MicrosoftTeams

Budete informováni o nainstalovaných modulech, které můžete potvrdit pomocí Y odpovědi.A Pokud je modul nainstalovaný, ale je zastaralý, můžete spuštěním následujícího příkazu modul aktualizovat:

	Update-Module MicrosoftTeams

2. Připojení k modulu Microsoft Teams

Po instalaci a připravenosti modulu se můžete připojit k modulu MicrosoftTeams pomocí následujícího příkazu. Zobrazí se výzva k přihlášení pomocí interaktivního okna. Uživatelský účet, který budete používat, musí mít oprávnění správce Teams. V opačném případě můžete získat access denied odpověď v dalších krocích.

Connect-MicrosoftTeams

3. Povolení konfigurace tenanta

Interoperabilita s prostředky komunikačních služeb se řídí prostřednictvím konfigurace tenanta a přiřazených zásad. Tenant Teams má jednu konfiguraci tenanta a uživatelé Teams přiřadili globální zásady nebo vlastní zásady. Další informace najdete v tématu Přiřazení zásad v Teams.

Po úspěšném přihlášení můžete spuštěním rutiny Set-CsTeamsAcsFederationConfiguration povolit prostředek Komunikační služby ve vašem tenantovi. Nahraďte text IMMUTABLE_RESOURCE_ID neměnným ID prostředku v komunikačním prostředku. Další podrobnosti o tom, jak tyto informace získat, najdete tady.

$allowlist = @('IMMUTABLE_RESOURCE_ID')
Set-CsTeamsAcsFederationConfiguration -EnableAcsUsers $True -AllowedAcsResources $allowlist

4. Povolení zásad tenanta

Každý uživatel Teams přiřadil, External Access Policy který určuje, jestli uživatelé komunikačních služeb mohou volat tohoto uživatele Teams. Pomocí rutiny Set-CsExternalAccessPolicy zajistěte, aby zásady přiřazené uživateli Teams byly nastaveny EnableAcsFederationAccess na $true

Set-CsExternalAccessPolicy -Identity Global -EnableAcsFederationAccess $true

Vytvoření nebo výběr fronty volání v Teams

Fronta hovorů v Teams je funkce v Microsoft Teams, která efektivně distribuuje příchozí hovory mezi skupinu určených uživatelů nebo agentů. Je užitečné pro scénáře zákaznické podpory nebo call centra. Volání se umístí do fronty a přiřadí se k dalšímu dostupnému agentovi na základě předem určené metody směrování. Agenti přijímají oznámení a můžou zpracovávat hovory pomocí ovládacích prvků volání v Teams. Tato funkce nabízí vytváření sestav a analýzy pro sledování výkonu. Zjednodušuje zpracování hovorů, zajišťuje konzistentní uživatelské prostředí a optimalizuje produktivitu agentů. Můžete vybrat existující nebo vytvořit novou frontu volání prostřednictvím Centra pro správu Teams.

Přečtěte si další informace o tom, jak vytvořit frontu volání pomocí Centra pro správu Teams.

Vyhledání ID objektu pro frontu volání

Po vytvoření fronty volání potřebujeme najít korelované ID objektu, abychom ho mohli později použít pro volání. ID objektu je připojené k účtu prostředku připojenému k frontě volání – otevřete kartu Účty zdrojů v Aplikaci Teams Admin a vyhledejte e-mail. Snímek obrazovky s účty prostředků na portálu pro správu Teams Všechny požadované informace o účtu prostředků najdete v Microsoft Graph Exploreru pomocí tohoto e-mailu ve vyhledávání.

https://graph.microsoft.com/v1.0/users/lab-test2-cq-@contoso.com

Ve výsledcích budeme moct najít pole ID.

    "userPrincipalName": "lab-test2-cq@contoso.com",
    "id": "31a011c2-2672-4dd0-b6f9-9334ef4999db"

Abychom mohli v volající aplikaci používat, musíme k tomuto ID přidat předponu. V současné době jsou podporovány následující položky:

  • Fronta volání veřejného cloudu: 28:orgid:<id>
  • Fronta volání cloudu pro státní správu: 28:gcch:<id>

Požadavky

Pro absolvování tohoto kurzu musí být splněné následující požadavky:

  • Účet Azure s aktivním předplatným. Vytvoření účtu zdarma

  • Nainstalujte sadu Visual Studio 2022 s Univerzální platforma Windows úlohou vývoje.

  • Nasazený prostředek komunikační služby. Vytvořte prostředek komunikační služby. Pro účely tohoto rychlého startu musíte zaznamenat připojovací řetězec.

  • 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 identity token issue --scope voip --connection-string "yourConnectionString"
    

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

  • Minimální podpora pro aplikace teams volající: 1.11.0

Nastavení

Vytvoření projektu

V sadě Visual Studio vytvořte nový projekt pomocí šablony Prázdná aplikace (Univerzální windows) pro nastavení jednostránka Univerzální platforma Windows (UPW).

Snímek obrazovky s oknem Nový projekt UPW v sadě Visual Studio

Nainstalujte balíček .

Vyberte projekt pravým tlačítkem a přejděte na Manage Nuget Packages instalaci Azure.Communication.Calling.WindowsClientverze 1.4.0 nebo vyšší. Zkontrolujte, Include Prerelease jestli je zaškrtnuté, jestli chcete zobrazit verze pro verzi Public Preview.

Vyžádat si přístup

Přejděte na Package.appxmanifest a vyberte Capabilities. Zkontrolujte a Internet (Client & Server) získejte Internet (Client) příchozí a odchozí přístup k internetu. Zkontrolujte Microphone , jestli chcete získat přístup ke zvukovému kanálu mikrofonu a Webcam získat přístup k videokamerě.

Snímek obrazovky s žádostí o přístup k internetu a mikrofonu v sadě Visual Studio

Nastavení architektury aplikace

Potřebujeme nakonfigurovat základní rozložení pro připojení naší logiky. Abychom mohli umístit odchozí hovor, musíme TextBox zadat ID uživatele volaného. Potřebujeme Start/Join call také tlačítko a Hang up tlačítko. Součástí Mute této ukázky jsou také zaškrtávací políčka, BackgroundBlur které demonstrují funkce přepínání zvukových stavů a efektů videa.

MainPage.xaml Otevřete projekt a přidejte uzel Grid do svéhoPage:

<Page
    x:Class="CallingQuickstart.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:CallingQuickstart"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}" Width="800" Height="600">

        <!-- Don't forget to replace ‘CallingQuickstart’ with your project’s name -->


    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="16*"/>
            <RowDefinition Height="30*"/>
            <RowDefinition Height="200*"/>
            <RowDefinition Height="60*"/>
            <RowDefinition Height="16*"/>
        </Grid.RowDefinitions>
        <TextBox Grid.Row="1" x:Name="CalleeTextBox" PlaceholderText="Who would you like to call?" TextWrapping="Wrap" VerticalAlignment="Center" Height="30" Margin="10,10,10,10" />

        <Grid x:Name="AppTitleBar" Background="LightSeaGreen">
            <TextBlock x:Name="QuickstartTitle" Text="Calling Quickstart sample title bar" Style="{StaticResource CaptionTextBlockStyle}" Padding="7,7,0,0"/>
        </Grid>

        <Grid Grid.Row="2">
            <Grid.RowDefinitions>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <MediaPlayerElement x:Name="LocalVideo" HorizontalAlignment="Center" Stretch="UniformToFill" Grid.Column="0" VerticalAlignment="Center" AutoPlay="True" />
            <MediaPlayerElement x:Name="RemoteVideo" HorizontalAlignment="Center" Stretch="UniformToFill" Grid.Column="1" VerticalAlignment="Center" AutoPlay="True" />
        </Grid>
        <StackPanel Grid.Row="3" Orientation="Vertical" Grid.RowSpan="2">
            <StackPanel Orientation="Horizontal">
                <Button x:Name="CallButton" Content="Start/Join call" Click="CallButton_Click" VerticalAlignment="Center" Margin="10,0,0,0" Height="40" Width="123"/>
                <Button x:Name="HangupButton" Content="Hang up" Click="HangupButton_Click" VerticalAlignment="Center" Margin="10,0,0,0" Height="40" Width="123"/>
                <CheckBox x:Name="MuteLocal" Content="Mute" Margin="10,0,0,0" Click="MuteLocal_Click" Width="74"/>
            </StackPanel>
        </StackPanel>
        <TextBox Grid.Row="5" x:Name="Stats" Text="" TextWrapping="Wrap" VerticalAlignment="Center" Height="30" Margin="0,2,0,0" BorderThickness="2" IsReadOnly="True" Foreground="LightSlateGray" />
    </Grid>
</Page>

MainPage.xaml.cs Otevřete a nahraďte obsah následující implementací:

using Azure.Communication.Calling.WindowsClient;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Core;
using Windows.Media.Core;
using Windows.Networking.PushNotifications;
using Windows.UI;
using Windows.UI.ViewManagement;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

namespace CallingQuickstart
{
    public sealed partial class MainPage : Page
    {
        private const string authToken = "<AUTHENTICATION_TOKEN>";

        private CallClient callClient;
        private CallTokenRefreshOptions callTokenRefreshOptions = new CallTokenRefreshOptions(false);
        private CallAgent callAgent;
        private CommunicationCall call;

        private LocalOutgoingAudioStream micStream;

        #region Page initialization
        public MainPage()
        {
            this.InitializeComponent();
            // Additional UI customization code goes here
        }

        protected override async void OnNavigatedTo(NavigationEventArgs e)
        {
            await InitCallAgentAndDeviceManagerAsync();

            base.OnNavigatedTo(e);
        }
        #endregion

        #region UI event handlers
        private async void CallButton_Click(object sender, RoutedEventArgs e)
        {
            // Start a call
        }

        private async void HangupButton_Click(object sender, RoutedEventArgs e)
        {
            // Hang up a call
        }

        private async void MuteLocal_Click(object sender, RoutedEventArgs e)
        {
            // Toggle mute/unmute audio state of a call
        }
        #endregion

        #region API event handlers
        private async void OnIncomingCallAsync(object sender, IncomingCallReceivedEventArgs args)
        {
            // Handle incoming call event
        }

        private async void OnStateChangedAsync(object sender, PropertyChangedEventArgs args)
        {
            // Handle connected and disconnected state change of a call
        }
        #endregion

        #region Helper methods

        private async Task InitCallAgentAndDeviceManagerAsync()
        {
            //Initialize the call agent and search for devices
        }


        private async Task<CommunicationCall> StartCallAsync(string acsCallee)
        {
            // Start a call to an Azure Communication Services user using the CallAgent and the callee id
        }

        #endregion
    }
}

Objektový model

V další tabulce jsou uvedené třídy a rozhraní, které zpracovávají některé z hlavních funkcí sady SDK pro volání služeb Azure Communication Services:

Název Popis
CallClient Jedná se CallClient o hlavní vstupní bod volající sady SDK.
CallAgent Slouží CallAgent ke spouštění a správě hovorů.
CommunicationCall Slouží CommunicationCall ke správě probíhajícího hovoru.
CallTokenCredential Slouží CallTokenCredential jako přihlašovací údaje tokenu k vytvoření instance CallAgent.
CallIdentifier Slouží CallIdentifier k reprezentaci identity uživatele, což může být jedna z následujících možností: UserCallIdentifieratd PhoneNumberCallIdentifier .

Ověření klienta

Inicializace CallAgent instance pomocí přístupového tokenu uživatele, který nám umožňuje provádět a přijímat volání, a volitelně získat instanci DeviceManager pro dotazování na konfigurace klientských zařízení.

V kódu nahraďte <AUTHENTICATION_TOKEN> přístupovým tokenem uživatele. Pokud ještě token nemáte k dispozici, projděte si dokumentaci k přístupovým tokenům uživatele.

Přidejte InitCallAgentAndDeviceManagerAsync funkci, která spouští sadu SDK. Tento pomocník je možné přizpůsobit tak, aby splňoval požadavky vaší aplikace.

        private async Task InitCallAgentAndDeviceManagerAsync()
        {
            this.callClient = new CallClient(new CallClientOptions() {
                Diagnostics = new CallDiagnosticsOptions() { 
                    
                    // make sure to put your project AppName
                    AppName = "CallingQuickstart",

                    AppVersion="1.0",

                    Tags = new[] { "Calling", "ACS", "Windows" }
                    }

                });

            // Set up local audio stream using the first mic enumerated
            var deviceManager = await this.callClient.GetDeviceManagerAsync();
            var mic = deviceManager?.Microphones?.FirstOrDefault();

            micStream = new LocalOutgoingAudioStream();

            var tokenCredential = new CallTokenCredential(authToken, callTokenRefreshOptions);

            var callAgentOptions = new CallAgentOptions()
            {
                DisplayName = $"{Environment.MachineName}/{Environment.UserName}",
            };

            this.callAgent = await this.callClient.CreateCallAgentAsync(tokenCredential, callAgentOptions);

            this.callAgent.IncomingCallReceived += OnIncomingCallAsync;
        }

Zahájení hovoru

Jakmile se StartCallOptions objekt získá, CallAgent můžete ho použít k zahájení volání služby Azure Communication Services:

        private async Task<CommunicationCall> StartCallAsync(string acsCallee)
        {
            var options = new StartCallOptions();
            var call = await this.callAgent.StartCallAsync( new [] { new MicrosoftTeamsAppCallIdentifier(acsCallee) }, options);
            return call;
        }

Ukončení hovoru

Ukončení aktuálního hovoru po kliknutí na Hang up tlačítko Přidejte implementaci do HangupButton_Click pro ukončení volání a zastavte streamy náhledu a videa.

        private async void HangupButton_Click(object sender, RoutedEventArgs e)
        {
            var call = this.callAgent?.Calls?.FirstOrDefault();
            if (call != null)
            {
                await call.HangUpAsync(new HangUpOptions() { ForEveryone = false });
            }
        }

Přepnutí ztlumení nebo zrušení ztlumení zvuku

Po kliknutí na Mute tlačítko ztlumte odchozí zvuk. Přidejte implementaci do MuteLocal_Click pro ztlumení volání.

        private async void MuteLocal_Click(object sender, RoutedEventArgs e)
        {
            var muteCheckbox = sender as CheckBox;

            if (muteCheckbox != null)
            {
                var call = this.callAgent?.Calls?.FirstOrDefault();

                if (call != null)
                {
                    if ((bool)muteCheckbox.IsChecked)
                    {
                        await call.MuteOutgoingAudioAsync();
                    }
                    else
                    {
                        await call.UnmuteOutgoingAudioAsync();
                    }
                }

                // Update the UI to reflect the state
            }
        }

Přijetí příchozího hovoru

IncomingCallReceived Jímka událostí je nastavena v pomocné rutině InitCallAgentAndDeviceManagerAsyncsdk bootstrap .

    this.callAgent.IncomingCallReceived += OnIncomingCallAsync;

Aplikace má příležitost ke konfiguraci způsobu přijetí příchozího hovoru, jako jsou druhy video a zvukového streamu.

        private async void OnIncomingCallAsync(object sender, IncomingCallReceivedEventArgs args)
        {
            var incomingCall = args.IncomingCall;

            var acceptCallOptions = new AcceptCallOptions() { };

            call = await incomingCall.AcceptAsync(acceptCallOptions);
            call.StateChanged += OnStateChangedAsync;
        }

Monitorování a reakce na událost změny stavu volání

StateChanged událost objektu CommunicationCall se aktivuje, když probíhající volání transakcí z jednoho stavu do druhého. Aplikace nabízí příležitosti k vyjádření změn stavu v uživatelském rozhraní nebo vložení obchodní logiky.

        private async void OnStateChangedAsync(object sender, PropertyChangedEventArgs args)
        {
            var call = sender as CommunicationCall;

            if (call != null)
            {
                var state = call.State;

                // Update the UI

                switch (state)
                {
                    case CallState.Connected:
                        {
                            await call.StartAudioAsync(micStream);

                            break;
                        }
                    case CallState.Disconnected:
                        {
                            call.StateChanged -= OnStateChangedAsync;

                            call.Dispose();

                            break;
                        }
                    default: break;
                }
            }
        }

Make call button work

Callee ID Jakmile hodnota null nebo není prázdná, můžete zahájit volání.

Stav volání se musí změnit pomocí OnStateChangedAsync akce.


    private async void CallButton_Click(object sender, RoutedEventArgs e)
    {
        var callString = CalleeTextBox.Text.Trim();

        if (!string.IsNullOrEmpty(callString))
        {
            call = await StartCallAsync(callString);

            call.StateChanged += OnStateChangedAsync;
        }
    
        
    }

Spuštění kódu

Kód můžete sestavit a spustit v sadě Visual Studio. Pro platformy řešení podporujeme ARM64, x64a x86.

Ruční postup nastavení hovoru:

  1. Spusťte aplikaci pomocí sady Visual Studio.
  2. Zadejte ID objektu fronty volání (s předponou) a vyberte tlačítko Zahájit hovor. Aplikace spustí odchozí volání do fronty volání s daným ID objektu.
  3. Volání je připojené k frontě volání.
  4. Uživatel komunikačních služeb je směrován prostřednictvím fronty volání na základě své konfigurace.

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: