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