public async ValueTask <AdminCommandResult> SendMessageAsync(CachedTextChannel channel, [Remainder] string text = null)
        {
            if (string.IsNullOrWhiteSpace(text) && Context.Message.Attachments.Count == 0)
            {
                return(CommandErrorLocalized("utility_send_empty"));
            }

            if (!string.IsNullOrWhiteSpace(text))
            {
                text = await text.FormatPlaceHoldersAsync(Context, random : Random);
            }

            var file     = new MemoryStream();
            var filename = string.Empty;

            if (Context.Message.Attachments.FirstOrDefault() is { } attachment)
            {
                await using var stream = await Http.GetStreamAsync(attachment.Url);

                await stream.CopyToAsync(file);

                file.Seek(0, SeekOrigin.Begin);
                filename = attachment.FileName;
            }

            if (JsonEmbed.TryParse(text, out var embed))
            {
                await channel.SendMessageAsync(
                    !string.IsNullOrWhiteSpace(filename)?new LocalAttachment(file, filename) : null, embed.Text,
                    embed : embed.ToLocalEmbed());

                return(CommandSuccess());
            }

            if (!string.IsNullOrWhiteSpace(filename))
            {
                await channel.SendMessageAsync(new LocalAttachment(file, filename), text);
            }
            else
            {
                await channel.SendMessageAsync(text);
            }
            return(CommandSuccess());
        }
        public async Task SendLoggingRevocationEmbedAsync(RevocablePunishment punishment, IUser target, IUser moderator,
                                                          CachedTextChannel logChannel, LocalizedLanguage language)
        {
            var typeName = punishment.GetType().Name.ToLower();
            var builder  = new LocalEmbedBuilder().WithWarnColor()
                           .WithTitle(_localization.Localize(language, $"punishment_{typeName}") +
                                      $" - {_localization.Localize(language, "punishment_case", punishment.Id)}")
                           .WithDescription(_localization.Localize(language, $"punishment_{typeName}_revoke_description_guild",
                                                                   $"**{target}** (`{target.Id}`)"))
                           .AddField(_localization.Localize(language, "title_reason"),
                                     punishment.RevocationReason ?? _localization.Localize(language, "punishment_noreason"))
                           .WithFooter(_localization.Localize(language, "punishment_moderator", moderator.Tag),
                                       moderator.GetAvatarUrl())
                           .WithTimestamp(punishment.RevokedAt ?? DateTimeOffset.UtcNow);

            if (punishment is Mute channelMute && channelMute.ChannelId.HasValue)
            {
                builder.AddField(_localization.Localize(language, "punishment_mute_channel"),
                                 _client.GetGuild(punishment.GuildId).GetTextChannel(channelMute.ChannelId.Value)?.Mention ?? "???");
            }

            await logChannel.SendMessageAsync(embed : builder.Build());
        }
        private async Task <IUserMessage> SendLoggingEmbedAsync(Punishment punishment, IUser target, IUser moderator, Punishment additionalPunishment, CachedTextChannel logChannel, LocalizedLanguage language)
        {
            var typeName = punishment.GetType().Name.ToLower();
            var builder  = new LocalEmbedBuilder()
                           .WithErrorColor()
                           .WithTitle(_localization.Localize(language, $"punishment_{typeName}") +
                                      $" - {_localization.Localize(language, "punishment_case", punishment.Id)}")
                           .AddField(_localization.Localize(language, "title_reason"),
                                     punishment.Reason ?? _localization.Localize(language, "punishment_needsreason",
                                                                                 Markdown.Code(
                                                                                     $"{_config.DefaultPrefix}reason {punishment.Id} [{_localization.Localize(language, "title_reason").ToLower()}]")))
                           .WithFooter(_localization.Localize(language, "punishment_moderator", moderator.Tag),
                                       moderator.GetAvatarUrl())
                           .WithTimestamp(punishment.CreatedAt);

            builder = punishment switch
            {
                Ban ban => builder.AddField(
                    _localization.Localize(language, "punishment_duration"), ban.Duration.HasValue
                        ? ban.Duration.Value.Humanize(minUnit: TimeUnit.Second, culture: language.Culture)
                        : _localization.Localize(language, "punishment_permanent")),
                Mute mute => builder.AddField(
                    _localization.Localize(language, "punishment_duration"), mute.Duration.HasValue
                        ? mute.Duration.Value.Humanize(minUnit: TimeUnit.Second, culture: language.Culture)
                        : _localization.Localize(language, "punishment_permanent")),
                _ => builder
            };

            if (punishment is Mute channelMute && channelMute.ChannelId.HasValue)
            {
                builder.AddField(_localization.Localize(language, "punishment_mute_channel"),
                                 _client.GetGuild(punishment.GuildId).GetTextChannel(channelMute.ChannelId.Value).Mention);
            }

            if (punishment is Warning)
            {
                using var ctx = new AdminDatabaseContext(_provider);

                var warningCount = await ctx.Punishments.OfType <Warning>().CountAsync(x =>
                                                                                       x.TargetId == target.Id && x.GuildId == punishment.GuildId && !x.RevokedAt.HasValue);

                builder.WithDescription(_localization.Localize(language, "punishment_warning_description_guild", $"**{target}** (`{target.Id}`)",
                                                               Markdown.Bold(warningCount.ToOrdinalWords(language.Culture))));

                if (!(additionalPunishment is null))
                {
                    builder.AddField(FormatAdditionalPunishment(additionalPunishment, language));
                }
            }
            else
            {
                builder.WithDescription(_localization.Localize(language, $"punishment_{typeName}_description_guild",
                                                               $"**{target}** (`{target.Id}`)"));
            }

            if (punishment.Format != ImageFormat.Default)
            {
                // TODO: Copying stream - is it necessary?
                var image = new MemoryStream(punishment.Image.ToArray());
                builder.WithImageUrl($"attachment://attachment.{punishment.Format.ToString().ToLower()}");
                return(await logChannel.SendMessageAsync(new LocalAttachment(image,
                                                                             $"attachment.{punishment.Format.ToString().ToLower()}"), embed : builder.Build()));
            }

            return(await logChannel.SendMessageAsync(embed : builder.Build()));
        }
        public async ValueTask <AdminCommandResult> ReplyToModmailAsync(int id,
                                                                        [Remainder, MustBe(StringLength.ShorterThan, LocalEmbedBuilder.MAX_DESCRIPTION_LENGTH)] string message)
        {
            var modmail = await Context.Database.Modmails.FindAsync(id);

            if (modmail is null)
            {
                return(CommandErrorLocalized("modmail_notfound"));
            }

            if (Context.IsPrivate && modmail.UserId != Context.User.Id)
            {
                return(CommandErrorLocalized("modmail_notfound"));
            }

            if (!Context.IsPrivate && modmail.GuildId != Context.Guild.Id)
            {
                return(CommandErrorLocalized("modmail_notfound"));
            }

            CachedTextChannel loggingChannel = null;

            if (Context.IsPrivate)
            {
                if (!(await Context.Database.GetLoggingChannelAsync(modmail.GuildId, LogType.Modmail) is CachedTextChannel
                      logChannel))
                {
                    return(CommandErrorLocalized("requireloggingchannel_notfound", args: LogType.Modmail));
                }

                if (modmail.ClosedBy.HasValue)
                {
                    return(modmail.ClosedBy.Value == ModmailTarget.User
                        ? CommandErrorLocalized("modmail_closed_user")
                        : CommandErrorLocalized("modmail_closed_guild"));
                }

                loggingChannel = logChannel;
            }
            else
            {
                if (modmail.ClosedBy.HasValue)
                {
                    return(modmail.ClosedBy.Value == ModmailTarget.User
                        ? CommandErrorLocalized("modmail_closed_user_guild")
                        : CommandErrorLocalized("modmail_closed_guild"));
                }
            }


            Context.Database.ModmailMessages.Add(new ModmailMessage(Context.IsPrivate ? ModmailTarget.User : ModmailTarget.Modteam, message, modmail));
            await Context.Database.SaveChangesAsync();

            if (Context.IsPrivate)
            {
                await loggingChannel.SendMessageAsync(embed : new LocalEmbedBuilder()
                                                      .WithColor(new Color(0x8ED0FF))
                                                      .WithAuthor(new LocalEmbedAuthorBuilder
                {
                    IconUrl =
                        modmail.IsAnonymous ? Discord.GetDefaultUserAvatarUrl(Context.User.Discriminator) : Context.User.GetAvatarUrl(),
                    Name = modmail.IsAnonymous ? Context.Localize("modmail_anonymous") : Context.User.Tag.Sanitize()
                })
                                                      .WithDescription(message)
                                                      .WithTitle(Context.Localize("modmail_title", modmail.Id))
                                                      .WithFooter(Context.Localize("modmail_reply_command",
                                                                                   Markdown.Code($"{Context.Prefix}mm reply {modmail.Id} [...]"),
                                                                                   Markdown.Code($"{Context.Prefix}mm close {modmail.Id}")))
                                                      .WithTimestamp(DateTimeOffset.UtcNow)
                                                      .Build());
            }
            else
            {
                _ = Task.Run(async() =>
                {
                    var user = await Context.Client.GetOrDownloadUserAsync(modmail.UserId);
                    _        = user.SendMessageAsync(embed: new LocalEmbedBuilder()
                                                     .WithColor(new Color(0x8ED0FF))
                                                     .WithAuthor(new LocalEmbedAuthorBuilder
                    {
                        IconUrl = Context.Guild.GetIconUrl(),
                        Name    = $"{Context.Guild.Name} modteam"
                    })
                                                     .WithDescription(message)
                                                     .WithTitle(Context.Localize("modmail_title", modmail.Id))
                                                     .WithFooter(Context.Localize("modmail_reply_command",
                                                                                  Markdown.Code($"{Context.Prefix}mm reply {modmail.Id} [...]"),
                                                                                  Markdown.Code($"{Context.Prefix}mm close {modmail.Id}")))
                                                     .WithTimestamp(DateTimeOffset.UtcNow)
                                                     .Build());
                });
            }

            return(CommandSuccessLocalized("modmail_reply_success"));
        }