microsoft/teams.net

Public

mirrored fromhttps://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
samples/migration-bot

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

core/src/Microsoft.Teams.Bot.Compat/CompatTeamsInfo.cs

682lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4using Microsoft.Bot.Builder;
5using Microsoft.Bot.Connector;
6using Microsoft.Bot.Schema;
7using Microsoft.Bot.Schema.Teams;
8using Microsoft.Teams.Bot.Apps;
9using Microsoft.Teams.Bot.Apps.Schema;
10using Microsoft.Teams.Bot.Core;
11using Microsoft.Teams.Bot.Core.Schema;
12using AppsTeams = Microsoft.Teams.Bot.Apps;
13using BotFrameworkTeams = Microsoft.Bot.Schema.Teams;
14
15namespace Microsoft.Teams.Bot.Compat;
16
17/// <summary>
18/// Provides utility methods for the events and interactions that occur within Microsoft Teams.
19/// This class adapts the Teams Bot Core SDK to the Bot Framework v4 SDK TeamsInfo API.
20/// </summary>
21public static class CompatTeamsInfo
22{
23 #region Helper Methods
24
25 private static readonly System.Text.Json.JsonSerializerOptions s_jsonOptions = new()
26 {
27 PropertyNameCaseInsensitive = true
28 };
29
30 private static ConversationClient GetConversationClient(ITurnContext turnContext)
31 {
32 IConnectorClient connectorClient = turnContext.TurnState.Get<IConnectorClient>()
33 ?? throw new InvalidOperationException("This method requires a connector client.");
34
35 if (connectorClient is CompatConnectorClient compatClient)
36 {
37 return ((CompatConversations)compatClient.Conversations)._client;
38 }
39
40 throw new InvalidOperationException("Connector client is not compatible.");
41 }
42
43 private static TeamsApiClient GetTeamsApiClient(ITurnContext turnContext)
44 {
45 return turnContext.TurnState.Get<TeamsApiClient>()
46 ?? throw new InvalidOperationException("This method requires TeamsApiClient.");
47 }
48
49 private static string GetServiceUrl(ITurnContext turnContext)
50 {
51 return turnContext.Activity.ServiceUrl
52 ?? throw new InvalidOperationException("ServiceUrl is required.");
53 }
54
55 private static AgenticIdentity GetIdentity(ITurnContext turnContext)
56 {
57 CoreActivity coreActivity = turnContext.Activity.FromCompatActivity();
58 return AgenticIdentity.FromProperties(coreActivity.From?.Properties) ?? new AgenticIdentity();
59 }
60
61 #endregion
62
63 #region Member & Participant Methods
64
65 /// <summary>
66 /// Gets the account of a single conversation member.
67 /// This works in one-on-one, group, and teams scoped conversations.
68 /// </summary>
69 /// <param name="turnContext">Turn context.</param>
70 /// <param name="userId">ID of the user in question.</param>
71 /// <param name="cancellationToken">Cancellation token.</param>
72 /// <returns>The member's channel account information.</returns>
73 public static async Task<BotFrameworkTeams.TeamsChannelAccount> GetMemberAsync(
74 ITurnContext turnContext,
75 string userId,
76 CancellationToken cancellationToken = default)
77 {
78 ArgumentNullException.ThrowIfNull(turnContext);
79 TeamInfo? teamInfo = turnContext.Activity.TeamsGetTeamInfo();
80
81 if (teamInfo?.Id != null)
82 {
83 return await GetTeamMemberAsync(turnContext, userId, teamInfo.Id, cancellationToken).ConfigureAwait(false);
84 }
85 else
86 {
87 string conversationId = turnContext.Activity?.Conversation?.Id
88 ?? throw new InvalidOperationException("The GetMember operation needs a valid conversation Id.");
89
90 if (userId == null)
91 {
92 throw new InvalidOperationException("The GetMember operation needs a valid user Id.");
93 }
94
95 ConversationClient client = GetConversationClient(turnContext);
96 Uri serviceUrl = new(GetServiceUrl(turnContext));
97 AgenticIdentity identity = GetIdentity(turnContext);
98
99 TeamsConversationAccount result = await client.GetConversationMemberAsync<Microsoft.Teams.Bot.Apps.Schema.TeamsConversationAccount>(
100 conversationId, userId, serviceUrl, identity, null, cancellationToken).ConfigureAwait(false);
101
102 return result.ToCompatTeamsChannelAccount();
103 }
104 }
105
106 /// <summary>
107 /// Gets the conversation members of a one-on-one or group chat.
108 /// </summary>
109 /// <param name="turnContext">Turn context.</param>
110 /// <param name="cancellationToken">Cancellation token.</param>
111 /// <returns>List of channel accounts.</returns>
112 [Obsolete("Microsoft Teams is deprecating the non-paged version of the getMembers API which this method uses. Please use GetPagedMembersAsync instead of this API.")]
113 public static async Task<IEnumerable<BotFrameworkTeams.TeamsChannelAccount>> GetMembersAsync(
114 ITurnContext turnContext,
115 CancellationToken cancellationToken = default)
116 {
117 ArgumentNullException.ThrowIfNull(turnContext);
118 TeamInfo? teamInfo = turnContext.Activity.TeamsGetTeamInfo();
119
120 if (teamInfo?.Id != null)
121 {
122 return await GetTeamMembersAsync(turnContext, teamInfo.Id, cancellationToken).ConfigureAwait(false);
123 }
124 else
125 {
126 string conversationId = turnContext.Activity?.Conversation?.Id
127 ?? throw new InvalidOperationException("The GetMembers operation needs a valid conversation Id.");
128
129 ConversationClient client = GetConversationClient(turnContext);
130 Uri serviceUrl = new(GetServiceUrl(turnContext));
131 AgenticIdentity identity = GetIdentity(turnContext);
132
133 IList<Core.Schema.ConversationAccount> members = await client.GetConversationMembersAsync(
134 conversationId, serviceUrl, identity, null, cancellationToken).ConfigureAwait(false);
135
136 return members.Select(m => m.ToCompatTeamsChannelAccount());
137 }
138 }
139
140 /// <summary>
141 /// Gets a paginated list of members of one-on-one, group, or team conversation.
142 /// </summary>
143 /// <param name="turnContext">Turn context.</param>
144 /// <param name="pageSize">Suggested number of entries on a page.</param>
145 /// <param name="continuationToken">Continuation token.</param>
146 /// <param name="cancellationToken">Cancellation token.</param>
147 /// <returns>Paged members result.</returns>
148 public static async Task<BotFrameworkTeams.TeamsPagedMembersResult> GetPagedMembersAsync(
149 ITurnContext turnContext,
150 int? pageSize = default,
151 string? continuationToken = default,
152 CancellationToken cancellationToken = default)
153 {
154 ArgumentNullException.ThrowIfNull(turnContext);
155 TeamInfo? teamInfo = turnContext.Activity.TeamsGetTeamInfo();
156
157 if (teamInfo?.Id != null)
158 {
159 return await GetPagedTeamMembersAsync(turnContext, teamInfo.Id, continuationToken, pageSize, cancellationToken).ConfigureAwait(false);
160 }
161 else
162 {
163 string conversationId = turnContext.Activity?.Conversation?.Id
164 ?? throw new InvalidOperationException("The GetMembers operation needs a valid conversation Id.");
165
166 ConversationClient client = GetConversationClient(turnContext);
167 Uri serviceUrl = new(GetServiceUrl(turnContext));
168 AgenticIdentity identity = GetIdentity(turnContext);
169
170 Core.PagedMembersResult pagedMembers = await client.GetConversationPagedMembersAsync(
171 conversationId, serviceUrl, pageSize, continuationToken, identity, null, cancellationToken).ConfigureAwait(false);
172
173 return pagedMembers.ToCompatTeamsPagedMembersResult();
174 }
175 }
176
177 /// <summary>
178 /// Gets the member of a teams scoped conversation.
179 /// </summary>
180 /// <param name="turnContext">Turn context.</param>
181 /// <param name="userId">User id.</param>
182 /// <param name="teamId">ID of the Teams team.</param>
183 /// <param name="cancellationToken">Cancellation token.</param>
184 /// <returns>Team member's channel account.</returns>
185 public static async Task<BotFrameworkTeams.TeamsChannelAccount> GetTeamMemberAsync(
186 ITurnContext turnContext,
187 string userId,
188 string? teamId = null,
189 CancellationToken cancellationToken = default)
190 {
191 ArgumentNullException.ThrowIfNull(turnContext);
192 string t = teamId ?? turnContext.Activity.TeamsGetTeamInfo()?.Id
193 ?? throw new InvalidOperationException("This method is only valid within the scope of MS Teams Team.");
194
195 if (userId == null)
196 {
197 throw new InvalidOperationException("The GetMember operation needs a valid user Id.");
198 }
199
200 ConversationClient client = GetConversationClient(turnContext);
201 Uri serviceUrl = new(GetServiceUrl(turnContext));
202 AgenticIdentity identity = GetIdentity(turnContext);
203
204 TeamsConversationAccount result = await client.GetConversationMemberAsync<Microsoft.Teams.Bot.Apps.Schema.TeamsConversationAccount>(
205 t, userId, serviceUrl, identity, null, cancellationToken).ConfigureAwait(false);
206
207 return result.ToCompatTeamsChannelAccount();
208 }
209
210 /// <summary>
211 /// Gets the list of BotFrameworkTeams.TeamsChannelAccounts within a team.
212 /// This only works in teams scoped conversations.
213 /// </summary>
214 /// <param name="turnContext">Turn context.</param>
215 /// <param name="teamId">ID of the Teams team.</param>
216 /// <param name="cancellationToken">Cancellation token.</param>
217 /// <returns>List of team members.</returns>
218 [Obsolete("Microsoft Teams is deprecating the non-paged version of the getMembers API which this method uses. Please use GetPagedTeamMembersAsync instead of this API.")]
219 public static async Task<IEnumerable<BotFrameworkTeams.TeamsChannelAccount>> GetTeamMembersAsync(
220 ITurnContext turnContext,
221 string? teamId = null,
222 CancellationToken cancellationToken = default)
223 {
224 ArgumentNullException.ThrowIfNull(turnContext);
225 string t = teamId ?? turnContext.Activity.TeamsGetTeamInfo()?.Id
226 ?? throw new InvalidOperationException("This method is only valid within the scope of MS Teams Team.");
227
228 ConversationClient client = GetConversationClient(turnContext);
229 Uri serviceUrl = new(GetServiceUrl(turnContext));
230 AgenticIdentity identity = GetIdentity(turnContext);
231
232 IList<Core.Schema.ConversationAccount> members = await client.GetConversationMembersAsync(
233 t, serviceUrl, identity, null, cancellationToken).ConfigureAwait(false);
234
235 return members.Select(m => m.ToCompatTeamsChannelAccount());
236 }
237
238 /// <summary>
239 /// Gets a paginated list of members of a team.
240 /// This only works in teams scoped conversations.
241 /// </summary>
242 /// <param name="turnContext">Turn context.</param>
243 /// <param name="teamId">ID of the Teams team.</param>
244 /// <param name="continuationToken">Continuation token.</param>
245 /// <param name="pageSize">Number of entries on the page.</param>
246 /// <param name="cancellationToken">Cancellation token.</param>
247 /// <returns>Paged team members result.</returns>
248 public static async Task<BotFrameworkTeams.TeamsPagedMembersResult> GetPagedTeamMembersAsync(
249 ITurnContext turnContext,
250 string? teamId = null,
251 string? continuationToken = default,
252 int? pageSize = default,
253 CancellationToken cancellationToken = default)
254 {
255 ArgumentNullException.ThrowIfNull(turnContext);
256 string t = teamId ?? turnContext.Activity.TeamsGetTeamInfo()?.Id
257 ?? throw new InvalidOperationException("This method is only valid within the scope of MS Teams Team.");
258
259 ConversationClient client = GetConversationClient(turnContext);
260 Uri serviceUrl = new(GetServiceUrl(turnContext));
261 AgenticIdentity identity = GetIdentity(turnContext);
262
263 Core.PagedMembersResult pagedMembers = await client.GetConversationPagedMembersAsync(
264 t, serviceUrl, pageSize, continuationToken, identity, null, cancellationToken).ConfigureAwait(false);
265
266 return pagedMembers.ToCompatTeamsPagedMembersResult();
267 }
268
269 #endregion
270
271 #region Meeting Methods
272
273 /// <summary>
274 /// Gets the information for the given meeting id.
275 /// </summary>
276 /// <param name="turnContext">Turn context.</param>
277 /// <param name="meetingId">The BASE64-encoded id of the Teams meeting.</param>
278 /// <param name="cancellationToken">Cancellation token.</param>
279 /// <returns>Meeting information.</returns>
280 public static async Task<BotFrameworkTeams.MeetingInfo> GetMeetingInfoAsync(
281 ITurnContext turnContext,
282 string? meetingId = null,
283 CancellationToken cancellationToken = default)
284 {
285 ArgumentNullException.ThrowIfNull(turnContext);
286 meetingId ??= turnContext.Activity.TeamsGetMeetingInfo()?.Id
287 ?? throw new InvalidOperationException("The meetingId can only be null if turnContext is within the scope of a MS Teams Meeting.");
288
289 TeamsApiClient client = GetTeamsApiClient(turnContext);
290 Uri serviceUrl = new(GetServiceUrl(turnContext));
291 AgenticIdentity identity = GetIdentity(turnContext);
292
293 AppsTeams.MeetingInfo result = await client.FetchMeetingInfoAsync(
294 meetingId, serviceUrl, identity, null, cancellationToken).ConfigureAwait(false);
295
296 return result.ToCompatMeetingInfo();
297 }
298
299 /// <summary>
300 /// Gets the details for the given meeting participant. This only works in teams meeting scoped conversations.
301 /// </summary>
302 /// <param name="turnContext">Turn context.</param>
303 /// <param name="meetingId">The id of the Teams meeting. BotFrameworkTeams.TeamsChannelData.Meeting.Id will be used if none provided.</param>
304 /// <param name="participantId">The id of the Teams meeting participant. From.AadObjectId will be used if none provided.</param>
305 /// <param name="tenantId">The id of the Teams meeting Tenant. BotFrameworkTeams.TeamsChannelData.Tenant.Id will be used if none provided.</param>
306 /// <param name="cancellationToken">Cancellation token.</param>
307 /// <returns>Team participant channel account.</returns>
308 public static async Task<BotFrameworkTeams.TeamsMeetingParticipant> GetMeetingParticipantAsync(
309 ITurnContext turnContext,
310 string? meetingId = null,
311 string? participantId = null,
312 string? tenantId = null,
313 CancellationToken cancellationToken = default)
314 {
315 ArgumentNullException.ThrowIfNull(turnContext);
316 meetingId ??= turnContext.Activity.TeamsGetMeetingInfo()?.Id
317 ?? throw new InvalidOperationException("This method is only valid within the scope of a MS Teams Meeting.");
318 participantId ??= turnContext.Activity.From.AadObjectId
319 ?? throw new InvalidOperationException($"{nameof(participantId)} is required.");
320 tenantId ??= turnContext.Activity.GetChannelData<BotFrameworkTeams.TeamsChannelData>()?.Tenant?.Id
321 ?? throw new InvalidOperationException($"{nameof(tenantId)} is required.");
322
323 TeamsApiClient client = GetTeamsApiClient(turnContext);
324 Uri serviceUrl = new(GetServiceUrl(turnContext));
325 AgenticIdentity identity = GetIdentity(turnContext);
326
327 MeetingParticipant result = await client.FetchParticipantAsync(
328 meetingId, participantId, tenantId, serviceUrl, identity, null, cancellationToken).ConfigureAwait(false);
329
330 return result.ToCompatTeamsMeetingParticipant();
331 }
332
333 /// <summary>
334 /// Sends a notification to meeting participants. This functionality is available only in teams meeting scoped conversations.
335 /// </summary>
336 /// <param name="turnContext">Turn context.</param>
337 /// <param name="notification">The notification to send to Teams.</param>
338 /// <param name="meetingId">The id of the Teams meeting. BotFrameworkTeams.TeamsChannelData.Meeting.Id will be used if none provided.</param>
339 /// <param name="cancellationToken">Cancellation token.</param>
340 /// <returns>Meeting notification response.</returns>
341 public static async Task<BotFrameworkTeams.MeetingNotificationResponse> SendMeetingNotificationAsync(
342 ITurnContext turnContext,
343 BotFrameworkTeams.MeetingNotificationBase? notification,
344 string? meetingId = null,
345 CancellationToken cancellationToken = default)
346 {
347 ArgumentNullException.ThrowIfNull(turnContext);
348 meetingId ??= turnContext.Activity.TeamsGetMeetingInfo()?.Id
349 ?? throw new InvalidOperationException("This method is only valid within the scope of a MS Teams Meeting.");
350 notification = notification ?? throw new InvalidOperationException($"{nameof(notification)} is required.");
351
352 TeamsApiClient client = GetTeamsApiClient(turnContext);
353 Uri serviceUrl = new(GetServiceUrl(turnContext));
354 AgenticIdentity identity = GetIdentity(turnContext);
355
356 // Convert Bot Framework MeetingNotificationBase to Core MeetingNotificationBase using JSON round-trip
357 string json = Newtonsoft.Json.JsonConvert.SerializeObject(notification);
358 AppsTeams.TargetedMeetingNotification? coreNotification = System.Text.Json.JsonSerializer.Deserialize<AppsTeams.TargetedMeetingNotification>(json, s_jsonOptions);
359
360
361 AppsTeams.MeetingNotificationResponse result = await client.SendMeetingNotificationAsync(
362 meetingId, coreNotification!, serviceUrl, identity, null, cancellationToken).ConfigureAwait(false);
363
364 return result.ToCompatMeetingNotificationResponse();
365 }
366
367 #endregion
368
369 #region Team & Channel Methods
370
371 /// <summary>
372 /// Gets the details for the given team id. This only works in teams scoped conversations.
373 /// </summary>
374 /// <param name="turnContext">Turn context.</param>
375 /// <param name="teamId">The id of the Teams team.</param>
376 /// <param name="cancellationToken">Cancellation token.</param>
377 /// <returns>Team details.</returns>
378 public static async Task<BotFrameworkTeams.TeamDetails> GetTeamDetailsAsync(
379 ITurnContext turnContext,
380 string? teamId = null,
381 CancellationToken cancellationToken = default)
382 {
383 ArgumentNullException.ThrowIfNull(turnContext);
384 string t = teamId ?? turnContext.Activity.TeamsGetTeamInfo()?.Id
385 ?? throw new InvalidOperationException("This method is only valid within the scope of MS Teams Team.");
386
387 TeamsApiClient client = GetTeamsApiClient(turnContext);
388 Uri serviceUrl = new(GetServiceUrl(turnContext));
389 AgenticIdentity identity = GetIdentity(turnContext);
390
391 AppsTeams.TeamDetails result = await client.FetchTeamDetailsAsync(
392 t, serviceUrl, identity, null, cancellationToken).ConfigureAwait(false);
393
394 return result.ToCompatTeamDetails();
395 }
396
397 /// <summary>
398 /// Returns a list of channels in a Team.
399 /// This only works in teams scoped conversations.
400 /// </summary>
401 /// <param name="turnContext">Turn context.</param>
402 /// <param name="teamId">ID of the Teams team.</param>
403 /// <param name="cancellationToken">Cancellation token.</param>
404 /// <returns>List of channel information.</returns>
405 public static async Task<IList<BotFrameworkTeams.ChannelInfo>> GetTeamChannelsAsync(
406 ITurnContext turnContext,
407 string? teamId = null,
408 CancellationToken cancellationToken = default)
409 {
410 ArgumentNullException.ThrowIfNull(turnContext);
411 string t = teamId ?? turnContext.Activity.TeamsGetTeamInfo()?.Id
412 ?? throw new InvalidOperationException("This method is only valid within the scope of MS Teams Team.");
413
414 TeamsApiClient client = GetTeamsApiClient(turnContext);
415 Uri serviceUrl = new(GetServiceUrl(turnContext));
416 AgenticIdentity identity = GetIdentity(turnContext);
417
418 ChannelList channelList = await client.FetchChannelListAsync(
419 t, serviceUrl, identity, null, cancellationToken).ConfigureAwait(false);
420
421 return channelList.Channels?.Select(c => c.ToCompatChannelInfo()).ToList() ?? [];
422 }
423
424 #endregion
425
426 #region Batch Messaging Methods
427
428 /// <summary>
429 /// Sends a message to the provided list of Teams members.
430 /// </summary>
431 /// <param name="turnContext">Turn context.</param>
432 /// <param name="activity">The activity to send.</param>
433 /// <param name="teamsMembers">The list of members.</param>
434 /// <param name="tenantId">The tenant ID.</param>
435 /// <param name="cancellationToken">Cancellation token.</param>
436 /// <returns>The operation Id.</returns>
437 public static async Task<string> SendMessageToListOfUsersAsync(
438 ITurnContext turnContext,
439 IActivity activity,
440 IList<BotFrameworkTeams.TeamMember> teamsMembers,
441 string tenantId,
442 CancellationToken cancellationToken = default)
443 {
444 ArgumentNullException.ThrowIfNull(turnContext);
445 activity = activity ?? throw new InvalidOperationException($"{nameof(activity)} is required.");
446 teamsMembers = teamsMembers ?? throw new InvalidOperationException($"{nameof(teamsMembers)} is required.");
447 tenantId = tenantId ?? throw new InvalidOperationException($"{nameof(tenantId)} is required.");
448
449 TeamsApiClient client = GetTeamsApiClient(turnContext);
450 Uri serviceUrl = new(GetServiceUrl(turnContext));
451 AgenticIdentity identity = GetIdentity(turnContext);
452 CoreActivity coreActivity = ((Activity)activity).FromCompatActivity();
453
454 List<AppsTeams.TeamMember> coreTeamsMembers = teamsMembers.Select(m => m.FromCompatTeamMember()).ToList();
455
456 return await client.SendMessageToListOfUsersAsync(
457 coreActivity, coreTeamsMembers, tenantId, serviceUrl, identity, null, cancellationToken).ConfigureAwait(false);
458 }
459
460 /// <summary>
461 /// Sends a message to the provided list of Teams channels.
462 /// </summary>
463 /// <param name="turnContext">Turn context.</param>
464 /// <param name="activity">The activity to send.</param>
465 /// <param name="channelsMembers">The list of channels.</param>
466 /// <param name="tenantId">The tenant ID.</param>
467 /// <param name="cancellationToken">Cancellation token.</param>
468 /// <returns>The operation Id.</returns>
469 public static async Task<string> SendMessageToListOfChannelsAsync(
470 ITurnContext turnContext,
471 IActivity activity,
472 IList<BotFrameworkTeams.TeamMember> channelsMembers,
473 string tenantId,
474 CancellationToken cancellationToken = default)
475 {
476 ArgumentNullException.ThrowIfNull(turnContext);
477 activity = activity ?? throw new InvalidOperationException($"{nameof(activity)} is required.");
478 channelsMembers = channelsMembers ?? throw new InvalidOperationException($"{nameof(channelsMembers)} is required.");
479 tenantId = tenantId ?? throw new InvalidOperationException($"{nameof(tenantId)} is required.");
480
481 TeamsApiClient client = GetTeamsApiClient(turnContext);
482 Uri serviceUrl = new(GetServiceUrl(turnContext));
483 AgenticIdentity identity = GetIdentity(turnContext);
484 CoreActivity coreActivity = ((Activity)activity).FromCompatActivity();
485
486 List<AppsTeams.TeamMember> coreChannelsMembers = channelsMembers.Select(m => m.FromCompatTeamMember()).ToList();
487
488 return await client.SendMessageToListOfChannelsAsync(
489 coreActivity, coreChannelsMembers, tenantId, serviceUrl, identity, null, cancellationToken).ConfigureAwait(false);
490 }
491
492 /// <summary>
493 /// Sends a message to all the users in a team.
494 /// </summary>
495 /// <param name="turnContext">The turn context.</param>
496 /// <param name="activity">The activity to send to the users in the team.</param>
497 /// <param name="teamId">The team ID.</param>
498 /// <param name="tenantId">The tenant ID.</param>
499 /// <param name="cancellationToken">Cancellation token.</param>
500 /// <returns>The operation Id.</returns>
501 public static async Task<string> SendMessageToAllUsersInTeamAsync(
502 ITurnContext turnContext,
503 IActivity activity,
504 string teamId,
505 string tenantId,
506 CancellationToken cancellationToken = default)
507 {
508 ArgumentNullException.ThrowIfNull(turnContext);
509 activity = activity ?? throw new InvalidOperationException($"{nameof(activity)} is required.");
510 teamId = teamId ?? throw new InvalidOperationException($"{nameof(teamId)} is required.");
511 tenantId = tenantId ?? throw new InvalidOperationException($"{nameof(tenantId)} is required.");
512
513 TeamsApiClient client = GetTeamsApiClient(turnContext);
514 Uri serviceUrl = new(GetServiceUrl(turnContext));
515 AgenticIdentity identity = GetIdentity(turnContext);
516 CoreActivity coreActivity = ((Activity)activity).FromCompatActivity();
517
518 return await client.SendMessageToAllUsersInTeamAsync(
519 coreActivity, teamId, tenantId, serviceUrl, identity, null, cancellationToken).ConfigureAwait(false);
520 }
521
522 /// <summary>
523 /// Sends a message to all the users in a tenant.
524 /// </summary>
525 /// <param name="turnContext">The turn context.</param>
526 /// <param name="activity">The activity to send to the tenant.</param>
527 /// <param name="tenantId">The tenant ID.</param>
528 /// <param name="cancellationToken">Cancellation token.</param>
529 /// <returns>The operation Id.</returns>
530 public static async Task<string> SendMessageToAllUsersInTenantAsync(
531 ITurnContext turnContext,
532 IActivity activity,
533 string tenantId,
534 CancellationToken cancellationToken = default)
535 {
536 ArgumentNullException.ThrowIfNull(turnContext);
537 activity = activity ?? throw new InvalidOperationException($"{nameof(activity)} is required.");
538 tenantId = tenantId ?? throw new InvalidOperationException($"{nameof(tenantId)} is required.");
539
540 TeamsApiClient client = GetTeamsApiClient(turnContext);
541 Uri serviceUrl = new(GetServiceUrl(turnContext));
542 AgenticIdentity identity = GetIdentity(turnContext);
543 CoreActivity coreActivity = ((Activity)activity).FromCompatActivity();
544
545 return await client.SendMessageToAllUsersInTenantAsync(
546 coreActivity, tenantId, serviceUrl, identity, null, cancellationToken).ConfigureAwait(false);
547 }
548
549 /// <summary>
550 /// Creates a new thread in a team chat and sends an activity to that new thread.
551 /// Use this method if you are using CloudAdapter where credentials are handled by the adapter.
552 /// </summary>
553 /// <param name="turnContext">Turn context.</param>
554 /// <param name="activity">The activity to send on starting the new thread.</param>
555 /// <param name="teamsChannelId">The Team's Channel ID, note this is distinct from the Bot Framework activity property with same name.</param>
556 /// <param name="botAppId">The bot's appId.</param>
557 /// <param name="cancellationToken">Cancellation token.</param>
558 /// <returns>Tuple with conversation reference and activity id.</returns>
559 public static async Task<Tuple<ConversationReference, string>> SendMessageToTeamsChannelAsync(
560 ITurnContext turnContext,
561 IActivity activity,
562 string teamsChannelId,
563 string botAppId,
564 CancellationToken cancellationToken = default)
565 {
566 ArgumentNullException.ThrowIfNull(turnContext);
567
568 if (turnContext.Activity == null)
569 {
570 throw new InvalidOperationException(nameof(turnContext.Activity));
571 }
572
573 ArgumentException.ThrowIfNullOrWhiteSpace(teamsChannelId);
574
575 ConversationReference? conversationReference = null;
576 string newActivityId = string.Empty;
577 string serviceUrl = turnContext.Activity.ServiceUrl;
578 Microsoft.Bot.Schema.ConversationParameters conversationParameters = new()
579 {
580 IsGroup = true,
581 ChannelData = new BotFrameworkTeams.TeamsChannelData { Channel = new BotFrameworkTeams.ChannelInfo { Id = teamsChannelId } },
582 Activity = (Activity)activity,
583 };
584
585 await turnContext.Adapter.CreateConversationAsync(
586 botAppId,
587 Channels.Msteams,
588 serviceUrl,
589 null,
590 conversationParameters,
591 (t, ct) =>
592 {
593 conversationReference = t.Activity.GetConversationReference();
594 newActivityId = t.Activity.Id;
595 return Task.CompletedTask;
596 },
597 cancellationToken).ConfigureAwait(false);
598
599 return new Tuple<ConversationReference, string>(conversationReference!, newActivityId);
600 }
601
602 #endregion
603
604 #region Batch Operation Management
605
606 /// <summary>
607 /// Gets the state of an operation.
608 /// </summary>
609 /// <param name="turnContext">Turn context.</param>
610 /// <param name="operationId">The operationId to get the state of.</param>
611 /// <param name="cancellationToken">Cancellation token.</param>
612 /// <returns>The state and responses of the operation.</returns>
613 public static async Task<BotFrameworkTeams.BatchOperationState> GetOperationStateAsync(
614 ITurnContext turnContext,
615 string operationId,
616 CancellationToken cancellationToken = default)
617 {
618 ArgumentNullException.ThrowIfNull(turnContext);
619 operationId = operationId ?? throw new InvalidOperationException($"{nameof(operationId)} is required.");
620
621 TeamsApiClient client = GetTeamsApiClient(turnContext);
622 Uri serviceUrl = new(GetServiceUrl(turnContext));
623 AgenticIdentity identity = GetIdentity(turnContext);
624
625 AppsTeams.BatchOperationState result = await client.GetOperationStateAsync(
626 operationId, serviceUrl, identity, null, cancellationToken).ConfigureAwait(false);
627
628 return result.ToCompatBatchOperationState();
629 }
630
631 /// <summary>
632 /// Gets the failed entries of a batch operation.
633 /// </summary>
634 /// <param name="turnContext">The turn context.</param>
635 /// <param name="operationId">The operationId to get the failed entries of.</param>
636 /// <param name="continuationToken">The continuation token.</param>
637 /// <param name="cancellationToken">Cancellation token.</param>
638 /// <returns>The list of failed entries of the operation.</returns>
639 public static async Task<BotFrameworkTeams.BatchFailedEntriesResponse> GetPagedFailedEntriesAsync(
640 ITurnContext turnContext,
641 string operationId,
642 string? continuationToken = null,
643 CancellationToken cancellationToken = default)
644 {
645 ArgumentNullException.ThrowIfNull(turnContext);
646 operationId = operationId ?? throw new InvalidOperationException($"{nameof(operationId)} is required.");
647
648 TeamsApiClient client = GetTeamsApiClient(turnContext);
649 Uri serviceUrl = new(GetServiceUrl(turnContext));
650 AgenticIdentity identity = GetIdentity(turnContext);
651
652 AppsTeams.BatchFailedEntriesResponse result = await client.GetPagedFailedEntriesAsync(
653 operationId, serviceUrl, continuationToken, identity, null, cancellationToken).ConfigureAwait(false);
654
655 return result.ToCompatBatchFailedEntriesResponse();
656 }
657
658 /// <summary>
659 /// Cancels a batch operation by its id.
660 /// </summary>
661 /// <param name="turnContext">The turn context.</param>
662 /// <param name="operationId">The id of the operation to cancel.</param>
663 /// <param name="cancellationToken">Cancellation token.</param>
664 /// <returns>A task representing the asynchronous operation.</returns>
665 public static async Task CancelOperationAsync(
666 ITurnContext turnContext,
667 string operationId,
668 CancellationToken cancellationToken = default)
669 {
670 ArgumentNullException.ThrowIfNull(turnContext);
671 operationId = operationId ?? throw new InvalidOperationException($"{nameof(operationId)} is required.");
672
673 TeamsApiClient client = GetTeamsApiClient(turnContext);
674 Uri serviceUrl = new(GetServiceUrl(turnContext));
675 AgenticIdentity identity = GetIdentity(turnContext);
676
677 await client.CancelOperationAsync(
678 operationId, serviceUrl, identity, null, cancellationToken).ConfigureAwait(false);
679 }
680
681 #endregion
682}
683