Esempio n. 1
0
        /// <summary>
        /// Send event card
        /// </summary>
        /// <param name="eventMessages">List of EventMessage</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        private async Task SendEventCard(List <EventMessage> eventMessages)
        {
            var tasks = Task.Run(() =>
            {
                Parallel.ForEach(eventMessages, new ParallelOptions {
                    MaxDegreeOfParallelism = MaxParallelism
                }, async(eventMessage) =>
                {
                    var activity  = eventMessage.Activity;
                    HeroCard card = null;
                    switch (eventMessage.MessageType)
                    {
                    case MessageType.Event:
                        card = CelebrationCard.GetEventCard(activity);
                        break;

                    case MessageType.Preview:
                        card = CelebrationCard.GetPreviewCard(activity);
                        break;
                    }

                    var task = this.connectorServiceHelper.SendPersonalMessageAsync(string.Empty, new List <Attachment> {
                        card.ToAttachment()
                    }, activity.ConversationId);

                    RetryWithExponentialBackoff retryBackOff = new RetryWithExponentialBackoff();
                    await retryBackOff.RunAsync(task, eventMessage, this.SuccessCallback, this.FailureCallback);
                });
            });

            await tasks;
        }
Esempio n. 2
0
        // Handle conversation update event (in personal scope)
        private async Task HandlePersonalConversationUpdateAsync(Activity activity)
        {
            this.logProvider.LogInfo($"Handling personal conversationUpdate in thread {activity.Conversation.Id}");

            // In personal scope we only handle events with MembersAdded
            var isBotAdded = activity.MembersAdded?.Any(member => member.Id == activity.Recipient.Id);

            if (isBotAdded == true)
            {
                var user = activity.From;
                this.logProvider.LogInfo($"Conversation was created with user {user.Id}");

                var objectId = user.Properties["aadObjectId"].ToString();
                var userInfo = await this.userManagementHelper.GetUserByAadObjectIdAsync(objectId);

                if (userInfo?.InstallationMethod != BotScope.Team)
                {
                    // Only send a welcome message to the users who were not previously seen in team scope
                    var reply = activity.CreateReply();
                    reply.Attachments.Add(CelebrationCard.GetWelcomeCardForInstaller().ToAttachment());
                    await this.connectorClient.Conversations.SendToConversationWithRetriesAsync(reply);
                }
                else
                {
                    this.logProvider.LogInfo($"Welcome message for {user.Id} was already sent in team scope");
                }
            }
        }
Esempio n. 3
0
        // Process the new members added to the team
        private async Task ProcessNewTeamMembersAsync(IEnumerable <TeamsChannelAccount> teamMembers, Team teamInfo)
        {
            this.logProvider.LogInfo($"Processing new members in team {teamInfo.Id}");

            var welcomeCard = CelebrationCard.GetWelcomeMessageForGeneralChannelAndTeamMembers(teamInfo.InstallerName, teamInfo.Name).ToAttachment();
            await Task.WhenAll(teamMembers.Select(member => this.ProcessNewTeamMemberAsync(member, teamInfo, welcomeCard)));
        }
Esempio n. 4
0
        /// <summary>
        /// Receives message from user and reply to it
        /// </summary>
        /// <param name="activity">Activity object</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation</returns>
        public async Task <HttpResponseMessage> Post([FromBody] Activity activity)
        {
            try
            {
                UserTelemetryInitializer.SetTelemetryUserId(HttpContext.Current, activity.From.Id);
                this.LogUserActivity(activity);

                using (var dialogScope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
                {
                    this.connectorClient = dialogScope.Resolve <IConnectorClient>();

                    if (activity.Type == ActivityTypes.Message)
                    {
                        if (activity.Value != null)
                        {
                            // Process messageBack events using the dialog framework
                            var dialog = dialogScope.Resolve <RootDialog>();
                            await Conversation.SendAsync(activity, () => dialog);
                        }
                        else
                        {
                            // Send welcome card if user send any message to bot
                            var reply = activity.CreateReply();
                            reply.Attachments.Add(CelebrationCard.GetWelcomeCardInResponseToUserMessage().ToAttachment());
                            await this.connectorClient.Conversations.SendToConversationWithRetriesAsync(reply);
                        }
                    }
                    else if (activity.Type == ActivityTypes.ConversationUpdate)
                    {
                        this.logProvider.LogInfo("Processing conversationUpdate activity");
                        switch (activity.Conversation.ConversationType)
                        {
                        case "personal":
                            await this.HandlePersonalConversationUpdateAsync(activity);

                            break;

                        case "channel":
                            await this.HandleTeamConversationUpdateAsync(activity);

                            break;

                        default:
                            this.logProvider.LogWarning($"Received unexpected conversationUpdate activity with conversationType {activity.Conversation.ConversationType}");
                            break;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                this.logProvider.LogError($"Failed to process activity {activity.Type}", ex);
                throw;
            }

            return(this.Request.CreateResponse(HttpStatusCode.OK));
        }
Esempio n. 5
0
        // Process the new member added to the team
        // This means recording the team membership, user information, and sending the user a welcome card
        private async Task ProcessNewTeamMemberAsync(TeamsChannelAccount member, Team teamInfo, Attachment welcomeCard)
        {
            this.logProvider.LogInfo($"Processing member {member.Id} in team {teamInfo.Id}");

            try
            {
                var attachments = new List <Attachment> {
                    welcomeCard
                };
                var aadObjectId = (member.ObjectId ?? member.Properties["aadObjectId"]).ToString();

                // Record the user's membership in the team
                this.logProvider.LogInfo($"Recording team membership");
                var userTeamMembership = new UserTeamMembership
                {
                    TeamId      = teamInfo.Id,
                    UserTeamsId = member.Id,
                };
                await this.userManagementHelper.AddUserTeamMembershipAsync(userTeamMembership);

                // See if the user has events to share
                var events = await this.eventDataProvider.GetEventsByOwnerObjectIdAsync(aadObjectId);

                if (events.Count > 0)
                {
                    this.logProvider.LogInfo($"User has {events.Count} existing events, will send invitation to share");
                    attachments.Add(CelebrationCard.GetShareEventAttachment(teamInfo.Id, teamInfo.Name, aadObjectId));
                }

                // Get the user record
                var userInfo = await this.userManagementHelper.GetUserByAadObjectIdAsync(aadObjectId);

                // Create conversation if needed
                var conversationId = userInfo?.ConversationId;
                if (conversationId == null)
                {
                    conversationId = await this.connectorClient.Conversations.CreateOrGetDirectConversationAsync(teamInfo.TenantId, member.Id);
                }

                // Send the personal welcome message
                this.logProvider.LogInfo($"Sending personal welcome message");
                await this.connectorClient.Conversations.SendCardListAsync(conversationId, attachments);

                this.logProvider.LogInfo("Saving member details");
                await this.StoreUserDetailsIfNeededAsync(member, teamInfo, conversationId);
            }
            catch (Exception ex)
            {
                this.logProvider.LogError($"Failed to process new member {member.Id} in {teamInfo.Id}", ex);
                throw;
            }
        }
Esempio n. 6
0
        // Handles event skip action
        private async Task HandleSkipEventAsync(IDialogContext context, IMessageActivity message)
        {
            var payload = ((JObject)message?.Value)?.ToObject <PreviewCardPayload>();

            if (payload == null)
            {
                this.logProvider.LogInfo("Received message with no payload");
                return;
            }

            // Get the event
            var celebrationEvent = await this.eventDataProvider.GetEventByIdAsync(payload.EventId, payload.OwnerAadObjectId);

            if (celebrationEvent == null)
            {
                this.logProvider.LogInfo($"Could not find event {payload.EventId} for user {payload.OwnerAadObjectId}");
                await context.PostAsync(Strings.EventDoesNotExistMessage);

                return;
            }

            // Get the occurrence
            var occurrence = await this.eventDataProvider.GetEventOccurrenceByIdAsync(payload.OccurrenceId, payload.EventId);

            if (occurrence == null)
            {
                this.logProvider.LogInfo($"Could not find occurrence {payload.OccurrenceId} of event {payload.EventId} for user {payload.OwnerAadObjectId} (likely out of date card)");
                await context.PostAsync(Strings.SkippedStaleEventMessage);

                return;
            }

            // Check that the occurrence is still in the future
            if (occurrence.DateTime < DateTimeOffset.UtcNow)
            {
                await context.PostAsync(Strings.EventPassedMessage);

                return;
            }

            // Mark the occurrence as skipped
            occurrence.Status = EventStatus.Skipped;
            await this.eventDataProvider.UpdateEventOccurrenceAsync(occurrence);

            // Update the card
            var updatedMessage = context.MakeMessage();

            updatedMessage.Attachments.Add(CelebrationCard.GetPreviewCard(celebrationEvent, payload.OccurrenceId, payload.OwnerName, false).ToAttachment());
            await this.connectorClient.Conversations.UpdateActivityAsync(message.Conversation.Id, message.ReplyToId, (Activity)updatedMessage);

            await context.PostAsync(string.Format(Strings.EventSkippedMessage, celebrationEvent.Title));
        }
        /// <summary>
        /// Handles event skip action.
        /// </summary>
        /// <param name="context">IDialogContext object.</param>
        /// <param name="activity">IAwaitable message activity.</param>
        /// <returns>Task.</returns>
        public async Task HandleEventSkipActions(IDialogContext context, IAwaitable <IMessageActivity> activity)
        {
            var message = (Activity)await activity;

            if (message.Value != null)
            {
                var replyMessage       = string.Empty;
                var previewCardPayload = ((JObject)message.Value).ToObject <PreviewCardPayload>();

                // Get event by eventId to check if it exist or not.
                CelebrationEvent celebrationEvent = await this.eventHelper.GetEventByEventIdAsync(previewCardPayload.EventId, previewCardPayload.OwnerAadObjectId);

                if (celebrationEvent != null)
                {
                    if (previewCardPayload.UpcomingEventDate > DateTime.UtcNow.Date)
                    {
                        await this.eventHelper.UpdateRecurringEventAsync(previewCardPayload.EventId, EventStatus.Skipped);

                        EventMessageActivity eventMessageActivity = new EventMessageActivity
                        {
                            Id        = celebrationEvent.Id,
                            OwnerName = previewCardPayload.OwnerName,
                            ImageUrl  = celebrationEvent.ImageURL,
                            Message   = celebrationEvent.Message,
                            Title     = celebrationEvent.Title,
                        };

                        // Update the card
                        IMessageActivity updatedMessage = context.MakeMessage();
                        updatedMessage.Attachments.Add(CelebrationCard.GetPreviewCard(eventMessageActivity, false).ToAttachment());
                        updatedMessage.ReplyToId = message.ReplyToId;
                        await this.connectorClient.Conversations.UpdateActivityAsync(message.Conversation.Id, message.ReplyToId, (Activity)updatedMessage);

                        replyMessage = string.Format(Strings.EventSkippedMessage, message.From.Name);
                    }
                    else
                    {
                        // event occurrence has already passed for current year.
                        replyMessage = string.Format(Strings.EventPassedMessage);
                    }
                }
                else
                {
                    replyMessage = string.Format(Strings.EventNotExistMessage, previewCardPayload.Title);
                }

                await context.PostAsync(replyMessage);

                context.Done <object>(null);
            }
        }
Esempio n. 8
0
        /// <summary>
        /// Handles Ignore event sharing action.
        /// </summary>
        /// <param name="context">IDialogContext object.</param>
        /// <param name="activity">IAwaitable message activity.</param>
        /// <returns>Task.</returns>
        public async Task HandleIgnoreEventSharingAction(IDialogContext context, IAwaitable <IMessageActivity> activity)
        {
            var message = (Activity)await activity;

            if (message.Value != null)
            {
                var replyMessage = "Ok, if you change your mind you can share the events from the Events tab.";
                ShareEventPayload shareEventPayload = ((JObject)message.Value).ToObject <ShareEventPayload>();

                // Update the card
                IMessageActivity updatedMessage = context.MakeMessage();
                updatedMessage.Attachments.Add(CelebrationCard.GetShareEventAttachementWithoutActionButton(shareEventPayload.TeamName));
                updatedMessage.ReplyToId = message.ReplyToId;
                await this.connectorClient.Conversations.UpdateActivityAsync(message.Conversation.Id, message.ReplyToId, (Activity)updatedMessage);

                await context.PostAsync(replyMessage);

                context.Done <object>(null);
            }
        }
        /// <summary>
        /// Receives message from user and reply to it.
        /// </summary>
        /// <param name="activity">activity object.</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public async Task <HttpResponseMessage> Post([FromBody] Activity activity)
        {
            UserTelemetryInitializer.SetTelemetryUserId(HttpContext.Current, activity.From.Id);

            this.LogUserActivity(activity);
            this.teamMembers = null;

            if (activity.Type == ActivityTypes.Invoke || (activity.Type == ActivityTypes.Message && activity.Value != null))
            {
                using (var dialogScope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
                {
                    var dialog = dialogScope.Resolve <RootDialog>();
                    await Conversation.SendAsync(activity, () => dialog);
                }
            }
            else
            {
                using (var dialogScope = DialogModule.BeginLifetimeScope(this.scope, activity))
                {
                    IConnectorClient connectorClient = dialogScope.Resolve <IConnectorClient>();
                    this.connectorServiceHelper = new ConnectorServiceHelper(connectorClient, this.logProvider);
                    if (activity.Type == ActivityTypes.Message)
                    {
                        Activity welcomeActivity = activity.CreateReply();
                        welcomeActivity.Attachments.Add(CelebrationCard.GetWelcomeCardInResponseToUserMessage().ToAttachment());
                        await connectorClient.Conversations.ReplyToActivityAsync(welcomeActivity);
                    }
                    else
                    {
                        await this.HandleSystemMessageAsync(connectorClient, activity);
                    }
                }
            }

            var response = this.Request.CreateResponse(HttpStatusCode.OK);

            return(response);
        }
        /// <summary>
        /// Send welcome message in general channel.
        /// </summary>
        /// <param name="activity">Activity instance.</param>
        /// <param name="isBotInstalledInTeam">true/false.</param>
        /// <param name="installerName">bot installer name.</param>
        /// <param name="teamName">TeamName</param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        private async Task SendWelcomeMessageToGeneralChannel(Activity activity, bool isBotInstalledInTeam, string installerName, string teamName)
        {
            this.logProvider.LogInfo("Sending welcome message to general channel.");

            Activity     welcomeActivity = activity.CreateReply();
            AdaptiveCard welcomeCard;

            if (isBotInstalledInTeam)
            {
                welcomeCard = CelebrationCard.GetWelcomeMessageForGeneralChannelAndTeamMembers(installerName, teamName);
            }
            else
            {
                welcomeCard = CelebrationCard.GetWelcomeCardForInstaller();
            }

            this.logProvider.LogInfo($"Welcome card json: {welcomeCard.ToJson()}");

            welcomeActivity.Attachments.Add(welcomeCard.ToAttachment());
            await this.connectorClient.Conversations.ReplyToActivityAsync(welcomeActivity);

            this.logProvider.LogInfo("Welcome message sent to general Chanel.");
        }
        // send welcome message to all team members
        private async Task SendWelcomeMessageToTeamMembers(IList <ChannelAccount> teamMembers, TeamsChannelData channelData, string installerName, string teamName)
        {
            this.logProvider.LogInfo($"Sending welcome message to all the team members. Total team members of team: {channelData.Team.Id} = {teamMembers.Count}");
            string conversationId = string.Empty;

            foreach (var member in teamMembers)
            {
                conversationId = this.connectorServiceHelper.CreateOrGetConversationIdAsync(channelData.Tenant.Id, member.Id);
                List <Attachment> attachmentList = new List <Attachment> {
                    CelebrationCard.GetWelcomeMessageForGeneralChannelAndTeamMembers(installerName, teamName).ToAttachment()
                };
                string userAadObjectId = (member.Properties["objectId"] ?? member.Properties["aadObjectId"]).ToString();
                List <CelebrationEvent> celebrationEvents = await(await this.eventHelper.GetEventsByOwnerObjectIdAsync(userAadObjectId)).ToListAsync();

                if (celebrationEvents.Count > 0)
                {
                    attachmentList.Add(CelebrationCard.GetShareEventAttachement(channelData.Team.Id, channelData.Team.Name, userAadObjectId));
                }

                this.logProvider.LogInfo($"Sending personal welcome message to {member.Name}", new Dictionary <string, string>
                {
                    { "TeamId", channelData.Team.Id },
                    { "UserTeamId", member.Id },
                    { "UserObjectId", userAadObjectId },
                    { "ConversationId", conversationId },
                    { "Attachment", attachmentList.ToString() },
                });

                await this.connectorServiceHelper.SendPersonalMessageAsync(string.Empty, attachmentList, conversationId);

                this.logProvider.LogInfo("Saving member details to database.");

                // Save team member details.
                await this.SaveMemberDetailsAsync(member, channelData, conversationId);
            }
        }
        /// <summary>
        /// Handles event Share action
        /// </summary>
        /// <param name="context">IDialogContext object</param>
        /// <param name="activity">IAwaitable message activity</param>
        /// <returns>Task.</returns>
        public async Task HandleEventShareAction(IDialogContext context, IAwaitable <IMessageActivity> activity)
        {
            var message      = (Activity)await activity;
            var replyMessage = string.Empty;

            if (message.Value != null)
            {
                ShareEventPayload shareEventPayload = ((JObject)message.Value).ToObject <ShareEventPayload>();

                try
                {
                    var teamMembers = await this.connectorClient.Conversations.GetConversationMembersAsync(shareEventPayload.TeamId);

                    var user = teamMembers.Select(x => x.AsTeamsChannelAccount()).Where(x => x.ObjectId == shareEventPayload.UserAadObjectId).FirstOrDefault();

                    // Fetch teamDetails from DB to check if bot is still present or un-installed from team
                    var document = await this.userManagementHelper.GetTeamsDetailsByTeamIdAsync(shareEventPayload.TeamId);

                    if (user == null)
                    {
                        replyMessage = string.Format(Strings.ShareWithTeamNotAMemberError, shareEventPayload.TeamName);
                    }
                    else if (document == null)
                    {
                        replyMessage = string.Format(Strings.ShareWithTeamsNotInstalledError, shareEventPayload.TeamName);
                    }
                    else
                    {
                        // Fetch all the events of user and share with team
                        var celebrationEvents = await this.eventDataProvider.GetEventsByOwnerObjectIdAsync(shareEventPayload.UserAadObjectId);

                        if (celebrationEvents.Count > 0)
                        {
                            foreach (var celebrationEvent in celebrationEvents)
                            {
                                celebrationEvent.Teams.Add(new Team {
                                    Id = shareEventPayload.TeamId
                                });
                                await this.eventDataProvider.UpdateEventAsync(celebrationEvent);
                            }
                        }

                        replyMessage = Strings.ShareWithTeamSuccessMessage;

                        // Update the card
                        IMessageActivity updatedMessage = context.MakeMessage();
                        updatedMessage.Attachments.Add(CelebrationCard.GetShareEventAttachmentWithoutActionButton(shareEventPayload.TeamName));
                        updatedMessage.ReplyToId = message.ReplyToId;
                        await this.connectorClient.Conversations.UpdateActivityAsync(message.Conversation.Id, message.ReplyToId, (Activity)updatedMessage);
                    }
                }
                catch (Exception ex)
                {
                    this.logProvider.LogError("Failed to share the existing event with team", ex, new Dictionary <string, string>()
                    {
                        { "TeamId", shareEventPayload.TeamId },
                        { "UserAadObjectId", shareEventPayload.UserAadObjectId },
                    });
                    replyMessage = Strings.ShareWithTeamGenericError;
                }

                await context.PostAsync(replyMessage);

                context.Done <object>(null);
            }
        }
Esempio n. 13
0
        /// <summary>
        /// Handles event Share action.
        /// </summary>
        /// <param name="context">IDialogContext object.</param>
        /// <param name="activity">IAwaitable message activity.</param>
        /// <returns>Task.</returns>
        public async Task HandleEventShareAction(IDialogContext context, IAwaitable <IMessageActivity> activity)
        {
            var message      = (Activity)await activity;
            var replyMessage = string.Empty;

            if (message.Value != null)
            {
                ShareEventPayload shareEventPayload = ((JObject)message.Value).ToObject <ShareEventPayload>();

                try
                {
                    var teamMembers = await this.connectorClient.Conversations.GetConversationMembersAsync(shareEventPayload.TeamId);

                    var user = teamMembers.Where(x => x.Properties["objectId"].ToString() == shareEventPayload.UserAadObjectId).ToList().FirstOrDefault();

                    var document = await this.userManagementHelper.GetTeamsDetailsByTeamIdAsync(shareEventPayload.TeamId);

                    bool isBotUnintsalledFromTeam = document == null ? true : false;

                    if (user == null)
                    {
                        replyMessage = $"You are no longer a member of {shareEventPayload.TeamName}.";
                    }
                    else if (isBotUnintsalledFromTeam)
                    {
                        replyMessage = "Someone uninstalled me from your team, I can no longer share these events there";
                    }
                    else
                    {
                        List <CelebrationEvent> celebrationEvents = await(await this.eventHelper.GetEventsByOwnerObjectIdAsync(
                                                                              shareEventPayload.UserAadObjectId)).ToListAsync();
                        if (celebrationEvents.Count > 0)
                        {
                            foreach (var celebrationEvent in celebrationEvents)
                            {
                                celebrationEvent.Teams.Add(new Team {
                                    Id = shareEventPayload.TeamId
                                });
                                CelebrationEvent updatedEvent = (dynamic)celebrationEvent;
                                updatedEvent.Teams = celebrationEvent.Teams;
                                await this.eventHelper.UpdateEventAsync(updatedEvent);
                            }
                        }

                        replyMessage = "I’ve set those events to be shared with the team when they occur.";

                        // Update the card
                        IMessageActivity updatedMessage = context.MakeMessage();
                        updatedMessage.Attachments.Add(CelebrationCard.GetShareEventAttachementWithoutActionButton(shareEventPayload.TeamName));
                        updatedMessage.ReplyToId = message.ReplyToId;
                        await this.connectorClient.Conversations.UpdateActivityAsync(message.Conversation.Id, message.ReplyToId, (Activity)updatedMessage);
                    }
                }
                catch (Exception ex)
                {
                    this.logProvider.LogError("Failed to share the existing event with team", ex, new Dictionary <string, string>()
                    {
                        {
                            "TeamId", shareEventPayload.TeamId
                        },
                        {
                            "TeamName", shareEventPayload.TeamName
                        },
                        {
                            "UserAadObjectId", shareEventPayload.UserAadObjectId
                        },
                    });

                    replyMessage = "Some error occurred to share the event with team. Please try again.";
                }

                await context.PostAsync(replyMessage);

                context.Done <object>(null);
            }
        }
Esempio n. 14
0
        // Handle team members added event (in team scope)
        private async Task HandleTeamMembersAddedAsync(Activity activity, TeamsChannelData channelData)
        {
            string teamId = channelData.Team.Id;

            this.logProvider.LogInfo($"Handling team members added event in team {teamId}");

            // Determine if the bot was installed to the team
            bool isBotAdded = activity.MembersAdded.Any(member => member.Id == activity.Recipient.Id);

            if (isBotAdded)
            {
                this.logProvider.LogInfo($"Bot was installed to team {teamId}");
                var properties = new Dictionary <string, string>
                {
                    { "Scope", activity.Conversation?.ConversationType },
                    { "TeamId", teamId },
                    { "InstallerId", activity.From.Id },
                };
                this.logProvider.LogEvent("AppInstalled", properties);
            }

            var membersAdded = activity.MembersAdded;

            // Ensure that we have an installation record for this team
            var isBackfill = false;
            var teamInfo   = await this.userManagementHelper.GetTeamsDetailsByTeamIdAsync(teamId);

            if ((teamInfo == null) && !isBotAdded)
            {
                this.logProvider.LogInfo($"Detected a missed installation to team {teamId}, will attempt to backfill");

                // We must have missed an event from this team-- attempt to backfill
                isBotAdded = true;
                isBackfill = true;
            }

            if (isBotAdded)
            {
                // Try to determine the name of the person that installed the app, which is usually the sender of the message (From.Id)
                // Note that in some cases we cannot resolve it to a team member, because the app was installed to the team programmatically via Graph
                string installerName = null;
                if (!isBackfill)
                {
                    installerName = activity.From?.Name;
                    if (installerName == null)
                    {
                        installerName = await this.GetUserNameAsync(activity.From, teamId);
                    }
                }

                // Get team details
                this.logProvider.LogInfo("Getting team details");
                var teamDetails = await this.connectorClient.GetTeamsConnectorClient().Teams.FetchTeamDetailsAsync(teamId);

                // Add team installation record
                this.logProvider.LogInfo("Recording team installation");
                teamInfo = new Team
                {
                    Id            = teamId,
                    Name          = teamDetails.Name,
                    ServiceUrl    = activity.ServiceUrl,
                    TenantId      = channelData.Tenant.Id,
                    InstallerName = installerName,
                };
                await this.userManagementHelper.SaveTeamDetailsAsync(teamInfo);

                // Send welcome message to the General channel
                this.logProvider.LogInfo("Sending welcome message to general channel");
                Activity reply = activity.CreateReply();
                reply.Attachments.Add(CelebrationCard.GetWelcomeMessageForGeneralChannelAndTeamMembers(installerName, teamDetails.Name).ToAttachment());
                await this.connectorClient.Conversations.SendToConversationWithRetriesAsync(reply);

                // Get all team members to welcome them
                this.logProvider.LogInfo("Getting all team members to send them a welcome message");
                membersAdded = await this.connectorClient.Conversations.GetConversationMembersAsync(teamId);
            }
            else
            {
                this.logProvider.LogInfo($"Members added to team {teamId} ({membersAdded?.Count} new members)");
            }

            // Process new team members
            await this.ProcessNewTeamMembersAsync(membersAdded.AsTeamsChannelAccounts(), teamInfo);
        }
Esempio n. 15
0
        /// <summary>
        /// Process request to send preview card
        /// </summary>
        /// <param name="currentDateTime">Current dateTime</param>
        /// <returns>A <see cref="Task"/>Representing the asynchronous operation</returns>
        public async Task <HttpResponseMessage> Post(string currentDateTime = "")
        {
            this.logProvider.LogInfo($"Processing events to send the reminder to owner. CurrentDateTime:{currentDateTime}");

            DateTimeOffset currentDateTimeOffset;

            if (!DateTimeOffset.TryParse(currentDateTime, null, DateTimeStyles.AdjustToUniversal, out currentDateTimeOffset))
            {
                currentDateTimeOffset = DateTimeOffset.UtcNow;
            }

            var events = await(await this.eventHelper.GetCelebrationEventsAsync(GetEventQuery(currentDateTimeOffset.Date))).ToListAsync();

            this.logProvider.LogInfo($"found {events.Count} which are coming in next 72 hours.");
            if (events.Count > 0)
            {
                var existingRecurringEvents = await(await this.eventHelper.GetRecurringEventsAsync(events.Select(x => x.Id).ToList())).ToListAsync();

                this.logProvider.LogInfo($"Found {existingRecurringEvents.Count} for which reminder has already sent");
                int    lastAttemptStatusCode = (int)HttpStatusCode.OK;
                string responseBody          = string.Empty;

                // remove events which exist in Occurrences collection
                events.RemoveAll(x => existingRecurringEvents.Any(y => y.EventId == x.Id));

                if (events.Count > 0)
                {
                    this.users = await this.userManagementHelper.GetUsersByAadObjectIdsAsync(events.Select(x => x.OwnerAadObjectId).ToList());

                    this.connectorServiceHelper = new ConnectorServiceHelper(this.CreateConnectorClient(this.users.FirstOrDefault().ServiceUrl), this.logProvider);
                }

                // Loop each event and make entry in Occurrences collection to send preview and event card.
                foreach (var celebrationEvent in events)
                {
                    this.logProvider.LogInfo("Processing event to send reminder", new Dictionary <string, string>()
                    {
                        { "EventId", celebrationEvent.Id }, { "UserObjectId", celebrationEvent.OwnerAadObjectId }
                    });

                    // Get event owner information.
                    var user = this.users.Where(x => x.AadObjectId == celebrationEvent.OwnerAadObjectId).FirstOrDefault();

                    // update conversation id if it is null.
                    await this.ModifyUserDetailsAsync(user);

                    DateTime       upcomingEventDate          = Common.GetUpcomingEventDate(celebrationEvent.Date, currentDateTimeOffset.Date);
                    var            timespan                   = Array.ConvertAll <string, int>(ApplicationSettings.TimeToPostCelebration.Split(':'), Convert.ToInt32);
                    DateTime       upcomingEventDateTime      = upcomingEventDate.AddHours(timespan[0]).AddMinutes(timespan[1]);
                    DateTimeOffset upcomingEventDateTimeInUTC = TimeZoneInfo.ConvertTimeToUtc(upcomingEventDateTime, TimeZoneInfo.FindSystemTimeZoneById(celebrationEvent.TimeZoneId));

                    // add an entry to Occurrence collection for all the upcoming event.
                    EventOccurrence eventOccurrence = new EventOccurrence
                    {
                        EventId = celebrationEvent.Id,
                        Date    = upcomingEventDateTimeInUTC,
                    };

                    await this.eventHelper.AddRecurringEventAsync(eventOccurrence);

                    // Do not send reminder if event is today.
                    if (upcomingEventDate != currentDateTimeOffset.Date)
                    {
                        // Add new entry to EventMessages collection for reminder.
                        EventMessage eventMessage = new EventMessage
                        {
                            OccurrenceId = eventOccurrence.Id,
                            EventId      = celebrationEvent.Id,
                            Activity     = this.GetEventMessageActivity(celebrationEvent, user),
                            MessageType  = MessageType.Preview,
                            ExpireAt     = upcomingEventDate.AddHours(24),
                        };

                        await this.eventHelper.AddEventMessageAsync(eventMessage);

                        bool      isMessageSentSuccessfully = false;
                        Exception exception = null;

                        try
                        {
                            HeroCard previewCard = CelebrationCard.GetPreviewCard(eventMessage.Activity);

                            string message = string.Format(Strings.PreviewText, user.UserName);

                            this.logProvider.LogInfo("Sending reminder message to the owner of the event", new Dictionary <string, string>()
                            {
                                { "EventId", celebrationEvent.Id },
                                { "Attachment", Newtonsoft.Json.JsonConvert.SerializeObject(previewCard) },
                                { "Message", message },
                            });

                            // Send reminder of event to owner.
                            await this.connectorServiceHelper.SendPersonalMessageAsync(
                                message,
                                new List <Attachment> {
                                previewCard.ToAttachment()
                            },
                                user.ConversationId);

                            this.logProvider.LogInfo($"Reminder message sent to the owner of the event. EventId: {celebrationEvent.Id}");
                        }
                        catch (HttpException httpException)
                        {
                            lastAttemptStatusCode = httpException.GetHttpCode();
                            responseBody          = httpException.GetHtmlErrorMessage();
                            exception             = httpException;
                        }
                        catch (ErrorResponseException errorResponseException)
                        {
                            lastAttemptStatusCode = (int)errorResponseException.Response.StatusCode;
                            responseBody          = errorResponseException.Response.Content.ToString();
                            exception             = errorResponseException;
                        }
                        catch (Exception ex)
                        {
                            lastAttemptStatusCode = (int)HttpStatusCode.BadRequest;
                            responseBody          = ex.ToString();
                        }
                        finally
                        {
                            if (!isMessageSentSuccessfully)
                            {
                                this.logProvider.LogError("Failed to send reminder for upcoming event.", exception, new Dictionary <string, string>
                                {
                                    { "EventId", eventMessage.EventId },
                                    { "OccurrenceId", eventMessage.OccurrenceId },
                                    { "eventActivity", eventMessage.Activity.ToString() },
                                    { "LastAttemptStatusCode", lastAttemptStatusCode.ToString() },
                                    { "LastAttemptTime", DateTime.UtcNow.ToString() },
                                    { "ConversationId", user.ConversationId },
                                });
                            }

                            MessageSendResult messageSendResult = new MessageSendResult()
                            {
                                LastAttemptTime = DateTime.UtcNow,
                                StatusCode      = lastAttemptStatusCode,
                                ResponseBody    = responseBody,
                            };

                            await this.eventHelper.UpdateEventMessageAsync(eventMessage.Id, messageSendResult);
                        }
                    }
                    else
                    {
                        this.logProvider.LogInfo("Not sending reminder for this event as its upcoming event date is today.");
                    }
                }
            }

            return(new HttpResponseMessage(HttpStatusCode.OK));
        }
        /// <summary>
        /// Process and send all the due events in a team.
        /// </summary>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        private async Task ProcessEvents()
        {
            foreach (var teamId in this.teamsEventsDictionary.Keys)
            {
                List <string> eventIds = new List <string>();
                this.teamsEventsDictionary.TryGetValue(teamId, out eventIds);
                List <Attachment>   cardAttachments            = new List <Attachment>();
                List <EventMessage> eventMessages              = new List <EventMessage>();
                const int           maxEventPerCarousel        = 6;
                const int           minEventToSendIndividually = 3;
                int counter = 0;
                List <EventNotificationCardPayload> eventNotificationCardPayloadList = new List <EventNotificationCardPayload>();
                List <Entity> entities = new List <Entity>();
                foreach (var eventId in eventIds)
                {
                    counter++;
                    var recurringEvent   = this.recurringEvents.Where(x => x.EventId == eventId).FirstOrDefault();
                    var celebrationEvent = this.celebrationEvents.Where(x => x.Id == eventId).FirstOrDefault();

                    // Get event owner information.
                    var user = this.users.Where(x => x.AadObjectId == celebrationEvent.OwnerAadObjectId).FirstOrDefault();

                    // Add an Entry to Messages collection.
                    EventMessage eventMessage = await this.AddEntryToEventMessagesCollection(teamId, recurringEvent, celebrationEvent, user);

                    eventMessages.Add(eventMessage);

                    // Get Hero card for event.
                    HeroCard card = CelebrationCard.GetEventCard(eventMessage.Activity);

                    EventNotificationCardPayload eventNotificationCardPayload = new EventNotificationCardPayload()
                    {
                        UserName    = user.UserName,
                        UserTeamsId = user.TeamsId,
                        Message     = $"<at>{user.UserName}</at> is celebrating {celebrationEvent.Title}",
                        Attachment  = card.ToAttachment(),
                    };

                    eventNotificationCardPayloadList.Add(eventNotificationCardPayload);

                    if (eventIds.Count > minEventToSendIndividually && (counter % maxEventPerCarousel == 0 || counter == eventIds.Count))
                    {
                        eventNotificationCardPayloadList = eventNotificationCardPayloadList.OrderBy(x => x.UserName).ToList();

                        string message = "Stop the presses! Today ";
                        foreach (var notificationPayload in eventNotificationCardPayloadList)
                        {
                            message = message + notificationPayload.Message + ",";
                            cardAttachments.Add(notificationPayload.Attachment);

                            AddMentionedEntities(entities, notificationPayload);
                        }

                        message = message.TrimEnd(',');
                        int position = message.LastIndexOf(',');
                        message = (message.Substring(0, position) + " and " + message.Substring(position + 1)).Replace(",", ", ") + ". That’s a lot of merrymaking for one day—pace yourselves! \n\n";

                        // Do not send separate message in case of 1 event.
                        if (eventNotificationCardPayloadList.Count == 1)
                        {
                            message = string.Empty;
                        }

                        // send event notification in team.
                        this.logProvider.LogInfo("Sending event message in team", new Dictionary <string, string>()
                        {
                            { "EventId", celebrationEvent.Id },
                            { "TeamId", teamId },
                            { "Attachment", cardAttachments.ToString() },
                            { "Message", message },
                        });
                        await this.SendEventCard(message, cardAttachments, teamId, eventMessages, entities);

                        // Reset list
                        cardAttachments = new List <Attachment>();
                        eventMessages   = new List <EventMessage>();
                        eventNotificationCardPayloadList = new List <EventNotificationCardPayload>();
                        entities = new List <Entity>();
                    }
                    else if (eventIds.Count <= minEventToSendIndividually)
                    {
                        this.logProvider.LogInfo("Sending event message in team", new Dictionary <string, string>()
                        {
                            { "EventId", celebrationEvent.Id },
                            { "TeamId", teamId },
                            { "Attachment", cardAttachments.ToString() },
                            { "Message", string.Empty },
                        });
                        await this.SendEventCard(string.Empty, new List <Attachment> {
                            eventNotificationCardPayload.Attachment
                        }, teamId, eventMessages);

                        // Reset list
                        cardAttachments = new List <Attachment>();
                        eventMessages   = new List <EventMessage>();
                        eventNotificationCardPayloadList = new List <EventNotificationCardPayload>();
                        entities = new List <Entity>();
                    }
                }
            }

            // Delete entry from occurrences collection.
            foreach (var recurringEvent in this.recurringEvents)
            {
                this.logProvider.LogInfo("Deleting recurring event", new Dictionary <string, string>()
                {
                    { "EventId", recurringEvent.EventId },
                    { "RecurringEventId", recurringEvent.Id },
                });
                await this.eventHelper.DeleteRecurringEventAsync(recurringEvent.Id, recurringEvent.EventId);
            }
        }
 /// <summary>
 /// Gets the card to send
 /// </summary>
 /// <returns>The card to send</returns>
 public Attachment GetCard()
 {
     return(CelebrationCard.GetEventCard(this.Event, this.User.DisplayName).ToAttachment());
 }
        // Send a messsage to the owner of the given event, reminding them that their event is coming up
        private async Task SendEventReminderAsync(CelebrationEvent celebrationEvent, DateTimeOffset currentDateTimeOffset)
        {
            this.logProvider.LogInfo($"Sending reminder for event {celebrationEvent.Id} (owner={celebrationEvent.OwnerAadObjectId}, date={celebrationEvent.Date.ToShortDateString()})");

            // Determine the next occurrence of the event
            var deliveryTimeZone = TimeZoneInfo.FindSystemTimeZoneById(celebrationEvent.TimeZoneId);
            var currentTimeInDeliveryTimeZone = TimeZoneInfo.ConvertTimeFromUtc(currentDateTimeOffset.UtcDateTime, deliveryTimeZone);
            var upcomingEventDateTime         = Common.GetNextOccurrenceAfterDateTime(celebrationEvent.Date.Add(this.timeToPostPreview), currentTimeInDeliveryTimeZone);
            var upcomingEventDateTimeInUTC    = TimeZoneInfo.ConvertTimeToUtc(upcomingEventDateTime, deliveryTimeZone);

            // Do not send reminder if the next occurrence is not in the window
            var timeUntilNextOccurrence = upcomingEventDateTimeInUTC - currentDateTimeOffset;

            if ((timeUntilNextOccurrence.TotalMinutes < 0) ||
                (upcomingEventDateTimeInUTC - currentDateTimeOffset) > TimeSpan.FromDays(this.daysInAdvanceToSendEventPreview))
            {
                this.logProvider.LogInfo($"Next occurrence of event {celebrationEvent.Id} is not in the next {this.daysInAdvanceToSendEventPreview} days");
                return;
            }

            // Add an entry to Occurrence collection for the event, so we know that we processed it
            var eventOccurrence = new EventOccurrence
            {
                EventId          = celebrationEvent.Id,
                OwnerAadObjectId = celebrationEvent.OwnerAadObjectId,
                DateTime         = upcomingEventDateTimeInUTC,
            };

            eventOccurrence = await this.eventDataProvider.AddEventOccurrenceAsync(eventOccurrence);

            // Do not send reminder if we are within the period specified in the configuration
            if ((upcomingEventDateTimeInUTC - currentDateTimeOffset) < this.minimumTimeToProcessEvent)
            {
                this.logProvider.LogInfo($"Not sending reminder for event {celebrationEvent.Id} which is due in less than {(int)this.minimumTimeToProcessEvent.TotalHours} hours");
                return;
            }

            // Get event owner information
            var user = await this.userManagementHelper.GetUserByAadObjectIdAsync(celebrationEvent.OwnerAadObjectId);

            await this.EnsureConversationWithUserAsync(user);

            // Send reminder of event to the owner
            var previewCard = CelebrationCard.GetPreviewCard(celebrationEvent, eventOccurrence.Id, user.DisplayName);
            var message     = string.Format(Strings.EventPreviewMessageText, user.DisplayName);
            var activity    = new Activity(ActivityTypes.Message)
            {
                Conversation = new ConversationAccount {
                    Id = user.ConversationId
                },
                Recipient = new ChannelAccount {
                    Id = user.TeamsId
                },
                Text        = message,
                Summary     = message,
                Attachments = new List <Attachment> {
                    previewCard.ToAttachment()
                },
                ServiceUrl = user.ServiceUrl,
            };

            // Add new entry to EventMessages collection for message type "preview" to track the status of message sent
            var eventMessage = new EventMessage
            {
                EventId      = celebrationEvent.Id,
                OccurrenceId = eventOccurrence.Id,
                Activity     = activity,
                TenantId     = user.TenantId,
                MessageType  = MessageType.Preview,
                ExpireAt     = upcomingEventDateTimeInUTC.AddHours(24),
            };

            eventMessage = await this.eventDataProvider.AddEventMessageAsync(eventMessage);

            // Send the message
            try
            {
                await eventMessage.SendAsync(this.connectorClientFactory);

                this.logProvider.LogInfo($"Reminder message sent to the owner of event {celebrationEvent.Id}");
            }
            catch (Exception ex)
            {
                this.logProvider.LogError($"Failed to send reminder for event {celebrationEvent.Id}", ex, new Dictionary <string, string>
                {
                    { "EventId", eventMessage.EventId },
                    { "OccurrenceId", eventMessage.OccurrenceId },
                    { "ConversationId", user.ConversationId },
                    { "OccurrenceDateTime", eventOccurrence.DateTime.ToString() },
                    { "LastAttemptTime", DateTimeOffset.UtcNow.ToString() },
                    { "LastAttemptStatusCode", eventMessage.MessageSendResult?.StatusCode.ToString() },
                    { "ResponseBody", eventMessage.MessageSendResult?.ResponseBody },
                });
                throw;
            }
            finally
            {
                // Record the result of the send
                await this.eventDataProvider.UpdateEventMessageAsync(eventMessage);
            }
        }