Exemple #1
0
        /// <summary>
        /// Invoked when Bot/Messaging Extension is installed in team to send welcome card.
        /// </summary>
        /// <param name="membersAdded">A list of all the members added to the conversation, as described by the conversation update activity.</param>
        /// <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 that represents welcome card when bot is added first time by user.</returns>
        /// <remarks>
        /// Reference link: https://docs.microsoft.com/en-us/dotnet/api/microsoft.bot.builder.activityhandler.onmembersaddedasync?view=botbuilder-dotnet-stable.
        /// </remarks>
        protected override async Task OnMembersAddedAsync(
            IList <ChannelAccount> membersAdded,
            ITurnContext <IConversationUpdateActivity> turnContext,
            CancellationToken cancellationToken)
        {
            try
            {
                turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext));
                this.RecordEvent(nameof(this.OnMembersAddedAsync), turnContext);

                var activity = turnContext.Activity;
                this.logger.LogInformation($"conversationType: {activity.Conversation.ConversationType}, membersAdded: {activity.MembersAdded?.Count}, membersRemoved: {activity.MembersRemoved?.Count}");

                if (activity.MembersAdded.FirstOrDefault(member => member.Id == activity.Recipient.Id) != null)
                {
                    this.logger.LogInformation($"Bot added {activity.Conversation.Id}");
                    var userWelcomeCardAttachment = WelcomeCard.GetWelcomeCardAttachmentForChannel(this.appBaseUri, this.localizer);
                    await turnContext.SendActivityAsync(MessageFactory.Attachment(userWelcomeCardAttachment), cancellationToken);
                }
                else
                {
                    this.logger.LogError("User data could not be found at OnMembersAddedAsync().");
                }
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, "Exception occurred while sending the welcome card to channel.", SeverityLevel.Error);
                throw;
            }
        }
Exemple #2
0
        /// <summary>
        /// Overriding to send card when award admin member is removed from team.
        /// </summary>
        /// <param name="membersRemoved">A member removed from team, as described by the conversation update 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>Notification card  when bot or member is removed from team.</returns>
        protected override async Task OnMembersRemovedAsync(IList <ChannelAccount> membersRemoved, ITurnContext <IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext));

            var activity = turnContext.Activity;

            this.logger.LogInformation($"conversationType: {activity.Conversation?.ConversationType}, membersRemoved: {membersRemoved?.Count}");
            if (activity.Conversation.ConversationType == ChannelConversationType)
            {
                var teamsDetails = turnContext.Activity.TeamsGetTeamInfo();
                var admin        = await this.configureAdminStorageProvider.GetAdminDetailAsync(teamsDetails.Id);

                // In application, there is a persona named 'Champion' who is the only person in team to create reward cycle, add awards and publish results.
                // In case if the Champion is removed from team, then the bot sends a new card to set up new Champion.
                if (membersRemoved.Any(member => member.AadObjectId == admin.AdminObjectId))
                {
                    this.logger.LogInformation($"Award captain is removed from team. {activity.Conversation.Id}");
                    await turnContext.SendActivityAsync(MessageFactory.Attachment(WelcomeCard.ConfigureNewAdminCard(this.localizer)), cancellationToken);
                }

                // Deleting team information from storage when bot is uninstalled from a team.
                else if (membersRemoved.Any(member => member.Id == activity.Recipient.Id))
                {
                    this.logger.LogInformation($"Bot removed {activity.Conversation.Id}");
                    var teamEntity = await this.teamStorageProvider.GetTeamDetailAsync(teamsDetails.Id);

                    bool operationStatus = await this.teamStorageProvider.DeleteTeamDetailAsync(teamEntity);

                    if (!operationStatus)
                    {
                        this.logger.LogInformation($"Unable to remove team details from table storage.");
                    }
                }
            }
        }
Exemple #3
0
        /// <summary>
        /// Send a welcome card if bot is installed in Team scope.
        /// </summary>
        /// <param name="turnContext">Provides context for a turn in a bot.</param>
        /// <returns>A task that represents a response.</returns>
        private async Task HandleMemberAddedInTeamAsync(ITurnContext <IConversationUpdateActivity> turnContext)
        {
            turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext));

            var userWelcomeCardAttachment = WelcomeCard.GetWelcomeCardAttachmentForTeam(this.botOptions.Value.AppBaseUri, localizer: this.localizer);
            await turnContext.SendActivityAsync(MessageFactory.Attachment(userWelcomeCardAttachment));

            var activity = turnContext.Activity;

            this.logger.LogInformation($"Bot added in team {turnContext.Activity.Conversation.Id}");

            // Storing team information to storage
            var teamsDetails = activity.TeamsGetTeamInfo();

            TeamEntity teamEntity = new TeamEntity
            {
                TeamId         = teamsDetails.Id,
                BotInstalledOn = DateTime.UtcNow,
                ServiceUrl     = activity.ServiceUrl,
            };

            bool operationStatus = await this.teamStorageProvider.UpsertTeamDetailAsync(teamEntity);

            if (!operationStatus)
            {
                this.logger.LogInformation($"Unable to store bot installation state in storage.");
            }
        }
Exemple #4
0
        /// <summary>
        /// Overriding to send welcome card once Bot/ME is installed in team.
        /// </summary>
        /// <param name="membersAdded">A list of all the members added to the conversation, as described by the conversation update 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>Welcome card  when bot is added first time by user.</returns>
        protected override async Task OnMembersAddedAsync(IList <ChannelAccount> membersAdded, ITurnContext <IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext));

            var activity = turnContext.Activity;

            this.logger.LogInformation($"conversationType: {activity.Conversation?.ConversationType}, membersAdded: {membersAdded?.Count}");

            if (membersAdded.Where(member => member.Id == activity.Recipient.Id).FirstOrDefault() != null)
            {
                this.logger.LogInformation($"Bot added {activity.Conversation.Id}");
                await turnContext.SendActivityAsync(MessageFactory.Attachment(WelcomeCard.GetCard(this.appBaseUrl, this.localizer)), cancellationToken);
            }

            var        teamsDetails = turnContext.Activity.TeamsGetTeamInfo();
            TeamEntity teamEntity   = new TeamEntity
            {
                TeamId         = teamsDetails.Id,
                BotInstalledOn = DateTime.UtcNow,
                ServiceUrl     = turnContext.Activity.ServiceUrl,
            };
            bool operationStatus = await this.teamStorageProvider.StoreOrUpdateTeamDetailAsync(teamEntity);

            if (!operationStatus)
            {
                this.logger.LogInformation($"Unable to store bot installed detail in table storage.");
            }
        }
        /// <summary>
        /// Send team welcome card to Team channel.
        /// </summary>
        /// <param name="turnContext">Provides context for a turn in a bot.</param>
        /// <returns>A task that represents a response.</returns>
        private async Task HandleMemberAddedInTeamAsync(ITurnContext <IConversationUpdateActivity> turnContext)
        {
            this.logger.LogInformation($"Bot added in team {turnContext.Activity.Conversation.Id}");
            var        channelData = turnContext.Activity.GetChannelData <TeamsChannelData>();
            Attachment welcomeCardAttachment;

            if (channelData.Team.Id == this.curatorTeamId)
            {
                welcomeCardAttachment = WelcomeCard.GetWelcomeCardAttachmentForCuratorTeam(this.botOptions.Value.AppBaseUri, this.localizer);
            }
            else
            {
                welcomeCardAttachment = WelcomeCard.GetWelcomeCardAttachmentForTeam(this.botOptions.Value.AppBaseUri, this.localizer);
            }

            // Storing team information to storage
            var        teamsDetails = turnContext.Activity.TeamsGetTeamInfo();
            TeamEntity teamEntity   = new TeamEntity
            {
                TeamId         = teamsDetails.Id,
                BotInstalledOn = DateTime.UtcNow,
                ServiceUrl     = turnContext.Activity.ServiceUrl,
            };

            bool operationStatus = await this.teamStorageProvider.StoreOrUpdateTeamDetailAsync(teamEntity);

            if (!operationStatus)
            {
                this.logger.LogWarning($"Unable to store bot Installation detail in storage.");
            }

            await turnContext.SendActivityAsync(MessageFactory.Attachment(welcomeCardAttachment));
        }
Exemple #6
0
        /// <summary>
        /// Overriding to send card when R&R admin member is removed from team.
        /// </summary>
        /// <param name="membersRemoved">A member removed from team, as described by the conversation update 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>Notification card  when bot or member is removed from team.</returns>
        protected override async Task OnMembersRemovedAsync(IList <ChannelAccount> membersRemoved, ITurnContext <IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext));

            var activity = turnContext.Activity;

            this.logger.LogInformation($"conversationType: {activity.Conversation?.ConversationType}, membersRemoved: {membersRemoved?.Count}");
            var teamsDetails = turnContext.Activity.TeamsGetTeamInfo();
            var admin        = await this.configureAdminStorageProvider.GetAdminDetailAsync(teamsDetails.Id);

            if (membersRemoved.Where(member => member.AadObjectId == admin.AdminObjectId).FirstOrDefault() != null)
            {
                this.logger.LogInformation($"Member removed {activity.Conversation.Id}");
                await turnContext.SendActivityAsync(MessageFactory.Attachment(WelcomeCard.GetCard(this.appBaseUrl, this.localizer)), cancellationToken);
            }
            else if (membersRemoved.Where(member => member.Id == activity.Recipient.Id).Any())
            {
                this.logger.LogInformation($"Bot removed {activity.Conversation.Id}");
                var teamEntity = await this.teamStorageProvider.GetTeamDetailAsync(teamsDetails.Id);

                bool operationStatus = await this.teamStorageProvider.DeleteTeamDetailAsync(teamEntity);

                if (!operationStatus)
                {
                    this.logger.LogInformation($"Unable to remove team details from table storage.");
                }
            }
        }
        /// <summary>
        /// Add user membership to storage if bot is installed in Team scope.
        /// </summary>
        /// <param name="turnContext">Provides context for a turn in a bot.</param>
        /// <returns>A task that represents a response.</returns>
        public async Task SendWelcomeCardInChannelAsync(ITurnContext <IConversationUpdateActivity> turnContext)
        {
            turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext), "Turncontext cannot be null");

            var userWelcomeCardAttachment = WelcomeCard.GetWelcomeCardAttachmentForTeam(this.botOptions.Value.AppBaseUri, this.localizer);
            await turnContext.SendActivityAsync(MessageFactory.Attachment(userWelcomeCardAttachment));
        }
        /// <summary>
        /// Sent welcome card to personal chat.
        /// </summary>
        /// <param name="turnContext">Provides context for a turn in a bot.</param>
        /// <returns>A task that represents a response.</returns>
        public async Task OnBotInstalledInPersonalAsync(ITurnContext <IConversationUpdateActivity> turnContext)
        {
            turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext), "Turncontext cannot be null");

            this.logger.LogInformation($"Bot added in personal scope for user {turnContext.Activity.From.AadObjectId}");
            var userWelcomeCardAttachment = WelcomeCard.GetWelcomeCardAttachmentForTeam(
                this.botOptions.Value.AppBaseUri,
                localizer: this.localizer);
            await turnContext.SendActivityAsync(MessageFactory.Attachment(userWelcomeCardAttachment));

            var  activity   = turnContext.Activity;
            User userEntity = new User
            {
                AADObjectId    = activity.From.AadObjectId,
                ConversationId = activity.Conversation.Id,
                BotInstalledOn = DateTime.UtcNow,
                ServiceUrl     = turnContext.Activity.ServiceUrl,
            };

            bool operationStatus = await this.userConfigurationRepository.UpsertUserConfigurationsAsync(userEntity);

            if (operationStatus)
            {
                this.logger.LogInformation($"Successfully stored bot installation state for user {activity.From.AadObjectId} in storage.");
            }
            else
            {
                this.logger.LogInformation($"Unable to store bot installation state for user {activity.From.AadObjectId} in storage.");
            }
        }
Exemple #9
0
        /// <summary>
        /// Sent welcome card to personal chat.
        /// </summary>
        /// <param name="turnContext">Provides context for a turn in a bot.</param>
        /// <returns>A task that represents a response.</returns>
        private async Task HandleMemberAddedinPersonalScopeAsync(ITurnContext <IConversationUpdateActivity> turnContext)
        {
            this.logger.LogInformation($"Bot added in personal {turnContext.Activity.Conversation.Id}");

            var userStateAccessors    = this.userState.CreateProperty <UserConversationState>(nameof(UserConversationState));
            var userConversationState = await userStateAccessors.GetAsync(turnContext, () => new UserConversationState());

            if (userConversationState.IsWelcomeCardSent)
            {
                return;
            }

            var userWelcomeCardAttachment = WelcomeCard.GetWelcomeCardAttachmentForPersonal(
                this.botOptions.Value.AppBaseUri,
                localizer: this.localizer,
                this.botOptions.Value.ManifestId,
                Constants.DiscoverTabEntityId);

            await turnContext.SendActivityAsync(MessageFactory.Attachment(userWelcomeCardAttachment));

            userConversationState.IsWelcomeCardSent = true;
            await userStateAccessors.SetAsync(turnContext, userConversationState);

            await this.userDetailProvider.AddUserDetailAsync(
                turnContext.Activity.Conversation.Id,
                turnContext.Activity.From.AadObjectId,
                turnContext.Activity.ServiceUrl);
        }
Exemple #10
0
 /// <summary>
 /// Initializes a new instance of the <see cref="RootDialog" /> class.
 /// </summary>
 /// <param name="dialogFactory">DialogFactory object.</param>
 /// <param name="authenticateUser">AuthenticateUser object.</param>
 /// <param name="carousel">CarouselHelp object.</param>
 /// <param name="welcomeCard">WelcomeCard object.</param>
 public RootDialog(
     IDialogFactory dialogFactory,
     AuthenticateUser authenticateUser,
     CarouselHelp carousel,
     WelcomeCard welcomeCard)
 {
     this.dialogFactory    = dialogFactory;
     this.authenticateUser = authenticateUser;
     this.carousel         = carousel;
     this.welcomeCard      = welcomeCard;
 }
Exemple #11
0
        /// <summary>
        /// Creates the adaptive card for the team welcome message.
        /// </summary>
        /// <returns>The Welcome Adaptive card.</returns>
        public Attachment CreateWelcomeCardAttachment()
        {
            var adaptiveWelcomeCardJson = WelcomeCard.GetCard();
            var adaptiveCardAttachment  = new Attachment()
            {
                ContentType = "application/vnd.microsoft.card.adaptive",
                Content     = JsonConvert.DeserializeObject(adaptiveWelcomeCardJson),
            };

            return(adaptiveCardAttachment);
        }
        /// <summary>
        /// Send Welcome card in teams channel.
        /// </summary>
        /// <param name="membersAdded">A list of all the members added to the conversation, as described by the conversation update 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>Welcome card  when Bot/Messaging Extension is added first time by user.</returns>
        protected override async Task OnMembersAddedAsync(IList <ChannelAccount> membersAdded, ITurnContext <IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            var activity = turnContext.Activity;

            this.logger.LogInformation($"conversationType: {activity.Conversation.ConversationType}, membersAdded: {activity.MembersAdded?.Count}, membersRemoved: {activity.MembersRemoved?.Count}");

            // If added member is bot then send welcome card in channel.
            if (membersAdded.Any(member => member.Id == activity.Recipient.Id))
            {
                this.logger.LogInformation($"Bot added {activity.Conversation.Id}");
                await turnContext.SendActivityAsync(MessageFactory.Attachment(WelcomeCard.GetWelcomeCardAttachment(this.appBaseUrl)));
            }
        }
Exemple #13
0
        /// <summary>
        /// Overriding to send welcome card once Bot/ME is installed in team.
        /// </summary>
        /// <param name="membersAdded">A list of all the members added to the conversation, as described by the conversation update 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>Welcome card  when bot is added first time by user.</returns>
        protected override async Task OnMembersAddedAsync(IList <ChannelAccount> membersAdded, ITurnContext <IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            var activity = turnContext.Activity;

            this.logger.LogInformation($"conversationType: {activity.Conversation?.ConversationType}, MemberCount: {membersAdded?.Count}");

            if (membersAdded.Where(member => member.Id == activity.Recipient.Id).FirstOrDefault() != null)
            {
                this.logger.LogInformation($"Bot added {activity.Conversation.Id}");
                var welcomeCardImageUrl = new Uri(new Uri(this.appBaseUrl), "/images/welcome.png");
                await turnContext.SendActivityAsync(MessageFactory.Attachment(WelcomeCard.GetWelcomeCardAttachment(welcomeCardImageUrl)), cancellationToken);
            }
        }
Exemple #14
0
        private static async Task MainAsync(string[] args)
        {
            var sc = CommonInitialize.Main();

            // Initialize the ASP.NET service provider and freeze this Task indefinitely.
            await using var services = ConfigureServices(sc);
            await CommonInitialize.ConfigureServicesAsync(services);

            var client          = services.GetRequiredService <DiscordSocketClient>();
            var db              = services.GetRequiredService <IDbService>();
            var censusEvents    = services.GetRequiredService <CensusEventService>();
            var mute            = services.GetRequiredService <MuteService>();
            var roleRemover     = services.GetRequiredService <TimedRoleManager>();
            var ffLogs          = services.GetRequiredService <FFLogsClient>();
            var web             = services.GetRequiredService <WebClient>();
            var lodestone       = services.GetRequiredService <CharacterLookup>();
            var keepClean       = services.GetRequiredService <KeepClean>();
            var ephemeralPinner = services.GetRequiredService <EphemeralPinManager>();
            var templates       = services.GetRequiredService <ITemplateProvider>();

            keepClean.Initialize();
            roleRemover.Initialize();
            ephemeralPinner.Initialize();
            await ffLogs.Initialize();

            client.ReactionAdded += (message, channel, reaction)
                                    => ReactionReceived.HandlerAdd(client, db, lodestone, message, channel, reaction);
            client.ReactionRemoved += (message, channel, reaction)
                                      => ReactionReceived.HandlerRemove(db, message, channel, reaction);

            client.ReactionAdded += (message, _, reaction)
                                    => VoteReactions.HandlerAdd(client, db, message, reaction);

            client.MessageDeleted  += (message, channel) => AuditDeletion.Handler(db, client, message, channel);
            client.MessageReceived += message => ChatCleanup.Handler(db, web, templates, message);

            client.MessageReceived += message => MessageCache.Handler(db, message);
            client.MessageReceived += message => TriggerDispatcher.Handler(client, message);

            client.UserJoined += user => WelcomeCard.Handler(client, templates, user);

            client.GuildMemberUpdated += censusEvents.GuildMemberUpdated;

            client.UserVoiceStateUpdated += mute.OnVoiceJoin;

            client.ButtonExecuted += component => Modmail.Handler(db, component);

            Log.Information("Prima.Stable logged in!");

            await Task.Delay(-1);
        }
        /// <summary>
        /// Handle when a message is addressed to the bot in personal scope.
        /// </summary>
        /// <param name="message">Message activity of bot.</param>
        /// <param name="turnContext">The turn context.</param>
        /// <param name="telemetryClient">The Application Insights telemetry client. </param>
        /// <param name="logger">Sends logs to the Application Insights service.</param>
        /// <param name="cardConfigurationStorageProvider">Provider to search card configuration details in Azure Table Storage.</param>
        /// <param name="environment">Hosting environment.</param>
        /// <param name="ticketGenerateStorageProvider">Provider to get ticket id to Azure Table Storage.</param>
        /// <param name="ticketDetailStorageProvider">Provider to store ticket details to Azure Table Storage.</param>
        /// <param name="microsoftAppCredentials">Microsoft Application credentials for Bot/ME.</param>
        /// <param name="appBaseUrl">Represents the Application base Uri.</param>
        /// <param name="localizer">The current cultures' string localizer.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns> A task that represents the work queued to execute for user message activity to bot.</returns>
        internal static async Task OnMessageActivityInPersonalChatAsync(
            IMessageActivity message,
            ITurnContext <IMessageActivity> turnContext,
            TelemetryClient telemetryClient,
            ILogger <RemoteSupportActivityHandler> logger,
            ICardConfigurationStorageProvider cardConfigurationStorageProvider,
            IHostingEnvironment environment,
            ITicketIdGeneratorStorageProvider ticketGenerateStorageProvider,
            ITicketDetailStorageProvider ticketDetailStorageProvider,
            MicrosoftAppCredentials microsoftAppCredentials,
            string appBaseUrl,
            IStringLocalizer <Strings> localizer,
            CancellationToken cancellationToken)
        {
            if (!string.IsNullOrEmpty(message.ReplyToId) && message.Value != null && ((JObject)message.Value).HasValues)
            {
                telemetryClient.TrackTrace("Card submitted in 1:1 chat.");
                await OnAdaptiveCardSubmitInPersonalChatAsync(message: message, turnContext : turnContext, ticketGenerateStorageProvider : ticketGenerateStorageProvider, ticketDetailStorageProvider : ticketDetailStorageProvider, cardConfigurationStorageProvider : cardConfigurationStorageProvider, microsoftAppCredentials : microsoftAppCredentials, logger : logger, appBaseUrl : appBaseUrl, environment : environment, localizer : localizer, cancellationToken : cancellationToken);

                return;
            }

            string text = (turnContext.Activity.Text ?? string.Empty).Trim().ToUpperInvariant();

            switch (text)
            {
            case Constants.NewRequestAction:
                logger.LogInformation("New request action called.");
                CardConfigurationEntity cardTemplateJson = await cardConfigurationStorageProvider.GetConfigurationAsync();

                IMessageActivity newTicketActivity = MessageFactory.Attachment(TicketCard.GetNewTicketCard(cardTemplateJson, localizer));
                await turnContext.SendActivityAsync(newTicketActivity);

                break;

            case Constants.NoCommand:
                return;

            default:
                if (turnContext.Activity.Attachments == null || turnContext.Activity.Attachments.Count == 0)
                {
                    // In case of ME when user clicks on closed or active requests the bot posts adaptive card of request details we don't have to consider this as invalid command.
                    logger.LogInformation("Unrecognized input in End User.");
                    await turnContext.SendActivityAsync(MessageFactory.Attachment(WelcomeCard.GetCard(appBaseUrl, localizer)));
                }

                break;
            }
        }
        // Handle members added conversationUpdate event in 1:1 chat
        private async Task OnMembersAddedToPersonalChatAsync(IList <ChannelAccount> membersAdded, ITurnContext <IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            var activity = turnContext.Activity;

            if (membersAdded.Any(m => m.Id == activity.Recipient.Id))
            {
                // User started chat with the bot in personal scope, for the first time
                this.telemetryClient.TrackTrace($"Bot added to 1:1 chat {activity.Conversation.Id}");

                var welcomeText = await this.configurationProvider.GetSavedEntityDetailAsync(ConfigurationEntityTypes.WelcomeMessageText);

                var userWelcomeCardAttachment = WelcomeCard.GetCard(welcomeText);
                await turnContext.SendActivityAsync(MessageFactory.Attachment(userWelcomeCardAttachment));
            }
        }
Exemple #17
0
        /// <summary>
        /// Handle 1:1 chat with members who started chat for the first time.
        /// </summary>
        /// <param name="membersAdded">Channel account information needed to route a message.</param>
        /// <param name="turnContext">Context object containing information cached for a single turn of conversation with a user.</param>
        /// <param name="logger">Sends logs to the Application Insights service.</param>
        /// <param name="appBaseUrl">Represents the Application base Uri.</param>
        /// <param name="localizer">The current cultures' string localizer.</param>
        /// <returns>A task that represents the work queued to execute.</returns>
        internal static async Task OnMembersAddedToPersonalChatAsync(
            IList <ChannelAccount> membersAdded,
            ITurnContext <IConversationUpdateActivity> turnContext,
            ILogger logger,
            string appBaseUrl,
            IStringLocalizer <Strings> localizer)
        {
            turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext));
            var activity = turnContext.Activity;

            if (membersAdded.Any(channelAccount => channelAccount.Id == activity.Recipient.Id))
            {
                // User started chat with the bot in personal scope, for the first time.
                logger.LogInformation($"Bot added to 1:1 chat {activity.Conversation.Id}");
                await turnContext.SendActivityAsync(MessageFactory.Attachment(WelcomeCard.GetCard(appBaseUrl, localizer)));
            }
        }
        /// <summary>
        /// Sends welcome card in 1:1 chat.
        /// </summary>
        /// <param name="membersAdded">Channel account information needed to route a message.</param>
        /// <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 that represents the work queued to execute.</returns>
        public async Task SendWelcomeCardInPersonalChatAsync(
            IList <ChannelAccount> membersAdded,
            ITurnContext <IConversationUpdateActivity> turnContext,
            CancellationToken cancellationToken)
        {
            var activity = turnContext.Activity;

            if (membersAdded.Any(channelAccount => channelAccount.Id == activity.Recipient.Id))
            {
                // User started chat with the bot in personal scope, for the first time.
                this.logger.LogInformation($"Bot added to 1:1 chat {activity.Conversation.Id}");
                var welcomeText = await this.configurationProvider.GetSavedEntityDetailAsync(ConfigurationEntityTypes.WelcomeMessageText).ConfigureAwait(false);

                var userWelcomeCardAttachment = WelcomeCard.GetCard(welcomeText);
                await turnContext.SendActivityAsync(MessageFactory.Attachment(userWelcomeCardAttachment), cancellationToken).ConfigureAwait(false);
            }
        }
Exemple #19
0
        // Load attachment from file.
        private Attachment CreateAdaptiveCardAttachment()
        {
            //// combine path for cross platform support
            //string[] paths = { ".", "Cards", "welcomeCard.json" };
            //string fullPath = Path.Combine(paths);
            //var adaptiveCard = File.ReadAllText(fullPath);

            var welcomeCs    = new WelcomeCard();
            var adaptiveCard = welcomeCs.GetWelcomeAdaptiveCard();

            return(new Attachment()
            {
                ContentType = "application/vnd.microsoft.card.adaptive",
                //Content = JsonConvert.DeserializeObject(adaptiveCard),
                Content = adaptiveCard
            });
        }
        /// <summary>
        /// Implemented this to provide logic when bot is added, to implement bot's welcome logic.
        /// </summary>
        /// <param name="membersAdded">A list of all the members added to the conversation, as described by the conversation update 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>Welcome card  when bot is added first time by user.</returns>
        protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            var activity = turnContext.Activity;
            this.logger.LogInformation($"conversationType: {activity.Conversation.ConversationType}, membersAdded: {activity.MembersAdded?.Count}, membersRemoved: {activity.MembersRemoved?.Count}");

            if (activity.MembersAdded.Where(member => member.Id != activity.Recipient.Id).FirstOrDefault() != null)
            {
                this.logger.LogInformation($"Bot added {activity.Conversation.Id}");
                var userStateAccessors = this.userState.CreateProperty<ConversationData>(nameof(ConversationData));
                var userdata = await userStateAccessors.GetAsync(turnContext, () => new ConversationData()).ConfigureAwait(false);
                if (userdata?.IsWelcomeCardSent == null || userdata?.IsWelcomeCardSent == false)
                {
                    userdata.IsWelcomeCardSent = true;
                    var userWelcomeCardAttachment = WelcomeCard.GetCard(this.appBaseUrl);
                    await turnContext.SendActivityAsync(MessageFactory.Attachment(userWelcomeCardAttachment)).ConfigureAwait(false);
                }
            }
        }
Exemple #21
0
        /// <summary>
        /// Handle when a message is addressed to the bot in personal scope.
        /// </summary>
        /// <param name="message">Message activity of bot.</param>
        /// <param name="turnContext">The turn context.</param>
        /// <param name="logger">Sends logs to the Application Insights service.</param>
        /// <param name="cardConfigurationStorageProvider">Provider to search card configuration details in Azure Table Storage.</param>
        /// <param name="ticketGenerateStorageProvider">Provider to get ticket id to Azure Table Storage.</param>
        /// <param name="ticketDetailStorageProvider">Provider to store ticket details to Azure Table Storage.</param>
        /// <param name="microsoftAppCredentials">Microsoft Application credentials for Bot/ME.</param>
        /// <param name="appBaseUrl">Represents the Application base Uri.</param>
        /// <param name="localizer">The current cultures' string localizer.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns> A task that represents the work queued to execute for user message activity to bot.</returns>
        internal static async Task OnMessageActivityInPersonalChatAsync(
            IMessageActivity message,
            ITurnContext <IMessageActivity> turnContext,
            ILogger logger,
            ICardConfigurationStorageProvider cardConfigurationStorageProvider,
            ITicketIdGeneratorStorageProvider ticketGenerateStorageProvider,
            ITicketDetailStorageProvider ticketDetailStorageProvider,
            MicrosoftAppCredentials microsoftAppCredentials,
            string appBaseUrl,
            IStringLocalizer <Strings> localizer,
            CancellationToken cancellationToken)
        {
            if (!string.IsNullOrEmpty(message.ReplyToId) && message.Value != null && ((JObject)message.Value).HasValues)
            {
                logger.LogInformation("Card submitted in 1:1 chat.");
                await OnAdaptiveCardSubmitInPersonalChatAsync(message, turnContext, ticketGenerateStorageProvider, ticketDetailStorageProvider, cardConfigurationStorageProvider, microsoftAppCredentials, logger, appBaseUrl, localizer, cancellationToken);

                return;
            }

            string text = (turnContext.Activity.Text ?? string.Empty).Trim().ToUpperInvariant();

            if (text.Equals(localizer.GetString("BotCommandNewRequest"), StringComparison.CurrentCultureIgnoreCase))
            {
                logger.LogInformation("New request action called.");
                CardConfigurationEntity cardTemplateJson = await cardConfigurationStorageProvider.GetConfigurationAsync();

                IMessageActivity newTicketActivity = MessageFactory.Attachment(TicketCard.GetNewTicketCard(cardTemplateJson, localizer));
                await turnContext.SendActivityAsync(newTicketActivity);
            }
            else if (text.Equals(localizer.GetString("No").ToString(), StringComparison.CurrentCultureIgnoreCase))
            {
                return;
            }
            else
            {
                if (turnContext.Activity.Attachments == null || turnContext.Activity.Attachments.Count == 0)
                {
                    // In case of ME when user clicks on closed or active requests the bot posts adaptive card of request details we don't have to consider this as invalid command.
                    logger.LogInformation("Unrecognized input in End User.");
                    await turnContext.SendActivityAsync(MessageFactory.Attachment(WelcomeCard.GetCard(appBaseUrl, localizer)));
                }
            }
        }
        /// <summary>
        /// Invoked when Bot/Messaging Extension is installed in team to send welcome card.
        /// </summary>
        /// <param name="membersAdded">A list of all the members added to the conversation, as described by the conversation update activity.</param>
        /// <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 that represents welcome card when bot is added first time by user.</returns>
        /// <remarks>
        /// Reference link: https://docs.microsoft.com/en-us/dotnet/api/microsoft.bot.builder.activityhandler.onmembersaddedasync?view=botbuilder-dotnet-stable.
        /// </remarks>
        protected override async Task OnMembersAddedAsync(
            IList <ChannelAccount> membersAdded,
            ITurnContext <IConversationUpdateActivity> turnContext,
            CancellationToken cancellationToken)
        {
            turnContext = turnContext ?? throw new ArgumentNullException(nameof(turnContext));
            this.RecordEvent(nameof(this.OnMembersAddedAsync), turnContext);

            var activity = turnContext.Activity;

            this.logger.LogInformation($"conversationType: {activity.Conversation.ConversationType}, membersAdded: {activity.MembersAdded?.Count}");

            if (activity.MembersAdded.Any(member => member.Id == activity.Recipient.Id))
            {
                this.logger.LogInformation($"Bot added {activity.Conversation.Id}");
                var userWelcomeCardAttachment = WelcomeCard.GetWelcomeCardAttachmentForChannel(this.appBaseUri, this.localizer);
                await turnContext.SendActivityAsync(MessageFactory.Attachment(userWelcomeCardAttachment), cancellationToken);
            }
        }
        public QnABot(IConfiguration configuration,
                      ILogger <QnABot> logger,
                      IQnAService qnAService,
                      WelcomeCard welcomeDialog,
                      SupportTicketCard supportTicketCard,
                      BotConversationState botConversationState,
                      UserFeedbackCard userFeedbackCard,
                      Incident incident)
        {
            _configuration        = configuration;
            _logger               = logger;
            _qnAService           = qnAService;
            _welcomeDialog        = welcomeDialog;
            _supportTicketCard    = supportTicketCard;
            _botConversationState = botConversationState;
            _incident             = incident;
            _userFeedbackCard     = userFeedbackCard;

            int.TryParse(configuration["MinConfidenceScore"], out _minConfidenceScore);
        }
        /// <summary>
        /// Sent welcome card to personal chat.
        /// </summary>
        /// <param name="turnContext">Provides context for a turn in a bot.</param>
        /// <returns>A task that represents a response.</returns>
        private async Task SendWelcomeCardInPersonalScopeAsync(ITurnContext <IConversationUpdateActivity> turnContext)
        {
            this.logger.LogInformation($"Bot added in personal {turnContext.Activity.Conversation.Id}");
            var userStateAccessors    = this.userState.CreateProperty <UserConversationState>(nameof(UserConversationState));
            var userConversationState = await userStateAccessors.GetAsync(turnContext, () => new UserConversationState());

            if (userConversationState?.IsWelcomeCardSent == null || userConversationState?.IsWelcomeCardSent == false)
            {
                userConversationState.IsWelcomeCardSent = true;
                await userStateAccessors.SetAsync(turnContext, userConversationState);

                var userWelcomeCardAttachment = WelcomeCard.GetWelcomeCardAttachmentForPersonal(
                    this.options.Value.AppBaseUri,
                    localizer: this.localizer,
                    this.botOptions.Value.ManifestId,
                    this.options.Value.DiscoverTabEntityId);

                await turnContext.SendActivityAsync(MessageFactory.Attachment(userWelcomeCardAttachment));
            }
        }
        /// <summary>
        /// Send welcome card to personal chat.
        /// </summary>
        /// <param name="turnContext">Provides context for a turn in a bot.</param>
        /// <returns>A task that represents a response.</returns>
        private async Task HandleMemberAddedInPersonalScopeAsync(ITurnContext <IConversationUpdateActivity> turnContext)
        {
            this.logger.LogInformation($"Bot added in personal {turnContext.Activity.Conversation.Id}");

            var userStateAccessors    = this.userState.CreateProperty <UserConversationState>(nameof(UserConversationState));
            var userConversationState = await userStateAccessors.GetAsync(turnContext, () => new UserConversationState());

            if (userConversationState.IsWelcomeCardSent)
            {
                return;
            }

            var userWelcomeCardAttachment = WelcomeCard.GetWelcomeCardAttachmentForPersonal(
                this.botOptions.Value.AppBaseUri,
                localizer: this.localizer);

            await turnContext.SendActivityAsync(MessageFactory.Attachment(userWelcomeCardAttachment));

            userConversationState.IsWelcomeCardSent = true;
            await userStateAccessors.SetAsync(turnContext, userConversationState);
        }
Exemple #26
0
        /// <summary>
        /// Get welcome card attachment to be sent in personal scope.
        /// </summary>
        /// <returns>User welcome card attachment.</returns>
        public Attachment GetWelcomeCardForPersonalScope()
        {
            var cardPayload        = this.GetCardPayload(WelcomeCardCacheKey, "\\WelcomeCard\\welcome-card.json");
            var welcomeCardOptions = new WelcomeCard
            {
                AppImage        = $"{this.botOptions.Value.AppBaseUri}/images/logo.png",
                TimesheetTabUrl = $"https://teams.microsoft.com/l/entity/{this.botOptions.Value.ManifestId}/fill-timesheet",
                WelcomeCardFillTimesheetButton = this.localizer.GetString("FillTimesheetButton"),
                WelcomeCardIntro    = this.localizer.GetString("WelcomeCardIntro"),
                WelcomeCardSubtitle = this.localizer.GetString("WelcomeCardSubtitle"),
                WelcomeCardTitle    = this.localizer.GetString("WelcomeCardTitle"),
            };
            var          template = new AdaptiveCardTemplate(cardPayload);
            var          cardJson = template.Expand(welcomeCardOptions);
            AdaptiveCard card     = AdaptiveCard.FromJson(cardJson).Card;

            var adaptiveCardAttachment = new Attachment()
            {
                ContentType = AdaptiveCard.ContentType,
                Content     = card,
            };

            return(adaptiveCardAttachment);
        }
        // Handle adaptive card submit in 1:1 chat
        // Submits the question or feedback to the SME team
        private async Task OnAdaptiveCardSubmitInPersonalChatAsync(IMessageActivity message, ITurnContext <IMessageActivity> turnContext, CancellationToken cancellationToken)
        {
            Attachment   smeTeamCard = null;    // Notification to SME team
            Attachment   userCard    = null;    // Acknowledgement to the user
            TicketEntity newTicket   = null;    // New ticket

            this.telemetryClient.TrackTrace($"Otro envio: {message.Text}");

            switch (message.Text)
            {
            case AskAnExpert:
            {
                this.telemetryClient.TrackTrace("Sending user ask an expert card (from answer)");

                var responseCardPayload = ((JObject)message.Value).ToObject <ResponseCardPayload>();
                await turnContext.SendActivityAsync(MessageFactory.Attachment(AskAnExpertCard.GetCard(responseCardPayload)));

                break;
            }

            case ShareFeedback:
            {
                this.telemetryClient.TrackTrace("Sending user share feedback card (from answer)");

                var responseCardPayload = ((JObject)message.Value).ToObject <ResponseCardPayload>();
                await turnContext.SendActivityAsync(MessageFactory.Attachment(ShareFeedbackCard.GetCard(responseCardPayload)));

                break;
            }

            case WelcomeMsg:
                this.telemetryClient.TrackTrace("The user as required the Welcome screen");
                var welcomeText = await this.configurationProvider.GetSavedEntityDetailAsync(ConfigurationEntityTypes.WelcomeMessageText);

                var userWelcomeCardAttachment = WelcomeCard.GetCard(welcomeText);
                await turnContext.SendActivityAsync(MessageFactory.Attachment(userWelcomeCardAttachment));

                break;

            case AskAnExpertCard.AskAnExpertSubmitText:
            {
                this.telemetryClient.TrackTrace($"Received question for expert");

                var askAnExpertPayload = ((JObject)message.Value).ToObject <AskAnExpertCardPayload>();

                // Validate required fields
                if (string.IsNullOrWhiteSpace(askAnExpertPayload.Title))
                {
                    var updateCardActivity = new Activity(ActivityTypes.Message)
                    {
                        Id           = turnContext.Activity.ReplyToId,
                        Conversation = turnContext.Activity.Conversation,
                        Attachments  = new List <Attachment> {
                            AskAnExpertCard.GetCard(askAnExpertPayload)
                        },
                    };
                    await turnContext.UpdateActivityAsync(updateCardActivity, cancellationToken);

                    return;
                }

                var userDetails = await this.GetUserDetailsInPersonalChatAsync(turnContext, cancellationToken);

                newTicket = await this.CreateTicketAsync(message, askAnExpertPayload, userDetails);

                smeTeamCard = new SmeTicketCard(newTicket).ToAttachment(message.LocalTimestamp);
                userCard    = new UserNotificationCard(newTicket).ToAttachment(Resource.NotificationCardContent, message.LocalTimestamp);
                break;
            }

            case ShareFeedbackCard.ShareFeedbackSubmitText:
            {
                this.telemetryClient.TrackTrace($"Received app feedback");

                var shareFeedbackPayload = ((JObject)message.Value).ToObject <ShareFeedbackCardPayload>();

                // Validate required fields
                if (!Enum.TryParse(shareFeedbackPayload.Rating, out FeedbackRating rating))
                {
                    var updateCardActivity = new Activity(ActivityTypes.Message)
                    {
                        Id           = turnContext.Activity.ReplyToId,
                        Conversation = turnContext.Activity.Conversation,
                        Attachments  = new List <Attachment> {
                            ShareFeedbackCard.GetCard(shareFeedbackPayload)
                        },
                    };
                    await turnContext.UpdateActivityAsync(updateCardActivity, cancellationToken);

                    return;
                }

                var userDetails = await this.GetUserDetailsInPersonalChatAsync(turnContext, cancellationToken);

                smeTeamCard = SmeFeedbackCard.GetCard(shareFeedbackPayload, userDetails);
                await turnContext.SendActivityAsync(MessageFactory.Text(Resource.ThankYouTextContent));

                break;
            }

            default:
                this.telemetryClient.TrackTrace($"Unexpected text in submit payload: {message.Text}", SeverityLevel.Warning);
                break;
            }

            // Send message to SME team
            if (smeTeamCard != null)
            {
                var channelId = await this.configurationProvider.GetSavedEntityDetailAsync(ConfigurationEntityTypes.TeamId);

                var resourceResponse = await this.SendCardToTeamAsync(turnContext, smeTeamCard, channelId, cancellationToken);

                // If a ticket was created, update the ticket with the conversation info
                if (newTicket != null)
                {
                    newTicket.SmeCardActivityId       = resourceResponse.ActivityId;
                    newTicket.SmeThreadConversationId = resourceResponse.Id;
                    await this.ticketsProvider.SaveOrUpdateTicketAsync(newTicket);
                }
            }

            // Send acknowledgment to the user
            if (userCard != null)
            {
                await turnContext.SendActivityAsync(MessageFactory.Attachment(userCard), cancellationToken);
            }
        }
        // Handle message activity in 1:1 chat
        private async Task OnMessageActivityInPersonalChatAsync(IMessageActivity message, ITurnContext <IMessageActivity> turnContext, CancellationToken cancellationToken)
        {
            if (!string.IsNullOrEmpty(message.ReplyToId) && (message.Value != null) && ((JObject)message.Value).HasValues)
            {
                this.telemetryClient.TrackTrace("Card submit in 1:1 chat");
                await this.OnAdaptiveCardSubmitInPersonalChatAsync(message, turnContext, cancellationToken);

                return;
            }

            string text = (message.Text ?? string.Empty).Trim().ToLower();

            this.telemetryClient.TrackTrace($"Se ha enviado esta solicitud: {text}");

            switch (text)
            {
            case AskAnExpert:
                this.telemetryClient.TrackTrace("Sending user ask an expert card");
                await turnContext.SendActivityAsync(MessageFactory.Attachment(AskAnExpertCard.GetCard()));

                break;

            case ShareFeedback:
                this.telemetryClient.TrackTrace("Sending user feedback card");
                await turnContext.SendActivityAsync(MessageFactory.Attachment(ShareFeedbackCard.GetCard()));

                break;

            case TakeATour:
                this.telemetryClient.TrackTrace("Sending user tour card");
                var userTourCards = TourCarousel.GetUserTourCards(this.appBaseUri);
                await turnContext.SendActivityAsync(MessageFactory.Carousel(userTourCards));

                break;

            case WelcomeMsg:
                this.telemetryClient.TrackTrace("The user as required the Welcome screen");
                var welcomeText = await this.configurationProvider.GetSavedEntityDetailAsync(ConfigurationEntityTypes.WelcomeMessageText);

                var userWelcomeCardAttachment  = WelcomeCard.GetCard(welcomeText);
                var userWelcomeCardAttachment2 = WelcomeCardPreguntas.GetCard(Resource.WelcomeTeamCardContent);
                await turnContext.SendActivityAsync(MessageFactory.Attachment(userWelcomeCardAttachment));

                await turnContext.SendActivityAsync(MessageFactory.Attachment(userWelcomeCardAttachment2));

                break;

            default:
                this.telemetryClient.TrackTrace("Sending input to QnAMaker");
                var queryResult = await this.GetAnswerFromQnAMakerAsync(text, turnContext, cancellationToken);

                if (queryResult != null)
                {
                    this.telemetryClient.TrackTrace("Sending user QnAMaker card");
                    await turnContext.SendActivityAsync(MessageFactory.Attachment(ResponseCard.GetCard(queryResult.Questions[0], queryResult.Answer, text)));
                }
                else
                {
                    var tileList = await this.MatchTagsWithMessageAsync(text);

                    if (tileList != null)
                    {
                        this.telemetryClient.TrackTrace("Sending user tags card");
                        await turnContext.SendActivityAsync(SuggestedLinkCard.GetTagsCarouselCards(text, tileList, Resource.CustomMessage));
                    }
                    else
                    {
                        this.telemetryClient.TrackTrace("Sending user with no matched tags result");
                        await turnContext.SendActivityAsync(MessageFactory.Attachment(UnrecognizedInputCard.GetCard(text, Resource.NoMatchedTagsMessage)));
                    }
                }

                break;
            }
        }
Exemple #29
0
        /// <summary>
        /// Invoked when members other than this bot (like a user) are added to the conversation.
        /// </summary>
        /// <param name="membersAdded">List of members added.</param>
        /// <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 that represents the work queued to execute.</returns>
        protected override async Task OnMembersAddedAsync(IList <ChannelAccount> membersAdded, ITurnContext <IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
        {
            var activity = turnContext?.Activity;

            this.telemetryClient.TrackTrace($"conversationType: {activity.Conversation.ConversationType}, membersAdded: {activity.MembersAdded?.Count}, membersRemoved: {activity.MembersRemoved?.Count}");

            if (activity.MembersAdded?.Where(member => member.Id != activity.Recipient.Id).FirstOrDefault() != null)
            {
                this.telemetryClient.TrackEvent("Bot installed", new Dictionary <string, string>()
                {
                    { "User", activity.From.AadObjectId }
                });
                var userStateAccessors = this.userState.CreateProperty <UserData>(nameof(UserData));
                var userdata           = await userStateAccessors.GetAsync(turnContext, () => new UserData()).ConfigureAwait(false);

                if (userdata?.IsWelcomeCardSent == null || userdata?.IsWelcomeCardSent == false)
                {
                    userdata.IsWelcomeCardSent = true;
                    await userStateAccessors.SetAsync(turnContext, userdata).ConfigureAwait(false);

                    var welcomeCardImageUrl = new Uri(baseUri: new Uri(this.appBaseUri), relativeUri: "/images/welcome.jpg");
                    await turnContext.SendActivityAsync(activity : MessageFactory.Attachment(WelcomeCard.GetWelcomeCardAttachment(welcomeCardImageUrl)), cancellationToken).ConfigureAwait(false);
                }
            }
        }
 /// <summary>
 /// Add user membership to storage if bot is installed in Team scope.
 /// </summary>
 /// <param name="turnContext">Provides context for a turn in a bot.</param>
 /// <returns>A task that represents a response.</returns>
 private async Task SendWelcomeCardInChannelAsync(ITurnContext <IConversationUpdateActivity> turnContext)
 {
     this.logger.LogInformation($"Bot added in team {turnContext.Activity.Conversation.Id}");
     var userWelcomeCardAttachment = WelcomeCard.GetWelcomeCardAttachmentForTeam(this.options.Value.AppBaseUri, this.localizer);
     await turnContext.SendActivityAsync(MessageFactory.Attachment(userWelcomeCardAttachment));
 }