diff --git a/.classpath b/.classpath index 7c63137..9f1f1b2 100644 --- a/.classpath +++ b/.classpath @@ -14,5 +14,6 @@ + diff --git a/commands.config b/commands.config index 5b3532f..e2280cb 100644 --- a/commands.config +++ b/commands.config @@ -8,7 +8,12 @@ boop=1 buildhelp=1 c++, g++, cplus, cpp=1 catch=1 +charactercreate=1 +charactereditbio=1 +charactereditname=1 +characterediturl=1 choose=1 +color, colour=1 crouton=1 dbflush=1 dbstats=1 @@ -25,6 +30,7 @@ indicator=1 info, about=1 invite=1 java, jdoodle=1 +leaderboard=1 map=1 perish, thenperish=1 ping=1 @@ -40,6 +46,7 @@ roll=1 rpend=1 rpg=1 rpstart=1 +searchcharacter=1 showpoll=1 shutdown=1 stats=1 diff --git a/lib/fuzzywuzzy-1.2.0.jar b/lib/fuzzywuzzy-1.2.0.jar new file mode 100644 index 0000000..a94b728 Binary files /dev/null and b/lib/fuzzywuzzy-1.2.0.jar differ diff --git a/locCommands.config b/locCommands.config index b1e2cc7..3a35955 100644 --- a/locCommands.config +++ b/locCommands.config @@ -7,10 +7,12 @@ benchmark, bench= rating= poll= rafflespin= +charactereditURL= bet= guildrolelist= stats= catch= +color, colour= guildroleadd= results= wolfram= @@ -21,6 +23,7 @@ rpend= java, jdoodle= rafflejoin= guildroleallowed= +leaderboard= buildHelp= dbflush= shutdown= @@ -29,6 +32,7 @@ tony, stark, dontfeelgood, dontfeelsogood= role= raffleend= ping= +charactereditbio= roll= blur= choose= @@ -36,6 +40,7 @@ rpstart= guildrolenotallowed= perish, thenperish= teey= +searchcharacter= dbstats= rafflestart= beans= @@ -46,7 +51,9 @@ info, about= showpoll= rpg= tweet= +charactercreate= help= +charactereditname= fetch= crouton= invite= diff --git a/locStrings.config b/locStrings.config index 7d69929..d402f1e 100644 --- a/locStrings.config +++ b/locStrings.config @@ -17,6 +17,10 @@ BetHistoryInfo=Sees the amount of beans Kitty has earned through your bets! [CommandCatch] CatchInfo=Play catch with yourself or with a friend with @! +[CommandLeaderboard] +LeaderboardInfo=Shows you the 10 users with the most beans on the server +LeaderboardTitle=Server Leaderboard + [CommandGuildRoleList] GuildRoleListOutput=The allowed roles are %s! GuildRoleListEmpty=There are no roles allowed! @@ -85,6 +89,17 @@ RaffleEndSuccess= RaffleEndInfo= RaffleEndFailure=I don't think there's one going! +[CommandCharacterSearch] +CharacterSearchNoCharacterFound=Couldn't find a character with that name or ID! +CharacterSearchMultipleCharacterHeader=I found a couple characters with that name, use `charactersearch UniqueID` with their Unique ID to find them! +CharacterSearchMultipleCharacter=%s is owned by %s. Unique ID is `%s` +CharacterSearchOneCharacter=I found one character with that name! \n Owner: %s | Name: %s | Bio: %s | Ref: <%s> | Unique ID: `%s` +CharacterSearchInfo=Find a character with their name or Unique ID! `charactersearch name` or `charactersearch UniqueID` + +[CommandColor] +ColorNotSearchable=Your color couldn't be parsed! Make sure it's in hex format. Try this! `!color 3F5D54` +ColorInfo=Looks up the provided hex color and outputs it as hex, rgb, hsl, hsv, xyz, and cmyk. Also attempts to name the color, and provies a sample of it! Usage example: `!color #365345` (the # can be omitted) + [CommandDoWork] DoWorkFinished=I finished with my work! :D DoWorkInfo=Occupies a core for a few seconds @@ -119,6 +134,13 @@ PollShowChoices=And the choices are:\n [CommandHelpBuilder] HelpBuilderInfo=Emit help for all commands as formatted HTML +[CommandCharacterEditURL] +CharacterEditURLInfo= +CharacterEditURLNotValid= +CharacterEditURLSuccess= +CharacterEditURLNotAuth= +CharacterEditURLIDNotValid= + [CommandRPG] RPGInfo=Admin+ command only right now - experimental text RPG! Format is !rpg RPGInvalid=Invalid RPG Command! @@ -141,7 +163,7 @@ InfoInfo=Provides author info and a link to Kitty's website GuildRoleRemoveNotAllowed=You're not allowed to add %s GuildRoleRemoveFailure=Couldn't remove %s from %s GuildRoleRemoveSuccess=Removed %s from %s! -GuildRoleRemoveInfo= +GuildRoleRemoveInfo=Removes the role specified! [CommandWolfram] WolframError=Something went wrong! @@ -149,7 +171,7 @@ WolframInfo=Will query wolframalpha with your question and give a full image out WolframNoArgs=You need to provide some arguments! [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) +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\n**Defaults**\nDefault Seed: Random,\nDefault Width: 35(max %s),\nDefault Height: 25(max %s)\n\n**Key**\nLand: ˶\nMountain: ▲\nBeach: =\nMarsh: ¥\nLava: # MapSeed=Seed MapInvalid=Invalid arguments provided! MapVersion=Using mapgen v0.1 @@ -171,6 +193,13 @@ RaffleStartInfo= HelpInfo=Lets you look up specific commands, or get a link to a list of all commands. HelpDisplay=You can get help with a specific command by typing `!help command`!\nGeneral Commands: `boop, roll, choose, help, info, vote, results, showpoll, wolfram, cplus, java, beans, role, bet, yeet` +[CommandCharacterEditBio] +CharacterEditBioSuccess= +CharacterEditBioIDNotValid= +CharacterEditBioNotAuth= +CharacterEditBioInfo= +CharacterEditBioNotValid= + [CommandTweet] TweetInfo=Kitty will tweet to her personal twitter account TweetError=Tweet command failed! @@ -181,6 +210,13 @@ GuildRoleAddFailure=Failed to add %s to %s GuildRoleAddNotAllowed=You are not allowed to add %s! GuildRoleAddInfo=Add a role to yourself! +[CommandCharacterEditName] +CharacterEditNameInfo= +CharacterEditNameNotValid= +CharacterEditNameIDNotValid= +CharacterEditNameSuccess= +CharacterEditNameNotAuth= + [CommandColiru] ColiruError=Please provide some c++ code to attempt to compile! ColiruInfo=Will try to compile any c++ code you put in! Supports up to C++14 standard, uses g++. @@ -194,6 +230,12 @@ YeetInfo=Yeet yourself or yeet a friend with @! [CommandCrouton] CroutonInfo=Crouton +[CommandCharacterCreate] +CharacterCreateDuplicate=You can't create a character with the same name! +CharacterCreateInfo=Create a character for kitty to hold onto for you! `createcharacter name, shortbio, refURL` +CharacterCreateSuccess=You successfully created a character! +CharacterCreateNullInfo=There's not enough info! + [CommandTeey] TeeyInfo=Teey yourself back into existance, or teey a friend with @! @@ -262,6 +304,6 @@ RaffleJoinInfo= RaffleJoinSuccess=You joined the raffle! [CommandDBFlush] -DBFlushInfo= +DBFlushInfo=Takes all objects queued for the database and writes them to the database, then says how many were written. diff --git a/src/commands/CommandBlurry.java b/src/commands/CommandBlurry.java index fcc0605..1e5e983 100644 --- a/src/commands/CommandBlurry.java +++ b/src/commands/CommandBlurry.java @@ -39,7 +39,7 @@ public class CommandBlurry extends Command { try { - filename = ImageUtils.DownloadFromURL(input.args.split(" ")[0], ".png"); + filename = ImageUtils.downloadFromURL(input.args.split(" ")[0], ".png"); preProcessed = new File(filename); } catch(Exception e) @@ -48,7 +48,7 @@ public class CommandBlurry extends Command if(input.mentions != null) person = input.mentions[0]; - filename = ImageUtils.DownloadFromURL(person.avatarID, ".png"); + filename = ImageUtils.downloadFromURL(person.avatarID, ".png"); if(filename == null) return; } diff --git a/src/commands/CommandCatch.java b/src/commands/CommandCatch.java index bd74917..31f4b57 100644 --- a/src/commands/CommandCatch.java +++ b/src/commands/CommandCatch.java @@ -48,7 +48,7 @@ public class CommandCatch extends Command else person = input.mentions[0]; - String catchFilename = ImageUtils.DownloadFromURL(person.avatarID, ".png"); + String catchFilename = ImageUtils.downloadFromURL(person.avatarID, ".png"); if(catchFilename == null) return; diff --git a/src/commands/CommandCharacterCreate.java b/src/commands/CommandCharacterCreate.java new file mode 100644 index 0000000..12a22c5 --- /dev/null +++ b/src/commands/CommandCharacterCreate.java @@ -0,0 +1,37 @@ +package commands; + +import core.CharacterManager; +import core.Command; +import core.LocStrings; +import dataStructures.KittyChannel; +import dataStructures.KittyGuild; +import dataStructures.KittyRating; +import dataStructures.KittyRole; +import dataStructures.KittyUser; +import dataStructures.Response; +import dataStructures.UserInput; + +public class CommandCharacterCreate extends Command +{ + public CommandCharacterCreate(KittyRole level, KittyRating rating) { super(level, rating); } + + @Override + public String HelpText() { return LocStrings.Stub("CharacterCreateInfo"); } + + @Override + public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res) + { + String [] info = input.args.split(","); + if(info.length < 3) + { + res.Call(LocStrings.Stub("CharacterCreateNullInfo")); + return; + } + if(CharacterManager.instance.addCharacter(user, info[0], info[1], info[2])) + { + res.Call(LocStrings.Stub("CharacterCreateSuccess")); + return; + } + res.Call(LocStrings.Stub("CharacterCreateDuplicate")); + } +} \ No newline at end of file diff --git a/src/commands/CommandCharacterEditBio.java b/src/commands/CommandCharacterEditBio.java new file mode 100644 index 0000000..52b57c7 --- /dev/null +++ b/src/commands/CommandCharacterEditBio.java @@ -0,0 +1,38 @@ +package commands; + +import core.CharacterManager; +import core.Command; +import core.LocStrings; +import dataStructures.*; + +public class CommandCharacterEditBio extends Command +{ + public CommandCharacterEditBio(KittyRole level, KittyRating rating) { super(level, rating); } + + @Override + public String HelpText() { return LocStrings.Stub("CharacterEditBioInfo"); } + + @Override + public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res) + { + try + { + Long.parseLong(input.args.split(" ")[0]); + } + catch(Exception e) + { + res.Call(LocStrings.Stub("CharacterEditBioNotValid")); + return; + } + KittyCharacter character = CharacterManager.instance.searchCharacter(input.args.split(" ")[0]).get(0); + if(character.getOwner().equals(user)) + { + CharacterManager.instance.editBio(character, input.args.substring(input.args.indexOf(' '))); + res.Call(LocStrings.Stub("CharacterEditBioSuccess")); + } + else + { + res.Call(LocStrings.Stub("CharacterEditBioNotAuth")); + } + } +} \ No newline at end of file diff --git a/src/commands/CommandCharacterEditName.java b/src/commands/CommandCharacterEditName.java new file mode 100644 index 0000000..0fadb64 --- /dev/null +++ b/src/commands/CommandCharacterEditName.java @@ -0,0 +1,38 @@ +package commands; + +import core.CharacterManager; +import core.Command; +import core.LocStrings; +import dataStructures.*; + +public class CommandCharacterEditName extends Command +{ + public CommandCharacterEditName(KittyRole level, KittyRating rating) { super(level, rating); } + + @Override + public String HelpText() { return LocStrings.Stub("CharacterEditNameInfo"); } + + @Override + public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res) + { + try + { + Long.parseLong(input.args.split(" ")[0]); + } + catch(Exception e) + { + res.Call(LocStrings.Stub("CharacterEditNameNotValid")); + return; + } + KittyCharacter character = CharacterManager.instance.searchCharacter(input.args.split(" ")[0]).get(0); + if(character.getOwner().equals(user)) + { + CharacterManager.instance.editName(character, input.args.substring(input.args.indexOf(' '))); + res.Call(LocStrings.Stub("CharacterEditNameSuccess")); + } + else + { + res.Call(LocStrings.Stub("CharacterEditNameNotAuth")); + } + } +} \ No newline at end of file diff --git a/src/commands/CommandCharacterEditURL.java b/src/commands/CommandCharacterEditURL.java new file mode 100644 index 0000000..887468a --- /dev/null +++ b/src/commands/CommandCharacterEditURL.java @@ -0,0 +1,38 @@ +package commands; + +import core.CharacterManager; +import core.Command; +import core.LocStrings; +import dataStructures.*; + +public class CommandCharacterEditURL extends Command +{ + public CommandCharacterEditURL(KittyRole level, KittyRating rating) { super(level, rating); } + + @Override + public String HelpText() { return LocStrings.Stub("CharacterEditURLInfo"); } + + @Override + public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res) + { + try + { + Long.parseLong(input.args.split(" ")[0]); + } + catch(Exception e) + { + res.Call(LocStrings.Stub("CharacterEditURLNotValid")); + return; + } + KittyCharacter character = CharacterManager.instance.searchCharacter(input.args.split(" ")[0]).get(0); + if(character.getOwner().equals(user)) + { + CharacterManager.instance.editRefImage(character, input.args.substring(input.args.indexOf(' '))); + res.Call(LocStrings.Stub("CharacterEditURLSuccess")); + } + else + { + res.Call(LocStrings.Stub("CharacterEditURLNotAuth")); + } + } +} \ No newline at end of file diff --git a/src/commands/CommandCharacterSearch.java b/src/commands/CommandCharacterSearch.java new file mode 100644 index 0000000..56ce5a6 --- /dev/null +++ b/src/commands/CommandCharacterSearch.java @@ -0,0 +1,40 @@ +package commands; + +import java.util.ArrayList; + +import core.CharacterManager; +import core.Command; +import core.LocStrings; +import dataStructures.*; + +public class CommandCharacterSearch extends Command +{ + public CommandCharacterSearch(KittyRole level, KittyRating rating) { super(level, rating); } + + @Override + public String HelpText() { return LocStrings.Stub("CharacterSearchInfo"); } + + @Override + public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res) + { + ArrayList characters = CharacterManager.instance.searchCharacter(input.args); + if(characters.size() < 1) + { + res.Call(LocStrings.Stub("CharacterSearchNoCharacterFound")); + return; + } + if(characters.size() > 1) + { + res.Call(LocStrings.Stub("CharacterSearchMultipleCharacterHeader")); + for(KittyCharacter character:characters) + { + res.Call(String.format(LocStrings.Stub("CharacterSearchMultipleCharacter"), character.getName(), character.getOwner().name, character.getUID())); + } + return; + } + else + { + res.Call(String.format(LocStrings.Stub("CharacterSearchOneCharacter"), characters.get(0).getOwner().name, characters.get(0).getName(), characters.get(0).getBio(), characters.get(0).getRefImage(), characters.get(0).getUID())); + } + } +} \ No newline at end of file diff --git a/src/commands/CommandChoose.java b/src/commands/CommandChoose.java index d5478bc..1143985 100644 --- a/src/commands/CommandChoose.java +++ b/src/commands/CommandChoose.java @@ -19,7 +19,14 @@ public class CommandChoose extends Command @Override public void onRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res) { + if(input.args.trim().length() == 0) + { + res.Call(Stats.instance.GetHelpText(input.key)); + return; + } + String [] choices = input.args.split(","); + if(choices.length == 1) { res.Call(LocStrings.stub("ChooseOne")); diff --git a/src/commands/CommandColor.java b/src/commands/CommandColor.java new file mode 100644 index 0000000..2e50bd8 --- /dev/null +++ b/src/commands/CommandColor.java @@ -0,0 +1,84 @@ +package commands; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.io.File; +import java.text.DecimalFormat; + +import core.Command; +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; +import network.NetworkTheColorAPI; +import network.NetworkTheColorAPI.ColorData; +import utils.ImageUtils; + +public class CommandColor extends Command +{ + private NetworkTheColorAPI theColorAPI = new NetworkTheColorAPI(); + + public CommandColor(KittyRole level, KittyRating rating) { super(level, rating); } + + @Override + public String HelpText() { return LocStrings.Stub("ColorInfo"); } + + @Override + public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res) + { + // First, try and parse out the color to make sure we can even get it. + ColorData colorData = theColorAPI.LookupHex(input.args.trim()); + + // Verify the color was even found + if(colorData == null) + { + res.Call(LocStrings.Stub("ColorNotSearchable")); + return; + } + + // Set up the variables we'll be using based on the color, and get the color itself. + int sideLength = 80; // It's 80x80 because as far as I know, that's the size of a thumbnail in discord. + int r = colorData.rgb.r; + int g = colorData.rgb.g; + int b = colorData.rgb.b; + int a = 255; // Alpha not supported at this time + Color parsed = new Color(r, g, b, a); + + // Construct the image example file that we intend to display locally + BufferedImage img = new BufferedImage(sideLength, sideLength, BufferedImage.TYPE_4BYTE_ABGR); + Graphics2D graphics = img.createGraphics(); + graphics.setColor(parsed); + graphics.fillRect(0, 0, sideLength, sideLength); + graphics.dispose(); + + String tempFileName = ImageUtils.writeTempImageData(img, ".png"); + File file = new File(tempFileName); + + // Build the response + KittyEmbed response = new KittyEmbed(); + String postEntry = ", "; + response.title = colorData.name.value + (colorData.name.exact_match_name ? "" : " (ish)"); + response.color = parsed; + response.descriptionText = "```"; + response.descriptionText += "\n hex: #" + colorData.hex.clean; + response.descriptionText += "\n rgb: " + String.format("%1$03d", r) + postEntry + String.format("%1$03d", g) + postEntry + String.format("%1$03d", b); + response.descriptionText += "\n hsl: " + String.format("%1$03d", colorData.hsl.h) + postEntry + String.format("%1$02d", colorData.hsl.s) + "%" + postEntry + String.format("%1$02d", colorData.hsl.l) + "%"; + response.descriptionText += "\n hsv: " + String.format("%1$03d", colorData.hsv.h) + postEntry + String.format("%1$02d", colorData.hsv.s) + "%" + postEntry + String.format("%1$02d", colorData.hsv.v) + "%"; + response.descriptionText += "\n xyz: " + String.format("%1$03d", colorData.XYZ.X) + postEntry + String.format("%1$03d", colorData.XYZ.Y) + postEntry + String.format("%1$03d", colorData.XYZ.Z); + response.descriptionText += "\ncmyk: " + String.format("%1$03d", colorData.cmyk.c) + postEntry + String.format("%1$03d", colorData.cmyk.m) + postEntry + String.format("%1$03d", colorData.cmyk.y) + postEntry + String.format("%1$03d", colorData.cmyk.k); + response.descriptionText += "```"; + response.thumbnailURL = "attachment://" + tempFileName; + response.footerText = "All percentages and values are rounded to the nearest whole number!" + (colorData.name.exact_match_name ? "" : " '" + colorData.name.value + "' is actually " + colorData.name.closest_named_hex + "."); + + // Send then delete the temp local files + res.CallEmbed(response); + ImageUtils.BlockingFileDelete(file); + }; + +} diff --git a/src/commands/CommandDBFlush.java b/src/commands/CommandDBFlush.java index fd7c08f..4f26c03 100644 --- a/src/commands/CommandDBFlush.java +++ b/src/commands/CommandDBFlush.java @@ -1,8 +1,7 @@ package commands; -import java.awt.Color; - import core.Command; +import core.Config; import core.DatabaseManager; import core.LocStrings; import dataStructures.KittyChannel; @@ -29,7 +28,7 @@ public class CommandDBFlush extends Command KittyEmbed embed = new KittyEmbed(); embed.title = "Database queue flushed"; embed.descriptionText = "**Dirty:** " + numUpdated; - embed.color = new Color(7*16, 8*16, 9*16); + embed.color = Config.ColorDefault; res.CallEmbed(embed); } diff --git a/src/commands/CommandDBStats.java b/src/commands/CommandDBStats.java index ff02330..7a856e4 100644 --- a/src/commands/CommandDBStats.java +++ b/src/commands/CommandDBStats.java @@ -1,10 +1,10 @@ package commands; -import java.awt.Color; import java.text.DateFormat; import java.text.SimpleDateFormat; import core.Command; +import core.Config; import core.DatabaseManager; import core.LocStrings; import dataStructures.KittyChannel; @@ -36,7 +36,7 @@ public class CommandDBStats extends Command 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); + embed.color = Config.ColorDefault; res.CallEmbed(embed); } diff --git a/src/commands/CommandHelp.java b/src/commands/CommandHelp.java index a03cea9..1fb6574 100644 --- a/src/commands/CommandHelp.java +++ b/src/commands/CommandHelp.java @@ -26,10 +26,6 @@ public class CommandHelp extends Command { help = LocStrings.stub("HelpDisplay"); } - else - { - help = "`" + input.args + "`: " + help; - } res.Call(help); } diff --git a/src/commands/CommandLeaderboard.java b/src/commands/CommandLeaderboard.java new file mode 100644 index 0000000..cfc06af --- /dev/null +++ b/src/commands/CommandLeaderboard.java @@ -0,0 +1,99 @@ +package commands; + +import java.util.ArrayList; +import java.util.List; + +import core.Command; +import core.Config; +import core.DatabaseManager; +import core.LocStrings; +import core.ObjectBuilderFactory; +import dataStructures.KittyChannel; +import dataStructures.KittyEmbed; +import dataStructures.KittyGuild; +import dataStructures.KittyRating; +import dataStructures.KittyRole; +import dataStructures.KittyUser; +import dataStructures.Pair; +import dataStructures.Response; +import dataStructures.UserInput; +import utils.GlobalLog; +import utils.LogFilter; + +public class CommandLeaderboard extends Command +{ + public CommandLeaderboard(KittyRole roleLevel, KittyRating contentRating) { super(roleLevel, contentRating); } + + @Override + public String HelpText() { return LocStrings.Stub("LeaderboardInfo"); } + + // Called when the command is run! + @Override + public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res) + { + // Start by force-flushing. We need to be up-to-date. + GlobalLog.Log("Flushed database for " + DatabaseManager.instance.upkeep() + " items."); + + // Get all users associated with a guild and get their bean count + String guildID = guild.uniqueID; + List> users = new ArrayList>(); + List out = DatabaseManager.instance.scrapeGlobalForString(guildID); + + for(String item : out) + { + String val = item.replace("" + guildID, ""); + if(!val.contains("-") && val.length() > 0) + { + String userID = val; + + if(userID.length() < 1) + continue; + + // Look up in the database the user beans + String dbUserData = DatabaseManager.instance.globalGetRemoteValue(guildID + userID); + + // Leverage the fact that the user is automatically read and parsed when constructed. + Long parsedBeans = KittyUser.parseBeans(KittyUser.prepareFromString(dbUserData)); + + // If we were only using cached users, we would use this. However, we're not. + // KittyUser cachedUser = ObjectBuilderFactory.getCachedUser(guildID, userID); + users.add(new Pair(parsedBeans, userID)); + } + } + + // Sort users by beans + users.sort((pair1, pair2) -> { + long difference = pair2.First - pair1.First; + + if(difference < Integer.MIN_VALUE) + return Integer.MIN_VALUE; + + if(difference > Integer.MAX_VALUE) + return Integer.MAX_VALUE; + + return (int)difference; + }); + + GlobalLog.Log(LogFilter.Command, "Sorted through " + users.size() + " KittyUsers for leaderboard purposes."); + + // Configure output + final int listSize = 10; + KittyEmbed embed = new KittyEmbed(); + embed.color = Config.ColorDefault; + embed.title = LocStrings.Stub("LeaderboardTitle"); + embed.descriptionText = ""; + + for(int i = 0; i < listSize && i < users.size(); ++i) + { + // Only now that we've sorted do we do the full construction and caching of users + Pair userPair = users.get(i); + KittyUser cachedUser = ObjectBuilderFactory.getKittyUser(guildID, userPair.Second); + + embed.descriptionText += "**" + (i + 1) + ":** " + userPair.First + " - " + cachedUser.name; + embed.descriptionText += "\n"; + } + + // Write out embed result + res.CallEmbed(embed); + } +} diff --git a/src/commands/CommandPerish.java b/src/commands/CommandPerish.java index 84a33b2..b743cdf 100644 --- a/src/commands/CommandPerish.java +++ b/src/commands/CommandPerish.java @@ -45,7 +45,7 @@ public class CommandPerish extends Command { try { - filename = ImageUtils.DownloadFromURL(input.args.split(" ")[0], ".png"); + filename = ImageUtils.downloadFromURL(input.args.split(" ")[0], ".png"); preProcessed = new File(filename); } catch(Exception e) @@ -54,7 +54,7 @@ public class CommandPerish extends Command if(input.mentions != null) person = input.mentions[0]; - filename = ImageUtils.DownloadFromURL(person.avatarID, ".png"); + filename = ImageUtils.downloadFromURL(person.avatarID, ".png"); if(filename == null) return; } diff --git a/src/commands/CommandStark.java b/src/commands/CommandStark.java index 3f3a2d5..e1e4d18 100644 --- a/src/commands/CommandStark.java +++ b/src/commands/CommandStark.java @@ -39,7 +39,7 @@ public class CommandStark extends Command { try { - filename = ImageUtils.DownloadFromURL(input.args.split(" ")[0], ".png"); + filename = ImageUtils.downloadFromURL(input.args.split(" ")[0], ".png"); preProcessed = new File(filename); } catch(Exception e) @@ -48,7 +48,7 @@ public class CommandStark extends Command if(input.mentions != null) person = input.mentions[0]; - filename = ImageUtils.DownloadFromURL(person.avatarID, ".png"); + filename = ImageUtils.downloadFromURL(person.avatarID, ".png"); if(filename == null) return; } diff --git a/src/commands/CommandTeey.java b/src/commands/CommandTeey.java index f11ba14..5ece546 100644 --- a/src/commands/CommandTeey.java +++ b/src/commands/CommandTeey.java @@ -48,7 +48,7 @@ public class CommandTeey extends Command else person = input.mentions[0]; - String yeeteeFilename = ImageUtils.DownloadFromURL(person.avatarID, ".png"); + String yeeteeFilename = ImageUtils.downloadFromURL(person.avatarID, ".png"); if(yeeteeFilename == null) return; diff --git a/src/commands/CommandWolfram.java b/src/commands/CommandWolfram.java index a1a0b6e..9483a23 100644 --- a/src/commands/CommandWolfram.java +++ b/src/commands/CommandWolfram.java @@ -29,7 +29,7 @@ public class CommandWolfram extends Command res.CallFile(pic, "png"); ImageUtils.BlockingFileDelete(pic); } - catch (IOException e) + catch (IOException e) { res.Call(LocStrings.stub("WolframError")); } diff --git a/src/commands/CommandYeet.java b/src/commands/CommandYeet.java index ac9f947..3e71bde 100644 --- a/src/commands/CommandYeet.java +++ b/src/commands/CommandYeet.java @@ -48,7 +48,7 @@ public class CommandYeet extends Command else person = input.mentions[0]; - String yeeteeFilename = ImageUtils.DownloadFromURL(person.avatarID, ".png"); + String yeeteeFilename = ImageUtils.downloadFromURL(person.avatarID, ".png"); if(yeeteeFilename == null) return; diff --git a/src/core/CharacterManager.java b/src/core/CharacterManager.java new file mode 100644 index 0000000..cdfb770 --- /dev/null +++ b/src/core/CharacterManager.java @@ -0,0 +1,94 @@ +package core; + +import java.util.ArrayList; +import java.util.Vector; + +import dataStructures.KittyCharacter; +import dataStructures.KittyTrackedLong; +import dataStructures.KittyUser; +import me.xdrop.fuzzywuzzy.FuzzySearch; +import utils.GlobalLog; +import utils.LogFilter; + +public class CharacterManager +{ + public static CharacterManager instance; + KittyTrackedLong uniqueID = new KittyTrackedLong("CharacterIDCounter", ""); + + private Vector characters = new Vector(); + public CharacterManager() + { + if(instance == null) + { + instance = this; + } + else + { + GlobalLog.Error(LogFilter.Core, "Attempted to create a second CharacterManager singleton!"); + return; + } + } + + public ArrayList searchCharacter(String query) + { + ArrayList chars = new ArrayList(); + try + { + long UID = Long.parseLong(query); + for(KittyCharacter character:characters) + { + if(character.getUID() == UID) + { + chars.add(character); + break; + } + } + }catch(Exception e) + { + for(KittyCharacter character:characters) + { + if(FuzzySearch.ratio(query.toLowerCase(), character.getName().toLowerCase()) > 60) + { + chars.add(character); + } + } + } + return chars; + } + + public boolean addCharacter(KittyUser user, String name, String bio, String refImage) + { + for(KittyCharacter character:characters) + { + if(character.getName().equals(name) && character.getOwner().discordID.equals(user.discordID)) + { + return false; + } + } + + characters.add(new KittyCharacter(user, name, bio, refImage, uniqueID.Get())); + uniqueID.Add(1); + return true; + } + + public void removeCharacter(KittyCharacter character, String name) + { + characters.remove(character); + } + + public void editRefImage(KittyCharacter character, String refImage) + { + character.editRefImage(refImage); + } + + public void editBio(KittyCharacter character, String bio) + { + character.editBio(bio); + } + + public void editName(KittyCharacter character, String name) + { + character.editName(name); + } + +} diff --git a/src/core/CommandManager.java b/src/core/CommandManager.java index ce7ed49..854059d 100644 --- a/src/core/CommandManager.java +++ b/src/core/CommandManager.java @@ -111,12 +111,12 @@ public class CommandManager } // Looks up command by name. If it exists, dumps help text, otherwise returns null. - public String GetCommandHelpText(String lookup) + public String GetCommandHelpText(String key) { - Command command = commands.get(lookup); + Command command = commands.get(key); if(command != null) - return command.getHelpText(); + return "`" + key + "`: " + command.getHelpText(); return null; } diff --git a/src/core/Config.java b/src/core/Config.java new file mode 100644 index 0000000..b202650 --- /dev/null +++ b/src/core/Config.java @@ -0,0 +1,8 @@ +package core; + +import java.awt.Color; + +public final class Config +{ + public static final Color ColorDefault = new Color(7*16, 8*16, 9*16); // A slate-grey +} diff --git a/src/core/DatabaseDriverKeyValue.java b/src/core/DatabaseDriverKeyValue.java index df8032e..01e63c1 100644 --- a/src/core/DatabaseDriverKeyValue.java +++ b/src/core/DatabaseDriverKeyValue.java @@ -2,6 +2,8 @@ package core; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; import network.JDBCDriver; import network.JDBCDriverSQLite; @@ -72,7 +74,7 @@ public class DatabaseDriverKeyValue // Creates a key. The key will be created if it doesn't exist, and the default value returned. public String CreateGetKey(String key) { - GlobalLog.Log(LogFilter.Database, "CreateGeyKey: key-" + key); + GlobalLog.Log(LogFilter.Database, "CreateGetKey: " + key); if(HasKey(key)) { @@ -119,6 +121,30 @@ public class DatabaseDriverKeyValue GlobalLog.Log(LogFilter.Database, "CreateKey status: " + status); } + // Get all keys containing a substring + public List GetKeysWith(String keySubstring) + { + String command = "SELECT * FROM " + tableName + " WHERE " + keyColumnName + " like ?"; + ResultSet result = driver.ExecuteReturningStatement(JDBCStatementType.Select, command, new String[] { "%" + keySubstring + "%" }); + + List keys = new ArrayList(); + + try + { + while(result.next()) + { + String key = (String) result.getObject(1); + keys.add(key); + } + } + catch (SQLException e) + { + e.printStackTrace(); + } + + return keys; + } + // Transforms a result into a string if possible. private String ResultAsString(ResultSet rs, String key) { diff --git a/src/core/DatabaseManager.java b/src/core/DatabaseManager.java index c4a12c1..4649e7f 100644 --- a/src/core/DatabaseManager.java +++ b/src/core/DatabaseManager.java @@ -1,6 +1,7 @@ package core; import java.util.Date; +import java.util.List; import java.util.Vector; import utils.GlobalLog; import utils.LogFilter; @@ -128,6 +129,10 @@ public class DatabaseManager } } + public List scrapeGlobalForString(String substring) + { + return globalDataDriver.GetKeysWith(substring); + } ///////////////// // Global Data // @@ -141,7 +146,8 @@ public class DatabaseManager } } - private String globalGetRemoteValue(String key) + // You can get values, but not modify them. + public String globalGetRemoteValue(String key) { synchronized(globalDataDriver) { @@ -170,7 +176,8 @@ public class DatabaseManager } } - private String characterGetRemoteValue(String key) + // You can get values, but not modify them. + public String characterGetRemoteValue(String key) { synchronized(characterDataDriver) { diff --git a/src/core/ObjectBuilderFactory.java b/src/core/ObjectBuilderFactory.java index 692953c..2547801 100644 --- a/src/core/ObjectBuilderFactory.java +++ b/src/core/ObjectBuilderFactory.java @@ -4,12 +4,23 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.concurrent.Semaphore; + +import javax.security.auth.login.LoginException; + import commands.*; import core.lua.PluginManager; import dataStructures.*; +import main.Main; +import net.dv8tion.jda.core.AccountType; +import net.dv8tion.jda.core.JDA; +import net.dv8tion.jda.core.JDABuilder; import net.dv8tion.jda.core.entities.Emote; +import net.dv8tion.jda.core.entities.Game; +import net.dv8tion.jda.core.entities.Guild; import net.dv8tion.jda.core.entities.Member; +import net.dv8tion.jda.core.entities.User; import net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent; +import offline.Ref; import utils.AdminControl; import utils.GlobalLog; import utils.LogFilter; @@ -53,6 +64,7 @@ public class ObjectBuilderFactory // TODO: Investigate using 'synchronized' instead potentially private static boolean hasInitialized; private static Semaphore initMutex = new Semaphore(1); + private static JDA kitty; // This is it, this is how the lazy init starts! private static void LazyInit() @@ -265,6 +277,7 @@ public class ObjectBuilderFactory return user; } + // TODO: Clean up public static KittyUser extractUserByJDAUser(String guildID, String name, String userID, String avatarID, String discordID) { LazyInit(); @@ -295,7 +308,9 @@ public class ObjectBuilderFactory return user; } - public static KittyUser getCachedUser(String guildID, String userID) + // There is some redundant lookup occurring. If the user isn't cached yet, then we construct them and cache them. + // The assumption is made that the user does, in fact, exist. + public static KittyUser getKittyUser(String guildID, String userID) { String uid = guildID + userID; KittyUser user = null; @@ -304,6 +319,16 @@ public class ObjectBuilderFactory user = userCache.get(uid); } + if(user == null) + { + Guild jdaGuild = kitty.getGuildById(guildID); + Member jdaMember = jdaGuild.getMemberById(userID); + User jdaUser = jdaMember.getUser(); + + user = ExtractUserByJDAUser(guildID, jdaMember.getNickname(), jdaUser.getId(), jdaUser.getAvatarUrl(), jdaUser.getId()); + updateUser(user, jdaMember); + } + return user; } @@ -326,6 +351,17 @@ public class ObjectBuilderFactory // Construction Methods // ////////////////////////// + public static KittyCore ConstructKittyCore() throws LoginException, InterruptedException + { + LazyInit(); + + kitty = new JDABuilder(AccountType.BOT).setToken(Ref.TestToken).buildBlocking(); + kitty.getPresence().setGame(Game.playing("with digital yarn")); + kitty.addEventListener(new Main()); + + return new KittyCore(kitty); + } + // Default construction of the command manager. In order to remotely resolve command enabling // and disabling, what we do is construct the commands with a localized pair that is checked against // the CommandEnabler object passed in. In theory, we could have multiple CommandManagers, tho we can @@ -361,39 +397,46 @@ public class ObjectBuilderFactory manager.Register(LocCommands.stub("raffleend"), new CommandRaffleEnd(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)); - manager.Register(LocCommands.stub("teey"), new CommandTeey(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("perish, thenperish"), new CommandPerish(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("yeet"), new CommandYeet(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("ping"), new CommandPing(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("boop"), new CommandBoop(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("roll"), new CommandRoll(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("choose"), new CommandChoose(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("help"), new CommandHelp(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("info, about"), new CommandInfo(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("vote"), new CommandPollVote(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("results"), new CommandPollResults(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("showpoll"), new CommandPollShow(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("wolfram"), new CommandWolfram(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("c++, g++, cplus, cpp"), new CommandColiru(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("java, jdoodle"), new CommandJDoodle(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("beans"), new CommandBeansShow(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("role"), new CommandRole(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("bet"), new CommandBetBeans(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("map"), new CommandMap(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("rpstart"), new CommandRPStart(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("rpend"), new CommandRPEnd(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("tony, stark, dontfeelgood, dontfeelsogood"), new CommandStark(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("blur"), new CommandBlurry(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("eightball, 8ball"), new CommandEightBall(KittyRole.General, KittyRating.Safe)); - manager.Register(LocCommands.stub("catch"), new CommandCatch(KittyRole.General, KittyRating.Safe)); - 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)); - manager.Register(LocCommands.stub("rafflejoin"), new CommandRaffleJoin(KittyRole.General, KittyRating.Safe)); + 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)); + manager.Register(LocCommands.Stub("teey"), new CommandTeey(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("perish, thenperish"), new CommandPerish(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("yeet"), new CommandYeet(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("ping"), new CommandPing(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("boop"), new CommandBoop(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("roll"), new CommandRoll(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("choose"), new CommandChoose(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("help"), new CommandHelp(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("info, about"), new CommandInfo(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("vote"), new CommandPollVote(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("results"), new CommandPollResults(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("showpoll"), new CommandPollShow(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("wolfram"), new CommandWolfram(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("c++, g++, cplus, cpp"), new CommandColiru(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("java, jdoodle"), new CommandJDoodle(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("beans"), new CommandBeansShow(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("role"), new CommandRole(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("bet"), new CommandBetBeans(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("map"), new CommandMap(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("rpstart"), new CommandRPStart(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("rpend"), new CommandRPEnd(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("tony, stark, dontfeelgood, dontfeelsogood"), new CommandStark(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("blur"), new CommandBlurry(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("eightball, 8ball"), new CommandEightBall(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("catch"), new CommandCatch(KittyRole.General, KittyRating.Safe)); + 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)); + manager.Register(LocCommands.Stub("rafflejoin"), new CommandRaffleJoin(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("charactercreate"), new CommandCharacterCreate(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("searchcharacter"), new CommandCharacterSearch(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("charactereditbio"), new CommandCharacterEditBio(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("charactereditname"), new CommandCharacterEditName(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("charactereditURL"), new CommandCharacterEditURL(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("leaderboard"), new CommandLeaderboard(KittyRole.General, KittyRating.Safe)); + manager.Register(LocCommands.Stub("color, colour"), new CommandColor(KittyRole.General, KittyRating.Safe)); return manager; } @@ -473,4 +516,4 @@ public class ObjectBuilderFactory return userCache.size(); } } -} +} \ No newline at end of file diff --git a/src/core/RPManager.java b/src/core/RPManager.java index e3d64e2..f56be78 100644 --- a/src/core/RPManager.java +++ b/src/core/RPManager.java @@ -6,9 +6,7 @@ import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.HashMap; import java.util.Map.Entry; - import dataStructures.*; -import net.dv8tion.jda.core.JDA; import utils.GlobalLog; import utils.LogFilter; @@ -57,7 +55,7 @@ public class RPManager return log; } - public static void Upkeep(JDA kitty) + public static void Upkeep(KittyCore kitty) { Response res = new Response(null, kitty); String reminder = ""; diff --git a/src/dataStructures/KittyCharacter.java b/src/dataStructures/KittyCharacter.java index 0812a8a..affef8b 100644 --- a/src/dataStructures/KittyCharacter.java +++ b/src/dataStructures/KittyCharacter.java @@ -1,5 +1,64 @@ package dataStructures; -public class KittyCharacter { - +public class KittyCharacter +{ + private KittyUser owner; + private String name; + private String bio; + private String refImage; + private long uniqueID; + + public KittyCharacter(KittyUser owner, String name, String bio, String refImage, long uniqueID) + { + this.owner = owner; + this.name = name; + this.bio = bio; + this.refImage = refImage; + this.uniqueID = uniqueID; + } + + public void editBio(String bio) + { + this.bio = bio; + } + + public void editRefImage(String refImage) + { + this.refImage = refImage; + } + + public void editName(String name) + { + this.name = name; + } + + public KittyUser getOwner() + { + return owner; + } + + public String getName() + { + return name; + } + + public String getBio() + { + return bio; + } + + public String getRefImage() + { + return refImage; + } + + public long getUID() + { + return uniqueID; + } + + public String toString() + { + return name + " " + bio + " " + refImage; + } } diff --git a/src/dataStructures/KittyCore.java b/src/dataStructures/KittyCore.java new file mode 100644 index 0000000..ff9e13d --- /dev/null +++ b/src/dataStructures/KittyCore.java @@ -0,0 +1,13 @@ +package dataStructures; + +import net.dv8tion.jda.core.JDA; + +public class KittyCore +{ + public final JDA jda; + + public KittyCore(JDA kitty) + { + this.jda = kitty; + } +} diff --git a/src/dataStructures/KittyEmbed.java b/src/dataStructures/KittyEmbed.java index 3bc8382..c871bbe 100644 --- a/src/dataStructures/KittyEmbed.java +++ b/src/dataStructures/KittyEmbed.java @@ -4,7 +4,7 @@ import java.awt.Color; public class KittyEmbed { - public String title = null; + public String title = null; // Required, but if this remails null or is empty, it's made a blank space in Response.java. public Color color = null; public String footerText = null; public String authorText = null; @@ -13,9 +13,5 @@ public class KittyEmbed public String descriptionText = null; public String bodyImageURL = null; public String imageURL = null; - - - public KittyEmbed() - { - } + public String thumbnailURL = null; // Did you know? This is the only field that can actually be a local image file. Use attachment://file.png as the string for it. } diff --git a/src/dataStructures/KittyUser.java b/src/dataStructures/KittyUser.java index 74e0179..538c818 100644 --- a/src/dataStructures/KittyUser.java +++ b/src/dataStructures/KittyUser.java @@ -64,29 +64,46 @@ public class KittyUser extends DatabaseTrackedObject return beans + "," + role.getValue(); } + public static String[] prepareFromString(String string) + { + return string.split(","); + } + + public static long parseBeans(String[] prepared) + { + return Integer.parseInt(prepared[0]); + } + + public static KittyRole parseRole(String[] prepared) + { + return KittyRole.valueOf(Integer.parseInt(prepared[1])).get(); + } + @Override public void DeSerialzie(String string) { try { - String[] strings = string.split(","); - beans = Integer.parseInt(strings[0]); - if(strings.length > 1) - { - role = KittyRole.valueOf(Integer.parseInt(strings[1])).get(); - } - else + String[] strings = prepareFromString(string); + if(strings.length < 2) { GlobalLog.Log(LogFilter.Database, "Upgrading user " + name + " to include 'role' in DB"); + // Mark ourselves dirty to re-write the role information stored in the user. // Just uses defaults from earlier again. MarkDirty(); } + else + { + beans = parseBeans(strings); + role = parseRole(strings); + } } catch (NumberFormatException e) { GlobalLog.Warn(LogFilter.Database, "Invalid user data for user " + name + "! " + "Starting over at 0 beans with a general role!"); + // We don't need to specify the role at this point because it is set at this point. // We use what the user was created with whatever defaults were in the factory. // Beans are maintained too, just in case there's some in cache. diff --git a/src/dataStructures/Response.java b/src/dataStructures/Response.java index 8c070cb..ab0fc03 100644 --- a/src/dataStructures/Response.java +++ b/src/dataStructures/Response.java @@ -2,7 +2,9 @@ package dataStructures; import java.io.File; import java.io.InputStream; + import net.dv8tion.jda.core.JDA; +import net.dv8tion.jda.core.MessageBuilder; import net.dv8tion.jda.core.entities.TextChannel; import net.dv8tion.jda.core.events.message.guild.*; import net.dv8tion.jda.core.EmbedBuilder; @@ -16,15 +18,15 @@ public class Response { // Variables private GuildMessageReceivedEvent event; - private JDA kitty; private final int discordMessageMax = 2000; private final int kittyMessageMax = 1950; + private JDA kitty; // Constructor - public Response(GuildMessageReceivedEvent event, JDA kitty) + public Response(GuildMessageReceivedEvent event, KittyCore kitty) { this.event = event; - this.kitty = kitty; + this.kitty = kitty.jda; } // Builds a nicely formatted embedded message based on information provided @@ -32,8 +34,16 @@ public class Response { EmbedBuilder embed = new EmbedBuilder(); - if(embedInfo.title != null) + if(embedInfo.title != null && !embedInfo.title.isEmpty()) + { embed.setTitle(embedInfo.title); + } + else + { + // We have to set the title to at least something that's not empty. + // By defualt, this creates nothing really visible. + embed.setTitle(" "); + } if(embedInfo.color != null) embed.setColor(embedInfo.color); @@ -48,9 +58,34 @@ public class Response embed.setAuthor(embedInfo.authorText, embedInfo.authorLink, embedInfo.authorImage); if(embedInfo.imageURL != null) - embed.setImage(embedInfo.imageURL); + embed.setImage(embedInfo.imageURL); - event.getChannel().sendMessage(embed.build()).queue(); + if(embedInfo.thumbnailURL != null && embedInfo.thumbnailURL.contains("attachment://")) + { + // This is the case where you have a thumbnail image, but you want it to be a local file. + // TODO: Make this more generic. Consider message builder for the entire thing. + String thumbPath = embedInfo.thumbnailURL; + String thumbName = thumbPath.replace("attachment://", ""); + + // Build the message and send if the file is valid + MessageBuilder message = new MessageBuilder(); + embed.setThumbnail(thumbPath); + message.setEmbed(embed.build()); + + File thumbImage = new File(thumbName); + if(thumbImage.exists()) + { + event.getChannel().sendFile(thumbImage, thumbName, message.build()).queue(); + } + } + else + { + if(embedInfo.thumbnailURL != null) + embed.setThumbnail(embedInfo.thumbnailURL); + + event.getChannel().sendMessage(embed.build()); + } + } // Queues a standard text-based message response to the channel that issued the command. diff --git a/src/dataStructures/UserInput.java b/src/dataStructures/UserInput.java index 00e950c..063c6a6 100644 --- a/src/dataStructures/UserInput.java +++ b/src/dataStructures/UserInput.java @@ -93,7 +93,7 @@ public class UserInput KittyUser [] KittyMentions = new KittyUser[JDAMentions.size()]; for(int i = 0; i < KittyMentions.length; i ++) { - KittyMentions [i] = ObjectBuilderFactory.getCachedUser(event.getGuild().getId(), JDAMentions.get(i).getUser().getId()); + KittyMentions [i] = ObjectBuilderFactory.getKittyUser(event.getGuild().getId(), JDAMentions.get(i).getUser().getId()); } return KittyMentions; } diff --git a/src/main/Main.java b/src/main/Main.java index bf8ee2a..2647acf 100644 --- a/src/main/Main.java +++ b/src/main/Main.java @@ -1,24 +1,31 @@ package main; +import java.util.List; + import javax.security.auth.login.LoginException; -import core.*; -import core.benchmark.BenchmarkManager; + +import core.CharacterManager; +import core.CommandEnabler; +import core.CommandManager; +import core.DatabaseManager; +import core.ObjectBuilderFactory; +import core.RPManager; +import core.Stats; import core.lua.PluginManager; -import core.lua.PluginUser; import dataStructures.KittyChannel; +import dataStructures.KittyCore; import dataStructures.KittyGuild; -import dataStructures.KittyRole; import dataStructures.KittyUser; import dataStructures.Response; import dataStructures.UserInput; import net.dv8tion.jda.core.AccountType; -import net.dv8tion.jda.core.entities.*; -import net.dv8tion.jda.core.events.message.guild.*; +import net.dv8tion.jda.core.JDA; +import net.dv8tion.jda.core.JDABuilder; +import net.dv8tion.jda.core.entities.Game; +import net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent; import net.dv8tion.jda.core.hooks.ListenerAdapter; -import offline.*; +import offline.Ref; import utils.GlobalLog; -import net.dv8tion.jda.core.*; -import java.util.*; // http://www.slf4j.org/ - this JDA logging tool has been disabled by specifying NOP implementation. // This is the application entry point, and bot startup location! @@ -27,30 +34,30 @@ import java.util.*; public class Main extends ListenerAdapter { // Variables and bot specific objects - private static JDA kitty; // TODO: Wrap + private static KittyCore kittyCore; private static DatabaseManager databaseManager; private static CommandEnabler commandEnabler; private static CommandManager commandManager; private static Stats stats; private static RPManager rpManager; private static PluginManager pluginManager; + private static CharacterManager charManager; // Initialization and setup public static void main(String[] args) throws InterruptedException, LoginException, Exception { // Factory startup. The ordering is intentional. GlobalLog.initialize(); - databaseManager = ObjectBuilderFactory.constructDatabaseManager(); - commandEnabler = ObjectBuilderFactory.constructCommandEnabler(); - commandManager = ObjectBuilderFactory.constructCommandManager(commandEnabler); - stats = ObjectBuilderFactory.constructStats(commandManager); - rpManager = ObjectBuilderFactory.constructRPManager(); - pluginManager = ObjectBuilderFactory.constructPluginManager(); + databaseManager = ObjectBuilderFactory.ConstructDatabaseManager(); + commandEnabler = ObjectBuilderFactory.ConstructCommandEnabler(); + commandManager = ObjectBuilderFactory.ConstructCommandManager(commandEnabler); + stats = ObjectBuilderFactory.ConstructStats(commandManager); + charManager = new CharacterManager(); + rpManager = ObjectBuilderFactory.ConstructRPManager(); + pluginManager = ObjectBuilderFactory.ConstructPluginManager(); // Bot startup - kitty = new JDABuilder(AccountType.BOT).setToken(Ref.TestToken).buildBlocking(); - kitty.getPresence().setGame(Game.playing("with digital yarn")); - kitty.addEventListener(new Main()); + kittyCore = ObjectBuilderFactory.ConstructKittyCore(); } // When a message is sent in a server that kitty is in, this is what's called. @@ -67,7 +74,7 @@ public class Main extends ListenerAdapter KittyChannel channel = ObjectBuilderFactory.extractChannel(event); // Specialized uncached objects - Response response = new Response(event, kitty); + Response response = new Response(event, kittyCore); UserInput input = new UserInput(event, guild); // Tweak object construction as necessary @@ -106,6 +113,6 @@ public class Main extends ListenerAdapter } // Run any upkeep in post we need to - Superintendent.PerCommandUpkeepPost(kitty, databaseManager); + Superintendent.PerCommandUpkeepPost(kittyCore, databaseManager); } } \ No newline at end of file diff --git a/src/main/Superintendent.java b/src/main/Superintendent.java index b7b19f4..f678058 100644 --- a/src/main/Superintendent.java +++ b/src/main/Superintendent.java @@ -8,12 +8,12 @@ import core.LocStrings; import core.RPManager; import core.Stats; import dataStructures.KittyChannel; +import dataStructures.KittyCore; import dataStructures.KittyGuild; import dataStructures.KittyRole; import dataStructures.KittyUser; import dataStructures.Response; import dataStructures.UserInput; -import net.dv8tion.jda.core.JDA; import net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent; import offline.Ref; @@ -87,7 +87,7 @@ public class Superintendent // 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) + public static boolean PerCommandUpkeepPost(KittyCore kittyCore, DatabaseManager databaseManager) { // Upkeep database lazily on occasion if(delayTimerCurrent.decrementAndGet() < 0) @@ -97,7 +97,7 @@ public class Superintendent } // Update command-specific - RPManager.Upkeep(bot); + RPManager.Upkeep(kittyCore); return true; } diff --git a/src/network/NetworkTheColorAPI.java b/src/network/NetworkTheColorAPI.java new file mode 100644 index 0000000..2bb9f0d --- /dev/null +++ b/src/network/NetworkTheColorAPI.java @@ -0,0 +1,89 @@ +package network; + +import com.google.gson.Gson; + +import utils.HTTPUtils; + +// This class is designed to easily query and ask for responses from https://www.thecolorapi.com/. +// Unlike some of the other networking classes, the query is much more wrapped. +public class NetworkTheColorAPI +{ + private static final Gson jsonParser = new Gson(); + + public class tca_XYZ + { + // This is also known as the CIE 1931 colorspace. + // Value meanings: https://en.wikipedia.org/wiki/CIE_1931_color_space + public int X; + public int Y; + public int Z; + } + + public class tca_cymk + { + public int c; // 0-100 + public int m; // 0-100 + public int y; // 0-100 + public int k; // 0-100 + } + + public class tca_name + { + public String value; // The name of the color in english + public boolean exact_match_name; // Is this exactly the name of the color searched for? + public String closest_named_hex; // The hex value of the actual value + } + + public class tca_hsv + { + public int h; // 0-255 + public int s; // 0-100, % + public int v; // 0-100, % + } + + public class tca_hsl + { + public int h; // 0-255 + public int s; // 0-100, % + public int l; // 0-100, % + } + + public class tca_rgb + { + public int r; // 0-255 + public int g; // 0-255 + public int b; // 0-255 + } + + public class tca_hex + { + public String clean; // format xxxxxx, with no # + } + + public class ColorData + { + public tca_name name; + public tca_hex hex; + public tca_rgb rgb; + public tca_hsl hsl; + public tca_hsv hsv; + public tca_cymk cmyk; + public tca_XYZ XYZ; + } + + public ColorData LookupHex(String hex) + { + try + { + hex = hex.replace("#", ""); + String response = HTTPUtils.SendGETRequest("https://www.thecolorapi.com/id?hex=" + hex); + + ColorData data = jsonParser.fromJson(response, ColorData.class); + return data; + } + catch (Exception e) + { + return null; + } + } +} diff --git a/src/utils/ImageUtils.java b/src/utils/ImageUtils.java index e85af12..7d8822a 100644 --- a/src/utils/ImageUtils.java +++ b/src/utils/ImageUtils.java @@ -5,19 +5,58 @@ import java.awt.image.BufferedImage; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; +import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.URL; +import javax.imageio.ImageIO; + public class ImageUtils { private static Long uniqueID = 0l; + public static String writeTempImageData(BufferedImage data, String extension) + { + String name = null; + synchronized(uniqueID) + { + String end = extension; + if(!extension.contains(".")) + { + end = "." + extension; + } + + name = "imagetempwriterutil_" + (uniqueID++) + end; + } + + try + { + File outputfile = new File(name); + extension = extension.replace(".", ""); + + boolean didWrite = ImageIO.write(data, extension, outputfile); + if(!didWrite) + { + GlobalLog.Error(LogFilter.Util, "Could not identify the writer for extension type '" + extension + "' - Did you accidentally include a period or other punctuation?"); + return null; + } + + return name; + } + catch (IOException e) + { + GlobalLog.Error(LogFilter.Util, "Exception in writeTempImageData: " + e.toString()); + return null; + } + + } + // Downloads the file at the url and assigns it a unique local string. // The string (filename) is returned, and can be used. It will have to be // deleted later. By assigning a unique name to the files, this is - public static String DownloadFromURL(String URL, String extension) + public static String downloadFromURL(String URL, String extension) { String name = null; synchronized(uniqueID) @@ -51,6 +90,7 @@ public class ImageUtils } catch(Exception e) { + GlobalLog.Error(LogFilter.Util, "Exception in downloadFromURL: " + e.toString()); return null; } }