package de.skuzzle.polly.sdk; import java.util.HashMap; import java.util.Map; import de.skuzzle.polly.sdk.exceptions.CommandException; import de.skuzzle.polly.sdk.exceptions.DisposingException; import de.skuzzle.polly.sdk.exceptions.InsufficientRightsException; import de.skuzzle.polly.sdk.roles.RoleManager; import de.skuzzle.polly.sdk.time.Milliseconds; import de.skuzzle.polly.sdk.time.Time; /** * <p>This extends a normal command to remember the time of last execution per user to * define execution delays.</p> * * <p>You can set the quiet attribute to <code>false</code> if you do not want users to be * notified that they can not execute the command again in such short delay.</p> * * @author Simon * @since 0.9 * @see Command */ public abstract class DelayedCommand extends Command { private int delay; private boolean quiet; private Map<User, Long> lastExecutions; /** * Creates a new delayed Command. See the documentation of {@link Command} for * detailed information on how to use commands. This delayed command is not quiet * and thus will report execution errors caused by too short execution times. * * @param polly The MyPolly instance. * @param commandName The command name. * @param delay The execution delay in milliseconds for this command. * @see Command */ public DelayedCommand(MyPolly polly, String commandName, int delay) { this(polly, commandName, delay, false); } /** * Creates a new delayed Command. See the documentation of {@link Command} for * detailed information on how to use commands. You can define whether this command * should be quiet or not. If quiet is set to <code>true</code>, this command will * simply ignore consecutive executions with too short delay. If set to * <code>false</code>, an error will be reported. * * @param polly The MyPolly instance. * @param commandName The command name. * @param delay The execution delay in milliseconds for this command. * @param quiet Whether the command should be quiet or not. */ public DelayedCommand(MyPolly polly, String commandName, int delay, boolean quiet) { super(polly, commandName); this.delay = delay; this.lastExecutions = new HashMap<User, Long>(); this.quiet = quiet; } /** * Sets whether this command is quiet or not. * * @param quiet <code>true</code> if you want errors caused by too short consecutive * executions to be ignored or <code>false</code> if you want them to be * reported. */ public void setQuiet(boolean quiet) { this.quiet = quiet; } /** * Gets whether this command is quiet. * * @return <code>true</code> if this command is quiet. */ public boolean isQuiet() { return this.quiet; } /** * Sets the delay in milliseconds for this command. A user who executes this command * has to wait at least the amount of milliseconds given here before executing it * again. * * @param delay The execution delay in ms. */ public void setDelay(int delay) { this.delay = delay; } /** * Gets the execution delay in milliseconds for this command. * * @return The execution delay. */ public int getDelay() { return this.delay; } @Override public void doExecute(User executer, String channel, boolean query, Signature signature) throws InsufficientRightsException, CommandException { if (signature.match(this.getHelpSignature0()) != null || signature.match(this.getHelpSignature1()) != null) { super.doExecute(executer, channel, query, signature); return; } if (!this.checkDelay(executer)) { return; } try { super.doExecute(executer, channel, query, signature); this.rememberExecution(executer); } catch (InsufficientRightsException e) { // do not remember execution if user has no rights to execute the command throw e; } catch (CommandException e) { // remember execution even if error occurred this.rememberExecution(executer); throw e; } } /** * Stores the current Polly system time as provided by * {@link Time#currentTimeMillis()} as last execution for the given user. * * @param user The user who executes this command. */ protected void rememberExecution(User user) { synchronized (this.lastExecutions) { this.lastExecutions.put(user, Time.currentTimeMillis()); } } /** * Checks whether the given user has waited at least the delay of this command before * executing it again. * * @param user The user to check. * @return <code>true</code> if the user is allowed to execute the command again. * @throws CommandException If this command is not quiet, the exception informs the * user how long he has to wait before executing the command again. */ protected boolean checkDelay(User user) throws CommandException { if (this.getMyPolly().roles().hasPermission(user, RoleManager.ADMIN_PERMISSION)) { return true; } Long i = null; synchronized (this.lastExecutions) { i = this.lastExecutions.get(user); } long diff = 0L; if (i == null || (diff = Time.currentTimeMillis() - i) >= this.delay) { return true; } if (this.quiet) { return false; } final long remaining = Milliseconds.toSeconds(this.delay - diff); final String f = this.getMyPolly().formatting().formatTimeSpan(remaining); throw new CommandException(Messages.bind(Messages.delayedCommandCantExecute, f)); } @Override protected void actualDispose() throws DisposingException { super.actualDispose(); this.lastExecutions.clear(); this.lastExecutions = null; } }