Пример #1
0
        /// <summary>
        /// Initializes a new instance of the <see cref="SearchService"/> class.
        /// </summary>
        /// <param name="optionsAccessor">A set of key/value application configuration properties.</param>
        /// <param name="ticketProvider"> TicketsProvider provided by dependency injection.</param>
        /// <param name="logger">Instance to send logs to the Application Insights service.</param>
        public SearchService(
            IOptionsMonitor <KnowledgeBaseSettings> optionsAccessor,
            ITicketsProvider ticketProvider,
            ILogger <SearchService> logger)
        {
            this.options = optionsAccessor.CurrentValue;
            var    searchDnsSuffix    = this.options.IsGCCHighDeployment == true ? "search.azure.us" : "search.windows.net";
            string searchServiceValue = this.options.SearchServiceName;

            this.searchServiceClient = new SearchServiceClient(
                searchServiceValue,
                new SearchCredentials(this.options.SearchServiceAdminApiKey));
            this.searchServiceClient.SearchDnsSuffix = searchDnsSuffix;

            this.searchIndexClient = new SearchIndexClient(
                searchServiceValue,
                TicketsIndexName,
                new SearchCredentials(this.options.SearchServiceQueryApiKey));
            this.searchIndexClient.SearchDnsSuffix = searchDnsSuffix;
            this.searchIndexingIntervalInMinutes   = Convert.ToInt32(this.options.SearchIndexingIntervalInMinutes);

            this.initializeTask = new Lazy <Task>(() => this.InitializeAsync(this.options.StorageConnectionString));
            this.ticketProvider = ticketProvider;
            this.logger         = logger;
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="MessagingExtension"/> class.
        /// </summary>
        /// <param name="searchService">searchService DI.</param>
        /// <param name="telemetryClient">telemetryClient DI.</param>
        /// <param name="configuration">configuration DI.</param>
        /// <param name="adapter">adapter DI.</param>
        /// <param name="configurationProvider">configurationProvider DI.</param>
        /// <param name="memoryCache">IMemoryCache DI.</param>
        /// <param name="ticketsProvider">ITicketsProvider DI.</param>
        public MessagingExtension(
            ISearchService searchService,
            TelemetryClient telemetryClient,
            IConfiguration configuration,
            IBotFrameworkHttpAdapter adapter,
            Common.Providers.IConfigurationProvider configurationProvider,
            IMemoryCache memoryCache,
            ITicketsProvider ticketsProvider)
        {
            this.searchService         = searchService;
            this.telemetryClient       = telemetryClient;
            this.configuration         = configuration;
            this.adapter               = adapter;
            this.configurationProvider = configurationProvider;
            this.appID           = this.configuration["MicrosoftAppId"];
            this.botAdapter      = (BotFrameworkAdapter)this.adapter;
            this.accessCache     = memoryCache;
            this.ticketsProvider = ticketsProvider;

            this.accessCacheExpiryInDays = Convert.ToInt32(this.configuration["AccessCacheExpiryInDays"]);
            if (this.accessCacheExpiryInDays <= 0)
            {
                this.accessCacheExpiryInDays = DefaultAccessCacheExpiryInDays;
            }

            // Ensure that the tables for the tickets are created by running a dummy query against the provider
            this.initializeTicketsProviderTask = new Lazy <Task>(() => this.ticketsProvider.GetTicketAsync(string.Empty));
        }
        /// <summary>
        /// Create a new ticket from the input.
        /// </summary>
        /// <param name="currentLanguageCode">Current applicable language code.</param>
        /// <param name="message">A message in a conversation.</param>
        /// <param name="data">Represents the submit data associated with the Ask An Expert card.</param>
        /// <param name="member">Teams channel account detailing user Azure Active Directory details.</param>
        /// <param name="ticketsProvider">Tickets Provider.</param>
        /// <returns>TicketEntity object.</returns>
        private static async Task <TicketEntity> CreateTicketAsync(
            string currentLanguageCode,
            IMessageActivity message,
            AskAnExpertCardPayload data,
            TeamsChannelAccount member,
            ITicketsProvider ticketsProvider)
        {
            IList <TicketEntity> ticketList = await ticketsProvider.GetTicketCountAsync();

            TicketEntity ticketEntity = new TicketEntity
            {
                LanguageCode  = currentLanguageCode,
                TicketId      = (10000 + ticketList.Count).ToString(), // Guid.NewGuid().ToString(),
                Status        = (int)TicketState.UnAnswered,
                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,
                KnowledgeBaseQuestion = data.KnowledgeBaseQuestion,
            };

            await ticketsProvider.UpsertTicketAsync(ticketEntity).ConfigureAwait(false);

            return(ticketEntity);
        }
        /// <summary>
        /// Helps to get the expert submit card.
        /// </summary>
        /// <param name="currentLanguageCode">Current applicable language code.</param>
        /// <param name="message">A message in a conversation.</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>
        /// <param name="ticketsProvider">Tickets Provider.</param>
        /// <returns>A task that represents the work queued to execute.</returns>
        public static async Task <TicketEntity> AskAnExpertSubmitText(
            string currentLanguageCode,
            IMessageActivity message,
            ITurnContext <IMessageActivity> turnContext,
            CancellationToken cancellationToken,
            ITicketsProvider ticketsProvider)
        {
            var askAnExpertSubmitTextPayload = ((JObject)message.Value).ToObject <AskAnExpertCardPayload>();

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

                return(null);
            }

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

            return(await CreateTicketAsync(currentLanguageCode, message, askAnExpertSubmitTextPayload, userDetails, ticketsProvider).ConfigureAwait(false));
        }
        /// <summary>
        /// Helps to get the expert submit card.
        /// </summary>
        /// <param name="message">A message in a conversation.</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>
        /// <param name="ticketsProvider">Tickets Provider.</param>
        /// <returns>A task that represents the work queued to execute.</returns>
        public static async Task <TicketEntity> AskAnExpertSubmitText(
            IMessageActivity message,
            ITurnContext <IMessageActivity> turnContext,
            CancellationToken cancellationToken,
            ITicketsProvider ticketsProvider)
        {
            var askAnExpertSubmitTextPayload = ((JObject)message.Value).ToObject <AskAnExpertCardPayload>();

            // Validate required fields.
            if (string.IsNullOrWhiteSpace(askAnExpertSubmitTextPayload?.Title) || askAnExpertSubmitTextPayload.Description.Length > 500)
            {
                var updateCardActivity = new Activity(ActivityTypes.Message)
                {
                    Id           = turnContext.Activity.ReplyToId,
                    Conversation = turnContext.Activity.Conversation,
                    Attachments  = new List <Attachment> {
                        AskAnExpertCard.GetCard(askAnExpertSubmitTextPayload)
                    },
                };
                try
                {
                    await turnContext.UpdateActivityAsync(updateCardActivity, cancellationToken).ConfigureAwait(false);
                }
                catch (ErrorResponseException)
                {
                    await turnContext.SendActivityAsync(Strings.InputTooLongWarning).ConfigureAwait(false);
                }

                return(null);
            }

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

            return(await CreateTicketAsync(message, askAnExpertSubmitTextPayload, userDetails, ticketsProvider).ConfigureAwait(false));
        }
        /// <summary>
        /// Create a new ticket from the input.
        /// </summary>
        /// <param name="message">A message in a conversation.</param>
        /// <param name="data">Represents the submit data associated with the Ask An Expert card.</param>
        /// <param name="member">Teams channel account detailing user Azure Active Directory details.</param>
        /// <param name="ticketsProvider">Tickets Provider.</param>
        /// <returns>TicketEntity object.</returns>
        private static async Task <TicketEntity> CreateTicketAsync(
            IMessageActivity message,
            AskAnExpertCardPayload data,
            TeamsChannelAccount member,
            ITicketsProvider ticketsProvider)
        {
            TicketEntity ticketEntity = new TicketEntity
            {
                TicketId      = Guid.NewGuid().ToString(),
                Status        = (int)TicketState.UnAssigned,
                DateCreated   = DateTime.Now,
                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,
                Subject             = data.Project,
            };

            await ticketsProvider.UpsertTicketAsync(ticketEntity).ConfigureAwait(false);

            return(ticketEntity);
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="ConversationService"/> class.
 /// </summary>
 /// <param name="configurationProvider">Configuration Provider.</param>
 /// <param name="logger">Instance to send logs to the Application Insights service.</param>
 /// <param name="qnaPairServiceFacade">Instance of QnA pair service class to call add/update/get QnA pair.</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 ConversationService(
     Common.Providers.IConfigurationDataProvider configurationProvider,
     IQnAPairServiceFacade qnaPairServiceFacade,
     ITicketsProvider ticketsProvider,
     INotificationService notificationService,
     ILogger <ConversationService> logger)
 {
     this.configurationProvider = configurationProvider ?? throw new ArgumentNullException(nameof(configurationProvider));
     this.ticketsProvider       = ticketsProvider ?? throw new ArgumentNullException(nameof(ticketsProvider));
     this.notificationService   = notificationService ?? throw new ArgumentNullException(nameof(notificationService));
     this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
     this.qnaPairServiceFacade = qnaPairServiceFacade ?? throw new ArgumentNullException(nameof(qnaPairServiceFacade));
 }
Пример #8
0
 /// <summary>
 /// Initializes a new instance of the <see cref="FaqPlusPlusBot"/> class.
 /// </summary>
 /// <param name="configurationProvider">Configuration Provider.</param>
 /// <param name="microsoftAppCredentials">Microsoft app credentials to use.</param>
 /// <param name="ticketsProvider">Tickets Provider.</param>
 /// <param name="qnaServiceProvider">Question and answer maker service provider.</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="smeBot">Instance of SME bot to handle actions on old cards sent with old bot version.</param>
 public UserActivityHandler(
     IConfigurationDataProvider configurationProvider,
     MicrosoftAppCredentials microsoftAppCredentials,
     ITicketsProvider ticketsProvider,
     IQnaServiceProvider qnaServiceProvider,
     IOptionsMonitor <BotSettings> optionsAccessor,
     ILogger <UserActivityHandler> logger,
     SmeActivityHandler smeBot)
     : base(configurationProvider, microsoftAppCredentials, ticketsProvider, qnaServiceProvider, optionsAccessor.CurrentValue, logger)
 {
     this.smeBot = smeBot;
     this.expertTeamNameCache = new MemoryCacheWithPolicy <string>();
 }
Пример #9
0
 /// <summary>
 /// Initializes a new instance of the <see cref="TicketsController"/> class.
 /// </summary>
 /// <param name="telemetryClient">Singleton TelemetryClient instance used to send telemetry to Azure application insights.</param>
 /// <param name="ticketsProvider">Tickets provider</param>
 /// <param name="configurationProvider">Configuration Data provider</param>
 /// <param name="httpContextAccessor">http context</param>
 /// <param name="adapter">Bot adapter</param>
 /// <param name="configuration">Configuration</param>
 public TicketsController(
     TelemetryClient telemetryClient,
     ITicketsProvider ticketsProvider,
     IConfigurationDataProvider configurationProvider,
     IHttpContextAccessor httpContextAccessor,
     IBotFrameworkHttpAdapter adapter,
     IConfiguration configuration)
 {
     this.telemetryClient       = telemetryClient;
     this.ticketsProvider       = ticketsProvider;
     this.httpContextAccessor   = httpContextAccessor;
     this.adapter               = adapter;
     this.configuration         = configuration;
     this.configurationProvider = configurationProvider;
 }
Пример #10
0
 /// <summary>
 /// Initializes a new instance of the <see cref="CommonTeamsActivityHandler"/> class.
 /// </summary>
 /// <param name="configurationProvider">Configuration Provider.</param>
 /// <param name="microsoftAppCredentials">Microsoft app credentials to use.</param>
 /// <param name="ticketsProvider">Tickets Provider.</param>
 /// <param name="qnaServiceProvider">Question and answer maker service provider.</param>
 /// <param name="options">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>
 public CommonTeamsActivityHandler(
     IConfigurationDataProvider configurationProvider,
     MicrosoftAppCredentials microsoftAppCredentials,
     ITicketsProvider ticketsProvider,
     IQnaServiceProvider qnaServiceProvider,
     BotSettings options,
     ILogger logger)
 {
     this.logger  = logger;
     this.options = options;
     this.configurationProvider   = configurationProvider;
     this.microsoftAppCredentials = microsoftAppCredentials;
     this.ticketsProvider         = ticketsProvider;
     this.qnaServiceProvider      = qnaServiceProvider;
     this.appBaseUri = this.options.AppBaseUri;
 }
Пример #11
0
 /// <summary>
 /// Initializes a new instance of the <see cref="FaqPlusPlusBot"/> class.
 /// </summary>
 /// <param name="telemetryClient"> Telemetry Client.</param>
 /// <param name="configurationProvider">Configuration Provider.</param>
 /// <param name="qnaMakerFactory">QnAMaker factory instance</param>
 /// <param name="messageExtension">Messaging extension instance</param>
 /// <param name="appBaseUri">Base URI at which the app is served</param>
 /// <param name="microsoftAppCredentials">Microsoft app credentials to use</param>
 /// <param name="ticketsProvider">The tickets provider.</param>
 public FaqPlusPlusBot(
     TelemetryClient telemetryClient,
     IConfigurationProvider configurationProvider,
     IQnAMakerFactory qnaMakerFactory,
     MessagingExtension messageExtension,
     string appBaseUri,
     MicrosoftAppCredentials microsoftAppCredentials,
     ITicketsProvider ticketsProvider)
 {
     this.telemetryClient         = telemetryClient;
     this.configurationProvider   = configurationProvider;
     this.qnaMakerFactory         = qnaMakerFactory;
     this.messageExtension        = messageExtension;
     this.appBaseUri              = appBaseUri;
     this.microsoftAppCredentials = microsoftAppCredentials;
     this.ticketsProvider         = ticketsProvider;
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="AskHRBot"/> class.
 /// </summary>
 /// <param name="telemetryClient"> Telemetry Client.</param>
 /// <param name="configurationProvider">Configuration Provider.</param>
 /// <param name="helpDataProvider">Help data provider instance.</param>
 /// <param name="qnaMakerFactory">QnAMaker factory instance</param>
 /// <param name="messageExtension">Messaging extension instance</param>
 /// <param name="appBaseUri">Base URI at which the app is served</param>
 /// <param name="expectedTenantId">Tenant id</param>
 /// <param name="microsoftAppCredentials">Microsoft app credentials to use</param>
 /// <param name="ticketsProvider">The tickets provider.</param>
 public AskHRBot(
     TelemetryClient telemetryClient,
     IConfigurationProvider configurationProvider,
     IHelpDataProvider helpDataProvider,
     IQnAMakerFactory qnaMakerFactory,
     MessagingExtension messageExtension,
     string appBaseUri,
     string expectedTenantId,
     MicrosoftAppCredentials microsoftAppCredentials,
     ITicketsProvider ticketsProvider)
 {
     this.telemetryClient         = telemetryClient;
     this.configurationProvider   = configurationProvider;
     this.helpDataProvider        = helpDataProvider;
     this.qnaMakerFactory         = qnaMakerFactory;
     this.messageExtension        = messageExtension;
     this.appBaseUri              = appBaseUri;
     this.microsoftAppCredentials = microsoftAppCredentials;
     this.ticketsProvider         = ticketsProvider;
     this.expectedTenantId        = expectedTenantId;
 }
Пример #13
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);
            }
        }
Пример #14
0
        /// <summary>
        /// Create a new ticket from the input.
        /// </summary>
        /// <param name="message">A message in a conversation.</param>
        /// <param name="data">Represents the submit data associated with the Ask An Expert card.</param>
        /// <param name="member">Teams channel account detailing user Azure Active Directory details.</param>
        /// <param name="ticketsProvider">Tickets Provider.</param>
        /// <returns>TicketEntity object.</returns>
        private static async Task <TicketEntity> CreateTicketAsync(
            IMessageActivity message,
            AskAnExpertCardPayload data,
            TeamsChannelAccount member,
            ITicketsProvider ticketsProvider)
        {
            //AtBot Conceirge Support

            //Boolean as to whether this is the Teams channel.  If not, we assume bot is being interfaced as an AtBot Agent
            bool isTeamsChannel = message.ChannelId == "msteams";

            ChannelAccount agentUser = message.From;

            //When creating a ticket from the bot acting as an agent, the requestor information will be limited to the UPN.  Additional graph calls
            //will be needed to pull AAD GUID and display name.

            TicketEntity ticketEntity = new TicketEntity
            {
                TicketId      = Guid.NewGuid().ToString(),
                Status        = (int)TicketState.Open,
                DateCreated   = DateTime.UtcNow,
                Title         = data.Title,
                Description   = data.Description,
                RequesterName = isTeamsChannel ? member.Name : agentUser.Name,
                RequesterUserPrincipalName = isTeamsChannel ? member.UserPrincipalName : agentUser.Name,
                RequesterGivenName         = isTeamsChannel ? member.GivenName : agentUser.Name,
                RequesterConversationId    = message.Conversation.Id,
                LastModifiedByName         = message.From.Name,
                LastModifiedByObjectId     = message.From.AadObjectId,
                UserQuestion        = data.UserQuestion,
                KnowledgeBaseAnswer = data.KnowledgeBaseAnswer,
            };

            await ticketsProvider.UpsertTicketAsync(ticketEntity).ConfigureAwait(false);

            return(ticketEntity);
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="ContentController"/> class.
 /// </summary>
 /// <param name="ticketsProvider">Tickets Provider.</param>
 public ContentController(ITicketsProvider ticketsProvider)
 {
     this.ticketsProvider = ticketsProvider;
 }