// Create a new ticket from the input private async Task <TicketEntity> CreateTicketAsync(IMessageActivity message, AskAnExpertCardPayload data, TeamsChannelAccount member) { TicketEntity ticketEntity = new TicketEntity { TicketId = Guid.NewGuid().ToString(), Status = (int)TicketState.Open, DateCreated = DateTime.UtcNow, Title = data.Title, Description = data.Description, RequesterName = member.Name, RequesterUserPrincipalName = member.UserPrincipalName, RequesterGivenName = member.GivenName, RequesterConversationId = message.Conversation.Id, LastModifiedByName = message.From.Name, LastModifiedByObjectId = message.From.AadObjectId, UserQuestion = data.UserQuestion, KnowledgeBaseAnswer = data.KnowledgeBaseAnswer }; await this.ticketsProvider.SaveOrUpdateTicketAsync(ticketEntity); return(ticketEntity); }
/// <summary> /// Handle when a message is addressed to the bot. /// </summary> /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param> /// <param name="cancellationToken">Propagates notification that operations should be canceled.</param> /// <returns>A Task resolving to either a login card or the adaptive card of the Reddit post.</returns> /// <remarks> /// For more information on bot messaging in Teams, see the documentation /// https://docs.microsoft.com/en-us/microsoftteams/platform/bots/how-to/conversations/conversation-basics?tabs=dotnet#receive-a-message . /// </remarks> protected override async Task OnMessageActivityAsync(ITurnContext <IMessageActivity> turnContext, CancellationToken cancellationToken) { turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext)); var activity = turnContext.Activity; try { string command; SubmitActionDataForTeamsBehavior valuesFromCard = null; if (string.IsNullOrWhiteSpace(activity.Text) && JObject.Parse(activity.Value?.ToString())["command"].ToString() == null) { return; } // We are supporting pause/resume matches either from bot command or Adaptive submit card action. if (string.IsNullOrWhiteSpace(activity.Text) && activity.Value != null) { valuesFromCard = ((JObject)activity.Value).ToObject <SubmitActionDataForTeamsBehavior>(); command = valuesFromCard.Command.Trim(); } else { command = activity.Text.Trim(); } if (activity.Conversation.ConversationType == PersonalConversationType) { // Command to get configure pair-up matches notification card. if (command.Equals(this.localizer.GetString("PauseMatchesCommand"), StringComparison.CurrentCultureIgnoreCase) || command == Constants.PauseMatchesCommand) { if (activity.Value == null) { // Send user matches card. await this.userTeamMappingsHelper.SendUserTeamMappingsCardAsync(turnContext, cancellationToken); return; } if (valuesFromCard.TeamId != null) { var userPairUpMappingEntity = await this.teamUserPairUpMappingRepository.GetAsync(activity.From.AadObjectId, valuesFromCard.TeamId); userPairUpMappingEntity.IsPaused = true; await this.teamUserPairUpMappingRepository.InsertOrMergeAsync(userPairUpMappingEntity); var configureUserMatchesNoticationCard = MessageFactory.Attachment(this.cardHelper.GetConfigureMatchesNotificationCard()); await turnContext.SendActivityAsync(configureUserMatchesNoticationCard, cancellationToken); return; } } // Command to get user matches card. else if (command.Equals(this.localizer.GetString("ConfigureMatchesCommand"), StringComparison.CurrentCultureIgnoreCase) || command == Constants.ConfigureMatchesCommand) { await this.userTeamMappingsHelper.SendUserTeamMappingsCardAsync(turnContext, cancellationToken); return; } // Command to update user pair-ups. else if (command.Equals(this.localizer.GetString("UpdateMatchesCommand"), StringComparison.CurrentCultureIgnoreCase) || command == Constants.UpdateMatchesCommand) { if (activity.Value == null) { // Send user matches card. await this.userTeamMappingsHelper.SendUserTeamMappingsCardAsync(turnContext, cancellationToken); return; } // Adaptive card submit action sends back only team id's which are being checked. // Explicitly setting choice set as empty array of string when all the choices are unchecked, // to update the IsPaused flag as 'false' for all the teams where user is a member. var choiceSet = JObject.Parse(activity.Value.ToString())["choiceset"] != null ? JObject.Parse(activity.Value?.ToString())["choiceset"].ToString().Split(",") : new string[0]; var userTeamMappings = await this.teamUserPairUpMappingRepository.GetAllAsync(activity.From.AadObjectId); foreach (var teamUserPair in userTeamMappings) { teamUserPair.IsPaused = choiceSet.Contains(teamUserPair.TeamId); await this.teamUserPairUpMappingRepository.InsertOrMergeAsync(teamUserPair); } var updateConfigurePairupCard = MessageFactory.Attachment(this.cardHelper.GetResumePairupNotificationCard()); await turnContext.SendActivityAsync(updateConfigurePairupCard, cancellationToken); return; } else if (command == Constants.ShareCommand) { if (valuesFromCard != null && !string.IsNullOrWhiteSpace(valuesFromCard.FeedbackDescription) && !string.IsNullOrWhiteSpace(valuesFromCard.FeedbackType)) { TeamsChannelAccount userDetails = await this.GetConversationUserDetailAsync(turnContext, cancellationToken); var teamNotificationAttachment = this.cardHelper.GetShareFeedbackNotificationCard(valuesFromCard, userDetails); var feedbackEntity = new FeedbackEntity { Feedback = valuesFromCard.FeedbackDescription, FeedbackId = Guid.NewGuid().ToString(), UserAadObjectId = userDetails.AadObjectId, SubmittedOn = DateTime.UtcNow, }; await this.notificationCardHelper.SendProactiveNotificationCardAsync(teamNotificationAttachment, this.botOptions.Value.AdminTeamId, activity.ServiceUrl); await turnContext.SendActivityAsync(this.localizer.GetString("FeedbackSubmittedMessage")); await this.feedbackDataRepository.InsertOrMergeAsync(feedbackEntity); } } else { await this.knowledgeBaseResponse.SendReplyToQuestionAsync(turnContext, activity.Text); } } else if (activity.Conversation.ConversationType == ChannelConversationType) { if (activity.Value != null) { await this.teamNotification.UpdateGroupApprovalNotificationAsync(turnContext); } else { // Send help card for unsupported bot command. await turnContext.SendActivityAsync(this.localizer.GetString("UnSupportedBotCommand")); return; } } } catch (Exception ex) { this.logger.LogError($"Error while processing message request. {ex.Message}"); await turnContext.SendActivityAsync(this.localizer.GetString("ErrorMessage")); return; } }
private static async Task <Attachment> GetDetailedRoasterCard(Activity activity, TeamsChannelAccount userDetails) { var details = JsonConvert.DeserializeObject <AirlineActionDetails>(activity.Value.ToString()); Crew crew = await CabinCrewPlansHelper.ReadJson(userDetails.UserPrincipalName); var datePlan = crew.plan.FirstOrDefault(c => c.flightDetails.flightStartDate.Date.ToString() == details.Id); return(CardHelper.GetDetailedRoster(datePlan)); }
/// <summary> /// This method will construct the card for SME team which will have the /// feedback details given by the user. /// </summary> /// <param name="data">user activity payload</param> /// <param name="userDetails">User details.</param> /// <returns>Sme facing feedback notification card.</returns> public static Attachment GetCard(ShareFeedbackCardPayload data, TeamsChannelAccount userDetails) { // Constructing adaptive card that is sent to SME team. AdaptiveCard smeFeedbackCard = new AdaptiveCard("1.0") { Body = new List <AdaptiveElement> { new AdaptiveTextBlock() { Text = Resource.SMEFeedbackHeaderText, Weight = AdaptiveTextWeight.Bolder, Size = AdaptiveTextSize.Medium, }, new AdaptiveTextBlock() { Text = string.Format(Resource.FeedbackAlertText, userDetails.Name), Wrap = true, }, new AdaptiveTextBlock() { Text = Resource.RatingTitle, Weight = AdaptiveTextWeight.Bolder, Wrap = true, }, new AdaptiveTextBlock() { Text = GetRatingDisplayText(data.Rating), Spacing = AdaptiveSpacing.None, Wrap = true, }, }, Actions = new List <AdaptiveAction> { new AdaptiveOpenUrlAction { Title = string.Format(Resource.ChatTextButton, userDetails.GivenName), UrlString = $"https://teams.microsoft.com/l/chat/0/0?users={Uri.EscapeDataString(userDetails.UserPrincipalName)}" } } }; // Description fact is available in the card only when user enters description text. if (!string.IsNullOrWhiteSpace(data.Description)) { smeFeedbackCard.Body.Add(new AdaptiveTextBlock() { Text = Resource.DescriptionText, Weight = AdaptiveTextWeight.Bolder, Wrap = true, }); smeFeedbackCard.Body.Add(new AdaptiveTextBlock() { Text = CardHelper.TruncateStringIfLonger(data.Description, CardHelper.DescriptionMaxDisplayLength), Spacing = AdaptiveSpacing.None, Wrap = true, }); } // Question asked fact and view article show card is available when feedback is on QnA Maker response. if (!string.IsNullOrWhiteSpace(data.KnowledgeBaseAnswer) && !string.IsNullOrWhiteSpace(data.UserQuestion)) { smeFeedbackCard.Body.Add(new AdaptiveFactSet { Facts = new List <AdaptiveFact> { new AdaptiveFact() { Title = Resource.QuestionAskedFactTitle, Value = data.UserQuestion, }, } }); smeFeedbackCard.Actions.AddRange(new List <AdaptiveAction> { new AdaptiveShowCardAction { Title = Resource.ViewArticleButtonText, Card = new AdaptiveCard("1.0") { Body = new List <AdaptiveElement> { new AdaptiveTextBlock { Text = CardHelper.TruncateStringIfLonger(data.KnowledgeBaseAnswer, CardHelper.KnowledgeBaseAnswerMaxDisplayLength), Wrap = true } } } } }); } return(new Attachment { ContentType = AdaptiveCard.ContentType, Content = smeFeedbackCard, }); }
/// <summary> /// Send Grouping message in channel from where group activity is invoked. /// </summary> /// <param name="teamId">Team id of team where bot is installed.</param> /// <param name="valuesFromTaskModule">Values obtained from task modules which entered by user.</param> /// <param name="membersGroupingWithChannel">A dictionary with members grouped into channels based on entered grouping criteria.</param> /// <param name="groupActivityCreator">Team owner who initiated the group activity.</param> /// <param name="turnContext">Provides context for a turn of a bot.</param> /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param> /// <returns>A task that sends the grouping message in channel.</returns> private async Task <string> SendAndStoreGroupingMessageAsync(string groupActivityId, string teamId, GroupDetail valuesFromTaskModule, Dictionary <int, IList <TeamsChannelAccount> > membersGroupingWithChannel, TeamsChannelAccount groupActivityCreator, ITurnContext <IInvokeActivity> turnContext, CancellationToken cancellationToken) { try { // Post grouping of members with channel details to channel from where bot is invoked. var groupingMessage = this.groupingHelper.GroupingMessage(valuesFromTaskModule, membersGroupingWithChannel, groupActivityCreator.Name); var groupingCardActivity = MessageFactory.Attachment(GroupActivityCard.GetGroupActivityCard(groupingMessage, groupActivityCreator.Name, valuesFromTaskModule)); await turnContext.SendActivityAsync(groupingCardActivity, cancellationToken); await this.channelHelper.StoreGroupActivityDetailsAsync(turnContext.Activity.ServiceUrl, groupActivityId, teamId, valuesFromTaskModule, groupActivityCreator.Name, groupingCardActivity.Conversation.Id, groupingCardActivity.Id); return(groupingMessage); } catch (Exception ex) { this.logger.LogError(ex, $"Error while creating grouping message for group activity for teamId - {teamId}"); return(null); } }
/// <summary> /// Create a new ticket from the input. /// </summary> /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param> /// <param name="ticketDetail">Ticket details from requested user.</param> /// <param name="ticketAdditionalDetails">Additional ticket details.</param> /// <param name="cardId">Card template id.</param> /// <param name="member"> User details who is currently having conversation.</param> /// <returns>TicketDetail object.</returns> public static TicketDetail GetNewTicketDetails(ITurnContext <IMessageActivity> turnContext, TicketDetail ticketDetail, string ticketAdditionalDetails, string cardId, TeamsChannelAccount member) { turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext)); ticketDetail = ticketDetail ?? throw new ArgumentNullException(nameof(ticketDetail)); ticketDetail.CreatedOn = ConvertToDateTimeoffset(DateTime.Now, turnContext.Activity.LocalTimestamp.Value.Offset); if (ticketDetail.IssueOccuredOn == DateTimeOffset.MinValue) { ticketDetail.IssueOccuredOn = ConvertToDateTimeoffset(DateTime.Now, turnContext.Activity.LocalTimestamp.Value.Offset); } else { ticketDetail.IssueOccuredOn = ConvertToDateTimeoffset(ticketDetail.IssueOccuredOn, turnContext.Activity.LocalTimestamp.Value.Offset); } ticketDetail.CreatedByObjectId = turnContext.Activity.From.AadObjectId; ticketDetail.CreatedByUserPrincipalName = member?.UserPrincipalName; ticketDetail.RequesterName = member.Name; ticketDetail.RequesterConversationId = turnContext.Activity.Conversation.Id; ticketDetail.RequesterTicketActivityId = turnContext.Activity.ReplyToId; ticketDetail.SmeConversationId = null; ticketDetail.SmeTicketActivityId = null; ticketDetail.TicketStatus = (int)TicketState.Unassigned; ticketDetail.Severity = (int)(TicketSeverity)Enum.Parse(typeof(TicketSeverity), ticketDetail.RequestType ?? TicketSeverity.Normal.ToString()); ticketDetail.AdditionalProperties = CardHelper.ValidateAdditionalTicketDetails(ticketAdditionalDetails, turnContext.Activity.LocalTimestamp.Value.Offset); ticketDetail.CardId = cardId; ticketDetail.AssignedToName = string.Empty; ticketDetail.AssignedToObjectId = string.Empty; return(ticketDetail); }
private static async Task <Attachment> GetDetailedRoasterCard(Activity activity, TeamsChannelAccount userDetails, GraphHelper graphHelper, IConfiguration configuration) { var details = JsonConvert.DeserializeObject <AirlineActionDetails>(activity.Value.ToString()); // Crew crew = await CabinCrewPlansHelper.ReadJson(userDetails.UserPrincipalName); string crewid = await graphHelper.GetUserEmployeeIdAsync(userDetails.UserPrincipalName); Crew crew = await CabinCrewPlansHelper.ReadJson(crewid, configuration); // Crew crew = await CabinCrewPlansHelper.ReadJson("10055"); // ${Debugging} var datePlan = crew.plan.FirstOrDefault(c => c.flightDetails.flightStartDate.Date.ToString() == details.Id); return(CardHelper.GetDetailedRoster(datePlan)); }
private async Task HandleActions(ITurnContext context, CancellationToken cancellationToken, Activity activity, TeamsChannelAccount userDetails) { var actionDetails = JsonConvert.DeserializeObject <ActionDetails>(activity.Value.ToString()); // var userDetails = await TeamsInfo.GetMemberAsync(context, context.Activity.From.Id);//await GetCurrentUserDetails(activity); var type = actionDetails.ActionType; Attachment card = null; string crewid = string.Empty; switch (type) { case Constants.ShowDetailedRoster: card = await GetDetailedRoasterCard(activity, userDetails, this.graphHelper, configuration); break; case Constants.NextWeekRoster: crewid = await graphHelper.GetUserEmployeeIdAsync(userDetails.UserPrincipalName); card = await CardHelper.GetWeeklyRosterCard(crewid, configuration); // card = await CardHelper.GetWeeklyRosterCard("10055"); // ${Debugging} break; case Constants.NextMonthRoster: crewid = await graphHelper.GetUserEmployeeIdAsync(userDetails.UserPrincipalName); card = CardHelper.GetMonthlyRosterCard(crewid); break; case Constants.WeatherCard: card = await GetWeatherCard(activity); break; case Constants.CurrencyCard: card = await GetCurrencyCard(activity); break; } await context.SendActivityAsync(MessageFactory.Attachment(card), cancellationToken); }
/// <summary> /// Checks whether or not the account is a guest user. /// </summary> /// <param name="account">The <see cref="TeamsChannelAccount"/> user to check.</param> /// <returns>True if the account is a guest user, false otherwise.</returns> private static bool IsGuestUser(TeamsChannelAccount account) { return(account.UserPrincipalName.IndexOf(ExternallyAuthenticatedUpnMarker, StringComparison.InvariantCultureIgnoreCase) >= 0); }
/// <summary> /// This method will construct the share feedback notification card for admin team. /// </summary> /// <param name="feedbackData">User activity payload.</param> /// <param name="userDetails">User details.</param> /// <returns>Share feedback notification card attachment.</returns> public Attachment GetShareFeedbackNotificationCard(SubmitActionDataForTeamsBehavior feedbackData, TeamsChannelAccount userDetails) { this.logger.LogInformation("Get share feedback notification card initiated."); var shareFeedbackCardContents = new ShareFeedbackCardData() { FeedbackText = this.localizer.GetString("Feedback"), FeedbackSubHeaderText = this.localizer.GetString("FeedbackSubHeaderText", userDetails.GivenName), FeedbackType = feedbackData.FeedbackType, DescriptionText = this.localizer.GetString("FeedbackDescriptionTitleText"), FeedbackDescription = feedbackData.FeedbackDescription, CreatedOnText = this.localizer.GetString("CreatedOn"), FeedbackCreatedDate = DateTime.UtcNow.ToShortDateString(), ChatWithUserButtonText = this.localizer.GetString("ChatWithMatchButtonText", userDetails.GivenName), ChatInitiateURL = new Uri($"{Constants.ChatInitiateURL}?users={Uri.EscapeDataString(userDetails.UserPrincipalName)}&message={Uri.EscapeDataString(this.localizer.GetString("InitiateChatText"))}").ToString(), }; var cardTemplate = this.GetCardTemplate(CardCacheConstants.ShareFeedbackJsonTemplate, ShareFeedbackCardFileName); var template = new AdaptiveCardTemplate(cardTemplate); var card = template.Expand(shareFeedbackCardContents); Attachment attachment = new Attachment() { ContentType = AdaptiveCard.ContentType, Content = AdaptiveCard.FromJson(card).Card, }; this.logger.LogInformation("Get share feedback notification card succeeded."); return(attachment); }
/// <summary> /// Method creates a private channel based on grouping result obtained from grouping criteria. /// </summary> /// <param name="accessToken">Token to access Microsoft Graph API.</param> /// <param name="groupId">Team Azure Active Directory object id of the channel where bot is installed.</param> /// <param name="membersGroupingWithChannel">A dictionary with members grouped into channels based on entered grouping criteria.</param> /// <param name="groupActivityCreator">Team owner who initiated group activity.</param> /// <param name="groupDetail">Values entered by user in task module.</param> /// <returns>Return the List of channels created using Microsoft Graph API.</returns> private async Task <Tuple <List <ChannelApiResponse>, List <string> > > CreatePrivateChannelAsync(string accessToken, string groupId, Dictionary <int, IList <TeamsChannelAccount> > membersGroupingWithChannel, TeamsChannelAccount groupActivityCreator, GroupDetail groupDetail) { List <ChannelApiResponse> privateChannelApiResponses = new List <ChannelApiResponse>(); var notCreatedChannels = new List <string>(); try { var privateChannelRequestData = new PrivateChannelRequest(); var channelCount = 1; foreach (var groupedChannel in membersGroupingWithChannel) { privateChannelRequestData.DisplayName = $"{groupDetail.GroupTitle}-{channelCount}"; privateChannelRequestData.Description = groupDetail.GroupDescription; privateChannelRequestData.MembershipType = groupDetail.ChannelType; privateChannelRequestData.Members = new List <ChannelMember>(); foreach (var groupMember in groupedChannel.Value) { var role = groupActivityCreator.AadObjectId.Contains(groupMember.AadObjectId) ? OwnerUserRole : MemberUserRole; privateChannelRequestData.Members.Add(new ChannelMember { UserOdataBind = $"{ChannelMemberGraphUrl}('{groupMember.AadObjectId}')", Roles = new List <string> { role } }); } string createChannelData = JsonConvert.SerializeObject(privateChannelRequestData); var privateChannels = await this.graphApiHelper.CreatePrivateChannelAsync(accessToken, createChannelData, groupId); if (privateChannels != null) { privateChannelApiResponses.Add(privateChannels); } else { notCreatedChannels.Add(string.Format("*{0}*", privateChannels.DisplayName)); this.logger.LogInformation($"Not able to create channel for: {privateChannels.DisplayName}"); } channelCount++; } return(new Tuple <List <ChannelApiResponse>, List <string> >(privateChannelApiResponses, notCreatedChannels)); } catch (Exception ex) { this.logger.LogError(ex, "Error while creating private channel for the team."); return(null); } }
/// <summary> /// Create public or private channel based on grouping criteria. /// </summary> /// <param name="accessToken">Token to access Microsoft Graph API.</param> /// <param name="groupActivityId">group activity Id.</param> /// <param name="teamId">Team id where messaging extension is installed.</param> /// <param name="groupId">Team Azure Active Directory object id of the channel where bot is installed.</param> /// <param name="valuesFromTaskModule">Group activity details from task module entered by user.</param> /// <param name="membersGroupingWithChannel">List of all members grouped in channels based on grouping criteria.</param> /// <param name="groupActivityCreator">Team owner who started the group activity.</param> /// <param name="turnContext">Provides context for a turn of a bot.</param> /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param> /// <returns>A task that returns true if channel is created successfully.</returns> private async Task CreateChannelAsync(string accessToken, string groupActivityId, string teamId, string groupId, GroupDetail valuesFromTaskModule, Dictionary <int, IList <TeamsChannelAccount> > membersGroupingWithChannel, TeamsChannelAccount groupActivityCreator, string groupingMessage, ITurnContext <IInvokeActivity> turnContext, CancellationToken cancellationToken) { try { string channelType = valuesFromTaskModule.ChannelType; Tuple <List <ChannelApiResponse>, List <string> > channelDetails = null; switch (channelType) { case Constants.PublicChannelType: channelDetails = await this.CreatePublicChannelAsync(accessToken, membersGroupingWithChannel, valuesFromTaskModule, groupId, groupActivityCreator.Name, groupingMessage, turnContext, cancellationToken); break; case Constants.PrivateChannelType: channelDetails = await this.CreatePrivateChannelAsync(accessToken, groupId, membersGroupingWithChannel, groupActivityCreator, valuesFromTaskModule); break; } if (channelDetails != null && channelDetails.Item1.Count > 0) { bool isChannelInfoSaved = await this.StoreChannelsCreatedDetailsAsync(teamId, groupActivityId, channelType, channelDetails.Item1, turnContext); if (!isChannelInfoSaved) { await turnContext.SendActivityAsync(Strings.CustomErrorMessage); this.logger.LogInformation($"Saving newly created channel details to table storage failed for teamId: {teamId}."); } } // show the list of channels to the user which are failed to get created. if (channelDetails.Item2.Count > 0) { StringBuilder channelsNotCreated = new StringBuilder(); foreach (var channel in channelDetails.Item2) { channelsNotCreated.AppendLine(channel).AppendLine(); } this.logger.LogError($"Number of channels failed to get created. TotalChannels {channelDetails.Item2.Count.ToString()}, Team {teamId}"); await turnContext.SendActivityAsync(MessageFactory.Attachment(GroupActivityCard.GetChannelCreationFailedCard(channelsNotCreated.ToString(), valuesFromTaskModule.GroupTitle))); } } catch (Exception ex) { await turnContext.SendActivityAsync(Strings.CustomErrorMessage); this.logger.LogError(ex, $"Error while creating channels for teamId: {teamId}"); } }
/// <summary> /// Validate channel count and create channel using Microsoft Graph API. /// </summary> /// <param name="token">Token to access Microsoft Graph API.</param> /// <param name="groupActivityId">Guid group activity Id.</param> /// <param name="teamId">Team id where messaging extension is installed.</param> /// <param name="groupId">Team Azure Active Directory object id of the channel where bot is installed.</param> /// <param name="groupingMessage">Grouping message with members mapped to groups.</param> /// <param name="valuesFromTaskModule">Group activity details obtained from task module as entered by user.</param> /// <param name="membersGroupingWithChannel">List of all members grouped in channels based on grouping criteria.</param> /// <param name="groupActivityCreator">Team owner who started the group activity.</param> /// <param name="turnContext">Provides context for a turn of a bot.</param> /// <param name="cancellationToken">A cancellation token that can be used by other objects or threads to receive notice of cancellation.</param> /// <returns>A task that returns true if channel is created successfully.</returns> public async Task ValidateAndCreateChannelAsync(string token, string groupActivityId, string teamId, string groupId, string groupingMessage, GroupDetail valuesFromTaskModule, Dictionary <int, IList <TeamsChannelAccount> > membersGroupingWithChannel, TeamsChannelAccount groupActivityCreator, ITurnContext <IInvokeActivity> turnContext, CancellationToken cancellationToken) { // Validate that channel count to be created are in limit with public channels less than 200 and private channel 30. bool?isValidChannelCount = await this.ValidateChannelCountAsync(token, valuesFromTaskModule.ChannelType, membersGroupingWithChannel.Count, groupId); if (isValidChannelCount == null) { await turnContext.SendActivityAsync(Strings.CustomErrorMessage); this.logger.LogInformation($"Channel count is null for teamId : {teamId}"); return; } if (isValidChannelCount == false) { await turnContext.SendActivityAsync(Strings.ChannelCountValidationText); this.logger.LogInformation($"Channel count is not valid for teamId : {teamId}"); return; } this.taskWrapper.Enqueue(this.CreateChannelAsync(token, groupActivityId, teamId, groupId, valuesFromTaskModule, membersGroupingWithChannel, groupActivityCreator, groupingMessage, turnContext, cancellationToken)); }