Skip to content
This repository was archived by the owner on Jan 5, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions libraries/Microsoft.Bot.Builder/Teams/TeamsActivityExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,17 @@ namespace Microsoft.Bot.Builder.Teams
/// </summary>
public static class TeamsActivityExtensions
{
/// <summary>
/// Gets the TeamsMeetingInfo object from the current activity.
/// </summary>
/// <param name="activity">This activity.</param>
/// <returns>The current activity's team's meeting, or null.</returns>
public static TeamsMeetingInfo TeamsGetMeetingInfo(this IActivity activity)
{
var channelData = activity.GetChannelData<TeamsChannelData>();
return channelData?.Meeting;
}

/// <summary>
/// Gets the Team's channel id from the current activity.
/// </summary>
Expand Down
22 changes: 22 additions & 0 deletions libraries/Microsoft.Bot.Builder/Teams/TeamsInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,28 @@ namespace Microsoft.Bot.Builder.Teams
/// </summary>
public static class TeamsInfo
{
/// <summary>
/// Gets the details for the given meeting participant. This only works in teams meeting scoped conversations.
/// </summary>
/// <param name="turnContext">Turn context.</param>
/// <param name="meetingId">The id of the Teams meeting. TeamsChannelData.Meeting.Id will be used if none provided.</param>
/// <param name="participantId">The id of the Teams meeting participant. From.AadObjectId will be used if none provided.</param>
/// <param name="tenantId">The id of the Teams meeting Tenant. TeamsChannelData.Tenant.Id will be used if none provided.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <remarks>InvalidOperationException will be thrown if meetingId, participantId or tenantId have not been
/// provided, and also cannot be retrieved from turnContext.Activity.</remarks>
/// <returns>Team participant channel account.</returns>
public static async Task<TeamsParticipantChannelAccount> GetParticipantAsync(ITurnContext turnContext, string meetingId = null, string participantId = null, string tenantId = null, CancellationToken cancellationToken = default)
Comment thread
EricDahlvang marked this conversation as resolved.
Outdated
{
meetingId ??= turnContext.Activity.TeamsGetMeetingInfo()?.Id ?? throw new InvalidOperationException("This method is only valid within the scope of a MS Teams Meeting.");
participantId ??= turnContext.Activity.From.AadObjectId ?? throw new InvalidOperationException($"{nameof(participantId)} is required.");
tenantId ??= turnContext.Activity.GetChannelData<TeamsChannelData>()?.Tenant?.Id ?? throw new InvalidOperationException($"{nameof(tenantId)} is required.");

#pragma warning disable CA2000 // Dispose objects before losing scope (we need to review this, disposing the connectorClient may have unintended consequences)
return await GetTeamsConnectorClient(turnContext).Teams.FetchParticipantAsync(meetingId, participantId, tenantId, cancellationToken).ConfigureAwait(false);
#pragma warning restore CA2000 // Dispose objects before losing scope
}

/// <summary>
/// Gets the details for the given team id. This only works in teams scoped conversations.
/// </summary>
Expand Down
211 changes: 100 additions & 111 deletions libraries/Microsoft.Bot.Connector/Teams/TeamsOperations.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// <auto-generated>
// <auto-generated>
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for
// license information.
Expand All @@ -13,10 +13,7 @@ namespace Microsoft.Bot.Connector.Teams
using Microsoft.Rest;
using Microsoft.Bot.Schema.Teams;
using Newtonsoft.Json;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
Expand Down Expand Up @@ -101,97 +98,8 @@ public TeamsOperations(TeamsConnectorClient client)
var _baseUrl = Client.BaseUri.AbsoluteUri;
var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "v3/teams/{teamId}/conversations").ToString();
_url = _url.Replace("{teamId}", System.Uri.EscapeDataString(teamId));
// Create HTTP transport objects
var _httpRequest = new HttpRequestMessage();
HttpResponseMessage _httpResponse = null;
_httpRequest.Method = new HttpMethod("GET");
_httpRequest.RequestUri = new System.Uri(_url);
// Set Headers


if (customHeaders != null)
{
foreach(var _header in customHeaders)
{
if (_httpRequest.Headers.Contains(_header.Key))
{
_httpRequest.Headers.Remove(_header.Key);
}
_httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value);
}
}

// Serialize Request
string _requestContent = null;
// Set Credentials
if (Client.Credentials != null)
{
cancellationToken.ThrowIfCancellationRequested();
await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false);
}
// Send Request
if (_shouldTrace)
{
ServiceClientTracing.SendRequest(_invocationId, _httpRequest);
}
cancellationToken.ThrowIfCancellationRequested();
_httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false);
if (_shouldTrace)
{
ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse);
}
HttpStatusCode _statusCode = _httpResponse.StatusCode;
cancellationToken.ThrowIfCancellationRequested();
string _responseContent = null;
if ((int)_statusCode != 200)
{
var ex = new HttpOperationException(string.Format("Operation returned an invalid status code '{0}'", _statusCode));
if (_httpResponse.Content != null) {
_responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
}
else {
_responseContent = string.Empty;
}
ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent);
ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent);
if (_shouldTrace)
{
ServiceClientTracing.Error(_invocationId, ex);
}
_httpRequest.Dispose();
if (_httpResponse != null)
{
_httpResponse.Dispose();
}
throw ex;
}
// Create Result
var _result = new HttpOperationResponse<ConversationList>();
_result.Request = _httpRequest;
_result.Response = _httpResponse;
// Deserialize Response
if ((int)_statusCode == 200)
{
_responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
try
{
_result.Body = Rest.Serialization.SafeJsonConvert.DeserializeObject<ConversationList>(_responseContent, Client.DeserializationSettings);
}
catch (JsonException ex)
{
_httpRequest.Dispose();
if (_httpResponse != null)
{
_httpResponse.Dispose();
}
throw new SerializationException("Unable to deserialize the response.", _responseContent, ex);
}
}
if (_shouldTrace)
{
ServiceClientTracing.Exit(_invocationId, _result);
}
return _result;
return await GetResponseAsync<ConversationList>(_url, _shouldTrace, _invocationId);
}

/// <summary>
Expand Down Expand Up @@ -245,17 +153,97 @@ public TeamsOperations(TeamsConnectorClient client)
var _baseUrl = Client.BaseUri.AbsoluteUri;
var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "v3/teams/{teamId}").ToString();
_url = _url.Replace("{teamId}", System.Uri.EscapeDataString(teamId));

return await GetResponseAsync<TeamDetails>(_url, _shouldTrace, _invocationId);
}

/// <summary>
/// Fetches Teams meeting participant details.
/// </summary>
/// <remarks>
/// Fetches details for a meeting particpant.
/// </remarks>
/// <param name='meetingId'>
/// Teams meeting id
/// </param>
/// <param name='participantId'>
/// Teams meeting participant id
/// </param>
/// <param name='tenantId'>
/// Teams meeting tenant id
/// </param>
/// <param name='customHeaders'>
/// Headers that will be added to request.
/// </param>
/// <param name='cancellationToken'>
/// The cancellation token.
/// </param>
/// <exception cref="HttpOperationException">
/// Thrown when the operation returned an invalid status code
/// </exception>
/// <exception cref="SerializationException">
/// Thrown when unable to deserialize the response
/// </exception>
/// <exception cref="ValidationException">
/// Thrown when a required parameter is null
/// </exception>
/// <exception cref="System.ArgumentNullException">
/// Thrown when a required parameter is null
/// </exception>
/// <return>
/// A response object containing the response body and response headers.
/// </return>
public async Task<HttpOperationResponse<TeamsParticipantChannelAccount>> FetchParticipantWithHttpMessagesAsync(string meetingId, string participantId, string tenantId, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
{
if (meetingId == null)
{
throw new ValidationException(ValidationRules.CannotBeNull, nameof(meetingId));
}

if (participantId == null)
{
throw new ValidationException(ValidationRules.CannotBeNull, nameof(participantId));
}

if (tenantId == null)
{
throw new ValidationException(ValidationRules.CannotBeNull, nameof(tenantId));
}

// Tracing
bool _shouldTrace = ServiceClientTracing.IsEnabled;
string _invocationId = null;
if (_shouldTrace)
{
_invocationId = ServiceClientTracing.NextInvocationId.ToString();
Dictionary<string, object> tracingParameters = new Dictionary<string, object>();
tracingParameters.Add("meetingId", meetingId);
tracingParameters.Add("participantId", participantId);
tracingParameters.Add("tenantId", tenantId);
tracingParameters.Add("cancellationToken", cancellationToken);
ServiceClientTracing.Enter(_invocationId, this, "GetParticipant", tracingParameters);
}
// Construct URL
var _baseUrl = Client.BaseUri.AbsoluteUri;
var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "v1/meetings/{meetingId}/participants/{participantId}?tenantId={tenantId}").ToString();
_url = _url.Replace("{meetingId}", System.Uri.EscapeDataString(meetingId));
_url = _url.Replace("{participantId}", System.Uri.EscapeDataString(participantId));
_url = _url.Replace("{tenantId}", System.Uri.EscapeDataString(tenantId));

return await GetResponseAsync<TeamsParticipantChannelAccount>(_url, _shouldTrace, _invocationId);
}

private async Task<HttpOperationResponse<T>> GetResponseAsync<T>(string url, bool shouldTrace, string invocationId, Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
{
// Create HTTP transport objects
var _httpRequest = new HttpRequestMessage();
HttpResponseMessage _httpResponse = null;
_httpRequest.Method = new HttpMethod("GET");
_httpRequest.RequestUri = new System.Uri(_url);
_httpRequest.RequestUri = new System.Uri(url);
// Set Headers


if (customHeaders != null)
{
foreach(var _header in customHeaders)
foreach (var _header in customHeaders)
{
if (_httpRequest.Headers.Contains(_header.Key))
{
Expand All @@ -274,33 +262,35 @@ public TeamsOperations(TeamsConnectorClient client)
await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false);
}
// Send Request
if (_shouldTrace)
if (shouldTrace)
{
ServiceClientTracing.SendRequest(_invocationId, _httpRequest);
ServiceClientTracing.SendRequest(invocationId, _httpRequest);
}
cancellationToken.ThrowIfCancellationRequested();
_httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false);
if (_shouldTrace)
if (shouldTrace)
{
ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse);
ServiceClientTracing.ReceiveResponse(invocationId, _httpResponse);
}
HttpStatusCode _statusCode = _httpResponse.StatusCode;
cancellationToken.ThrowIfCancellationRequested();
string _responseContent = null;
if ((int)_statusCode != 200)
{
var ex = new HttpOperationException(string.Format("Operation returned an invalid status code '{0}'", _statusCode));
if (_httpResponse.Content != null) {
if (_httpResponse.Content != null)
{
_responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
}
else {
else
{
_responseContent = string.Empty;
}
ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent);
ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent);
if (_shouldTrace)
if (shouldTrace)
{
ServiceClientTracing.Error(_invocationId, ex);
ServiceClientTracing.Error(invocationId, ex);
}
_httpRequest.Dispose();
if (_httpResponse != null)
Expand All @@ -310,7 +300,7 @@ public TeamsOperations(TeamsConnectorClient client)
throw ex;
}
// Create Result
var _result = new HttpOperationResponse<TeamDetails>();
var _result = new HttpOperationResponse<T>();
_result.Request = _httpRequest;
_result.Response = _httpResponse;
// Deserialize Response
Expand All @@ -319,7 +309,7 @@ public TeamsOperations(TeamsConnectorClient client)
_responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false);
try
{
_result.Body = Rest.Serialization.SafeJsonConvert.DeserializeObject<TeamDetails>(_responseContent, Client.DeserializationSettings);
_result.Body = Rest.Serialization.SafeJsonConvert.DeserializeObject<T>(_responseContent, Client.DeserializationSettings);
}
catch (JsonException ex)
{
Expand All @@ -331,12 +321,11 @@ public TeamsOperations(TeamsConnectorClient client)
throw new SerializationException("Unable to deserialize the response.", _responseContent, ex);
}
}
if (_shouldTrace)
if (shouldTrace)
{
ServiceClientTracing.Exit(_invocationId, _result);
ServiceClientTracing.Exit(invocationId, _result);
}
return _result;
}

}
}
Loading