publicclassCommand{//no need to be abstract anymore //make it final to ensure thread safety?
protectedStringname;
protectedStringdesc;
protectedStringusage;
protectedList<String>alias;//fixed-size in this case is negligible, as it will not be edited later on anyways
protectedList<String>remarks=null;
protectedbooleanisDisabled;
protectedEnumSet<Permission>perms=EnumSet.noneOf(Permission.class);//so that not configured commands would have a default of no perms needed, instead of null //WILL NOT ALLOW DENY OVERRIDE FOR NOW
protectedintbotUserLevel;//0 = everyone, 1 = BotMod, 2 = BotOwner; negative value = either BotUser or required perm
protectedList<String>examples;
protectedExecuteImplimpl=null;
privateCommandparent=null;
privateStringcat;//not used; remove? //actually gets used now
privateLinkedHashMap<String,Command>subCmds=newLinkedHashMap<>();//preserve order
//only used by nested commands, since all explicitly declared commands override execute(); hot creation of subcommands are possible, but permission setting via !desp settings is not supported
publicinterfaceExecuteImpl{//DONE make this a seperate class to store the ever increasing params? if so, protected constructor since it doesnt need to be instantiated elsewhere other than registerSubCommand here //implemented nested commands instead
publicvoidregisterSubCommand(Stringname,List<String>aliases,Command.ExecuteImplexe,Stringusage,List<String>examples,Stringdesc,List<String>remarks){//subcmd has its own examples? //DONE subcmd permissions and botuserlevel //probably not needed, as its gonna be binded to guild instead of cmds soon enough
publicenumBotUserLevel{//DONE? implement this DONE test this
DEFAULT,
BOT_MOD,
BOT_OWNER
}
publicStringgetCategory(){//is null if it is a subcommand
returncat;
}
publicStringgetName(){
returnname;
}
publicStringgetDesc(){
returndesc;
}
publicStringgetUsage(){
returnusage;
}
publicList<String>getAliases(){
returnalias;
}
publicList<String>getRemarks(){
returnremarks;
}
publicEnumSet<Permission>getDefaultPerms(){//DONE make this channel dependent?
returnperms;
}
publicintgetRequiredBotUserLevel(){
returnbotUserLevel;
}
publicList<String>getExamples(){
returnexamples;
}
publicbooleanisDisabled(){//FIXED think of what to do with disabledGuild; it makes the command class not thread safe; store in DB instead, and be channel based?
returnisDisabled;
}
//even when multiple thread references to one specific command, it should be thread safe because it doesnt modify anything in the execute stage; and even the hashmap is somewhat immutable as it will never be changed after finished loading
//i dont think i need to even volatile that hashmap
//the only thing i might need to do to make it thread safe is to make the hashmap a concurrenthashmap
publicCommandResultexecute(TextChannelchannel,Userauthor,Messagemsg,String[]args){//if you want a main command to work with sub commands, just super() this and then write the main command stuff
OffsetDateTimetimesent=OffsetDateTime.now();
try(Connectioncon=DiscordBot.db.getConnection();Statements=con.createStatement()){//not the best, but i cant share connection through threads
botUserLevel=temp.getRequiredBotUserLevel();//if not set get parent's
}
Stringperms=subCmd.hasSubCommand()?null:MiscUtils.getMissingPerms(MiscUtils.getActivePerms(s,channel,subCmd),botUserLevel,channel.getGuild().getMember(author),channel);//yet again pass to handler
if(impl==null)returnnewCommandResult(CommandResultType.INVALIDARGS,"Unknown sub command! Check `"+prefix+"help "+this.getName()+"` for more info.");
}
}else{//only run if no execute definition, if not let it override default messages
if(impl==null)returnnewCommandResult(CommandResultType.INVALIDARGS,"Please specify a subcommand. You can view them with `"+prefix+"help "+this.name+"`.");
}
}
returnimpl.execute(channel,author,msg,args);//fallback if no subcommand found; useful for defining custom messages or default command for a set of subcommands
.exceptionally(ex->newCommandResult(CommandResultType.ERROR,ExceptionUtils.getStackTrace(ex.getCause())))//getCause should always work as ex is a CompletionException
.thenAccept(success).thenAccept(r->{//DONE thenAccept parses the CommandResult