public async Task Members(CommandContext ctx)
            {
                if (!await CheckLock.WaitAsync(0).ConfigureAwait(false))
                {
                    await ctx.Channel.SendMessageAsync("Another check is already in progress").ConfigureAwait(false);

                    return;
                }

                try
                {
                    await ctx.ReactWithAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);

                    var members = GetMembers(ctx.Client);
                    await using var compressedResult = Config.MemoryStreamManager.GetStream();
                    await using var memoryStream     = Config.MemoryStreamManager.GetStream();
                    await using var writer           = new StreamWriter(memoryStream, new UTF8Encoding(false), 4096, true);
                    foreach (var member in members)
                    {
                        await writer.WriteLineAsync($"{member.Username}\t{member.Nickname}\t{member.JoinedAt:O}\t{(string.Join(',', member.Roles.Select(r => r.Name)))}").ConfigureAwait(false);
                    }
                    await writer.FlushAsync().ConfigureAwait(false);

                    memoryStream.Seek(0, SeekOrigin.Begin);
                    if (memoryStream.Length <= Config.AttachmentSizeLimit)
                    {
                        await ctx.Channel.SendMessageAsync(new DiscordMessageBuilder().WithFile("names.txt", memoryStream)).ConfigureAwait(false);

                        return;
                    }

                    await using var gzip = new GZipStream(compressedResult, CompressionLevel.Optimal, true);
                    await memoryStream.CopyToAsync(gzip).ConfigureAwait(false);

                    await gzip.FlushAsync().ConfigureAwait(false);

                    compressedResult.Seek(0, SeekOrigin.Begin);
                    if (compressedResult.Length <= Config.AttachmentSizeLimit)
                    {
                        await ctx.Channel.SendMessageAsync(new DiscordMessageBuilder().WithFile("names.txt.gz", compressedResult)).ConfigureAwait(false);
                    }
                    else
                    {
                        await ctx.Channel.SendMessageAsync($"Dump is too large: {compressedResult.Length} bytes").ConfigureAwait(false);
                    }
                }
                catch (Exception e)
                {
                    Config.Log.Warn(e, "Failed to dump guild members");
                    await ctx.ReactWithAsync(Config.Reactions.Failure, "Failed to dump guild members").ConfigureAwait(false);
                }
                finally
                {
                    CheckLock.Release();
                    await ctx.RemoveReactionAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);
                }
            }
Exemple #2
0
            public async Task Zalgo(CommandContext ctx)
            {
                if (!await CheckLock.WaitAsync(0).ConfigureAwait(false))
                {
                    await ctx.RespondAsync("Another check is already in progress").ConfigureAwait(false);

                    return;
                }

                try
                {
                    await ctx.ReactWithAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);

                    var result       = new StringBuilder("List of users who do not meet Rule #7 requirements:").AppendLine();
                    var headerLength = result.Length;
                    var members      = GetMembers(ctx.Client);
                    foreach (var member in members)
                    {
                        if (UsernameZalgoMonitor.NeedsRename(member.DisplayName))
                        {
                            result.AppendLine($"{member.Mention} please change your nickname according to Rule #7");
                        }
                    }
                    if (result.Length == headerLength)
                    {
                        result.AppendLine("No naughty users 🎉");
                    }
                    await ctx.SendAutosplitMessageAsync(result, blockStart : "", blockEnd : "").ConfigureAwait(false);
                }
                catch (Exception e)
                {
                    var msg = "Failed to check display names for zalgo for all guild members";
                    Config.Log.Warn(e, msg);
                    await ctx.ReactWithAsync(Config.Reactions.Failure, msg).ConfigureAwait(false);
                }
                finally
                {
                    CheckLock.Release();
                    await ctx.RemoveReactionAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);
                }
            }
            public async Task Zalgo(CommandContext ctx)
            {
                if (!await CheckLock.WaitAsync(0).ConfigureAwait(false))
                {
                    await ctx.Channel.SendMessageAsync("Another check is already in progress").ConfigureAwait(false);

                    return;
                }

                try
                {
                    await ctx.ReactWithAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);

                    var result       = new StringBuilder("List of users who do not meet Rule #7 requirements:").AppendLine();
                    var headerLength = result.Length;
                    var members      = GetMembers(ctx.Client);
                    foreach (var member in members)
                    {
                        try
                        {
                            var displayName = member.DisplayName;
                            if (!UsernameZalgoMonitor.NeedsRename(displayName))
                            {
                                continue;
                            }

                            var nickname = UsernameZalgoMonitor.StripZalgo(displayName, member.Username, member.Id).Sanitize();
                            try
                            {
                                await member.ModifyAsync(m => m.Nickname = nickname).ConfigureAwait(false);

                                result.AppendLine($"{member.Mention} have been automatically renamed from {displayName} to {nickname} according Rule #7");
                            }
                            catch (Exception e)
                            {
                                Config.Log.Warn(e, $"Failed to rename member {member.GetUsernameWithNickname()}");
                                result.AppendLine($"{member.Mention} please change your nickname according to Rule #7 (suggestion: {nickname})");
                            }
                        }
                        catch (Exception e)
                        {
                            Config.Log.Warn(e, $"Failed to audit username for {member.Id}");
                        }
                    }
                    if (result.Length == headerLength)
                    {
                        result.AppendLine("No naughty users 🎉");
                    }
                    await ctx.SendAutosplitMessageAsync(result, blockStart : "", blockEnd : "").ConfigureAwait(false);
                }
                catch (Exception e)
                {
                    var msg = "Failed to check display names for zalgo for all guild members";
                    Config.Log.Warn(e, msg);
                    await ctx.ReactWithAsync(Config.Reactions.Failure, msg).ConfigureAwait(false);
                }
                finally
                {
                    CheckLock.Release();
                    await ctx.RemoveReactionAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);
                }
            }
            private static async void SpoofingCheck(CommandContext ctx)
            {
                if (!CheckLock.Wait(0))
                {
                    await ctx.Channel.SendMessageAsync("Another check is already in progress").ConfigureAwait(false);

                    return;
                }

                try
                {
                    await ctx.ReactWithAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);

                    var members = GetMembers(ctx.Client);
                    if (members.Count < 2)
                    {
                        return;
                    }

                    var result         = new StringBuilder("List of potential impersonators → victims:").AppendLine();
                    var headerLength   = result.Length;
                    var checkedMembers = new List <DiscordMember>(members.Count)
                    {
                        members[0]
                    };
                    for (var i = 1; i < members.Count; i++)
                    {
                        var member  = members[i];
                        var victims = UsernameSpoofMonitor.GetPotentialVictims(ctx.Client, member, true, true, checkedMembers);
                        if (victims.Any())
                        {
                            result.Append(member.GetMentionWithNickname()).Append(" → ").AppendLine(string.Join(", ", victims.Select(m => m.GetMentionWithNickname())));
                        }
                        checkedMembers.Add(member);
                    }

                    await using var compressedStream   = Config.MemoryStreamManager.GetStream();
                    await using var uncompressedStream = Config.MemoryStreamManager.GetStream();
                    await using (var writer = new StreamWriter(uncompressedStream, new UTF8Encoding(false), 4096, true))
                    {
                        await writer.WriteAsync(result.ToString()).ConfigureAwait(false);

                        await writer.FlushAsync().ConfigureAwait(false);
                    }
                    uncompressedStream.Seek(0, SeekOrigin.Begin);
                    if (result.Length <= headerLength)
                    {
                        await ctx.Channel.SendMessageAsync("No potential name spoofing was detected").ConfigureAwait(false);

                        return;
                    }

                    if (uncompressedStream.Length <= Config.AttachmentSizeLimit)
                    {
                        await ctx.Channel.SendMessageAsync(new DiscordMessageBuilder().WithFile("spoofing_check_results.txt", uncompressedStream)).ConfigureAwait(false);

                        return;
                    }

                    await using (var gzip = new GZipStream(compressedStream, CompressionLevel.Optimal, true))
                    {
                        await uncompressedStream.CopyToAsync(gzip).ConfigureAwait(false);

                        gzip.Flush();
                    }
                    compressedStream.Seek(0, SeekOrigin.Begin);
                    if (compressedStream.Length <= Config.AttachmentSizeLimit)
                    {
                        await ctx.Channel.SendMessageAsync(new DiscordMessageBuilder().WithFile("spoofing_check_results.txt.gz", compressedStream)).ConfigureAwait(false);
                    }
                    else
                    {
                        await ctx.Channel.SendMessageAsync($"Dump is too large: {compressedStream.Length} bytes").ConfigureAwait(false);
                    }
                }
                catch (Exception e)
                {
                    Config.Log.Error(e);
                    //should be extra careful, as async void will run on a thread pull, and will terminate the whole application with an uncaught exception
                    try { await ctx.ReactWithAsync(Config.Reactions.Failure, "(X_X)").ConfigureAwait(false); } catch { }
                }
                finally
                {
                    CheckLock.Release();
                    await ctx.RemoveReactionAsync(Config.Reactions.PleaseWait).ConfigureAwait(false);
                }
            }