/// <summary>
        /// Notify a pairup.
        /// </summary>
        /// <param name="teamModel">DB team model info.</param>
        /// <param name="teamName">MS-Teams team name</param>
        /// <param name="pair">The pairup</param>
        /// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
        /// <returns>Number of users notified successfully</returns>
        private async Task <int> NotifyPairAsync(TeamInstallInfo teamModel, string teamName, Tuple <ChannelAccount, ChannelAccount> pair, CancellationToken cancellationToken)
        {
            // Get the default culture info to use in resource files.
            var cultureName = CloudConfigurationManager.GetSetting("DefaultCulture");

            Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = CultureInfo.GetCultureInfo(cultureName);

            this.telemetryClient.TrackTrace($"Sending pairup notification to {pair.Item1.Id} and {pair.Item2.Id}");

            var teamsPerson1 = JObject.FromObject(pair.Item1).ToObject <TeamsChannelAccount>();
            var teamsPerson2 = JObject.FromObject(pair.Item2).ToObject <TeamsChannelAccount>();

            // Fill in person2's info in the card for person1
            var cardForPerson1 = PairUpNotificationAdaptiveCard.GetCard(teamName, teamsPerson1, teamsPerson2, this.botDisplayName);

            // Fill in person1's info in the card for person2
            var cardForPerson2 = PairUpNotificationAdaptiveCard.GetCard(teamName, teamsPerson2, teamsPerson1, this.botDisplayName);

            // Send notifications and return the number that was successful
            var notifyResults = await Task.WhenAll(
                this.conversationHelper.NotifyUserAsync(this.botAdapter, teamModel.ServiceUrl, teamModel.TeamId, MessageFactory.Attachment(cardForPerson1), teamsPerson1, teamModel.TenantId, cancellationToken),
                this.conversationHelper.NotifyUserAsync(this.botAdapter, teamModel.ServiceUrl, teamModel.TeamId, MessageFactory.Attachment(cardForPerson2), teamsPerson2, teamModel.TenantId, cancellationToken));

            return(notifyResults.Count(wasNotified => wasNotified));
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Save information about the team from which the bot was removed.
        /// </summary>
        /// <param name="serviceUrl">The service url</param>
        /// <param name="teamId">The team id</param>
        /// <param name="tenantId">The tenant id</param>
        /// <returns>Tracking task</returns>
        public Task SaveRemoveFromTeam(string serviceUrl, string teamId, string tenantId)
        {
            var teamInstallInfo = new TeamInstallInfo
            {
                TeamId   = teamId,
                TenantId = tenantId,
            };

            return(this.dataProvider.UpdateTeamInstallStatusAsync(teamInstallInfo, false));
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Save information about the team to which the bot was added.
        /// </summary>
        /// <param name="serviceUrl">The service url</param>
        /// <param name="teamId">The team id</param>
        /// <param name="tenantId">The tenant id</param>
        /// <param name="botInstaller">Person that has added the bot to the team</param>
        /// <returns>Tracking task</returns>
        public Task SaveAddedToTeam(string serviceUrl, string teamId, string tenantId, string botInstaller)
        {
            var teamInstallInfo = new TeamInstallInfo
            {
                ServiceUrl    = serviceUrl,
                TeamId        = teamId,
                TenantId      = tenantId,
                InstallerName = botInstaller
            };

            return(this.dataProvider.UpdateTeamInstallStatusAsync(teamInstallInfo, true));
        }
Ejemplo n.º 4
0
        /// <summary>
        /// Updates team installation status in store. If the bot is installed, the info is saved, otherwise info for the team is deleted.
        /// </summary>
        /// <param name="team">The team installation info</param>
        /// <param name="installed">Value that indicates if bot is installed</param>
        /// <returns>Tracking task</returns>
        public async Task TeamInstallUpdate(TeamInstallInfo team, bool installed)
        {
            await this.EnsureInitializedAsync();

            if (installed)
            {
                var response = await this.documentClient.UpsertDocumentAsync(this.teamsCollection.SelfLink, team);
            }
            else
            {
                var documentUri = UriFactory.CreateDocumentUri(this.database.Id, this.teamsCollection.Id, team.Id);
                var response    = await this.documentClient.DeleteDocumentAsync(documentUri, new RequestOptions { PartitionKey = new PartitionKey(team.Id) });
            }
        }
Ejemplo n.º 5
0
        private static async Task <List <ChannelAccount> > GetOptedInUsers(TeamInstallInfo teamInfo)
        {
            var optedInUsers = new List <ChannelAccount>();

            var members = await GetTeamMembers(teamInfo.ServiceUrl, teamInfo.TeamId, teamInfo.TenantId);

            foreach (var member in members)
            {
                var optInStatus = MeetupBotDataProvider.GetUserOptInStatus(teamInfo.TenantId, member.ObjectId);

                if (optInStatus == null || optInStatus.OptedIn)
                {
                    optedInUsers.Add(member);
                }
            }

            return(optedInUsers);
        }
Ejemplo n.º 6
0
        private static async Task <List <TeamsChannelAccount> > GetOptedInUsers(TeamInstallInfo teamInfo, Dictionary <string, UserOptInInfo> optInInfo)
        {
            var optedInUsers = new List <TeamsChannelAccount>();

            var members = await GetTeamMembers(teamInfo.ServiceUrl, teamInfo.TeamId, teamInfo.TenantId);

            foreach (var member in members)
            {
                var isBot = string.IsNullOrEmpty(member.Surname);
                optInInfo.TryGetValue(member.ObjectId, out UserOptInInfo optInStatus);

                if ((optInStatus == null || optInStatus.OptedIn) && !isBot)
                {
                    optedInUsers.Add(member);
                }
            }

            return(optedInUsers);
        }
Ejemplo n.º 7
0
        /// <summary>
        /// Notify a pairup.
        /// </summary>
        /// <param name="teamModel">DB team model info.</param>
        /// <param name="teamName">MS-Teams team name</param>
        /// <param name="pair">The pairup</param>
        /// <param name="cancellationToken">Propagates notification that operations should be canceled.</param>
        /// <returns>Number of users notified successfully</returns>
        private async Task <int> NotifyPairAsync(TeamInstallInfo teamModel, string teamName, Tuple <ChannelAccount, ChannelAccount> pair, CancellationToken cancellationToken)
        {
            this.telemetryClient.TrackTrace($"Sending pairup notification to {pair?.Item1?.Id} and {pair?.Item2?.Id}");

            var teamsPerson1 = JObject.FromObject(pair.Item1).ToObject <TeamsChannelAccount>();
            var teamsPerson2 = JObject.FromObject(pair.Item2).ToObject <TeamsChannelAccount>();

            // Fill in person2's info in the card for person1
            var cardForPerson1 = PairUpNotificationAdaptiveCard.GetCard(teamName, teamsPerson1, teamsPerson2, this.botDisplayName);

            // Fill in person1's info in the card for person2
            var cardForPerson2 = PairUpNotificationAdaptiveCard.GetCard(teamName, teamsPerson2, teamsPerson1, this.botDisplayName);

            // Send notifications and return the number that was successful
            var notifyResults = await Task.WhenAll(
                this.conversationHelper.NotifyUserAsync(this.botAdapter, teamModel.ServiceUrl, teamModel.TeamId, MessageFactory.Attachment(cardForPerson1), teamsPerson1, teamModel.TenantId, cancellationToken),
                this.conversationHelper.NotifyUserAsync(this.botAdapter, teamModel.ServiceUrl, teamModel.TeamId, MessageFactory.Attachment(cardForPerson2), teamsPerson2, teamModel.TenantId, cancellationToken));

            return(notifyResults.Count(wasNotified => wasNotified));
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Gets user info from the data store, or else generates it
        /// </summary>
        /// <param name="userId">User object Id</param>
        /// <param name="teamModel">DB team model info</param>
        /// <returns>List of pairs</returns>
        private async Task <UserInfo> GetOrCreateUserInfoAsync(string userId, TeamInstallInfo teamModel)
        {
            this.telemetryClient.TrackTrace($"Getting info for {userId}");

            UserInfo userInfo = await this.dataProvider.GetUserInfoAsync(userId);

            if (userInfo == null)
            {
                this.telemetryClient.TrackTrace($"{userId} info is not saved, generating now");

                userInfo = new UserInfo()
                {
                    TenantId      = teamModel.TenantId,
                    UserId        = userId,
                    OptedIn       = true,
                    ServiceUrl    = teamModel.ServiceUrl,
                    RecentPairUps = new List <UserInfo>(),
                };
            }

            return(userInfo);
        }
Ejemplo n.º 9
0
        private async Task <List <ChannelAccount> > GetOptedInUsers(ConnectorClient connectorClient, TeamInstallInfo teamInfo)
        {
            // Pull the roster of specified team and then remove everyone who has opted out explicitly
            var members = await connectorClient.Conversations.GetConversationMembersAsync(teamInfo.TeamId);

            this.telemetryClient.TrackTrace($"Found {members.Count} in team {teamInfo.TeamId}");

            var tasks   = members.Select(m => this.dataProvider.GetUserInfoAsync(m.AsTeamsChannelAccount().ObjectId));
            var results = await Task.WhenAll(tasks);

            return(members
                   .Zip(results, (member, userInfo) => ((userInfo == null) || userInfo.OptedIn) ? member : null)
                   .Where(m => m != null)
                   .ToList());
        }
        /// <summary>
        /// Get list of opted in users to start matching process
        /// </summary>
        /// <param name="dbMembersLookup">Lookup of DB users opt-in status</param>
        /// <param name="teamInfo">The team that the bot has been installed to</param>
        /// <returns>Opted in users' channels</returns>
        private async Task <List <ChannelAccount> > GetOptedInUsersAsync(Dictionary <string, bool> dbMembersLookup, TeamInstallInfo teamInfo)
        {
            // Pull the roster of specified team and then remove everyone who has opted out explicitly
            var members = await this.conversationHelper.GetTeamMembers(this.botAdapter, teamInfo);

            this.telemetryClient.TrackTrace($"Found {members.Count} in team {teamInfo.TeamId}");

            return(members
                   .Where(member => member != null)
                   .Where(member =>
            {
                var memberObjectId = this.GetChannelUserObjectId(member);
                return !dbMembersLookup.ContainsKey(memberObjectId) || dbMembersLookup[memberObjectId];
            })
                   .ToList());
        }
Ejemplo n.º 11
0
        private async Task <List <ChannelAccount> > GetOptedInUsers(ConnectorClient connectorClient, TeamInstallInfo teamInfo)
        {
            // Pull the roster of specified team and then remove everyone who has opted out explicitly
            var members = await connectorClient.Conversations.GetConversationMembersAsync(teamInfo.TeamId);

            this.telemetryClient.TrackTrace($"Found {members.Count} in team {teamInfo.TeamId}");

            var tasks   = members.Select(m => this.dataProvider.GetUserInfoAsync(m.AsTeamsChannelAccount().ObjectId));
            var results = await Task.WhenAll(tasks);

            //---------testing------------
            this.telemetryClient.TrackTrace($"Testing, before filtering {members.Count} in team {teamInfo.TeamId}");
            foreach (var m in members)
            {
                this.telemetryClient.TrackTrace($"Channel member: {m.Name}, role: {m.Role}");
            }

            var members2 = members
                           .Zip(results, (member, userInfo) => ((userInfo == null) || userInfo.OptedIn) ? member : null)
                           .Where(m => m != null)
                           .ToList();

            this.telemetryClient.TrackTrace($"Testing, after filtering {members2.Count} in team {teamInfo.TeamId}");
            foreach (var m in members2)
            {
                this.telemetryClient.TrackTrace($"OptIn member: {m.Name}, role: {m.Role}");
            }

            var members3 = members
                           .Zip(results, (member, userInfo) => ((userInfo == null) || userInfo.OptedIn) ? member : null)
                           .Where(m => m == null)
                           .ToList();

            this.telemetryClient.TrackTrace($"Testing, during filtering {members3.Count} filtered out in team {teamInfo.TeamId}");
            foreach (var m in members3)
            {
                this.telemetryClient.TrackTrace($"OptOut member: {m.Name}, role: {m.Role}");
            }

            //----------------------------

            return(members2);
        }
Ejemplo n.º 12
0
        /// <summary>
        /// Pair list of users into groups of 2 users per group
        /// </summary>
        /// <param name="users">Users accounts</param>
        /// <param name="teamModel">DB team model info.</param>
        /// <returns>List of pairs</returns>
        private List <Tuple <ChannelAccount, ChannelAccount> > MakePairs(List <ChannelAccount> users, TeamInstallInfo teamModel)
        {
            if (users.Count > 1)
            {
                this.telemetryClient.TrackTrace($"Making {users.Count / 2} pairs among {users.Count} users");
            }
            else
            {
                this.telemetryClient.TrackTrace($"Pairs could not be made because there is only 1 user in the team");
            }

            this.Randomize(users);
            LinkedList <ChannelAccount> queue = new LinkedList <ChannelAccount>(users);
            var pairs = new List <Tuple <ChannelAccount, ChannelAccount> >();

            while (queue.Count > 0)
            {
                ChannelAccount pairUserOne = queue.First.Value;
                ChannelAccount pairUserTwo = null;

                UserInfo pairUserOneInfo = this.GetOrCreateUserInfoAsync(this.GetChannelUserObjectId(pairUserOne), teamModel)?.Result;
                this.telemetryClient.TrackTrace($"Dequeuing (1) {pairUserOneInfo?.UserId}");
                queue.RemoveFirst();

                if (pairUserOneInfo.RecentPairUps == null)
                {
                    pairUserOneInfo.RecentPairUps = new List <UserInfo>();
                }

                bool foundPerfectPairing = false;

                for (LinkedListNode <ChannelAccount> restOfQueue = queue.First; restOfQueue != null; restOfQueue = restOfQueue.Next)
                {
                    pairUserTwo = restOfQueue.Value;
                    UserInfo pairUserTwoInfo = this.GetOrCreateUserInfoAsync(this.GetChannelUserObjectId(pairUserTwo), teamModel)?.Result;

                    if (pairUserTwoInfo.RecentPairUps == null)
                    {
                        pairUserTwoInfo.RecentPairUps = new List <UserInfo>();
                    }

                    this.telemetryClient.TrackTrace($"Processing {pairUserOneInfo?.UserId} and {pairUserTwoInfo?.UserId}");

                    // check if userone and usertwo have already paired recently
                    if (this.SamePairNotCreatedRecently(pairUserOneInfo, pairUserTwoInfo))
                    {
                        this.telemetryClient.TrackTrace($"Pairing {pairUserOneInfo?.UserId} and {pairUserTwoInfo?.UserId}");

                        pairs.Add(new Tuple <ChannelAccount, ChannelAccount>(pairUserOne, pairUserTwo));
                        this.UpdateUserRecentlyPairedAsync(pairUserOneInfo, pairUserTwoInfo);

                        // Remove pairUserTwo since user has been paired
                        this.telemetryClient.TrackTrace($"Dequeuing (2) {pairUserTwoInfo?.UserId}");
                        queue.Remove(pairUserTwo);
                        foundPerfectPairing = true;
                        break;
                    }
                }

                // Not possible to find a perfect pairing, so just use next.
                if (!foundPerfectPairing)
                {
                    this.telemetryClient.TrackTrace($"No perfect pair; selecting next user");
                    pairUserTwo = queue.First?.Value;

                    if (pairUserTwo != null)
                    {
                        pairs.Add(new Tuple <ChannelAccount, ChannelAccount>(pairUserOne, pairUserTwo));
                        queue.RemoveFirst();
                        this.telemetryClient.TrackTrace($"Pair formed; dequeued next user");
                    }
                    else
                    {
                        this.telemetryClient.TrackTrace($"No more users left to pair with");
                    }
                }
            }

            this.telemetryClient.TrackTrace($"Formed {pairs.Count} pairs");

            return(pairs);
        }