Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
59 changes: 56 additions & 3 deletions libraries/Microsoft.Bot.Builder/Teams/TeamsInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,11 +206,11 @@ public static Task<TeamsChannelAccount> GetMemberAsync(ITurnContext turnContext,
}

/// <summary>
/// Creates a new thread in a team chat and sends an activity to that new thread.
/// Creates a new thread in a team chat and sends an activity to that new thread. Use this method if you are using BotFrameworkAdapter and are handling credentials in your code.
/// </summary>
/// <param name="turnContext"> Turn context. </param>
/// <param name="activity"> ID of the Teams team. </param>
/// <param name="teamsChannelId"> cancellation token. </param>
/// <param name="activity"> The activity to send on starting the new thread. </param>
/// <param name="teamsChannelId"> The Team's Channel ID, note this is distinct from the Bot Framework activity property with same name. </param>
/// <param name="credentials"> Microsoft app credentials. </param>
/// <param name="cancellationToken"> The cancellation token. </param>
/// <returns>Team Details.</returns>
Expand Down Expand Up @@ -262,6 +262,59 @@ public static async Task<Tuple<ConversationReference, string>> SendMessageToTeam
return new Tuple<ConversationReference, string>(conversationReference, newActivityId);
}

/// <summary>
/// Creates a new thread in a team chat and sends an activity to that new thread. Use this method if you are using CloudAdapter where credentials are handled by the adapter.
/// </summary>
/// <param name="turnContext"> Turn context. </param>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: spacing looks odd before/after the param comments here

/// <param name="activity"> The activity to send on starting the new thread. </param>
/// <param name="teamsChannelId"> The Team's Channel ID, note this is distinct from the Bot Framework activity property with same name. </param>
/// <param name="botAppId"> The bot's appId. </param>
/// <param name="cancellationToken"> The cancellation token. </param>
/// <returns>Team Details.</returns>
public static async Task<Tuple<ConversationReference, string>> SendMessageToTeamsChannelAsync(ITurnContext turnContext, IActivity activity, string teamsChannelId, string botAppId, CancellationToken cancellationToken = default)
{
if (turnContext == null)
{
throw new ArgumentNullException(nameof(turnContext));
}

if (turnContext.Activity == null)
{
throw new InvalidOperationException(nameof(turnContext.Activity));
}

if (string.IsNullOrEmpty(teamsChannelId))
{
throw new ArgumentNullException(nameof(teamsChannelId));
}

ConversationReference conversationReference = null;
var newActivityId = string.Empty;
var serviceUrl = turnContext.Activity.ServiceUrl;
var conversationParameters = new ConversationParameters
{
IsGroup = true,
ChannelData = new { channel = new { id = teamsChannelId } },
Activity = (Activity)activity,
};

await turnContext.Adapter.CreateConversationAsync(
botAppId,
Channels.Msteams,
serviceUrl,
null,
conversationParameters,
(t, ct) =>
{
conversationReference = t.Activity.GetConversationReference();
newActivityId = t.Activity.Id;
return Task.CompletedTask;
},
cancellationToken).ConfigureAwait(false);

return new Tuple<ConversationReference, string>(conversationReference, newActivityId);
}

private static async Task<IEnumerable<TeamsChannelAccount>> GetMembersAsync(IConnectorClient connectorClient, string conversationId, CancellationToken cancellationToken)
{
if (conversationId == null)
Expand Down
111 changes: 111 additions & 0 deletions tests/Microsoft.Bot.Builder.Tests/Teams/TeamsInfoTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.Bot.Connector.Authentication;
using Microsoft.Bot.Schema;
using Microsoft.Bot.Schema.Teams;
using Moq;
using Newtonsoft.Json.Linq;
using Xunit;

Expand Down Expand Up @@ -51,6 +52,61 @@ public async Task TestSendMessageToTeamsChannelAsync()
await handler.OnTurnAsync(turnContext);
}

[Fact]
public async Task TestSendMessageToTeamsChannel2Async()
{
// Arrange

var expectedTeamsChannelId = "teams-channel-id";
var expectedAppId = "app-id";
var expectedServiceUrl = "service-url";
var expectedActivityId = "activity-id";
var expectedConversationId = "conversation-id";

var requestActivity = new Activity { ServiceUrl = expectedServiceUrl };

var adapter = new TestCreateConversationAdapter(expectedActivityId, expectedConversationId);

var turnContextMock = new Mock<ITurnContext>();
turnContextMock.Setup(tc => tc.Activity).Returns(requestActivity);
turnContextMock.Setup(tc => tc.Adapter).Returns(adapter);

var activity = new Activity
{
Type = "message",
Text = "Test-SendMessageToTeamsChannelAsync",
ChannelId = Channels.Msteams,
ChannelData = new TeamsChannelData
{
Team = new TeamInfo
{
Id = "team-id",
},
},
};

// Act

var r = await TeamsInfo.SendMessageToTeamsChannelAsync(turnContextMock.Object, activity, expectedTeamsChannelId, expectedAppId, CancellationToken.None);

// Assert

Assert.Equal(expectedConversationId, r.Item1.Conversation.Id);
Assert.Equal(expectedActivityId, r.Item2);
Assert.Equal(expectedAppId, adapter.AppId);
Assert.Equal(Channels.Msteams, adapter.ChannelId);
Assert.Equal(expectedServiceUrl, adapter.ServiceUrl);
Assert.Null(adapter.Audience);

var channelData = adapter.ConversationParameters.ChannelData;

var channel = channelData.GetType().GetProperty("channel").GetValue(channelData, null);
var id = channel.GetType().GetProperty("id").GetValue(channel, null);

Assert.Equal(expectedTeamsChannelId, id);
Assert.Equal(adapter.ConversationParameters.Activity, activity);
}

[Fact]
public async Task TestGetMeetingInfoAsync()
{
Expand Down Expand Up @@ -586,5 +642,60 @@ protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage reques
return Task.FromResult(response);
}
}

private class TestCreateConversationAdapter : BotAdapter
{
private string _activityId;

private string _conversationId;

public TestCreateConversationAdapter(string activityId, string conversationId)
{
_activityId = activityId;
_conversationId = conversationId;
}

public string AppId { get; set; }

public string ChannelId { get; set; }

public string ServiceUrl { get; set; }

public string Audience { get; set; }

public ConversationParameters ConversationParameters { get; set; }

public override Task CreateConversationAsync(string botAppId, string channelId, string serviceUrl, string audience, ConversationParameters conversationParameters, BotCallbackHandler callback, CancellationToken cancellationToken)
{
AppId = botAppId;
ChannelId = channelId;
ServiceUrl = serviceUrl;
Audience = audience;
ConversationParameters = conversationParameters;

var activity = new Activity { Id = _activityId, Conversation = new ConversationAccount { Id = _conversationId } };

var mockTurnContext = new Mock<ITurnContext>();
mockTurnContext.Setup(tc => tc.Activity).Returns(activity);

callback(mockTurnContext.Object, cancellationToken);
return Task.CompletedTask;
}

public override Task DeleteActivityAsync(ITurnContext turnContext, ConversationReference reference, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

public override Task<ResourceResponse[]> SendActivitiesAsync(ITurnContext turnContext, Activity[] activities, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}

public override Task<ResourceResponse> UpdateActivityAsync(ITurnContext turnContext, Activity activity, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
}
}