Example #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);
        }
Example #2
0
        private async Task ExecuteCommandAsync(int counter, Guid correlationId, ILogger logger, CommandRegistrationFindResult findResult, ICommand command, Stopwatch stopwatch, TimeSpan verificationElapsed, TimeSpan parsingElapsed, TimeSpan gatewayPing)
        {
            IDisposable typingState = null;

            if (findResult.Registration.Flags.HasFlag(CommandFlags.TypingIndicator))
            {
                typingState = command.Message.Channel.EnterTypingState();
            }

            var result = CommandResult.Failed;

            try
            {
                using (var scope = _clientServiceProvider.CreateScope())
                {
                    var module        = scope.ServiceProvider.GetRequiredService(findResult.Registration.ModuleType);
                    var commandLogger = _loggerFactory.CreateLogger(findResult.Registration.ModuleType)
                                        .WithCommandScope(command.Message, correlationId, findResult.Registration, findResult.Usage);

                    await findResult.Registration.Handler.Invoke(module, command, commandLogger, default); // TODO: cancellation
                }

                result = CommandResult.Succeeded;
            }
            catch (Exceptions.AbortException ex)
            {
                if (ex.Pages != null)
                {
                    await _communicator.CommandReply(command.Message.Channel, ex.Pages);
                }
                else if (!string.IsNullOrEmpty(ex.Message))
                {
                    await _communicator.CommandReply(command.Message.Channel, ex.Message);
                }

                result = CommandResult.Succeeded;
            }
            catch (Exceptions.IncorrectParametersCommandException ex)
            {
                await _communicator.CommandReplyIncorrectParameters(command.Message.Channel, findResult.Registration, ex.Message, findResult.Prefix, ex.ShowUsage);
            }
            catch (Exceptions.UnclearParametersCommandException ex)
            {
                await _communicator.CommandReplyUnclearParameters(command.Message.Channel, findResult.Registration, ex.Message, findResult.Prefix, ex.ShowUsage);
            }
            catch (Exceptions.MissingPermissionsException ex)
            {
                await _communicator.CommandReplyMissingPermissions(command.Message.Channel, findResult.Registration, ex.Permissions, ex.Message);
            }
            catch (Exceptions.MissingBotPermissionsException ex)
            {
                await _communicator.CommandReplyMissingBotPermissions(command.Message.Channel, findResult.Registration, ex.Permissions);
            }
            catch (Exceptions.MissingBotChannelPermissionsException ex)
            {
                await _communicator.CommandReplyMissingBotPermissions(command.Message.Channel, findResult.Registration, ex.Permissions);
            }
            catch (Exceptions.CommandException ex)
            {
                await _communicator.CommandReplyError(command.Message.Channel, ex.Message);
            }
            catch (Discord.Net.HttpException ex) when(ex.DiscordCode == 50001)
            {
                await _communicator.CommandReplyMissingBotAccess(command.Message.Channel, findResult.Registration);
            }
            catch (Discord.Net.HttpException ex) when(ex.DiscordCode == 50013)
            {
                await _communicator.CommandReplyMissingBotPermissions(command.Message.Channel, findResult.Registration);
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "Exception encountered while processing command {Command} (nr: {CommandCounter}) in module {Module}", findResult.Registration.PrimaryUsage.InvokeUsage, counter, findResult.Registration.ModuleType);
                await _communicator.CommandReplyGenericFailure(command.Message.Channel);
            }
            finally
            {
                if (typingState != null)
                {
                    typingState.Dispose();
                }
            }

            var totalElapsed = stopwatch.Elapsed;

            logger.LogInformation("Command {CommandCounter} {Result} in {TotalElapsed:F3}s (v: {VerificationElapsed:F3}s, p: {ParsingElapsed:F3}s, e: {ExecutionElapsed:F3}s, g: {GatewayDelay:F3}s)", counter, result, totalElapsed.TotalSeconds, verificationElapsed.TotalSeconds, (parsingElapsed - verificationElapsed).TotalSeconds, (totalElapsed - parsingElapsed).TotalSeconds, gatewayPing.TotalSeconds);
        }