diff --git a/commands.config b/commands.config index 63fd985..db12800 100644 --- a/commands.config +++ b/commands.config @@ -1,47 +1,48 @@ -indicator=1 -boop=1 -tony, stark, dontfeelgood, dontfeelsogood=1 -yeet=1 -role=1 -ping=1 -rating=1 -roll=1 -blur=1 -choose=1 -poll=1 -rpstart=1 -bet=1 -perish, thenperish=1 -teey=1 -stats=1 +[CommandEnabler] beans=1 -eightball, 8ball=1 -vote=1 -results=1 -wolfram=1 -map=1 -info, about=1 -givebeans=1 +benchmark, bench=1 +bet=1 +bethistory=1 +blur=1 +boop=1 +buildhelp=1 c++, g++, cplus, cpp=1 -work=1 -showpoll=1 -rpend=1 -rpg=1 -tweet=1 -java, jdoodle=1 -help=1 -buildHelp=1 -invite=1 -shutdown=1 -addguildrole=1 -allowedguildrole=1 -guildroleremove=1 +catch=1 +choose=1 +crouton=1 +dbflush=1 +dbstats=1 +eightball, 8ball=1 +fetch=1 +givebeans=1 guildroleadd=1 guildroleallowed=1 -fetch=1 -guildrolenotallowed=1 -catch=1 guildrolelist=1 -benchmark, bench=1 -bethistory=1 -crouton=1 +guildrolenotallowed=1 +guildroleremove=1 +help=1 +indicator=1 +info, about=1 +invite=1 +java, jdoodle=1 +map=1 +perish, thenperish=1 +ping=1 +poll=1 +rating=1 +results=1 +role=1 +roll=1 +rpend=1 +rpg=1 +rpstart=1 +showpoll=1 +shutdown=1 +stats=1 +teey=1 +tony, stark, dontfeelgood, dontfeelsogood=1 +tweet=1 +vote=1 +wolfram=1 +work=1 +yeet=1 diff --git a/locCommands.config b/locCommands.config index d71456e..5ef2ec1 100644 --- a/locCommands.config +++ b/locCommands.config @@ -20,6 +20,7 @@ perish, thenperish= teey= guildrolelist= stats= +dbstats= beans= eightball, 8ball= catch= @@ -43,6 +44,7 @@ buildHelp= fetch= crouton= invite= +dbflush= shutdown= diff --git a/locStrings.config b/locStrings.config index 14e8611..23c4205 100644 --- a/locStrings.config +++ b/locStrings.config @@ -138,6 +138,9 @@ RatingWarning=Warning: NSFW may slip through, images are only based on tags on t RatingChanged=Kittybot content set to RatingInvalid=Invalid content rating +[CommandDBStats] +DBStatsInfo= + [CommandGiveBeans] GiveBeansNoneMentioned=You didn't mention anyone! GiveBeansSuccess=Gave %s %s beans! @@ -229,6 +232,9 @@ WolframError=Something went wrong! WolframInfo=Will query wolframalpha with your question and give a full image output of the answer WolframNoArgs=You need to provide some arguments! +[CommandDBFlush] +DBFlushInfo= + [CommandMap] MapInfo=Generates a map! You can pass additional information if you want with the flags `-s-w-h`. If one of the fields isn't provided, its default will be used. Note that adjusting the width and height impacts the map outcomes.\n\nDefault seed: Random,\nDefault Width: 35(max %s),\nDefault Height: 25(max %s) MapSeed=Seed diff --git a/src/commands/CommandCrouton.java b/src/commands/CommandCrouton.java index e86d47c..e08821c 100644 --- a/src/commands/CommandCrouton.java +++ b/src/commands/CommandCrouton.java @@ -13,7 +13,7 @@ import dataStructures.KittyUser; import dataStructures.Response; import dataStructures.UserInput; -public class CommandCrouton extends Command +public class CommandCrouton extends Command { public CommandCrouton(KittyRole level, KittyRating rating) { super(level, rating); } diff --git a/src/commands/CommandDBFlush.java b/src/commands/CommandDBFlush.java new file mode 100644 index 0000000..484ac61 --- /dev/null +++ b/src/commands/CommandDBFlush.java @@ -0,0 +1,36 @@ +package commands; + +import java.awt.Color; + +import core.Command; +import core.DatabaseManager; +import core.LocStrings; +import dataStructures.KittyChannel; +import dataStructures.KittyEmbed; +import dataStructures.KittyGuild; +import dataStructures.KittyRating; +import dataStructures.KittyRole; +import dataStructures.KittyUser; +import dataStructures.Response; +import dataStructures.UserInput; + +public class CommandDBFlush extends Command +{ + public CommandDBFlush(KittyRole level, KittyRating rating) { super(level, rating); } + + @Override + public String HelpText() { return LocStrings.Stub("DBFlushInfo"); } + + @Override + public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res) + { + int numUpdated = DatabaseManager.instance.Upkeep(); + + KittyEmbed embed = new KittyEmbed(); + embed.title = "Database queue flushed"; + embed.descriptionText = "**Dirty:** " + numUpdated; + embed.color = new Color(7*16, 8*16, 9*16); + + res.CallEmbed(embed); + } +} diff --git a/src/commands/CommandDBStats.java b/src/commands/CommandDBStats.java new file mode 100644 index 0000000..ae8c012 --- /dev/null +++ b/src/commands/CommandDBStats.java @@ -0,0 +1,43 @@ +package commands; + +import java.awt.Color; +import java.text.DateFormat; +import java.text.SimpleDateFormat; + +import core.Command; +import core.DatabaseManager; +import core.LocStrings; +import dataStructures.KittyChannel; +import dataStructures.KittyEmbed; +import dataStructures.KittyGuild; +import dataStructures.KittyRating; +import dataStructures.KittyRole; +import dataStructures.KittyUser; +import dataStructures.Response; +import dataStructures.UserInput; + +public class CommandDBStats extends Command +{ + DateFormat dateFormat; + public CommandDBStats(KittyRole level, KittyRating rating) + { + super(level, rating); + dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); + } + + @Override + public String HelpText() { return LocStrings.Stub("DBStatsInfo"); } + + @Override + public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res) + { + KittyEmbed embed = new KittyEmbed(); + embed.title = "Database Info"; + embed.descriptionText = "**Tracked Items:** " + DatabaseManager.instance.GetTrackedObjectsSize(); + embed.descriptionText += "\n"; + embed.descriptionText += "**Last Upkeep:** " + dateFormat.format(DatabaseManager.instance.GetLastUpkeep()) + " UTC-7"; + embed.color = new Color(7*16, 8*16, 9*16); + + res.CallEmbed(embed); + } +} diff --git a/src/core/BaseKeyValueFile.java b/src/core/BaseKeyValueFile.java new file mode 100644 index 0000000..cb27fc5 --- /dev/null +++ b/src/core/BaseKeyValueFile.java @@ -0,0 +1,90 @@ +package core; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; +import java.util.ListIterator; +import java.util.function.Consumer; + +import dataStructures.Pair; +import utils.GlobalLog; +import utils.LogFilter; +import utils.io.FileUtils; + +public class BaseKeyValueFile +{ + // Variables + public static final String headerStart = "["; + public static final String headerEnd = "]"; + public static final String pairSplit = "="; + public static final char pairSeparator = '\n'; + + protected final String filename; + protected final String header; + + + // Constructor + public BaseKeyValueFile(String filename) + { + this.filename = filename; + this.header = headerStart + this.getClass().getSimpleName() + headerEnd; + } + + // Reads in and calls the specifid function for each keyvalue pair we find + protected void Parse(Consumer> keyValueCallback) + { + File f = new File(filename); + if(f.isFile() && f.canRead()) + { + String content = FileUtils.ReadContent(f).trim(); + String[] lines = content.split("" + pairSeparator); + + for(int i = 0; i < lines.length; ++i) + { + if(lines[i].contains(header)) + continue; + + String[] pair = lines[i].split(pairSplit); + + if(pair.length < 2) + continue; + + String key = pair[0].trim().toLowerCase(); + String value = pair[1].trim().toLowerCase(); + + keyValueCallback.accept(new Pair(key, value)); + } + } + } + + // Writes out a set of keyvalue pairs + protected void Write(List> toWrite) + { + try + { + ListIterator> iter = toWrite.listIterator(); + + String outString = ""; + outString += header + pairSeparator; + + while(iter.hasNext()) + { + Pair pair = iter.next(); + String key = pair.First.toLowerCase(); + String value = pair.Second.toLowerCase(); + + outString += key + pairSplit + value + pairSeparator; + } + + BufferedWriter writer = new BufferedWriter(new FileWriter(filename)); + writer.write(outString); + writer.close(); + } + catch (IOException e) + { + GlobalLog.Error(LogFilter.Core, "Issue writing file " + filename + ": " + e.getMessage()); + } + } +} diff --git a/src/core/LocBase.java b/src/core/BaseLocFile.java similarity index 96% rename from src/core/LocBase.java rename to src/core/BaseLocFile.java index 15f6c8f..7525e27 100644 --- a/src/core/LocBase.java +++ b/src/core/BaseLocFile.java @@ -9,15 +9,15 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import dataStructures.TaggedPairStore; -import utils.FileUtils; import utils.GlobalLog; import utils.LogFilter; import utils.io.FileMonitor; +import utils.io.FileUtils; import utils.io.MonitoredFile; // A quick-and-dirty localization tool that scrapes the project for calls to itself, then // generates/updates a file externally with all the stub values as keys that are localized. -public abstract class LocBase +public abstract class BaseLocFile { // Pre-defined values public static final String KittySourceDirectory = "./src"; @@ -37,8 +37,8 @@ public abstract class LocBase // File monitoring protected FileMonitor fileMonitor; - // Ok... so this is an array because if it's not an array, the parser will parse the string - public LocBase(String filename, String functionName) + // Constructor + public BaseLocFile(String filename, String functionName) { this.filename = filename; this.functionName = functionName; diff --git a/src/core/CommandEnabler.java b/src/core/CommandEnabler.java index a45f047..25a83cc 100644 --- a/src/core/CommandEnabler.java +++ b/src/core/CommandEnabler.java @@ -1,13 +1,11 @@ package core; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; - -import utils.FileUtils; +import java.util.List; +import java.util.Vector; +import dataStructures.Pair; import utils.GlobalLog; import utils.LogFilter; @@ -15,12 +13,9 @@ import utils.LogFilter; // commands that are being looked up will behave slightly differently so trimming // rules for this file are different than the localization ones - this is more // aggresive with whitespace removal. -public class CommandEnabler +public class CommandEnabler extends BaseKeyValueFile { // Config/const variables - public static final String filename = "commands.config"; - public static final String pairSplit = "="; - public static final char pairSeparator = '\n'; public static final String enabled = "1"; public static final String disabled = "0"; public static final boolean defaultEnabledState = true; @@ -28,13 +23,18 @@ public class CommandEnabler // Local variables private HashMap enabledMap; // Quick lookup private ArrayList keyList; // Tracking ordering for later + private final static String name = "commands.config"; public CommandEnabler() { + super(name); + + // Create/Init variables GlobalLog.Log(LogFilter.Core, "Initializing " + this.getClass().getSimpleName()); enabledMap = new HashMap<>(); keyList = new ArrayList<>(); + // Startup ReadIn(); GetTrackedCommands(); WriteOut(); @@ -43,30 +43,17 @@ public class CommandEnabler // Reads in the config file and parses it, keeping tabs on the order it read things private void ReadIn() { - File f = new File(filename); - if(f.isFile() && f.canRead()) - { - String content = FileUtils.ReadContent(f).trim(); - String[] lines = content.split("" + pairSeparator); + Parse((pair) ->{ + String key = pair.First; + String value = pair.Second; - for(int i = 0; i < lines.length; ++i) - { - String[] pair = lines[i].split(pairSplit); - - if(pair.length < 2) - continue; - - String key = pair[0].trim(); - String value = pair[1].trim().toLowerCase(); - - keyList.add(key); + keyList.add(key); - if(value.equalsIgnoreCase(enabled)) - enabledMap.putIfAbsent(key, true); - else - enabledMap.putIfAbsent(key, false); - } - } + if(value.equalsIgnoreCase(enabled)) + enabledMap.putIfAbsent(key, true); + else + enabledMap.putIfAbsent(key, false); + }); } // Look up the already scraped values from the localizer and store them if they @@ -77,7 +64,8 @@ public class CommandEnabler for(int i = 0; i < unloc.size(); ++i) { - String command = unloc.get(i); + String command = unloc.get(i).toLowerCase(); + if(enabledMap.putIfAbsent(command, defaultEnabledState) == null) { GlobalLog.Log(LogFilter.Strings, "Identified new toggleable raw command: " + command); @@ -89,35 +77,31 @@ public class CommandEnabler // Write out enabled/disabled file info. private void WriteOut() { - try + List> list = new Vector>(); + + for(int i = 0; i < keyList.size(); ++i) { - String outString = ""; - for(int i = 0; i < keyList.size(); ++i) - { - String key = keyList.get(i); - String value = enabled; - - if(enabledMap.get(key) == false) - value = disabled; - - outString += key + pairSplit + value + pairSeparator; - } + String key = keyList.get(i).toLowerCase(); + String value = enabled.toLowerCase(); - BufferedWriter writer = new BufferedWriter(new FileWriter(filename)); - writer.write(outString); - writer.close(); - } - catch (IOException e) - { - GlobalLog.Error(LogFilter.Core, "Command enabler issue writing file! " + e.getMessage()); + if(enabledMap.get(key) == false) + value = disabled.toLowerCase(); + + list.add(new Pair(key, value)); } + + Collections.sort(list, (c1, c2) -> { return c1.First.compareTo(c2.First); }); + + Write(list); } // Looks up a key to see if it's enabled or not public boolean IsEnabled(String key) { - if(enabledMap.containsKey(key)) - return enabledMap.get(key); + String toCheck = key.toLowerCase(); + + if(enabledMap.containsKey(toCheck)) + return enabledMap.get(toCheck); return true; } diff --git a/src/core/DatabaseDriver.java b/src/core/DatabaseDriver.java index be95113..b751e32 100644 --- a/src/core/DatabaseDriver.java +++ b/src/core/DatabaseDriver.java @@ -5,6 +5,7 @@ import java.sql.SQLException; import network.JDBCDriver; import network.JDBCDriverSQLite; +import network.JDBCStatementType; import utils.GlobalLog; import utils.LogFilter; @@ -28,14 +29,25 @@ public class DatabaseDriver driver = null; } + // Makes sure a table exists with the specified name. + // The keyName specifies the key column label, and the valueName specifies the value column label. + public void EnsureTableExists(String tableName, String keyName, String valueName) + { + // Require a global table if it doesn't exist already + driver.ExecuteStatement(JDBCStatementType.Create, "CREATE TABLE IF NOT EXISTS " + tableName + " (" + keyName + " text PRIMARY KEY, " + valueName + " text)", null); + } + // Set up and create a table in the database public boolean Connect() { driver = new JDBCDriverSQLite(); - driver.Connect(); + if(driver.Connect() == false) + { + return false; + } - // Require a global table if it doesn't exist already - driver.ExecuteStatement("CREATE TABLE IF NOT EXISTS " + globalTableName + " (" + globalKeyName + " text PRIMARY KEY, " + globalValueName + " text);", null); + // Verify tables we want to use exist + EnsureTableExists(globalTableName, globalKeyName, globalValueName); // General table. Do not remove. return true; } @@ -75,15 +87,16 @@ public class DatabaseDriver // Prototype formatting for key updating private void UpdateKey(String key, String value) { - String command = "UPDATE " + globalTableName + " SET " + globalValueName + " = ? WHERE " + globalKeyName + " = ?;"; - driver.ExecuteStatement(command, new String[] { key, value }); + String command = "UPDATE " + globalTableName + " SET " + globalValueName + " = ? WHERE " + globalKeyName + " = ?"; + boolean status = driver.ExecuteStatement(JDBCStatementType.Update, command, new String[] { value, key }); + GlobalLog.Log(LogFilter.Database, "UpdateKey status: " + status); } // Protoype updating for seeing if a key exists private boolean HasKey(String key) { - String command = "SELECT COUNT(1) as count FROM " + globalTableName + " WHERE " + globalKeyName + " = ?;"; - ResultSet set = driver.ExecuteReturningStatement(command, new String[] { key }); + String command = "SELECT COUNT(1) as count FROM " + globalTableName + " WHERE " + globalKeyName + " = ?"; + ResultSet set = driver.ExecuteReturningStatement(JDBCStatementType.Select, command, new String[] { key }); String out = ResultAsString(set, "count"); return out.charAt(0) == '1'; } @@ -91,16 +104,17 @@ public class DatabaseDriver // Prototype for getting a key private String GetKey(String key) { - String command = "SELECT " + globalValueName + " as searchedKey FROM " + globalTableName +" WHERE " + globalKeyName + " = ?;"; - ResultSet set = driver.ExecuteReturningStatement(command, new String[] { key }); + String command = "SELECT " + globalValueName + " as searchedKey FROM " + globalTableName +" WHERE " + globalKeyName + " = ?"; + ResultSet set = driver.ExecuteReturningStatement(JDBCStatementType.Select, command, new String[] { key }); return ResultAsString(set, "searchedKey"); } // Prototype for creating a key private void CreateKey(String key, String value) { - String command = "INSERT INTO " + globalTableName + " (GlobalKey, GlobalValue) VALUES (?, ?);"; - driver.ExecuteStatement(command, new String[] { key, value }); + String command = "INSERT INTO " + globalTableName + " (GlobalKey, GlobalValue) VALUES (?, ?)"; + boolean status = driver.ExecuteStatement(JDBCStatementType.Insert, command, new String[] { key, value }); + GlobalLog.Log(LogFilter.Database, "CreateKey status: " + status); } // Transforms a result into a string if possible. diff --git a/src/core/DatabaseManager.java b/src/core/DatabaseManager.java index b1f72ec..638fe2b 100644 --- a/src/core/DatabaseManager.java +++ b/src/core/DatabaseManager.java @@ -1,7 +1,7 @@ package core; -import java.util.ArrayList; - +import java.util.Date; +import java.util.Vector; import utils.GlobalLog; import utils.LogFilter; @@ -11,11 +11,14 @@ public class DatabaseManager public static DatabaseManager instance = null; // Private internal variables - private ArrayList trackedObjects; + private Vector trackedObjects; private DatabaseDriver driver; + private Date lastUpkeep; public DatabaseManager() { + GlobalLog.Log(LogFilter.Database, "Creating database manager"); + if(instance == null) { instance = this; @@ -26,40 +29,75 @@ public class DatabaseManager return; } - trackedObjects = new ArrayList(); + lastUpkeep = new Date(); + trackedObjects = new Vector(); driver = new DatabaseDriver(); - driver.Connect(); + + if(driver.Connect() == false) + { + GlobalLog.Error("Database failed to connect. Currently, without the DB, this bot can not run."); + System.exit(1); + } } // Thumbs through registered objects and syncs them with the database. // Consider moving this operation to a separate thread. - public void Upkeep() + public int Upkeep() { - for(int i = 0 ; i < trackedObjects.size(); ++i) + synchronized(trackedObjects) { - DatabaseTrackedObject dto = trackedObjects.get(i); + int numUpdated = 0; - if(dto.IsDirty()) + for(int i = 0 ; i < trackedObjects.size(); ++i) { - SetRemoteValue(dto.identifier, dto.Serialize()); - dto.Resolve(); + DatabaseTrackedObject dto = trackedObjects.get(i); + + if(dto.IsDirty()) + { + SetRemoteValue(dto.identifier, dto.Serialize()); + dto.Resolve(); + ++numUpdated; + } } + + lastUpkeep = new Date(); + + return numUpdated; } } public void Register(DatabaseTrackedObject tracked) { - trackedObjects.add(tracked); - tracked.DeSerialzie(GetRemoteValue(tracked.identifier)); + synchronized(trackedObjects) + { + trackedObjects.add(tracked); + tracked.DeSerialzie(GetRemoteValue(tracked.identifier)); + } } public String GetRemoteValue(String key) { - return driver.CreateGetKey(key); + synchronized(driver) + { + return driver.CreateGetKey(key); + } } public void SetRemoteValue(String key, String value) { - driver.CreateSetKey(key, value); + synchronized(driver) + { + driver.CreateSetKey(key, value); + } + } + + public Date GetLastUpkeep() + { + return lastUpkeep; + } + + public int GetTrackedObjectsSize() + { + return trackedObjects.size(); } } diff --git a/src/core/LocCommands.java b/src/core/LocCommands.java index 7d75228..6d5b9bd 100644 --- a/src/core/LocCommands.java +++ b/src/core/LocCommands.java @@ -8,7 +8,7 @@ import dataStructures.Pair; // Performs the same localization for the strings associated with command names as // is performed with general strings in the application -public class LocCommands extends LocBase +public class LocCommands extends BaseLocFile { public static final String fileName = "locCommands.config"; public static final String function = "LocCommands.Stub"; diff --git a/src/core/LocStrings.java b/src/core/LocStrings.java index d4a0bd0..7b95573 100644 --- a/src/core/LocStrings.java +++ b/src/core/LocStrings.java @@ -7,7 +7,7 @@ import utils.io.FileMonitor; // A quick-and-dirty localization tool that scrapes the project for calls to itself, then // generates/updates a file externally (phrases.config) with all the stub values as keys that // can then be localized. -public class LocStrings extends LocBase +public class LocStrings extends BaseLocFile { public static final String fileName = "locStrings.config"; public static final String function = "LocStrings.Stub"; diff --git a/src/core/ObjectBuilderFactory.java b/src/core/ObjectBuilderFactory.java index 92aeadb..63ffa4c 100644 --- a/src/core/ObjectBuilderFactory.java +++ b/src/core/ObjectBuilderFactory.java @@ -328,22 +328,28 @@ public class ObjectBuilderFactory CommandManager manager = new CommandManager(commandEnabler); + // Dev manager.Register(LocCommands.Stub("work"), new CommandDoWork(KittyRole.Dev, KittyRating.Safe)); manager.Register(LocCommands.Stub("shutdown"), new CommandShutdown(KittyRole.Dev, KittyRating.Safe)); manager.Register(LocCommands.Stub("stats"), new CommandStats(KittyRole.Dev, KittyRating.Safe)); manager.Register(LocCommands.Stub("invite"), new CommandInvite(KittyRole.Dev, KittyRating.Safe)); manager.Register(LocCommands.Stub("buildHelp"), new CommandHelpBuilder(KittyRole.Dev, KittyRating.Safe)); manager.Register(LocCommands.Stub("tweet"), new CommandTweet(KittyRole.Dev, KittyRating.Safe)); + manager.Register(LocCommands.Stub("dbflush"), new CommandDBFlush(KittyRole.Dev, KittyRating.Safe)); + manager.Register(LocCommands.Stub("dbstats"), new CommandDBStats(KittyRole.Dev, KittyRating.Safe)); + // Admin manager.Register(LocCommands.Stub("rating"), new CommandRating(KittyRole.Admin, KittyRating.Safe)); manager.Register(LocCommands.Stub("indicator"), new CommandChangeIndicator(KittyRole.Admin, KittyRating.Safe)); manager.Register(LocCommands.Stub("guildroleallowed"), new CommandGuildRoleAllowed(KittyRole.Admin, KittyRating.Safe)); manager.Register(LocCommands.Stub("guildrolenotallowed"), new CommandGuildRoleNotAllowed(KittyRole.Admin, KittyRating.Safe)); + // Mod manager.Register(LocCommands.Stub("poll"), new CommandPollManage(KittyRole.Mod, KittyRating.Safe)); manager.Register(LocCommands.Stub("givebeans"), new CommandGiveBeans(KittyRole.Mod, KittyRating.Safe)); manager.Register(LocCommands.Stub("rpg"), new CommandRPG(KittyRole.Mod, KittyRating.Safe)); + // General manager.Register(LocCommands.Stub("fetch"), new CommandFetch(KittyRole.General, KittyRating.Safe)); manager.Register(LocCommands.Stub("guildroleadd"), new CommandGuildRoleAdd(KittyRole.General, KittyRating.Safe)); manager.Register(LocCommands.Stub("guildroleremove"), new CommandGuildRoleRemove(KittyRole.General, KittyRating.Safe)); @@ -375,7 +381,6 @@ public class ObjectBuilderFactory manager.Register(LocCommands.Stub("guildrolelist"), new CommandGuildRoleList(KittyRole.General, KittyRating.Safe)); manager.Register(LocCommands.Stub("bethistory"), new CommandBetHistory(KittyRole.General, KittyRating.Safe)); manager.Register(LocCommands.Stub("crouton"), new CommandCrouton(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.Stub("benchmark, bench"), new CommandBenchmark(KittyRole.General, KittyRating.Safe)); return manager; diff --git a/src/core/Settings.java b/src/core/Settings.java new file mode 100644 index 0000000..9ffdf42 --- /dev/null +++ b/src/core/Settings.java @@ -0,0 +1,5 @@ +package core; + +public class Settings { + +} diff --git a/src/core/benchmark/BenchmarkManager.java b/src/core/benchmark/BenchmarkManager.java index 166cab4..dcf5612 100644 --- a/src/core/benchmark/BenchmarkManager.java +++ b/src/core/benchmark/BenchmarkManager.java @@ -6,8 +6,8 @@ import java.util.Collections; import java.util.List; import dataStructures.Pair; -import utils.FileUtils; import utils.io.DirectoryMonitor; +import utils.io.FileUtils; import utils.io.MonitoredFile; // All things considered, this doesn't need to be particularly efficient since anything diff --git a/src/dataStructures/KittyRole.java b/src/dataStructures/KittyRole.java index baec91c..edda23e 100644 --- a/src/dataStructures/KittyRole.java +++ b/src/dataStructures/KittyRole.java @@ -18,8 +18,8 @@ public enum KittyRole return value; } - public static Optional valueOf(int value) - { - return Arrays.stream(values()).filter(role -> role.value == value).findFirst(); - } + public static Optional valueOf(int value) + { + return Arrays.stream(values()).filter(role -> role.value == value).findFirst(); + } } diff --git a/src/main/Superintendent.java b/src/main/Superintendent.java index 84da5fc..207a642 100644 --- a/src/main/Superintendent.java +++ b/src/main/Superintendent.java @@ -1,5 +1,7 @@ package main; +import java.util.concurrent.atomic.AtomicInteger; + import core.DatabaseManager; import core.LocCommands; import core.LocStrings; @@ -68,20 +70,6 @@ public class Superintendent return true; } - // This is for stuff that we need to do on a regular basis, but don't - // necessarily want running at all points in time. - // Happens just after the command / plugin runs. - public static boolean PerCommandUpkeepPost(JDA bot, DatabaseManager databaseManager) - { - // Upkeep database - databaseManager.Upkeep(); - - // Update command-specific - RPManager.Upkeep(bot); - - return true; - } - // Only called once per command. Good for lazily updating. // Happens just before the command / plugin runs. public static boolean PerCommandUpkeepPre() @@ -92,4 +80,25 @@ public class Superintendent return true; } + + private static final Integer delayTimerReset = 5; + private static AtomicInteger delayTimerCurrent = new AtomicInteger(delayTimerReset); + + // This is for stuff that we need to do on a regular basis, but don't + // necessarily want running at all points in time. + // Happens just after the command / plugin runs. + public static boolean PerCommandUpkeepPost(JDA bot, DatabaseManager databaseManager) + { + // Upkeep database lazily on occasion + if(delayTimerCurrent.decrementAndGet() < 0) + { + databaseManager.Upkeep(); + delayTimerCurrent.set(delayTimerReset); + } + + // Update command-specific + RPManager.Upkeep(bot); + + return true; + } } diff --git a/src/network/JDBCDriver.java b/src/network/JDBCDriver.java index 639c1dd..bd99ee1 100644 --- a/src/network/JDBCDriver.java +++ b/src/network/JDBCDriver.java @@ -15,6 +15,6 @@ public abstract class JDBCDriver // Executes a SQL command with the database. Returns if it was executed successfully, or in the // case of the returning statement, returns the ResultSet. Args are placed into the prepared statement // in place of each '?' places into it. - public abstract boolean ExecuteStatement(String command, String[] args); - public abstract ResultSet ExecuteReturningStatement(String command, String[] args); + public abstract boolean ExecuteStatement(JDBCStatementType type, String command, String[] args); + public abstract ResultSet ExecuteReturningStatement(JDBCStatementType type, String command, String[] args); } diff --git a/src/network/JDBCDriverMySQL.java b/src/network/JDBCDriverMySQL.java index 2a29596..5fbb404 100644 --- a/src/network/JDBCDriverMySQL.java +++ b/src/network/JDBCDriverMySQL.java @@ -17,13 +17,13 @@ public class JDBCDriverMySQL extends JDBCDriver } @Override - public boolean ExecuteStatement(String statement, String[] args) { + public boolean ExecuteStatement(JDBCStatementType type, String statement, String[] args) { // TODO Auto-generated method stub return false; } @Override - public ResultSet ExecuteReturningStatement(String statement, String[] args) { + public ResultSet ExecuteReturningStatement(JDBCStatementType type, String statement, String[] args) { // TODO Auto-generated method stub return null; } diff --git a/src/network/JDBCDriverPostgreSQL.java b/src/network/JDBCDriverPostgreSQL.java index 823530a..b6765eb 100644 --- a/src/network/JDBCDriverPostgreSQL.java +++ b/src/network/JDBCDriverPostgreSQL.java @@ -17,13 +17,13 @@ public class JDBCDriverPostgreSQL extends JDBCDriver } @Override - public boolean ExecuteStatement(String statement, String[] args) { + public boolean ExecuteStatement(JDBCStatementType type, String statement, String[] args) { // TODO Auto-generated method stub return false; } @Override - public ResultSet ExecuteReturningStatement(String statement, String[] args) { + public ResultSet ExecuteReturningStatement(JDBCStatementType type, String statement, String[] args) { // TODO Auto-generated method stub return null; } diff --git a/src/network/JDBCDriverSQLite.java b/src/network/JDBCDriverSQLite.java index 98a9688..8fc64ee 100644 --- a/src/network/JDBCDriverSQLite.java +++ b/src/network/JDBCDriverSQLite.java @@ -64,7 +64,7 @@ public class JDBCDriverSQLite extends JDBCDriver } @Override - public ResultSet ExecuteReturningStatement(String command, String[] args) + public ResultSet ExecuteReturningStatement(JDBCStatementType type, String command, String[] args) { if(connection == null) return null; @@ -77,11 +77,21 @@ public class JDBCDriverSQLite extends JDBCDriver if(args != null && args.length > 0) { PreparedStatement statement = connection.prepareStatement(command); + ResultSet set = null; for(int i = 0; i < args.length; ++i) statement.setString(i + 1, args[i]); - ResultSet set = statement.executeQuery(); + switch(type) + { + case Select: + set = statement.executeQuery(); + break; + + default: + throw new Exception("Unsupported statement type for this function!"); + } + return set; } else @@ -91,7 +101,7 @@ public class JDBCDriverSQLite extends JDBCDriver return set; } } - catch (SQLException e) + catch (Exception e) { try { @@ -106,7 +116,7 @@ public class JDBCDriverSQLite extends JDBCDriver } } - public boolean ExecuteStatement(String command, String[] args) + public boolean ExecuteStatement(JDBCStatementType type, String command, String[] args) { if(connection == null) return false; @@ -118,12 +128,34 @@ public class JDBCDriverSQLite extends JDBCDriver { if(args != null && args.length > 0) { + boolean executed = false; PreparedStatement statement = connection.prepareStatement(command); + //GlobalLog.Log("COMMAND IS -- " + command); for(int i = 0; i < args.length; ++i) + { + //GlobalLog.Log("Args: " + args[i]); statement.setString(i + 1, args[i]); - - boolean executed = statement.execute(); + } + + switch(type) + { + case Create: + executed = statement.executeUpdate() == 1; + break; + + case Update: + executed = statement.executeUpdate() == 1; + break; + + case Insert: + executed = statement.executeUpdate() == 1; + break; + + default: + throw new Exception("Unsupported statement type for this function!"); + } + return executed; } else @@ -133,7 +165,7 @@ public class JDBCDriverSQLite extends JDBCDriver return executed; } } - catch (SQLException e) + catch (Exception e) { try { diff --git a/src/network/JDBCStatementType.java b/src/network/JDBCStatementType.java new file mode 100644 index 0000000..96aa884 --- /dev/null +++ b/src/network/JDBCStatementType.java @@ -0,0 +1,25 @@ +package network; + +import java.util.Arrays; +import java.util.Optional; + +public enum JDBCStatementType +{ + Insert (0), Update(1), Select(2), Create(4); + + private final int value; + private JDBCStatementType(int value) + { + this.value = value; + } + + public int getValue() + { + return value; + } + + public static Optional valueOf(int value) + { + return Arrays.stream(values()).filter(role -> role.value == value).findFirst(); + } +} diff --git a/src/utils/GlobalLog.java b/src/utils/GlobalLog.java index 5914d59..0131587 100644 --- a/src/utils/GlobalLog.java +++ b/src/utils/GlobalLog.java @@ -7,8 +7,11 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; +import utils.io.FileUtils; + public class GlobalLog { + private static final String directory = "logs/"; private static final String log = "Log"; private static final String warn = "Warning"; private static final String error = "ERROR"; @@ -19,7 +22,10 @@ public class GlobalLog { DateFormat dF = new SimpleDateFormat("yyyy_MM_dd_HH-mm"); Date today = new Date(); - outputLog = new PrintWriter((dF.format(today) + ".log"), "UTF-8"); + + FileUtils.CreateDirectoryIfDoesntExist(directory); + outputLog = new PrintWriter(directory + (dF.format(today) + ".log"), "UTF-8"); + GlobalLog.Log(LogFilter.Util, "Finished initializing logging system"); } private static void Write(String status, LogFilter filter, String body) diff --git a/src/utils/io/DirectoryMonitor.java b/src/utils/io/DirectoryMonitor.java index 3f7c493..28f33f2 100644 --- a/src/utils/io/DirectoryMonitor.java +++ b/src/utils/io/DirectoryMonitor.java @@ -9,8 +9,6 @@ import java.util.List; import java.util.function.Consumer; import java.util.stream.Stream; -import utils.FileUtils; - // NOTE: Consider shifting internal behavior to https://docs.oracle.com/javase/tutorial/essential/io/notification.html // for external stability and support public class DirectoryMonitor diff --git a/src/utils/io/FileMonitor.java b/src/utils/io/FileMonitor.java index 283052a..80de434 100644 --- a/src/utils/io/FileMonitor.java +++ b/src/utils/io/FileMonitor.java @@ -3,8 +3,6 @@ package utils.io; import java.nio.file.Paths; import java.util.function.Consumer; -import utils.FileUtils; - // Monitors a single file for changes - the file must exist and is expected to continue to exist. public class FileMonitor { diff --git a/src/utils/FileUtils.java b/src/utils/io/FileUtils.java similarity index 87% rename from src/utils/FileUtils.java rename to src/utils/io/FileUtils.java index 97fe636..21edfd6 100644 --- a/src/utils/FileUtils.java +++ b/src/utils/io/FileUtils.java @@ -1,4 +1,4 @@ -package utils; +package utils.io; import java.io.File; import java.io.IOException; @@ -9,8 +9,20 @@ import java.nio.file.Paths; import java.util.ArrayList; import java.util.stream.Stream; +import utils.GlobalLog; +import utils.LogFilter; + public class FileUtils { + public static void CreateDirectoryIfDoesntExist(String directoryName) + { + File directory = new File(directoryName); + + if (! directory.exists()){ + directory.mkdir(); + } + } + // Reads all lines from a file as a string public static String ReadContent(File file) { return ReadContent(file.toPath()); } public static String ReadContent(Path filePath)