microsoft/teams.net

Public

mirrored from https://github.com/microsoft/teams.netAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
copilot/sub-pr-338

Branches

Tags

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

Clone

HTTPS

Download ZIP

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

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