Exemplo n.º 1
0
        private Task HandleMessageReceived(SocketMessage message)
        {
            TaskHelper.FireForget(async() =>
            {
                try
                {
                    var channel = message.Channel as SocketTextChannel;
                    if (channel == null)
                    {
                        return;
                    }

                    var user = message.Author as IGuildUser;
                    if (user == null)
                    {
                        return;
                    }

                    if (user.IsBot)
                    {
                        return;
                    }

                    var settings = await _settings.Read <RolesSettings>(channel.Guild.Id, false);
                    if (settings == null || settings.RoleChannel != channel.Id)
                    {
                        return;
                    }

                    var logger = _logger.WithScope(message);
                    if (!channel.Guild.CurrentUser.GetPermissions(channel).SendMessages)
                    {
                        logger.LogInformation("Can't assign role because of missing SendMessage permissions");
                        return;
                    }

                    using (await _roleAssignmentUserMutex.ClaimAsync(user.Id)) // To prevent race-conditions when spamming roles
                    {
                        try
                        {
                            logger.LogInformation("Received role channel message {MessageContent}");

                            string msgContent = message.Content.Trim();
                            bool remove       = false;
                            if (msgContent.StartsWith("-"))
                            {
                                msgContent = msgContent.Substring(1);
                                remove     = true;
                            }

                            msgContent = msgContent.TrimStart('-', '+');
                            msgContent = msgContent.Trim();

                            // First try to match an alias case sensitive
                            var roleAar = settings.AssignableRoles.FirstOrDefault(x => x.Names.Any(y => string.Compare(y, msgContent) == 0) && channel.Guild.GetRole(x.RoleId) != null);

                            // Then try current role names case sensitive
                            if (roleAar == null)
                            {
                                roleAar = settings.AssignableRoles
                                          .Select(x => (Aar: x, Role: channel.Guild.GetRole(x.RoleId)))
                                          .FirstOrDefault(x => x.Role != null && string.Compare(x.Role.Name, msgContent) == 0)
                                          .Aar;
                            }

                            // Then alias case insensitive
                            if (roleAar == null)
                            {
                                roleAar = settings.AssignableRoles.FirstOrDefault(x => x.Names.Any(y => string.Compare(y, msgContent, true, GlobalDefinitions.Culture) == 0) && channel.Guild.GetRole(x.RoleId) != null);
                            }

                            // And current role names case insensitive
                            if (roleAar == null)
                            {
                                roleAar = settings.AssignableRoles
                                          .Select(x => (Aar: x, Role: channel.Guild.GetRole(x.RoleId)))
                                          .FirstOrDefault(x => x.Role != null && string.Compare(x.Role.Name, msgContent, true, GlobalDefinitions.Culture) == 0)
                                          .Aar;
                            }

                            if (roleAar == null)
                            {
                                var response = await _communicator.CommandReplyError(message.Channel, "This is not a self-assignable role.");
                                if (settings.ClearRoleChannel)
                                {
                                    response.First().DeleteAfter(3);
                                }

                                return;
                            }

                            // Check group settings
                            if (!remove && roleAar.Groups.Any())
                            {
                                var existingAssignableRoles = settings.AssignableRoles
                                                              .Where(x => user.RoleIds.Contains(x.RoleId) || user.RoleIds.Contains(x.SecondaryId))
                                                              .ToList();

                                foreach (var existingAssignableRole in existingAssignableRoles)
                                {
                                    foreach (var commonGroup in existingAssignableRole.Groups.Intersect(roleAar.Groups))
                                    {
                                        if (!settings.GroupSettings.TryGetValue(commonGroup, out var groupSetting))
                                        {
                                            continue;
                                        }

                                        if (groupSetting.Limit > 0)
                                        {
                                            var groupRoleCount = existingAssignableRoles.Count(x => x.Groups.Contains(commonGroup));
                                            if (groupRoleCount >= groupSetting.Limit)
                                            {
                                                var response = await _communicator.CommandReplyError(message.Channel, $"You can't add any more roles from group `{commonGroup}`.");
                                                if (settings.ClearRoleChannel)
                                                {
                                                    response.First().DeleteAfter(3);
                                                }

                                                return;
                                            }
                                        }
                                    }
                                }
                            }

                            var addRoles    = new List <ulong>();
                            var removeRoles = new List <ulong>();
                            if (roleAar.SecondaryId != 0)
                            {
                                // Bias role (more complex logic)
                                if (remove)
                                {
                                    // Remove also secondary
                                    removeRoles.Add(roleAar.RoleId);
                                    removeRoles.Add(roleAar.SecondaryId);
                                }
                                else
                                {
                                    var primaryRoles = settings.AssignableRoles.Where(x => x.SecondaryId != 0);

                                    // If the user doesn't have the primary already
                                    if (!user.RoleIds.Any(x => x == roleAar.RoleId))
                                    {
                                        // Check if user has any primary role
                                        if (user.RoleIds.Any(x => primaryRoles.Any(y => y.RoleId == x)))
                                        {
                                            // Assign secondary
                                            addRoles.Add(roleAar.SecondaryId);
                                        }
                                        else
                                        {
                                            // Assign primary and delete secondary
                                            addRoles.Add(roleAar.RoleId);
                                            removeRoles.Add(roleAar.SecondaryId);
                                        }
                                    }
                                    else
                                    {
                                        removeRoles.Add(roleAar.SecondaryId); // Try to remove secondary just in case (cleanup)
                                    }
                                }
                            }
                            else
                            {
                                // Regular role
                                if (remove)
                                {
                                    removeRoles.Add(roleAar.RoleId);
                                }
                                else
                                {
                                    addRoles.Add(roleAar.RoleId);
                                }
                            }

                            try
                            {
                                if (addRoles.Count > 0)
                                {
                                    await user.AddRolesAsync(addRoles.Select(x => channel.Guild.GetRole(x)).Where(x => x != null));
                                }

                                if (removeRoles.Count > 0)
                                {
                                    await user.RemoveRolesAsync(removeRoles.Select(x => channel.Guild.GetRole(x)).Where(x => x != null));
                                }

                                var guildRole = channel.Guild.GetRole(roleAar.RoleId);
                                if (guildRole != null)
                                {
                                    var response = await _communicator.SendMessage(message.Channel, string.Format(remove ? "You no longer have the **{0}** role." : "You now have the **{0}** role.", guildRole.Name));
                                    if (settings.ClearRoleChannel)
                                    {
                                        response.First().DeleteAfter(3);
                                    }
                                }
                            }
                            catch (Discord.Net.HttpException ex) when(ex.HttpCode == HttpStatusCode.Unauthorized || ex.HttpCode == HttpStatusCode.Forbidden)
                            {
                                await _communicator.CommandReplyError(message.Channel, "The bot doesn't have the necessary permissions. If you're the admin, please make sure the bot can Manage Roles and all the assignable roles are placed below the bot's highest role.");
                            }
                        }
                        catch (Exception ex)
                        {
                            logger.LogError(ex, "Failed to assign roles");
                            await _communicator.CommandReplyGenericFailure(message.Channel);
                        }
                        finally
                        {
                            if (settings.ClearRoleChannel)
                            {
                                message.DeleteAfter(3);
                            }
                        }
                    }
                }
                catch (Exception ex)
                {
                    _logger.WithScope(message).LogError(ex, "Failed to process potential role assignment message");
                }
            });

            return(Task.CompletedTask);
        }