public static async Task <int?> GetResearchVsoIdFromVso(string channelId, string uniqueName) { var properties = new Dictionary <string, string> { { "class", "ConversationHelpers" }, { "function", "GetResearchVsoIdFromVso" }, { "channelId", channelId }, { "from", uniqueName } }; int?vsoId = null; try { var workItems = await VsoHelper.GetWorkItemsForUser( VsoHelper.ResearchTaskType, channelId, //channelId == ActivityHelper.SmsChannelId ? PromptPhoneNumber.FormatPhoneNumber(uniqueName) : uniqueName); uniqueName); if (workItems != null) { vsoId = workItems.Select(wi => wi.Id).FirstOrDefault(); } } catch (System.Exception e) { WebApiConfig.TelemetryClient.TrackException(e, properties); } properties.Add("vsoId", vsoId != null ? vsoId.ToString() : "not set"); WebApiConfig.TelemetryClient.TrackEvent("GetResearchVsoIdFromVso", properties); return(vsoId); }
public async Task StartAsync(IDialogContext context) { if (context == null) { throw new ArgumentNullException(nameof(context)); } var activity = (IMessageActivity)context.Activity; EndUserAndAgentConversationMappingState mappingState = await VsoHelper.GetStateFromVsoGivenAgentConversationId(activity.Conversation.Id); if (ActivityHelper.HasAttachment(activity)) { await context.PostWithRetryAsync( $"Sending file attachments to user is not supported. " + $"Please send it via SharePoint > Share > Email. Email is in VSO ticket"); } else { await ActivityHelper.SendMessageToUserEx((IMessageActivity)context.Activity, mappingState.EndUserName, mappingState.EndUserId, activity.Text.Replace(DialogMatches.ReplyToUser + " ", ""), mappingState.VsoId); } await OnlineStatus.SetMemberActive( context.Activity.From.Name, context.Activity.From.Id, OnlineStatus.AgentMemberType); context.Done <object>(null); }
public bool Vote(string key, int item) { try { if (key != null && new[] { 1, 2 }.Contains(item)) { var itemNumber = item == 1 ? ItemNumber.Item1 : ItemNumber.Item2; var submission = _submissionEntityService.IncrementVote(itemNumber, key); // tell everyone the updated count Clients.Group(key).UpdateVotes(submission.Item1Votes, submission.Item2Votes); // is the updated count more than the threshold? If so, create a work item if (!submission.WorkItemCreated && (submission.Item1Votes >= submission.Threshold || submission.Item2Votes >= submission.Threshold)) { VsoHelper.CreateWorkItem(submission, submission.OAuthRefreshToken); _submissionEntityService.SetCreatedWorkItemFlag(key); } return(true); } return(false); } catch (Exception) { return(false); } }
public static async Task <int?> GetVsoIdFromConversation(IDialogContext endUserDialogContext) { int?vsoId = null; try { string status = "not set"; if (endUserDialogContext.ConversationData.TryGetValue("VsoId", out string vsoIdFromConversation)) { int convertedVsoId = Convert.ToInt32(vsoIdFromConversation); status = await VsoHelper.GetProjectStatus(convertedVsoId); if (!status.ToLower().Contains("closed")) { vsoId = convertedVsoId; } } WebApiConfig.TelemetryClient.TrackEvent("GetVsoIdFromConversation", new Dictionary <string, string> { { "class", "HelloDialog" }, { "function", "GetVsoIdFromConversation" }, { "from", endUserDialogContext.Activity.From.Name }, { "vsoId", vsoId != null ? vsoId.ToString() : "not set" }, { "vsoIdStatus", status }, }); } catch (VssServiceException e) { if (e.Message.Contains("does not exist")) { // we might have deleted this item. WebApiConfig.TelemetryClient.TrackException(e, new Dictionary <string, string> { { "class", "HelloDialog" }, { "function", "GetVsoIdFromConversation" }, { "dialog", "HelloDialog" }, { "from", endUserDialogContext.Activity.From.Name } }); return(null); } } catch (System.Exception e) { WebApiConfig.TelemetryClient.TrackException(e, new Dictionary <string, string> { { "class", "HelloDialog" }, { "function", "GetVsoIdFromConversation" }, { "dialog", "HelloDialog" }, { "from", endUserDialogContext.Activity.From.Name } }); throw; } return(vsoId); }
public static async Task <bool> RelayMessageToAgentIfThereIsAnOpenResearchProject(IDialogContext context) { int?vsoId = await GetResearchVsoIdFromVso(context.Activity.ChannelId, context.Activity.From.Name); if (vsoId == null) { return(false); } var userProfile = await UserProfileHelper.GetUserProfile(context); context.UserData.SetValue(UserProfileHelper.UserProfileKey, userProfile); string agentConversationId = await VsoHelper.GetAgentConversationIdForVso((int)vsoId); if (string.IsNullOrEmpty(agentConversationId)) { return(false); } await context.PostWithRetryAsync("OK, I'll add that to the project."); //await SendAutoReplyIfNeeded(context, vsoId); IMessageActivity messageActivity = (IMessageActivity)context.Activity; if (ActivityHelper.HasAttachment(messageActivity)) { await context.PostWithRetryAsync( $"Sending file attachments is not supported. " + $"Please send it via an accessible link within Microsoft"); } else { await ActivityHelper.SendMessageToAgentAsReplyToConversationInAgentsChannel( messageActivity, messageActivity.Text, agentConversationId, (int)vsoId); } await OnlineStatus.SetMemberActive(context.Activity.From.Name, context.Activity.From.Id, OnlineStatus.EndUserMemberType); return(true); }
private async Task CloseProject(IDialogContext context, IMessageActivity message) { if (TryParseVsoId(message.Text, out int vsoId)) { await context.PostWithRetryAsync($"Sure I can help close project #{vsoId}"); await VsoHelper.CloseProject(vsoId); await context.PostWithRetryAsync($"{vsoId} project is now closed."); await PromptForCreatingNewProjectAfterClosingExistingOne(context); } else { await context.PostWithRetryAsync("Sorry, I ran into an error"); context.Call(new UserHelpDialog(), EndDialog); } }
public AuthModule() { // callback URL from the VSO OAuth page Get["/oauth"] = _ => { var oauthData = this.Bind <OAuthPostData>(); TokenModel tokenModel = VsoHelper.DoAuthenticationPost(oauthData.Code, false); return(View["TokenView", tokenModel]); }; // URL to renew or initialize OAuth Get["/oauth/init"] = _ => { TokenModel token = null; if (Request.Cookies.ContainsKey("VsoToken")) { var code = Request.Cookies["VsoToken"]; token = JsonConvert.DeserializeObject <TokenModel>(code); } else { Response.AsRedirect("/oauth/request"); } TokenModel tokenModel = VsoHelper.DoAuthenticationPost(token.refreshToken, true); if (!string.IsNullOrEmpty(tokenModel.Error)) { return(View["TokenView"]); } if (HttpContext.Current.Request.UrlReferrer != null) { return(Response.AsRedirect(HttpContext.Current.Request.UrlReferrer.ToString())); } else { return(Response.AsRedirect("/")); } }; // Start the OAuth request process Get["/oauth/request"] = _ => Response.AsRedirect(VsoHelper.GenerateAuthorizeUrl()); }
public IndexModule() { // Root Get["/"] = _ => { // todo: redirect to init if we already have a cookie - refresh the token?? var newModel = new SubmissionModel { Threshold = 5 }; return(View["Index.cshtml", newModel]); }; // About Get["/about"] = _ => View["About"]; Post["/submit"] = _ => { var saveData = this.Bind <SubmissionModel>(); var service = new SubmissionEntityService(); var urlKey = GetRandomString(4); // some random key if (VsoHelper.GetActiveUser() != null) { var savedObject = service.Add(urlKey, VsoHelper.GetActiveUser().refreshToken, saveData); } else { var savedObject = service.Add(urlKey, "", saveData); } return(Response.AsRedirect("/v/" + urlKey)); }; Post["/refresh"] = _ => { var saveData = this.Bind <SubmissionModel>(); return(View["Index", saveData]); }; }
private async Task GetProject(IDialogContext context, IMessageActivity message) { if (TryParseVsoId(message.Text, out int vsoId)) { await context.PostWithRetryAsync($"Let me get the status of {vsoId}"); try { string projectDetails = await VsoHelper.GetProjectSummary(vsoId); await context.PostWithRetryAsync(projectDetails); await PromptForConnectToAgentAfterGettingProjectDetails(context); } catch (System.Exception e) { Trace.TraceInformation($"Sorry, I ran into an error closing project #{vsoId}. Exception = {e.Message}"); context.Call(new UserHelpDialog(), EndDialog); } } else { await LetUserKnowWeRanIntoAnIssueAndSendToAgentDialog(context); } }
public VsoModule() { var token = VsoHelper.GetActiveUser(); // get accounts Get["/vso/accounts/"] = _ => { if (token != null) { var accounts = VsoHelper.GetAccounts(token.accessToken); if (accounts != null) { return(Response.AsJson(accounts.value.Select(a => new { a.accountName }))); } } return(Response.AsJson(new { })); }; // get projects for an account Get["/vso/projects/{account}"] = _ => { if (token != null) { var account = (string)_.account; var projects = VsoHelper.GetProjects(account, token.accessToken); if (projects != null) { return(Response.AsJson(projects.value.Select(p => new { p.name }))); } } return(Response.AsJson(new { })); }; // get work item types for an account and project Get["/vso/workitemtypes/{account}/{project}"] = _ => { if (token != null) { var account = (string)_.account; var project = (string)_.project; var witypes = VsoHelper.GetWorkItemTypes(account, project, token.accessToken); if (witypes != null) { return(Response.AsJson( witypes.value.Select(t => new { t.name, state = GetState(t.name) }))); } } return(Response.AsJson(new { })); }; Get["/vso/test/{account}/{project}"] = _ => { if (token != null) { var account = (string)_.account; var project = (string)_.project; var witypes = VsoHelper.GetWorkItemTypes(account, project, token.accessToken); if (witypes != null) { return(View["VsoTest", witypes]); } } return(View["VsoTest", new { error = "Hmm, something bad happened :(" }]); }; }
private async Task OnDeadlineSelected(IDialogContext context, IAwaitable<IEnumerable<DateTime>> result) { try { // "result" contains the date (or array of dates) returned from the prompt IEnumerable<DateTime> momentOrRange = await result; var deadline = momentOrRange.First(); // DeadlinePrompt.MomentOrRangeToString(momentOrRange); // Store date context.ConversationData.SetValue("deadline", deadline); var description = context.ConversationData.GetValue<string>("description"); string mobilePhone = string.Empty; string alias = string.Empty; if (!context.UserData.TryGetValue(UserProfileHelper.UserProfileKey, out UserProfile userProfile)) { mobilePhone = userProfile.MobilePhone; alias = userProfile.Alias; } var vsoTicketNumber = await VsoHelper.CreateTaskInVso(VsoHelper.VirtualAssistanceTaskType, context.Activity.From.Name, description, ConfigurationManager.AppSettings["AgentToAssignVsoTasksTo"], deadline, "", null, context.Activity.ChannelId); MicrosoftAppCredentials.TrustServiceUrl(ActivityHelper.TeamsServiceEndpoint); AdaptiveCard card = new AdaptiveCard(); card.Body.Add(new AdaptiveTextBlock() { Text = $"New Virtual Assistance request from {context.Activity.From.Name}. VSO:{vsoTicketNumber}", Size = AdaptiveTextSize.Large, Wrap = true, Separator = true }); var summary = new AdaptiveFactSet { Facts = new List<AdaptiveFact> { new AdaptiveFact("Who", context.Activity.From.Name), new AdaptiveFact("What", description), new AdaptiveFact("When", deadline.ToString()), new AdaptiveFact("Vso", vsoTicketNumber.ToString()), } }; card.Body.Add(summary); using (var connectorClient = await BotConnectorUtility.BuildConnectorClientAsync(ActivityHelper.TeamsServiceEndpoint)) { var channelInfo = GetHardcodedChannelId(); context.ConversationData.SetValue("VsoId", vsoTicketNumber); context.ConversationData.SetValue("EndUserConversationId", context.Activity.Conversation.Id); var conversationResourceResponse = await ConversationHelpers.CreateAgentConversation(channelInfo, card, $"New research request from {context.Activity.Recipient.Name}", connectorClient, vsoTicketNumber, context.Activity as IMessageActivity); EndUserAndAgentConversationMappingState state = new EndUserAndAgentConversationMappingState(vsoTicketNumber.ToString(), context.Activity.From.Name, context.Activity.From.Id, context.Activity.Conversation.Id, conversationResourceResponse.Id); await state.SaveInVso(vsoTicketNumber.ToString()); } await context.PostWithRetryAsync("Thank you! I have posted following to internal agents. " + "I will be in touch with you shortly. " + $"Please use reference #{vsoTicketNumber} for this request in future. " + $"What: {description}. When: {deadline}."); context.Done<object>(null); } catch (TooManyAttemptsException) { await context.PostWithRetryAsync("TooManyAttemptsException. Restarting now..."); } catch (System.Exception e) { WebApiConfig.TelemetryClient.TrackException(e, new Dictionary<string, string> { {"dialog", "InternetResearchDialog" }, {"function", "OnDeadlineSelected" } }); throw; } }
private async Task OnConfirmResearchDialog(IDialogContext context, IAwaitable <bool> result) { if (result == null) { throw new InvalidOperationException((nameof(result)) + Strings.NullException); } var sendIt = await result; if (sendIt) { var additionalInfoFromUser = context.ConversationData.GetValue <string>(AdditionalInfoKey); var description = context.ConversationData.GetValue <string>(DescriptionKey); var deadline = DateTime.Parse(context.ConversationData.GetValue <string>(DeadlineKey)); var vsoTicketNumber = await VsoHelper.CreateTaskInVso(VsoHelper.ResearchTaskType, context.Activity.From.Name, description + Environment.NewLine + additionalInfoFromUser, ConfigurationManager.AppSettings["AgentToAssignVsoTasksTo"], deadline, "", userProfile, context.Activity.ChannelId); context.ConversationData.SetValue(VsoIdKey, vsoTicketNumber); context.ConversationData.SetValue(EndUserConversationIdKey, context.Activity.Conversation.Id); try { var conversationTitle = $"Web research request from {userProfile} via {context.Activity.ChannelId} due {deadline}"; string agentConversationId = await ConversationHelpers.CreateAgentConversationEx(context, conversationTitle, CreateCardForAgent(context, additionalInfoFromUser, description, deadline, vsoTicketNumber), userProfile); EndUserAndAgentConversationMappingState state = new EndUserAndAgentConversationMappingState(vsoTicketNumber.ToString(), context.Activity.From.Name, context.Activity.From.Id, context.Activity.Conversation.Id, agentConversationId); await state.SaveInVso(vsoTicketNumber.ToString()); await context.PostWithRetryAsync("Sure. I have sent your request to a freelancer. " + $"Please use #{vsoTicketNumber} for referencing this request in future. " + "At this point, any message you send will be sent directly to the freelancer. They may take time to respond, " + "or may have clarifying questions which I will relay back to you."); context.Done <object>(true); } catch (System.Exception e) { await context.PostWithRetryAsync("Sorry, I ran into an issue while connecting with agent. Please try again later."); WebApiConfig.TelemetryClient.TrackException(e, new Dictionary <string, string> { { "function", "OnConfirmResearchDialog.CreateAgentConversation" } }); context.Done <object>(false); } } else { await context.PostWithRetryAsync("Okay, I have cancelled this request."); context.Done <object>(false); } }
private static async Task SendAutoReplyIfNeeded(IDialogContext context, int?vsoId) { // Check when was the last time we sent message to agent var timeStampWhenLastMessageWasSentByAgent = await OnlineStatus.GetTimeWhenMemberWasLastActive(OnlineStatus.AgentMemberType); var timeSinceLastMessageWasSentByAgent = DateTime.UtcNow.Subtract((DateTime)timeStampWhenLastMessageWasSentByAgent); bool autoReplyWasSentAWhileBack = DateTime.UtcNow.Subtract(GetAutoReplySentOnTimeStamp(context)) .TotalMinutes > MinutesToWaitBeforeSendingAutoReply; if (timeSinceLastMessageWasSentByAgent.TotalMinutes >= MinutesToWaitForAgentOnlineBeforeSendingAutoReply && autoReplyWasSentAWhileBack) { await context.PostWithRetryAsync($"Hi {UserProfileHelper.GetFriendlyName(context)}, " + $"My experts are working on Project #{vsoId}. " + $"Current status of this project is {await VsoHelper.GetProjectStatus((int)vsoId)}. " + "Either experts are busy or offline at the moment. " + $"They were online {timeSinceLastMessageWasSentByAgent.TimeAgo()}. Please wait. "); SetAutoReplySentOnTimeStamp(context); } }
private async Task CreateProject(IDialogContext context) { try { var description = context.ConversationData.GetValue <string>(DescriptionKey); var userProfile = context.UserData.GetValue <UserProfile>(UserProfileHelper.UserProfileKey); var deadline = DateTime.UtcNow.AddHours(_minHoursToCompleteResearch); var vsoTicketNumber = await VsoHelper.CreateTaskInVso(VsoHelper.ResearchTaskType, context.Activity.From.Name, description, ConfigurationManager.AppSettings["AgentToAssignVsoTasksTo"], deadline, "", userProfile, context.Activity.ChannelId); context.ConversationData.SetValue(VsoIdKey, vsoTicketNumber); context.ConversationData.SetValue(EndUserConversationIdKey, context.Activity.Conversation.Id); var conversationTitle = $"Web research request from {userProfile} via {context.Activity.ChannelId} due {deadline}"; string agentConversationId = await ConversationHelpers.CreateAgentConversationEx(context, conversationTitle, CreateCardForAgent(context, description, deadline, vsoTicketNumber, userProfile), userProfile); EndUserAndAgentConversationMappingState state = new EndUserAndAgentConversationMappingState(vsoTicketNumber.ToString(), context.Activity.From.Name, context.Activity.From.Id, context.Activity.Conversation.Id, agentConversationId); await state.SaveInVso(vsoTicketNumber.ToString()); WebApiConfig.TelemetryClient.TrackEvent("CreateProject", new Dictionary <string, string>() { { "from", context.Activity.From.Name }, { UserProfileHelper.UserProfileKey, userProfile.ToString() }, { DescriptionKey, description }, { "deadline", deadline.ToString() }, { VsoIdKey, vsoTicketNumber.ToString() }, }); await context.PostWithRetryAsync($"OK, your research project is #{vsoTicketNumber}. " + $"We'll get to work on this shortly and send you a confirmation email.\n\n\n\n" + $"In the meantime, feel free to tell me more, like: " + $"what do you want to do with this info?"); context.Done <object>(true); } catch (System.Exception e) { try { if (context.ConversationData.TryGetValue(VsoIdKey, out string vsoTicketNumber)) { // close this ticket await VsoHelper.CloseProject(Convert.ToInt32(vsoTicketNumber)); } } catch (System.Exception exception) { WebApiConfig.TelemetryClient.TrackException(exception, new Dictionary <string, string> { { "debugNote", "Error closing project during exception received in CreateProject" }, { "CreateProjectException", e.ToString() }, }); } await context.PostWithRetryAsync("Sorry, I ran into an issue while connecting with agent. Please try again later."); WebApiConfig.TelemetryClient.TrackException(e, new Dictionary <string, string> { { "function", "OnConfirmResearchDialog.CreateAgentConversation" } }); context.Done <object>(false); } }