Exemplo n.º 1
0
        private async Task ProcessRegistrationMessageReaction(SlackEventFullDto <ReactionEventDto> slackEventDto, string workspaceMemberId)
        {
            string message = Messages.OnboardingDm(workspaceMemberId);
            await _slackService.ChatPostMessage(workspaceMemberId, message, true);

            return;
        }
Exemplo n.º 2
0
        private async Task ProcessReactionAddedEvent(SlackEventFullDto <ReactionEventDto> slackEventDto)
        {
            string channelId = slackEventDto.Event.Item.Channel;

            // process reaction event based on channel
            if (channelId == _introductionChannelId)
            {
                await ProcessReactionIntroAddedEvent(slackEventDto);
            }

            if (channelId == _privateIntroChannelId)
            {
                await ProcessPrivateIntroReactionAddedEvent(slackEventDto);
            }

            if (channelId == _privateRegistrationChannelId)
            {
                await ProcessPrivateRegistrationReactionAddedEvent(slackEventDto);
            }

            if (channelId == _privateProjectsChannelId)
            {
                await ProcessPrivateProjectsReactionAddedEvent(slackEventDto);
            }
        }
Exemplo n.º 3
0
        private async Task <string> ParseMemberFromMessage(SlackEventFullDto <ReactionEventDto> slackEventDto)
        {
            SlackMessageDto messageDto = await _slackService.ChatRetrieveMessage(slackEventDto.Event.Item.Channel, slackEventDto.Event.Item.Ts);

            MessageDetailsDto initialMessage = messageDto.Messages.FirstOrDefault();
            int    startIndex = initialMessage.Text.IndexOf('@') + 1;
            int    endIndex   = initialMessage.Text.IndexOf('>') - 1;
            int    length     = endIndex - startIndex + 1;
            string memberId   = initialMessage.Text.Substring(startIndex, length);

            return(memberId);
        }
Exemplo n.º 4
0
        public async Task ProcessEvent(SlackEventDto slackEventDto)
        {
            if (slackEventDto.Type != "event_callback")
            {
                return;
            }

            string    eventData = slackEventDto.EventRaw.Value.ToString();
            EventType eventType = ParseEventType(eventData);

            if (eventType.SubType == "channel_join")
            {
                return;
            }

            switch (eventType.Type)
            {
            // team_join and user_change use same event type, TeamJoinEventDto.
            case "team_join":
                SlackEventFullDto <TeamJoinEventDto> teamJoinEvent = MapSlackEventObject <TeamJoinEventDto>(slackEventDto, eventData);
                await ProcessTeamJoinEvent(teamJoinEvent);

                break;

            case "user_change":
                SlackEventFullDto <TeamJoinEventDto> userChangeEvent = MapSlackEventObject <TeamJoinEventDto>(slackEventDto, eventData);
                await ProcessUserChangeEvent(userChangeEvent);

                break;

            case "message":
                SlackEventFullDto <MessageChannelsEventDto> messageEvent = MapSlackEventObject <MessageChannelsEventDto>(slackEventDto, eventData);
                await ProcessMessageEvent(messageEvent);

                break;

            // manage user technologie through reaction events
            case "reaction_added":
                SlackEventFullDto <ReactionEventDto> reactionAddedEvent = MapSlackEventObject <ReactionEventDto>(slackEventDto, eventData);
                await ProcessReactionAddedEvent(reactionAddedEvent);

                break;

            case "reaction_removed":
                SlackEventFullDto <ReactionEventDto> reactionRemovedEvent = MapSlackEventObject <ReactionEventDto>(slackEventDto, eventData);
                await ProcessReactionRemovedEvent(reactionRemovedEvent);

                break;

            default:
                break;
            }
        }
Exemplo n.º 5
0
        private async Task ProcessReactionRemovedEvent(SlackEventFullDto <ReactionEventDto> slackEventDto)
        {
            if (InvalidTechnologyReaction(slackEventDto))
            {
                return;
            }

            string          reactionName = slackEventDto.Event.Reaction;
            SlackMessageDto messageDto   = await _slackService.ChatRetrieveMessage(slackEventDto.Event.Item.Channel, slackEventDto.Event.Item.Ts);

            MessageDetailsDto initialMessage = messageDto.Messages.First();
            Reaction          reaction       = initialMessage.Reactions?.Find(r => r.Name == reactionName);

            // Only delete technology if it's the last
            // instance of the particular reaction and
            // if the remaining reactions do not belong
            // to privileged members.
            if (reaction != null)
            {
                bool reactedByPrivilegedUsers = reaction.Users.Intersect(_privilegedMembers.Members).Count() > 0;
                bool reactedByOwner           = reaction.Users.Contains(initialMessage.User);

                if (reactedByPrivilegedUsers || reactedByOwner)
                {
                    return;
                }
            }

            string technologyName = reactionName.Remove(reactionName.LastIndexOf("-"));

            // if technology name uses keywords - replace them with appropriate
            // value
            foreach (var word in _keywords)
            {
                technologyName = technologyName.Replace(word.Key, word.Value);
            }

            string     workspaceId       = slackEventDto.TeamId;
            string     workspaceMemberId = slackEventDto.Event.ItemUser;
            UserEntity user = await GetUserEntity(workspaceId, workspaceMemberId);

            TechnologyEntity technology = user.UserTechnologies.Find(tech => tech.Name == technologyName);

            // if technology has been deleted that for
            // some reason doesn't exist in db return
            if (technology == null)
            {
                return;
            }
            await _technologiesStorage.DeleteAsync(technology.Id);
        }
Exemplo n.º 6
0
        private async Task ProcessProjectRecommendationsReaction(SlackEventFullDto <ReactionEventDto> slackEventDto, string workspaceMemberId)
        {
            string     workspaceId = slackEventDto.TeamId;
            UserEntity user        = await GetUserEntity(workspaceId, workspaceMemberId);

            var userTechnologies = user.UserTechnologies.Select(t => t.Name);

            // retrieve all slack member records with matching technologies
            List <ProjectTechnologies> projects = await _technologiesStorage.GetProjectTechnologiesAsync(userTechnologies.ToArray());

            // Remove self technologies and convert to lookup
            if (projects.Count == 0)
            {
                // no projects with associated technologies,
                // no project recommendations available
                await _slackService.ChatPostMessage(_privateIntroChannelId, $"no project recommendations for <@{workspaceMemberId}> and tech {string.Join(", ", userTechnologies)}");

                return;
            }

            var projectsLookup = projects.ToLookup(p => p.Id.ToString(), p => p.TechnologyName);
            // hashset used to filter out duplicates from list in order to generate powerset
            var uniqueTechnologiesList = new HashSet <string>(projects.Select(p => p.TechnologyName)).ToList();
            List <List <string> > technologiesPowerSet = GeneratePowerSet(uniqueTechnologiesList);
            string message = Messages.ProjectRecommendationsBasedOnSkillsMessage(workspaceMemberId);
            Dictionary <string, List <string> > projectRecommendations = FindRecommendations(technologiesPowerSet, projectsLookup);

            foreach (var recommendation in projectRecommendations)
            {
                if (recommendation.Value.Count == 0)
                {
                    // exclude the empty set
                    continue;
                }

                string techList = $"{recommendation.Key}\n";
                string projectRecommendationList = string.Empty;

                foreach (var projectId in recommendation.Value)
                {
                    var project = projects.Find(p => p.Id.ToString() == projectId);
                    projectRecommendationList = $"{projectRecommendationList}\nProject: {project.ProjectName}\nDescription: {project.ProjectDescription}\nWorkspace: {project.ProjectWorkspaceLink}\n\n";
                }

                message = $"{message}{techList}{projectRecommendationList}";
            }

            await _slackService.ChatPostMessage(workspaceMemberId, message, true, false);

            return;
        }
Exemplo n.º 7
0
        private async Task ProcessDeveloperRecommendationsReaction(SlackEventFullDto <ReactionEventDto> slackEventDto, string workspaceMemberId)
        {
            string     workspaceId = slackEventDto.TeamId;
            UserEntity user        = await GetUserEntity(workspaceId, workspaceMemberId);

            var userTechnologies = user.UserTechnologies.Select(t => t.Name);

            // retrieve all slack member records with matching technologies
            List <DeveloperTechnologies> technologies = await _chatAppUserStorage.GetDeveloperTechnologiesAsync(userTechnologies.ToArray());

            // Remove self technologies and convert to lookup
            technologies.RemoveAll(t => t.WorkspaceMemberId == workspaceMemberId);
            if (technologies.Count == 0)
            {
                // no members with technologies, no
                // recommendations available
                await _slackService.ChatPostMessage(_privateIntroChannelId, $"no collaborator recommendations for <@{workspaceMemberId}> and tech {string.Join(", ", userTechnologies)}");

                return;
            }

            var technologiesLookup = technologies.ToLookup(t => t.WorkspaceMemberId, t => t.Name);
            // hashset used to filter out duplicates from list in order to generate powerset
            var uniqueTechnologiesList = new HashSet <string>(technologies.Select(t => t.Name)).ToList();
            List <List <string> > technologiesPowerSet = GeneratePowerSet(uniqueTechnologiesList);
            string message = Messages.DeveloperRecommendationsBasedOnSkillsMessage(workspaceMemberId);
            Dictionary <string, List <string> > developerRecommendations = FindRecommendations(technologiesPowerSet, technologiesLookup);

            foreach (var recommendation in developerRecommendations)
            {
                if (recommendation.Value.Count == 0)
                {
                    // exclude the empty set
                    continue;
                }

                string memberList = string.Empty;
                foreach (var memberId in recommendation.Value)
                {
                    var tech = technologies.Find(t => t.WorkspaceMemberId == memberId);
                    memberList = $"{memberList}\n<@{memberId}>,  <{_mainUrl}/profile/{tech.UserId}|contact>";
                }

                string techList = $"\n{recommendation.Key}\n";
                message = $"{message}{techList}{memberList}\n";
            }

            await _slackService.ChatPostMessage(workspaceMemberId, message, true, false);

            return;
        }
Exemplo n.º 8
0
        private SlackEventFullDto <T> MapSlackEventObject <T>(SlackEventDto slackEventDto, string innerEventData)
        {
            SlackEventFullDto <T> slackEventFullDto = new SlackEventFullDto <T>();

            slackEventFullDto.ApiAppId    = slackEventDto.ApiAppId;
            slackEventFullDto.AuthedUsers = slackEventDto.AuthedUsers;
            slackEventFullDto.EventId     = slackEventDto.EventId;
            slackEventFullDto.EventTime   = slackEventDto.EventTime;
            slackEventFullDto.TeamId      = slackEventDto.TeamId;
            slackEventFullDto.Token       = slackEventDto.Token;
            slackEventFullDto.Type        = slackEventFullDto.Type;

            slackEventFullDto.Event = JsonConvert.DeserializeObject <T>(innerEventData);

            return(slackEventFullDto);
        }
Exemplo n.º 9
0
        private async Task ProcessPrivateProjectsReactionAddedEvent(SlackEventFullDto <ReactionEventDto> slackEventDto)
        {
            string reaction = slackEventDto.Event.Reaction;

            if (reaction == "share")
            {
                SlackMessageDto message = await _slackService.ChatRetrieveMessage(slackEventDto.Event.Item.Channel, slackEventDto.Event.Item.Ts);

                MessageDetailsDto messageDetails = message.Messages.FirstOrDefault();
                // replace unicode left and right quote
                var        text           = messageDetails.Text.Replace('\u201c', '"').Replace('\u201d', '"');
                ProjectDto project        = JsonConvert.DeserializeObject <ProjectDto>(text);
                string     projectMessage = Messages.ProjectPostedMessage(project, $"{_mainUrl}/projects/{project.Id}");
                await _slackService.ChatPostMessage(_projectIdeasChannel, projectMessage, asUser : true, unfurlLinks : false);
            }
        }
Exemplo n.º 10
0
        private async Task ProcessPrivateRegistrationReactionAddedEvent(SlackEventFullDto <ReactionEventDto> slackEventDto)
        {
            string reaction = slackEventDto.Event.Reaction;

            if (reaction == "message")
            {
                string workspaceMemberId = await ParseMemberFromMessage(slackEventDto);
                await ProcessRegistrationMessageReaction(slackEventDto, workspaceMemberId);
            }

            if (reaction == "mail")
            {
                // TODO: Implement mail for members that register via UI but
                // never join slack group
            }
        }
Exemplo n.º 11
0
        private async Task ProcessReactionIntroAddedEvent(SlackEventFullDto <ReactionEventDto> slackEventDto)
        {
            if (InvalidTechnologyReaction(slackEventDto))
            {
                return;
            }

            // get technology from reaction and persist
            string reaction       = slackEventDto.Event.Reaction;
            string technologyName = reaction.Remove(reaction.LastIndexOf("-"));

            // if technology name uses keywords - replace them with appropriate
            // value
            foreach (var word in _keywords)
            {
                technologyName = technologyName.Replace(word.Key, word.Value);
            }

            string     workspaceId       = slackEventDto.TeamId;
            string     workspaceMemberId = slackEventDto.Event.ItemUser;
            UserEntity user = await GetUserEntity(workspaceId, workspaceMemberId);

            TechnologyEntity technology = new TechnologyEntity()
            {
                Name   = technologyName,
                UserId = user.Id
            };

            TechnologyEntity technologyExists = user.UserTechnologies.Find(t => t.Name == technologyName);

            // avoid duplicate entries by checking if
            // technology already exists in db
            if (technologyExists != null)
            {
                return;
            }

            // TODO: Replace with call to update user so that collab
            // suggestions are generated.
            await _technologiesStorage.CreateAsync(technology);

            return;
        }
Exemplo n.º 12
0
        private async Task ProcessPrivateIntroReactionAddedEvent(SlackEventFullDto <ReactionEventDto> slackEventDto)
        {
            string reaction = slackEventDto.Event.Reaction;

            if (reaction == "developers")
            {
                string workspaceMemberId = await ParseMemberFromMessage(slackEventDto);
                await ProcessDeveloperRecommendationsReaction(slackEventDto, workspaceMemberId);

                return;
            }

            if (reaction == "projects")
            {
                string workspaceMemberId = await ParseMemberFromMessage(slackEventDto);
                await ProcessProjectRecommendationsReaction(slackEventDto, workspaceMemberId);

                return;
            }

            return;
        }
Exemplo n.º 13
0
        private async Task ProcessUserChangeEvent(SlackEventFullDto <TeamJoinEventDto> slackEventDto)
        {
            _userStorage = new UserEntity();

            if (slackEventDto.Event.User.IsBot)
            {
                return;
            }

            string workspaceId       = slackEventDto.Event.User.SlackTeamId;
            string workspaceMemberId = slackEventDto.Event.User.SlackId;

            UserEntity existingUser = await GetUserEntity(workspaceId, workspaceMemberId);

            existingUser.Email             = slackEventDto.Event.User.Profile.Email;
            existingUser.Timezone          = slackEventDto.Event.User.Timezone;
            existingUser.Locale            = slackEventDto.Event.User.Locale;
            existingUser.ProfilePictureUrl = slackEventDto.Event.User.Profile.Image192;

            await _userStorage.UpdateAsync(existingUser);

            return;
        }
Exemplo n.º 14
0
        private bool InvalidTechnologyReaction(SlackEventFullDto <ReactionEventDto> slackEventDto)
        {
            bool invalidTechnologyReaction = false;

            // Validate reaction performed in intro channel
            if (slackEventDto.Event.Item.Channel != _introductionChannelId)
            {
                invalidTechnologyReaction = true;
            }

            // Validate reaction is performed on message
            if (slackEventDto.Event.Item.Type != "message")
            {
                invalidTechnologyReaction = true;
            }

            // Validate reaction performed by privileged member
            // or message owner
            string reactingMember = slackEventDto.Event.User;
            string messageOwner   = slackEventDto.Event.ItemUser;

            if (!_privilegedMembers.Members.Contains(reactingMember) && reactingMember != messageOwner)
            {
                invalidTechnologyReaction = true;
            }

            // Validate reaction is of type -tech or -lang
            string reaction = slackEventDto.Event.Reaction;

            if (!reaction.Contains("-lang") && !reaction.Contains("-tech"))
            {
                invalidTechnologyReaction = true;
            }

            return(invalidTechnologyReaction);
        }
Exemplo n.º 15
0
        private async Task ProcessMessageEvent(SlackEventFullDto <MessageChannelsEventDto> slackEventDto)
        {
            // Validate the message was received posted on introduce yourself channel
            _userStorage = new UserEntity();
            if (slackEventDto.Event.Channel != _introductionChannelId)
            {
                return;
            }

            string workspaceId       = slackEventDto.TeamId;
            string workspaceMemberId = slackEventDto.Event.User;

            UserEntity user = await GetUserEntity(workspaceId, workspaceMemberId);

            if (string.IsNullOrWhiteSpace(user.Bio))
            {
                user.Bio = slackEventDto.Event.Text;
                await _userStorage.UpdateAsync(user);

                await _slackService.ChatPostMessage(_privateIntroChannelId, $"<@{workspaceMemberId}> posted intro.");
            }

            return;
        }
Exemplo n.º 16
0
        private async Task ProcessTeamJoinEvent(SlackEventFullDto <TeamJoinEventDto> slackEventDto)
        {
            _userStorage = new UserEntity();

            if (slackEventDto.Event.User.IsBot)
            {
                return;
            }

            string workspaceId       = slackEventDto.Event.User.SlackTeamId;
            string workspaceMemberId = slackEventDto.Event.User.SlackId;

            SlackUserInfoDto slackUserInfoDto = await _slackService.GetSlackUserInfo(workspaceMemberId);

            string chatAppMemberEmail = slackUserInfoDto.User.Profile.Email;

            UserEntity existingUser = await _userStorage.FindAsync(u => u.Email == chatAppMemberEmail);

            if (existingUser == null)
            {
                // User email is not registered to frontend so
                // register user and also associate new chat app
                // user to new user entity
                string username = await GenerateUsername();

                string     password   = GenerateTemporaryPassword();
                User       user       = new User(username, slackUserInfoDto.User.Profile.Email, slackUserInfoDto.User.Timezone, slackUserInfoDto.User.Locale, true, "", slackEventDto.Event.User.Profile.Image192);
                UserEntity userEntity = _mapper.Map <UserEntity>(user);
                userEntity.HashedPassword = HashPassword(user, password);


                UserEntity newUser = await _userStorage.CreateAsync(userEntity);

                ChatAppUserEntity newChatAppUser = new ChatAppUserEntity()
                {
                    WorkspaceId       = workspaceId,
                    WorkspaceMemberId = workspaceMemberId,
                    UserId            = newUser.Id,
                };

                await _chatAppUserStorage.CreateAsync(newChatAppUser);

                string message = Messages.OnboardingMessage(workspaceMemberId, user.Email, password);
                await _slackService.ChatPostMessage(workspaceMemberId, message);

                await _slackService.ChatPostMessage(_privateRegistrationChannelId, $"<@{workspaceMemberId}>, `{username}`, `{user.Email}` joined the slack group. Send follow up.");
            }
            else
            {
                // User email is already register to frontend
                // associate new chat user record with existing user
                // TODO: Picture may be overwritten here if member
                // previously uploaded a profile pic through frontend,
                // remove once we have profile upload functionality on UI.
                existingUser.ProfilePictureUrl = slackEventDto.Event.User.Profile.Image192;

                ChatAppUserEntity newChatAppUser = new ChatAppUserEntity()
                {
                    WorkspaceId       = workspaceId,
                    WorkspaceMemberId = workspaceMemberId,
                    UserId            = existingUser.Id,
                };

                await _userStorage.UpdateAsync(existingUser);

                await _chatAppUserStorage.CreateAsync(newChatAppUser);

                string message = Messages.OnboardingRegisteredMessage(workspaceMemberId);
                await _slackService.ChatPostMessage(workspaceMemberId, message);

                await _slackService.ChatPostMessage(_privateRegistrationChannelId, $"<@{workspaceMemberId}>, `{existingUser.Username}`, `{existingUser.Username}` registered and joined the slack group. Send follow up.");
            }

            return;
        }