Search code examples
javaregexbotstwitch

String regex in Java for Twitch-Bot


Currently I'm programming a Twitch-Bot with pIRC on Java.

I want to sort out commands like e.g !<command> [<tagged-user>]

For now I use that code:

private boolean checkForCommand(String message) {
    Pattern pattern = Pattern.compile("^(!)\\w+");
    Matcher m = pattern.matcher(message);
    return m.find();
}

public void onMessage(String channel, String sender, String login,String hostname, String message){
    String user = sender;
    if(checkForCommand(message)){
        String[] arguments;
        if(message.split("\\s+").length > 1){
            arguments = message.split("\\s+");
        }else {
            arguments = new String[1];
            arguments[0] = message;
        }
        String command = arguments[0].substring(1, arguments[0].length());
        if(arguments.length > 1){
            user = arguments[1];
        }
        respondForCommand(command,user); // User gets respond for his command.
    }
}

I get for example !info testuser:

testuser [here is the command response]

I want to improve the code by only regex-ing the hole process.

Basically I want a regex what reads out the command-name and if there is someone tagged it should also read the tagged username.

Kinda like Pattern pattern = Pattern.compile("!COMMANDNAME [TAGGEDUSER]");

Thank you for helping me, I struggelt alot with regex lately, but I want to understand it. Online regexing wont help me much.


Solution

  • Use capturing groups. Your current regex puts the '!' in a capturing group for some reason, but it doesn't use it. Instead, put the parentheses around the important parts of the pattern that you want to capture.

    Pattern pattern = Pattern.compile("^!(\\w+)(?:\\s+(\\S+))?");
    

    What we have here is:

    • A "!"
    • One or more word characters that are expected to be the command. They are surrounded by parentheses, so they are the first capturing group.
    • Then a non-capturing group for an optional argument. Since it's optional, we add a '?' after this group. In this group:
      • One or more spaces
      • A capturing group for one or more non-spaces

    So basically, we have two capturing groups, but one of them will not be matched if there is no argument.

    Now if you call matcher.find(), you can use matcher.group(1) and matcher.group(2) to access the captured groups. group(2) may return null as it won't be matched if there is no argument.

    Demonstration code:

    public class SimpleTest {
        public static void main(String[] args) {
            Pattern pattern = Pattern.compile("^!(\\w+)(?:\\s+(\\S+))?");
            Matcher matcher;
            String[] testStrings = { "!command john", "!command john something", "!commandWithoutArguments",
                    "!commandNoArgsButSpaces   ", "Not A command." };
    
            for (String testString : testStrings) {
                System.out.print("String <<" + testString + ">> ");
                matcher = pattern.matcher(testString);
                if (matcher.find()) {
                    String command = matcher.group(1);
                    String user = matcher.group(2);
                    if (user == null) {
                        user = "default";
                    }
                    System.out.println("is a command: " + command + " user: " + user + ".");
                } else {
                    System.out.println("is not a command.");
                }
            }
        }
    }
    

    Output:

    String <<!command john>> is a command: command user: john.
    String <<!command john something>> is a command: command user: john.
    String <<!commandWithoutArguments>> is a command: commandWithoutArguments user: default.
    String <<!commandNoArgsButSpaces   >> is a command: commandNoArgsButSpaces user: default.
    String <<Not A command.>> is not a command.