<commit_before>package com.tterrag.k9.commands.api;

import java.io.File;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.reflect.ClassPath;
import com.google.common.reflect.ClassPath.ClassInfo;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.tterrag.k9.commands.CommandControl;
import com.tterrag.k9.util.NonNull;
import com.tterrag.k9.util.NullHelper;
import com.tterrag.k9.util.Nullable;
import com.tterrag.k9.util.Requirements;
import com.tterrag.k9.util.Threads;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import sx.blah.discord.handle.obj.IGuild;
import sx.blah.discord.handle.obj.IMessage;
import sx.blah.discord.handle.obj.IUser;
import sx.blah.discord.util.RequestBuffer;

@Slf4j
public enum CommandRegistrar {
	
	INSTANCE;
	
    @NonNull
	static final File DATA_FOLDER = NullHelper.notnullJ(Paths.get("command_data").toFile(), "Path#toFile");
	static {
		DATA_FOLDER.mkdirs();
	}
	
	private final Map<String, ICommand> commands = Maps.newTreeMap();
	private final CommandControl ctrl = new CommandControl();
	private final Timer autoSaveTimer = new Timer();
	
	private final @NonNull GsonBuilder builder = new GsonBuilder();
	private @NonNull Gson gson = new Gson();
	
	private boolean finishedDefaultSlurp;
	private boolean locked;
	
	private CommandRegistrar() {
		autoSaveTimer.scheduleAtFixedRate(new TimerTask() {
			
			@Override
			public void run() {
				INSTANCE.saveAll();
			}
		}, TimeUnit.SECONDS.toMillis(30), TimeUnit.MINUTES.toMillis(5));
	}

	private static final Pattern FLAG_PATTERN = Pattern.compile("^(--?)(\\w+)(?:[=\\s](?:\"(.*?)\"|(\\S+)))?");

	public void invokeCommand(IMessage message, String name, String argstr) {
		ICommand command = findCommand(message.getGuild(), name);
		if (command == null) {
		    return;
		}
        // This is hardcoded BS but it's for potentially destructive actions like killing the bot, or wiping caches, so I think it's fine. Proper permission handling below.
		if (command.admin()) {
		    if (!isAdmin(message.getAuthor())) {
		        return;
		    }
		}
		
		Requirements req = command.requirements();
		if (!req.matches(message.getAuthor(), message.getGuild())) {
		    IMessage msg = message.getChannel().sendMessage("You do not have permission to use this command!");
		    Threads.sleep(5000);
		    msg.delete();
		    return;
		}
		
		argstr = Strings.nullToEmpty(argstr);
		
		CommandContext ctx = new CommandContext(message);

		Map<Flag, String> flags = new HashMap<>();
		Map<Argument<?>, String> args = new HashMap<>();
		
		Map<Character, Flag> keyToFlag = command.getFlags().stream().collect(Collectors.toMap(Flag::name, f -> f));
	    Map<String, Flag> longKeyToFlag = command.getFlags().stream().collect(Collectors.toMap(Flag::longFormName, f -> f));

		Matcher matcher = FLAG_PATTERN.matcher(argstr);
        while (matcher.find()) {
            String flagname = matcher.group(2);
            List<Flag> foundFlags;
            if (matcher.group().startsWith("--")) {
                foundFlags = Collections.singletonList(longKeyToFlag.get(flagname));
            } else if (matcher.group().startsWith("-")) {
                foundFlags = Lists.newArrayList(flagname.chars().mapToObj(i -> keyToFlag.get((char) i)).toArray(Flag[]::new));
            } else {
                continue;
            }
            if (foundFlags.contains(null)) {
                ctx.reply("Unknown flag \"" + flagname + "\".");
                return;
            }
            
            String toreplace = matcher.group(1) + matcher.group(2);

            for (int i = 0; i < foundFlags.size(); i++) {
                Flag flag = foundFlags.get(i);
                String value = null;
                if (i == foundFlags.size() - 1) {
                    if (flag.canHaveValue()) {
                        value = matcher.group(3);
                        if (value == null) {
                            value = matcher.group(4);
                        }
                        toreplace = matcher.group();
                    }
                }
                if (value == null && flag.needsValue()) {
                    ctx.reply("Flag \"" + flag.longFormName() + "\" requires a value.");
                    return;
                }

                flags.put(flag, value == null ? flag.getDefaultValue() : value);
            }
            toreplace = Pattern.quote(toreplace) + "\\s*";
            argstr = argstr.replaceFirst(toreplace, "").trim();
            matcher.reset(argstr);
        }

        for (Argument<?> arg : command.getArguments()) {
            boolean required = arg.required(flags.keySet());<commit_msg>Change grammar of flag error to be a bit more accurate<commit_after>package com.tterrag.k9.commands.api;

import java.io.File;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.reflect.ClassPath;
import com.google.common.reflect.ClassPath.ClassInfo;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.tterrag.k9.commands.CommandControl;
import com.tterrag.k9.util.NonNull;
import com.tterrag.k9.util.NullHelper;
import com.tterrag.k9.util.Nullable;
import com.tterrag.k9.util.Requirements;
import com.tterrag.k9.util.Threads;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import sx.blah.discord.handle.obj.IGuild;
import sx.blah.discord.handle.obj.IMessage;
import sx.blah.discord.handle.obj.IUser;
import sx.blah.discord.util.RequestBuffer;

@Slf4j
public enum CommandRegistrar {
	
	INSTANCE;
	
    @NonNull
	static final File DATA_FOLDER = NullHelper.notnullJ(Paths.get("command_data").toFile(), "Path#toFile");
	static {
		DATA_FOLDER.mkdirs();
	}
	
	private final Map<String, ICommand> commands = Maps.newTreeMap();
	private final CommandControl ctrl = new CommandControl();
	private final Timer autoSaveTimer = new Timer();
	
	private final @NonNull GsonBuilder builder = new GsonBuilder();
	private @NonNull Gson gson = new Gson();
	
	private boolean finishedDefaultSlurp;
	private boolean locked;
	
	private CommandRegistrar() {
		autoSaveTimer.scheduleAtFixedRate(new TimerTask() {
			
			@Override
			public void run() {
				INSTANCE.saveAll();
			}
		}, TimeUnit.SECONDS.toMillis(30), TimeUnit.MINUTES.toMillis(5));
	}

	private static final Pattern FLAG_PATTERN = Pattern.compile("^(--?)(\\w+)(?:[=\\s](?:\"(.*?)\"|(\\S+)))?");

	public void invokeCommand(IMessage message, String name, String argstr) {
		ICommand command = findCommand(message.getGuild(), name);
		if (command == null) {
		    return;
		}
        // This is hardcoded BS but it's for potentially destructive actions like killing the bot, or wiping caches, so I think it's fine. Proper permission handling below.
		if (command.admin()) {
		    if (!isAdmin(message.getAuthor())) {
		        return;
		    }
		}
		
		Requirements req = command.requirements();
		if (!req.matches(message.getAuthor(), message.getGuild())) {
		    IMessage msg = message.getChannel().sendMessage("You do not have permission to use this command!");
		    Threads.sleep(5000);
		    msg.delete();
		    return;
		}
		
		argstr = Strings.nullToEmpty(argstr);
		
		CommandContext ctx = new CommandContext(message);

		Map<Flag, String> flags = new HashMap<>();
		Map<Argument<?>, String> args = new HashMap<>();
		
		Map<Character, Flag> keyToFlag = command.getFlags().stream().collect(Collectors.toMap(Flag::name, f -> f));
	    Map<String, Flag> longKeyToFlag = command.getFlags().stream().collect(Collectors.toMap(Flag::longFormName, f -> f));

		Matcher matcher = FLAG_PATTERN.matcher(argstr);
        while (matcher.find()) {
            String flagname = matcher.group(2);
            List<Flag> foundFlags;
            if (matcher.group().startsWith("--")) {
                foundFlags = Collections.singletonList(longKeyToFlag.get(flagname));
            } else if (matcher.group().startsWith("-")) {
                foundFlags = Lists.newArrayList(flagname.chars().mapToObj(i -> keyToFlag.get((char) i)).toArray(Flag[]::new));
            } else {
                continue;
            }
            if (foundFlags.contains(null)) {
                ctx.reply("Unknown flag(s) \"" + flagname + "\".");
                return;
            }
            
            String toreplace = matcher.group(1) + matcher.group(2);

            for (int i = 0; i < foundFlags.size(); i++) {
                Flag flag = foundFlags.get(i);
                String value = null;
                if (i == foundFlags.size() - 1) {
                    if (flag.canHaveValue()) {
                        value = matcher.group(3);
                        if (value == null) {
                            value = matcher.group(4);
                        }
                        toreplace = matcher.group();
                    }
                }
                if (value == null && flag.needsValue()) {
                    ctx.reply("Flag \"" + flag.longFormName() + "\" requires a value.");
                    return;
                }

                flags.put(flag, value == null ? flag.getDefaultValue() : value);
            }
            toreplace = Pattern.quote(toreplace) + "\\s*";
            argstr = argstr.replaceFirst(toreplace, "").trim();
            matcher.reset(argstr);
        }

        for (Argument<?> arg : command.getArguments()) {
            boolean required = arg.required(flags.keySet());
{'fraction_non_alphanumeric': 0.13630123553637968, 'fraction_numerical': 0.004706805255932536, 'mean_word_length': 2.630829476682093, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 29, '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': '1692972', 'n_tokens_mistral': 3265, 'n_tokens_neox': 3059, 'n_words': 643}