Skip to content
This repository was archived by the owner on Jan 5, 2026. It is now read-only.

Commit 3f17fe5

Browse files
authored
Update TeamsInfo to support CloudAdapter (#5728)
* teams info update in progress * update TeamsInfo for CloudAdapter and unit test * updated doc comments
1 parent 5d71f0a commit 3f17fe5

2 files changed

Lines changed: 167 additions & 3 deletions

File tree

libraries/Microsoft.Bot.Builder/Teams/TeamsInfo.cs

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,11 @@ public static Task<TeamsChannelAccount> GetMemberAsync(ITurnContext turnContext,
206206
}
207207

208208
/// <summary>
209-
/// Creates a new thread in a team chat and sends an activity to that new thread.
209+
/// 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.
210210
/// </summary>
211211
/// <param name="turnContext"> Turn context. </param>
212-
/// <param name="activity"> ID of the Teams team. </param>
213-
/// <param name="teamsChannelId"> cancellation token. </param>
212+
/// <param name="activity"> The activity to send on starting the new thread. </param>
213+
/// <param name="teamsChannelId"> The Team's Channel ID, note this is distinct from the Bot Framework activity property with same name. </param>
214214
/// <param name="credentials"> Microsoft app credentials. </param>
215215
/// <param name="cancellationToken"> The cancellation token. </param>
216216
/// <returns>Team Details.</returns>
@@ -262,6 +262,59 @@ public static async Task<Tuple<ConversationReference, string>> SendMessageToTeam
262262
return new Tuple<ConversationReference, string>(conversationReference, newActivityId);
263263
}
264264

265+
/// <summary>
266+
/// 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.
267+
/// </summary>
268+
/// <param name="turnContext"> Turn context. </param>
269+
/// <param name="activity"> The activity to send on starting the new thread. </param>
270+
/// <param name="teamsChannelId"> The Team's Channel ID, note this is distinct from the Bot Framework activity property with same name. </param>
271+
/// <param name="botAppId"> The bot's appId. </param>
272+
/// <param name="cancellationToken"> The cancellation token. </param>
273+
/// <returns>Team Details.</returns>
274+
public static async Task<Tuple<ConversationReference, string>> SendMessageToTeamsChannelAsync(ITurnContext turnContext, IActivity activity, string teamsChannelId, string botAppId, CancellationToken cancellationToken = default)
275+
{
276+
if (turnContext == null)
277+
{
278+
throw new ArgumentNullException(nameof(turnContext));
279+
}
280+
281+
if (turnContext.Activity == null)
282+
{
283+
throw new InvalidOperationException(nameof(turnContext.Activity));
284+
}
285+
286+
if (string.IsNullOrEmpty(teamsChannelId))
287+
{
288+
throw new ArgumentNullException(nameof(teamsChannelId));
289+
}
290+
291+
ConversationReference conversationReference = null;
292+
var newActivityId = string.Empty;
293+
var serviceUrl = turnContext.Activity.ServiceUrl;
294+
var conversationParameters = new ConversationParameters
295+
{
296+
IsGroup = true,
297+
ChannelData = new { channel = new { id = teamsChannelId } },
298+
Activity = (Activity)activity,
299+
};
300+
301+
await turnContext.Adapter.CreateConversationAsync(
302+
botAppId,
303+
Channels.Msteams,
304+
serviceUrl,
305+
null,
306+
conversationParameters,
307+
(t, ct) =>
308+
{
309+
conversationReference = t.Activity.GetConversationReference();
310+
newActivityId = t.Activity.Id;
311+
return Task.CompletedTask;
312+
},
313+
cancellationToken).ConfigureAwait(false);
314+
315+
return new Tuple<ConversationReference, string>(conversationReference, newActivityId);
316+
}
317+
265318
private static async Task<IEnumerable<TeamsChannelAccount>> GetMembersAsync(IConnectorClient connectorClient, string conversationId, CancellationToken cancellationToken)
266319
{
267320
if (conversationId == null)

tests/Microsoft.Bot.Builder.Tests/Teams/TeamsInfoTests.cs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Microsoft.Bot.Connector.Authentication;
1313
using Microsoft.Bot.Schema;
1414
using Microsoft.Bot.Schema.Teams;
15+
using Moq;
1516
using Newtonsoft.Json.Linq;
1617
using Xunit;
1718

@@ -51,6 +52,61 @@ public async Task TestSendMessageToTeamsChannelAsync()
5152
await handler.OnTurnAsync(turnContext);
5253
}
5354

55+
[Fact]
56+
public async Task TestSendMessageToTeamsChannel2Async()
57+
{
58+
// Arrange
59+
60+
var expectedTeamsChannelId = "teams-channel-id";
61+
var expectedAppId = "app-id";
62+
var expectedServiceUrl = "service-url";
63+
var expectedActivityId = "activity-id";
64+
var expectedConversationId = "conversation-id";
65+
66+
var requestActivity = new Activity { ServiceUrl = expectedServiceUrl };
67+
68+
var adapter = new TestCreateConversationAdapter(expectedActivityId, expectedConversationId);
69+
70+
var turnContextMock = new Mock<ITurnContext>();
71+
turnContextMock.Setup(tc => tc.Activity).Returns(requestActivity);
72+
turnContextMock.Setup(tc => tc.Adapter).Returns(adapter);
73+
74+
var activity = new Activity
75+
{
76+
Type = "message",
77+
Text = "Test-SendMessageToTeamsChannelAsync",
78+
ChannelId = Channels.Msteams,
79+
ChannelData = new TeamsChannelData
80+
{
81+
Team = new TeamInfo
82+
{
83+
Id = "team-id",
84+
},
85+
},
86+
};
87+
88+
// Act
89+
90+
var r = await TeamsInfo.SendMessageToTeamsChannelAsync(turnContextMock.Object, activity, expectedTeamsChannelId, expectedAppId, CancellationToken.None);
91+
92+
// Assert
93+
94+
Assert.Equal(expectedConversationId, r.Item1.Conversation.Id);
95+
Assert.Equal(expectedActivityId, r.Item2);
96+
Assert.Equal(expectedAppId, adapter.AppId);
97+
Assert.Equal(Channels.Msteams, adapter.ChannelId);
98+
Assert.Equal(expectedServiceUrl, adapter.ServiceUrl);
99+
Assert.Null(adapter.Audience);
100+
101+
var channelData = adapter.ConversationParameters.ChannelData;
102+
103+
var channel = channelData.GetType().GetProperty("channel").GetValue(channelData, null);
104+
var id = channel.GetType().GetProperty("id").GetValue(channel, null);
105+
106+
Assert.Equal(expectedTeamsChannelId, id);
107+
Assert.Equal(adapter.ConversationParameters.Activity, activity);
108+
}
109+
54110
[Fact]
55111
public async Task TestGetMeetingInfoAsync()
56112
{
@@ -586,5 +642,60 @@ protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage reques
586642
return Task.FromResult(response);
587643
}
588644
}
645+
646+
private class TestCreateConversationAdapter : BotAdapter
647+
{
648+
private string _activityId;
649+
650+
private string _conversationId;
651+
652+
public TestCreateConversationAdapter(string activityId, string conversationId)
653+
{
654+
_activityId = activityId;
655+
_conversationId = conversationId;
656+
}
657+
658+
public string AppId { get; set; }
659+
660+
public string ChannelId { get; set; }
661+
662+
public string ServiceUrl { get; set; }
663+
664+
public string Audience { get; set; }
665+
666+
public ConversationParameters ConversationParameters { get; set; }
667+
668+
public override Task CreateConversationAsync(string botAppId, string channelId, string serviceUrl, string audience, ConversationParameters conversationParameters, BotCallbackHandler callback, CancellationToken cancellationToken)
669+
{
670+
AppId = botAppId;
671+
ChannelId = channelId;
672+
ServiceUrl = serviceUrl;
673+
Audience = audience;
674+
ConversationParameters = conversationParameters;
675+
676+
var activity = new Activity { Id = _activityId, Conversation = new ConversationAccount { Id = _conversationId } };
677+
678+
var mockTurnContext = new Mock<ITurnContext>();
679+
mockTurnContext.Setup(tc => tc.Activity).Returns(activity);
680+
681+
callback(mockTurnContext.Object, cancellationToken);
682+
return Task.CompletedTask;
683+
}
684+
685+
public override Task DeleteActivityAsync(ITurnContext turnContext, ConversationReference reference, CancellationToken cancellationToken)
686+
{
687+
throw new NotImplementedException();
688+
}
689+
690+
public override Task<ResourceResponse[]> SendActivitiesAsync(ITurnContext turnContext, Activity[] activities, CancellationToken cancellationToken)
691+
{
692+
throw new NotImplementedException();
693+
}
694+
695+
public override Task<ResourceResponse> UpdateActivityAsync(ITurnContext turnContext, Activity activity, CancellationToken cancellationToken)
696+
{
697+
throw new NotImplementedException();
698+
}
699+
}
589700
}
590701
}

0 commit comments

Comments
 (0)