You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Microsoft.Graph.Communications SDK的Teams呼叫操作及代码评审

Hey there! Let's tackle your two technical requests with clear explanations and actionable fixes:

1. Using Microsoft.Graph.Communications SDK for Core Teams Call Operations

Here's a straightforward breakdown of how to implement call creation, answering, and transfer:

Creating a Teams Call

First, ensure your StatefulClient is initialized correctly (like in your Bot constructor). Then define the target participant and media settings to place the call:

// Initialize your stateful client (reuse your existing builder logic)
var client = new StatefulClientBuilder("YourBotName", "YourAppId", graphLogger)
    .SetAuthenticationProvider(authProvider)
    .SetNotificationUri(notificationUri)
    .Build();

// Define the target user for the call
var targetParticipant = new InvitationParticipantInfo
{
    Identity = new IdentitySet
    {
        User = new Identity
        {
            Id = "target-user-object-id",
            AdditionalData = new Dictionary<string, object> { { "tenantId", "your-tenant-id" } }
        }
    },
    EndpointType = EndpointType.Default
};

// Configure media (audio-only for IVR scenarios)
var mediaConfig = new List<MediaInfo>();

// Place the call
var newCall = await client.Calls().PlaceCallAsync(
    new[] { targetParticipant },
    mediaConfig,
    new[] { Modality.Audio });

Answering an Incoming Call

Handle the OnIncoming event to accept the call asynchronously, with proper error handling:

private void CallsOnIncoming(ICallCollection sender, CollectionEventArgs<ICall> args)
{
    var incomingCall = args.AddedResources.First();
    var mediaConfig = new List<MediaInfo>();

    Task.Run(async () =>
    {
        try
        {
            await incomingCall.AnswerAsync(mediaConfig, new[] { Modality.Audio }).ConfigureAwait(false);
            graphLogger.Info("Incoming call answered successfully");
            // Attach your call handler logic here
        }
        catch (Exception ex)
        {
            graphLogger.Error(ex, "Failed to answer incoming call");
        }
    });
}

Transferring an Active Call

Critical Note: You can only transfer a call after it has been successfully answered and is in an active state. Here's how to execute it properly:

// Assuming you have an active call instance (from OnIncoming or your call handler)
var transferTarget = new InvitationParticipantInfo
{
    Identity = new IdentitySet
    {
        User = new Identity
        {
            Id = "transfer-target-user-id",
            AdditionalData = new Dictionary<string, object> { { "tenantId", "your-tenant-id" } }
        }
    },
    EndpointType = EndpointType.Default
};

// Execute transfer only after the call is connected
await activeCall.TransferAsync(transferTarget).ConfigureAwait(false);
2. Fixing Your IVR Bot Transfer Code

Looking at your modified CallsOnIncoming method, the core issue is you're trying to transfer the call before it's been fully answered. The Graph API won't process a transfer request for a call that's still in the incoming state. Here's the breakdown and fix:

What Was Broken

  • You commented out the await answerTask call, so the call never completes the answer process before you attempt transfer.
  • Even if you hadn't commented it out, you were triggering both answerTask and transferTask in parallel, meaning the transfer might execute before the call is active.

Fixed Code

Adjust the logic to wait for the call to be answered first, then initiate the transfer:

private void CallsOnIncoming(ICallCollection sender, CollectionEventArgs<ICall> args)
{
    var call = args.AddedResources.First();
    
    // Keep your existing target setup
    Dictionary<string, object> _additionalData = new Dictionary<string, object>();
    _additionalData.Add("tenantId", "72f988bf-86f1-41af-91ab-2d7cd011db47");
    var target = new InvitationParticipantInfo
    {
        LanguageId = "en-US",
        Region = "westus",
        EndpointType = EndpointType.Default,
        Identity = new IdentitySet
        {
            User = new Identity
            {
                AdditionalData = _additionalData,
                Id = "77b1cacb-c38b-4b57-a4a5-f5919360bc24"
            }
        }
    };

    var callee = call.Resource.Targets.First();
    var callType = callee?.Identity?.GetApplicationInstance() == null ? CallType.BotIncoming : CallType.BotEndpointIncoming;
    var callHandler = new IncomingCallHandler(this, call);
    this.callHandlers[call.Resource.Id] = callHandler;

    var mediaConfig = new List<MediaInfo>();

    // Async workflow: Answer first, THEN transfer
    Task.Run(async () =>
    {
        try
        {
            // Wait for the call to be fully answered
            await call.AnswerAsync(mediaConfig, new[] { Modality.Audio }).ConfigureAwait(false);
            this.graphLogger.Info("Incoming call answered successfully");

            // Now transfer the active call
            await call.TransferAsync(target).ConfigureAwait(false);
            this.graphLogger.Info("Call transferred successfully to target user");
        }
        catch (Exception ex)
        {
            this.graphLogger.Error(ex, $"Error handling call: {ex.Message}");
        }
    });
}

Additional Checks to Ensure Success

  1. Permissions: Verify your app has the required Graph API permissions: Calls.Initiate.All and Calls.AccessMedia.All (make sure admin consent is granted).
  2. Target Validation: Double-check the target user's ID and tenant ID are correct—ensure the user is active in Teams.
  3. Logging: Check your Application Insights or graph logs for specific error messages if the transfer still fails (common issues include permission errors or invalid target identities).

内容的提问来源于stack exchange,提问作者Miguel

火山引擎 最新活动