Skip to the content.

代码分析:通过 ACS 将用户加入到 Teams 会议

上一篇文章我们讲了如何让自己的 webapp 程序可以加入到 teams 的会议中。这篇文章我们来详细看一下代码。

代码说明:创建 User Access Token

上篇文章我们用了一段 c# 代码来生成 user access token。

class Program
{
    static async Task Main(string[] args)
    {
        string connectionString = Environment.GetEnvironmentVariable("COMMUNICATION_SERVICES_CONNECTION_STRING");
        var client = new CommunicationIdentityClient(connectionString);

        // Create an identity
        var identityResponse = await client.CreateUserAsync();
        var identity = identityResponse.Value;
        Console.WriteLine($"\nCreated an identity with ID: {identity.Id}");

        // Issue an access token with a validity of 24 hours and the "voip" scope for an identity
        var tokenResponse = await client.GetTokenAsync(identity, scopes: new[] { CommunicationTokenScope.VoIP });
        var token = tokenResponse.Value.Token;
        var expiresOn = tokenResponse.Value.ExpiresOn;
        Console.WriteLine($"\nIssued an access token with 'voip' scope that expires at {expiresOn}:");
        Console.WriteLine(token);
    }
}

这段代码使用了 Azure.Identity、Azure.Communication.Identity、Azure.Core、Azure.Communication 和 System 这几个库。

首先,它使用了环境变量 COMMUNICATION_SERVICES_CONNECTION_STRING 来获取连接字符串,然后使用该连接字符串创建一个 CommunicationIdentityClient 实例。

然后,它使用该实例调用 CreateUserAsync 方法来创建一个新的身份。

接着,它使用该实例调用 GetTokenAsync 方法来请求一个有效期为 24 小时,且包含 “voip” 权限的访问令牌。返回的结果里有我们需要的 token 和 token 过期的时间。

实际上我们也可以使用 python 来生成 user access token。

import os
from datetime import timedelta
from azure.communication.identity import CommunicationIdentityClient, CommunicationUserIdentifier

connection_string = os.environ["COMMUNICATION_SERVICES_CONNECTION_STRING"]
client = CommunicationIdentityClient.from_connection_string(connection_string)

# Create an identity
identity = client.create_user()
existingIdentity = identity	

# Issue an access token with a validity of 24 hours and the "voip" scope for an identity
token_result = client.get_token(identity, ["voip"])
print("\nIssued an access token with 'voip' scope that expires at " + token_result.expires_on + ":")
print(token_result.token)

我们实际上还可以刷新,回撤 token。

// Refresh access tokens
var identityToRefresh = new CommunicationUserIdentifier(identity.Id);
var refreshTokenResponse = await client.GetTokenAsync(identityToRefresh, scopes: new[] { CommunicationTokenScope.VoIP });

// Revoke access tokens
await client.RevokeTokensAsync(identity);
Console.WriteLine($"\nSuccessfully revoked all access tokens for identity with ID: {identity.Id}");

// Delete an identity
await client.DeleteUserAsync(identity);
Console.WriteLine($"\nDeleted the identity with ID: {identity.Id}");

代码说明:通过 ACS 将用户加入到 Teams 会议

代码由两部分组成,一个是html 页面

<!DOCTYPE html>
<html>
<head>
    <title>Communication Client - Calling Sample</title>
</head>
<body>
    <h4>Azure Communication Services</h4>
    <h1>Teams meeting join quickstart</h1>
    <input id="teams-link-input" type="text" placeholder="Teams meeting link"
           style="margin-bottom:1em; width: 300px;" />
    <p>Call state <span style="font-weight: bold" id="call-state">-</span></p>
    <p><span style="font-weight: bold" id="recording-state"></span></p>
    <div>
        <button id="join-meeting-button" type="button" disabled="false">
            Join Teams Meeting
        </button>
        <button id="hang-up-button" type="button" disabled="true">
            Hang Up
        </button>
    </div>
    <script src="./bundle.js"></script>
</body>

</html>

HTML 文件定义了一个简单的网页界面,其中包含一个输入框用于输入 Teams 会议链接,两个按钮用于加入/退出会议,以及显示当前通话状态和录音状态的文本。

另一个是 javascript client.js 主文件:

import { CallClient } from "@azure/communication-calling";
import { Features } from "@azure/communication-calling";
import { AzureCommunicationTokenCredential } from '@azure/communication-common';

let call;
let callAgent;
const meetingLinkInput = document.getElementById('teams-link-input');
const hangUpButton = document.getElementById('hang-up-button');
const teamsMeetingJoinButton = document.getElementById('join-meeting-button');
const callStateElement = document.getElementById('call-state');
const recordingStateElement = document.getElementById('recording-state');

async function init() {
    const callClient = new CallClient();
    const tokenCredential = new AzureCommunicationTokenCredential("<USER ACCESS TOKEN>");
    callAgent = await callClient.createCallAgent(tokenCredential, { displayName: 'ACS user' });
    teamsMeetingJoinButton.disabled = false;
}
init();

hangUpButton.addEventListener("click", async () => {
    await call.hangUp();
    hangUpButton.disabled = true;
    teamsMeetingJoinButton.disabled = false;
    callStateElement.innerText = '-';
});

teamsMeetingJoinButton.addEventListener("click", () => {
    call = callAgent.join({ meetingLink: meetingLinkInput.value }, {});

    call.on('stateChanged', () => {
        callStateElement.innerText = call.state;
    })

    call.feature(Features.Recording).on('isRecordingActiveChanged', () => {
        if (call.feature(Features.Recording).isRecordingActive) {
            recordingStateElement.innerText = "This call is being recorded";
        }
        else {
            recordingStateElement.innerText = "";
        }
    });
    hangUpButton.disabled = false;
    teamsMeetingJoinButton.disabled = true;
});

这段 JavaScript 代码是一个简单的前端示例,用于使用 Azure Communication Services 加入 Teams 会议。它包括两个主要部分:

初始化:在页面加载完成后,会自动调用 init() 函数。该函数使用 CallClient 类创建一个 callAgent 对象,并使用 AzureCommunicationTokenCredential 类将用户访问令牌传递给 callAgent。

事件监听:页面上有两个按钮,“Join Teams Meeting” 和 “Hang Up”。当用户点击 “Join Teams Meeting” 按钮时,会调用 callAgent.join() 方法加入会议,并将会议链接从输入框中读取。当用户点击 “Hang Up” 按钮时,会调用 call.hangUp() 方法挂断当前通话。

在这两个按钮的点击事件中还有一些其他的功能:

  • 当用户点击 “Join Teams Meeting” 按钮时,会启用 “Hang Up” 按钮,并禁用 “Join Teams Meeting” 按钮。
  • 当用户点击 “Hang Up” 按钮时,会禁用 “Hang Up” 按钮,并启用 “Join Teams Meeting” 按钮。
  • 当用户点击 “Join Teams Meeting” 按钮时,会监听通话状态更改事件,并在页面上显示当前通话状态。
  • 当用户点击 “Join Teams Meeting” 按钮时,会监听录制状态更改

可以看到当我们使用了官方的 sdk package @azure/communication-calling 和 ``@azure/communication-common` 后,代码实际上不多,比较简单。

Written on June 2, 2022