Merge remote-tracking branch 'origin/master'

This commit is contained in:
Matthew Cech
2020-03-26 16:49:50 -07:00
28 changed files with 1297 additions and 1077 deletions
+96 -2
View File
@@ -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");
}
}
+1 -1
View File
@@ -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)
+71 -71
View File
@@ -1,72 +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"));
}
}
}
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"));
}
}
}
}
+70 -59
View File
@@ -1,60 +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;
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 || !guild.channels.get(Long.getLong(channel.uniqueID)).mature)
{
if(!guild.channels.get(Long.getLong(channel.uniqueID)).mature)
{
res.send("This channel is SFW only! Have some (probably) wholesome content ^^~");
}
response = searcher.getE6(input.args + " rating:safe").output();
try
{
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"));
}
}
}
}
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"));
}
}
}
}
}
+1
View File
@@ -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
+2 -1
View File
@@ -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));
}
}
+23 -23
View File
@@ -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));
}
}
+42
View File
@@ -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));
}
}
+478 -478
View File
@@ -1,479 +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<String, KittyGuild> guildCache;
// Key: channel string id, Value: channel information
private static HashMap<String, KittyChannel> channelCache;
// Key: guild string id + user string id, Value: user information
private static HashMap<String, KittyUser> 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<String, KittyGuild>();
userCache = new HashMap<String, KittyUser>();
channelCache = new HashMap<String, KittyChannel>();
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<Emote> emotes = event.getGuild().getEmotes();
ArrayList<String> emotesString = new ArrayList<String>();
HashMap<Long, KittyChannel> channels = new HashMap<Long, KittyChannel>();
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.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<String, KittyGuild> guildCache;
// Key: channel string id, Value: channel information
private static HashMap<String, KittyChannel> channelCache;
// Key: guild string id + user string id, Value: user information
private static HashMap<String, KittyUser> 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<String, KittyGuild>();
userCache = new HashMap<String, KittyUser>();
channelCache = new HashMap<String, KittyChannel>();
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<Emote> emotes = event.getGuild().getEmotes();
ArrayList<String> emotesString = new ArrayList<String>();
HashMap<Long, KittyChannel> channels = new HashMap<Long, KittyChannel>();
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();
}
}
}
+14 -14
View File
@@ -1,15 +1,15 @@
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;
}
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;
}
}
+180 -180
View File
@@ -1,180 +1,180 @@
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<String> hasVoted = new ArrayList<String>();
public ArrayList<String> emoji = new ArrayList<String>();
public ArrayList <KittyPoll> choices = new ArrayList<KittyPoll>();
public HashMap <Long, KittyChannel> channels;
public AdminControl control;
public AudioUtils audio;
public ArrayList <KittyUser> raffleUsersUnchosen = new ArrayList<KittyUser>();
public ArrayList <KittyUser> raffleUsersChosen = new ArrayList<KittyUser>();
// 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 <String> emoji, AudioUtils audio, HashMap<Long, KittyChannel> 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<Long, KittyChannel> 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();
}
}
}
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<String> hasVoted = new ArrayList<String>();
public ArrayList<String> emoji = new ArrayList<String>();
public ArrayList <KittyPoll> choices = new ArrayList<KittyPoll>();
public HashMap <Long, KittyChannel> channels;
public AdminControl control;
public AudioUtils audio;
public ArrayList <KittyUser> raffleUsersUnchosen = new ArrayList<KittyUser>();
public ArrayList <KittyUser> raffleUsersChosen = new ArrayList<KittyUser>();
// 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 <String> emoji, AudioUtils audio, HashMap<Long, KittyChannel> 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<Long, KittyChannel> 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();
}
}
}
+134 -134
View File
@@ -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<String> 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<String> 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;
}
}
+75 -36
View File
@@ -24,22 +24,54 @@ public class NetworkE6
// 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 final String API_ROOT = "https://e621.net/posts.json?";
private static int maxSearchResults_ = 10;
private static String[] blacklist = Ref.e621Blacklist;
private class E6ResponseObject
{
// public varaibles matching the case and the type we want for JSON.
// 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 String file_url;
public String id;
public String tags;
public String [] artist;
public String [] sources;
public String score;
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;
}
@@ -59,33 +91,36 @@ public class NetworkE6
// 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_);
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.
E6ResponseObject[] imageObj = jsonParser_.fromJson(res, E6ResponseObject[].class);
// 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.length < 1)
if(imageObj.posts.length < 1)
{
}
else
{
for(int i = 0; i < imageObj.length; ++i)
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++)
{
if(imageObj[i].tags.contains(blacklist[j]))
for(int k = 0; k < TagsList.length; k ++)
{
System.out.println("BLACKLISTED " + blacklist[j]);
blacklisted = true;
if(TagsList[k].equals(blacklist[j]))
{
System.out.println("BLACKLISTED " + blacklist[j]);
blacklisted = true;
}
}
}
@@ -94,10 +129,15 @@ public class NetworkE6
return null;
}
// 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]);
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!");
@@ -105,13 +145,13 @@ public class NetworkE6
String sources = "";
if(imageObj[i].sources != null)
if(imageObj.posts[i].sources != null)
{
for(int k = 0; k < imageObj[i].sources.length; k ++)
for(int k = 0; k < imageObj.posts[i].sources.length; k ++)
{
if(!imageObj[i].sources[k].endsWith(imageObj[i].file_url.substring(imageObj[i].file_url.lastIndexOf('.'))))
if(!imageObj.posts[i].sources[k].endsWith(imageObj.posts[i].file.url.substring(imageObj.posts[i].file.url.lastIndexOf('.'))))
{
String source = imageObj[i].sources[k];
String source = imageObj.posts[i].sources[k];
if(source.contains("//www."))
{
source = source.substring(source.indexOf(".") + 1);
@@ -122,31 +162,30 @@ public class NetworkE6
source = source.substring(source.indexOf("/") + 2);
source = source.substring(0, source.indexOf('.'));
}
sources += " [" + source + "](" + imageObj[i].sources[k] + ") ";
sources += " [" + source + "](" + imageObj.posts[i].sources[k] + ") ";
}
}
image.editDescriptionText("Score: " + imageObj[i].score + "\n**Source:**" + sources);
image.editDescriptionText("Score: " + imageObj.posts[i].score.total + "\n**Source:**" + sources);
}
else
{
image.editDescriptionText("Score: " + imageObj[i].score);
image.editDescriptionText("Score: " + imageObj.posts[i].score.total);
}
if(imageObj[i].tags.split(" ").length > 15)
String descTags = "";
if(imageObj.posts[i].tags.general.length > 15)
{
String [] splitTags = imageObj[i].tags.split(" ");
String descTags = "";
for(int o = 0; o < 15; o++)
{
descTags += splitTags[o] + ", ";
descTags += imageObj.posts[i].tags.general[o] + ", ";
}
image.editFooterText("[Tags] " + descTags + " etc.");
}
else
{
image.editFooterText("[Tags] " + imageObj[i].tags.replace(" ", ", "));
descTags = imageObj.posts[i].tags.general.toString();
image.editFooterText("[Tags] " + descTags);
}
if(image.output().authorText != null)
+49 -48
View File
@@ -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=";
NetworkE6 e6Sauce = new NetworkE6();
private class SauceNAOResponseObject
{
List<SauceNAOResult> 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 e6Sauce.getE6("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<SauceNAOResult> 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];
}
}
+14 -1
View File
@@ -73,9 +73,22 @@ 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("#", "").trim();
String hexUpper = hex.toUpperCase();
// Verify the number is hex.
+13
View File
@@ -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
{