diff --git a/.classpath b/.classpath
index 6cf8f5a..30f7b88 100644
--- a/.classpath
+++ b/.classpath
@@ -27,5 +27,6 @@
+
diff --git a/assets/beans/BeansBG.png b/assets/beans/BeansBG.png
new file mode 100644
index 0000000..ae1bd6d
Binary files /dev/null and b/assets/beans/BeansBG.png differ
diff --git a/assets/beans/hunBean.png b/assets/beans/hunBean.png
new file mode 100644
index 0000000..824db96
Binary files /dev/null and b/assets/beans/hunBean.png differ
diff --git a/assets/beans/oneBean.png b/assets/beans/oneBean.png
new file mode 100644
index 0000000..ec96286
Binary files /dev/null and b/assets/beans/oneBean.png differ
diff --git a/assets/beans/tenBean.png b/assets/beans/tenBean.png
new file mode 100644
index 0000000..36a3f92
Binary files /dev/null and b/assets/beans/tenBean.png differ
diff --git a/assets/beans/thouBean.png b/assets/beans/thouBean.png
new file mode 100644
index 0000000..3b4eeb8
Binary files /dev/null and b/assets/beans/thouBean.png differ
diff --git a/assets/config.csv b/assets/config.csv
index 437a63e..bdc348e 100644
--- a/assets/config.csv
+++ b/assets/config.csv
@@ -17,6 +17,8 @@
"Localized Commands","dbstats",""
"Localized Commands","defaultdance",""
"Localized Commands","derp",""
+"Localized Commands","derpi",""
+"Localized Commands","e6",""
"Localized Commands","e621",""
"Localized Commands","eightball, 8ball",""
"Localized Commands","fetch",""
@@ -48,6 +50,7 @@
"Localized Commands","stats",""
"Localized Commands","teey",""
"Localized Commands","tony, stark, dontfeelgood, dontfeelsogood","tony, stark, dontfeelgood, dontfeelsogood, snap"
+"Localized Commands","tradebeans, trade",""
"Localized Commands","tweet",""
"Localized Commands","vote",""
"Localized Commands","wolfram",""
@@ -110,8 +113,10 @@
"Localized Strings","DBFlushInfo","Takes all objects queued for the database and writes them to the database, then says how many were written."
"Localized Strings","DBStatsInfo","Shows current statistics for the database"
"Localized Strings","DefaultDanceInfo","y'all know forknife, get ready to dance, or make a friend dance with @!"
+"Localized Strings","DerpiError",""
"Localized Strings","DoWorkFinished","I finished with my work! :D"
"Localized Strings","DoWorkInfo","Occupies a core for a few seconds"
+"Localized Strings","E6Blacklisted","I can't show that image ;3; (it may be blacklisted)"
"Localized Strings","EightBallError","Hmm? You'll need to ask a question!"
"Localized Strings","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."
"Localized Strings","EightBallMaybe1","Reply hazy, try again."
@@ -182,7 +187,11 @@
"Localized Strings","MapVersion","Using mapgen v0.1"
"Localized Strings","MapWidth",""
"Localized Strings","ModInfo","Mod is a super command for admin or mod use only!\n\t`mod ban @user` will ban a user from the server\n\t`mod kick @user` will kick the user from the server"
+"Localized Strings","MusicAddTrack",""
"Localized Strings","MusicInfo",""
+"Localized Strings","MusicVolChanged","Volume set to %s!"
+"Localized Strings","MusicVolCurrent","Volume is at %s!"
+"Localized Strings","MusicVolOOB",""
"Localized Strings","PerishInfo","Adds a red, 'a n g e r y' overlay to your icon or the icon of a friend you mentioned"
"Localized Strings","PingInfo","Will respond with Pong!"
"Localized Strings","PingResponse","Pong!"
@@ -271,6 +280,8 @@
"Command Enabler","dbstats","1"
"Command Enabler","defaultdance","1"
"Command Enabler","derp","1"
+"Command Enabler","derpi","1"
+"Command Enabler","e6","1"
"Command Enabler","e621","1"
"Command Enabler","eightball, 8ball","1"
"Command Enabler","fetch","1"
@@ -313,6 +324,7 @@
"Command Enabler","stats","1"
"Command Enabler","teey","1"
"Command Enabler","tony, stark, dontfeelgood, dontfeelsogood","1"
+"Command Enabler","tradebeans, trade","1"
"Command Enabler","tweet","1"
"Command Enabler","vote","1"
"Command Enabler","wolfram","1"
diff --git a/assets/fonts/B612Mono-Regular.ttf b/assets/fonts/B612Mono-Regular.ttf
new file mode 100644
index 0000000..ec09c98
Binary files /dev/null and b/assets/fonts/B612Mono-Regular.ttf differ
diff --git a/assets/plugins/derpisource.lua b/assets/plugins/derpisource.lua
new file mode 100644
index 0000000..455eaeb
--- /dev/null
+++ b/assets/plugins/derpisource.lua
@@ -0,0 +1,22 @@
+function plugin(message, user)
+ local index;
+ local concat;
+ if(string.find(message, "https://derpicdn.net/img"))then
+ if(string.find(message, "view/"))then
+ index = string.find(message, "/[^/]*$");
+ return "!derpi ID:" .. string.sub(message, index + 1, string.find(message, ".[^.]*$")-1);
+ else
+ if(string.find(message, "download/"))then
+ index = string.find(message, "_");
+ concat = string.sub(message, 0, index-1);
+ return "!derpi ID:" .. string.sub(concat, string.find(message, "/[^/]*$") + 1);
+ else
+ index = string.find(message, "/[^/]*$");
+ concat = string.sub(message, 0, index - 1);
+ index = string.find(concat, "/[^/]*$");
+
+ return "!derpi ID:" .. string.sub(concat, index + 1);
+ end
+ end
+ end
+end
\ No newline at end of file
diff --git a/assets/plugins/e6source.lua b/assets/plugins/e6source.lua
new file mode 100644
index 0000000..e215906
--- /dev/null
+++ b/assets/plugins/e6source.lua
@@ -0,0 +1,8 @@
+function plugin(message, user)
+ if(string.find(message, "https://static1.e621.net/data") or string.find(message, "https://static1.e926.net/data"))then
+ if(string.find(message, " "))then
+ message = string.sub(message, 0, string.find(message, " "));
+ end
+ return "!e6 md5:" .. string.sub(message, string.find(message, "/[^/]*$") + 1 ,string.find(message, ".[^.]*$") - 1);
+ end
+end
\ No newline at end of file
diff --git a/beanImage.png b/beanImage.png
new file mode 100644
index 0000000..4de82bd
Binary files /dev/null and b/beanImage.png differ
diff --git a/lib/nanohttpd-2.3.1.jar b/lib/nanohttpd-2.3.1.jar
new file mode 100644
index 0000000..ade9bbe
Binary files /dev/null and b/lib/nanohttpd-2.3.1.jar differ
diff --git a/src/commands/general/CommandBeansShow.java b/src/commands/general/CommandBeansShow.java
index f87b862..3b0d6fc 100644
--- a/src/commands/general/CommandBeansShow.java
+++ b/src/commands/general/CommandBeansShow.java
@@ -1,8 +1,23 @@
package commands.general;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+
+import javax.imageio.ImageIO;
+
import core.Command;
+import core.Constants;
import core.LocStrings;
-import dataStructures.*;
+import dataStructures.KittyChannel;
+import dataStructures.KittyGuild;
+import dataStructures.KittyRating;
+import dataStructures.KittyRole;
+import dataStructures.KittyUser;
+import dataStructures.Response;
+import dataStructures.UserInput;
public class CommandBeansShow extends Command
{
@@ -15,7 +30,15 @@ public class CommandBeansShow extends Command
public void onRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
{
if(input.mentions == null)
- res.send(String.format(LocStrings.stub("BeansShowDisplay"), user.getBeans()));
+ {
+ res.send("" + user.getBeans());
+ try {
+ res.sendFile(combineImages(user.getBeans()), "png");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+// res.send(String.format(LocStrings.stub("BeansShowDisplay"), user.getBeans()));
+ }
else
{
String mentionedBeans = "";
@@ -26,4 +49,75 @@ public class CommandBeansShow extends Command
res.send(String.format(LocStrings.stub("BeansShowMentioned"), mentionedBeans));
}
}
+
+ private File combineImages(long beans) throws IOException
+ {
+ // Acquire images
+ BufferedImage imageBase = ImageIO.read(new File(Constants.AssetDirectory + "beans/BeansBG.png"));
+ BufferedImage thouBean = ImageIO.read(new File(Constants.AssetDirectory + "beans/thouBean.png"));
+ BufferedImage hunBean = ImageIO.read(new File(Constants.AssetDirectory + "beans/hunBean.png"));
+ BufferedImage tenBean = ImageIO.read(new File(Constants.AssetDirectory + "beans/tenBean.png"));
+ BufferedImage oneBean = ImageIO.read(new File(Constants.AssetDirectory + "beans/oneBean.png"));
+
+ final int baseWidth = imageBase.getWidth();
+ final int baseHeight = imageBase.getHeight();
+
+ long calcBean = beans;
+
+ System.out.println("BEANS: " + beans);
+
+ long thousands = beans / 1000;
+ calcBean = beans % 1000;
+ long hundreds = (calcBean / 100);
+ calcBean = calcBean % 100;
+ long tens = (calcBean / 10);
+ calcBean = calcBean % 10;
+ long ones = calcBean;
+
+ BufferedImage beanImage = new BufferedImage(baseWidth, baseHeight, BufferedImage.TYPE_INT_ARGB);
+ Graphics beanGraphics = beanImage.getGraphics();
+
+ for(int i = 0; i < ones; i++)
+ {
+ int x = (int)(Math.random() * (baseWidth - oneBean.getWidth())) + 1;
+ int y = (int)(Math.random() * (baseHeight - oneBean.getHeight())) + 1;
+ beanGraphics.drawImage(oneBean, x, y, null);
+ }
+
+ for(int i = 0; i < tens; i++)
+ {
+ int x = (int)(Math.random() * (baseWidth - tenBean.getWidth())) + 1;
+ int y = (int)(Math.random() * (baseHeight - tenBean.getHeight())) + 1;
+ beanGraphics.drawImage(tenBean, x, y, null);
+ }
+
+ for(int i = 0; i < hundreds; i++)
+ {
+ int x = (int)(Math.random() * (baseWidth - hunBean.getWidth())) + 1;
+ int y = (int)(Math.random() * (baseHeight - hunBean.getHeight())) + 1;
+ beanGraphics.drawImage(hunBean, x, y, null);
+ }
+
+ for(int i = 0; i < thousands; i++)
+ {
+ int x = (int)(Math.random() * (baseWidth - thouBean.getWidth())) + 1;
+ int y = (int)(Math.random() * (baseHeight - thouBean.getHeight())) + 1;
+ beanGraphics.drawImage(thouBean, x, y, null);
+ }
+
+
+ try {
+ @SuppressWarnings("unused")
+ Font font = null;
+ font = Font.createFont(Font.TRUETYPE_FONT, new File(Constants.AssetDirectory +"fonts/B612Mono-Regular.ttf"));
+ } catch (Exception e)
+ {
+ }
+ Font beanFont = new Font("font", Font.BOLD, 45);
+ beanGraphics.setFont(beanFont);
+ beanGraphics.drawString("YOU GOT " + beans + " BEANS", 0, baseHeight / 2);
+
+ ImageIO.write(beanImage, "PNG", new File("beanImage.png"));
+ return new File("beanImage.png");
+ }
}
diff --git a/src/commands/general/CommandBetBeans.java b/src/commands/general/CommandBetBeans.java
index bcb14b0..29b65ad 100644
--- a/src/commands/general/CommandBetBeans.java
+++ b/src/commands/general/CommandBetBeans.java
@@ -20,7 +20,7 @@ public class CommandBetBeans extends Command
{
String call = "";
- int min = 50;
+ int min = 20;
int bet = 0;
int win = 0;
diff --git a/src/commands/general/CommandBoop.java b/src/commands/general/CommandBoop.java
index cb9104b..b54bc02 100644
--- a/src/commands/general/CommandBoop.java
+++ b/src/commands/general/CommandBoop.java
@@ -68,22 +68,23 @@ public class CommandBoop extends Command
{
if(input.mentions == null)
{
+ // No one is booped, just the bot!
boopTracker.applyBoop();
res.send(String.format(LocStrings.stub("BoopStandard"), user.name, boopTracker.howMany()));
}
else
{
+ // When someone boops one other person
if(input.mentions.length == 1)
{
- boopTracker.applyBoop();
res.send(String.format(LocStrings.stub("BoopPerson"), user.name, input.mentions[0].name));
return;
}
+ // A number of people have been booped
String booped = "";
for(int i = 0; i < input.mentions.length; i++)
{
- boopTracker.applyBoop();
if(i < input.mentions.length-1)
booped += input.mentions[i].name + ", ";
else
diff --git a/src/commands/general/CommandColor.java b/src/commands/general/CommandColor.java
index d159bc0..6d2293b 100644
--- a/src/commands/general/CommandColor.java
+++ b/src/commands/general/CommandColor.java
@@ -32,7 +32,7 @@ public class CommandColor extends Command
public void onRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
{
// First, try and parse out the color to make sure we can even get it.
- ColorData colorData = theColorAPI.lookupHex(input.args.trim());
+ ColorData colorData = theColorAPI.lookupHex(input.args);
// Verify the color was even found
if(colorData == null)
diff --git a/src/commands/general/CommandDerpi.java b/src/commands/general/CommandDerpi.java
new file mode 100644
index 0000000..c082ccf
--- /dev/null
+++ b/src/commands/general/CommandDerpi.java
@@ -0,0 +1,72 @@
+package commands.general;
+
+import core.Command;
+import core.LocStrings;
+import dataStructures.KittyChannel;
+import dataStructures.KittyEmbed;
+import dataStructures.KittyGuild;
+import dataStructures.KittyRating;
+import dataStructures.KittyRole;
+import dataStructures.KittyUser;
+import dataStructures.Response;
+import dataStructures.UserInput;
+import network.NetworkDerpi;
+
+public class CommandDerpi extends Command
+{
+ NetworkDerpi searcher = new NetworkDerpi();
+ KittyEmbed response;
+
+ // Required constructor
+ public CommandDerpi(KittyRole level, KittyRating rating) { super(level, rating); }
+
+ @Override
+ public String getHelpText() { return "Will search derpibooru for the tags entered, if KittyBot's content filter allows it"; }
+
+ @Override
+ public void onRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
+ {
+ if(guild.contentRating == KittyRating.Filtered)
+ {
+ try
+ {
+ response = searcher.getDerpi(input.args.replace(',', ' ') + " safe").output();
+ res.send(response);
+ }
+ catch(Exception e)
+ {
+ res.send(LocStrings.stub("DerpiError"));
+ }
+ }
+ else
+ {
+ if(input.args == null || input.args.length() == 0)
+ res.send("DerpiNoSearchError");
+ else
+ {
+ response = searcher.getDerpi(input.args).output();
+ try
+ {
+ res.send(response);
+ }
+ catch(Exception e)
+ {
+ res.send(LocStrings.stub("DerpiError"));
+ }
+ }
+ }
+
+ if(input.args.startsWith("ID:"))
+ {
+ response = searcher.getDerpiByID(input.args.substring(input.args.indexOf(':'))).output();
+ try
+ {
+ res.send(response);
+ }
+ catch(Exception e)
+ {
+ res.send(LocStrings.stub("DerpiError"));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/commands/general/CommandE6.java b/src/commands/general/CommandE6.java
new file mode 100644
index 0000000..cfd5551
--- /dev/null
+++ b/src/commands/general/CommandE6.java
@@ -0,0 +1,71 @@
+package commands.general;
+
+import core.Command;
+import core.LocStrings;
+import dataStructures.*;
+import network.NetworkE6;
+
+public class CommandE6 extends Command
+{
+ NetworkE6 searcher = new NetworkE6();
+ KittyEmbed response;
+ GenericImage image;
+
+ public CommandE6(KittyRole level, KittyRating rating) { super(level, rating); }
+
+ @Override
+ public String getHelpText()
+ {
+ String toRet = "Will search e6";
+ toRet += "21 for the tags entered, if KittyBot's content filter allows it";
+ return toRet;
+ }
+
+ @Override
+ public void onRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response res)
+ {
+ if(guild.contentRating == KittyRating.Filtered)
+ {
+ image = searcher.getE6(input.args + " rating:safe");
+ try
+ {
+ response = image.output();
+ }
+ catch(Exception e)
+ {
+ res.send(LocStrings.stub("E6Blacklisted"));
+ return;
+ }
+ try
+ {
+ if(response.bodyImageURL == null)
+ {
+ Exception e = new Exception();
+ throw e;
+ }
+ res.send(response);
+ }
+ catch(Exception e)
+ {
+ res.send(LocStrings.stub("E6Blacklisted"));
+ }
+ }
+ else
+ {
+ if(input.args == null || input.args.length() == 0)
+ res.send("E6NoSearchError");
+ else
+ {
+ try
+ {
+ response = searcher.getE6(input.args).output();
+ res.send(response);
+ }
+ catch(Exception e)
+ {
+ res.send(LocStrings.stub("E6Blacklisted"));
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/commands/general/CommandTradeBeans.java b/src/commands/general/CommandTradeBeans.java
index 5829c0e..c3b17e1 100644
--- a/src/commands/general/CommandTradeBeans.java
+++ b/src/commands/general/CommandTradeBeans.java
@@ -27,13 +27,22 @@ public class CommandTradeBeans extends Command
}
int beans = 0;
- try {
+
+ try
+ {
beans = Integer.parseInt(input.args.split(" ")[0]);
}
catch (NumberFormatException e)
{
- res.send(LocStrings.stub("TradeBeansIntParseError"));
- return;
+ try
+ {
+ beans = Integer.parseInt(input.args.split(" ")[1]);
+ }
+ catch(Exception eInner)
+ {
+ res.send(LocStrings.stub("TradeBeansIntParseError"));
+ return;
+ }
}
if(user.getBeans() < beans)
diff --git a/src/commands/music/CommandMusicMain.java b/src/commands/music/CommandMusicMain.java
index 7e71fc6..a3534a8 100644
--- a/src/commands/music/CommandMusicMain.java
+++ b/src/commands/music/CommandMusicMain.java
@@ -18,6 +18,7 @@ public class CommandMusicMain extends Command
framework.addCommand("skip", new SubCommandSkip(KittyRole.General, KittyRating.Safe));
framework.addCommand("stop", new SubCommandStop(KittyRole.General, KittyRating.Safe));
framework.addCommand("playlist", new SubCommandPlaylist(KittyRole.General, KittyRating.Safe));
+ framework.addCommand("volume", new SubCommandVolume(KittyRole.General, KittyRating.Safe));
}
@Override
diff --git a/src/commands/music/SubCommandAddTrack.java b/src/commands/music/SubCommandAddTrack.java
index 39febae..3dbe47e 100644
--- a/src/commands/music/SubCommandAddTrack.java
+++ b/src/commands/music/SubCommandAddTrack.java
@@ -1,5 +1,6 @@
package commands.music;
+import core.LocStrings;
import core.SubCommand;
import core.SubCommandFormattable;
import dataStructures.KittyChannel;
@@ -27,6 +28,6 @@ public class SubCommandAddTrack extends SubCommand
video = YT.getYT(video);
}
guild.audio.playVideo(video);
- return new SubCommandFormattable(video);
+ return new SubCommandFormattable(String.format(LocStrings.stub("MusicAddTrack"), video));
}
}
\ No newline at end of file
diff --git a/src/commands/music/SubCommandSkip.java b/src/commands/music/SubCommandSkip.java
index b57ee52..78d0bf6 100644
--- a/src/commands/music/SubCommandSkip.java
+++ b/src/commands/music/SubCommandSkip.java
@@ -1,23 +1,23 @@
-package commands.music;
-
-import core.SubCommand;
-import core.SubCommandFormattable;
-import dataStructures.KittyChannel;
-import dataStructures.KittyGuild;
-import dataStructures.KittyRating;
-import dataStructures.KittyRole;
-import dataStructures.KittyUser;
-import dataStructures.UserInput;
-
-public class SubCommandSkip extends SubCommand
-{
- public SubCommandSkip(KittyRole level, KittyRating rating) { super(level, rating); }
- {
-
- }
- @Override
- public SubCommandFormattable OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input)
- {
- return new SubCommandFormattable(guild.audio.skipVideo(guild.audio.player));
- }
-}
+package commands.music;
+
+import core.SubCommand;
+import core.SubCommandFormattable;
+import dataStructures.KittyChannel;
+import dataStructures.KittyGuild;
+import dataStructures.KittyRating;
+import dataStructures.KittyRole;
+import dataStructures.KittyUser;
+import dataStructures.UserInput;
+
+public class SubCommandSkip extends SubCommand
+{
+ public SubCommandSkip(KittyRole level, KittyRating rating) { super(level, rating); }
+ {
+
+ }
+ @Override
+ public SubCommandFormattable OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input)
+ {
+ return new SubCommandFormattable(guild.audio.skipVideo(guild.audio.player));
+ }
+}
diff --git a/src/commands/music/SubCommandVolume.java b/src/commands/music/SubCommandVolume.java
new file mode 100644
index 0000000..ab45d59
--- /dev/null
+++ b/src/commands/music/SubCommandVolume.java
@@ -0,0 +1,42 @@
+package commands.music;
+
+import core.LocStrings;
+import core.SubCommand;
+import core.SubCommandFormattable;
+import dataStructures.KittyChannel;
+import dataStructures.KittyGuild;
+import dataStructures.KittyRating;
+import dataStructures.KittyRole;
+import dataStructures.KittyUser;
+import dataStructures.UserInput;
+
+public class SubCommandVolume extends SubCommand
+{
+ public SubCommandVolume(KittyRole level, KittyRating rating) { super(level, rating); }
+ {
+
+ }
+ @Override
+ public SubCommandFormattable OnRun(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input)
+ {
+ int newVol = guild.audio.getVolume(guild.audio.player);
+ if(input.args != null)
+ {
+ try
+ {
+ newVol = Integer.parseInt(input.args);
+ if(newVol > 100 || newVol <1)
+ {
+ return new SubCommandFormattable(String.format(LocStrings.stub("MusicVolOOB")));
+ }
+ guild.audio.changeVolume(guild.audio.player, newVol);
+ }
+ catch(Exception e)
+ {
+ return new SubCommandFormattable(String.format(LocStrings.stub("MusicVolCurrent"), newVol));
+ }
+
+ }
+ return new SubCommandFormattable(String.format(LocStrings.stub("MusicVolChanged"), newVol));
+ }
+}
\ No newline at end of file
diff --git a/src/core/CommandManager.java b/src/core/CommandManager.java
index 6eace6f..fc4dd6b 100644
--- a/src/core/CommandManager.java
+++ b/src/core/CommandManager.java
@@ -108,6 +108,10 @@ public class CommandManager
this.register(LocCommands.stub("music"), new CommandMusicMain(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("defaultdance"), new CommandDefaultDance(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("getsauce"), new CommandGetSauce(KittyRole.General, KittyRating.Safe));
+ this.register(LocCommands.stub("tradebeans, trade"), new CommandTradeBeans(KittyRole.General, KittyRating.Safe));
+
+ this.register(LocCommands.stub("e6"), new CommandE6(KittyRole.General, KittyRating.Filtered));
+ this.register(LocCommands.stub("derpi"), new CommandDerpi(KittyRole.General, KittyRating.Filtered));
}
// Allows the command manager to keep track of a command. Takes a pair (the un-localized and localzied commands)
diff --git a/src/core/ObjectBuilderFactory.java b/src/core/ObjectBuilderFactory.java
index a17d2ed..f01912d 100644
--- a/src/core/ObjectBuilderFactory.java
+++ b/src/core/ObjectBuilderFactory.java
@@ -1,478 +1,479 @@
-package core;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.concurrent.Semaphore;
-
-import javax.security.auth.login.LoginException;
-
-import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
-import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
-import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
-
-//import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
-//import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
-
-import core.lua.PluginManager;
-import dataStructures.KittyChannel;
-import dataStructures.KittyCore;
-import dataStructures.KittyGuild;
-import dataStructures.KittyRating;
-import dataStructures.KittyRole;
-import dataStructures.KittyStartupMode;
-import dataStructures.KittyUser;
-import main.Main;
-import net.dv8tion.jda.core.AccountType;
-import net.dv8tion.jda.core.JDA;
-import net.dv8tion.jda.core.JDABuilder;
-import net.dv8tion.jda.core.entities.Emote;
-import net.dv8tion.jda.core.entities.Game;
-import net.dv8tion.jda.core.entities.Guild;
-import net.dv8tion.jda.core.entities.Member;
-import net.dv8tion.jda.core.entities.User;
-import net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent;
-import offline.Ref;
-import utils.AdminControl;
-//import utils.AudioUtils;
-import utils.GlobalLog;
-import utils.LogFilter;
-import utils.audio.AudioUtils;
-
-// 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
-public class ObjectBuilderFactory
-{
- // Key: guild string id, Value: guild information
- private static HashMap guildCache;
-
- // Key: channel string id, Value: channel information
- private static HashMap channelCache;
-
- // Key: guild string id + user string id, Value: user information
- private static HashMap userCache;
-
- // For tracking and managing object sync
- private static DatabaseManager database;
-
- // Stats tracking and whatnot... for setting stats too internally potentially
- private static Stats stats;
-
- // RPManger for tracking RP system
- private static RPManager rpManager;
-
- // Plugin manager
- private static PluginManager pluginManager;
-
- // Command manager
- private static CommandManager commandManager;
-
- //Audio track manager
- private static AudioPlayerManager playerManager = new DefaultAudioPlayerManager();
-
- // 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;
-
- // Config
- @SuppressWarnings("unused") private static Config config;
-
- // 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);
- private static JDA kitty;
-
- // This is it, this is how the lazy init starts!
- private static void lazyInit()
- {
- if(hasInitialized)
- return;
-
- try
- {
- initMutex.acquire();
- try
- {
- // structure initialization
- // Construct necessary data structures.
- guildCache = new HashMap();
- userCache = new HashMap();
- channelCache = new HashMap();
- database = null;
- stats = null;
-
- AudioSourceManagers.registerRemoteSources(playerManager);
- AudioSourceManagers.registerLocalSource(playerManager);
-
- // 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.
- config = new Config();
- }
- 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.");
- }
- }
-
- ////////////////////////
- // Extraction Methods //
- ////////////////////////
-
- // Explicitly locks: guildCache
- public static KittyGuild extractGuild(GuildMessageReceivedEvent event)
- {
- lazyInit();
-
- // Look up the guild. This process can only happen in a single-threaded way
- // because of the nature of the cache. We wait until the last second to
- // look up the guild.
- String uid = event.getGuild().getId();
-
- List emotes = event.getGuild().getEmotes();
- ArrayList emotesString = new ArrayList();
- String emote;
- String emoteFix;
- for(int i = 0; i < emotes.size(); i++)
- {
- emote = emotes.get(i).toString();
- emoteFix = "<:" + emote.substring(2, emote.indexOf("("));
- emoteFix += ":" + emote.substring(emote.indexOf("(")+1, emote.length()-1) + ">";
- emotesString.add(emoteFix);
- }
-
- // 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)
- {
- KittyGuild cachedGuild = guildCache.get(uid);
- if(cachedGuild != null)
- {
- guild = cachedGuild;
- }
- else
- {
- // Construct a new guild with defaults
- guild = new KittyGuild(uid, new AdminControl(event.getGuild()), emotesString, new AudioUtils(event.getGuild(), playerManager));
- DatabaseManager.instance.globalRegister(guild);
- guildCache.put(uid, guild);
- }
- }
-
- return guild;
- }
-
- // Explicitly locks: guildCache
- public static KittyRole extractRole(GuildMessageReceivedEvent event)
- {
- lazyInit();
-
- // Looks up the user role. If none is found we check to see if they own
- // the guild if not, they're assumed to be
- // allowed to use the bot at a general level.
- KittyRole role = KittyRole.General;
-
- if(event.getAuthor().getId() == event.getGuild().getOwner().getUser().getId())
- {
- role = KittyRole.Admin;
- }
-
- String uid = event.getGuild().getId() + event.getAuthor().getId();
- synchronized(userCache)
- {
- KittyUser cachedUser = userCache.get(uid);
- if(cachedUser != null)
- role = cachedUser.getRole();
- }
-
- return role;
- }
-
- // Explicitly locks: guildCache
- // Extracts the content rating information it can from the provided event.
- public static KittyRating extractContentRating(GuildMessageReceivedEvent event)
- {
- lazyInit();
-
- // Look up content rating of the guild, returns a safe content rating.
- KittyRating contentRating = KittyRating.Safe;
- String uid = event.getGuild().getId();
- synchronized(guildCache)
- {
- KittyGuild cachedGuild = guildCache.get(uid);
- if(cachedGuild != null)
- contentRating = cachedGuild.contentRating;
- }
-
- return contentRating;
- }
-
- // Implicitly locks guild cache by calling ExtractGuild
- public static KittyChannel extractChannel(GuildMessageReceivedEvent event)
- {
- lazyInit();
-
- String channelID = event.getChannel().getId();
- String guildID = event.getGuild().getId();
- KittyChannel channel = null;
-
- synchronized(channelCache)
- {
- KittyChannel cachedChannel = channelCache.get(channelID);
-
- if(cachedChannel != null)
- {
- channel = cachedChannel;
- }
- else
- {
- KittyGuild cachedGuild = guildCache.get(guildID);
- channel = new KittyChannel(channelID, cachedGuild);
- channelCache.put(channelID, channel);
- }
- }
-
- return channel;
- }
-
- // Implicitly locks guild cache by calling ExtractRole and ExtractGuild
- public static KittyUser extractUser(GuildMessageReceivedEvent event)
- {
- lazyInit();
-
- String uid = event.getGuild().getId() + event.getAuthor().getId();
- KittyUser user = null;
- synchronized(userCache)
- {
- KittyUser cachedUser = userCache.get(uid);
- if(cachedUser != null)
- {
- updateUser(cachedUser, event.getMember());
- user = cachedUser;
- }
- else
- {
- KittyRole role = extractRole(event);
- KittyGuild guild = extractGuild(event);
-
- String name;
- if(event.getMember().getNickname() == null)
- name = event.getAuthor().getName();
- else
- name = event.getMember().getNickname();
-
- String discordID = event.getMember().getUser().getId();
- String avatarID = event.getAuthor().getAvatarUrl();
- user = new KittyUser(name, guild, role, uid, avatarID, discordID);
- DatabaseManager.instance.globalRegister(user);
- userCache.put(uid, user);
- }
- }
-
- if(event.getMessage().getMentionedMembers().isEmpty())
- return user;
-
- Member mentioned;
- for(int i = 0; i < event.getMessage().getMentionedMembers().size(); i++)
- {
- mentioned = event.getMessage().getMentionedMembers().get(i);
- if(mentioned.getNickname() != null)
- extractUserByJDAUser(event.getGuild().getId(), mentioned.getNickname(),
- mentioned.getUser().getId(), mentioned.getUser().getAvatarUrl(), mentioned.getUser().getId());
- else
- extractUserByJDAUser(event.getGuild().getId(), mentioned.getUser().getName(),
- mentioned.getUser().getId(), mentioned.getUser().getAvatarUrl(), mentioned.getUser().getId());
- }
-
- return user;
- }
-
- // TODO: Clean up
- public static KittyUser extractUserByJDAUser(String guildID, String name, String userID, String avatarID, String discordID)
- {
- lazyInit();
-
- String uid = guildID + userID;
- KittyUser user = null;
- synchronized(userCache)
- {
- KittyUser cachedUser = userCache.get(uid);
- if(cachedUser != null)
- {
- if(name != null)
- cachedUser.name = name;
-
- cachedUser.avatarID = avatarID;
- user = cachedUser;
- }
- else
- {
- KittyRole role = KittyRole.General;
- KittyGuild guild = guildCache.get(guildID);
- user = new KittyUser(name, guild, role, uid, avatarID, discordID);
- DatabaseManager.instance.globalRegister(user);
- userCache.put(uid, user);
- }
- }
-
- return user;
- }
-
- // There is some redundant lookup occurring. If the user isn't cached yet, then we construct them and cache them.
- // The assumption is made that the user does, in fact, exist.
- public static KittyUser getKittyUser(String guildID, String userID)
- {
- String uid = guildID + userID;
- KittyUser user = null;
- synchronized(userCache)
- {
- user = userCache.get(uid);
- }
-
- if(user == null)
- {
- Guild jdaGuild = kitty.getGuildById(guildID);
- if(jdaGuild == null) return null;
-
- Member jdaMember = jdaGuild.getMemberById(userID);
- if(jdaMember == null) return null;
-
- User jdaUser = jdaMember.getUser();
- if(jdaUser == null) return null;
-
- user = extractUserByJDAUser(guildID, jdaMember.getNickname(), jdaUser.getId(), jdaUser.getAvatarUrl(), jdaUser.getId());
- updateUser(user, jdaMember);
- }
-
- return user;
- }
-
- // Updates a user's name appropriately based on nickname if available
- public static void updateUser(KittyUser user, Member member)
- {
- if(member.getNickname() == null)
- {
- user.name = member.getUser().getName();
- }
- else
- {
- user.name = member.getNickname();
- }
-
- user.avatarID = member.getUser().getAvatarUrl();
- }
-
-
- //////////////////////////
- // Construction Methods //
- //////////////////////////
- // Builds the core of the bot
- public static KittyCore constructKittyCore() throws LoginException, InterruptedException
- {
- lazyInit();
-
- // Determine token based on config. Default to test token.
- String tokenToUse = Ref.TestToken;
- if(ConfigGlobals.getStartupMode() == KittyStartupMode.Release)
- tokenToUse = Ref.Token;
-
- // Construct bot based on token
- kitty = new JDABuilder(AccountType.BOT)
- .setToken(tokenToUse).buildBlocking();
- kitty.getPresence().setGame(Game.playing("with a laser pointer"));
- kitty.addEventListener(new Main());
-
- return new KittyCore(kitty);
- }
-
- // Default construction of the command manager. In order to remotely resolve command enabling
- // and disabling, what we do is construct the commands with a localized pair that is checked against
- // the CommandEnabler object passed in. In theory, we could have multiple CommandManagers, tho we can
- // only have one CommandEnabler.
- public static CommandManager constructCommandManager(CommandEnabler commandEnabler)
- {
- lazyInit();
-
- if(commandManager == null)
- commandManager = new CommandManager(commandEnabler);
-
- return commandManager;
- }
-
- // 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.
- public static DatabaseManager constructDatabaseManager()
- {
- lazyInit();
-
- if(database == null)
- database = new DatabaseManager();
-
- return database;
- }
-
- public static Stats constructStats(CommandManager manager)
- {
- lazyInit();
-
- if(stats == null)
- stats = new Stats(manager);
-
- return stats;
- }
-
- public static RPManager constructRPManager()
- {
- lazyInit();
-
- if(rpManager == null)
- rpManager = new RPManager();
-
- return rpManager;
- }
-
- public static PluginManager constructPluginManager()
- {
- lazyInit();
-
- if(pluginManager == null)
- pluginManager = new PluginManager(Constants.AssetDirectory + "plugins/");
-
- return pluginManager;
- }
-
-
- /////////////////////
- // Utility Methods //
- /////////////////////
-
- // Returns number of cached guilds (does not equal total users in the database, only what's in memory)
- public static Integer getGuildCount()
- { synchronized(guildCache)
- {
- return guildCache.size();
- }
- }
-
- // Returns number of cached users (does not equal total users in the database, only what's in memory)
- public static Integer getUserCount()
- { synchronized(userCache)
- {
- return userCache.size();
- }
- }
+package core;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+
+import javax.security.auth.login.LoginException;
+
+import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
+import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
+import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
+
+//import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
+//import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
+
+import core.lua.PluginManager;
+import dataStructures.KittyChannel;
+import dataStructures.KittyCore;
+import dataStructures.KittyGuild;
+import dataStructures.KittyRating;
+import dataStructures.KittyRole;
+import dataStructures.KittyStartupMode;
+import dataStructures.KittyUser;
+import main.Main;
+import net.dv8tion.jda.core.AccountType;
+import net.dv8tion.jda.core.JDA;
+import net.dv8tion.jda.core.JDABuilder;
+import net.dv8tion.jda.core.entities.Emote;
+import net.dv8tion.jda.core.entities.Game;
+import net.dv8tion.jda.core.entities.Guild;
+import net.dv8tion.jda.core.entities.Member;
+import net.dv8tion.jda.core.entities.User;
+import net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent;
+import offline.Ref;
+import utils.AdminControl;
+//import utils.AudioUtils;
+import utils.GlobalLog;
+import utils.LogFilter;
+import utils.audio.AudioUtils;
+
+// 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
+public class ObjectBuilderFactory
+{
+ // Key: guild string id, Value: guild information
+ private static HashMap guildCache;
+
+ // Key: channel string id, Value: channel information
+ private static HashMap channelCache;
+
+ // Key: guild string id + user string id, Value: user information
+ private static HashMap userCache;
+
+ // For tracking and managing object sync
+ private static DatabaseManager database;
+
+ // Stats tracking and whatnot... for setting stats too internally potentially
+ private static Stats stats;
+
+ // RPManger for tracking RP system
+ private static RPManager rpManager;
+
+ // Plugin manager
+ private static PluginManager pluginManager;
+
+ // Command manager
+ private static CommandManager commandManager;
+
+ //Audio track manager
+ private static AudioPlayerManager playerManager = new DefaultAudioPlayerManager();
+
+ // 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;
+
+ // Config
+ @SuppressWarnings("unused") private static Config config;
+
+ // 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);
+ private static JDA kitty;
+
+ // This is it, this is how the lazy init starts!
+ private static void lazyInit()
+ {
+ if(hasInitialized)
+ return;
+
+ try
+ {
+ initMutex.acquire();
+ try
+ {
+ // structure initialization
+ // Construct necessary data structures.
+ guildCache = new HashMap();
+ userCache = new HashMap();
+ channelCache = new HashMap();
+ database = null;
+ stats = null;
+
+ AudioSourceManagers.registerRemoteSources(playerManager);
+ AudioSourceManagers.registerLocalSource(playerManager);
+
+ // 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.
+ config = new Config();
+ }
+ 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.");
+ }
+ }
+
+ ////////////////////////
+ // Extraction Methods //
+ ////////////////////////
+
+ // Explicitly locks: guildCache
+ public static KittyGuild extractGuild(GuildMessageReceivedEvent event)
+ {
+ lazyInit();
+
+ // Look up the guild. This process can only happen in a single-threaded way
+ // because of the nature of the cache. We wait until the last second to
+ // look up the guild.
+ String uid = event.getGuild().getId();
+
+ List emotes = event.getGuild().getEmotes();
+ ArrayList emotesString = new ArrayList();
+ HashMap channels = new HashMap();
+ String emote;
+ String emoteFix;
+ channels.put(Long.getLong(uid), extractChannel(event));
+ for(int i = 0; i < emotes.size(); i++)
+ {
+ emote = emotes.get(i).toString();
+ emoteFix = "<:" + emote.substring(2, emote.indexOf("("));
+ emoteFix += ":" + emote.substring(emote.indexOf("(")+1, emote.length()-1) + ">";
+ emotesString.add(emoteFix);
+ }
+
+ // 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)
+ {
+ KittyGuild cachedGuild = guildCache.get(uid);
+ if(cachedGuild != null)
+ {
+ guild = cachedGuild;
+ }
+ else
+ {
+ // Construct a new guild with defaults
+ guild = new KittyGuild(uid, new AdminControl(event.getGuild()), emotesString, new AudioUtils(event.getGuild(), playerManager), channels);
+ DatabaseManager.instance.globalRegister(guild);
+ guildCache.put(uid, guild);
+ }
+ }
+
+ return guild;
+ }
+
+ // Explicitly locks: guildCache
+ public static KittyRole extractRole(GuildMessageReceivedEvent event)
+ {
+ lazyInit();
+
+ // Looks up the user role. If none is found we check to see if they own
+ // the guild if not, they're assumed to be
+ // allowed to use the bot at a general level.
+ KittyRole role = KittyRole.General;
+
+ if(event.getAuthor().getId() == event.getGuild().getOwner().getUser().getId())
+ {
+ role = KittyRole.Admin;
+ }
+
+ String uid = event.getGuild().getId() + event.getAuthor().getId();
+ synchronized(userCache)
+ {
+ KittyUser cachedUser = userCache.get(uid);
+ if(cachedUser != null)
+ role = cachedUser.getRole();
+ }
+
+ return role;
+ }
+
+ // Explicitly locks: guildCache
+ // Extracts the content rating information it can from the provided event.
+ public static KittyRating extractContentRating(GuildMessageReceivedEvent event)
+ {
+ lazyInit();
+
+ // Look up content rating of the guild, returns a safe content rating.
+ KittyRating contentRating = KittyRating.Safe;
+ String uid = event.getGuild().getId();
+ synchronized(guildCache)
+ {
+ KittyGuild cachedGuild = guildCache.get(uid);
+ if(cachedGuild != null)
+ contentRating = cachedGuild.contentRating;
+ }
+
+ return contentRating;
+ }
+
+ // Implicitly locks guild cache by calling ExtractGuild
+ public static KittyChannel extractChannel(GuildMessageReceivedEvent event)
+ {
+ lazyInit();
+ String channelID = event.getChannel().getId();
+ KittyChannel channel = null;
+
+ synchronized(channelCache)
+ {
+ KittyChannel cachedChannel = channelCache.get(channelID);
+ boolean mature;
+
+ if(cachedChannel != null)
+ {
+ channel = cachedChannel;
+ }
+ else
+ {
+ mature = event.getChannel().isNSFW();
+ channel = new KittyChannel(channelID, mature);
+ channelCache.put(channelID, channel);
+ }
+ }
+
+ return channel;
+ }
+
+ // Implicitly locks guild cache by calling ExtractRole and ExtractGuild
+ public static KittyUser extractUser(GuildMessageReceivedEvent event)
+ {
+ lazyInit();
+
+ String uid = event.getGuild().getId() + event.getAuthor().getId();
+ KittyUser user = null;
+ synchronized(userCache)
+ {
+ KittyUser cachedUser = userCache.get(uid);
+ if(cachedUser != null)
+ {
+ updateUser(cachedUser, event.getMember());
+ user = cachedUser;
+ }
+ else
+ {
+ KittyRole role = extractRole(event);
+ KittyGuild guild = extractGuild(event);
+
+ String name;
+ if(event.getMember().getNickname() == null)
+ name = event.getAuthor().getName();
+ else
+ name = event.getMember().getNickname();
+
+ String discordID = event.getMember().getUser().getId();
+ String avatarID = event.getAuthor().getAvatarUrl();
+ user = new KittyUser(name, guild, role, uid, avatarID, discordID);
+ DatabaseManager.instance.globalRegister(user);
+ userCache.put(uid, user);
+ }
+ }
+
+ if(event.getMessage().getMentionedMembers().isEmpty())
+ return user;
+
+ Member mentioned;
+ for(int i = 0; i < event.getMessage().getMentionedMembers().size(); i++)
+ {
+ mentioned = event.getMessage().getMentionedMembers().get(i);
+ if(mentioned.getNickname() != null)
+ extractUserByJDAUser(event.getGuild().getId(), mentioned.getNickname(),
+ mentioned.getUser().getId(), mentioned.getUser().getAvatarUrl(), mentioned.getUser().getId());
+ else
+ extractUserByJDAUser(event.getGuild().getId(), mentioned.getUser().getName(),
+ mentioned.getUser().getId(), mentioned.getUser().getAvatarUrl(), mentioned.getUser().getId());
+ }
+
+ return user;
+ }
+
+ // TODO: Clean up
+ public static KittyUser extractUserByJDAUser(String guildID, String name, String userID, String avatarID, String discordID)
+ {
+ lazyInit();
+
+ String uid = guildID + userID;
+ KittyUser user = null;
+ synchronized(userCache)
+ {
+ KittyUser cachedUser = userCache.get(uid);
+ if(cachedUser != null)
+ {
+ if(name != null)
+ cachedUser.name = name;
+
+ cachedUser.avatarID = avatarID;
+ user = cachedUser;
+ }
+ else
+ {
+ KittyRole role = KittyRole.General;
+ KittyGuild guild = guildCache.get(guildID);
+ user = new KittyUser(name, guild, role, uid, avatarID, discordID);
+ DatabaseManager.instance.globalRegister(user);
+ userCache.put(uid, user);
+ }
+ }
+
+ return user;
+ }
+
+ // There is some redundant lookup occurring. If the user isn't cached yet, then we construct them and cache them.
+ // The assumption is made that the user does, in fact, exist.
+ public static KittyUser getKittyUser(String guildID, String userID)
+ {
+ String uid = guildID + userID;
+ KittyUser user = null;
+ synchronized(userCache)
+ {
+ user = userCache.get(uid);
+ }
+
+ if(user == null)
+ {
+ Guild jdaGuild = kitty.getGuildById(guildID);
+ if(jdaGuild == null) return null;
+
+ Member jdaMember = jdaGuild.getMemberById(userID);
+ if(jdaMember == null) return null;
+
+ User jdaUser = jdaMember.getUser();
+ if(jdaUser == null) return null;
+
+ user = extractUserByJDAUser(guildID, jdaMember.getNickname(), jdaUser.getId(), jdaUser.getAvatarUrl(), jdaUser.getId());
+ updateUser(user, jdaMember);
+ }
+
+ return user;
+ }
+
+ // Updates a user's name appropriately based on nickname if available
+ public static void updateUser(KittyUser user, Member member)
+ {
+ if(member.getNickname() == null)
+ {
+ user.name = member.getUser().getName();
+ }
+ else
+ {
+ user.name = member.getNickname();
+ }
+
+ user.avatarID = member.getUser().getAvatarUrl();
+ }
+
+
+ //////////////////////////
+ // Construction Methods //
+ //////////////////////////
+ // Builds the core of the bot
+ public static KittyCore constructKittyCore() throws LoginException, InterruptedException
+ {
+ lazyInit();
+
+ // Determine token based on config. Default to test token.
+ String tokenToUse = Ref.TestToken;
+ if(ConfigGlobals.getStartupMode() == KittyStartupMode.Release)
+ tokenToUse = Ref.PoguToken;
+
+ // Construct bot based on token
+ kitty = new JDABuilder(AccountType.BOT)
+ .setToken(tokenToUse).buildBlocking();
+ kitty.getPresence().setGame(Game.playing("with a laser pointer"));
+ kitty.addEventListener(new Main());
+
+ return new KittyCore(kitty);
+ }
+
+ // Default construction of the command manager. In order to remotely resolve command enabling
+ // and disabling, what we do is construct the commands with a localized pair that is checked against
+ // the CommandEnabler object passed in. In theory, we could have multiple CommandManagers, tho we can
+ // only have one CommandEnabler.
+ public static CommandManager constructCommandManager(CommandEnabler commandEnabler)
+ {
+ lazyInit();
+
+ if(commandManager == null)
+ commandManager = new CommandManager(commandEnabler);
+
+ return commandManager;
+ }
+
+ // 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.
+ public static DatabaseManager constructDatabaseManager()
+ {
+ lazyInit();
+
+ if(database == null)
+ database = new DatabaseManager();
+
+ return database;
+ }
+
+ public static Stats constructStats(CommandManager manager)
+ {
+ lazyInit();
+
+ if(stats == null)
+ stats = new Stats(manager);
+
+ return stats;
+ }
+
+ public static RPManager constructRPManager()
+ {
+ lazyInit();
+
+ if(rpManager == null)
+ rpManager = new RPManager();
+
+ return rpManager;
+ }
+
+ public static PluginManager constructPluginManager()
+ {
+ lazyInit();
+
+ if(pluginManager == null)
+ pluginManager = new PluginManager(Constants.AssetDirectory + "plugins/");
+
+ return pluginManager;
+ }
+
+
+ /////////////////////
+ // Utility Methods //
+ /////////////////////
+
+ // Returns number of cached guilds (does not equal total users in the database, only what's in memory)
+ public static Integer getGuildCount()
+ { synchronized(guildCache)
+ {
+ return guildCache.size();
+ }
+ }
+
+ // Returns number of cached users (does not equal total users in the database, only what's in memory)
+ public static Integer getUserCount()
+ { synchronized(userCache)
+ {
+ return userCache.size();
+ }
+ }
}
\ No newline at end of file
diff --git a/src/dataStructures/GenericImage.java b/src/dataStructures/GenericImage.java
index ffe7212..092b250 100644
--- a/src/dataStructures/GenericImage.java
+++ b/src/dataStructures/GenericImage.java
@@ -1,40 +1,57 @@
package dataStructures;
-// This is a structure representing an image reponse for handing around.
+import java.awt.Color;
+
+// This is a structure representing an image response for handing around.
public class GenericImage
{
- private String artist;
- private String postURL;
- private String imageURL;
+ private KittyEmbed response= new KittyEmbed();
public GenericImage(String artist, String postURL, String imageURL)
{
- this.artist = artist;
- this.postURL = postURL;
- this.imageURL = imageURL;
+ response.authorText = null;
+ response.color = new Color(0,0,0);
}
public void editArtist(String artist)
{
- this.artist = artist;
+ response.authorText = artist;
+ }
+
+ public void editAuthorImage(String URL)
+ {
+ response.authorImage = URL;
}
public void editPostURL(String postURL)
{
- this.postURL = postURL;
+ response.authorLink = postURL;
}
public void editImageURL(String imageURL)
{
- this.imageURL = imageURL;
+ response.imageURL = imageURL;
}
- public String toString()
+ public void editDescriptionText(String score)
{
- if(imageURL.isEmpty())
- {
- return "I couldn't find anything! Please try again!";
- }
- return "Artist: " + artist + "\n<" + postURL.trim() + ">\n" + imageURL;
+ response.descriptionText = score;
+ }
+
+ public void editFooterText(String tags)
+ {
+ response.footerText = tags;
+ }
+
+ public KittyEmbed output()
+ {
+ return response;
+ }
+
+ public boolean isValid()
+ {
+ if(response.title != null)
+ return true;
+ return false;
}
}
diff --git a/src/dataStructures/KittyChannel.java b/src/dataStructures/KittyChannel.java
index 92c93de..71fc794 100644
--- a/src/dataStructures/KittyChannel.java
+++ b/src/dataStructures/KittyChannel.java
@@ -1,31 +1,15 @@
-package dataStructures;
-
-//import core.DatabaseTrackedObject;
-
-public class KittyChannel //extends DatabaseTrackedObject
-{
- public String uniqueID;
- public KittyGuild guild;
-
- //public KittyRating rating;
- //public bool isVoice;
-
- public KittyChannel(String uniqueID, KittyGuild guild)
- {
- //super(uniqueID);
- this.uniqueID = uniqueID;
- this.guild = guild;
- }
-
-// @Override
-// public String serialize()
-// {
-// return "";
-// }
-//
-// @Override
-// public void deSerialzie(String string)
-// {
-//
-// }
-}
+package dataStructures;
+
+
+public class KittyChannel
+{
+ public String uniqueID;
+ public boolean mature;
+
+ public KittyChannel(String uniqueID, boolean mature)
+ {
+ //super(uniqueID);
+ this.uniqueID = uniqueID;
+ this.mature = false;
+ }
+}
\ No newline at end of file
diff --git a/src/dataStructures/KittyGuild.java b/src/dataStructures/KittyGuild.java
index ceb056d..6e18606 100644
--- a/src/dataStructures/KittyGuild.java
+++ b/src/dataStructures/KittyGuild.java
@@ -1,176 +1,180 @@
-package dataStructures;
-
-import java.util.ArrayList;
-
-import core.DatabaseManager;
-import core.DatabaseTrackedObject;
-import utils.AdminControl;
-import utils.audio.AudioUtils;
-
-// Context for a given guild for kittybot. Primarily designed to hold guild-specific settings.
-public class KittyGuild extends DatabaseTrackedObject
-{
- // Variables
- public final String uniqueID;
- public KittyRating contentRating;
- public KittyUser guildOwner;
- public boolean polling;
- public String poll;
- public boolean raffling;
- public int raffleCost;
- public ArrayList hasVoted = new ArrayList();
- public ArrayList emoji = new ArrayList();
- public ArrayList choices = new ArrayList();
- public AdminControl control;
- public AudioUtils audio;
- public ArrayList raffleUsersUnchosen = new ArrayList();
- public ArrayList raffleUsersChosen = new ArrayList();
-
- // Database synced info
- private final String roleListName = "guildRoles";
- public final KittyTrackedVector roleList;
-
- private final String beansName = "guildBeans";
- public final KittyTrackedLong beans;
-
- private String commandIndicator;
-
- private void registerTrackedObjects()
- {
- DatabaseManager.instance.globalRegister(roleList);
- DatabaseManager.instance.globalRegister(beans);
- }
-
- // Default content for a guild
- public KittyGuild(String uniqueID, AdminControl adminControl, ArrayList emoji, AudioUtils audio)
- {
-
- super(uniqueID);
- this.uniqueID = uniqueID;
- roleList = new KittyTrackedVector(roleListName, uniqueID);
- beans = new KittyTrackedLong(beansName, uniqueID);
- registerTrackedObjects();
-
- control = adminControl;
- this.contentRating = KittyRating.Safe;
- this.polling = false;
- this.emoji = emoji;
- this.audio = audio;
- setCommandIndicator("!");
- }
-
- // Explicit constructor
- public KittyGuild(String commandIndicator, KittyRating contentRating, KittyUser guildOwner, String uniqueID, AudioUtils audio)
- {
- super(uniqueID);
- this.uniqueID = uniqueID;
- roleList = new KittyTrackedVector(roleListName, uniqueID);
- beans = new KittyTrackedLong(beansName, uniqueID);
- registerTrackedObjects();
-
- setCommandIndicator(commandIndicator);
- this.contentRating = contentRating;
- this.polling = false;
- this.guildOwner = guildOwner;
- this.audio = audio;
- }
-
- public String startPoll(String poll)
- {
- polling = true;
- this.poll = poll;
- return "Poll `" + poll + "` started! Don't forget to add choices with `!poll choice`~";
- }
-
- public String addChoiceToPoll(String choice)
- {
- choices.add(new KittyPoll(choice));
- return "Added `" + choice + "` to poll!";
- }
-
- public String endPoll()
- {
- choices.clear();
- hasVoted.clear();
- this.polling = false;
- poll = null;
- return "Poll ended!";
- }
-
- //Raffle methods
- public boolean startRaffle(int beanCost)
- {
- if(raffling)
- {
- return false;
- }
- raffleCost = beanCost;
- raffling = true;
- return true;
- }
-
- public boolean endRaffle()
- {
- if(raffling)
- {
- raffleCost = 0;
- raffling = false;
- raffleUsersChosen.removeAll(raffleUsersChosen);
- raffleUsersUnchosen.removeAll(raffleUsersUnchosen);
- return true;
- }
- return false;
- }
-
- public boolean joinRaffle(KittyUser user)
- {
- if(!raffleUsersChosen.contains(user) && !raffleUsersUnchosen.contains(user) && user.getBeans() > raffleCost && raffling)
- {
- user.changeBeans(raffleCost);
- raffleUsersUnchosen.add(user);
- return true;
- }
- return false;
- }
-
- public KittyUser chooseRaffleWinner()
- {
- if(!raffleUsersUnchosen.isEmpty())
- {
- KittyUser chosen = raffleUsersUnchosen.get((int) (Math.random() * raffleUsersUnchosen.size()));
- raffleUsersUnchosen.remove(chosen);
- raffleUsersChosen.add(chosen);
- return chosen;
- }
- return null;
- }
-
- @Override
- public String serialize()
- {
- return commandIndicator;
- }
-
- @Override
- public void deSerialzie(String string)
- {
- if(string == null || string.length() == 0)
- setCommandIndicator("!");
- else
- setCommandIndicator(string);
- }
-
- public String getCommandIndicator()
- {
- return commandIndicator;
- }
-
- public void setCommandIndicator(String newIndicator)
- {
- if(newIndicator != commandIndicator)
- {
- commandIndicator = newIndicator;
- markDirty();
- }
- }
-}
+package dataStructures;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import core.DatabaseManager;
+import core.DatabaseTrackedObject;
+import utils.AdminControl;
+import utils.audio.AudioUtils;
+
+// Context for a given guild for kittybot. Primarily designed to hold guild-specific settings.
+public class KittyGuild extends DatabaseTrackedObject
+{
+ // Variables
+ public final String uniqueID;
+ public KittyRating contentRating;
+ public KittyUser guildOwner;
+ public boolean polling;
+ public String poll;
+ public boolean raffling;
+ public int raffleCost;
+ public ArrayList hasVoted = new ArrayList();
+ public ArrayList emoji = new ArrayList();
+ public ArrayList choices = new ArrayList();
+ public HashMap channels;
+ public AdminControl control;
+ public AudioUtils audio;
+ public ArrayList raffleUsersUnchosen = new ArrayList();
+ public ArrayList raffleUsersChosen = new ArrayList();
+
+ // Database synced info
+ private final String roleListName = "guildRoles";
+ public final KittyTrackedVector roleList;
+
+ private final String beansName = "guildBeans";
+ public final KittyTrackedLong beans;
+
+ private String commandIndicator;
+
+ private void registerTrackedObjects()
+ {
+ DatabaseManager.instance.globalRegister(roleList);
+ DatabaseManager.instance.globalRegister(beans);
+ }
+
+ // Default content for a guild
+ public KittyGuild(String uniqueID, AdminControl adminControl, ArrayList emoji, AudioUtils audio, HashMap channels)
+ {
+
+ super(uniqueID);
+ this.uniqueID = uniqueID;
+ roleList = new KittyTrackedVector(roleListName, uniqueID);
+ beans = new KittyTrackedLong(beansName, uniqueID);
+ registerTrackedObjects();
+
+ control = adminControl;
+ this.contentRating = KittyRating.Safe;
+ this.polling = false;
+ this.emoji = emoji;
+ this.audio = audio;
+ this.channels = channels;
+ setCommandIndicator("!");
+ }
+
+ // Explicit constructor
+ public KittyGuild(String commandIndicator, KittyRating contentRating, KittyUser guildOwner, String uniqueID, AudioUtils audio, HashMap channels)
+ {
+ super(uniqueID);
+ this.uniqueID = uniqueID;
+ roleList = new KittyTrackedVector(roleListName, uniqueID);
+ beans = new KittyTrackedLong(beansName, uniqueID);
+ registerTrackedObjects();
+
+ setCommandIndicator(commandIndicator);
+ this.contentRating = contentRating;
+ this.polling = false;
+ this.guildOwner = guildOwner;
+ this.audio = audio;
+ this.channels = channels;
+ }
+
+ public String startPoll(String poll)
+ {
+ polling = true;
+ this.poll = poll;
+ return "Poll `" + poll + "` started! Don't forget to add choices with `!poll choice`~";
+ }
+
+ public String addChoiceToPoll(String choice)
+ {
+ choices.add(new KittyPoll(choice));
+ return "Added `" + choice + "` to poll!";
+ }
+
+ public String endPoll()
+ {
+ choices.clear();
+ hasVoted.clear();
+ this.polling = false;
+ poll = null;
+ return "Poll ended!";
+ }
+
+ //Raffle methods
+ public boolean startRaffle(int beanCost)
+ {
+ if(raffling)
+ {
+ return false;
+ }
+ raffleCost = beanCost;
+ raffling = true;
+ return true;
+ }
+
+ public boolean endRaffle()
+ {
+ if(raffling)
+ {
+ raffleCost = 0;
+ raffling = false;
+ raffleUsersChosen.removeAll(raffleUsersChosen);
+ raffleUsersUnchosen.removeAll(raffleUsersUnchosen);
+ return true;
+ }
+ return false;
+ }
+
+ public boolean joinRaffle(KittyUser user)
+ {
+ if(!raffleUsersChosen.contains(user) && !raffleUsersUnchosen.contains(user) && user.getBeans() > raffleCost && raffling)
+ {
+ user.changeBeans(-raffleCost);
+ raffleUsersUnchosen.add(user);
+ return true;
+ }
+ return false;
+ }
+
+ public KittyUser chooseRaffleWinner()
+ {
+ if(!raffleUsersUnchosen.isEmpty())
+ {
+ KittyUser chosen = raffleUsersUnchosen.get((int) (Math.random() * raffleUsersUnchosen.size()));
+ raffleUsersUnchosen.remove(chosen);
+ raffleUsersChosen.add(chosen);
+ return chosen;
+ }
+ return null;
+ }
+
+ @Override
+ public String serialize()
+ {
+ return commandIndicator;
+ }
+
+ @Override
+ public void deSerialzie(String string)
+ {
+ if(string == null || string.length() == 0)
+ setCommandIndicator("!");
+ else
+ setCommandIndicator(string);
+ }
+
+ public String getCommandIndicator()
+ {
+ return commandIndicator;
+ }
+
+ public void setCommandIndicator(String newIndicator)
+ {
+ if(newIndicator != commandIndicator)
+ {
+ commandIndicator = newIndicator;
+ markDirty();
+ }
+ }
+}
diff --git a/src/main/Main.java b/src/main/Main.java
index 23e3b7d..3c937b8 100644
--- a/src/main/Main.java
+++ b/src/main/Main.java
@@ -1,135 +1,135 @@
-package main;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import javax.security.auth.login.LoginException;
-
-import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
-import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
-import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
-import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
-import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
-import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
-import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
-import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
-import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame;
-
-import core.CharacterManager;
-import core.CommandEnabler;
-import core.CommandManager;
-import core.DatabaseManager;
-import core.ObjectBuilderFactory;
-import core.RPManager;
-import core.Stats;
-import core.lua.PluginManager;
-import dataStructures.KittyChannel;
-import dataStructures.KittyCore;
-import dataStructures.KittyGuild;
-import dataStructures.KittyUser;
-import dataStructures.Response;
-import dataStructures.UserInput;
-import net.dv8tion.jda.core.AccountType;
-import net.dv8tion.jda.core.JDA;
-import net.dv8tion.jda.core.JDABuilder;
-import net.dv8tion.jda.core.audio.AudioSendHandler;
-import net.dv8tion.jda.core.entities.Guild;
-import net.dv8tion.jda.core.entities.TextChannel;
-import net.dv8tion.jda.core.entities.VoiceChannel;
-import net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent;
-import net.dv8tion.jda.core.hooks.ListenerAdapter;
-import net.dv8tion.jda.core.managers.AudioManager;
-import offline.Ref;
-import utils.GlobalLog;
-
-// 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 bot specific objects
- private static KittyCore kittyCore;
- private static DatabaseManager databaseManager;
- private static CommandManager commandManager;
- private static Stats stats;
- private static RPManager rpManager;
- private static PluginManager pluginManager;
- private static CharacterManager charManager;
-
-
- // Initialization and setup
- public static void main(String[] args) throws InterruptedException, LoginException, Exception
- {
- // Factory startup. The ordering is intentional.
- GlobalLog.initialize();
- databaseManager = ObjectBuilderFactory.constructDatabaseManager();
- commandManager = ObjectBuilderFactory.constructCommandManager(CommandEnabler.instance); // TODO: Untangle this singleton
- stats = ObjectBuilderFactory.constructStats(commandManager);
- charManager = new CharacterManager();
- rpManager = ObjectBuilderFactory.constructRPManager();
- pluginManager = ObjectBuilderFactory.constructPluginManager();
-
- // Bot startup
- kittyCore = ObjectBuilderFactory.constructKittyCore();
- }
-
- // When a message is sent in a server that kitty is in, this is what's called.
- @Override
- public void onGuildMessageReceived(GuildMessageReceivedEvent event)
- {
- // Tweak the event as necessary
- if(!Superintendent.preProcessSetup(event, stats))
- return;
-
- // Factory objects
- KittyUser user = ObjectBuilderFactory.extractUser(event);
- KittyGuild guild = ObjectBuilderFactory.extractGuild(event);
- KittyChannel channel = ObjectBuilderFactory.extractChannel(event);
-
- // Specialized uncached objects
- Response response = new Response(event, kittyCore);
- UserInput input = new UserInput(event, guild);
-
- // Tweak object construction as necessary
- if(!Superintendent.postProcessSetup(event, user, guild, channel, response, input))
- return;
-
- // Track beans!
- user.changeBeans(1);
-
- // RP logging system
- RPManager.instance.addLine(channel, user, input);
-
- // Run any pre-emptive upkeep we need to
- if(!Superintendent.perCommandUpkeepPre(pluginManager))
- return;
-
- // Attempt to spin up a command. If the command doesn't exist.
- // Run plugins right before invoking the commands but after all other setup.
- if(commandManager.invokeOnNewThread(guild, channel, user, input, response) == false)
- {
- List pluginOutput = pluginManager.runAll(input.message, user);
-
- if(pluginOutput != null && pluginOutput.size() > 0)
- {
- for(int i = 0; i < pluginOutput.size(); ++i)
- {
- if(pluginOutput.get(i).startsWith("!"))
- {
- commandManager.invokeOnNewThread(guild, channel, user, new UserInput(pluginOutput.get(i).substring(1), guild), response);
- }
- else
- {
- response.send(pluginOutput.get(i));
- }
- }
- }
- }
-
- // Run any upkeep in post we need to
- if(!Superintendent.perCommandUpkeepPost(kittyCore, databaseManager))
- return;
- }
+package main;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.security.auth.login.LoginException;
+
+import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
+import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
+import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
+import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
+import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
+import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
+import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
+import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
+import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame;
+
+import core.CharacterManager;
+import core.CommandEnabler;
+import core.CommandManager;
+import core.DatabaseManager;
+import core.ObjectBuilderFactory;
+import core.RPManager;
+import core.Stats;
+import core.lua.PluginManager;
+import dataStructures.KittyChannel;
+import dataStructures.KittyCore;
+import dataStructures.KittyGuild;
+import dataStructures.KittyUser;
+import dataStructures.Response;
+import dataStructures.UserInput;
+import net.dv8tion.jda.core.AccountType;
+import net.dv8tion.jda.core.JDA;
+import net.dv8tion.jda.core.JDABuilder;
+import net.dv8tion.jda.core.audio.AudioSendHandler;
+import net.dv8tion.jda.core.entities.Guild;
+import net.dv8tion.jda.core.entities.TextChannel;
+import net.dv8tion.jda.core.entities.VoiceChannel;
+import net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent;
+import net.dv8tion.jda.core.hooks.ListenerAdapter;
+import net.dv8tion.jda.core.managers.AudioManager;
+import offline.Ref;
+import utils.GlobalLog;
+
+// 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 bot specific objects
+ private static KittyCore kittyCore;
+ private static DatabaseManager databaseManager;
+ private static CommandManager commandManager;
+ private static Stats stats;
+ private static RPManager rpManager;
+ private static PluginManager pluginManager;
+ private static CharacterManager charManager;
+
+
+ // Initialization and setup
+ public static void main(String[] args) throws InterruptedException, LoginException, Exception
+ {
+ // Factory startup. The ordering is intentional.
+ GlobalLog.initialize();
+ databaseManager = ObjectBuilderFactory.constructDatabaseManager();
+ commandManager = ObjectBuilderFactory.constructCommandManager(CommandEnabler.instance); // TODO: Untangle this singleton
+ stats = ObjectBuilderFactory.constructStats(commandManager);
+ charManager = new CharacterManager();
+ rpManager = ObjectBuilderFactory.constructRPManager();
+ pluginManager = ObjectBuilderFactory.constructPluginManager();
+
+ // Bot startup
+ kittyCore = ObjectBuilderFactory.constructKittyCore();
+ }
+
+ // When a message is sent in a server that kitty is in, this is what's called.
+ @Override
+ public void onGuildMessageReceived(GuildMessageReceivedEvent event)
+ {
+ // Tweak the event as necessary
+ if(!Superintendent.preProcessSetup(event, stats))
+ return;
+
+ // Factory objects
+ KittyUser user = ObjectBuilderFactory.extractUser(event);
+ KittyGuild guild = ObjectBuilderFactory.extractGuild(event);
+ KittyChannel channel = ObjectBuilderFactory.extractChannel(event);
+
+ // Specialized uncached objects
+ Response response = new Response(event, kittyCore);
+ UserInput input = new UserInput(event, guild);
+
+ // Tweak object construction as necessary
+ if(!Superintendent.postProcessSetup(event, user, guild, channel, response, input))
+ return;
+
+ // Track beans!
+ user.changeBeans(1);
+
+ // RP logging system
+ RPManager.instance.addLine(channel, user, input);
+
+ // Run any pre-emptive upkeep we need to
+ if(!Superintendent.perCommandUpkeepPre(pluginManager))
+ return;
+
+ // Attempt to spin up a command. If the command doesn't exist.
+ // Run plugins right before invoking the commands but after all other setup.
+ if(commandManager.invokeOnNewThread(guild, channel, user, input, response) == false)
+ {
+ List pluginOutput = pluginManager.runAll(input.message, user);
+
+ if(pluginOutput != null && pluginOutput.size() > 0)
+ {
+ for(int i = 0; i < pluginOutput.size(); ++i)
+ {
+ if(pluginOutput.get(i).startsWith("!"))
+ {
+ commandManager.invokeOnNewThread(guild, channel, user, new UserInput(pluginOutput.get(i).substring(1), guild), response);
+ }
+ else
+ {
+ response.send(pluginOutput.get(i));
+ }
+ }
+ }
+ }
+
+ // Run any upkeep in post we need to
+ if(!Superintendent.perCommandUpkeepPost(kittyCore, databaseManager))
+ return;
+ }
}
\ No newline at end of file
diff --git a/src/network/NetworkColiru.java b/src/network/NetworkColiru.java
index 6cf23fd..ceb475c 100644
--- a/src/network/NetworkColiru.java
+++ b/src/network/NetworkColiru.java
@@ -6,14 +6,41 @@ public class NetworkColiru
{
public String compileCPlusPlus(String query)
{
- // Escape characters that need escaping.
- // Primarily types of whitespace.
+ // Escape characters that need escaping - mostly whitespace.
query = query.replace("\\n", "\\\\n");
query = query.replace("\\t", "\\\\t");
query = query.replace("\n", "\\n");
query = query.replace("\"", "\\\"");
query = query.replace("\t", "\\t");
+ query = query.replace("`", "");
+
+// // replace backticks on only the edges of the string.
+// StringBuilder s = new StringBuilder(query);
+//
+// for(int i = 0; i < s.length(); ++i)
+// {
+// if(s.charAt(i) != '`')
+// break;
+//
+// s.setCharAt(i, ' ');
+// }
+//
+// for(int i = s.length() - 1; i >= 0; --i)
+// {
+// if(s.charAt(i) != '`')
+// break;
+//
+// s.setCharAt(i, ' ');
+// }
+//
+// query = s.toString().trim();
+ // Determine if the query is effectively empty or not. If it is, don't attempt to compile it.
+ if(query.trim().length() == 0)
+ {
+ return "I can't compile nothing!";
+ }
+
// Send the compilation request!
String result = HTTPUtils.sendPOSTRequest("http://coliru.stacked-crooked.com/compile"
, "{ \"cmd\": \"g++ main.cpp && ./a.out\", \"src\": \"" + query + "\" }");
diff --git a/src/network/NetworkDerpi.java b/src/network/NetworkDerpi.java
index 4463b40..b0451b2 100644
--- a/src/network/NetworkDerpi.java
+++ b/src/network/NetworkDerpi.java
@@ -14,6 +14,7 @@ public class NetworkDerpi
{
public String image;
public String tags;
+ public String score;
}
private class InitialRequest
@@ -29,16 +30,26 @@ public class NetworkDerpi
String res = HTTPUtils.sendGETRequest(mainURL + query + "&random_image=1&key=" + Ref.derpiKey);
if(res != null)
{
+ InitialRequest obj;
// Use class evaluation on an array of the response object to be able to hold multiple.
- InitialRequest obj = jsonParser_.fromJson(res, InitialRequest.class);
+ try
+ {
+ obj = jsonParser_.fromJson(res, InitialRequest.class);
+ }
+ catch(Exception e)
+ {
+ return null;
+ }
+
if(res != null)
{
String res2 = HTTPUtils.sendGETRequest("https://derpibooru.org/" + obj.id + ".json");
- image.editPostURL("");
+ image.editPostURL("https://derpibooru.org/" + obj.id);
DerpiResponseObject imageObj = jsonParser_.fromJson(res2, DerpiResponseObject.class);
- image.editImageURL("https:" + imageObj.image.substring(0, imageObj.image.indexOf('_')) + imageObj.image.substring(imageObj.image.lastIndexOf('.')));
+ image.editImageURL("https:" + imageObj.image.substring(0, imageObj.image.indexOf('_')) +
+ imageObj.image.substring(imageObj.image.lastIndexOf('.')));
if(imageObj.tags.contains("artist:"))
{
String [] sepTags = imageObj.tags.split(",");
@@ -54,8 +65,20 @@ public class NetworkDerpi
artists += sepTags[i].substring(sepTags[i].indexOf(":")+1);
}
}
+ image.editAuthorImage("https://derpicdn.net/favicon.ico");
image.editArtist(artists);
+ String tags = "";
+ for(int i = 0; i < sepTags.length && i < 15; i++)
+ {
+ if(!sepTags[i].contains("artist:"))
+ {
+ tags += sepTags[i];
+ }
+ }
+
+ image.editFooterText("[Tags]" + tags);
+ image.editDescriptionText("Score: " + imageObj.score);
}
else
{
@@ -66,4 +89,46 @@ public class NetworkDerpi
return image;
}
+
+ public GenericImage getDerpiByID(String id)
+ {
+ GenericImage image = new GenericImage(" ", " ", " ");
+ String artists = "";
+ image.editAuthorImage("https://derpicdn.net/favicon.ico");
+ String res;
+ try
+ {
+ res = HTTPUtils.sendGETRequest("https://derpibooru.org/" + id + ".json");
+ }
+ catch(Exception e)
+ {
+ return null;
+ }
+ DerpiResponseObject imageObj = jsonParser_.fromJson(res, DerpiResponseObject.class);
+ if(imageObj.tags.contains("artist:"))
+ {
+ String [] sepTags = imageObj.tags.split(",");
+ for(int i = 0; i < sepTags.length; i++)
+ {
+ if(sepTags[i].contains("artist:"))
+ {
+ if(!artists.equals(""))
+ {
+ artists += " and ";
+ }
+ artists += sepTags[i].substring(sepTags[i].indexOf(":")+1);
+ }
+ }
+ }
+ if(artists != "")
+ image.editArtist(artists);
+ else
+ image.editArtist("Artist Unknown!");
+
+ image.editFooterText("[Tags]" + imageObj.tags);
+ image.editDescriptionText("Score: " + imageObj.score);
+ image.editImageURL("https:" + imageObj.image.substring(0, imageObj.image.indexOf('_')) +
+ imageObj.image.substring(imageObj.image.lastIndexOf('.')));
+ return image;
+ }
}
\ No newline at end of file
diff --git a/src/network/NetworkE6.java b/src/network/NetworkE6.java
new file mode 100644
index 0000000..80db428
--- /dev/null
+++ b/src/network/NetworkE6.java
@@ -0,0 +1,201 @@
+package network;
+
+import com.google.gson.Gson;
+
+import dataStructures.GenericImage;
+import offline.Ref;
+import utils.HTTPUtils;
+
+/**
+ * This is the e621 request class, designed for form and parse requests that
+ * use the e621 API, and is entirely static.
+ *
+ * If you ever find this class randomly not working,
+ * it may be a good idea to make sure the user agent string is set in
+ * HTTPUtils to something other than a browser emulating string, or the default
+ * java one.
+ *
+ * @author Wisp
+ * Edited by Rin
+ */
+public class NetworkE6
+{
+ ///////////////////////////////////////
+ // Internal JSON class and variables //
+ //////////////////////////////////.////
+ private static final Gson jsonParser_ = new Gson();
+ private static final String API_ROOT = "https://e621.net/posts.json?";
+ private static int maxSearchResults_ = 10;
+ private static String[] blacklist = Ref.e621Blacklist;
+
+ private class E6ResponseObject
+ {
+ // public variables matching the case and the type we want for JSON.
+ // There are many more fields, but if we don't provide some it just
+ // doesn't bother parsing them.
+ public E6ResponseObjectPost [] posts;
+
+ }
+
+ private class E6ResponseObjectPost
+ {
+ public String id;
+ public String [] sources;
+ public String[] pools;
+ public String rating;
+ public E6ResponseObjectFile file;
+ public E6ResponseObjectScore score;
+ public E6ResponseObjectTags tags;
+ public E6ResponseObjectRelationships relationships;
+ }
+
+ private class E6ResponseObjectFile
+ {
+ public String url;
+ }
+
+ private class E6ResponseObjectScore
+ {
+ public String total;
+ }
+
+ private class E6ResponseObjectTags
+ {
+ public String[] general;
+ public String[] artist;
+ }
+
+ private class E6ResponseObjectRelationships
+ {
+ public String has_children;
+ public String[] children;
+ }
+
+
+
+
+
+ ////////////////////
+ // Static methods //
+ ////////////////////
+ // Requests a specific image, then returns a few.
+ public GenericImage getE6(String input)
+ {
+ GenericImage image = new GenericImage("","","");
+ boolean blacklisted;
+ // Clean up request and replace problematic characters for the query string.
+ input = input.trim();
+ input = input.replace("+", "%2B");
+ input = input.replace(" ", "%20");
+
+ // Configure and send request. Note: Random ordering added as first
+ // tag by default. User-provided tags, therefore, will override it.
+ // If order:score is provided, that will be honored over order:random.
+ String res = HTTPUtils.sendGETRequest(API_ROOT + "tags=order:random%20" + input + "&limit=" + maxSearchResults_);
+
+ if(res != null)
+ {
+
+ // Use class evaluation on an array of the response imageObject to be able to hold multiple.
+ System.out.println("Still");
+ E6ResponseObject imageObj = jsonParser_.fromJson(res, E6ResponseObject.class);
+ System.out.println("Broken");
+ // For now, we really just wanna display images and their source.
+ // Append them all separately to a response string w/ some flavor text.
+ if(imageObj.posts.length < 1)
+ {
+
+ }
+ else
+ {
+ for(int i = 0; i < imageObj.posts.length; ++i)
+ {
+ String [] TagsList = imageObj.posts[i].tags.general;
+ blacklisted = false;
+ for(int j = 0; j < blacklist.length; j++)
+ {
+ for(int k = 0; k < TagsList.length; k ++)
+ {
+ if(TagsList[k].equals(blacklist[j]))
+ {
+ System.out.println("BLACKLISTED " + blacklist[j]);
+ blacklisted = true;
+ }
+ }
+ }
+
+ if(blacklisted)
+ {
+ return null;
+ }
+ // We will always have a file URL. That's a given.
+ image.editImageURL(imageObj.posts[i].file.url);
+ image.editPostURL("https://e621.net/posts/" + imageObj.posts[i].id);
+ if(imageObj.posts[i].tags.artist.length > 0)
+ {
+ String artists = "";
+ for(int j = 0; j < imageObj.posts[i].tags.artist.length; j++)
+ artists += imageObj.posts[i].tags.artist[j];
+ image.editArtist(artists);
+ }
+ else
+ image.editArtist("Artist Not Found!");
+
+ image.editAuthorImage("https://e621.net/favicon.ico");
+
+ String sources = "";
+
+ if(imageObj.posts[i].sources != null)
+ {
+ for(int k = 0; k < imageObj.posts[i].sources.length; k ++)
+ {
+ if(!imageObj.posts[i].sources[k].endsWith(imageObj.posts[i].file.url.substring(imageObj.posts[i].file.url.lastIndexOf('.'))))
+ {
+ String source = imageObj.posts[i].sources[k];
+ if(source.contains("//www."))
+ {
+ source = source.substring(source.indexOf(".") + 1);
+ source = source.substring(0, source.indexOf('.'));
+ }
+ else
+ {
+ source = source.substring(source.indexOf("/") + 2);
+ source = source.substring(0, source.indexOf('.'));
+ }
+ sources += " [" + source + "](" + imageObj.posts[i].sources[k] + ") ";
+ }
+ }
+ image.editDescriptionText("Score: " + imageObj.posts[i].score.total + "\n**Source:**" + sources);
+ }
+ else
+ {
+ image.editDescriptionText("Score: " + imageObj.posts[i].score.total);
+ }
+
+
+ String descTags = "";
+ if(imageObj.posts[i].tags.general.length > 15)
+ {
+ for(int o = 0; o < 15; o++)
+ {
+ descTags += imageObj.posts[i].tags.general[o] + ", ";
+ }
+ image.editFooterText("[Tags] " + descTags + " etc.");
+ }
+ else
+ {
+ descTags = imageObj.posts[i].tags.general.toString();
+ image.editFooterText("[Tags] " + descTags);
+ }
+
+ if(image.output().authorText != null)
+ {
+ return image;
+ }
+ }
+ }
+
+ }
+ return image;
+ }
+}
\ No newline at end of file
diff --git a/src/network/NetworkE621.java b/src/network/NetworkE621.java
deleted file mode 100644
index ef913a0..0000000
--- a/src/network/NetworkE621.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package network;
-
-import com.google.gson.Gson;
-import dataStructures.GenericImage;
-import offline.Ref;
-import utils.*;
-
-/**
- * This is the e621 request class, designed for form and parse requests that
- * use the e621 API, and is entirely static.
- *
- * If you ever find this class randomly not working,
- * it may be a good idea to make sure the user agent string is set in
- * HTTPUtils to something other than a browser emulating string, or the default
- * java one.
- *
- * @author Wisp
- * Edited by Rin
- */
-public class NetworkE621
-{
- ///////////////////////////////////////
- // Internal JSON class and variables //
- //////////////////////////////////.////
- 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 = Ref.e621Blacklist;
- private class E621ResponseObject
- {
- // public varaibles matching the case and the type we want for JSON.
- // There are many more fields, but if we don't provide some it just
- // doesn't bother parsing them.
- public String file_url;
- public String id;
- public String tags;
- public String [] artist;
- }
-
-
-
- ////////////////////
- // Static methods //
- ////////////////////
- // Requests a specific image, then returns a few.
- public GenericImage getE621(String input)
- {
-
- GenericImage image = new GenericImage("","","");
- boolean blacklisted;
- // Clean up request and replace problematic characters for the query string.
- input = input.trim();
- input = input.replace("+", "%2B");
- input = input.replace(" ", "%20");
-
- // Configure and send request. Note: Random ordering added as first
- // tag by default. User-provided tags, therefore, will override it.
- // If order:score is provided, that will be honored over order:random.
- String res = HTTPUtils.sendPOSTRequest(API_ROOT
- , "tags=order:random%20" + input + "&limit=" + maxSearchResults_);
-
-
- if(res != null)
- {
- // Use class evaluation on an array of the response imageObject to be able to hold multiple.
- E621ResponseObject[] imageObj = jsonParser_.fromJson(res, E621ResponseObject[].class);
-
- // For now, we really just wanna display images and their source.
- // Append them all separately to a response string w/ some flavor text.
- if(imageObj.length < 1)
- {
-
- }
- else
- {
- for(int i = 0; i < imageObj.length; ++i)
- {
- blacklisted = false;
- for(int j = 0; j < blacklist.length; j++)
- {
- if(imageObj[i].tags.contains(blacklist[j]))
- {
- blacklisted = true;
- }
- }
-
- if(blacklisted)
- {
- continue;
- }
- // We will always have a file URL. That's a given.
- image.editImageURL(imageObj[i].file_url);
- image.editPostURL("https://e621.net/post/show/" + imageObj[i].id);
- if(imageObj[i].artist.length > 0)
- image.editArtist(imageObj[i].artist[0]);
- else
- image.editArtist("Artist Not Found!");
- }
- }
-
- }
- return image;
- }
-}
\ No newline at end of file
diff --git a/src/network/NetworkSauceNAO.java b/src/network/NetworkSauceNAO.java
index 93c030f..38e6408 100644
--- a/src/network/NetworkSauceNAO.java
+++ b/src/network/NetworkSauceNAO.java
@@ -1,48 +1,49 @@
-package network;
-
-import java.util.List;
-
-import com.google.gson.Gson;
-
-import offline.Ref;
-import utils.HTTPUtils;
-
-public class NetworkSauceNAO
-{
- private static final Gson jsonParser_ = new Gson();
- private static final String API_ROOT = "https://saucenao.com/search.php?db=999&output_type=2&numres=10&api_key=" + Ref.SauceNAOKey +"&url=";
- NetworkE621 e621Sauce = new NetworkE621();
- private class SauceNAOResponseObject
- {
- List results;
- }
-
- private class SauceNAOResult
- {
- SauceNAOData data;
- }
-
- private class SauceNAOData
- {
- String [] ext_urls;
- }
-
- public String getSauce(String input)
- {
- input = input.trim();
- input = input.replace("+", "%2B");
- input = input.replace(" ", "%20");
-
- String res = HTTPUtils.sendGETRequest(API_ROOT + input);
-
- SauceNAOResponseObject sauce = jsonParser_.fromJson(res, SauceNAOResponseObject.class);
- String sauceUrl = sauce.results.get(0).data.ext_urls[0];
- if(sauceUrl.startsWith("https://e621.net/post/show/"))
- {
- System.out.println("id:" + sauceUrl.substring(26));
- return e621Sauce.getE621("id:" + sauceUrl.substring(27)).toString();
- }
- return sauce.results.get(0).data.ext_urls[0];
- }
-
-}
+package network;
+
+import java.util.List;
+
+import com.google.gson.Gson;
+
+import offline.Ref;
+import utils.HTTPUtils;
+
+public class NetworkSauceNAO
+{
+ private static final Gson jsonParser_ = new Gson();
+ private static final String API_ROOT = "https://saucenao.com/search.php?dbmask=268435456&output_type=2&numres=10&api_key=" + Ref.SauceNAOKey +"&url=";
+ NetworkE6 e6Sauce = new NetworkE6();
+ private class SauceNAOResponseObject
+ {
+ List results;
+ }
+
+ private class SauceNAOResult
+ {
+ SauceNAOData data;
+ }
+
+ private class SauceNAOData
+ {
+ String [] ext_urls;
+ }
+
+ public String getSauce(String input)
+ {
+ input = input.trim();
+ input = input.replace("+", "%2B");
+ input = input.replace(" ", "%20");
+
+ String res = HTTPUtils.sendGETRequest(API_ROOT + input);
+
+ SauceNAOResponseObject sauce = jsonParser_.fromJson(res, SauceNAOResponseObject.class);
+ String sauceUrl = sauce.results.get(0).data.ext_urls[0];
+ if(sauceUrl.startsWith("https://e621.net/post/show/"))
+ {
+ System.out.println("id:" + sauceUrl.substring(26));
+ System.out.println("HERE");
+ return e6Sauce.getE6("id:" + sauceUrl.substring(27)).toString();
+ }
+ return sauce.results.get(0).data.ext_urls[0];
+ }
+
+}
diff --git a/src/network/NetworkTheColorAPI.java b/src/network/NetworkTheColorAPI.java
index 5f2e2b2..76725b6 100644
--- a/src/network/NetworkTheColorAPI.java
+++ b/src/network/NetworkTheColorAPI.java
@@ -73,9 +73,36 @@ public class NetworkTheColorAPI
public ColorData lookupHex(String hex)
{
+ if(hex == null)
+ return null;
+
+ hex = hex.replace("#", "").trim();
+ int len = hex.length();
+
+ if(len == 0)
+ return null;
+
+ // Verify we're either a full or half length color. Intermediate colors not supported.
+ // Alpha is also not supported.
+ if(len != 6 && len != 3)
+ return null;
+
try
{
- hex = hex.replace("#", "");
+ String hexUpper = hex.toUpperCase();
+
+ // Verify the number is hex.
+ for(int i = 0; i < hexUpper.length(); ++i)
+ {
+ char current = hexUpper.charAt(i);
+ Boolean isValidNum = current >= '0' && current <= '9';
+ Boolean isValidChar = current >= 'A' && current <= 'F';
+
+ // If it's not a valid character or a valid number, return null/
+ if(!isValidNum && !isValidChar)
+ return null;
+ }
+
String response = HTTPUtils.sendGETRequest("https://www.thecolorapi.com/id?hex=" + hex);
ColorData data = jsonParser.fromJson(response, ColorData.class);
diff --git a/src/utils/audio/AudioUtils.java b/src/utils/audio/AudioUtils.java
index 7430926..18586f0 100644
--- a/src/utils/audio/AudioUtils.java
+++ b/src/utils/audio/AudioUtils.java
@@ -119,6 +119,16 @@ public class AudioUtils
return null;
}
+ public int getVolume(AudioPlayer player)
+ {
+ return player.getVolume();
+ }
+
+ public void changeVolume(AudioPlayer player, int newVol)
+ {
+ player.setVolume(newVol);
+ }
+
public String skipVideo(AudioPlayer player)
{
player.stopTrack();
@@ -152,6 +162,9 @@ class MusicController extends Thread
@Override
public void run()
{
+ try {
+ Thread.sleep(5000);
+ } catch (InterruptedException e) {}
AudioTrack nowPlaying = null;
do
{