<commit_before>const Command = require("../base/Command");
// const {inspect} = require('util');

class GuildSearch extends Command {
    constructor(client) {
        super(client, {
            name: "guildsearch",
            category: "SWGoH",
            aliases: ["search", "gs"],
            permissions: ["EMBED_LINKS"],
            flags: {
                "ships": {
                    aliases: ["s", "ship"]
                },
                reverse: {
                    aliases: ["rev"]
                },
                mods: {
                    aliases: ["mod", "m"]
                },
                stars: {
                    aliases: ["*", "star", "rarity"]
                },
                gear: {
                    aliases: ["g"]
                },
                zetas: {
                    aliases: ["zeta", "z"]
                }
            },
            subArgs: {
                sort: {
                    aliases: []
                },
                stat: {
                    aliases: ["stats"]
                },
                top: {
                    aliases: []
                }
            }
        });
    }

    async run(client, message, args, options) { // eslint-disable-line no-unused-vars
        let starLvl = 0;
        const reverse = options.flags.reverse;
        const rarityMap = {
            "ONESTAR": 1,
            "TWOSTAR": 2,
            "THREESTAR": 3,
            "FOURSTAR": 4,
            "FIVESTAR": 5,
            "SIXSTAR": 6,
            "SEVENSTAR": 7
        };
        let top = null;
        if (options.subArgs.top !== null) {
            const t = parseInt(options.subArgs.top);
            if (!isNaN(t) && t > 0 && t <= 50) {
                top = t;
            } else {
                return super.error(message, "Invalid argument for -top. Must be between 1 and 50");
            }
        }

        // If there's enough elements in args, and it's in the format of a number*
        if (!options.flags.mods && args.length && !isNaN(parseInt(args[args.length-1])) && /^\d+$/.test(args[args.length-1].toString())) {
            starLvl = parseInt(args.pop());
            if (starLvl < 0 || starLvl > 7) {
                return super.error(message, message.language.get("COMMAND_GUILDSEARCH_BAD_STAR"), {example: "guildsearch c3po 7"});
            }
        }
        
        let tmp;
        let character;
        let allyCode, searchChar, err;
        if (!options.flags.mods && !options.flags.stars && !options.flags.gear) {
            tmp = await super.getUserAndChar(message, args);
            allyCode = tmp.allyCode;
            searchChar = tmp.searchChar;
            err = tmp.err;

            if (options.flags.ships && options.flags.stats) {
                // TODO Lang this
                return super.error(message, message.language.get("COMMAND_GUILDSEARCH_SHIP_STATS"));
            } else if (!searchChar) {
                return super.error(message, "Missing character to search for.", {example: "guildsearch c3po"});
            }
            
            const chars = !options.flags.ships ? client.findChar(searchChar, client.characters) : client.findChar(searchChar, client.ships, true);
            
            if (chars.length === 0) {
                return super.error(message, message.language.get("COMMAND_GUILDSEARCH_NO_RESULTS", searchChar), {example: "guildsearch c3po"});
            } else if (chars.length > 1) {
                const charL = [];
                const charS = chars.sort((p, c) => p.name > c.name ? 1 : -1);
                charS.forEach(c => {
                    charL.push(c.name);
                });
                return super.error(message, message.language.get("COMMAND_GUILDSEARCH_CHAR_LIST", charL.join("\n")));
            } else {
                character = chars[0];
            }

        } else {
            tmp = await super.getUserAndChar(message, args, false);
            allyCode = tmp.allyCode;
            searchChar = tmp.searchChar;
            err = tmp.err;
        }

        if (err) {
            return super.error(message, err);
        }
        
        // Check for conflicting flags/ subArgs
        const checkArr = ["ships", "mods", "stat", "gear"];
        const checkRes = Object.keys(options.flags).map(k => checkArr.includes(k) ? options.flags[k] : null).concat(Object.keys(options.subArgs).map(k => checkArr.includes(k) ? options.subArgs[k] : null));
        if (checkRes.filter(c => c).length > 1) {
            return super.error(message, message.language.get("COMMAND_GUILDSEARCH_CONFLICTING", client.codeBlock(checkArr.map(c => "-" + c).join("\n"))));
        }
        // Then check with stars instead of ships since those work together
        const checkArr2 = ["stars", "mods", "stat", "gear"];
        const checkRes2 = Object.keys(options.flags).map(k => checkArr.includes(k) ? options.flags[k] : null).concat(Object.keys(options.subArgs).map(k => checkArr.includes(k) ? options.subArgs[k] : null));
        if (checkRes2.filter(c => c).length > 1) {
            return super.error(message, message.language.get("COMMAND_GUILDSEARCH_CONFLICTING", client.codeBlock(checkArr2.map(c => "-" + c).join("\n"))));
        }

        const msg = await message.channel.send(message.language.get("COMMAND_GUILDSEARCH_PLEASE_WAIT"));

        const cooldown = client.getPlayerCooldown(message.author.id);

        if (options.flags.gear) {
            // List an overview of the guild's upper geared characters
            let sortBy = null;
            if (options.subArgs.sort) {
                sortBy = options.subArgs.sort;
                if (isNaN(sortBy) || ![9,10,11,12].includes(parseInt(sortBy))) {
                    return super.error(message, message.language.get("COMMAND_GUILDSEARCH_INVALID_SORT", "9,10,11,12"));
                }
            }
            let guild = null;
            try {
                guild = await client.swgohAPI.guild(allyCode, null, cooldown);
            } catch (e) {
                console.log("ERROR(GS) getting guild: " + e);
                return message.channel.send({embed: {
                    author: { name: "Something Broke getting your guild's roster" },
                    description: client.codeBlock(e) + "Please try again in a bit."
                }});
            }
            let gRoster;
            if (!guild || !guild.roster || !guild.roster.length) {
                return msg.edit(message.language.get("BASE_SWGOH_NO_GUILD"));
            } else {
                msg.edit("Found guild `" + guild.name + "`!");
                const oldLen = guild.roster.length;
                guild.roster = guild.roster.filter(m => m.allyCode !== null);
                if (guild.roster.length !== oldLen) {
                    guild.warnings = guild.warnings || [];
                    guild.warnings.push(`Could not get info for ${oldLen - guild.roster.length} players`);
                }
                gRoster = guild.roster.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1).map(m => m.allyCode);
            }

            if (!gRoster.length) {
                return msg.edit("I can't find any players in the requested guild.");
            }
            let guildGG;
            try {
                guildGG = await client.swgohAPI.guildGG(gRoster, null, cooldown);
            } catch (e) {
                console.log("ERROR(GS_GEAR) getting guild: " + e);
                // Spit out the gId so I can go check on why it's breaking
                console.log("GuildID: " + guild.id);
                return super.error(msg, client.codeBlock(e), {title: "Something Broke while getting your guild's characters", footer: "Please try again in a bit", edit: true});
            }
            const starOut = {};

            Object.keys(guildGG.roster).forEach(char => {
                guildGG.roster[char].forEach(p => {
                    starOut[p.player] = starOut[p.player] || {};
                    if (starOut[p.player][p.gearLevel]) {
                        starOut[p.player][p.gearLevel] += 1;
                    } else {
                        starOut[p.player][p.gearLevel] = 1;
                    }
                });
            });

            let tableIn = Object.keys(starOut).map(k => {
                return {
                    nine: starOut[k]["9"] || 0,
                    ten: starOut[k]["10"] || 0,
                    eleven:  starOut[k]["11"] || 0,
                    twelve: starOut[k]["12"] || 0,
                    name: k 
                };
            });

            switch (sortBy) {
                case "9":
                    tableIn = tableIn.sort((a, b) => a.nine < b.nine ? 1 : -1);
                    break;
                case "10":
                    tableIn = tableIn.sort((a, b) => a.ten < b.ten ? 1 : -1);
                    break;
                case "11":
                    tableIn = tableIn.sort((a, b) => a.eleven < b.eleven ? 1 : -1);
                    break;
                case "12":
                    tableIn = tableIn.sort((a, b) => a.twelve < b.twelve ? 1 : -1);
                    break;
                default:
                    tableIn = tableIn.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1);
            }

            const tableOut = client.makeTable({
                nine:  {value: "g9", startWith: "`[", endWith: "|", align: "right"},
                ten:   {value: "g10", endWith: "|", align: "right"},
                eleven:   {value: "g11", endWith: "|", align: "right"},
                twelve: {value: "g12", endWith: "]`", align: "right"},
                name:  {value: "", align: "left"}
            }, tableIn);

            const outMsgArr = client.msgArray(tableOut, "\n", 900);
            const fields = [];
            outMsgArr.forEach(m => {
                fields.push({
                    name: "-",
                    value: m
                });
            });

            if (options.defaults) {
                fields.push({
                    name: "Default flags used:",
                    value: client.codeBlock(options.defaults)
                });
            }

            const footer = client.updatedFooter(guild.updated, message, "guild", cooldown);
            return msg.edit({embed: {
                author: {
                    name: `${guild.name} ${message.language.get("COMMAND_GUILDSEARCH_GEAR_SUM")}`
                },
                fields: fields,
                footer: footer
            }});
        } else if (options.flags.stars) {
            // List an overview of the guild's upper starred characters
            let sortBy = null;
            if (options.subArgs.sort) {
                sortBy = options.subArgs.sort;
                if (isNaN(sortBy) || ![5,6,7].includes(parseInt(sortBy))) {
                    return super.error(message, message.language.get("COMMAND_GUILDSEARCH_INVALID_SORT", "5,6,7"));
                }
            }
            let guild = null;
            try {
                guild = await client.swgohAPI.guild(allyCode, null, cooldown);
            } catch (e) {
                console.log("ERROR(GS) getting guild: " + e);
                return message.channel.send({embed: {
                    author: { name: "Something Broke getting your guild's roster" },
                    description: client.codeBlock(e) + "Please try again in a bit."
                }});
            }
            let gRoster;
            if (!guild || !guild.roster || !guild.roster.length) {
                return msg.edit(message.language.get("BASE_SWGOH_NO_GUILD"));
            } else {
                msg.edit("Found guild `" + guild.name + "`!");
                const oldLen = guild.roster.length;
                guild.roster = guild.roster.filter(m => m.allyCode !== null);
                if (guild.roster.length !== oldLen) {
                    guild.warnings = guild.warnings || [];
                    guild.warnings.push(`Could not get info for ${oldLen - guild.roster.length} players`);
                }
                gRoster = guild.roster.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1).map(m => m.allyCode);
            }

            if (!gRoster.length) {
                return msg.edit("I can't find any players in the requested guild.");
            }
            let guildGG;
            try {
                guildGG = await client.swgohAPI.guildGG(gRoster, null, cooldown);
            } catch (e) {
                console.log("ERROR(GS) getting guild: " + e);
                // Spit out the gId so I can go check on why it's breaking
                console.log("GuildID: " + guild.id);
                return super.error(msg, client.codeBlock(e), {title: "Something Broke while getting your guild's characters", footer: "Please try again in a bit", edit: true});
            }
            const starOut = {};

            console.log(guildGG.roster["PHANTOM2"]);

            // Object.keys(guildGG.roster).forEach(char => {
            for (const char of Object.keys(guildGG.roster)) {
                if (options.flags.ships && guildGG.roster[char][0].type === "CHARACTER") {
                    continue;
                } else if (!options.flags.ships && guildGG.roster[char][0].type === "SHIP") {
                    continue;
                }
                guildGG.roster[char].forEach(p => {
                    starOut[p.player] = starOut[p.player] || {};
                    if (starOut[p.player][p.starLevel]) {
                        starOut[p.player][p.starLevel] += 1;
                    } else {
                        starOut[p.player][p.starLevel] = 1;
                    }
                });
            }

            let tableIn = Object.keys(starOut).map(k => {
                return {
                    five: starOut[k]["5"] || 0,
                    six:  starOut[k]["6"] || 0,
                    seven: starOut[k]["7"] || 0,
                    name: k 
                };
            });

            switch (sortBy) {
                case "5":
                    tableIn = tableIn.sort((a, b) => a.five < b.five ? 1 : -1);
                    break;
                case "6":
                    tableIn = tableIn.sort((a, b) => a.six < b.six ? 1 : -1);
                    break;
                case "7":
                    tableIn = tableIn.sort((a, b) => a.seven < b.seven ? 1 : -1);
                    break;
                default: 
                    tableIn = tableIn.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1);
            }

            const tableOut = client.makeTable({
                five:  {value: "5*", startWith: "`[", endWith: "|", align: "right"},
                six:   {value: "6*", endWith: "|", align: "right"},
                seven: {value: "7*", endWith: "]`", align: "right"},
                name:  {value: "", align: "left"}
            }, tableIn);
 
            const outMsgArr = client.msgArray(tableOut, "\n", 900);
            const fields = [];
            outMsgArr.forEach(m => {
                fields.push({<commit_msg>Added the 4* chars in for -stars<commit_after>const Command = require("../base/Command");
// const {inspect} = require('util');

class GuildSearch extends Command {
    constructor(client) {
        super(client, {
            name: "guildsearch",
            category: "SWGoH",
            aliases: ["search", "gs"],
            permissions: ["EMBED_LINKS"],
            flags: {
                "ships": {
                    aliases: ["s", "ship"]
                },
                reverse: {
                    aliases: ["rev"]
                },
                mods: {
                    aliases: ["mod", "m"]
                },
                stars: {
                    aliases: ["*", "star", "rarity"]
                },
                gear: {
                    aliases: ["g"]
                },
                zetas: {
                    aliases: ["zeta", "z"]
                }
            },
            subArgs: {
                sort: {
                    aliases: []
                },
                stat: {
                    aliases: ["stats"]
                },
                top: {
                    aliases: []
                }
            }
        });
    }

    async run(client, message, args, options) { // eslint-disable-line no-unused-vars
        let starLvl = 0;
        const reverse = options.flags.reverse;
        const rarityMap = {
            "ONESTAR": 1,
            "TWOSTAR": 2,
            "THREESTAR": 3,
            "FOURSTAR": 4,
            "FIVESTAR": 5,
            "SIXSTAR": 6,
            "SEVENSTAR": 7
        };
        let top = null;
        if (options.subArgs.top !== null) {
            const t = parseInt(options.subArgs.top);
            if (!isNaN(t) && t > 0 && t <= 50) {
                top = t;
            } else {
                return super.error(message, "Invalid argument for -top. Must be between 1 and 50");
            }
        }

        // If there's enough elements in args, and it's in the format of a number*
        if (!options.flags.mods && args.length && !isNaN(parseInt(args[args.length-1])) && /^\d+$/.test(args[args.length-1].toString())) {
            starLvl = parseInt(args.pop());
            if (starLvl < 0 || starLvl > 7) {
                return super.error(message, message.language.get("COMMAND_GUILDSEARCH_BAD_STAR"), {example: "guildsearch c3po 7"});
            }
        }
        
        let tmp;
        let character;
        let allyCode, searchChar, err;
        if (!options.flags.mods && !options.flags.stars && !options.flags.gear) {
            tmp = await super.getUserAndChar(message, args);
            allyCode = tmp.allyCode;
            searchChar = tmp.searchChar;
            err = tmp.err;

            if (options.flags.ships && options.flags.stats) {
                // TODO Lang this
                return super.error(message, message.language.get("COMMAND_GUILDSEARCH_SHIP_STATS"));
            } else if (!searchChar) {
                return super.error(message, "Missing character to search for.", {example: "guildsearch c3po"});
            }
            
            const chars = !options.flags.ships ? client.findChar(searchChar, client.characters) : client.findChar(searchChar, client.ships, true);
            
            if (chars.length === 0) {
                return super.error(message, message.language.get("COMMAND_GUILDSEARCH_NO_RESULTS", searchChar), {example: "guildsearch c3po"});
            } else if (chars.length > 1) {
                const charL = [];
                const charS = chars.sort((p, c) => p.name > c.name ? 1 : -1);
                charS.forEach(c => {
                    charL.push(c.name);
                });
                return super.error(message, message.language.get("COMMAND_GUILDSEARCH_CHAR_LIST", charL.join("\n")));
            } else {
                character = chars[0];
            }

        } else {
            tmp = await super.getUserAndChar(message, args, false);
            allyCode = tmp.allyCode;
            searchChar = tmp.searchChar;
            err = tmp.err;
        }

        if (err) {
            return super.error(message, err);
        }
        
        // Check for conflicting flags/ subArgs
        const checkArr = ["ships", "mods", "stat", "gear"];
        const checkRes = Object.keys(options.flags).map(k => checkArr.includes(k) ? options.flags[k] : null).concat(Object.keys(options.subArgs).map(k => checkArr.includes(k) ? options.subArgs[k] : null));
        if (checkRes.filter(c => c).length > 1) {
            return super.error(message, message.language.get("COMMAND_GUILDSEARCH_CONFLICTING", client.codeBlock(checkArr.map(c => "-" + c).join("\n"))));
        }
        // Then check with stars instead of ships since those work together
        const checkArr2 = ["stars", "mods", "stat", "gear"];
        const checkRes2 = Object.keys(options.flags).map(k => checkArr.includes(k) ? options.flags[k] : null).concat(Object.keys(options.subArgs).map(k => checkArr.includes(k) ? options.subArgs[k] : null));
        if (checkRes2.filter(c => c).length > 1) {
            return super.error(message, message.language.get("COMMAND_GUILDSEARCH_CONFLICTING", client.codeBlock(checkArr2.map(c => "-" + c).join("\n"))));
        }

        const msg = await message.channel.send(message.language.get("COMMAND_GUILDSEARCH_PLEASE_WAIT"));

        const cooldown = client.getPlayerCooldown(message.author.id);

        if (options.flags.gear) {
            // List an overview of the guild's upper geared characters
            let sortBy = null;
            if (options.subArgs.sort) {
                sortBy = options.subArgs.sort;
                if (isNaN(sortBy) || ![9,10,11,12].includes(parseInt(sortBy))) {
                    return super.error(message, message.language.get("COMMAND_GUILDSEARCH_INVALID_SORT", "9,10,11,12"));
                }
            }
            let guild = null;
            try {
                guild = await client.swgohAPI.guild(allyCode, null, cooldown);
            } catch (e) {
                console.log("ERROR(GS) getting guild: " + e);
                return message.channel.send({embed: {
                    author: { name: "Something Broke getting your guild's roster" },
                    description: client.codeBlock(e) + "Please try again in a bit."
                }});
            }
            let gRoster;
            if (!guild || !guild.roster || !guild.roster.length) {
                return msg.edit(message.language.get("BASE_SWGOH_NO_GUILD"));
            } else {
                msg.edit("Found guild `" + guild.name + "`!");
                const oldLen = guild.roster.length;
                guild.roster = guild.roster.filter(m => m.allyCode !== null);
                if (guild.roster.length !== oldLen) {
                    guild.warnings = guild.warnings || [];
                    guild.warnings.push(`Could not get info for ${oldLen - guild.roster.length} players`);
                }
                gRoster = guild.roster.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1).map(m => m.allyCode);
            }

            if (!gRoster.length) {
                return msg.edit("I can't find any players in the requested guild.");
            }
            let guildGG;
            try {
                guildGG = await client.swgohAPI.guildGG(gRoster, null, cooldown);
            } catch (e) {
                console.log("ERROR(GS_GEAR) getting guild: " + e);
                // Spit out the gId so I can go check on why it's breaking
                console.log("GuildID: " + guild.id);
                return super.error(msg, client.codeBlock(e), {title: "Something Broke while getting your guild's characters", footer: "Please try again in a bit", edit: true});
            }
            const starOut = {};

            Object.keys(guildGG.roster).forEach(char => {
                guildGG.roster[char].forEach(p => {
                    starOut[p.player] = starOut[p.player] || {};
                    if (starOut[p.player][p.gearLevel]) {
                        starOut[p.player][p.gearLevel] += 1;
                    } else {
                        starOut[p.player][p.gearLevel] = 1;
                    }
                });
            });

            let tableIn = Object.keys(starOut).map(k => {
                return {
                    nine: starOut[k]["9"] || 0,
                    ten: starOut[k]["10"] || 0,
                    eleven:  starOut[k]["11"] || 0,
                    twelve: starOut[k]["12"] || 0,
                    name: k 
                };
            });

            switch (sortBy) {
                case "9":
                    tableIn = tableIn.sort((a, b) => a.nine < b.nine ? 1 : -1);
                    break;
                case "10":
                    tableIn = tableIn.sort((a, b) => a.ten < b.ten ? 1 : -1);
                    break;
                case "11":
                    tableIn = tableIn.sort((a, b) => a.eleven < b.eleven ? 1 : -1);
                    break;
                case "12":
                    tableIn = tableIn.sort((a, b) => a.twelve < b.twelve ? 1 : -1);
                    break;
                default:
                    tableIn = tableIn.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1);
            }

            const tableOut = client.makeTable({
                nine:  {value: "g9", startWith: "`[", endWith: "|", align: "right"},
                ten:   {value: "g10", endWith: "|", align: "right"},
                eleven:   {value: "g11", endWith: "|", align: "right"},
                twelve: {value: "g12", endWith: "]`", align: "right"},
                name:  {value: "", align: "left"}
            }, tableIn);

            const outMsgArr = client.msgArray(tableOut, "\n", 900);
            const fields = [];
            outMsgArr.forEach(m => {
                fields.push({
                    name: "-",
                    value: m
                });
            });

            if (options.defaults) {
                fields.push({
                    name: "Default flags used:",
                    value: client.codeBlock(options.defaults)
                });
            }

            const footer = client.updatedFooter(guild.updated, message, "guild", cooldown);
            return msg.edit({embed: {
                author: {
                    name: `${guild.name} ${message.language.get("COMMAND_GUILDSEARCH_GEAR_SUM")}`
                },
                fields: fields,
                footer: footer
            }});
        } else if (options.flags.stars) {
            // List an overview of the guild's upper starred characters
            let sortBy = null;
            if (options.subArgs.sort) {
                sortBy = options.subArgs.sort;
                if (isNaN(sortBy) || ![4,5,6,7].includes(parseInt(sortBy))) {
                    return super.error(message, message.language.get("COMMAND_GUILDSEARCH_INVALID_SORT", "4,5,6,7"));
                }
            }
            let guild = null;
            try {
                guild = await client.swgohAPI.guild(allyCode, null, cooldown);
            } catch (e) {
                console.log("ERROR(GS) getting guild: " + e);
                return message.channel.send({embed: {
                    author: { name: "Something Broke getting your guild's roster" },
                    description: client.codeBlock(e) + "Please try again in a bit."
                }});
            }
            let gRoster;
            if (!guild || !guild.roster || !guild.roster.length) {
                return msg.edit(message.language.get("BASE_SWGOH_NO_GUILD"));
            } else {
                msg.edit("Found guild `" + guild.name + "`!");
                const oldLen = guild.roster.length;
                guild.roster = guild.roster.filter(m => m.allyCode !== null);
                if (guild.roster.length !== oldLen) {
                    guild.warnings = guild.warnings || [];
                    guild.warnings.push(`Could not get info for ${oldLen - guild.roster.length} players`);
                }
                gRoster = guild.roster.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1).map(m => m.allyCode);
            }

            if (!gRoster.length) {
                return msg.edit("I can't find any players in the requested guild.");
            }
            let guildGG;
            try {
                guildGG = await client.swgohAPI.guildGG(gRoster, null, cooldown);
            } catch (e) {
                console.log("ERROR(GS) getting guild: " + e);
                // Spit out the gId so I can go check on why it's breaking
                console.log("GuildID: " + guild.id);
                return super.error(msg, client.codeBlock(e), {title: "Something Broke while getting your guild's characters", footer: "Please try again in a bit", edit: true});
            }
            const starOut = {};
            for (const char of Object.keys(guildGG.roster)) {
                if (options.flags.ships && guildGG.roster[char][0].type === "CHARACTER") {
                    continue;
                } else if (!options.flags.ships && guildGG.roster[char][0].type === "SHIP") {
                    continue;
                }
                guildGG.roster[char].forEach(p => {
                    starOut[p.player] = starOut[p.player] || {};
                    if (starOut[p.player][p.starLevel]) {
                        starOut[p.player][p.starLevel] += 1;
                    } else {
                        starOut[p.player][p.starLevel] = 1;
                    }
                });
            }

            let tableIn = Object.keys(starOut).map(k => {
                return {
                    four: starOut[k]["4"] || 0,
                    five: starOut[k]["5"] || 0,
                    six:  starOut[k]["6"] || 0,
                    seven: starOut[k]["7"] || 0,
                    name: k 
                };
            });

            switch (sortBy) {
                case "4":
                    tableIn = tableIn.sort((a, b) => a.four < b.four ? 1 : -1);
                    break;
                case "5":
                    tableIn = tableIn.sort((a, b) => a.five < b.five ? 1 : -1);
                    break;
                case "6":
                    tableIn = tableIn.sort((a, b) => a.six < b.six ? 1 : -1);
                    break;
                case "7":
                    tableIn = tableIn.sort((a, b) => a.seven < b.seven ? 1 : -1);
                    break;
                default: 
                    tableIn = tableIn.sort((a, b) => a.name.toLowerCase() > b.name.toLowerCase() ? 1 : -1);
            }

            const tableOut = client.makeTable({
                four:  {value: "4*", startWith: "`[", endWith: "|", align: "right"},
                five:  {value: "5*", endWith: "|", align: "right"},
                six:   {value: "6*", endWith: "|", align: "right"},
                seven: {value: "7*", endWith: "]`", align: "right"},
                name:  {value: "", align: "left"}
            }, tableIn);
 
            const outMsgArr = client.msgArray(tableOut, "\n", 900);
            const fields = [];
            outMsgArr.forEach(m => {
                fields.push({
{'fraction_non_alphanumeric': 0.14000655307994758, 'fraction_numerical': 0.008453473132372215, 'mean_word_length': 1.3632210607820363, 'pattern_counts': {'":': 31, '<': 22, '<?xml version=': 0, '>': 89, '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': '776153', 'n_tokens_mistral': 9099, 'n_tokens_neox': 8466, 'n_words': 1838}