Auto stash before merge of "master" and "origin/master"

This commit is contained in:
Alex Stewart
2020-02-25 18:25:17 -05:00
parent d5346b3727
commit 6be6bd4033
17 changed files with 1356 additions and 1271 deletions
+235 -234
View File
@@ -1,234 +1,235 @@
package core;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import commands.characters.CommandCharacterMain;
import commands.general.*;
import commands.guildrole.CommandGuildRoleMain;
import commands.mod.CommandModMain;
import commands.music.CommandMusicMain;
import commands.poll.CommandPollMain;
import commands.raffle.CommandRaffleMain;
import dataStructures.*;
import utils.GlobalLog;
import utils.LogFilter;
public class CommandManager
{
// Variables
private HashMap<String, Command> commands;
private ArrayList<CommandThread> threadAccumulator;
private CommandEnabler commandEnabler;
private long invokeCount;
// Singleton
public static CommandManager instance;
// Default Constructor
public CommandManager(CommandEnabler commandEnabler)
{
GlobalLog.log(LogFilter.Core, "Initializing " + this.getClass().getSimpleName());
if(instance == null)
{
this.commands = new HashMap<String, Command>();
this.threadAccumulator = new ArrayList<CommandThread>();
this.invokeCount = 0;
this.commandEnabler = commandEnabler;
registerAllCommands();
instance = this;
}
else
{
GlobalLog.error(LogFilter.Core, "You can't have two of the following: " + this.getClass().getSimpleName());
}
}
// Registers all the commands for the instance
public void registerAllCommands()
{
this.commands.clear();
// Dev
this.register(LocCommands.stub("work"), new CommandDoWork(KittyRole.Dev, KittyRating.Safe));
this.register(LocCommands.stub("shutdown"), new CommandShutdown(KittyRole.Dev, KittyRating.Safe));
this.register(LocCommands.stub("stats"), new CommandStats(KittyRole.Dev, KittyRating.Safe));
this.register(LocCommands.stub("invite"), new CommandInvite(KittyRole.Dev, KittyRating.Safe));
this.register(LocCommands.stub("buildHelp"), new CommandHelpBuilder(KittyRole.Dev, KittyRating.Safe));
this.register(LocCommands.stub("tweet"), new CommandTweet(KittyRole.Dev, KittyRating.Safe));
this.register(LocCommands.stub("dbflush"), new CommandDBFlush(KittyRole.Dev, KittyRating.Safe));
this.register(LocCommands.stub("dbstats"), new CommandDBStats(KittyRole.Dev, KittyRating.Safe));
// Admin
this.register(LocCommands.stub("rating"), new CommandRating(KittyRole.Admin, KittyRating.Safe));
this.register(LocCommands.stub("indicator"), new CommandChangeIndicator(KittyRole.Admin, KittyRating.Safe));
// Mod
this.register(LocCommands.stub("givebeans"), new CommandGiveBeans(KittyRole.Mod, KittyRating.Safe));
this.register(LocCommands.stub("mod"), new CommandModMain(KittyRole.Mod, KittyRating.Safe));
// General
this.register(LocCommands.stub("fetch"), new CommandFetch(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("teey"), new CommandTeey(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("perish, thenperish"), new CommandPerish(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("yeet"), new CommandYeet(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("ping"), new CommandPing(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("boop"), new CommandBoop(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("roll"), new CommandRoll(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("choose"), new CommandChoose(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("help"), new CommandHelp(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("info, about"), new CommandInfo(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("wolfram"), new CommandWolfram(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("c++, g++, cplus, cpp"), new CommandColiru(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("java, jdoodle"), new CommandJDoodle(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("beans"), new CommandBeansShow(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("kittyrole"), new CommandRole(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("bet"), new CommandBetBeans(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("map"), new CommandMap(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("rpstart"), new CommandRPStart(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("rpend"), new CommandRPEnd(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("tony, stark, dontfeelgood, dontfeelsogood"), new CommandStark(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("blur"), new CommandBlurry(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("eightball, 8ball"), new CommandEightBall(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("catch"), new CommandCatch(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("bethistory"), new CommandBetHistory(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("crouton"), new CommandCrouton(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("benchmark, bench"), new CommandBenchmark(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("leaderboard"), new CommandLeaderboard(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("color, colour"), new CommandColor(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("remind"), new CommandRemind(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("role"), new CommandGuildRoleMain(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("character"), new CommandCharacterMain(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("raffle"), new CommandRaffleMain(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("poll"), new CommandPollMain(KittyRole.General, KittyRating.Safe));
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("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)
// and the command associated with the localized commands.
public void register(Pair<String, String> pair, Command command)
{
if(pair == null || pair.Second == null)
return;
String[] keys = pair.Second.split(",");
for(int i = 0; i < keys.length; ++i)
{
String key = keys[i].trim().toLowerCase();
command.registeredNames.add(key);
Command old = commands.put(key, command);
if(old != null)
{
GlobalLog.warn(LogFilter.Core, "Writing over a command with the key " + key);
return;
}
GlobalLog.log(LogFilter.Core, "Command registered under key " + key);
}
}
// Calls the command but on a whole new thread!
public boolean invokeOnNewThread(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response responseContext)
{
// This is here to prevent spinning up a thread if this wasn't even a command.
if(input == null || !input.isValid())
return false;
// At this point, verify that the command is enabled. If we don't have any info
// on if it's enabled or not, we assume it's enabled to prevent broken functionality due
// to formatting or casing issues.
if(input.key != null && !commandEnabler.isEnabled(input.key))
return false;
Command command = commands.get(input.key);
if(command != null)
{
// Spin up a thread and begin it. The thread carries the info needed to invoke the command.
CommandThread newThread = new CommandThread(this, input, guild, channel, user, responseContext);
threadAccumulator.add(newThread);
newThread.start();
return true;
}
else
{
GlobalLog.warn(LogFilter.Command, "User " + user.name + " tried to invoke command that doesn't exist: " + input.key);
return false;
}
}
// Calls the command specified with the key, providing user information arguments, etc.
void invoke(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response responseContext)
{
if(input == null || !input.isValid())
return;
Command command = commands.get(input.key);
if(command != null)
{
++invokeCount;
command.invoke(guild, channel, user, input, responseContext);
}
}
// Looks up command by name. If it exists, dumps help text, otherwise returns null.
public String getCommandHelpText(String key)
{
Command command = commands.get(key);
if(command != null)
return "`" + key + "`: " + command.getHelpText();
return null;
}
// Returns the number of commands sent so far during this program run
public long getInvokeCount()
{
return invokeCount;
}
// Returns all commands by name
public ArrayList<Command> getAllRegisteredCommands()
{
ArrayList<Command> cmds = new ArrayList<Command>();
for(Entry<String, Command> entry : commands.entrySet())
cmds.add(entry.getValue());
return cmds;
}
// Info about threads running packaged into an object
public class ThreadData
{
public HashMap<Thread.State, Integer> states = new HashMap<Thread.State, Integer>();
}
public ThreadData dumpThreadData()
{
ThreadData data = new ThreadData();
for(int i = 0; i < threadAccumulator.size(); ++i)
{
Thread.State state = threadAccumulator.get(i).getState();
Integer num = data.states.get(state);
if(num == null)
num = 0;
data.states.put(state, num + 1);
}
return data;
}
}
package core;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map.Entry;
import commands.characters.CommandCharacterMain;
import commands.general.*;
import commands.guildrole.CommandGuildRoleMain;
import commands.mod.CommandModMain;
import commands.music.CommandMusicMain;
import commands.poll.CommandPollMain;
import commands.raffle.CommandRaffleMain;
import dataStructures.*;
import utils.GlobalLog;
import utils.LogFilter;
public class CommandManager
{
// Variables
private HashMap<String, Command> commands;
private ArrayList<CommandThread> threadAccumulator;
private CommandEnabler commandEnabler;
private long invokeCount;
// Singleton
public static CommandManager instance;
// Default Constructor
public CommandManager(CommandEnabler commandEnabler)
{
GlobalLog.log(LogFilter.Core, "Initializing " + this.getClass().getSimpleName());
if(instance == null)
{
this.commands = new HashMap<String, Command>();
this.threadAccumulator = new ArrayList<CommandThread>();
this.invokeCount = 0;
this.commandEnabler = commandEnabler;
registerAllCommands();
instance = this;
}
else
{
GlobalLog.error(LogFilter.Core, "You can't have two of the following: " + this.getClass().getSimpleName());
}
}
// Registers all the commands for the instance
public void registerAllCommands()
{
this.commands.clear();
// Dev
this.register(LocCommands.stub("work"), new CommandDoWork(KittyRole.Dev, KittyRating.Safe));
this.register(LocCommands.stub("shutdown"), new CommandShutdown(KittyRole.Dev, KittyRating.Safe));
this.register(LocCommands.stub("stats"), new CommandStats(KittyRole.Dev, KittyRating.Safe));
this.register(LocCommands.stub("invite"), new CommandInvite(KittyRole.Dev, KittyRating.Safe));
this.register(LocCommands.stub("buildHelp"), new CommandHelpBuilder(KittyRole.Dev, KittyRating.Safe));
this.register(LocCommands.stub("tweet"), new CommandTweet(KittyRole.Dev, KittyRating.Safe));
this.register(LocCommands.stub("dbflush"), new CommandDBFlush(KittyRole.Dev, KittyRating.Safe));
this.register(LocCommands.stub("dbstats"), new CommandDBStats(KittyRole.Dev, KittyRating.Safe));
// Admin
this.register(LocCommands.stub("rating"), new CommandRating(KittyRole.Admin, KittyRating.Safe));
this.register(LocCommands.stub("indicator"), new CommandChangeIndicator(KittyRole.Admin, KittyRating.Safe));
// Mod
this.register(LocCommands.stub("givebeans"), new CommandGiveBeans(KittyRole.Mod, KittyRating.Safe));
this.register(LocCommands.stub("mod"), new CommandModMain(KittyRole.Mod, KittyRating.Safe));
// General
this.register(LocCommands.stub("fetch"), new CommandFetch(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("teey"), new CommandTeey(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("perish, thenperish"), new CommandPerish(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("yeet"), new CommandYeet(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("ping"), new CommandPing(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("boop"), new CommandBoop(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("roll"), new CommandRoll(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("choose"), new CommandChoose(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("help"), new CommandHelp(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("info, about"), new CommandInfo(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("wolfram"), new CommandWolfram(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("c++, g++, cplus, cpp"), new CommandColiru(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("java, jdoodle"), new CommandJDoodle(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("beans"), new CommandBeansShow(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("kittyrole"), new CommandRole(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("bet"), new CommandBetBeans(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("map"), new CommandMap(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("rpstart"), new CommandRPStart(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("rpend"), new CommandRPEnd(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("tony, stark, dontfeelgood, dontfeelsogood"), new CommandStark(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("blur"), new CommandBlurry(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("eightball, 8ball"), new CommandEightBall(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("catch"), new CommandCatch(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("bethistory"), new CommandBetHistory(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("crouton"), new CommandCrouton(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("benchmark, bench"), new CommandBenchmark(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("leaderboard"), new CommandLeaderboard(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("color, colour"), new CommandColor(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("remind"), new CommandRemind(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("role"), new CommandGuildRoleMain(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("character"), new CommandCharacterMain(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("raffle"), new CommandRaffleMain(KittyRole.General, KittyRating.Safe));
this.register(LocCommands.stub("poll"), new CommandPollMain(KittyRole.General, KittyRating.Safe));
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"), 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)
// and the command associated with the localized commands.
public void register(Pair<String, String> pair, Command command)
{
if(pair == null || pair.Second == null)
return;
String[] keys = pair.Second.split(",");
for(int i = 0; i < keys.length; ++i)
{
String key = keys[i].trim().toLowerCase();
command.registeredNames.add(key);
Command old = commands.put(key, command);
if(old != null)
{
GlobalLog.warn(LogFilter.Core, "Writing over a command with the key " + key);
return;
}
GlobalLog.log(LogFilter.Core, "Command registered under key " + key);
}
}
// Calls the command but on a whole new thread!
public boolean invokeOnNewThread(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response responseContext)
{
// This is here to prevent spinning up a thread if this wasn't even a command.
if(input == null || !input.isValid())
return false;
// At this point, verify that the command is enabled. If we don't have any info
// on if it's enabled or not, we assume it's enabled to prevent broken functionality due
// to formatting or casing issues.
if(input.key != null && !commandEnabler.isEnabled(input.key))
return false;
Command command = commands.get(input.key);
if(command != null)
{
// Spin up a thread and begin it. The thread carries the info needed to invoke the command.
CommandThread newThread = new CommandThread(this, input, guild, channel, user, responseContext);
threadAccumulator.add(newThread);
newThread.start();
return true;
}
else
{
GlobalLog.warn(LogFilter.Command, "User " + user.name + " tried to invoke command that doesn't exist: " + input.key);
return false;
}
}
// Calls the command specified with the key, providing user information arguments, etc.
void invoke(KittyGuild guild, KittyChannel channel, KittyUser user, UserInput input, Response responseContext)
{
if(input == null || !input.isValid())
return;
Command command = commands.get(input.key);
if(command != null)
{
++invokeCount;
command.invoke(guild, channel, user, input, responseContext);
}
}
// Looks up command by name. If it exists, dumps help text, otherwise returns null.
public String getCommandHelpText(String key)
{
Command command = commands.get(key);
if(command != null)
return "`" + key + "`: " + command.getHelpText();
return null;
}
// Returns the number of commands sent so far during this program run
public long getInvokeCount()
{
return invokeCount;
}
// Returns all commands by name
public ArrayList<Command> getAllRegisteredCommands()
{
ArrayList<Command> cmds = new ArrayList<Command>();
for(Entry<String, Command> entry : commands.entrySet())
cmds.add(entry.getValue());
return cmds;
}
// Info about threads running packaged into an object
public class ThreadData
{
public HashMap<Thread.State, Integer> states = new HashMap<Thread.State, Integer>();
}
public ThreadData dumpThreadData()
{
ThreadData data = new ThreadData();
for(int i = 0; i < threadAccumulator.size(); ++i)
{
Thread.State state = threadAccumulator.get(i).getState();
Integer num = data.states.get(state);
if(num == null)
num = 0;
data.states.put(state, num + 1);
}
return data;
}
}
+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();
}
}
}