0

I'm using listeners provided by Pircbotx (https://github.com/pircbotx/pircbotx/wiki/Documentation) to detect when a command is found in Twitch chat, and I am trying to use a different method depending on which Command is called (format is !command). Classes used: Listeners, Command.

Commands are stored in an array of Command objects, comprised of one String (name). Each Command object will ultimately use its own method that will be defined in the Command class. The Listeners object when instantiated will immediately place every element of the array into a hash table (commands).

When Listeners detects a message, it is stored using a local String variable (msg). When this happens, a loop iterates through the Command object array, and then.... is supposed to call the method that corresponds to that particular object, in this case Command.addDeath(). That's where I'm stuck.

I was previously using a bunch of if statements for my listeners, but when there's a bunch of commands things will get really, really messy. Apologies in advance if the formatting in my code block is weird, I'm pretty new to utilizing Stackverflow, and I'm also a Java novice that's learning as I go along. After looking at the code again, it would appear I don't really need the hash table - but I'm leaving it in there just in case you guys have any better ideas for what to do with them.

public class Listeners {

String name;
String message;    
private static MessageEvent event;
Command [] commandNames = {new Command("!clearchat", new Command("!addDeath")}; 
Hashtable<String, Command> commands = new Hashtable<String, Command>();

 public Listeners() {
    for (int i = 0; i < commandNames.length; i++) {
        commands.put(commandNames[i].name, new Command(commandNames[i].name));
    }   
    
    if (event.getMessage() != null) {
        String msg = event.getMessage();
        for (int x = 0; x < commandNames.length; x++ ) {
            if (msg.startsWith(commandNames[x].name)) {
                // call Command method here 
            }
        }
    }   
}

And here is the Command class:

public class Command {

String name;

public Command(String name) {
        this.name = name;
}

public static void addDeath() {
    DeathCounter.addDeath();
    Listeners.sendMessage("Death Counter: " + DeathCounter.getDeaths());
} 
}

1 Answers1

0

You can use an interface for your commands:

public interface Command {
    public abstract void execute();
}

Then have your commands implement the interface:

public class DeathCommand implements Command {
    @Override
    public void execute() {
        DeathCounter.addDeath();
        Listeners.sendMessage("Death Counter: " + DeathCounter.getDeaths());
    }
}

In your listener class, map the command strings to instances of the corresponding command:

public class Listeners {

    String name;
    String message;
    private static MessageEvent event;
    static Map<String, Command> commands = new HashMap<>();

    static {
        commands.put("!addDeath", new DeathCommand());
    }

    public Listeners() {
        if (event.getMessage() != null) {
            String msg = event.getMessage();
            Optional<String> key = commands.keySet().stream().filter(k -> msg.startsWith(k)).findFirst();
            key.ifPresent(s -> commands.get(s).execute());
        }
    }
}

I replaced your Hashtable with a HashMap which is better in most ways (but is used the same way).

I'm a bit skeptical about having the map as a static member, but since I'm not familiar with your use case I leave that bit as is.

Edit

All Java classes have a default constructor (with no parameters) unless you write your own constructor (you have to write the default one yourself if you still want it). Interfaces don't have constructors since they can't be instantiated directly. They just specify methods that implementing classes must have. This allows you to have references (named fields/variables) of the interface type and be able to call the methods without knowing, or having to know, which implementation it is.

Example:

Command com = new DeathCommand();
com.execute(); // <- this will run the execute() code in the DeathCommand class
Command com2 = new SomeOtherCommand();
com2.execute(); // <- this will run the execute() code in the SomeOtherCommand class

The above code for Command is complete. There is nothing more. As for DeathCommand, and other implementations, you'll need to add what code is needed.

Each class and interface goes in it's own file named as the type:

Command.java
DeathCommand.java

Regarding HashTable vs HashMap. I should have said that it's better to use Map and it's implementations. If you need thread safety, use ConcurrentHashMap as agilob pointed out since regular HashMap is not thread safe.

DarkMatter
  • 1,007
  • 7
  • 13
  • This looks promising! I'll try this in just a moment and report back. – Its_me_billy Dec 30 '20 at 22:33
  • Hashtable is synchronized, opposed to HashMap which isn't thread-safe, and since you're using `static Map` it should be thread-safe, Hashtable was better choice for it. – agilob Dec 30 '20 at 22:41
  • You can also eliminate `static {}` block by using concurrent `new ConcurrentHashMap(Map.of("!addDeath", new DeathCommand()));` – agilob Dec 30 '20 at 22:43
  • Would you mind showing me what the code in Command.Java would look like? I'm asking because I'm not very familiar with interfaces. I don't know if DeathCommand goes within or outside the interface - I also don't know if I should include a Command constructor... but I think I must, because otherwise I can't instantiate it later. – Its_me_billy Dec 30 '20 at 22:54
  • @Its_me_billy See my edit and let me know if you need any more clarifications. – DarkMatter Dec 30 '20 at 23:13