/// <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)); }
/// <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>(); }
/// <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; }
/// <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; }
/// <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; }
/// <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> /// 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; }