代码分析:通过 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` 后,代码实际上不多,比较简单。