<commit_before>package me.mrkirby153.KirBot.command.executors.admin

import com.mrkirby153.bfs.sql.QueryBuilder
import me.mrkirby153.KirBot.Bot
import me.mrkirby153.KirBot.command.CommandCategory
import me.mrkirby153.KirBot.command.annotations.Command
import me.mrkirby153.KirBot.command.annotations.CommandDescription
import me.mrkirby153.KirBot.command.annotations.IgnoreWhitelist
import me.mrkirby153.KirBot.command.annotations.LogInModlogs
import me.mrkirby153.KirBot.command.args.CommandContext
import me.mrkirby153.KirBot.listener.WaitUtils
import me.mrkirby153.KirBot.user.CLEARANCE_MOD
import me.mrkirby153.KirBot.utils.Context
import me.mrkirby153.KirBot.utils.checkPermissions
import me.mrkirby153.kcutils.Time
import net.dv8tion.jda.api.Permission
import java.util.concurrent.CompletableFuture

class CommandClean {

    private val confirmAmount = 100

    @Command(name = "all", arguments = ["[amount:int]"], parent = "clean", clearance = CLEARANCE_MOD, category = CommandCategory.MODERATION)
    @CommandDescription("Cleans messages from everyone in the current channel")
    @LogInModlogs
    @IgnoreWhitelist
    fun allClean(context: Context, cmdContext: CommandContext) {
        val builder = cmdContext.getBuilder()
        builder.where("channel", context.channel.id)
        purgeMessages(context, builder.queryIds())
    }

    @Command(name = "user", arguments = ["<user:snowflake>", "[amount:int]"], parent = "clean", clearance = CLEARANCE_MOD, category = CommandCategory.MODERATION)
    @CommandDescription("Cleans messages from a specific user in the current channel")
    @LogInModlogs
    @IgnoreWhitelist
    fun userClean(context: Context, cmdContext: CommandContext) {
        val builder = cmdContext.getBuilder()
        builder.where("channel", context.channel.id)
        builder.where("author", cmdContext.get<String>("user"))
        purgeMessages(context, builder.queryIds())
    }

    @Command(name = "bots", arguments = ["[amount:int]"], parent = "clean", clearance = CLEARANCE_MOD, category = CommandCategory.MODERATION)
    @CommandDescription("Cleans messages sent by bots in the current channel")
    @LogInModlogs
    @IgnoreWhitelist
    fun botClean(context: Context, cmdContext: CommandContext) {
        val builder = cmdContext.getBuilder()
        builder.where("channel", context.channel.id)
        builder.leftJoin("seen_users", "server_messages.author", "=", "seen_users.id")
        builder.where("bot", true)
        purgeMessages(context, builder.queryIds())
    }

    @Command(name = "everywhere", arguments = ["<user:snowflake>", "[amount:int]"], parent="clean", clearance = CLEARANCE_MOD, category = CommandCategory.MODERATION)
    @LogInModlogs
    @CommandDescription("Cleans messages sent by the given user across all channels")
    @IgnoreWhitelist
    fun cleanEverywhere(context: Context, cmdContext: CommandContext) {
        val builder = cmdContext.getBuilder()
        builder.where("server_id", context.guild.id)
        builder.where("author", cmdContext.getNotNull("user"))
        purgeMessages(context, builder.queryIds())
    }

    @Command(name = "between", arguments=["<first:snowflake>", "<last:snowflake>"], parent="clean", clearance = CLEARANCE_MOD, category = CommandCategory.MODERATION)
    @LogInModlogs
    @CommandDescription("Cleans messages sent by all users between the given snowflakes")
    @IgnoreWhitelist
    fun cleanBetween(context: Context, cmdContext: CommandContext) {
        val builder = getBuilder()
        builder.where("id", ">=", cmdContext.getNotNull("first"))
        builder.where("id", "<=", cmdContext.getNotNull("last"))
        builder.where("channel", context.channel.id)
        purgeMessages(context, builder.queryIds())
    }
    private fun purgeMessages(context: Context, messages: List<String>) {
        fun doClean() {
            val m = context.channel.sendMessage(":repeat: Processing...").complete()
            val start = System.currentTimeMillis()
            val buckets = mutableMapOf<String, MutableList<String>>()
            val builder = QueryBuilder()
            builder.table("server_messages")
            builder.whereIn("id", messages.toTypedArray())
            builder.select("id", "channel")
            builder.where("deleted", false)
            builder.query().forEach {
                buckets.getOrPut(it.getString("channel"), { mutableListOf() }).add(
                        it.getString("id"))
            }

            Bot.LOG.debug("Purging ${messages.size} messages across ${buckets.size} channels")
            val cf = mutableListOf<CompletableFuture<*>>()
            var failedChannels = 0
            buckets.forEach { (channelId, messages) ->
                val channel = Bot.shardManager.getTextChannelById(channelId)
                if(channel == null || !channel.checkPermissions(Permission.MESSAGE_MANAGE)) {
                    failedChannels++
                    return@forEach
                }
                cf.addAll(channel.purgeMessagesById(messages).toTypedArray())
            }
            CompletableFuture.allOf(*cf.toTypedArray()).thenAccept {
                val msg = buildString {
                    append("Finished in `")
                    append(Time.format(1, System.currentTimeMillis()-start))
                    append("`. Deleted ")
                    append(messages.size)
                    append(" messages")
                    if(failedChannels > 0) {
                        append(". Could not delete messages in ")
                        append(failedChannels)
                        append(" channels")
                    }
                }
                m.editMessage(msg).queue()
            }
        }
        if (messages.size >= confirmAmount) {
            val msg = context.channel.sendMessage(
                    ":warning: You're about to delete ${messages.size} messages. Are you sure you want to do this?").complete()
            WaitUtils.confirmYesNo(msg, context.author, {
                msg.delete().queue()
                doClean()
            })
        } else {
            doClean()
        }
    }

    private fun getBuilder(amount: Long? = 50): QueryBuilder {
        return QueryBuilder().table("server_messages").where("deleted", false).select(
                "server_messages.id")
                .orderBy("server_messages.id", "DESC").apply {
                    if(amount != null)
                        limit(amount)
                }
    }

    private fun QueryBuilder.queryIds(): List<String> {
        return this.query().map { it.getString("id") }
    }

    private fun CommandContext.getBuilder(): QueryBuilder {
        return getBuilder(this.get<Int>("amount")?.toLong() ?: 50L)
    }
}<commit_msg>Fix issue where clean loads forever<commit_after>package me.mrkirby153.KirBot.command.executors.admin

import com.mrkirby153.bfs.sql.QueryBuilder
import me.mrkirby153.KirBot.Bot
import me.mrkirby153.KirBot.command.CommandCategory
import me.mrkirby153.KirBot.command.annotations.Command
import me.mrkirby153.KirBot.command.annotations.CommandDescription
import me.mrkirby153.KirBot.command.annotations.IgnoreWhitelist
import me.mrkirby153.KirBot.command.annotations.LogInModlogs
import me.mrkirby153.KirBot.command.args.CommandContext
import me.mrkirby153.KirBot.listener.WaitUtils
import me.mrkirby153.KirBot.user.CLEARANCE_MOD
import me.mrkirby153.KirBot.utils.Context
import me.mrkirby153.KirBot.utils.checkPermissions
import me.mrkirby153.kcutils.Time
import net.dv8tion.jda.api.Permission
import java.util.concurrent.CompletableFuture

class CommandClean {

    private val confirmAmount = 100

    @Command(name = "all", arguments = ["[amount:int]"], parent = "clean",
            clearance = CLEARANCE_MOD, category = CommandCategory.MODERATION)
    @CommandDescription("Cleans messages from everyone in the current channel")
    @LogInModlogs
    @IgnoreWhitelist
    fun allClean(context: Context, cmdContext: CommandContext) {
        val builder = cmdContext.getBuilder()
        builder.where("channel", context.channel.id)
        purgeMessages(context, builder.queryIds())
    }

    @Command(name = "user", arguments = ["<user:snowflake>", "[amount:int]"], parent = "clean",
            clearance = CLEARANCE_MOD, category = CommandCategory.MODERATION)
    @CommandDescription("Cleans messages from a specific user in the current channel")
    @LogInModlogs
    @IgnoreWhitelist
    fun userClean(context: Context, cmdContext: CommandContext) {
        val builder = cmdContext.getBuilder()
        builder.where("channel", context.channel.id)
        builder.where("author", cmdContext.get<String>("user"))
        purgeMessages(context, builder.queryIds())
    }

    @Command(name = "bots", arguments = ["[amount:int]"], parent = "clean",
            clearance = CLEARANCE_MOD, category = CommandCategory.MODERATION)
    @CommandDescription("Cleans messages sent by bots in the current channel")
    @LogInModlogs
    @IgnoreWhitelist
    fun botClean(context: Context, cmdContext: CommandContext) {
        val builder = cmdContext.getBuilder()
        builder.where("channel", context.channel.id)
        builder.leftJoin("seen_users", "server_messages.author", "=", "seen_users.id")
        builder.where("bot", true)
        purgeMessages(context, builder.queryIds())
    }

    @Command(name = "everywhere", arguments = ["<user:snowflake>", "[amount:int]"],
            parent = "clean", clearance = CLEARANCE_MOD, category = CommandCategory.MODERATION)
    @LogInModlogs
    @CommandDescription("Cleans messages sent by the given user across all channels")
    @IgnoreWhitelist
    fun cleanEverywhere(context: Context, cmdContext: CommandContext) {
        val builder = cmdContext.getBuilder()
        builder.where("server_id", context.guild.id)
        builder.where("author", cmdContext.getNotNull("user"))
        purgeMessages(context, builder.queryIds())
    }

    @Command(name = "between", arguments = ["<first:snowflake>", "<last:snowflake>"],
            parent = "clean", clearance = CLEARANCE_MOD, category = CommandCategory.MODERATION)
    @LogInModlogs
    @CommandDescription("Cleans messages sent by all users between the given snowflakes")
    @IgnoreWhitelist
    fun cleanBetween(context: Context, cmdContext: CommandContext) {
        val builder = getBuilder()
        builder.where("id", ">=", cmdContext.getNotNull("first"))
        builder.where("id", "<=", cmdContext.getNotNull("last"))
        builder.where("channel", context.channel.id)
        purgeMessages(context, builder.queryIds())
    }

    private fun purgeMessages(context: Context, messages: List<String>) {
        fun doClean() {
            val m = context.channel.sendMessage(":repeat: Processing...").complete()
            val start = System.currentTimeMillis()
            val buckets = mutableMapOf<String, MutableList<String>>()
            val builder = QueryBuilder()
            builder.table("server_messages")
            builder.whereIn("id", messages.toTypedArray())
            builder.select("id", "channel")
            builder.where("deleted", false)
            builder.query().forEach {
                buckets.getOrPut(it.getString("channel"), { mutableListOf() }).add(
                        it.getString("id"))
            }

            Bot.LOG.debug("Purging ${messages.size} messages across ${buckets.size} channels")
            val cf = mutableListOf<CompletableFuture<*>>()
            var failedChannels = 0
            buckets.forEach { (channelId, messages) ->
                val channel = Bot.shardManager.getTextChannelById(channelId)
                if (channel == null || !channel.checkPermissions(Permission.MESSAGE_MANAGE)) {
                    failedChannels++
                    return@forEach
                }
                cf.addAll(channel.purgeMessagesById(messages).toTypedArray())
            }
            CompletableFuture.allOf(*cf.toTypedArray()).handle { _, _ ->
                val msg = buildString {
                    append("Finished in `")
                    append(Time.format(1, System.currentTimeMillis() - start))
                    append("`. Deleted ")
                    append(messages.size)
                    append(" messages")
                    if (failedChannels > 0) {
                        append(". Could not delete messages in ")
                        append(failedChannels)
                        append(" channels")
                    }
                }
                m.editMessage(msg).queue()
            }
        }
        if (messages.size >= confirmAmount) {
            val msg = context.channel.sendMessage(
                    ":warning: You're about to delete ${messages.size} messages. Are you sure you want to do this?").complete()
            WaitUtils.confirmYesNo(msg, context.author, {
                msg.delete().queue()
                doClean()
            })
        } else {
            doClean()
        }
    }

    private fun getBuilder(amount: Long? = 50): QueryBuilder {
        return QueryBuilder().table("server_messages").where("deleted", false).select(
                "server_messages.id")
                .orderBy("server_messages.id", "DESC").apply {
                    if (amount != null)
                        limit(amount)
                }
    }

    private fun QueryBuilder.queryIds(): List<String> {
        return this.query().map { it.getString("id") }
    }

    private fun CommandContext.getBuilder(): QueryBuilder {
        return getBuilder(this.get<Int>("amount")?.toLong() ?: 50L)
    }
}
{'fraction_non_alphanumeric': 0.11797092937894582, 'fraction_numerical': 0.007781529878138306, 'mean_word_length': 2.6483663631494374, 'pattern_counts': {'":': 4, '<': 29, '<?xml version=': 0, '>': 36, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '19567', 'n_tokens_mistral': 3799, 'n_tokens_neox': 3653, 'n_words': 726}