diff --git a/.classpath b/.classpath
index c1a5172..b116255 100644
--- a/.classpath
+++ b/.classpath
@@ -13,6 +13,5 @@
-
diff --git a/LICENSE b/LICENSE.md
similarity index 95%
rename from LICENSE
rename to LICENSE.md
index 9a6b930..9626f73 100644
--- a/LICENSE
+++ b/LICENSE.md
@@ -1,6 +1,6 @@
BSD 3-Clause License
-Copyright (c) 2019, AlexStewartCode
+Copyright (c) 2019, AlexStewartCode and individual contributors.
All rights reserved.
Redistribution and use in source and binary forms, with or without
diff --git a/README.md b/README.md
index bc4f1cc..6c0e87f 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,6 @@
# KittyBot
-Fun and useful Discord bot
+Fun and useful Discord bot designed to be able searching websites, playing games, compiling code, and much more!
+
+This repo is an eclipse project, and can be opened in the IDE directly. Authentication keys will need to be placed in a file called `Ref.java` at `src/offline/`. This folder isn't present in this repository by default.
+
+If you're looking to contribute, feel free to submit a pull request! Take a look at the styleguide to get some pointers on formatting for this repo!
\ No newline at end of file
diff --git a/STYLEGUIDE.md b/STYLEGUIDE.md
new file mode 100644
index 0000000..989ea8d
--- /dev/null
+++ b/STYLEGUIDE.md
@@ -0,0 +1,65 @@
+# KittyBot Style Guide
+
+This document represents the reasonable defaults and expectations for this project, and provides some rationale for the style restrictions! There can always be exceptions to the rules, but they may need justifying.
+
+Code Formatting
+---
+
+#### Indenting
+Tabs are used so that individual developers can configure their own indent width settings that won't be enforced on others working on the project.
+
+```java
+// I'm not indented!
+{
+ // I'm indented with one tab!
+}
+```
+
+#### Braces
+When braces are present, Allman style is used meaning that braces start on the next line. Braces are used to prevent scope confusion. Omitting braces on single-line statements is fine if the situation is clear.
+
+```java
+if(foo)
+{
+ System.out.println("This");
+ System.out.println("has");
+ System.out.println("braces!");
+}
+
+if(bar)
+ System.out.println("No braces!");
+```
+
+#### Naming
+Class names and function names are `PascalCase`. Variable names are `camelCase`.
+
+```java
+public class MyClass
+{
+ private Foo myVariable;
+
+ public function MyFuction()
+ {
+ // ...
+ }
+}
+```
+
+Favor a broad to narrow naming scheme to promote autocomplete grouping. For example, use a name like `userDescriptionHeader` instead of `headerUserDescription` because there may be other variables related to a user or description, but there's likely only one header section of a description.
+
+
+#### Ternary Operator (`?:`)
+Don't.
+
+
+Architecture
+---
+
+#### Commands
+All commands get spawned on their own threads, and should be implemented in a thread-safe way. Commands all derive from the Command.java class, and use the custom types defined in KittyBot. In order to make any later changes easier and decouple the command logic from core and routing, no JDA structures should be directly exposed in commands, and instead everything needed should be added to the kitty structures that add space to the system. Commands should override the `HelpText` and `Run` functions. Commands are registered in the ObjectBuilderFactory, and the command registration structure can be used for sub-commands as needed. See the RPG for an example.
+
+#### Tracked Values
+Does something need to be tracked in a database? Make sure the class with the data you want inherits from DatabaseTrackedObject, and override the appropriate functions. The object, when marked as dirty, will automatically be written to the database when the next database upkeep tick occurs. Refer to CommandBoop to see an example of a tracked global value, and KittyUser for tracked per-user values.
+
+#### Tokens, Secrets, etc
+Things like tokens, private keys, an secrets are kept in the offline package. This file is intentionally excluded from the repo. If it shouldn't be in the repo, put it in the offline package.
diff --git a/assets/teey/frames/teey 0.png b/assets/teey/frames/teey 0.png
new file mode 100644
index 0000000..571a7b7
Binary files /dev/null and b/assets/teey/frames/teey 0.png differ
diff --git a/assets/teey/frames/teey 1.png b/assets/teey/frames/teey 1.png
new file mode 100644
index 0000000..c2ebcae
Binary files /dev/null and b/assets/teey/frames/teey 1.png differ
diff --git a/assets/teey/frames/teey 10.png b/assets/teey/frames/teey 10.png
new file mode 100644
index 0000000..6d1ef61
Binary files /dev/null and b/assets/teey/frames/teey 10.png differ
diff --git a/assets/teey/frames/teey 11.png b/assets/teey/frames/teey 11.png
new file mode 100644
index 0000000..205094c
Binary files /dev/null and b/assets/teey/frames/teey 11.png differ
diff --git a/assets/teey/frames/teey 12.png b/assets/teey/frames/teey 12.png
new file mode 100644
index 0000000..ff584a5
Binary files /dev/null and b/assets/teey/frames/teey 12.png differ
diff --git a/assets/teey/frames/teey 13.png b/assets/teey/frames/teey 13.png
new file mode 100644
index 0000000..d5d540b
Binary files /dev/null and b/assets/teey/frames/teey 13.png differ
diff --git a/assets/teey/frames/teey 14.png b/assets/teey/frames/teey 14.png
new file mode 100644
index 0000000..911e2ce
Binary files /dev/null and b/assets/teey/frames/teey 14.png differ
diff --git a/assets/teey/frames/teey 15.png b/assets/teey/frames/teey 15.png
new file mode 100644
index 0000000..68a0c93
Binary files /dev/null and b/assets/teey/frames/teey 15.png differ
diff --git a/assets/teey/frames/teey 16.png b/assets/teey/frames/teey 16.png
new file mode 100644
index 0000000..9cfe7c1
Binary files /dev/null and b/assets/teey/frames/teey 16.png differ
diff --git a/assets/teey/frames/teey 17.png b/assets/teey/frames/teey 17.png
new file mode 100644
index 0000000..0959b54
Binary files /dev/null and b/assets/teey/frames/teey 17.png differ
diff --git a/assets/teey/frames/teey 18.png b/assets/teey/frames/teey 18.png
new file mode 100644
index 0000000..febf7f7
Binary files /dev/null and b/assets/teey/frames/teey 18.png differ
diff --git a/assets/teey/frames/teey 19.png b/assets/teey/frames/teey 19.png
new file mode 100644
index 0000000..d1eaa98
Binary files /dev/null and b/assets/teey/frames/teey 19.png differ
diff --git a/assets/teey/frames/teey 2.png b/assets/teey/frames/teey 2.png
new file mode 100644
index 0000000..d3146fd
Binary files /dev/null and b/assets/teey/frames/teey 2.png differ
diff --git a/assets/teey/frames/teey 20.png b/assets/teey/frames/teey 20.png
new file mode 100644
index 0000000..b6c4089
Binary files /dev/null and b/assets/teey/frames/teey 20.png differ
diff --git a/assets/teey/frames/teey 21.png b/assets/teey/frames/teey 21.png
new file mode 100644
index 0000000..e89dc8d
Binary files /dev/null and b/assets/teey/frames/teey 21.png differ
diff --git a/assets/teey/frames/teey 22.png b/assets/teey/frames/teey 22.png
new file mode 100644
index 0000000..ca4ce04
Binary files /dev/null and b/assets/teey/frames/teey 22.png differ
diff --git a/assets/teey/frames/teey 23.png b/assets/teey/frames/teey 23.png
new file mode 100644
index 0000000..4646793
Binary files /dev/null and b/assets/teey/frames/teey 23.png differ
diff --git a/assets/teey/frames/teey 3.png b/assets/teey/frames/teey 3.png
new file mode 100644
index 0000000..2a21759
Binary files /dev/null and b/assets/teey/frames/teey 3.png differ
diff --git a/assets/teey/frames/teey 4.png b/assets/teey/frames/teey 4.png
new file mode 100644
index 0000000..657b6bc
Binary files /dev/null and b/assets/teey/frames/teey 4.png differ
diff --git a/assets/teey/frames/teey 5.png b/assets/teey/frames/teey 5.png
new file mode 100644
index 0000000..3a19846
Binary files /dev/null and b/assets/teey/frames/teey 5.png differ
diff --git a/assets/teey/frames/teey 6.png b/assets/teey/frames/teey 6.png
new file mode 100644
index 0000000..8997f49
Binary files /dev/null and b/assets/teey/frames/teey 6.png differ
diff --git a/assets/teey/frames/teey 7.png b/assets/teey/frames/teey 7.png
new file mode 100644
index 0000000..683d00a
Binary files /dev/null and b/assets/teey/frames/teey 7.png differ
diff --git a/assets/teey/frames/teey 8.png b/assets/teey/frames/teey 8.png
new file mode 100644
index 0000000..b51c7e2
Binary files /dev/null and b/assets/teey/frames/teey 8.png differ
diff --git a/assets/teey/frames/teey 9.png b/assets/teey/frames/teey 9.png
new file mode 100644
index 0000000..e69440e
Binary files /dev/null and b/assets/teey/frames/teey 9.png differ
diff --git a/commands.config b/commands.config
new file mode 100644
index 0000000..56f8bd9
--- /dev/null
+++ b/commands.config
@@ -0,0 +1,37 @@
+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
+beans=1
+eightball, 8ball=1
+vote=1
+results=1
+wolfram=1
+map=1
+info, about=1
+givebeans=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
diff --git a/lib/ini4j-0.5.4.jar b/lib/ini4j-0.5.4.jar
deleted file mode 100644
index 499f0af..0000000
Binary files a/lib/ini4j-0.5.4.jar and /dev/null differ
diff --git a/locCommands.config b/locCommands.config
new file mode 100644
index 0000000..dbb7feb
--- /dev/null
+++ b/locCommands.config
@@ -0,0 +1,40 @@
+[ObjectBuilderFactory]
+indicator=
+boop=
+tony, stark, dontfeelgood, dontfeelsogood=
+yeet=
+role=
+ping=
+rating=
+roll=
+blur=
+choose=
+poll=
+rpstart=
+bet=
+addguildrole=
+perish, thenperish=
+teey=
+stats=
+allowedguildrole=
+beans=
+eightball, 8ball=
+vote=
+results=
+wolfram=
+map=
+info, about=
+givebeans=
+c++, g++, cplus, cpp=
+work=
+showpoll=
+rpend=
+rpg=
+tweet=
+java, jdoodle=
+help=
+buildHelp=
+invite=
+shutdown=
+
+
diff --git a/locStrings.config b/locStrings.config
new file mode 100644
index 0000000..cc2fd23
--- /dev/null
+++ b/locStrings.config
@@ -0,0 +1,188 @@
+[CommandPollManage]
+PollManageInfo='start' will start a new poll with the query of the line you put after, 'choice' will add a choice to the poll, 'stop' will end the poll
+
+[CommandBeansShow]
+BeansShowDisplay=You have %s beans!
+BeansShowInfo=Displays how many beans you have
+
+[CommandHelp]
+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`
+
+[CommandRoll]
+RollError=Didn't work ;3;
+RollInfo=Based on input of xdy where x is number of dice and y is faces kitty will roll that amount of dice, display the individual rolls and total
+
+[CommandStats]
+StatsInfo=Displays the actively running KittyBot application information
+
+[CommandTweet]
+TweetInfo=Kitty will tweet to her personal twitter account
+TweetError=Tweet command failed!
+
+[CommandRole]
+RoleInfo=Will show the current role you have, Admins can change others roles with input of 'role x @y' with x being blacklist, general, mod, or admin. Blacklist will not allow the user to interact with kitty anymore, general mod and admin will give the user access to those commands.
+RoleStandardResponse=Your role is
+RoleError=You aren't allowed to do that! You must have the KittyRole '%' or higher!
+RoleChanged=Changed %s to role `%s`!
+RoleNeededRole=Please enter `general`, `mod`, or `admin`!
+
+[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++.
+
+[CommandJDoodle]
+JDoodleInfo=Will compile any java code you put in! Supports Java 1.8
+JDoodleError=Please provide some java code to get it compiled!
+
+[CommandChangeIndicator]
+ChangeIndicatorInfo=Changes the command indicator to any single character. By default, it's '!'. If more than one character is provided, the first one is used!
+ChangeIndicatorError=Please specify a letter or symbol to use!
+ChangeIndicatorChanged=Indicator changed to `%s`
+
+[CommandPerish]
+PerishInfo=Adds a red, 'a n g e r y' overlay to your icon or the icon of a friend you mentioned
+
+[CommandStark]
+StarkInfo=Snaps your icon or the icon of a friend you mentioned
+
+[CommandYeet]
+YeetInfo=Yeet yourself or yeet a friend with @!
+
+[CommandAllowedGuildRole]
+AllowedGuildRoleInfo=
+AllowedGuildRoleDuplicate=Can't add the same role twice!
+AllowedGuildRoleSuccess=Added %s to the allowed roles!
+
+[CommandPollVote]
+PollVoteNotValidNumber=That's not a vaild number!
+PollVoteInfo=Vote in a poll with the choice number, won't work if no poll is running, you can't change your vote once you have cast it! Be careful!
+PollVoteNoPoll=There is no poll running!
+PollVoteAlreadyVoted=You already voted
+PollVoteNotValidVote=%s That's not a vaild vote!
+PollVoteSuccess=You successfully voted for
+
+[CommandShutdown]
+ShutdownInfo=Stops kitty. `-s` or `safe` as an argument attempts to sync off the database before shutdown.
+
+[CommandTeey]
+TeeyInfo=Teey yourself back into existance, or teey a friend with @!
+
+[CommandBetBeans]
+BetBeansLowBet=Please bet with at least 50 beans!
+BetBeansInfo=Allows you to bet your beans, set your amount after the command! Warning! House always wins!\nSets of 3 will get 2x, 4 will get 10x, and 5 will get 1000x
+BetBeansNotValid=That's not a valid bet!
+BetBeansLose=Sorry, you didn't win, try again!
+BetBeansNotEnough=You don't have enough beans!
+BetBeansWin=You won %s beans!
+
+[CommandDoWork]
+DoWorkFinished=I finished with my work! :D
+DoWorkInfo=Occupies a core for a few seconds
+
+[CommandRPStart]
+RPStartInfo=Starts an rp, add people to it by mentioning them!
+
+[CommandBlurry]
+BlurryInfo=Blurs your icon or the icon of a friend you mentioned
+
+[CommandPollResults]
+PollResultsResponse=%s people voted for %s `%s` with `%s%` !\n
+PollResultsInfo=Will show current results of a poll and percentages of votes per choice
+
+[CommandRating]
+RatingInfo='0' is fully sfw (Derpi and e621 searches are disabled), '1' is filtered (kitty auto appends a sfw tag on any searches), '2' is nsfw (any search will go through). Some other words are supported for setting filter as well.
+RatingWarning=Warning: NSFW may slip through, images are only based on tags on their respective sites!
+RatingChanged=Kittybot content set to
+RatingInvalid=Invalid content rating
+
+[CommandGiveBeans]
+GiveBeansNoneMentioned=You didn't mention anyone!
+GiveBeansSuccess=Gave %s %s beans!
+GiveBeansInvalid=That's not a valid number!
+GiveBeansInfo=Gives beans to the mentioned users!
+
+[CommandPollShow]
+PollShowNoPoll=There is no poll running!
+PollShowInfo=Will show the current poll running
+PollShowPoll=The current poll is `%s`\n
+PollShowChoices=And the choices are:\n
+
+[CommandHelpBuilder]
+HelpBuilderInfo=Emit help for all commands as formatted HTML
+
+[CommandRPEnd]
+RPEndError=You can't end this RP!
+RPEndInfo=Ends RP in channel and gives you a .txt file!
+RPEndFileOut=Here's your file!
+
+[CommandRPG]
+RPGInfo=Admin+ command only right now - experimental text RPG! Format is !rpg
+RPGInvalid=Invalid RPG Command!
+
+[CommandBoop]
+BoopPerson=%s booped %s!
+BoopInfo=Kitty will react with a counter
+BoopStandard=Woah! %s booped me! That's %s total!
+BoopMultiple=%s booped several others - %s!
+
+[CommandInfo]
+InfoResponse=I'm made by `Rin#8904` and `Reverie Wisp#3703`!\nYou can find more info about me along with a Patreon link to support us and GitHub link for filing bugs https://www.rinsnowmew.com/bot/
+InfoInfo=Provides author info and a link to Kitty's website
+
+[CommandPing]
+PingResponse=Pong!
+PingInfo=Will respond with Pong!
+
+[CommandChoose]
+ChooseChoice=I chooooooose %s!
+ChooseOne=I can't choose from *one* thing!
+ChooseInfo=With an input of x,y,z where x y and z are all choices, kitty will choose one
+
+[CommandEightBall]
+EightBallYes4=Yes - definitely.
+EightBallYes3=Without a doubt.
+EightBallYes2=It is decidedly so.
+EightBallYes1=It is certain.
+EightBallYes8=Outlook good.
+EightBallYes7=Most likely.
+EightBallYes6=As I see it, yes.
+EightBallYes5=You may rely on it.
+EightBallMaybe1=Reply hazy, try again.
+EightBallMaybe2=Ask again later.
+EightBallError=Hmm? You'll need to ask a question!
+EightBallMaybe3=Better not tell you now.
+EightBallMaybe4=Cannot predict now.
+EightBallMaybe5=Concentrate and ask again.
+EightBallInfo=Answers a yes or no question! Warning: Kitty can not actually tell the future, she claims no responsibility for any lion mauling, lack of lottery wins, or felony charges.
+EightBallNo5=Very doubtful.
+EightBallNo3=My sources say no.
+EightBallNo4=Outlook not so good.
+EightBallNo1=Don't count on it.
+EightBallNo2=My reply is no.
+EightBallYes9=Yes.
+EightBallYes10=Signs point to yes.
+
+[CommandInvite]
+InviteInfo=Provides a direct invite link for KittyBot
+
+[CommandWolfram]
+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!
+
+[CommandAddGuildRole]
+AddGuildRoleInfo=Add a role to yourself!
+AddGuildRoleNotAllowed=You are not allowed to add %s!
+AddGuildRoleSuccess=Added %s to %s
+AddGuildRoleFailure=Failed to add %s to %s
+
+[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
+MapInvalid=Invalid arguments provided!
+MapVersion=Using mapgen v0.1
+MapWidth=Width
+MapHeight=Height
+
+
diff --git a/localization.config b/localization.config
deleted file mode 100644
index b810ae8..0000000
--- a/localization.config
+++ /dev/null
@@ -1,21 +0,0 @@
-[CommandBeansShow]
-Displays how many beans you have =
-You have %s beans! =
-
-[CommandBetBeans]
-Please bet with at least 50 beans! =
-Allows you to bet your beans, set your amount after the command! Warning! House always wins!\nSets of 3 will get 5x, 4 will get 10x, and 5 will get 1000x =
-That's not a valid bet! =
-Sorry, you didn't win, try again! =
-You don't have enough beans! =
-You won %s beans! =
-
-[CommandBoop]
-%s booped %s! =
-Kitty will react with a counter =
-Woah! %s booped me! That's %s total! =
-%s booped several others - %s! =
-
-[CommandBlurry]
-Blurs your icon or the icon of a friend you mentioned =
-
diff --git a/src/commands/CommandAddGuildRole.java b/src/commands/CommandAddGuildRole.java
new file mode 100644
index 0000000..3c20873
--- /dev/null
+++ b/src/commands/CommandAddGuildRole.java
@@ -0,0 +1,40 @@
+package commands;
+
+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 CommandAddGuildRole extends Command
+{
+ public CommandAddGuildRole(KittyRole level, KittyRating rating) { super(level, rating); }
+
+ @Override
+ public String HelpText() { return LocStrings.Stub("AddGuildRoleInfo"); };
+
+ @Override
+ public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
+ {
+ String role = input.args.split(" ")[0];
+ if(guild.allowedRole.contains(role))
+ {
+ if(guild.control.addRole(user.discordID, role))
+ {
+ res.Call(String.format(LocStrings.Stub("AddGuildRoleSuccess"), role, user.name));
+ }
+ else
+ {
+ res.Call(String.format(LocStrings.Stub("AddGuildRoleFailure"), role, user.name));
+ }
+ }
+ else
+ {
+ res.Call(String.format(LocStrings.Stub("AddGuildRoleNotAllowed"), role));
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/commands/CommandAllowedGuildRole.java b/src/commands/CommandAllowedGuildRole.java
new file mode 100644
index 0000000..f71697e
--- /dev/null
+++ b/src/commands/CommandAllowedGuildRole.java
@@ -0,0 +1,39 @@
+package commands;
+
+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 CommandAllowedGuildRole extends Command
+{
+ public CommandAllowedGuildRole(KittyRole level, KittyRating rating) { super(level, rating); }
+
+ @Override
+ public String HelpText() { return LocStrings.Stub("AllowedGuildRoleInfo"); }
+
+ @Override
+ public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
+ {
+ String [] roles = input.args.split(",");
+ String role;
+ for(int i = 0; i < roles.length; i++)
+ {
+ role = roles[i].trim();
+ if(guild.allowedRole.contains(role))
+ {
+ res.Call(LocStrings.Stub("AllowedGuildRoleDuplicate"));
+ }
+ else
+ {
+ guild.allowedRole.add(role);
+ res.Call(String.format(LocStrings.Stub("AllowedGuildRoleSuccess"), role));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/commands/CommandBeansShow.java b/src/commands/CommandBeansShow.java
index e832ddb..16d3f62 100644
--- a/src/commands/CommandBeansShow.java
+++ b/src/commands/CommandBeansShow.java
@@ -1,7 +1,7 @@
package commands;
import core.Command;
-import core.Localizer;
+import core.LocStrings;
import dataStructures.*;
public class CommandBeansShow extends Command
@@ -9,11 +9,11 @@ public class CommandBeansShow extends Command
public CommandBeansShow(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return Localizer.Stub("Displays how many beans you have"); };
+ public String HelpText() { return LocStrings.Stub("BeansShowInfo"); };
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
{
- res.Call(String.format(Localizer.Stub("You have %s beans!"), user.GetBeans()));
+ res.Call(String.format(LocStrings.Stub("BeansShowDisplay"), user.GetBeans()));
}
}
diff --git a/src/commands/CommandBetBeans.java b/src/commands/CommandBetBeans.java
index 2019ee0..06df15a 100644
--- a/src/commands/CommandBetBeans.java
+++ b/src/commands/CommandBetBeans.java
@@ -1,7 +1,9 @@
package commands;
+import java.util.Random;
+
import core.Command;
-import core.Localizer;
+import core.LocStrings;
import dataStructures.*;
public class CommandBetBeans extends Command
@@ -9,7 +11,7 @@ public class CommandBetBeans extends Command
public CommandBetBeans(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return Localizer.Stub("Allows you to bet your beans, set your amount after the command! Warning! House always wins!\nSets of 3 will get 5x, 4 will get 10x, and 5 will get 1000x"); }
+ public String HelpText() { return LocStrings.Stub("BetBeansInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
@@ -22,19 +24,19 @@ public class CommandBetBeans extends Command
bet = Integer.parseInt(input.args);
if(bet < 50)
{
- res.Call(Localizer.Stub("Please bet with at least 50 beans!"));
+ res.Call(LocStrings.Stub("BetBeansLowBet"));
return;
}
}
catch (NumberFormatException e)
{
- res.Call(Localizer.Stub("That's not a valid bet!"));
+ res.Call(LocStrings.Stub("BetBeansNotValid"));
return;
}
if(user.GetBeans() < bet)
{
- res.Call(Localizer.Stub("You don't have enough beans!"));
+ res.Call(LocStrings.Stub("BetBeansNotEnough"));
return;
}
@@ -49,12 +51,12 @@ public class CommandBetBeans extends Command
if(win == 0)
{
- res.Call(Localizer.Stub("Sorry, you didn't win, try again!"));
+ res.Call(LocStrings.Stub("BetBeansLose"));
return;
}
user.ChangeBeans(bet*win);
- res.Call(String.format(Localizer.Stub("You won %s beans!"), "" + (bet*win)));
+ res.Call(String.format(LocStrings.Stub("BetBeansWin"), "" + (bet*win)));
}
private int getWinning(int [] slots)
@@ -69,7 +71,7 @@ public class CommandBetBeans extends Command
counter = 1;
if(counter == 3)
- winning = 5;
+ winning = 2;
if(counter == 4)
winning = 10;
@@ -83,10 +85,12 @@ public class CommandBetBeans extends Command
private int[] getSlots()
{
+ Random gen = new Random();
int[] nums = new int[5];
for (int i = 0; i < nums.length; i++)
{
- nums[i] = (int) (Math.random() * 5) + 1;
+ nums[i] = Math.abs(gen.nextInt() % 7) + 1;
+ System.out.println(nums [i]);
}
return nums;
}
@@ -138,6 +142,12 @@ public class CommandBetBeans extends Command
case 5:
slotHearts += ":purple_heart:";
break;
+ case 6:
+ slotHearts += ":black_heart:";
+ break;
+ case 7:
+ slotHearts += ":broken_heart:";
+ break;
}
}
diff --git a/src/commands/CommandBlurry.java b/src/commands/CommandBlurry.java
index 5bb6ef5..c875ff2 100644
--- a/src/commands/CommandBlurry.java
+++ b/src/commands/CommandBlurry.java
@@ -8,7 +8,7 @@ import java.io.IOException;
import javax.imageio.ImageIO;
import core.Command;
-import core.Localizer;
+import core.LocStrings;
import dataStructures.*;
import utils.ImageUtils;
@@ -17,7 +17,7 @@ public class CommandBlurry extends Command
public CommandBlurry(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return Localizer.Stub("Blurs your icon or the icon of a friend you mentioned"); };
+ public String HelpText() { return LocStrings.Stub("BlurryInfo"); };
private static Long num = 0l;
diff --git a/src/commands/CommandBoop.java b/src/commands/CommandBoop.java
index df81882..ccd0a41 100644
--- a/src/commands/CommandBoop.java
+++ b/src/commands/CommandBoop.java
@@ -60,7 +60,7 @@ public class CommandBoop extends Command
}
@Override
- public String HelpText() { return Localizer.Stub("Kitty will react with a counter"); }
+ public String HelpText() { return LocStrings.Stub("BoopInfo"); }
// Called when the command is run!
@Override
@@ -69,14 +69,14 @@ public class CommandBoop extends Command
if(input.mentions == null)
{
boopTracker.ApplyBoop();
- res.Call(String.format(Localizer.Stub("Woah! %s booped me! That's %s total!"), user.name, boopTracker.HowMany()));
+ res.Call(String.format(LocStrings.Stub("BoopStandard"), user.name, boopTracker.HowMany()));
}
else
{
if(input.mentions.length == 1)
{
boopTracker.ApplyBoop();
- res.Call(String.format(Localizer.Stub("%s booped %s!"), user.name, input.mentions[0].name));
+ res.Call(String.format(LocStrings.Stub("BoopPerson"), user.name, input.mentions[0].name));
return;
}
@@ -90,7 +90,7 @@ public class CommandBoop extends Command
booped += "and " + input.mentions[i].name;
}
- res.Call(String.format(Localizer.Stub("%s booped several others - %s!"), user.name, booped));
+ res.Call(String.format(LocStrings.Stub("BoopMultiple"), user.name, booped));
}
}
}
\ No newline at end of file
diff --git a/src/commands/CommandChangeIndicator.java b/src/commands/CommandChangeIndicator.java
index 0a7a725..babdc4d 100644
--- a/src/commands/CommandChangeIndicator.java
+++ b/src/commands/CommandChangeIndicator.java
@@ -1,6 +1,7 @@
package commands;
import core.Command;
+import core.LocStrings;
import dataStructures.*;
public class CommandChangeIndicator extends Command
@@ -8,7 +9,7 @@ public class CommandChangeIndicator extends Command
public CommandChangeIndicator(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Changes the command indicator to any single character. By default, it's '!'. If more than one character is provided, the first one is used!"; }
+ public String HelpText() { return LocStrings.Stub("ChangeIndicatorInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
@@ -16,11 +17,11 @@ public class CommandChangeIndicator extends Command
String arg = input.args.trim();
if(arg.length() == 0)
{
- res.Call("Please specify a letter or symbol to use!");
+ res.Call(LocStrings.Stub("ChangeIndicatorError"));
return;
}
guild.SetCommandIndicator(arg.substring(0, 1));
- res.Call("Indicator changed to `" + guild.GetCommandIndicator() +"`!");
+ res.Call(String.format(LocStrings.Stub("ChangeIndicatorChanged"), guild.GetCommandIndicator()));
}
}
diff --git a/src/commands/CommandChoose.java b/src/commands/CommandChoose.java
index d65672e..e68f783 100644
--- a/src/commands/CommandChoose.java
+++ b/src/commands/CommandChoose.java
@@ -14,7 +14,7 @@ public class CommandChoose extends Command
public CommandChoose(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "With an input of x,y,z where x y and z are all choices, kitty will choose one"; }
+ public String HelpText() { return LocStrings.Stub("ChooseInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
@@ -22,10 +22,10 @@ public class CommandChoose extends Command
String [] choices = input.args.split(",");
if(choices.length == 1)
{
- res.Call("I can't choose from *one* thing!");
+ res.Call(LocStrings.Stub("ChooseOne"));
return;
}
- res.Call("I chooooooose" + choices[(int) (Math.random()*choices.length)] + "!");
+ res.Call(String.format(LocStrings.Stub("ChooseChoice"), (choices[(int) (Math.random()*choices.length)]).toString()));
}
}
diff --git a/src/commands/CommandColiru.java b/src/commands/CommandColiru.java
index 25145fa..b2f5f8e 100644
--- a/src/commands/CommandColiru.java
+++ b/src/commands/CommandColiru.java
@@ -1,6 +1,7 @@
package commands;
import core.Command;
+import core.LocStrings;
import dataStructures.*;
import network.NetworkColiru;
@@ -11,11 +12,17 @@ public class CommandColiru extends Command
public CommandColiru(KittyRole level, KittyRating rating) { super(level, rating);}
@Override
- public String HelpText() { return "Will try to compile any c++ code you put in! Supports up to C++14 standard, uses g++."; }
+ public String HelpText() { return LocStrings.Stub("ColiruInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
{
+ if(input.args.trim().length() < 1)
+ {
+ res.Call(LocStrings.Stub("ColiruError"));
+ return;
+ }
+
res.Call(compiler.compileCPlus(input.args));
}
diff --git a/src/commands/CommandDoWork.java b/src/commands/CommandDoWork.java
index e219695..c37f85f 100644
--- a/src/commands/CommandDoWork.java
+++ b/src/commands/CommandDoWork.java
@@ -15,7 +15,7 @@ public class CommandDoWork extends Command
public CommandDoWork(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Occupies a core for a few seconds"; }
+ public String HelpText() { return LocStrings.Stub("DoWorkInfo"); }
// Called when the command is run!
@Override
@@ -28,6 +28,6 @@ public class CommandDoWork extends Command
thispieceofshit += Math.log((double)i);
}
- res.Call("I finished with my work! :D");
+ res.Call(LocStrings.Stub("DoWorkFinished"));
}
}
\ No newline at end of file
diff --git a/src/commands/CommandEightBall.java b/src/commands/CommandEightBall.java
index 642e943..2e90413 100644
--- a/src/commands/CommandEightBall.java
+++ b/src/commands/CommandEightBall.java
@@ -1,25 +1,48 @@
package commands;
import core.Command;
+import core.LocStrings;
import dataStructures.*;
public class CommandEightBall extends Command
{
- String [] answers = {"It is certain.", "It is decidedly so.", "Without a doubt.", "Yes - definitely.", "You may rely on it.", "As I see it, yes.", "Most likely.",
- "Outlook good.", "Yes.", "Signs point to yes.", "Reply hazy, try again.", "Ask again later.", "Better not tell you now.", "Cannot predict now.",
- "Concentrate and ask again.", "Don't count on it.", "My reply is no.", "My sources say no.", "Outlook not so good.", "Very doubtful."};
- public CommandEightBall(KittyRole level, KittyRating rating)
+ String [] answers =
{
- super(level, rating);
- }
+ LocStrings.Stub("EightBallYes1")
+ , LocStrings.Stub("EightBallYes2")
+ , LocStrings.Stub("EightBallYes3")
+ , LocStrings.Stub("EightBallYes4")
+ , LocStrings.Stub("EightBallYes5")
+ , LocStrings.Stub("EightBallYes6")
+ , LocStrings.Stub("EightBallYes7")
+ , LocStrings.Stub("EightBallYes8")
+ , LocStrings.Stub("EightBallYes9")
+ , LocStrings.Stub("EightBallYes10")
+
+ , LocStrings.Stub("EightBallMaybe1")
+ , LocStrings.Stub("EightBallMaybe2")
+ , LocStrings.Stub("EightBallMaybe3")
+ , LocStrings.Stub("EightBallMaybe4")
+ , LocStrings.Stub("EightBallMaybe5")
+
+ , LocStrings.Stub("EightBallNo1")
+ , LocStrings.Stub("EightBallNo2")
+ , LocStrings.Stub("EightBallNo3")
+ , LocStrings.Stub("EightBallNo4")
+ , LocStrings.Stub("EightBallNo5")
+ };
+
+ public CommandEightBall(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Answers a yes or no question! Warning: Kitty can not actually tell the future,"
- + " she claims no responsibility for any lion mauling, lack of lottery wins, or felony charges."; }
+ public String HelpText() { return LocStrings.Stub("EightBallInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
{
+ if(input.args.trim().length() < 1)
+ res.Call(LocStrings.Stub("EightBallError"));
+
res.Call(answers[(int) (Math.random()*answers.length)]);
}
}
diff --git a/src/commands/CommandGiveBeans.java b/src/commands/CommandGiveBeans.java
index 5f9a7a1..a1469e9 100644
--- a/src/commands/CommandGiveBeans.java
+++ b/src/commands/CommandGiveBeans.java
@@ -1,6 +1,7 @@
package commands;
import core.Command;
+import core.LocStrings;
import dataStructures.*;
public class CommandGiveBeans extends Command
@@ -8,7 +9,7 @@ public class CommandGiveBeans extends Command
public CommandGiveBeans (KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Gives beans to the mentioned users!"; }
+ public String HelpText() { return LocStrings.Stub("GiveBeansInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
@@ -19,20 +20,20 @@ public class CommandGiveBeans extends Command
}
catch (NumberFormatException e)
{
- res.Call("That's not a valid number!");
+ res.Call(LocStrings.Stub("GiveBeansInvalid"));
return;
}
if(input.mentions == null)
{
- res.Call("You didn't mention anyone!");
+ res.Call(LocStrings.Stub("GiveBeansNoneMentioned"));
return;
}
for(int i = 0; i < input.mentions.length; i++)
{
input.mentions[i].ChangeBeans(beans);
- res.Call("Gave " + input.mentions[i].name + " " + beans + " beans!");
+ res.Call(String.format(LocStrings.Stub("GiveBeansSuccess"), input.mentions[i].name, "" + beans));
}
}
}
diff --git a/src/commands/CommandHelp.java b/src/commands/CommandHelp.java
index 580554b..fd5bd0f 100644
--- a/src/commands/CommandHelp.java
+++ b/src/commands/CommandHelp.java
@@ -9,14 +9,13 @@ import dataStructures.KittyUser;
import dataStructures.Response;
import dataStructures.UserInput;
-// TODO: Convert this to a per-command help string! This lets us do all sorts of neat stuff,
-// mostly tho it lets us construct this on the fly or by hand for a specific command!
+// Either allows for specifically looking up commands or gets a list of general commands to try out!
public class CommandHelp extends Command
{
public CommandHelp(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Lets you look up specific commands, or get a link to a list of all commands."; }
+ public String HelpText() { return LocStrings.Stub("HelpInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
@@ -25,11 +24,7 @@ public class CommandHelp extends Command
if(help == null)
{
- help = "You can get help with a specific command by typing `!help command`!"
- + "\nYou can also look at "
- + "\nGeneral Commands: `boop, roll, choose, help, info, vote, "
- + "results, showpoll, wolfram, cplus, java,"
- + "beans, role, bet, yeet`";
+ help = LocStrings.Stub("HelpDisplay");
}
else
{
diff --git a/src/commands/CommandHelpBuilder.java b/src/commands/CommandHelpBuilder.java
index 968b949..89ef69b 100644
--- a/src/commands/CommandHelpBuilder.java
+++ b/src/commands/CommandHelpBuilder.java
@@ -7,6 +7,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import core.Command;
+import core.LocStrings;
import core.Stats;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
@@ -25,7 +26,7 @@ public class CommandHelpBuilder extends Command {
}
@Override
- public String HelpText() { return "Emit help for all commands as formatted HTML"; }
+ public String HelpText() { return LocStrings.Stub("HelpBuilderInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
diff --git a/src/commands/CommandInfo.java b/src/commands/CommandInfo.java
index 99ae8b8..7fee7bf 100644
--- a/src/commands/CommandInfo.java
+++ b/src/commands/CommandInfo.java
@@ -1,6 +1,7 @@
package commands;
import core.Command;
+import core.LocStrings;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
import dataStructures.KittyRating;
@@ -14,13 +15,11 @@ public class CommandInfo extends Command
public CommandInfo(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Provides author info and a link to Kitty's website"; }
+ public String HelpText() { return LocStrings.Stub("InfoInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
{
- String info = "I'm made by `Rin#8904` and `Reverie Wisp#3703`!\n"
- + "You can find more info about me along with a Patreon link to support us and GitHub link for filing bugs https://www.rinsnowmew.com/bot/" ;
- res.Call(info);
+ res.Call(LocStrings.Stub("InfoResponse"));
}
}
\ No newline at end of file
diff --git a/src/commands/CommandInvite.java b/src/commands/CommandInvite.java
index 3bcb6a4..d74dda5 100644
--- a/src/commands/CommandInvite.java
+++ b/src/commands/CommandInvite.java
@@ -1,6 +1,7 @@
package commands;
import core.Command;
+import core.LocStrings;
import dataStructures.*;
import offline.Ref;
@@ -9,7 +10,7 @@ public class CommandInvite extends Command
public CommandInvite(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Provies a direct invite link for KittyBot"; }
+ public String HelpText() { return LocStrings.Stub("InviteInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
diff --git a/src/commands/CommandJDoodle.java b/src/commands/CommandJDoodle.java
index a2add42..ae244e4 100644
--- a/src/commands/CommandJDoodle.java
+++ b/src/commands/CommandJDoodle.java
@@ -1,6 +1,7 @@
package commands;
import core.Command;
+import core.LocStrings;
import dataStructures.*;
import network.NetworkJDoodle;
@@ -11,11 +12,17 @@ public class CommandJDoodle extends Command
public CommandJDoodle(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Will compile any java code you put in! Supports Java 1.8"; }
+ public String HelpText() { return LocStrings.Stub("JDoodleInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
{
+ if(input.args.trim().length() < 1)
+ {
+ res.Call(LocStrings.Stub("JDoodleError"));
+ return;
+ }
+
res.Call(compiler.compileJava(input.args));
}
}
diff --git a/src/commands/CommandMap.java b/src/commands/CommandMap.java
index d4fb4a9..803b93b 100644
--- a/src/commands/CommandMap.java
+++ b/src/commands/CommandMap.java
@@ -3,6 +3,7 @@ package commands;
import java.util.Random;
import core.Command;
+import core.LocStrings;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
import dataStructures.KittyRating;
@@ -22,7 +23,7 @@ public class CommandMap extends Command
public CommandMap(KittyRole roleLevel, KittyRating contentRating) { super(roleLevel, contentRating); }
@Override
- public String HelpText() { return "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 "+ MaxWidth + "),\nDefault Height: 25(max " + MaxHeight + ")"; }
+ public String HelpText() { return String.format(LocStrings.Stub("MapInfo"), "" + MaxWidth, "" + MaxHeight); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
@@ -59,7 +60,7 @@ public class CommandMap extends Command
}
catch(NumberFormatException e)
{
- res.Call("Invalid arguments provided!");
+ res.Call(LocStrings.Stub("MapInvalid"));
return;
}
@@ -75,8 +76,10 @@ public class CommandMap extends Command
}
// Response header creation
- header += "Using mapgen v0.1\n";
- header += "Seed: `" + seed + "`, Width: `"+ width +"`, Height: `"+ height + "`\n";
+ header += LocStrings.Stub("MapVersion") + "\n";
+ header += LocStrings.Stub("MapSeed") + ": `" + seed + "`, ";
+ header += LocStrings.Stub("MapWidth") + "`"+ width +"`, ";
+ header += LocStrings.Stub("MapHeight") + ": `"+ height + "`\n";
// Response body creation
body += "```\n";
diff --git a/src/commands/CommandPerish.java b/src/commands/CommandPerish.java
index 475c099..73ca6e6 100644
--- a/src/commands/CommandPerish.java
+++ b/src/commands/CommandPerish.java
@@ -8,6 +8,7 @@ import java.io.IOException;
import javax.imageio.ImageIO;
import core.Command;
+import core.LocStrings;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
import dataStructures.KittyRating;
@@ -22,7 +23,7 @@ public class CommandPerish extends Command
public CommandPerish(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Adds a red, 'a n g e r y' overlay to your icon or the icon of a friend you mentioned"; };
+ public String HelpText() { return LocStrings.Stub("PerishInfo"); };
private static Long num = 0l;
diff --git a/src/commands/CommandPing.java b/src/commands/CommandPing.java
index 137d3a9..7de6205 100644
--- a/src/commands/CommandPing.java
+++ b/src/commands/CommandPing.java
@@ -15,12 +15,12 @@ public class CommandPing extends Command
public CommandPing(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Will respond with Pong!"; }
+ public String HelpText() { return LocStrings.Stub("PingInfo"); }
// Called when the command is run!
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
{
- res.Call("Pong!");
+ res.Call(LocStrings.Stub("PingResponse"));
}
}
diff --git a/src/commands/CommandPollManage.java b/src/commands/CommandPollManage.java
index 33230a0..1a0114d 100644
--- a/src/commands/CommandPollManage.java
+++ b/src/commands/CommandPollManage.java
@@ -14,7 +14,7 @@ public class CommandPollManage extends Command
public CommandPollManage(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "'start' will start a new poll with the query of the line you put after, 'choice' will add a choice to the poll, 'stop' will end the poll"; }
+ public String HelpText() { return LocStrings.Stub("PollManageInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
diff --git a/src/commands/CommandPollResults.java b/src/commands/CommandPollResults.java
index 8178955..ac36aff 100644
--- a/src/commands/CommandPollResults.java
+++ b/src/commands/CommandPollResults.java
@@ -10,7 +10,7 @@ public class CommandPollResults extends Command
public CommandPollResults(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Will show current results of a poll and percentages of votes per choice"; }
+ public String HelpText() { return LocStrings.Stub("PollResultsInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
@@ -24,10 +24,11 @@ public class CommandPollResults extends Command
totalVotes += votes.get(i).votes;
}
+ results += "The poll was " + guild.poll + "\n";
+
for(int i = 0; i < votes.size(); i++)
{
-
- results += votes.get(i).votes + " people voted for `" + votes.get(i).choice + "` with `" + (int)(((double)votes.get(i).votes) / ((double)totalVotes) * 100) + "%`!\n";
+ results += String.format(LocStrings.Stub("PollResultsResponse"), votes.get(i).votes, votes.get(i).choice, (int)(((double)votes.get(i).votes) / ((double)totalVotes) * 100));
}
res.Call(results);
diff --git a/src/commands/CommandPollShow.java b/src/commands/CommandPollShow.java
index 46df883..08cde19 100644
--- a/src/commands/CommandPollShow.java
+++ b/src/commands/CommandPollShow.java
@@ -8,18 +8,18 @@ public class CommandPollShow extends Command
public CommandPollShow(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Will show the current poll running"; }
+ public String HelpText() { return LocStrings.Stub("PollShowInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
{
if(!guild.polling)
{
- res.Call("There is no poll running!");
+ res.Call(LocStrings.Stub("PollShowNoPoll"));
return;
}
- String poll = "The current poll is `" + guild.poll + "`\n";
- poll += "And the choices are:\n";
+ String poll = String.format(LocStrings.Stub("PollShowPoll"), guild.poll);
+ poll += LocStrings.Stub("PollShowChoices");
for(int i = 0; i < guild.choices.size(); i++)
{
poll += (i+1) + ": `" + guild.choices.get(i).choice + "`\n";
diff --git a/src/commands/CommandPollVote.java b/src/commands/CommandPollVote.java
index 120b371..d3b48f5 100644
--- a/src/commands/CommandPollVote.java
+++ b/src/commands/CommandPollVote.java
@@ -8,7 +8,7 @@ public class CommandPollVote extends Command
public CommandPollVote(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Vote in a poll with the choice number, won't work if no poll is running, you can't change your vote once you have cast it! Be careful!"; }
+ public String HelpText() { return LocStrings.Stub("PollVoteInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
@@ -17,7 +17,7 @@ public class CommandPollVote extends Command
{
if(guild.hasVoted.contains(user.uniqueID))
{
- res.Call("You already voted");
+ res.Call(LocStrings.Stub("PollVoteAlreadyVoted"));
return;
}
try
@@ -25,21 +25,23 @@ public class CommandPollVote extends Command
int voteNum = Integer.parseInt(input.args)-1;
if(voteNum >= guild.choices.size() || voteNum < 0)
{
- res.Call(voteNum + " That's not a vaild vote!");
+ res.Call(String.format(LocStrings.Stub("PollVoteNotValidVote"), voteNum));
return;
}
+
KittyPoll polled = guild.choices.get(voteNum);
polled.votes++;
guild.hasVoted.add(user.uniqueID);
- res.Call("You successfully voted for `" + polled.choice + "`!");
+ res.Call(LocStrings.Stub("PollVoteSuccess") + " `" + polled.choice + "`!");
return;
}
catch (NumberFormatException e)
{
- res.Call("That's not a vaild number!");
+ res.Call(LocStrings.Stub("PollVoteNotValidNumber"));
return;
}
}
- res.Call("There is no poll running!");
+
+ res.Call(LocStrings.Stub("PollVoteNoPoll"));
}
}
diff --git a/src/commands/CommandRPEnd.java b/src/commands/CommandRPEnd.java
index 1f97c5f..17005d6 100644
--- a/src/commands/CommandRPEnd.java
+++ b/src/commands/CommandRPEnd.java
@@ -5,6 +5,7 @@ import java.io.FileNotFoundException;
import java.io.UnsupportedEncodingException;
import core.Command;
+import core.LocStrings;
import core.RPManager;
import dataStructures.*;
@@ -13,24 +14,30 @@ public class CommandRPEnd extends Command
public CommandRPEnd (KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Ends RP in channel"; }
+ public String HelpText() { return LocStrings.Stub("RPEndInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
{
File sending = null;
- try {
+
+ try
+ {
sending = RPManager.instance.endRP(channel, user);
- } catch (FileNotFoundException | UnsupportedEncodingException e)
+ }
+ catch (FileNotFoundException | UnsupportedEncodingException e)
{
System.out.println("I don't know how you got here");
}
+
if(sending != null)
{
res.CallFile(sending, "txt");
- res.Call("Here's your file!");
+ res.Call(LocStrings.Stub("RPEndFileOut"));
}
else
- res.Call("You can't end this RP!");
+ {
+ res.Call(LocStrings.Stub("RPEndError"));
+ }
}
}
diff --git a/src/commands/CommandRPG.java b/src/commands/CommandRPG.java
index dd2273c..306aac8 100644
--- a/src/commands/CommandRPG.java
+++ b/src/commands/CommandRPG.java
@@ -1,6 +1,7 @@
package commands;
import core.Command;
+import core.LocStrings;
import core.rpg.RPGFramework;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
@@ -22,7 +23,7 @@ public class CommandRPG extends Command
}
@Override
- public String HelpText() { return "Admin+ command only right now - experimental text RPG! Format is !rpg "; };
+ public String HelpText() { return LocStrings.Stub("RPGInfo"); };
@Override
@@ -30,7 +31,9 @@ public class CommandRPG extends Command
{
if(input.args == null || input.args.length() == 0)
{
- res.Call(HelpText());
+ String output = HelpText();
+ System.out.println("Out: |" + output + "|");
+ res.Call(output);
return;
}
@@ -42,7 +45,7 @@ public class CommandRPG extends Command
if(result == null)
{
- res.Call("Invalid RPG Command!");
+ res.Call(LocStrings.Stub("RPGInvalid"));
return;
}
diff --git a/src/commands/CommandRPStart.java b/src/commands/CommandRPStart.java
index dce9b3b..b433cd0 100644
--- a/src/commands/CommandRPStart.java
+++ b/src/commands/CommandRPStart.java
@@ -3,6 +3,7 @@ package commands;
import java.util.ArrayList;
import core.Command;
+import core.LocStrings;
import core.RPManager;
import dataStructures.*;
@@ -11,7 +12,7 @@ public class CommandRPStart extends Command
public CommandRPStart (KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Starts an rp, add people to it by mentioning them!"; }
+ public String HelpText() { return LocStrings.Stub("RPStartInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
diff --git a/src/commands/CommandRating.java b/src/commands/CommandRating.java
index c2a09c2..0adaae6 100644
--- a/src/commands/CommandRating.java
+++ b/src/commands/CommandRating.java
@@ -1,6 +1,7 @@
package commands;
import core.Command;
+import core.LocStrings;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
import dataStructures.KittyRating;
@@ -14,7 +15,7 @@ public class CommandRating extends Command
public CommandRating(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "'0' is fully sfw (Derpi and e621 searches are disabled), '1' is filtered (kitty auto appends a sfw tag on any searches), '2' is nsfw (any search will go through). Some other words are supported for setting filter as well."; }
+ public String HelpText() { return LocStrings.Stub("RatingInfo"); }
// Called when the command is run!
@Override
@@ -54,11 +55,11 @@ public class CommandRating extends Command
}
if(newRating != null)
- res.Call("Kittybot content set to " + newRating);
+ res.Call(LocStrings.Stub("RatingChanged") + " " + newRating);
else
- res.Call("Invalid content rating `" + input.args + "`");
+ res.Call(LocStrings.Stub("RatingInvalid") + " `" + input.args + "`");
if(newRating.equals("Filtered"))
- res.Call("Warning: NSFW may slip through, images are only based on tags on their respective sites!");
+ res.Call(LocStrings.Stub("RatingWarning"));
}
}
\ No newline at end of file
diff --git a/src/commands/CommandRole.java b/src/commands/CommandRole.java
index 196ddda..e9633b8 100644
--- a/src/commands/CommandRole.java
+++ b/src/commands/CommandRole.java
@@ -1,6 +1,7 @@
package commands;
import core.Command;
+import core.LocStrings;
import dataStructures.*;
public class CommandRole extends Command
@@ -8,20 +9,20 @@ public class CommandRole extends Command
public CommandRole (KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Will show the current role you have, Admins can change others roles with input of 'role x @y' with x being blacklist, general, mod, or admin. Blacklist will not allow the user to interact with kitty anymore, general mod and admin will give the user access to those commands."; }
+ public String HelpText() { return LocStrings.Stub("RoleInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
{
if(input.args.isEmpty())
{
- res.Call("Your role is " + user.GetRole().name() + "!");
+ res.Call(LocStrings.Stub("RoleStandardResponse") + " " + user.GetRole().name() + "!");
return;
}
if(user.GetRole().getValue() < KittyRole.Admin.getValue())
{
- res.Call("You aren't allowed to do that! You must have the KittyRole '" + KittyRole.Admin.toString() + "' or higher!");
+ res.Call(String.format(LocStrings.Stub("RoleError"), KittyRole.Admin.toString()));
return;
}
@@ -45,7 +46,7 @@ public class CommandRole extends Command
break;
default:
- res.Call("Please enter `general`, `mod`, or `admin`!");
+ res.Call(LocStrings.Stub("RoleNeededRole"));
return;
}
String users = "";
@@ -55,6 +56,6 @@ public class CommandRole extends Command
users += input.mentions[i].name + " ";
}
- res.Call("Changed " + users + " to role `" + newRole.name() + "`!");
+ res.Call(String.format(LocStrings.Stub("RoleChanged"), users, newRole.name()));
}
}
diff --git a/src/commands/CommandRoll.java b/src/commands/CommandRoll.java
index d2f11b2..5a9ef7e 100644
--- a/src/commands/CommandRoll.java
+++ b/src/commands/CommandRoll.java
@@ -16,7 +16,7 @@ public class CommandRoll extends Command
public CommandRoll(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Based on input of xdy where x is number of dice and y is faces kitty will roll that amount of dice, display the individual rolls and total"; }
+ public String HelpText() { return LocStrings.Stub("RollInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
@@ -27,7 +27,7 @@ public class CommandRoll extends Command
}
catch(Exception e)
{
- res.Call("Didn't work ;3;");
+ res.Call(LocStrings.Stub("RollError"));
}
}
diff --git a/src/commands/CommandShutdown.java b/src/commands/CommandShutdown.java
index 7339c51..812aed5 100644
--- a/src/commands/CommandShutdown.java
+++ b/src/commands/CommandShutdown.java
@@ -2,6 +2,7 @@ package commands;
import core.Command;
import core.DatabaseManager;
+import core.LocStrings;
import core.Stats;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
@@ -10,6 +11,8 @@ import dataStructures.KittyRole;
import dataStructures.KittyUser;
import dataStructures.Response;
import dataStructures.UserInput;
+import utils.GlobalLog;
+import utils.LogFilter;
// NOTE(wisp): This is a sort of special command.
public class CommandShutdown extends Command
@@ -17,7 +20,7 @@ public class CommandShutdown extends Command
public CommandShutdown(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Stops kitty. `-s` or `safe` as an argument attempts to sync off the database before shutdown."; }
+ public String HelpText() { return LocStrings.Stub("ShutdownInfo"); }
// Called when the command is run!
@Override
@@ -40,13 +43,14 @@ public class CommandShutdown extends Command
if(isSafe)
{
- DatabaseManager.instance.Upkeep(); // Force upkeep, this works so long as on main thread.
- res.CallImmediate("Forced shutdown, database synced before abandoning threads.");
+ // Force upkeep, this works so long as upkeep is on the main thread.
+ DatabaseManager.instance.Upkeep();
+ GlobalLog.Error(LogFilter.Command, "Forced shutdown, database synced before abandoning threads.");
System.exit(0);
}
else
{
- res.CallImmediate("Forced immediate shutdown, threads abandoned without sync.");
+ GlobalLog.Error(LogFilter.Command, "Forced immediate shutdown, threads abandoned without sync.");
System.exit(0);
}
}
diff --git a/src/commands/CommandStark.java b/src/commands/CommandStark.java
index d27fa04..16378c8 100644
--- a/src/commands/CommandStark.java
+++ b/src/commands/CommandStark.java
@@ -8,6 +8,7 @@ import java.io.IOException;
import javax.imageio.ImageIO;
import core.Command;
+import core.LocStrings;
import dataStructures.*;
import utils.ImageUtils;
@@ -16,7 +17,7 @@ public class CommandStark extends Command
public CommandStark(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Snaps your icon or the icon of a friend you mentioned"; };
+ public String HelpText() { return LocStrings.Stub("StarkInfo"); };
private static Long num = 0l;
diff --git a/src/commands/CommandStats.java b/src/commands/CommandStats.java
index fe865a3..7f4f254 100644
--- a/src/commands/CommandStats.java
+++ b/src/commands/CommandStats.java
@@ -1,6 +1,7 @@
package commands;
import core.Command;
+import core.LocStrings;
import core.CommandManager.ThreadData;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
@@ -16,7 +17,7 @@ public class CommandStats extends Command
public CommandStats(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "Displays the actively running KittyBot application information"; }
+ public String HelpText() { return LocStrings.Stub("StatsInfo"); }
// Called when the command is run!
@Override
diff --git a/src/commands/CommandTeey.java b/src/commands/CommandTeey.java
new file mode 100644
index 0000000..6a3394a
--- /dev/null
+++ b/src/commands/CommandTeey.java
@@ -0,0 +1,71 @@
+package commands;
+
+import java.io.File;
+import java.io.IOException;
+import javax.imageio.ImageIO;
+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;
+import utils.ImageOverlayBuilder;
+import utils.ImageUtils;
+
+public class CommandTeey extends Command
+{
+ // Required constructor
+ public CommandTeey(KittyRole level, KittyRating rating) { super(level, rating); }
+
+ private static Long num = 0l;
+
+ @Override
+ public String HelpText() { return LocStrings.Stub("TeeyInfo"); }
+
+ // Called when the command is run!
+ @Override
+ public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
+ {
+ String name = null;
+ File teeyFile = null;
+ File teeyeeFile = null;
+
+ synchronized(num)
+ {
+ name = "teey_" + num + ".gif";
+ ++num;
+ }
+
+ try
+ {
+ KittyUser person = null;
+
+ if(input.mentions == null)
+ person = user;
+ else
+ person = input.mentions[0];
+
+ String yeeteeFilename = ImageUtils.DownloadFromURL(person.avatarID, ".png");
+ if(yeeteeFilename == null)
+ return;
+
+ teeyeeFile = new File(yeeteeFilename);
+ ImageOverlayBuilder builder = new ImageOverlayBuilder("assets/teey/frames/", "teey ", 24, 18);
+ builder.Overlay(ImageIO.read(teeyeeFile), name);
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+
+ teeyFile = new File (name);
+ res.CallFile(teeyFile, "gif");
+
+ // Thread cleanup...
+ ImageUtils.BlockingFileDelete(teeyFile);
+ ImageUtils.BlockingFileDelete(teeyeeFile);
+ }
+}
diff --git a/src/commands/CommandTweet.java b/src/commands/CommandTweet.java
index 947a26d..8c9770a 100644
--- a/src/commands/CommandTweet.java
+++ b/src/commands/CommandTweet.java
@@ -1,6 +1,7 @@
package commands;
import core.Command;
+import core.LocStrings;
import dataStructures.*;
import network.NetworkTwitter;
@@ -10,15 +11,16 @@ public class CommandTweet extends Command
public CommandTweet(KittyRole level, KittyRating rating) { super(level, rating); }
@Override
- public String HelpText() { return "With an input of x,y,z where x y and z are all choices, kitty will choose one"; }
+ public String HelpText() { return LocStrings.Stub("TweetInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
{
+ //TODO: Figure out how to pass pictures correctly
try {
res.Call(tweet.tweet(input.args));
} catch (Exception e) {
- res.Call("FUCKED UP HARD");
+ res.Call(LocStrings.Stub("TweetError"));
}
}
}
diff --git a/src/commands/CommandWolfram.java b/src/commands/CommandWolfram.java
index 8f48c2e..4947261 100644
--- a/src/commands/CommandWolfram.java
+++ b/src/commands/CommandWolfram.java
@@ -3,6 +3,7 @@ package commands;
import java.io.File;
import java.io.IOException;
import core.Command;
+import core.LocStrings;
import dataStructures.*;
import network.*;
import utils.ImageUtils;
@@ -14,13 +15,13 @@ public class CommandWolfram extends Command
public CommandWolfram(KittyRole level, KittyRating rating) { super(level, rating);}
@Override
- public String HelpText() { return "Will query wolframalpha with your question and give a full image output of the answer"; }
+ public String HelpText() { return LocStrings.Stub("WolframInfo"); }
@Override
public void OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
{
if(input.args == null || input.args.trim().length() == 0)
- res.Call("You need to provide some arguments!");
+ res.Call(LocStrings.Stub("WolframNoArgs"));
try
{
@@ -30,7 +31,7 @@ public class CommandWolfram extends Command
}
catch (IOException e)
{
- res.Call("Something went wrong!");
+ res.Call(LocStrings.Stub("WolframError"));
}
}
}
\ No newline at end of file
diff --git a/src/commands/CommandYeet.java b/src/commands/CommandYeet.java
index 646771e..65ea728 100644
--- a/src/commands/CommandYeet.java
+++ b/src/commands/CommandYeet.java
@@ -1,25 +1,10 @@
package commands;
-import java.awt.image.BufferedImage;
-import java.awt.image.DataBufferByte;
-import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;
-import java.util.Calendar;
-import java.util.Iterator;
-
-import javax.imageio.IIOException;
-import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
-import javax.imageio.ImageTypeSpecifier;
-import javax.imageio.ImageWriteParam;
-import javax.imageio.ImageWriter;
-import javax.imageio.metadata.IIOMetadata;
-import javax.imageio.metadata.IIOMetadataNode;
-import javax.imageio.stream.FileImageOutputStream;
-import javax.imageio.stream.ImageOutputStream;
-
import core.Command;
+import core.LocStrings;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
import dataStructures.KittyRating;
@@ -27,18 +12,18 @@ import dataStructures.KittyRole;
import dataStructures.KittyUser;
import dataStructures.Response;
import dataStructures.UserInput;
+import utils.ImageOverlayBuilder;
import utils.ImageUtils;
public class CommandYeet extends Command
{
-
// Required constructor
public CommandYeet(KittyRole level, KittyRating rating) { super(level, rating); }
private static Long num = 0l;
@Override
- public String HelpText() { return "Yeet yourself or yeet a friend with @!"; }
+ public String HelpText() { return LocStrings.Stub("YeetInfo"); }
// Called when the command is run!
@Override
@@ -59,20 +44,17 @@ public class CommandYeet extends Command
KittyUser person = null;
if(input.mentions == null)
- {
person = user;
- }
else
- {
person = input.mentions[0];
- }
String yeeteeFilename = ImageUtils.DownloadFromURL(person.avatarID, ".png");
if(yeeteeFilename == null)
return;
yeeteeFile = new File(yeeteeFilename);
- YEET(ImageIO.read(yeeteeFile), name);
+ ImageOverlayBuilder builder = new ImageOverlayBuilder("assets/yeet/frames/", "yeet ", 24, 18);
+ builder.Overlay(ImageIO.read(yeeteeFile), name);
}
catch (IOException e)
{
@@ -86,251 +68,4 @@ public class CommandYeet extends Command
ImageUtils.BlockingFileDelete(yeetFile);
ImageUtils.BlockingFileDelete(yeeteeFile);
}
-
- // Lets get some trash established for the yeet
- public static final boolean VERBOSE = false;
- public static final int NONE = -1;
- public static final int PIXEL_BYTE_LENGTH = 4; // 32-bit PNG, RGBA
- public static final String YEET_BASE_PATH = "assets/yeet/frames/";
- public static final int YEET_BASE_SIZE = 24;
- public static final int YEET_FPS = 18;
-
- // Some small logging functions... cause we're professionals...
- public static void Verbose(String str) { if(VERBOSE) Log("[Verbose] " + str); }
- public static void Log(String str) { System.out.println("[Log] " + str); }
- public static void Warn(String str) { System.out.println("[Warn] " + str); }
- public static void Error(String str) { System.out.println("[Error] " + str); }
-
- // Performs the Y E E T (image overlay per frame specified, catches IO errors as a note.)
- public static void YEET(BufferedImage overlay, String outpath)
- {
- try
- {
- long start = Calendar.getInstance().getTimeInMillis();
-
- ProcessYeet(overlay, outpath);
-
- long end = Calendar.getInstance().getTimeInMillis();
- Log("Took " + (end - start) + "ms");
- }
- catch (IOException e)
- {
- e.printStackTrace();
- }
- }
-
- // Processing the image frames to construct a gif. Relies on (0,255,0) pixel for image center specification.
- public static void ProcessYeet(BufferedImage overlay, String outfileName) throws IOException
- {
- BufferedImage[] frames = new BufferedImage[YEET_BASE_SIZE];
-
- for(int i = 0; i < YEET_BASE_SIZE; ++i)
- {
- BufferedImage out = CombineImages(YEET_BASE_PATH + "yeet " + i + ".png", overlay);
-
- if(out != null)
- frames[i] = out;
- else
- Error("There was an issue with frame " + i);
- }
-
- Verbose("Processed " + YEET_BASE_SIZE + " frames, writing...");
- ImageOutputStream output = new FileImageOutputStream(new File(outfileName));
- GifSequenceWriter writer = new GifSequenceWriter(output, frames[0].getType(), YEET_FPS, true);
-
- for(int i = 0; i < YEET_BASE_SIZE; ++i)
- writer.writeToSequence(frames[i]);
-
- writer.close();
- output.close();
- }
-
- // Performs an overlay at a pixel location. We're making some assumptions here, mostly that there is going
- // to be an RGBA-32-bit encoded PNG image read in for our parsing purposes. We then look for a 0, 255, 0
- // green pixel on the base frame to apply the overlay buffer to, centered.
- public static BufferedImage CombineImages(String pathBase, BufferedImage overlay) throws IOException
- {
- // Acquire images
- BufferedImage imageBase = null;
- BufferedImage imageOverlay = overlay;
-
- imageBase = ImageIO.read(new File(pathBase));
-
- // Grab data we know won't change. As a side note, I discovered you can get specific pixels a bit
- // differently later, but it was too late, I wrote the pixel specific code already.
- final byte[] pixelsBase = ((DataBufferByte) imageBase.getRaster().getDataBuffer()).getData();
- final int baseWidth = imageBase.getWidth();
- final int overlayWidth = imageOverlay.getWidth();
- final int baseHeight = imageBase.getHeight();
- final int overlayHeight = imageOverlay.getHeight();
-
- // Warn about odd sizing when applicable
- if(overlayHeight > baseHeight || overlayWidth > baseWidth)
- Warn("Size mismatch, overlay is larger. May function, but not supported.");
-
- // Skim base picture for solid green pixel to use as target center
- int targetX = NONE;
- int targetY = NONE;
- for(int i = 0; i < pixelsBase.length; i += PIXEL_BYTE_LENGTH)
- {
- final int x = ((i / PIXEL_BYTE_LENGTH) % baseWidth);
- final int y = ((i / PIXEL_BYTE_LENGTH) / baseWidth);
-
- final int r = (pixelsBase[i]);
- final int g = (pixelsBase[i + 1]);
- final int b = (pixelsBase[i + 2]);
- //final int a = (pixelsBase[i + 3]);
-
- // Max contrast in 32-bit PNG for green is no red no blue, max green (ratio), max alpha (implied 0 here)
- if(r == -1 && g == 0 && b == -1)
- {
- //Log("Found target pixel at x: " + x + " y: " + y);
- targetX = x;
- targetY = y;
- break;
- }
- }
-
- // Skip overlay if we have no target location
- if(targetX == NONE || targetY == NONE)
- {
- Verbose("No overlay required for this frame.");
- return imageBase;
- }
-
- // Re-create a new byte array and establish overlay bounds
- final int left = targetX - overlayWidth / 2;
- final int right = targetX + overlayWidth / 2;
- final int top = targetY - overlayHeight / 2;
- final int bottom = targetY + overlayHeight / 2;
-
- // Apply overlay when appropriate
- for(int i = 0; i < pixelsBase.length; i += PIXEL_BYTE_LENGTH)
- {
- final int x = ((i / PIXEL_BYTE_LENGTH) % baseWidth);
- final int y = ((i / PIXEL_BYTE_LENGTH) / baseWidth);
-
- // If we're within the bounds of where the overlay picture should go...
- if(x > left && x < right && y > top && y < bottom)
- {
- final int overlayX = x - left;
- final int overlayY = y - top;
-
- // Skip out of bounds pixels
- if(overlayX < 0 || overlayY < 0 || overlayX >= baseWidth || overlayY >= baseHeight)
- continue;
-
- // Re-write bytes as overlay
- imageBase.setRGB(x, y, imageOverlay.getRGB(overlayX, overlayY));
- }
- }
-
- // Give the frame back
- return imageBase;
- }
-
-
- // GifSequenceWriter.java
- //
- // Created by Elliot Kroo on 2009-04-25,
- // (Small modifications by wisp in 2018)
- //
- // This work is licensed under the Creative Commons Attribution 3.0 Unported License.
- // To view a copy of this license visit http://creativecommons.org/licenses/by/3.0/
- // NOTE(wisp): This is Elliot's original license
- public static class GifSequenceWriter
- {
- protected ImageWriter gifWriter;
- protected ImageWriteParam imageWriteParam;
- protected IIOMetadata imageMetaData;
-
- // NOTE: This is limited to things that divide into 1000 cleanly as multiples of 10.
- public GifSequenceWriter(ImageOutputStream outputStream, int imageType, int framesPerSecond, boolean isLooping) throws IIOException, IOException
- {
- gifWriter = getWriter();
- imageWriteParam = gifWriter.getDefaultWriteParam();
- ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(imageType);
-
- imageMetaData = gifWriter.getDefaultImageMetadata(imageTypeSpecifier, imageWriteParam);
-
- String metaFormatName = imageMetaData.getNativeMetadataFormatName();
-
- IIOMetadataNode root = (IIOMetadataNode) imageMetaData.getAsTree(metaFormatName);
- IIOMetadataNode graphicsControlExtensionNode = getNode(root, "GraphicControlExtension");
- graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
- graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
- graphicsControlExtensionNode.setAttribute("transparentColorFlag", "FALSE");
- graphicsControlExtensionNode.setAttribute("delayTime", "" + (int) ((1000.0 / framesPerSecond) / 10.0)); // THis line isn't super well honored
- graphicsControlExtensionNode.setAttribute("transparentColorIndex","0");
-
- IIOMetadataNode appEntensionsNode = getNode( root, "ApplicationExtensions");
- IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");
- child.setAttribute("applicationID", "NETSCAPE");
- child.setAttribute("authenticationCode", "2.0");
-
- int loopValue = isLooping ? 0 : 1;
- child.setUserObject(new byte[]{ 0x1, (byte) (loopValue & 0xFF), (byte) ((loopValue >> 8) & 0xFF)});
- appEntensionsNode.appendChild(child);
-
- imageMetaData.setFromTree(metaFormatName, root);
-
- gifWriter.setOutput(outputStream);
- gifWriter.prepareWriteSequence(null);
- }
-
- public void writeToSequence(RenderedImage img) throws IOException
- {
- gifWriter.writeToSequence(new IIOImage(img, null, imageMetaData), imageWriteParam);
- }
-
- /**
- * Close this GifSequenceWriter object. This does not close the underlying
- * stream, just finishes off the GIF.
- */
- public void close() throws IOException
- {
- gifWriter.endWriteSequence();
- }
-
- /**
- * Returns the first available GIF ImageWriter using
- * ImageIO.getImageWritersBySuffix("gif").
- *
- * @return a GIF ImageWriter object
- * @throws IIOException if no GIF image writers are returned
- */
- private static ImageWriter getWriter() throws IIOException
- {
- Iterator iter = ImageIO.getImageWritersBySuffix("gif");
-
- if(!iter.hasNext())
- throw new IIOException("No GIF Image Writers Exist");
- else
- return iter.next();
- }
-
- /**
- * Returns an existing child node, or creates and returns a new child node (if
- * the requested node does not exist).
- *
- * @param rootNode the IIOMetadataNode to search for the child node.
- * @param nodeName the name of the child node.
- *
- * @return the child node, if found or a new node created with the given name.
- */
- private static IIOMetadataNode getNode(IIOMetadataNode rootNode, String nodeName)
- {
- int nodeCount = rootNode.getLength();
- for (int i = 0; i < nodeCount; i++)
- {
- if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName) == 0)
- return((IIOMetadataNode) rootNode.item(i));
- }
-
- IIOMetadataNode node = new IIOMetadataNode(nodeName);
- rootNode.appendChild(node);
-
- return(node);
- }
- }
}
diff --git a/src/core/Command.java b/src/core/Command.java
index e252f9c..a8defdb 100644
--- a/src/core/Command.java
+++ b/src/core/Command.java
@@ -1,7 +1,6 @@
package core;
import java.util.ArrayList;
-
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
import dataStructures.KittyRating;
@@ -42,7 +41,7 @@ public abstract class Command
return false;
}
- //TODO(wisp, rin): ADD CHANNEL CHECK HERE
+ //TODO: ADD CHANNEL CHECK HERE
if(user.GetRole().getValue() >= roleLevel.getValue())
{
diff --git a/src/core/CommandEnabler.java b/src/core/CommandEnabler.java
new file mode 100644
index 0000000..a45f047
--- /dev/null
+++ b/src/core/CommandEnabler.java
@@ -0,0 +1,124 @@
+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.HashMap;
+
+import utils.FileUtils;
+import utils.GlobalLog;
+import utils.LogFilter;
+
+// While some of the patterns in this file are similar to the localization files,
+// 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
+{
+ // 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;
+
+ // Local variables
+ private HashMap enabledMap; // Quick lookup
+ private ArrayList keyList; // Tracking ordering for later
+
+ public CommandEnabler()
+ {
+ GlobalLog.Log(LogFilter.Core, "Initializing " + this.getClass().getSimpleName());
+ enabledMap = new HashMap<>();
+ keyList = new ArrayList<>();
+
+ ReadIn();
+ GetTrackedCommands();
+ WriteOut();
+ }
+
+ // 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);
+
+ 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);
+
+ 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
+ // don't already exist in the lookup. Defaults to defaultEnabledState.
+ private void GetTrackedCommands()
+ {
+ ArrayList unloc = LocCommands.GetUnlocalizedCommands();
+
+ for(int i = 0; i < unloc.size(); ++i)
+ {
+ String command = unloc.get(i);
+ if(enabledMap.putIfAbsent(command, defaultEnabledState) == null)
+ {
+ GlobalLog.Log(LogFilter.Strings, "Identified new toggleable raw command: " + command);
+ keyList.add(command);
+ }
+ }
+ }
+
+ // Write out enabled/disabled file info.
+ private void WriteOut()
+ {
+ try
+ {
+ 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;
+ }
+
+ 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());
+ }
+ }
+
+ // 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);
+
+ return true;
+ }
+}
diff --git a/src/core/CommandManager.java b/src/core/CommandManager.java
index e3e8970..dac2714 100644
--- a/src/core/CommandManager.java
+++ b/src/core/CommandManager.java
@@ -6,6 +6,7 @@ import java.util.Map.Entry;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
import dataStructures.KittyUser;
+import dataStructures.Pair;
import dataStructures.Response;
import dataStructures.UserInput;
import utils.GlobalLog;
@@ -16,22 +17,40 @@ public class CommandManager
// Variables
private HashMap commands;
private ArrayList threadAccumulator;
+ private CommandEnabler commandEnabler;
private long invokeCount;
-
// Default Constructor
- public CommandManager()
+ public CommandManager(CommandEnabler commandEnabler)
{
- commands = new HashMap();
- threadAccumulator = new ArrayList();
- invokeCount = 0;
+ this.commands = new HashMap();
+ this.threadAccumulator = new ArrayList();
+ this.invokeCount = 0;
+ this.commandEnabler = commandEnabler;
}
- // Allows the command manager to keep track of a command.
- public void Register(String key, Command command)
- {
- if(key == null)
- return;
+ // Allows the command manager to keep track of a command. Takes a pair (the un-localized and localzied commands)
+ // and the command associated with the localized commands.
+ public void Register(Pair pair, Command command)
+ {
+ if(pair == null || pair.Second == null)
+ return;
+
+ // If we haven't already split a multisplit command (or even assessed that),
+ // then verify if we even need to register the commands at all. If it's
+ // not enabled, don't register it.
+ if(pair.First != null && !commandEnabler.IsEnabled(pair.First))
+ return;
+
+ String key = pair.Second;
+
+ if(key.contains(","))
+ {
+
+ String[] keys = key.split(",");
+ Register(keys, command);
+ return;
+ }
key = key.toLowerCase();
command.registeredNames.add(key);
@@ -51,7 +70,7 @@ public class CommandManager
public void Register(String[] keys, Command command)
{
for(int i = 0; i < keys.length; ++i)
- Register(keys[i], command);
+ Register(new Pair(null, keys[i].trim()), command);
}
// Calls the command but on a whole new thread!
diff --git a/src/core/LocBase.java b/src/core/LocBase.java
new file mode 100644
index 0000000..8106779
--- /dev/null
+++ b/src/core/LocBase.java
@@ -0,0 +1,190 @@
+package core;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.nio.charset.Charset;
+import java.nio.file.Files;
+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;
+
+// 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
+{
+ // Pre-defined values
+ public static final String KittySourceDirectory = "./src";
+
+ // Filename
+ public final String filename; // Example: "localization.config";
+ public final String functionName; // Example: "Localizer.Stub";
+
+ // Local translation storage
+ protected TaggedPairStore stringStore;
+
+ // Logging
+ private void Log(String str) { GlobalLog.Log(LogFilter.Strings, str); }
+ private void Warn(String str) { GlobalLog.Warn(LogFilter.Strings, str); }
+ private void Error(String str) { GlobalLog.Error(LogFilter.Strings, str); }
+
+ // 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)
+ {
+ this.filename = filename;
+ this.functionName = functionName;
+ }
+
+ // Structure used for holding a pair of strings and any other info we need
+ // about localized information that is being looked up.
+ private class LocInfo
+ {
+ public String file;
+ public String phrase;
+
+ public LocInfo(String f, String p)
+ {
+ this.file = f;
+ this.phrase = p;
+ }
+ }
+
+ // Do processing on each path in the scraped directory here, assuming it's .java
+ public void StripForContents(Path path, ArrayList strings)
+ {
+ String filename = path.getFileName().toString();
+ if(filename.contains(".java"))
+ {
+ String contents = FileUtils.ReadContent(path);
+ String[] split = contents.split(functionName);
+
+ // Identify all localizer function calls
+ for(int i = 1; i < split.length; ++i)
+ {
+ int loc = split[i].indexOf(")");
+ String noWhitespace = split[i].replaceAll("\\s+","");
+
+ if(loc != -1)
+ {
+ if(noWhitespace.charAt(noWhitespace.indexOf(")") - 1) == '"' && split[i].charAt(loc - 2) != '\\')
+ {
+ // At this point, we find the first ), then verify there's a ") behind it, and that
+ // the " is not an escaped character.
+ try
+ {
+ String toLocalize = split[i].substring(2, loc - 1);
+
+ strings.add(new LocInfo(filename.substring(0, filename.lastIndexOf('.')), toLocalize));
+
+ Log("Found lookup call in " + path + ": " + toLocalize);
+ }
+ catch(IndexOutOfBoundsException e)
+ {
+ Warn("Found phrase but couldn't parse in file " + path);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // Nothing for now, but in the future will return a parsed and localized version of
+ // the string in question if one can be found. If the localized string is empty,
+ // returns a the key instead which is the default phrase.
+ public String GetKey(String input)
+ {
+ if(stringStore == null)
+ return input;
+
+ String value = stringStore.GetKey(input);
+ if(value == null || value.trim().length() < 1)
+ return input;
+
+ return value;
+ }
+
+ // Reads a file to string, adapted from https://stackoverflow.com/a/326440/5383198
+ private String ReadFileAsString(String path, Charset encoding)
+ {
+ try
+ {
+ byte[] encoded = Files.readAllBytes(Paths.get(path));
+ return new String(encoded, encoding);
+ }
+ catch (IOException e)
+ {
+ Warn("No file found to read from!");
+ }
+
+ return null;
+ }
+
+ // Update localization from the disk on file. Creates the file if it doesn't exist.
+ // This file is internally formatted as an ini file.
+ public void UpdateLocFromDisk()
+ {
+ Log("Attempting to read localization file: " + filename);
+
+ try
+ {
+ String fileContents = ReadFileAsString(filename, Charset.defaultCharset());
+
+ if(fileContents == null)
+ {
+ File file = new File(filename);
+ file.createNewFile();
+ }
+
+ stringStore = new TaggedPairStore(fileContents);
+ }
+ catch(IOException e)
+ {
+ Error("IO exception during localization file read");
+ }
+ }
+
+ // Rewrites out at the specified filename with existing stubs.
+ // This preserves existing localized phrases.
+ public void SaveLocToDisk()
+ {
+ Log("Attempting to write updated localization file");
+
+ try
+ {
+ PrintWriter pw = new PrintWriter(filename);
+ pw.println(stringStore.toString());
+ pw.close();
+ }
+ catch(IOException e)
+ {
+ Error("IO exception during localization file write");
+ }
+ }
+
+ private void TryStripSpecified(Path path, ArrayList toFill)
+ {
+ try
+ {
+ StripForContents(path, toFill);
+ }
+ catch(Exception e)
+ {
+ Error("issue with file: " + path.toString());
+ }
+ }
+
+ // Scrape the project and generate all the possible localizeable phrases.
+ // This stubs out phrases to be localized.
+ public void ScrapeAll()
+ {
+ ArrayList localizeList = new ArrayList();
+ FileUtils.AcquireAllFiles(KittySourceDirectory).forEach((path) -> TryStripSpecified(path, localizeList));
+
+ for(LocInfo toStub : localizeList)
+ stringStore.AddKeyValue(toStub.file, toStub.phrase, toStub.phrase);
+ }
+}
diff --git a/src/core/LocCommands.java b/src/core/LocCommands.java
new file mode 100644
index 0000000..04c6ed5
--- /dev/null
+++ b/src/core/LocCommands.java
@@ -0,0 +1,50 @@
+package core;
+
+import java.util.ArrayList;
+import utils.GlobalLog;
+import utils.LogFilter;
+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 static final String fileName = "locCommands.config";
+ public static final String function = "LocCommands.Stub";
+
+ private static LocCommands instance;
+
+ public LocCommands()
+ {
+ super(fileName, function);
+
+ GlobalLog.Log(LogFilter.Core, "Initializing " + this.getClass().getSimpleName());
+
+ if(instance == null)
+ {
+ instance = this;
+
+ UpdateLocFromDisk();
+ ScrapeAll();
+ SaveLocToDisk();
+ }
+ else
+ {
+ GlobalLog.Error(LogFilter.Core, "You can't have two of the following: " + this.getClass().getSimpleName());
+ }
+ }
+
+ // Returns a pair, the raw key and the stub key
+ public static Pair Stub(String toStub)
+ {
+ return new Pair(toStub, instance.GetKey(toStub));
+ }
+
+ // Gets all of the un-translated defaults in the commands list.
+ public static ArrayList GetUnlocalizedCommands()
+ {
+ ArrayList raw = new ArrayList<>();
+ instance.stringStore.ForEach((pair) -> raw.add((String)((Pair, ?>)pair).First ));
+ return raw;
+ }
+}
diff --git a/src/core/LocStrings.java b/src/core/LocStrings.java
new file mode 100644
index 0000000..2cee4ed
--- /dev/null
+++ b/src/core/LocStrings.java
@@ -0,0 +1,40 @@
+package core;
+
+import utils.GlobalLog;
+import utils.LogFilter;
+
+// 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 static final String fileName = "locStrings.config";
+ public static final String function = "LocStrings.Stub";
+
+ private static LocStrings instance;
+
+ public LocStrings()
+ {
+ super(fileName, function);
+
+ GlobalLog.Log(LogFilter.Core, "Initializing " + this.getClass().getSimpleName());
+
+ if(instance == null)
+ {
+ instance = this;
+
+ UpdateLocFromDisk();
+ ScrapeAll();
+ SaveLocToDisk();
+ }
+ else
+ {
+ GlobalLog.Error(LogFilter.Core, "You can't have two of the following: " + this.getClass().getSimpleName());
+ }
+ }
+
+ public static String Stub(String toStub)
+ {
+ return instance.GetKey(toStub);
+ }
+}
\ No newline at end of file
diff --git a/src/core/Localizer.java b/src/core/Localizer.java
deleted file mode 100644
index 8498f9a..0000000
--- a/src/core/Localizer.java
+++ /dev/null
@@ -1,182 +0,0 @@
-package core;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.HashMap;
-import org.ini4j.InvalidFileFormatException;
-import org.ini4j.Profile.Section;
-import org.ini4j.Wini;
-
-import utils.FileUtils;
-import utils.GlobalLog;
-import utils.LogFilter;
-
-// 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 Localizer
-{
- // Filename
- public final static String filename = "localization.config";
- public final static String functionName = "Localizer.Stub";
-
- // Local translation
- private static HashMap translated = new HashMap();
-
- // Logging
- private static void Log(String str) { GlobalLog.Log(LogFilter.Strings, str); }
- private static void Warn(String str) { GlobalLog.Warn(LogFilter.Strings, str); }
- private static void Error(String str) { GlobalLog.Error(LogFilter.Strings, str); }
-
- // Structure used for holding a pair of strings and any other info we need
- // about localized information that is being looked up.
- private static class LocInfo
- {
- public String file;
- public String phrase;
-
- public LocInfo(String f, String p)
- {
- this.file = f;
- this.phrase = p;
- }
- }
-
- // Do processing on each path in the scraped directory here, assuming it's .java
- public static void StripForContents(Path path, ArrayList strings)
- {
- String filename = path.getFileName().toString();
- if(filename.contains(".java"))
- {
- String contents = FileUtils.ReadContent(path);
- String[] split = contents.split(functionName);
-
- // Identify all localizer function calls
- for(int i = 1; i < split.length; ++i)
- {
- int loc = split[i].indexOf(")");
-
- if(loc != -1)
- {
- if(split[i].charAt(loc - 1) == '"' && split[i].charAt(loc - 2) != '\\')
- {
- // At this point, we find the first ), then verify there's a ") behind it, and that
- // the " is not an escaped character.
- try
- {
- String toLocalize = split[i].substring(2, loc - 1);
-
- strings.add(new LocInfo(filename.substring(0, filename.lastIndexOf('.')), toLocalize));
-
- Log("Found stubbed phrase in " + path + " : " + toLocalize);
- }
- catch(IndexOutOfBoundsException e)
- {
- Warn("Found phrase but couldn't parse in file " + path);
- }
- }
- }
- }
- }
- }
-
- // Nothing for now, but in the future will return a parsed and localized version of
- // the string in question if one can be found.
- public static String Stub(String input)
- {
- if(translated.containsKey(input))
- {
- String value = translated.get(input).phrase;
- if(value.trim().length() > 0)
- return value;
- }
-
- return input;
- }
-
- // Update localization from the disk on file. Creates the file if it doesn't exist.
- // This file is internally formatted as an ini file.
- public static void UpdateLocFromDisk()
- {
- Log("Attempting to read localization file");
- try
- {
- File file = new File(filename);
- file.createNewFile();
-
- Wini ini = new Wini(file);
-
- for(String str : ini.keySet())
- {
- // Store everything in all .ini sections
- Section sec = ini.get(str);
- for(String s : sec.keySet())
- {
- if(!translated.containsKey(s))
- translated.put(s, new LocInfo(sec.getName(), sec.get(s, String.class)));
- }
- }
- }
- catch(InvalidFileFormatException e)
- {
- Error("File issue with format during localization file read");
- }
- catch(IOException e)
- {
- Error("IO exception during localization file read");
- }
- }
-
- // Rewrites out at the specified filename with existing stubs.
- // This preserves existing localized phrases.
- public static void SaveLocToDisk()
- {
- Log("Attempting to write updated localization file");
- try
- {
- PrintWriter pw = new PrintWriter(filename);
- pw.close();
-
- File file = new File(filename);
- file.createNewFile();
-
- Wini ini = new Wini(file);
-
- for(String s : translated.keySet())
- {
- LocInfo toWrite = translated.get(s);
- ini.put(toWrite.file, s, toWrite.phrase);
- }
-
- ini.store();
- }
- catch(InvalidFileFormatException e)
- {
- Error("File issue with format during localization file write");
- }
- catch(IOException e)
- {
- Error("IO exception during localization file write");
- }
- }
-
- // Scrape the project and generate all the possible localizeable phrases.
- // This stubs out phrases to be localized.
- public static void ScrapeAll()
- {
- ArrayList localizeList = new ArrayList();
- FileUtils.AcquireAllFiles(".\\src").forEach((path) -> StripForContents(path, localizeList));
-
- for(LocInfo toStub : localizeList)
- {
- if(!translated.containsKey(toStub.phrase))
- {
- translated.put(toStub.phrase, new LocInfo(toStub.file, ""));
- Log("Found new stubbed phrase '" + toStub.phrase + "' in " + toStub.file);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/src/core/ObjectBuilderFactory.java b/src/core/ObjectBuilderFactory.java
index c709df4..a186f28 100644
--- a/src/core/ObjectBuilderFactory.java
+++ b/src/core/ObjectBuilderFactory.java
@@ -3,17 +3,58 @@ package core;
import java.util.HashMap;
import java.util.concurrent.Semaphore;
-import commands.*;
-import dataStructures.*;
+import commands.CommandAddGuildRole;
+import commands.CommandAllowedGuildRole;
+import commands.CommandBeansShow;
+import commands.CommandBetBeans;
+import commands.CommandBlurry;
+import commands.CommandBoop;
+import commands.CommandChangeIndicator;
+import commands.CommandChoose;
+import commands.CommandColiru;
+import commands.CommandDoWork;
+import commands.CommandEightBall;
+import commands.CommandGiveBeans;
+import commands.CommandHelp;
+import commands.CommandHelpBuilder;
+import commands.CommandInfo;
+import commands.CommandInvite;
+import commands.CommandJDoodle;
+import commands.CommandMap;
+import commands.CommandPerish;
+import commands.CommandPing;
+import commands.CommandPollManage;
+import commands.CommandPollResults;
+import commands.CommandPollShow;
+import commands.CommandPollVote;
+import commands.CommandRPEnd;
+import commands.CommandRPG;
+import commands.CommandRPStart;
+import commands.CommandRating;
+import commands.CommandRole;
+import commands.CommandRoll;
+import commands.CommandShutdown;
+import commands.CommandStark;
+import commands.CommandStats;
+import commands.CommandTeey;
+import commands.CommandTweet;
+import commands.CommandWolfram;
+import commands.CommandYeet;
+import dataStructures.KittyChannel;
+import dataStructures.KittyGuild;
+import dataStructures.KittyRating;
+import dataStructures.KittyRole;
+import dataStructures.KittyUser;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent;
+import utils.AdminControl;
import utils.GlobalLog;
import utils.LogFilter;
// NOTE(wisp): Isolated factory to assist with storage and caching if needed.
// This also minimizes the number of places JDA interacts with our codebase.
// As it stands, if the object name begins with Kitty, it's constructed here.
-// TODO: Make all methods ID based instead of event based
+// TODO: Make all methods ID based instead of event based
public class ObjectBuilderFactory
{
// Key: guild string id, Value: guild information
@@ -34,9 +75,20 @@ public class ObjectBuilderFactory
// RPManger for tracking RP system
private static RPManager rpManager;
- // Lazy initialization style for
+ // Localization classes - these are singletons, but should be initialized before almost all other
+ // things so their inclusion in the factory is to ensure they're started at the correct time.
+ @SuppressWarnings("unused") private static LocStrings locStrings;
+ @SuppressWarnings("unused") private static LocCommands locCommands;
+
+ // Handles if we can or can't use specific commands, parsing a config file based on loc data to do so.
+ private static CommandEnabler commandEnabler;
+
+ // Lazy initialization multithreaded mutex stuff to prevent explosions.
+ // TODO: Investigate using 'synchronized' instead potentially
private static boolean hasInitialized;
private static Semaphore initMutex = new Semaphore(1);
+
+ // This is it, this is how the lazy init starts!
private static void LazyInit()
{
if(hasInitialized)
@@ -47,26 +99,31 @@ public class ObjectBuilderFactory
initMutex.acquire();
try
{
- // Initialization here. This is where we could read from something external.
+ // structure initialization
+ // Construct necessary data structures.
guildCache = new HashMap();
userCache = new HashMap();
channelCache = new HashMap();
database = null;
stats = null;
+
+ // Start by reading from things that are external. Because
+ // we require these things to be resolved before the rest of the application,
+ // we place them here.
+ locStrings = new LocStrings();
+ locCommands = new LocCommands();
}
finally
{
initMutex.release();
+ hasInitialized = true;
}
}
catch(InterruptedException ie)
{
GlobalLog.Error(LogFilter.Core, "Issue during object builder lazy initialization."
- + " The factory was not initialized, "
- + "and kitty will not be able to continue functionally.");
+ + " The factory was not initialized, and kitty will not be able to continue functionally.");
}
-
- hasInitialized = true;
}
// Explicitly locks: guildCache
@@ -79,7 +136,7 @@ public class ObjectBuilderFactory
// look up the guild.
String uid = event.getGuild().getId();
- // ince we're lazily initialized, we can synchronize w/ the
+ // once we're lazily initialized, we can synchronize w/ the
// guildCache object now instead of having to use a mutex.
KittyGuild guild = null;
synchronized (guildCache)
@@ -92,7 +149,7 @@ public class ObjectBuilderFactory
else
{
// Construct a new guild with defaults
- guild = new KittyGuild(uid);
+ guild = new KittyGuild(uid, new AdminControl(event.getGuild()));
DatabaseManager.instance.Register(guild);
guildCache.put(uid, guild);
}
@@ -276,58 +333,72 @@ public class ObjectBuilderFactory
user.avatarID = member.getUser().getAvatarUrl();
}
- // Default construction of the command manager.
- // TODO(wisp): We want to be able to keep all this data
- // stored off in a file at some point, so we can reflect it onto the
- // project and build it per-guild. That's for later now tho.
- public static CommandManager ConstructCommandManager()
+ // Constructs a CommandEnabler if it doesn't exist, and gets the existing one if it does.
+ public static CommandEnabler ConstructCommandEnabler()
+ {
+ LazyInit();
+
+ if(commandEnabler == null)
+ commandEnabler = new CommandEnabler();
+
+ return commandEnabler;
+ }
+
+ // 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
+ // only have one CommandEnabler.
+ public static CommandManager ConstructCommandManager(CommandEnabler commandEnabler)
{
LazyInit();
- CommandManager manager = new CommandManager();
+ CommandManager manager = new CommandManager(commandEnabler);
- manager.Register("work", new CommandDoWork(KittyRole.Dev, KittyRating.Safe));
- manager.Register("shutdown", new CommandShutdown(KittyRole.Dev, KittyRating.Safe));
- manager.Register("stats", new CommandStats(KittyRole.Dev, KittyRating.Safe));
- manager.Register("invite", new CommandInvite(KittyRole.Dev, KittyRating.Safe));
- manager.Register("buildHelp", new CommandHelpBuilder(KittyRole.Dev, KittyRating.Safe));
- manager.Register("tweet", new CommandTweet(KittyRole.Dev, KittyRating.Safe));
+ 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("rating", new CommandRating(KittyRole.Admin, KittyRating.Safe));
- manager.Register("indicator", new CommandChangeIndicator(KittyRole.Admin, KittyRating.Safe));
+ 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("allowedguildrole"), new CommandAllowedGuildRole(KittyRole.Admin, KittyRating.Safe));
- manager.Register("poll", new CommandPollManage(KittyRole.Mod, KittyRating.Safe));
- manager.Register("givebeans", new CommandGiveBeans(KittyRole.Mod, KittyRating.Safe));
- manager.Register("rpg", new CommandRPG(KittyRole.Mod, KittyRating.Safe));
+ 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));
- manager.Register(new String[]{"perish", "thenperish"}, new CommandPerish(KittyRole.General, KittyRating.Safe));
- manager.Register("yeet", new CommandYeet(KittyRole.General, KittyRating.Safe));
- manager.Register("ping", new CommandPing(KittyRole.General, KittyRating.Safe));
- manager.Register("boop", new CommandBoop(KittyRole.General, KittyRating.Safe));
- manager.Register("roll", new CommandRoll(KittyRole.General, KittyRating.Safe));
- manager.Register("choose", new CommandChoose(KittyRole.General, KittyRating.Safe));
- manager.Register("help", new CommandHelp(KittyRole.General, KittyRating.Safe));
- manager.Register(new String[] {"info", "about"}, new CommandInfo(KittyRole.General, KittyRating.Safe));
- manager.Register("vote", new CommandPollVote(KittyRole.General, KittyRating.Safe));
- manager.Register("results", new CommandPollResults(KittyRole.General, KittyRating.Safe));
- manager.Register("showpoll", new CommandPollShow(KittyRole.General, KittyRating.Safe));
- manager.Register("wolfram", new CommandWolfram(KittyRole.General, KittyRating.Safe));
- manager.Register(new String[] {"c++", "g++", "cplus",}, new CommandColiru(KittyRole.General, KittyRating.Safe));
- manager.Register(new String[] {"java", "jdoodle" }, new CommandJDoodle(KittyRole.General, KittyRating.Safe));
- manager.Register("beans", new CommandBeansShow(KittyRole.General, KittyRating.Safe));
- manager.Register("role", new CommandRole(KittyRole.General, KittyRating.Safe));
- manager.Register("bet", new CommandBetBeans(KittyRole.General, KittyRating.Safe));
- manager.Register("map", new CommandMap(KittyRole.General, KittyRating.Safe));
- manager.Register("rpstart", new CommandRPStart(KittyRole.General, KittyRating.Safe));
- manager.Register("rpend", new CommandRPEnd(KittyRole.General, KittyRating.Safe));
- manager.Register(new String[] {"tony", "stark", "dontfeelgood", "dontfeelsogood"}, new CommandStark(KittyRole.General, KittyRating.Safe));
- manager.Register("blur", new CommandBlurry(KittyRole.General, KittyRating.Safe));
- manager.Register(new String [] {"eightball", "8ball"}, new CommandEightBall(KittyRole.General, KittyRating.Safe));
+ manager.Register(LocCommands.Stub("addguildrole"), new CommandAddGuildRole(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));
return manager;
}
- // NOTE(wisp): Default database manager construction. It can be constructed
+ // Default database manager construction. It can be constructed
// in different ways, and so we construct it outside of the constructor for
// the factory since it doesn't have to be present / can be elsewhere.
// Effectively we cache the database here.
diff --git a/src/dataStructures/KittyGuild.java b/src/dataStructures/KittyGuild.java
index c35791e..4a9ad22 100644
--- a/src/dataStructures/KittyGuild.java
+++ b/src/dataStructures/KittyGuild.java
@@ -2,6 +2,7 @@ package dataStructures;
import java.util.ArrayList;
import core.DatabaseTrackedObject;
+import utils.AdminControl;
// Context for a given guild for kittybot. Primarily designed to hold guild-specific settings.
public class KittyGuild extends DatabaseTrackedObject
@@ -12,17 +13,19 @@ public class KittyGuild extends DatabaseTrackedObject
public boolean polling;
public String poll;
public ArrayList hasVoted = new ArrayList();
+ public ArrayList allowedRole = new ArrayList();
public ArrayList choices = new ArrayList();
+ public AdminControl control;
private String commandIndicator;
// Default content for a guild
- public KittyGuild(String uniqueID)
+ public KittyGuild(String uniqueID, AdminControl adminControl)
{
super(uniqueID);
-
+ control = adminControl;
this.uniqueID = uniqueID;
- this.contentRating = KittyRating.Safe;
+ this.contentRating = KittyRating.Safe;
this.polling = false;
SetCommandIndicator("!");
}
diff --git a/src/dataStructures/Pair.java b/src/dataStructures/Pair.java
new file mode 100644
index 0000000..e4771db
--- /dev/null
+++ b/src/dataStructures/Pair.java
@@ -0,0 +1,14 @@
+package dataStructures;
+
+// A generic class designed to hold two types, as a first and second variable.
+public class Pair
+{
+ public T1 First;
+ public T2 Second;
+
+ public Pair(T1 first, T2 second)
+ {
+ this.First = first;
+ this.Second = second;
+ }
+}
diff --git a/src/dataStructures/TaggedPairStore.java b/src/dataStructures/TaggedPairStore.java
new file mode 100644
index 0000000..25b6a87
--- /dev/null
+++ b/src/dataStructures/TaggedPairStore.java
@@ -0,0 +1,193 @@
+package dataStructures;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
+// This is a class designed to parse ini inspired key-value pairs that are sectioned off.
+// The difference here is that this is more permissive than an ini file, and only accepts
+// a single split character, not the traditional set an ini does. All of the following are valid:
+//
+// [ExampleSection]
+// Key=Value
+// Valid Ridiculous Key&\n\t_.:;foo = \tValid Ridiculous Value*&%$()^.[]{}@
+// EmptyValue=
+//
+// Note that the sections are NOT designed to allow for duplicate keys across them.
+// This is a restriction of the structure, but can be changed later potentially.
+// The only value not allowed in a key or value is the KeyValueSplit.
+public class TaggedPairStore
+{
+ // Variables
+ public final char SectionStart = '[';
+ public final char SectionEnd = ']';
+ public final char PairLineSeparator = '\n';
+ public final String PairSplit = "=";
+
+ // [Key: SectionName, [Key: KeyString, Value: ValueString]]
+ private HashMap> taggedPairs;
+
+ // [Key: KeyString, Value: ValueString]]
+ private HashMap allPairs;
+
+ // String constructor that parses the input string into the object
+ public TaggedPairStore(String input)
+ {
+ taggedPairs = new HashMap>();
+ allPairs = new HashMap();
+ Parse(input);
+ }
+
+ // Calls back on each item in the entire structure. Provides section, then a pair of the keyString and valueString.
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public void ForEach(BiConsumer super String, Pair super String, ? super String>> action)
+ {
+ Iterator it = taggedPairs.entrySet().iterator();
+ while (it.hasNext())
+ {
+ Map.Entry pair = (Map.Entry)it.next();
+ Iterator internal = ((HashMap)pair.getValue()).entrySet().iterator();
+
+ while(internal.hasNext())
+ {
+ Map.Entry internalPair = (Map.Entry)internal.next();
+ action.accept((String)pair.getKey(), new Pair((String)internalPair.getKey(), (String)internalPair.getValue()));
+ }
+ }
+ }
+
+ // Calls back each item in the structure but does not priv
+ @SuppressWarnings({"rawtypes"})
+ public void ForEach(Consumer> action)
+ {
+ Iterator it = allPairs.entrySet().iterator();
+ while (it.hasNext())
+ {
+ Map.Entry pair = (Map.Entry)it.next();
+ action.accept(new Pair((String)pair.getKey(), (String)pair.getValue()));
+ }
+ }
+
+ // Parses the internal hashmap as a string then reutrns it, featuring sections.
+ // Iterates over all key/value pairs in the section and print them. Does not print
+ // the value of a given key if it is the same as the key.
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public String toString()
+ {
+ String out = "";
+
+ // Iterates over the sections and
+ Iterator it = taggedPairs.entrySet().iterator();
+ while (it.hasNext())
+ {
+ Map.Entry pair = (Map.Entry)it.next();
+ out += ("" + SectionStart + pair.getKey() + SectionEnd + PairLineSeparator);
+
+ Iterator internal = ((HashMap)pair.getValue()).entrySet().iterator();
+ while(internal.hasNext())
+ {
+ Map.Entry internalPair = (Map.Entry)internal.next();
+ String key = (String)internalPair.getKey();
+ String value = (String)internalPair.getValue();
+
+ if(key == value)
+ out += (key + PairSplit) + PairLineSeparator;
+ else
+ out += (key + PairSplit + value) + PairLineSeparator;
+ }
+
+ out += PairLineSeparator;
+ }
+
+ return out;
+ }
+
+ // Parses out the string passed in into the sectionkeyValue HashMap.
+ // Any character used in a split call is escaped just in case on
+ // account of some characters having specific regex meanings.
+ private void Parse(String input)
+ {
+ if(input == null)
+ return;
+
+ String[] sections = input.split("\\" + SectionStart);
+
+ for(int sec = 0; sec < sections.length; ++sec)
+ {
+ // Gather information about the contents of the section, and the header.
+ String section = sections[sec];
+ if(section.length() < 2)
+ continue;
+
+ int pos = section.indexOf(SectionEnd);
+ if(pos == -1)
+ continue;
+
+ // Parse section name. If it already exists, don't bother making it.
+ String sectionName = section.substring(0, pos);
+ AddSection(sectionName);
+
+ // Parse out the pairs within the section, split them all out.
+ String unparsedPairs = section.substring(pos + 1);
+ String[] pairs = unparsedPairs.split("\\" + PairLineSeparator);
+
+ // Parse out valid key-value pairs, and store them in the specified section.
+ // At this point, we can be guarenteed that sectionName is in the Hashmap.
+ for(int pair = 0; pair < pairs.length; ++pair)
+ {
+ String line = pairs[pair];
+ int splitPos = line.indexOf(PairSplit);
+ if(splitPos < 0)
+ continue;
+
+ String key = line.substring(0, splitPos);
+ String value = line.substring(splitPos + PairSplit.length());
+ taggedPairs.get(sectionName).putIfAbsent(key, value);
+ allPairs.putIfAbsent(key, value);
+ }
+ }
+ }
+
+ // Dumps out a string array
+ @SuppressWarnings("unused")
+ private void Dump(String[] toPrint)
+ {
+ System.out.println("Length: " + toPrint.length);
+
+ for(int i = 0; i < toPrint.length; ++i)
+ System.out.println(toPrint[i]);
+ }
+
+ // Adds a KeyValue pair to the specified section if it's not already there.
+ // Also creates the section if it's not already present.
+ public void AddKeyValue(String sectionName, String key, String value)
+ {
+ AddSection(sectionName);
+ taggedPairs.get(sectionName).putIfAbsent(key, value);
+ allPairs.putIfAbsent(key, value);
+ }
+
+ // Adds a given section to the hashmap if it's not already present
+ public void AddSection(String sectionName)
+ {
+ taggedPairs.putIfAbsent(sectionName, new HashMap());
+ }
+
+ // Returns a HashMap of Keys to Values for a given section
+ @SuppressWarnings("unchecked")
+ public HashMap GetSection(String sectionName)
+ {
+ return (HashMap) taggedPairs.get(sectionName).clone();
+ }
+
+ // Look up a global key
+ public String GetKey(String key)
+ {
+ if(allPairs.containsKey(key))
+ return allPairs.get(key);
+
+ return null;
+ }
+}
diff --git a/src/main/Main.java b/src/main/Main.java
index 50ae429..2b7bc6a 100644
--- a/src/main/Main.java
+++ b/src/main/Main.java
@@ -1,7 +1,6 @@
package main;
import javax.security.auth.login.LoginException;
-
import core.*;
import dataStructures.KittyChannel;
import dataStructures.KittyGuild;
@@ -14,35 +13,33 @@ import net.dv8tion.jda.core.entities.*;
import net.dv8tion.jda.core.events.message.guild.*;
import net.dv8tion.jda.core.hooks.ListenerAdapter;
import offline.*;
-import core.Localizer;
import utils.GlobalLog;
import net.dv8tion.jda.core.*;
-// NOTE(wisp): http://www.slf4j.org/ - this JDA logging tool has been disabled by specifying NOP implementation.
-// NOTE(wisp): Application entry point!
+// 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!
+
+@SuppressWarnings("unused")
public class Main extends ListenerAdapter
{
// Variables and stuff
private static JDA kitty;
private static CommandManager commandManager;
+ private static CommandEnabler commandEnabler;
private static DatabaseManager databaseManager;
private static Stats stats;
private static RPManager rpManager;
-
+
// Main test location
public static void main(String[] args) throws InterruptedException, LoginException, Exception
{
- // Facotry startup.
+ // Factory startup. The ordering is intentional.
databaseManager = ObjectBuilderFactory.ConstructDatabaseManager();
- commandManager = ObjectBuilderFactory.ConstructCommandManager();
+ commandEnabler = ObjectBuilderFactory.ConstructCommandEnabler();
+ commandManager = ObjectBuilderFactory.ConstructCommandManager(commandEnabler);
rpManager = ObjectBuilderFactory.ConstructRPManager();
stats = ObjectBuilderFactory.ConstructStats(commandManager);
- // Localizer startup - Potentially integrate with the factory.
- Localizer.UpdateLocFromDisk();
- Localizer.ScrapeAll();
- Localizer.SaveLocToDisk();
-
// Bot startup
kitty = new JDABuilder(AccountType.BOT).setToken(Ref.TestToken).buildBlocking();
kitty.getPresence().setGame(Game.playing("with a new build"));
@@ -56,8 +53,6 @@ public class Main extends ListenerAdapter
if(!PreProcessSetup(event))
return;
- GlobalLog.Log("Parseable message recieved!");
-
// Factory objects
KittyUser user = ObjectBuilderFactory.ExtractUser(event);
KittyGuild guild = ObjectBuilderFactory.ExtractGuild(event);
@@ -74,7 +69,7 @@ public class Main extends ListenerAdapter
// Track beans!
user.ChangeBeans(1);
- //RP logging system
+ // RP logging system
RPManager.instance.addLine(channel, user, input);
// Issue the command
@@ -140,6 +135,7 @@ public class Main extends ListenerAdapter
{
databaseManager.Upkeep();
RPManager.Upkeep(kitty);
+
return true;
}
}
\ No newline at end of file
diff --git a/src/network/NetworkDerpi.java b/src/network/NetworkDerpi.java
index 2719b4d..04f82f5 100644
--- a/src/network/NetworkDerpi.java
+++ b/src/network/NetworkDerpi.java
@@ -1,7 +1,6 @@
package network;
import com.google.gson.Gson;
-import core.*;
import dataStructures.GenericImage;
import offline.*;
import utils.*;
diff --git a/src/network/NetworkE621.java b/src/network/NetworkE621.java
index 5017964..6cadeca 100644
--- a/src/network/NetworkE621.java
+++ b/src/network/NetworkE621.java
@@ -1,8 +1,8 @@
package network;
import com.google.gson.Gson;
-import core.*;
import dataStructures.GenericImage;
+import offline.Ref;
import utils.*;
/**
@@ -25,7 +25,7 @@ public class NetworkE621
private static final Gson jsonParser_ = new Gson();
private static final String API_ROOT = "https://e621.net/post/index.json?";
private static int maxSearchResults_ = 10;
- private static String[] blacklist = {"theallseeingeye","scat","diaper","cub"};
+ private static String[] blacklist = Ref.e621Blacklist;
private class E621ResponseObject
{
// public varaibles matching the case and the type we want for JSON.
diff --git a/src/utils/AdminControl.java b/src/utils/AdminControl.java
new file mode 100644
index 0000000..7124f75
--- /dev/null
+++ b/src/utils/AdminControl.java
@@ -0,0 +1,29 @@
+package utils;
+
+import net.dv8tion.jda.core.entities.Guild;
+import net.dv8tion.jda.core.managers.GuildController;
+
+public class AdminControl
+{
+ private GuildController guildCon;
+ private Guild guild;
+
+ public AdminControl(Guild guild)
+ {
+ this.guild = guild;
+ guildCon = guild.getController();
+ }
+
+ public boolean addRole(String memberID, String roleName)
+ {
+ try
+ {
+ guildCon.addRolesToMember(guild.getMemberById(memberID), guild.getRolesByName(roleName, true)).complete();
+ }
+ catch(Exception e)
+ {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/utils/FileUtils.java b/src/utils/FileUtils.java
index 4d3ef48..716fae3 100644
--- a/src/utils/FileUtils.java
+++ b/src/utils/FileUtils.java
@@ -1,5 +1,6 @@
package utils;
+import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
@@ -11,6 +12,7 @@ import java.util.stream.Stream;
public class FileUtils
{
// 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)
{
StringBuilder contentBuilder = new StringBuilder();
@@ -24,7 +26,7 @@ public class FileUtils
}
catch (IOException e)
{
- e.printStackTrace();
+ GlobalLog.Error(LogFilter.Util, e.getMessage());
}
return contentBuilder.toString();
@@ -35,13 +37,18 @@ public class FileUtils
{
ArrayList items = new ArrayList();
+ File tmpDir = new File(startingDir);
+ if(!tmpDir.exists())
+ return new ArrayList();
+
+
try
{
Files.find(Paths.get(startingDir), 999, (path, attributes) -> attributes.isRegularFile()).forEach(items::add);
}
catch (IOException e)
{
- e.printStackTrace();
+ GlobalLog.Error(LogFilter.Util, e.getMessage());
}
return items;
diff --git a/src/utils/GlobalLog.java b/src/utils/GlobalLog.java
index f350d2d..2cf28b9 100644
--- a/src/utils/GlobalLog.java
+++ b/src/utils/GlobalLog.java
@@ -13,7 +13,7 @@ public class GlobalLog
}
public static void Log(String msg) { Write(log, LogFilter.Debug, msg); }
- public static void Warn(String msg) {Write(warn, LogFilter.Debug, msg); }
+ public static void Warn(String msg) { Write(warn, LogFilter.Debug, msg); }
public static void Error(String msg) { Write(error, LogFilter.Debug, msg); }
public static void Fatal(String msg) throws Exception { Write(fatal, LogFilter.Debug, msg); throw new Exception(msg); }
diff --git a/src/utils/ImageOverlayBuilder.java b/src/utils/ImageOverlayBuilder.java
new file mode 100644
index 0000000..068e95d
--- /dev/null
+++ b/src/utils/ImageOverlayBuilder.java
@@ -0,0 +1,308 @@
+package utils;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferByte;
+import java.awt.image.RenderedImage;
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Calendar;
+import java.util.Iterator;
+import javax.imageio.IIOException;
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageTypeSpecifier;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
+import javax.imageio.metadata.IIOMetadata;
+import javax.imageio.metadata.IIOMetadataNode;
+import javax.imageio.stream.FileImageOutputStream;
+import javax.imageio.stream.ImageOutputStream;
+
+// A class that overlays an image based on a series of frames that will be converted into a gif.
+// The class targets a pixel with max Green value and no red or blue that's fully opaque (0, 255, 0)
+// and then places the center of the overlay image on the specified location. Must be 32-bit PNGs with RGBA format.
+public class ImageOverlayBuilder
+{
+ // Lets get some trash established for the yeet
+ public static final boolean VERBOSE = false;
+ public static final int NONE = -1;
+ public static final int PIXEL_BYTE_LENGTH = 4; // 32-bit PNG, RGBA
+ public static final int DEFAULT_FPS = 18;
+ public static final String FRAME_FILE_EXTENSION = ".png";
+ public static final String LOG_HEADER = "[Overlay] ";
+
+ // Logging intermediate functions
+ private static void Verbose(String str) { if(VERBOSE) Log("[Verbose] " + str); }
+ private static void Log(String str) { GlobalLog.Log(LogFilter.Util, LOG_HEADER + str); }
+ private static void Warn(String str) { GlobalLog.Warn(LogFilter.Util, LOG_HEADER + str); }
+ private static void Error(String str) { GlobalLog.Error(LogFilter.Util, LOG_HEADER + str); }
+
+ // Local variables
+ private final String basePath; // The folder location of the files
+ private final String baseName; // This is the consistent part of the filename of each frame, for example "image "
+ private final int frameCount; // The number of frames in the image
+ private final int fps; // This doesn't quite line up, multiples of 10 are as specific as it gets.
+
+ // Required minimal constructor
+ public ImageOverlayBuilder(String basePath, String baseName, int frameCount)
+ {
+ this(basePath, baseName, frameCount, DEFAULT_FPS);
+ }
+
+ // Constructor with framerate specification
+ public ImageOverlayBuilder(String basePath, String baseName, int frameCount, int fps)
+ {
+ this.basePath = basePath;
+ this.baseName = baseName;
+ this.frameCount = frameCount;
+ this.fps = fps;
+ }
+
+ // Performs a per-frame overlay, catches IO errors as a note.)
+ public void Overlay(BufferedImage overlay, String outpath)
+ {
+ try
+ {
+ long start = Calendar.getInstance().getTimeInMillis();
+
+ ProcessOverlay(overlay, outpath);
+
+ long end = Calendar.getInstance().getTimeInMillis();
+ Log("Took " + (end - start) + "ms");
+ }
+ catch (IOException e)
+ {
+ Error(e.getMessage());
+ }
+ }
+
+ // Processing the image frames to construct a gif. Relies on (0,255,0) pixel for image center specification.
+ private void ProcessOverlay(BufferedImage overlay, String outfileName) throws IOException
+ {
+ BufferedImage[] frames = new BufferedImage[frameCount];
+
+ for(int i = 0; i < frameCount; ++i)
+ {
+ BufferedImage out = CombineImages(basePath + baseName + i + FRAME_FILE_EXTENSION , overlay);
+
+ if(out != null)
+ frames[i] = out;
+ else
+ Error("There was an issue with overlaying frame " + i);
+ }
+
+ Verbose("Processed " + frameCount + " frames, writing...");
+ ImageOutputStream output = new FileImageOutputStream(new File(outfileName));
+ GifSequenceWriter writer = new GifSequenceWriter(output, frames[0].getType(), fps, true);
+
+ for(int i = 0; i < frameCount; ++i)
+ writer.writeToSequence(frames[i]);
+
+ writer.close();
+ output.close();
+ }
+
+ // Performs an overlay at a pixel location. We're making some assumptions here, mostly that there is going
+ // to be an RGBA-32-bit encoded PNG image read in for our parsing purposes. We then look for a 0, 255, 0
+ // green pixel on the base frame to apply the overlay buffer to, centered.
+ private BufferedImage CombineImages(String pathBase, BufferedImage overlay) throws IOException
+ {
+ // Acquire images
+ BufferedImage imageBase = null;
+ BufferedImage imageOverlay = overlay;
+
+ imageBase = ImageIO.read(new File(pathBase));
+
+ // Grab data we know won't change. As a side note, I discovered you can get specific pixels a bit
+ // differently later, but it was too late, I wrote the pixel specific code already.
+ final byte[] pixelsBase = ((DataBufferByte) imageBase.getRaster().getDataBuffer()).getData();
+ final int baseWidth = imageBase.getWidth();
+ final int overlayWidth = imageOverlay.getWidth();
+ final int baseHeight = imageBase.getHeight();
+ final int overlayHeight = imageOverlay.getHeight();
+
+ // Warn about odd sizing when applicable
+ if(overlayHeight > baseHeight || overlayWidth > baseWidth)
+ Warn("Size mismatch, overlay is larger. May function, but not supported.");
+
+ // Skim base picture for solid green pixel to use as target center
+ int targetX = NONE;
+ int targetY = NONE;
+ for(int i = 0; i < pixelsBase.length; i += PIXEL_BYTE_LENGTH)
+ {
+ final int x = ((i / PIXEL_BYTE_LENGTH) % baseWidth);
+ final int y = ((i / PIXEL_BYTE_LENGTH) / baseWidth);
+
+ final int r = (pixelsBase[i]);
+ final int g = (pixelsBase[i + 1]);
+ final int b = (pixelsBase[i + 2]);
+ //final int a = (pixelsBase[i + 3]);
+
+ // Max contrast in 32-bit PNG for green is no red no blue, max green (ratio), max alpha (implied 0 here)
+ if(r == -1 && g == 0 && b == -1)
+ {
+ //Log("Found target pixel at x: " + x + " y: " + y);
+ targetX = x;
+ targetY = y;
+ break;
+ }
+ }
+
+ // Skip overlay if we have no target location
+ if(targetX == NONE || targetY == NONE)
+ {
+ Verbose("No overlay required for this frame.");
+ return imageBase;
+ }
+
+ // Re-create a new byte array and establish overlay bounds
+ final int left = targetX - overlayWidth / 2;
+ final int right = targetX + overlayWidth / 2;
+ final int top = targetY - overlayHeight / 2;
+ final int bottom = targetY + overlayHeight / 2;
+
+ // Apply overlay when appropriate
+ for(int i = 0; i < pixelsBase.length; i += PIXEL_BYTE_LENGTH)
+ {
+ final int x = ((i / PIXEL_BYTE_LENGTH) % baseWidth);
+ final int y = ((i / PIXEL_BYTE_LENGTH) / baseWidth);
+
+ // If we're within the bounds of where the overlay picture should go...
+ // ...then get bytes and manually apply alpha overlay.
+ if(x > left && x < right && y > top && y < bottom)
+ {
+ final int overlayX = x - left;
+ final int overlayY = y - top;
+
+ // Skip out of bounds pixels
+ if(overlayX < 0 || overlayY < 0 || overlayX >= baseWidth || overlayY >= baseHeight)
+ continue;
+
+ byte[] overlayBytes = ByteBuffer.allocate(4).putInt(imageOverlay.getRGB(overlayX, overlayY)).array();
+ byte[] baseBytes = ByteBuffer.allocate(4).putInt(imageBase.getRGB(x, y)).array();
+ float overlayT = (overlayBytes[0] & 0xFF) / 255.0f;
+
+ // Java has a restriction where we can't actually manupulate unsigned bytes apparently(?) so
+ // what this does is convert the specific bytes to ints in order to manipuate them, then takes the
+ // byte buffer and parses the far right byte out of the int after manipulating the value.
+ byte[] output = new byte[4];
+ output[0] = (byte)0b11111111;
+ output[1] = ByteBuffer.allocate(4).putInt((int) ((baseBytes[1] & 0xFF) - (overlayT * ((baseBytes[1] & 0xFF) - (overlayBytes[1] & 0xFF))))).array()[3];
+ output[2] = ByteBuffer.allocate(4).putInt((int) ((baseBytes[2] & 0xFF) - (overlayT * ((baseBytes[2] & 0xFF) - (overlayBytes[2] & 0xFF))))).array()[3];
+ output[3] = ByteBuffer.allocate(4).putInt((int) ((baseBytes[3] & 0xFF) - (overlayT * ((baseBytes[3] & 0xFF) - (overlayBytes[3] & 0xFF))))).array()[3];
+
+ // Re-write bytes as overlay
+ imageBase.setRGB(x, y, ByteBuffer.wrap(output).getInt());
+ }
+ }
+
+ // Give the frame back
+ return imageBase;
+ }
+
+
+ // GifSequenceWriter.java
+ //
+ // Created by Elliot Kroo on 2009-04-25,
+ // (Small modifications by wisp in 2018)
+ //
+ // This work is licensed under the Creative Commons Attribution 3.0 Unported License.
+ // To view a copy of this license visit http://creativecommons.org/licenses/by/3.0/
+ // NOTE: ^ This is Elliot's original license
+ private static class GifSequenceWriter
+ {
+ protected ImageWriter gifWriter;
+ protected ImageWriteParam imageWriteParam;
+ protected IIOMetadata imageMetaData;
+
+ // NOTE: This is limited to things that divide into 1000 cleanly as multiples of 10.
+ public GifSequenceWriter(ImageOutputStream outputStream, int imageType, int framesPerSecond, boolean isLooping) throws IIOException, IOException
+ {
+ gifWriter = getWriter();
+ imageWriteParam = gifWriter.getDefaultWriteParam();
+ ImageTypeSpecifier imageTypeSpecifier = ImageTypeSpecifier.createFromBufferedImageType(imageType);
+
+ imageMetaData = gifWriter.getDefaultImageMetadata(imageTypeSpecifier, imageWriteParam);
+
+ String metaFormatName = imageMetaData.getNativeMetadataFormatName();
+
+ IIOMetadataNode root = (IIOMetadataNode) imageMetaData.getAsTree(metaFormatName);
+ IIOMetadataNode graphicsControlExtensionNode = getNode(root, "GraphicControlExtension");
+ graphicsControlExtensionNode.setAttribute("disposalMethod", "none");
+ graphicsControlExtensionNode.setAttribute("userInputFlag", "FALSE");
+ graphicsControlExtensionNode.setAttribute("transparentColorFlag", "FALSE");
+ graphicsControlExtensionNode.setAttribute("delayTime", "" + (int) ((1000.0 / framesPerSecond) / 10.0)); // THis line isn't super well honored
+ graphicsControlExtensionNode.setAttribute("transparentColorIndex","0");
+
+ IIOMetadataNode appEntensionsNode = getNode( root, "ApplicationExtensions");
+ IIOMetadataNode child = new IIOMetadataNode("ApplicationExtension");
+ child.setAttribute("applicationID", "NETSCAPE");
+ child.setAttribute("authenticationCode", "2.0");
+
+ int loopValue = isLooping ? 0 : 1;
+ child.setUserObject(new byte[]{ 0x1, (byte) (loopValue & 0xFF), (byte) ((loopValue >> 8) & 0xFF)});
+ appEntensionsNode.appendChild(child);
+
+ imageMetaData.setFromTree(metaFormatName, root);
+
+ gifWriter.setOutput(outputStream);
+ gifWriter.prepareWriteSequence(null);
+ }
+
+ public void writeToSequence(RenderedImage img) throws IOException
+ {
+ gifWriter.writeToSequence(new IIOImage(img, null, imageMetaData), imageWriteParam);
+ }
+
+ /**
+ * Close this GifSequenceWriter object. This does not close the underlying
+ * stream, just finishes off the GIF.
+ */
+ public void close() throws IOException
+ {
+ gifWriter.endWriteSequence();
+ }
+
+ /**
+ * Returns the first available GIF ImageWriter using
+ * ImageIO.getImageWritersBySuffix("gif").
+ *
+ * @return a GIF ImageWriter object
+ * @throws IIOException if no GIF image writers are returned
+ */
+ private static ImageWriter getWriter() throws IIOException
+ {
+ Iterator iter = ImageIO.getImageWritersBySuffix("gif");
+
+ if(!iter.hasNext())
+ throw new IIOException("No GIF Image Writers Exist");
+ else
+ return iter.next();
+ }
+
+ /**
+ * Returns an existing child node, or creates and returns a new child node (if
+ * the requested node does not exist).
+ *
+ * @param rootNode the IIOMetadataNode to search for the child node.
+ * @param nodeName the name of the child node.
+ *
+ * @return the child node, if found or a new node created with the given name.
+ */
+ private static IIOMetadataNode getNode(IIOMetadataNode rootNode, String nodeName)
+ {
+ int nodeCount = rootNode.getLength();
+ for (int i = 0; i < nodeCount; i++)
+ {
+ if (rootNode.item(i).getNodeName().compareToIgnoreCase(nodeName) == 0)
+ return((IIOMetadataNode) rootNode.item(i));
+ }
+
+ IIOMetadataNode node = new IIOMetadataNode(nodeName);
+ rootNode.appendChild(node);
+
+ return(node);
+ }
+ }
+}
diff --git a/src/utils/LogFilter.java b/src/utils/LogFilter.java
index eeb9d3a..22742fe 100644
--- a/src/utils/LogFilter.java
+++ b/src/utils/LogFilter.java
@@ -2,7 +2,8 @@ package utils;
public enum LogFilter
{
- Debug(0), Command(1), Core(2), Database(4), Response(8), Network(16), Strings(32); //8, 16, 32... (flags, so we can | together later)
+ // Assign numbers as flags, so we can | ('or') them together as necessary
+ Debug(0), Command(1), Core(2), Util(4), Database(8), Response(16), Network(32), Strings(64);
private final int value;
private LogFilter(int value)