Exemple #1
0
 /// <summary>
 /// Initializes a new instance of the <see cref="MainDialog"/> class.
 /// </summary>
 /// <param name="configuration">Application configuration.</param>
 /// <param name="meetingProvider">Helper which exposes methods required for meeting creation process.</param>
 /// <param name="activityStorageProvider">Storage provider to perform fetch, insert and update operation on ActivityEntities table.</param>
 /// <param name="favoriteStorageProvider">Storage provider to perform fetch, insert, update and delete operation on UserFavorites table.</param>
 /// <param name="tokenHelper">Helper for generating and validating JWT token.</param>
 /// <param name="telemetryClient">Telemetry client for logging events and errors.</param>
 /// <param name="userConfigurationStorageProvider">Storage provider to perform fetch operation on UserConfiguration table.</param>
 /// <param name="meetingHelper">Helper class which exposes methods required for meeting creation.</param>
 public MainDialog(IConfiguration configuration, IMeetingProvider meetingProvider, IActivityStorageProvider activityStorageProvider, IFavoriteStorageProvider favoriteStorageProvider, ITokenHelper tokenHelper, TelemetryClient telemetryClient, IUserConfigurationStorageProvider userConfigurationStorageProvider, IMeetingHelper meetingHelper)
     : base(nameof(MainDialog), configuration["ConnectionName"], telemetryClient)
 {
     this.tokenHelper                      = tokenHelper;
     this.telemetryClient                  = telemetryClient;
     this.activityStorageProvider          = activityStorageProvider;
     this.favoriteStorageProvider          = favoriteStorageProvider;
     this.userConfigurationStorageProvider = userConfigurationStorageProvider;
     this.meetingProvider                  = meetingProvider;
     this.meetingHelper                    = meetingHelper;
     this.AddDialog(new OAuthPrompt(
                        nameof(OAuthPrompt),
                        new OAuthPromptSettings
     {
         ConnectionName = this.ConnectionName,
         Text           = Strings.SignInRequired,
         Title          = Strings.SignIn,
         Timeout        = 120000,
     }));
     this.AddDialog(
         new WaterfallDialog(
             nameof(WaterfallDialog),
             new WaterfallStep[] { this.PromptStepAsync, this.CommandStepAsync, this.ProcessStepAsync }));
     this.InitialDialogId = nameof(WaterfallDialog);
 }
        /// <summary>
        /// Get the results from Azure search service and populate the result (card + preview).
        /// </summary>
        /// <param name="languageCode">Current expert team's applicable language code.</param>
        /// <param name="query">Query which the user had typed in message extension search.</param>
        /// <param name="commandId">Command id to determine which tab in message extension has been invoked.</param>
        /// <param name="count">Count for pagination.</param>
        /// <param name="skip">Skip for pagination.</param>
        /// <param name="localTimestamp">Local timestamp of the user activity.</param>
        /// <param name="searchService">Search service.</param>
        /// <param name="knowledgeBaseSearchService">Knowledgebase search service.</param>
        /// <param name="activityStorageProvider">Activity storage provider.</param>
        /// <param name="appBaseUri">Application base uri.</param>
        /// <returns><see cref="Task"/> Returns MessagingExtensionResult which will be used for providing the card.</returns>
        public static async Task <MessagingExtensionResult> GetSearchResultAsync(
            string languageCode,
            string query,
            string commandId,
            int?count,
            int?skip,
            DateTimeOffset?localTimestamp,
            ISearchService searchService,
            IKnowledgeBaseSearchService knowledgeBaseSearchService,
            IActivityStorageProvider activityStorageProvider,
            string appBaseUri)
        {
            MessagingExtensionResult composeExtensionResult = new MessagingExtensionResult
            {
                Type             = "result",
                AttachmentLayout = AttachmentLayoutTypes.List,
                Attachments      = new List <MessagingExtensionAttachment>(),
            };

            IList <TicketEntity> searchServiceResults = new List <TicketEntity>();

            // commandId should be equal to Id mentioned in Manifest file under composeExtensions section.
            switch (commandId)
            {
            case RecentCommandId:
                searchServiceResults = await searchService.SearchTicketsAsync(TicketSearchScope.RecentTickets, languageCode, query, count, skip).ConfigureAwait(false);

                composeExtensionResult = GetMessagingExtensionResult(commandId, localTimestamp, searchServiceResults, appBaseUri);
                break;

            case OpenCommandId:
                searchServiceResults = await searchService.SearchTicketsAsync(TicketSearchScope.UnAnsweredTickets, languageCode, query, count, skip).ConfigureAwait(false);

                composeExtensionResult = GetMessagingExtensionResult(commandId, localTimestamp, searchServiceResults, appBaseUri);
                break;

            case AssignedCommandId:
                searchServiceResults = await searchService.SearchTicketsAsync(TicketSearchScope.AnsweredTickets, languageCode, query, count, skip).ConfigureAwait(false);

                composeExtensionResult = GetMessagingExtensionResult(commandId, localTimestamp, searchServiceResults, appBaseUri);
                break;

            case KnowledgebaseQuestionCommandId:
                var azureSearchEntities = await knowledgeBaseSearchService.GetAzureSearchEntitiesAsync(query).ConfigureAwait(false);

                if (azureSearchEntities.Any())
                {
                    var activitiesData = await activityStorageProvider.GetActivityEntitiesAsync().ConfigureAwait(false);

                    composeExtensionResult.Attachments = MessagingExtensionQnaCard.GetAllKbQuestionsCard(azureSearchEntities, activitiesData);
                }

                break;
            }

            return(composeExtensionResult);
        }
        public NotifyApiController(IBotFrameworkHttpAdapter adapter, IConfiguration configuration, ConcurrentDictionary <string, ConversationReference> conversationReferences, TelemetryClient telemetryClient, IActivityStorageProvider activityStorageProvider, IUserConfigurationStorageProvider userConfigurationStorageProvider, IFavoriteStorageProvider favoriteStorageProvider, IMeetingProvider meetingProvider, IMeetingHelper meetingHelper, ITokenHelper tokenHelper)
        {
            _adapter = adapter;
            _conversationReferences = conversationReferences;
            _appId = configuration["MicrosoftAppId"];

            this.telemetryClient                  = telemetryClient;
            this.activityStorageProvider          = activityStorageProvider;
            this.userConfigurationStorageProvider = userConfigurationStorageProvider;
            this.favoriteStorageProvider          = favoriteStorageProvider;
            this.meetingProvider                  = meetingProvider;
            this.meetingHelper = meetingHelper;
            this.tokenHelper   = tokenHelper;
        }
Exemple #4
0
 /// <summary>
 /// Initializes a new instance of the <see cref="BookAMeetingBot{T}"/> class.
 /// </summary>
 /// <param name="conversationState">Reads and writes conversation state for your bot to storage.</param>
 /// <param name="userState">Reads and writes user specific data to storage.</param>
 /// <param name="dialog">Dialog to be invoked.</param>
 /// <param name="tokenHelper">Generating and validating JWT token.</param>
 /// <param name="activityStorageProvider">Storage provider to perform insert, update and delete operation on ActivityEntities table.</param>
 /// <param name="favoriteStorageProvider">Storage provider to perform insert, update and delete operation on UserFavorites table.</param>
 /// <param name="meetingProvider">Provider for exposing methods required to perform meeting creation.</param>
 /// <param name="telemetryClient">Telemetry client for logging events and errors.</param>
 /// <param name="userConfigurationStorageProvider">Storage provider to perform insert and update operation on UserConfiguration table.</param>
 /// <param name="appBaseUri">Application base URL.</param>
 /// <param name="instrumentationKey">Instrumentation key for application insights logging.</param>
 /// <param name="tenantId">Valid tenant id for which bot will operate.</param>
 public BookAMeetingBot(ConversationState conversationState, UserState userState, T dialog, ITokenHelper tokenHelper, IActivityStorageProvider activityStorageProvider, IFavoriteStorageProvider favoriteStorageProvider, IMeetingProvider meetingProvider, TelemetryClient telemetryClient, IUserConfigurationStorageProvider userConfigurationStorageProvider, string appBaseUri, string instrumentationKey, string tenantId)
 {
     this.conversationState                = conversationState;
     this.userState                        = userState;
     this.dialog                           = dialog;
     this.tokenHelper                      = tokenHelper;
     this.activityStorageProvider          = activityStorageProvider;
     this.favoriteStorageProvider          = favoriteStorageProvider;
     this.meetingProvider                  = meetingProvider;
     this.telemetryClient                  = telemetryClient;
     this.userConfigurationStorageProvider = userConfigurationStorageProvider;
     this.appBaseUri                       = appBaseUri;
     this.instrumentationKey               = instrumentationKey;
     this.tenantId                         = tenantId;
 }
        /// <summary>
        /// Initializes a new instance of the <see cref="QnAPairServiceFacade"/> class.
        /// </summary>
        /// <param name="configurationProvider">Configuration Provider.</param>
        /// <param name="activityStorageProvider">Activity storage provider.</param>
        /// <param name="qnaServiceProvider">QnA service provider.</param>
        /// <param name="botSettings">Represents a set of key/value application configuration properties for FaqPlusPlus bot.</param>ram>
        /// <param name="logger">Instance to send logs to the Application Insights service.</param>
        public QnAPairServiceFacade(
            Common.Providers.IConfigurationDataProvider configurationProvider,
            IQnaServiceProvider qnaServiceProvider,
            IActivityStorageProvider activityStorageProvider,
            IOptionsMonitor<BotSettings> botSettings,
            ILogger<QnAPairServiceFacade> logger)
        {
            this.configurationProvider = configurationProvider ?? throw new ArgumentNullException(nameof(configurationProvider));
            this.qnaServiceProvider = qnaServiceProvider ?? throw new ArgumentNullException(nameof(qnaServiceProvider));
            this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
            this.activityStorageProvider = activityStorageProvider ?? throw new ArgumentNullException(nameof(activityStorageProvider));
            if (botSettings == null)
            {
                throw new ArgumentNullException(nameof(botSettings));
            }

            this.options = botSettings.CurrentValue;
            this.appBaseUri = this.options.AppBaseUri;
        }
Exemple #6
0
        /// <summary>
        /// Initializes a new instance of the <see cref="MessagingExtensionActivity"/> class.
        /// </summary>
        /// <param name="configurationProvider">Configuration Provider.</param>
        /// <param name="activityStorageProvider">Activity storage provider.</param>
        /// <param name="qnaServiceProvider">Question and answer maker service provider.</param>
        /// <param name="searchService">SearchService dependency injection.</param>
        /// <param name="botAdapter">Bot adapter dependency injection.</param>
        /// <param name="memoryCache">IMemoryCache dependency injection.</param>
        /// <param name="knowledgeBaseSearchService">KnowledgeBaseSearchService dependency injection.</param>
        /// <param name="optionsAccessor">A set of key/value application configuration properties for FaqPlusPlus bot.</param>
        /// <param name="logger">Instance to send logs to the Application Insights service.</param>
        /// <param name="ticketsProvider">Instance of Ticket provider helps in fetching and storing information in storage table.</param>
        /// <param name="notificationService">Notifies in expert's Team chat.</param>
        public MessagingExtensionActivity(
            Common.Providers.IConfigurationDataProvider configurationProvider,
            IQnaServiceProvider qnaServiceProvider,
            IActivityStorageProvider activityStorageProvider,
            ISearchService searchService,
            BotFrameworkAdapter botAdapter,
            IMemoryCache memoryCache,
            IKnowledgeBaseSearchService knowledgeBaseSearchService,
            IOptionsMonitor <BotSettings> optionsAccessor,
            ILogger <MessagingExtensionActivity> logger,
            ITicketsProvider ticketsProvider,
            INotificationService notificationService)
        {
            this.configurationProvider   = configurationProvider ?? throw new ArgumentNullException(nameof(configurationProvider));
            this.qnaServiceProvider      = qnaServiceProvider ?? throw new ArgumentNullException(nameof(qnaServiceProvider));
            this.activityStorageProvider = activityStorageProvider ?? throw new ArgumentNullException(nameof(activityStorageProvider));
            this.searchService           = searchService ?? throw new ArgumentNullException(nameof(searchService));
            this.botAdapter  = botAdapter ?? throw new ArgumentNullException(nameof(botAdapter));
            this.accessCache = memoryCache ?? throw new ArgumentNullException(nameof(memoryCache));
            this.logger      = logger ?? throw new ArgumentNullException(nameof(logger));
            this.knowledgeBaseSearchService = knowledgeBaseSearchService ?? throw new ArgumentNullException(nameof(knowledgeBaseSearchService));
            this.ticketsProvider            = ticketsProvider ?? throw new ArgumentNullException(nameof(ticketsProvider));
            this.notificationService        = notificationService ?? throw new ArgumentNullException(nameof(notificationService));
            if (optionsAccessor == null)
            {
                throw new ArgumentNullException(nameof(optionsAccessor));
            }

            this.options    = optionsAccessor.CurrentValue;
            this.appId      = this.options.ExpertAppId;
            this.appBaseUri = this.options.AppBaseUri;
            this.accessCacheExpiryInDays = this.options.AccessCacheExpiryInDays;
            if (this.accessCacheExpiryInDays <= 0)
            {
                this.accessCacheExpiryInDays = DefaultAccessCacheExpiryInDays;
                this.logger.LogInformation($"Configuration option is not present or out of range for AccessCacheExpiryInDays and the default value is set to: {this.accessCacheExpiryInDays}", SeverityLevel.Information);
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="BotCommandResolver"/> class.
        /// </summary>
        /// <param name="configurationProvider">Configuration Provider.</param>
        /// <param name="activityStorageProvider">Activity storage provider.</param>
        /// <param name="qnaServiceProvider">QnA service provider.</param>
        /// <param name="logger">Instance to send logs to the Application Insights service.</param>
        /// <param name="qnaPairService">Instance of QnA pair service class to call add/update/get QnA pair.</param>
        /// <param name="botSettings">Represents a set of key/value application configuration properties for FaqPlusPlus bot.</param>
        /// <param name="conversationService">Conversation service to send adaptive card in personal and teams chat.</param>
        public BotCommandResolver(
            Common.Providers.IConfigurationDataProvider configurationProvider,
            IQnaServiceProvider qnaServiceProvider,
            IActivityStorageProvider activityStorageProvider,
            IQnAPairServiceFacade qnaPairService,
            IOptionsMonitor <BotSettings> botSettings,
            ILogger <BotCommandResolver> logger,
            IConversationService conversationService)
        {
            this.configurationProvider   = configurationProvider ?? throw new ArgumentNullException(nameof(configurationProvider));
            this.qnaServiceProvider      = qnaServiceProvider ?? throw new ArgumentNullException(nameof(qnaServiceProvider));
            this.activityStorageProvider = activityStorageProvider ?? throw new ArgumentNullException(nameof(activityStorageProvider));
            this.logger              = logger ?? throw new ArgumentNullException(nameof(logger));
            this.qnaPairService      = qnaPairService ?? throw new ArgumentNullException(nameof(qnaPairService));
            this.conversationService = conversationService ?? throw new ArgumentNullException(nameof(conversationService));
            if (botSettings == null)
            {
                throw new ArgumentNullException(nameof(botSettings));
            }

            var options = botSettings.CurrentValue;

            this.appBaseUri = options.AppBaseUri;
        }
Exemple #8
0
        /// <summary>
        /// Delete qna pair.
        /// </summary>
        /// <param name="turnContext">Turn context.</param>
        /// <param name="qnaServiceProvider">Qna Service provider.</param>
        /// <param name="activityStorageProvider">Activity Storage Provider.</param>
        /// <param name="logger">Logger.</param>
        /// <param name="cancellationToken">Cancellation Token.</param>
        /// <returns>A task that represents the work queued to execute.</returns>
        public static async Task DeleteQnaPair(
            ITurnContext <IMessageActivity> turnContext,
            IQnaServiceProvider qnaServiceProvider,
            IActivityStorageProvider activityStorageProvider,
            ILogger logger,
            CancellationToken cancellationToken)
        {
            QnASearchResult searchResult;
            Attachment      attachment;

            var activity      = (Activity)turnContext.Activity;
            var activityValue = ((JObject)activity.Value).ToObject <AdaptiveSubmitActionData>();
            QnASearchResultList qnaAnswerResponse = await qnaServiceProvider.GenerateAnswerAsync(activityValue?.OriginalQuestion, isTestKnowledgeBase : false).ConfigureAwait(false);

            bool isSameQuestion = false;

            searchResult = qnaAnswerResponse.Answers.First();

            // Check if question exist in the knowledgebase.
            if (searchResult != null && searchResult.Questions.Count > 0)
            {
                // Check if the deleted question & result returned from the knowledgebase are same.
                isSameQuestion = searchResult.Questions.First().ToUpperInvariant() == activityValue?.OriginalQuestion.ToUpperInvariant().Trim();
            }

            // Delete the QnA pair if question exist in the knowledgebase & exactly the same question user wants to delete.
            if (searchResult.Id != -1 && isSameQuestion)
            {
                await qnaServiceProvider.DeleteQnaAsync(searchResult.Id.Value).ConfigureAwait(false);

                logger.LogInformation($"Question deleted by: {activity.Conversation.AadObjectId}");
                attachment = MessagingExtensionQnaCard.DeletedEntry(activityValue?.OriginalQuestion, searchResult.Answer, activity.From.Name, activityValue?.UpdateHistoryData);
                ActivityEntity activityEntity = new ActivityEntity {
                    ActivityReferenceId = searchResult.Metadata.FirstOrDefault(x => x.Name == Constants.MetadataActivityReferenceId)?.Value
                };

                bool operationStatus = await activityStorageProvider.DeleteActivityEntityAsync(activityEntity).ConfigureAwait(false);

                if (!operationStatus)
                {
                    logger.LogInformation($"Unable to delete the activity data from table storage.");
                }

                var updateCardActivity = new Activity(ActivityTypes.Message)
                {
                    Id           = turnContext.Activity.ReplyToId,
                    Conversation = turnContext.Activity.Conversation,
                    Attachments  = new List <Attachment> {
                        attachment
                    },
                };

                // Send deleted question and answer card as response.
                await turnContext.UpdateActivityAsync(updateCardActivity, cancellationToken).ConfigureAwait(false);
            }
            else
            {
                // check if question and answer is present in unpublished version.
                qnaAnswerResponse = await qnaServiceProvider.GenerateAnswerAsync(activityValue?.OriginalQuestion, isTestKnowledgeBase : true).ConfigureAwait(false);

                if (qnaAnswerResponse?.Answers?.First().Id != -1)
                {
                    await turnContext.SendActivityAsync(MessageFactory.Text(string.Format(CultureInfo.InvariantCulture, Strings.WaitMessage, activityValue?.OriginalQuestion))).ConfigureAwait(false);
                }
            }

            return;
        }