package chatty.util.commands;
import chatty.Helper;
import chatty.util.DateTime;
import chatty.util.StringUtil;
import chatty.util.api.StreamInfo;
import chatty.util.api.TwitchApi;
import chatty.util.settings.Settings;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
/**
* Managing custom commands, that return the text they are defined with, which
* can then be entered like text in the input box. Custom commands are
* case-insensitive.
*
* @author tduva
*/
public class CustomCommands {
private static final Logger LOGGER = Logger.getLogger(CustomCommands.class.getName());
private final Map<String, Map<String, CustomCommand>> commands = new HashMap<>();
private final Map<String, Map<String, CustomCommand>> replacements = new HashMap<>();
private final Settings settings;
private final TwitchApi api;
public CustomCommands(Settings settings, TwitchApi api) {
this.settings = settings;
this.api = api;
}
/**
* Returns the text associated with the given command, inserting the given
* parameters as defined.
*
* @param commandName The command
* @param parameters The parameters, each seperated by a space from eachother
* @param channel
* @return A {@code String} as result of the command, or {@code null} if the
* command doesn't exist or the number of parameters were invalid
*/
public synchronized String command(String commandName, Parameters parameters, String channel) {
CustomCommand command = getCommand(commands, commandName, channel);
if (command != null) {
return command(command, parameters, channel);
}
return null;
}
public synchronized String command(CustomCommand command, Parameters parameters, String channel) {
// Add some more parameters
parameters.put("chan", Helper.toStream(channel));
if (!command.getIdentifiersWithPrefix("stream").isEmpty()) {
System.out.println("request");
String stream = Helper.toValidStream(channel);
StreamInfo streamInfo = api.getStreamInfo(stream, null);
if (streamInfo.isValid()) {
parameters.put("streamstatus", streamInfo.getFullStatus());
if (streamInfo.getOnline()) {
parameters.put("streamuptime", DateTime.agoUptimeCompact2(streamInfo.getTimeStartedWithPicnic()));
parameters.put("streamtitle", streamInfo.getTitle());
parameters.put("streamgame", streamInfo.getGame());
parameters.put("streamviewers", String.valueOf(streamInfo.getViewers()));
}
}
}
// Add parameters for custom replacements
Set<String> customIdentifiers = command.getIdentifiersWithPrefix("_");
for (String identifier : customIdentifiers) {
CustomCommand replacement = getCommand(replacements, identifier, channel);
if (replacement != null) {
parameters.put(identifier, replacement.replace(parameters));
}
}
return command.replace(parameters);
}
/**
* Checks if the given command exists (case-insensitive).
*
* @param command The command
* @return {@code true} if the command exists, {@code false} otherwise
*/
public synchronized boolean containsCommand(String command, String chan) {
return getCommand(commands, command, chan) != null;
}
/**
* Load the commands from the settings. Everything before the first space
* is interpreted as command name, removing a leading "/" if present.
* Everything else is used as the command parameters.
*/
public synchronized void loadFromSettings() {
List<String> commandsToLoad = settings.getList("commands");
commands.clear();
replacements.clear();
for (String c : commandsToLoad) {
if (c != null && !c.isEmpty()) {
String[] split = c.split(" ", 2);
if (split.length == 2) {
String commandName = split[0];
if (commandName.startsWith("/")) {
commandName = commandName.substring(1);
}
commandName = StringUtil.toLowerCase(commandName.trim());
String chan = null;
if (commandName.contains("#")) {
String[] splitChan = commandName.split("#", 2);
commandName = splitChan[0];
chan = splitChan[1];
}
// Trim when loading, to ensure consistent behaviour
// (in-line menu commands and Test-button parsing trim too)
String commandValue = split[1].trim();
if (!commandName.isEmpty()) {
CustomCommand parsedCommand = CustomCommand.parse(commandValue);
if (parsedCommand.getError() == null) {
if (commandName.startsWith("_") && commandName.length() > 1) {
addCommand(replacements, commandName, chan, parsedCommand);
} else {
addCommand(commands, commandName, chan, parsedCommand);
}
} else {
LOGGER.warning("Error parsing custom command: "+parsedCommand.getError());
}
}
}
}
}
}
/**
* Get the CustomCommand from the given dataset, based on name and channel.
* This will prefer the command variation of the channel, but fallback on
* the non-restricted command, if that is available.
*
* @param commands The dataset to retrieve the Custom Command from
* @param commandName The name of the command
* @param channel The channel the command is run in
* @return
*/
private static CustomCommand getCommand(Map<String, Map<String, CustomCommand>> commands,
String commandName, String channel) {
commandName = StringUtil.toLowerCase(commandName);
channel = StringUtil.toLowerCase(Helper.toStream(channel));
if (!commands.containsKey(commandName)) {
return null;
}
Map<String, CustomCommand> variations = commands.get(commandName);
if (variations.containsKey(channel)) {
// If the channel parameter was null, this will try to get the
// non-restricted command
return variations.get(channel);
}
// Non-restricted commands are saved under "null"
return variations.get(null);
}
/**
* Add the given CustomCommand to the dataset, with it's name and channel.
*
* @param commands The dataset to store the CustomCommand in
* @param commandName The name of the command (all lowercase)
* @param channel The channel the command is restricted to (all lowercase),
* can be null if non-restricted
* @param parsedCommand The CustomCommand
*/
private static void addCommand(Map<String, Map<String, CustomCommand>> commands,
String commandName, String channel, CustomCommand parsedCommand) {
if (!commands.containsKey(commandName)) {
commands.put(commandName, new HashMap<>());
}
commands.get(commandName).put(channel, parsedCommand);
}
}