package util.settings;
import face.*;
import gui.ChatPane;
import gui.CombinedChatPane;
import gui.forms.GUIMain;
import irc.Donor;
import irc.Subscriber;
import irc.account.Account;
import irc.account.AccountManager;
import irc.account.OAuth;
import irc.account.Task;
import lib.pircbot.ChannelManager;
import sound.Sound;
import sound.SoundEngine;
import thread.ShutdownHook;
import thread.ThreadEngine;
import util.Permissions;
import util.Utils;
import util.comm.Command;
import util.comm.ConsoleCommand;
import util.misc.Donation;
import javax.swing.filechooser.FileSystemView;
import javax.swing.text.StyleConstants;
import java.awt.*;
import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.*;
/**
* This class is the container for every setting Botnak has.
* There's accounts, booleans of all sorts, and ints, you name it.
*/
public class Settings {
//accounts
public static AccountManager accountManager = null;
public static ChannelManager channelManager = null;
//donations
public static DonationManager donationManager = null;
public static boolean loadedDonationSounds = false;
public static SubscriberManager subscriberManager = null;
public static boolean loadedSubSounds = false;
//directories
public static File defaultDir = new File(FileSystemView.getFileSystemView().getDefaultDirectory().getAbsolutePath()
+ File.separator + "Botnak");
public static File tmpDir = new File(defaultDir + File.separator + "_temp");
public static File faceDir = new File(defaultDir + File.separator + "Faces");
public static File nameFaceDir = new File(defaultDir + File.separator + "NameFaces");
public static File twitchFaceDir = new File(defaultDir + File.separator + "TwitchFaces");
public static File frankerFaceZDir = new File(defaultDir + File.separator + "FrankerFaceZ");
public static File subIconsDir = new File(defaultDir + File.separator + "SubIcons");
public static File subSoundDir = new File(defaultDir + File.separator + "SubSounds");
public static File donationSoundDir = new File(defaultDir + File.separator + "DonationSounds");
public static File logDir = new File(defaultDir + File.separator + "Logs");
//files
public static File accountsFile = new File(defaultDir + File.separator + "acc.ini");
public static File tabsFile = new File(defaultDir + File.separator + "tabs.txt");
public static File soundsFile = new File(defaultDir + File.separator + "sounds.txt");
public static File faceFile = new File(defaultDir + File.separator + "faces.txt");
public static File twitchFaceFile = new File(defaultDir + File.separator + "twitchfaces.txt");
public static File userColFile = new File(defaultDir + File.separator + "usercols.txt");
public static File commandsFile = new File(defaultDir + File.separator + "commands.txt");
public static File ccommandsFile = new File(defaultDir + File.separator + "chatcom.txt");
public static File defaultsFile = new File(defaultDir + File.separator + "defaults.ini");
public static File lafFile = new File(defaultDir + File.separator + "laf.txt");
public static File windowFile = new File(defaultDir + File.separator + "window.txt");
public static File keywordsFile = new File(defaultDir + File.separator + "keywords.txt");
public static File donorsFile = new File(defaultDir + File.separator + "donors.txt");
public static File donationsFile = new File(defaultDir + File.separator + "donations.txt");
public static File subsFile = new File(defaultDir + File.separator + "subs.txt");
//appearance
public static String lookAndFeel = "lib.jtattoo.com.jtattoo.plaf.hifi.HiFiLookAndFeel";
//Graphite = "lib.jtattoo.com.jtattoo.plaf.graphite.GraphiteLookAndFeel"
public static String date;
//System Tray
public static Setting<Boolean> stShowMentions = new Setting<>("ST_DisplayDonations", false, Boolean.class);
public static Setting<Boolean> stShowDonations = new Setting<>("ST_DisplayMentions", false, Boolean.class);
public static Setting<Boolean> stShowActivity = new Setting<>("ST_DisplayActivity", false, Boolean.class);
public static Setting<Boolean> stMuted = new Setting<>("", false, Boolean.class);
public static Setting<Boolean> stUseSystemTray = new Setting<>("ST_UseSystemTray", false, Boolean.class);
public static Setting<Boolean> stShowSubscribers = new Setting<>("ST_DisplaySubscribers", false, Boolean.class);
public static Setting<Boolean> stShowNewFollowers = new Setting<>("ST_DisplayFollowers", false, Boolean.class);
//Bot Reply
public static Setting<Boolean> botAnnounceSubscribers = new Setting<>(""/*TODO*/, true, Boolean.class);
public static Setting<Boolean> botShowYTVideoDetails = new Setting<>(""/*TODO*/, true, Boolean.class);
public static Setting<Boolean> botShowTwitchVODDetails = new Setting<>(""/*TODO*/, true, Boolean.class);
public static Setting<Boolean> botShowPreviousSubSound = new Setting<>(""/*TODO*/, true, Boolean.class);
public static Setting<Boolean> botShowPreviousDonSound = new Setting<>(""/*TODO*/, true, Boolean.class);
public static Setting<Boolean> botUnshortenURLs = new Setting<>(""/*TODO*/, true, Boolean.class);
public static Setting<Boolean> ffzFacesEnable = new Setting<>("", true, Boolean.class);
public static Setting<Boolean> ffzFacesUseAll = new Setting<>("", false, Boolean.class);
public static Setting<Boolean> actuallyClearChat = new Setting<>("", false, Boolean.class);
public static Setting<Boolean> showDonorIcons = new Setting<>("", true, Boolean.class);
public static Setting<Boolean> showTabPulses = new Setting<>("", true, Boolean.class);
public static Setting<Boolean> trackDonations = new Setting<>("TrackDonations", false, Boolean.class);
public static Setting<Boolean> trackFollowers = new Setting<>("TrackFollowers", false, Boolean.class);
public static Setting<Boolean> cleanupChat = new Setting<>("ClearChat", true, Boolean.class);
public static Setting<Boolean> logChat = new Setting<>("LogChat", false, Boolean.class);
//Icons TODO are these needed anymore?
public static Setting<Boolean> useMod = new Setting<>("UseMod", false, Boolean.class);
public static Setting<Boolean> useAdmin = new Setting<>("UseAdmin", false, Boolean.class);
public static Setting<Boolean> useStaff = new Setting<>("UseStaff", false, Boolean.class);
public static Setting<Boolean> useBroad = new Setting<>("UseBroad", false, Boolean.class);
public static Setting<URL> modIcon = new Setting<>("CustomMod", Settings.class.getResource("/image/mod.png"), URL.class);
public static Setting<URL> broadIcon = new Setting<>("CustomBroad", Settings.class.getResource("/image/broad.png"), URL.class);
public static Setting<URL> adminIcon = new Setting<>("CustomAdmin", Settings.class.getResource("/image/mod.png"), URL.class);
public static Setting<URL> staffIcon = new Setting<>("CustomStaff", Settings.class.getResource("/image/mod.png"), URL.class);
public static Setting<URL> turboIcon = new Setting<>("", Settings.class.getResource("/image/turbo.png"), URL.class);
public static Setting<Boolean> autoReconnectAccounts = new Setting<>("", true, Boolean.class);
public static Setting<Float> soundVolumeGain = new Setting<>(""/*TODO*/, 100f, Float.class);
public static Setting<Integer> faceMaxHeight = new Setting<>("FaceMaxHeight", 20, Integer.class);
public static Setting<Integer> chatMax = new Setting<>("MaxChat", 100, Integer.class);
public static Setting<Integer> botReplyType = new Setting<>("BotReplyType", 0, Integer.class);
//0 = none, 1 = botnak user only, 2 = everyone
public static Setting<String> lastFMAccount = new Setting<>("LastFMAccount", "", String.class);
public static Setting<String> defaultSoundDir = new Setting<>("SoundDir", "", String.class);
public static Setting<String> defaultFaceDir = new Setting<>("FaceDir", "", String.class);
public static Setting<String> donationClientID = new Setting<>("DCID", "", String.class);
public static Setting<String> donationAuthCode = new Setting<>("DCOAUTH", "", String.class);
public static Setting<Boolean> scannedInitialDonations = new Setting<>("RanInitDonations", true, Boolean.class);
public static Setting<Boolean> scannedInitialSubscribers = new Setting<>("RanInitSub", false, Boolean.class);
public static Setting<Integer> soundEngineDelay = new Setting<>("SoundEngineDelay", 10000, Integer.class);
public static Setting<Integer> soundEnginePermission = new Setting<>("SoundEnginePerm", 1, Integer.class);
public static Setting<Boolean> alwaysOnTop = new Setting<>("AlwaysOnTop", false, Boolean.class);
public static Setting<Font> font = new Setting<>("Font", new Font("Calibri", Font.PLAIN, 18), Font.class);
private static ArrayList<Setting> settings;
public static Window WINDOW = new Window();
public static LookAndFeel LAF = new LookAndFeel();
public static TabState TAB_STATE = new TabState();
public static Subscribers SUBSCRIBERS = new Subscribers();
public static Donations DONATIONS = new Donations();
public static Donors DONORS = new Donors();
public static Sounds SOUNDS = new Sounds();
public static Commands COMMANDS = new Commands();
public static UserColors USER_COLORS = new UserColors();
public static TwitchFaces TWITCH_FACES = new TwitchFaces();
public static Faces FACES = new Faces();
public static Keywords KEYWORDS = new Keywords();
public static void init() {
long time = System.currentTimeMillis();
SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yy");
date = sdf.format(new Date(time));
defaultDir.mkdirs();
// This clears the temp directory
if (tmpDir.exists())
ShutdownHook.deleteTempOnExit();
tmpDir.mkdirs();
faceDir.mkdirs();
nameFaceDir.mkdirs();
twitchFaceDir.mkdirs();
subIconsDir.mkdirs();
subSoundDir.mkdirs();
donationSoundDir.mkdirs();
//collection for bulk saving/loading
//(otherwise it would have been a bunch of hardcode...)
settings = new ArrayList<>();
try {
Field[] fields = Settings.class.getDeclaredFields();
for (Field f : fields) {
if (f.getType().getName().equals(Setting.class.getName())) {
settings.add((Setting) f.get(null));
}
}
} catch (Exception e) {
GUIMain.log(e);
}
/**
* "After the fact":
* These change listeners are used to update the main GUI upon the setting change.
*/
soundEngineDelay.addChangeListener(GUIMain.instance::updateSoundDelay);
soundEnginePermission.addChangeListener(GUIMain.instance::updateSoundPermission);
botReplyType.addChangeListener(GUIMain.instance::updateBotReplyPerm);
font.addChangeListener(f -> {
StyleConstants.setFontFamily(GUIMain.norm, f.getFamily());
StyleConstants.setFontSize(GUIMain.norm, f.getSize());
StyleConstants.setBold(GUIMain.norm, f.isBold());
StyleConstants.setItalic(GUIMain.norm, f.isItalic());
});
alwaysOnTop.addChangeListener(GUIMain.instance::updateAlwaysOnTopStatus);
}
/**
* This void loads everything Botnak will use, and sets the appropriate settings.
*/
public static void load() {
if (Utils.areFilesGood(WINDOW.getFile().getAbsolutePath())) WINDOW.load();
accountManager = new AccountManager();
channelManager = new ChannelManager();
accountManager.start();
donationManager = new DonationManager();
subscriberManager = new SubscriberManager();
if (Utils.areFilesGood(accountsFile.getAbsolutePath())) {
GUIMain.log("Loading accounts...");
loadPropData(0);
}
if (Utils.areFilesGood(defaultsFile.getAbsolutePath())) {
GUIMain.log("Loading defaults...");
loadPropData(1);
}
if (Utils.areFilesGood(tabsFile.getAbsolutePath()) && accountManager.getUserAccount() != null) {
GUIMain.log("Loading tabs...");
TAB_STATE.load();
GUIMain.channelPane.setSelectedIndex(TabState.startIndex);
GUIMain.log("Loaded tabs!");
}
if (Utils.areFilesGood(soundsFile.getAbsolutePath())) {
GUIMain.log("Loading sounds...");
SOUNDS.load();
GUIMain.log("Loaded sounds!");
}
if (subSoundDir.exists())
{
String[] dirList = subSoundDir.list();
if (dirList != null && dirList.length > 0)
{
GUIMain.log("Loading sub sounds...");
doLoadSubSounds();
}
}
if (donationSoundDir.exists())
{
String[] dirList = donationSoundDir.list();
if (dirList != null && dirList.length > 0)
{
GUIMain.log("Loading donation sounds...");
doLoadDonationSounds();
}
}
if (Utils.areFilesGood(userColFile.getAbsolutePath())) {
GUIMain.log("Loading user colors...");
USER_COLORS.load();
GUIMain.log("Loaded user colors!");
}
if (Utils.areFilesGood(donorsFile.getAbsolutePath())) {
GUIMain.log("Loading donors...");
DONORS.load();
GUIMain.log("Loaded donors!");
}
if (Utils.areFilesGood(donationsFile.getAbsolutePath())) {
GUIMain.log("Loading donations...");
DONATIONS.load();//these are stored locally
if (Donations.mostRecent != null) {
donationManager.setLastDonation(Donations.mostRecent);
GUIMain.log(String.format("Most recent donation: %s for %s", Donations.mostRecent.getFromWho(),
DonationManager.getCurrencyFormat().format(Donations.mostRecent.getAmount())));
}
if (!Donations.donations.isEmpty()) {
donationManager.fillDonations(Donations.donations);
Donations.donations.clear();
Donations.donations = null;
Donations.mostRecent = null;
GUIMain.log("Loaded donations!");
}
}
//checks online for offline donations and adds them
if (donationManager.canCheck() && scannedInitialDonations.getValue()) {
ThreadEngine.submit(() ->
{
donationManager.checkDonations(false);
donationManager.ranFirstCheck = true;
});
}
if (!scannedInitialDonations.getValue()) {
ThreadEngine.submit(() ->
{
donationManager.scanInitialDonations(0);
scannedInitialDonations.setValue(true);
});
}
if (accountManager.getUserAccount() != null && accountManager.getUserAccount().getOAuth().canReadSubscribers())
{
if (!scannedInitialSubscribers.getValue() && accountManager.getUserAccount() != null) {
subscriberManager.scanInitialSubscribers(accountManager.getUserAccount().getName(),
accountManager.getUserAccount().getOAuth(), 0, new HashSet<>());
}
}
if (Utils.areFilesGood(subsFile.getAbsolutePath())) {
GUIMain.log("Loading subscribers...");
SUBSCRIBERS.load();
if (Subscribers.mostRecent != null) {
subscriberManager.setLastSubscriber(Subscribers.mostRecent);
GUIMain.log("Most recent subscriber: " + Subscribers.mostRecent.getName());
}
if (!Subscribers.subscribers.isEmpty()) subscriberManager.fillSubscribers(Subscribers.subscribers);
Subscribers.subscribers.clear();
Subscribers.subscribers = null;
Subscribers.mostRecent = null;
GUIMain.log("Loaded subscribers!");
}
if (Utils.areFilesGood(commandsFile.getAbsolutePath())) {
GUIMain.log("Loading text commands...");
COMMANDS.load();
GUIMain.log("Loaded text commands!");
}
if (subIconsDir.exists()) {
File[] subIcons = subIconsDir.listFiles();
if (subIcons != null && subIcons.length > 0) {
GUIMain.log("Loading subscriber icons...");
loadSubIcons(subIcons);
}
}
File[] nameFaces = nameFaceDir.listFiles();
if (nameFaces != null && nameFaces.length > 0) {
GUIMain.log("Loading name faces...");
loadNameFaces(nameFaces);
}
if (ffzFacesEnable.getValue()) {
frankerFaceZDir.mkdirs();
File[] files = frankerFaceZDir.listFiles();
if (files != null && files.length > 0) {
GUIMain.log("Loading FrankerFaceZ faces...");
loadFFZFaces(files);
}
}
if (keywordsFile.exists()) {
GUIMain.log("Loading keywords...");
KEYWORDS.load();
} else {
if (accountManager.getUserAccount() != null) {
GUIMain.keywordMap.put(accountManager.getUserAccount().getName(), Color.orange);
}
}
GUIMain.log("Loading console commands...");
loadConsoleCommands();//has to be out of the check for files for first time boot
if (Utils.areFilesGood(faceFile.getAbsolutePath())) {
GUIMain.log("Loading custom faces...");
FACES.load();
FaceManager.doneWithFaces = true;
GUIMain.log("Loaded custom faces!");
}
GUIMain.log("Loading default Twitch faces...");
if (Utils.areFilesGood(twitchFaceFile.getAbsolutePath())) {
TWITCH_FACES.load();
}
FaceManager.loadDefaultFaces();
}
/**
* This handles saving all the settings that need saved.
*/
public static void save() {
LAF.save();
WINDOW.save();
if (accountManager.getUserAccount() != null || accountManager.getBotAccount() != null) savePropData(0);
savePropData(1);
if (!SoundEngine.getEngine().getSoundMap().isEmpty()) SOUNDS.save();
if (!FaceManager.faceMap.isEmpty()) FACES.save();
if (!FaceManager.twitchFaceMap.isEmpty()) TWITCH_FACES.save();
TAB_STATE.save();
if (!GUIMain.userColMap.isEmpty()) USER_COLORS.save();
if (GUIMain.loadedCommands()) COMMANDS.save();
if (!GUIMain.keywordMap.isEmpty()) KEYWORDS.save();
if (!donationManager.getDonors().isEmpty()) DONORS.save();
if (!donationManager.getDonations().isEmpty()) DONATIONS.save();
if (!subscriberManager.getSubscribers().isEmpty()) SUBSCRIBERS.save();
saveConCommands();
}
/**
* *********VOIDS*************
*/
public static void loadPropData(int type) {
Properties p = new Properties();
if (type == 0) {//accounts
try {
p.load(new FileInputStream(accountsFile));
String userNorm = p.getProperty("UserNorm", "").toLowerCase();
String userNormPass = p.getProperty("UserNormPass", "");
String status = p.getProperty("CanStatus", "false");
String commercial = p.getProperty("CanCommercial", "false");
if (!userNorm.equals("") && !userNormPass.equals("") && userNormPass.contains("oauth")) {
boolean stat = Boolean.parseBoolean(status);
if (!stat) {
GUIMain.instance.updateStatusOption.setEnabled(false);
GUIMain.instance.updateStatusOption.setToolTipText("Enable \"Edit Title and Game\" in the Authorize GUI to use this feature.");
}
boolean ad = Boolean.parseBoolean(commercial);
if (!ad) {
GUIMain.instance.runAdMenu.setEnabled(false);
GUIMain.instance.runAdMenu.setToolTipText("Enable \"Play Commercials\" in the Authorize GUI to use this feature.");
}
boolean subs = Boolean.parseBoolean(p.getProperty("CanParseSubscribers", "false"));
boolean followed = Boolean.parseBoolean(p.getProperty("CanParseFollowedStreams", "false"));
if (followed) trackFollowers.setValue(true);
accountManager.setUserAccount(new Account(userNorm, new OAuth(userNormPass, stat, ad, subs, followed)));
}
String userBot = p.getProperty("UserBot", "").toLowerCase();
String userBotPass = p.getProperty("UserBotPass", "");
if (!userBot.equals("") && !userBotPass.equals("") && userBotPass.contains("oauth")) {
accountManager.setBotAccount(new Account(userBot, new OAuth(userBotPass, false, false, false, false)));
}
if (accountManager.getUserAccount() != null) {
accountManager.addTask(new Task(null, Task.Type.CREATE_VIEWER_ACCOUNT, null));
}
if (accountManager.getBotAccount() != null) {
accountManager.addTask(new Task(null, Task.Type.CREATE_BOT_ACCOUNT, null));
}
} catch (Exception e) {
GUIMain.log(e);
}
}
if (type == 1) {//defaults
try {
p.load(new FileInputStream(defaultsFile));
settings.forEach(s -> s.load(p));
if (logChat.getValue()) logDir.mkdirs();
GUIMain.log("Loaded defaults!");
} catch (Exception e) {
GUIMain.log(e);
}
}
}
public static void savePropData(int type) {
Properties p = new Properties();
File writerFile = null;
String detail = "";
switch (type) {
case 0:
Account user = accountManager.getUserAccount();
Account bot = accountManager.getBotAccount();
if (user != null) {
OAuth key = user.getOAuth();
p.put("UserNorm", user.getName());
p.put("UserNormPass", key.getKey());
p.put("CanStatus", String.valueOf(key.canSetTitle()));
p.put("CanCommercial", String.valueOf(key.canPlayAd()));
p.put("CanParseSubscribers", String.valueOf(key.canReadSubscribers()));
p.put("CanParseFollowedStreams", String.valueOf(key.canReadFollowed()));
}
if (bot != null) {
OAuth key = bot.getOAuth();
p.put("UserBot", bot.getName());
p.put("UserBotPass", key.getKey());
}
writerFile = accountsFile;
detail = "Account Info";
break;
case 1:
settings.forEach(s -> s.save(p));
writerFile = defaultsFile;
detail = "Defaults/Other Settings";
break;
default:
break;
}
if (writerFile != null) {
try {
p.store(new FileWriter(writerFile), detail);
} catch (Exception e) {
GUIMain.log(e);
}
} else {
GUIMain.log("Failed to store settings due to some unforseen exception!");
}
}
/**
* Sounds
*/
private static class Sounds extends AbstractFileSave {
@Override
public void handleLineLoad(String line) {
String[] split = line.split(",", 3);
String[] split2add = split[2].split(",");//files
int perm = 0;
try {
perm = Integer.parseInt(split[1]);
} catch (NumberFormatException e) {
GUIMain.log(split[0] + " has a problem. Making it public.");
}
SoundEngine.getEngine().getSoundMap().put(split[0].toLowerCase(), new Sound(perm, split2add));
}
@Override
public void handleLineSave(PrintWriter pw) {
Iterator<Map.Entry<String, Sound>> keys = SoundEngine.getEngine().getSoundMap().entrySet().iterator();
StringBuilder sb = new StringBuilder();
while (keys.hasNext()) {
Map.Entry<String, Sound> entry = keys.next();
sb.append(entry.getKey());
sb.append(",");
sb.append(entry.getValue().getPermission());
for (String sound : entry.getValue().getSounds().data) {
sb.append(",");
sb.append(sound);
}
pw.println(sb.toString());
sb.setLength(0);
}
}
@Override
public File getFile() {
return soundsFile;
}
}
/**
* User Colors
*/
public static class UserColors extends AbstractFileSave {
@Override
public void handleLineLoad(String line) {
String[] split = line.split(",");
GUIMain.userColMap.put(split[0], new Color(Integer.parseInt(split[1]), Integer.parseInt(split[2]), Integer.parseInt(split[3])));
}
@Override
public void handleLineSave(PrintWriter pw) {
Iterator<Map.Entry<String, Color>> entries = GUIMain.userColMap.entrySet().iterator();
StringBuilder sb = new StringBuilder();
while (entries.hasNext()) {
Map.Entry<String, Color> entry = entries.next();
sb.append(entry.getKey());
sb.append(",");
sb.append(entry.getValue().getRed());
sb.append(",");
sb.append(entry.getValue().getGreen());
sb.append(",");
sb.append(entry.getValue().getBlue());
pw.println(sb.toString());
sb.setLength(0);
}
}
@Override
public File getFile() {
return userColFile;
}
}
/**
* Normal faces
*/
public static class Faces extends AbstractFileSave {
@Override
public void handleLineLoad(String line) {
String[] split = line.split(",");
// name name/regex path
FaceManager.faceMap.put(split[0], new Face(split[1], split[2]));
}
@Override
public void handleLineSave(PrintWriter pw) {
FaceManager.faceMap.keySet().stream().filter(s -> s != null && FaceManager.faceMap.get(s) != null).forEach(s -> {
Face fa = FaceManager.faceMap.get(s);
pw.println(s + "," + fa.getRegex() + "," + fa.getFilePath());
});
}
@Override
public File getFile() {
return faceFile;
}
}
/**
* Twitch Faces
*/
public static class TwitchFaces extends AbstractFileSave {
@Override
public void handleLineLoad(String line) {
try {
String[] split = line.split(",");
int emoteID = Integer.parseInt(split[0]);
TwitchFace tf = new TwitchFace(split[1],
new File(twitchFaceDir + File.separator + String.valueOf(emoteID) + ".png").getAbsolutePath(),
Boolean.parseBoolean(split[2]));
FaceManager.twitchFaceMap.put(emoteID, tf);
} catch (Exception e) {
GUIMain.log(e);
}
}
@Override
public void handleLineSave(PrintWriter pw) {
FaceManager.twitchFaceMap.keySet().stream().filter(s -> s != null && FaceManager.twitchFaceMap.get(s) != null)
.forEach(s -> {
TwitchFace fa = FaceManager.twitchFaceMap.get(s);
pw.println(s + "," + fa.getRegex() + "," + Boolean.toString(fa.isEnabled()));
});
}
@Override
public File getFile() {
return twitchFaceFile;
}
}
/**
* Commands
* <p>
* trigger[message (content)[arguments?
*/
public static class Commands extends AbstractFileSave {
@Override
public void handleLineLoad(String line) {
String[] split = line.split("\\[");
String[] contents = split[1].split("]");
Command c = new Command(split[0], contents);
if (split.length > 2) {
c.addArguments(split[2].split(","));
}
GUIMain.commandSet.add(c);
}
@Override
public void handleLineSave(PrintWriter pw) {
for (Command next : GUIMain.commandSet) {
if (next != null) {
String name = next.getTrigger();
String[] contents = next.getMessage().data;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < contents.length; i++) {
sb.append(contents[i]);
if (i != (contents.length - 1)) sb.append("]");
}
pw.print(name + "[" + sb.toString());
if (next.hasArguments()) {
pw.print("[");
for (int i = 0; i < next.countArguments(); i++) {
pw.print(next.getArguments().get(i));
if (i != (next.countArguments() - 1)) pw.print(",");
}
}
pw.println();
}
}
}
@Override
public File getFile() {
return commandsFile;
}
}
/**
* Console Commands
*/
public static void saveConCommands() {
try (PrintWriter br = new PrintWriter(ccommandsFile)) {
for (ConsoleCommand next : GUIMain.conCommands) {
if (next != null) {
String name = next.getTrigger();
String action = next.getAction().toString();
int classPerm = next.getClassPermission();
String certainPerm = "null";
if (next.getCertainPermissions() != null) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < next.getCertainPermissions().length; i++) {
sb.append(next.getCertainPermissions()[i]);
if (i != (next.getCertainPermissions().length - 1)) sb.append(",");
}
certainPerm = sb.toString();
}
br.println(name + "[" + action + "[" + classPerm + "[" + certainPerm);
}
}
} catch (Exception e) {
GUIMain.log(e);
}
}
private static ConsoleCommand.Action getAction(String key) {
ConsoleCommand.Action act = null;
for (ConsoleCommand.Action a : ConsoleCommand.Action.values()) {
if (a.toString().equalsIgnoreCase(key)) {
act = a;
break;
}
}
return act;
}
public static void loadConsoleCommands() {
HashSet<ConsoleCommand> hardcoded = new HashSet<>();
hardcoded.add(new ConsoleCommand("addface", ConsoleCommand.Action.ADD_FACE, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("changeface", ConsoleCommand.Action.CHANGE_FACE, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("removeface", ConsoleCommand.Action.REMOVE_FACE, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("toggleface", ConsoleCommand.Action.TOGGLE_FACE, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("addsound", ConsoleCommand.Action.ADD_SOUND, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("changesound", ConsoleCommand.Action.CHANGE_SOUND, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("removesound", ConsoleCommand.Action.REMOVE_SOUND, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("setsound", ConsoleCommand.Action.SET_SOUND_DELAY, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("togglesound", ConsoleCommand.Action.TOGGLE_SOUND, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("stopsound", ConsoleCommand.Action.STOP_SOUND, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("stopallsounds", ConsoleCommand.Action.STOP_ALL_SOUNDS, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("addkeyword", ConsoleCommand.Action.ADD_KEYWORD, Permissions.Permission.BROADCASTER.permValue, null));
hardcoded.add(new ConsoleCommand("removekeyword", ConsoleCommand.Action.REMOVE_KEYWORD, Permissions.Permission.BROADCASTER.permValue, null));
hardcoded.add(new ConsoleCommand("setcol", ConsoleCommand.Action.SET_USER_COL, Permissions.Permission.VIEWER.permValue, null));
hardcoded.add(new ConsoleCommand("setpermission", ConsoleCommand.Action.SET_COMMAND_PERMISSION, Permissions.Permission.BROADCASTER.permValue, null));
hardcoded.add(new ConsoleCommand("addcommand", ConsoleCommand.Action.ADD_TEXT_COMMAND, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("removecommand", ConsoleCommand.Action.REMOVE_TEXT_COMMAND, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("adddonation", ConsoleCommand.Action.ADD_DONATION, Permissions.Permission.BROADCASTER.permValue, null));
hardcoded.add(new ConsoleCommand("setsoundperm", ConsoleCommand.Action.SET_SOUND_PERMISSION, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("setnameface", ConsoleCommand.Action.SET_NAME_FACE, Permissions.Permission.SUBSCRIBER.permValue, null));
hardcoded.add(new ConsoleCommand("removenameface", ConsoleCommand.Action.REMOVE_NAME_FACE, Permissions.Permission.SUBSCRIBER.permValue, null));
hardcoded.add(new ConsoleCommand("playad", ConsoleCommand.Action.PLAY_ADVERT, Permissions.Permission.BROADCASTER.permValue, null));
hardcoded.add(new ConsoleCommand("settitle", ConsoleCommand.Action.SET_STREAM_TITLE, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("title", ConsoleCommand.Action.SEE_STREAM_TITLE, Permissions.Permission.VIEWER.permValue, null));
hardcoded.add(new ConsoleCommand("setgame", ConsoleCommand.Action.SET_STREAM_GAME, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("game", ConsoleCommand.Action.SEE_STREAM_GAME, Permissions.Permission.VIEWER.permValue, null));
hardcoded.add(new ConsoleCommand("startraffle", ConsoleCommand.Action.START_RAFFLE, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("addrafflewinner", ConsoleCommand.Action.ADD_RAFFLE_WINNER, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("stopraffle", ConsoleCommand.Action.STOP_RAFFLE, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("removerafflewinner", ConsoleCommand.Action.REMOVE_RAFFLE_WINNER, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("winners", ConsoleCommand.Action.SEE_WINNERS, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("startpoll", ConsoleCommand.Action.START_POLL, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("vote", ConsoleCommand.Action.VOTE_POLL, Permissions.Permission.VIEWER.permValue, null));
hardcoded.add(new ConsoleCommand("pollresult", ConsoleCommand.Action.POLL_RESULT, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("cancelpoll", ConsoleCommand.Action.CANCEL_POLL, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("song", ConsoleCommand.Action.NOW_PLAYING, Permissions.Permission.VIEWER.permValue, null));
hardcoded.add(new ConsoleCommand("soundstate", ConsoleCommand.Action.SEE_SOUND_STATE, Permissions.Permission.MODERATOR.permValue, null));
hardcoded.add(new ConsoleCommand("uptime", ConsoleCommand.Action.SHOW_UPTIME, Permissions.Permission.VIEWER.permValue, null));
hardcoded.add(new ConsoleCommand("lastsubsound", ConsoleCommand.Action.SEE_PREV_SOUND_SUB, Permissions.Permission.VIEWER.permValue, null));
hardcoded.add(new ConsoleCommand("lastdonationsound", ConsoleCommand.Action.SEE_PREV_SOUND_DON, Permissions.Permission.VIEWER.permValue, null));
hardcoded.add(new ConsoleCommand("botreply", ConsoleCommand.Action.SEE_OR_SET_REPLY_TYPE, Permissions.Permission.BROADCASTER.permValue, null));
hardcoded.add(new ConsoleCommand("volume", ConsoleCommand.Action.SEE_OR_SET_VOLUME, Permissions.Permission.MODERATOR.permValue, null));
if (Utils.areFilesGood(ccommandsFile.getAbsolutePath())) {
try (BufferedReader br = new BufferedReader(new InputStreamReader(ccommandsFile.toURI().toURL().openStream()))) {
String line;
while ((line = br.readLine()) != null) {
String[] split = line.split("\\[");
ConsoleCommand.Action a = getAction(split[1]);
int classPerm;
try {
classPerm = Integer.parseInt(split[2]);
} catch (Exception e) {
classPerm = -1;
}
String[] customUsers = null;
if (!split[3].equalsIgnoreCase("null")) {
customUsers = split[3].split(",");
}
GUIMain.conCommands.add(new ConsoleCommand(split[0], a, classPerm, customUsers));
}
if (GUIMain.conCommands.size() != hardcoded.size()) { //something's not right...
for (ConsoleCommand hard : hardcoded) {
boolean isAdded = false;
for (ConsoleCommand loaded : GUIMain.conCommands) {
if (hard.getTrigger().equalsIgnoreCase(loaded.getTrigger())) {
isAdded = true;
//this ensures every hard coded ConCommand is loaded for Botnak
break;
}
}
if (!isAdded) {//if it isn't in the file, add it
GUIMain.conCommands.add(hard);
}
}
}
} catch (Exception e) {
GUIMain.log(e);
}
} else { //first time boot/reset/deleted file etc.
GUIMain.conCommands.addAll(new ArrayList<>(hardcoded));
}
GUIMain.log("Loaded console commands!");
}
/**
* Keywords
*/
public static class Keywords extends AbstractFileSave {
@Override
public void handleLineLoad(String line) {
String[] split = line.split(",");
int r, g, b;
try {
r = Integer.parseInt(split[1]);
} catch (Exception e) {
r = 255;
}
try {
g = Integer.parseInt(split[2]);
} catch (Exception e) {
g = 255;
}
try {
b = Integer.parseInt(split[3]);
} catch (Exception e) {
b = 255;
}
GUIMain.keywordMap.put(split[0], new Color(r, g, b));
}
@Override
public void handleLineSave(PrintWriter pw) {
Set<String> keys = GUIMain.keywordMap.keySet();
keys.stream().filter(Objects::nonNull).forEach(word ->
{
Color c = GUIMain.keywordMap.get(word);
pw.println(word + "," + c.getRed() + "," + c.getGreen() + "," + c.getBlue());
});
}
@Override
public File getFile() {
return keywordsFile;
}
}
/**
* Donors
*/
public static class Donors extends AbstractFileSave {
@Override
public void handleLineLoad(String line) {
String[] split = line.split(",");
double amount;
try {
amount = Double.parseDouble(split[1]);
} catch (Exception e) {
amount = 0.0;
}
donationManager.getDonors().add(new Donor(split[0], amount));
}
@Override
public void handleLineSave(PrintWriter pw) {
donationManager.getDonors().forEach(d -> pw.println(d.getName() + "," +
DonationManager.getDecimalFormat().format(d.getDonated())));
}
@Override
public File getFile() {
return donorsFile;
}
}
/**
* Donations.
*/
public static class Donations extends AbstractFileSave {
private static HashSet<Donation> donations = new HashSet<>();
private static Donation mostRecent = null;
@Override
public void handleLineLoad(String line) {
String[] split = line.split("\\[");
double amount;
try {
amount = Double.parseDouble(split[3]);
} catch (Exception e) {
amount = 0.0;
}
Donation d = new Donation(split[0], split[1], split[2], amount, Date.from(Instant.parse(split[4])));
if ((mostRecent == null || mostRecent.compareTo(d) > 0) && !d.getDonationID().equals("LOCAL"))
mostRecent = d;
donations.add(d);
}
@Override
public void handleLineSave(PrintWriter pw) {
donationManager.getDonations().stream().sorted().forEach(d ->
pw.println(d.getDonationID() + "[" + d.getFromWho() + "[" + d.getNote() + "["
+ DonationManager.getDecimalFormat().format(d.getAmount()) + "["
+ Instant.ofEpochMilli(d.getDateReceived().getTime()).toString()));
}
@Override
public File getFile() {
return donationsFile;
}
}
/**
* Subscribers of your own channel
* <p>
* Saves each subscriber with the first date Botnak meets them
* and each month check to see if they're still subbed, if not, make them an ex-subscriber
*/
private static class Subscribers extends AbstractFileSave {
private static HashSet<Subscriber> subscribers = new HashSet<>();
private static Subscriber mostRecent = null;
@Override
public void handleLineLoad(String line) {
String[] split = line.split("\\[");
Subscriber s = new Subscriber(split[0], LocalDateTime.parse(split[1]), Boolean.parseBoolean(split[2]), Integer.parseInt(split[3]));
subscribers.add(s);
if (mostRecent == null || mostRecent.compareTo(s) > 0) mostRecent = s;
}
@Override
public void handleLineSave(PrintWriter pw) {
subscriberManager.getSubscribers().stream().sorted().forEach(
s -> pw.println(s.getName() + "[" + s.getStarted().toString() + "[" + String.valueOf(s.isActive())
+ "[" + s.getStreak()));
}
@Override
public File getFile() {
return subsFile;
}
}
/**
* Tab State
* <p>
* This is for opening back up to the correct tab, with the correct pane showing.
* This also handles saving the combined tabs.
* Format:
* <p>
* Single:
* bool bool string
* single[ selected[ name
* <p>
* Combined:
* bool boolean String String String[] (split(","))
* single[ selected[ activeChannel[ title[ every,channel,in,it,separated,by,commas
* <p>
* No spaces though.
* <p>
* Single tabs can be invisible if they are in a combined tab.
*/
private static class TabState extends AbstractFileSave {
public static int startIndex = 0;
@Override
public void handleLineLoad(String line) {
String[] split = line.split("\\[");
boolean isSingle = Boolean.parseBoolean(split[0]);
boolean isSelected = Boolean.parseBoolean(split[1]);
if (isSingle) {
String channel = split[2];
if (accountManager.getUserAccount() != null) {
String channelName = "#" + channel;
GUIMain.channelSet.add(channelName);
}
ChatPane cp = ChatPane.createPane(channel);
GUIMain.chatPanes.put(cp.getChannel(), cp);
GUIMain.channelPane.insertTab(cp.getChannel(), null, cp.getScrollPane(), null, cp.getIndex());
if (isSelected) startIndex = cp.getIndex();
} else {
String activeChannel = split[2];
String title = split[3];
String[] channels = split[4].split(",");
ArrayList<ChatPane> cps = new ArrayList<>();
for (String c : channels) {
if (accountManager.getUserAccount() != null) {
String channelName = "#" + c;
GUIMain.channelSet.add(channelName);
}
ChatPane cp = ChatPane.createPane(c);
GUIMain.chatPanes.put(cp.getChannel(), cp);
cps.add(cp);
}
CombinedChatPane ccp = CombinedChatPane.createCombinedChatPane(cps.toArray(new ChatPane[cps.size()]));
GUIMain.channelPane.insertTab(ccp.getTabTitle(), null, ccp.getScrollPane(), null, ccp.getIndex());
ccp.setCustomTitle(title);
if (isSelected) startIndex = ccp.getIndex();
if (!activeChannel.equalsIgnoreCase("all")) {
ccp.setActiveChannel(activeChannel);
ccp.setActiveScrollPane(activeChannel);
}
GUIMain.combinedChatPanes.add(ccp);
}
}
@Override
public void handleLineSave(PrintWriter pw) {
int currentSelectedIndex = GUIMain.channelPane.getSelectedIndex();
for (int i = 1; i < GUIMain.channelPane.getTabCount() - 1; i++) {
ChatPane current = Utils.getChatPane(i);
if (current != null) {
if (current.isTabVisible()) {
boolean selected = current.getIndex() == currentSelectedIndex;
pw.println("true[" + String.valueOf(selected) + "[" + current.getChannel());
}
} else {
CombinedChatPane cc = Utils.getCombinedChatPane(i);
if (cc != null) {
//all the panes in them should be set to false
//their indexes are technically going to be -1
boolean selected = cc.getIndex() == currentSelectedIndex;
pw.print("false[" + String.valueOf(selected) + "[" + cc.getActiveChannel() + "[" + cc.getTabTitle() + "[");
String[] chans = cc.getChannels();
for (int s = 0; s < chans.length; s++) {
String toPrint = chans[s];
pw.print(toPrint);
if (s != chans.length - 1) pw.print(",");
}
pw.println();
}
}
}
}
@Override
public File getFile() {
return tabsFile;
}
}
/**
* Look and Feel
*/
public static class LookAndFeel extends AbstractFileSave {
@Override
public void handleLineLoad(String line) {
if (line.contains("jtattoo")) lookAndFeel = line;
}
@Override
public void handleLineSave(PrintWriter pw) {
pw.println(lookAndFeel);
}
@Override
public File getFile() {
return lafFile;
}
}
/**
* Window Location and Size
*/
private static class Window extends AbstractFileSave {
@Override
public void handleLineLoad(String line) {
String[] parts = line.substring(1).split(",");
String first = parts[0];
String second = parts[1];
if (line.startsWith("p")) {
try {
int x = Integer.parseInt(first);
int y = Integer.parseInt(second);
GUIMain.instance.setLocation(x, y);
} catch (Exception e) {
GUIMain.log(e);
}
} else if (line.startsWith("s")) {
try {
double w = Double.parseDouble(first);
double h = Double.parseDouble(second);
GUIMain.instance.setSize((int) w, (int) h);
} catch (Exception e) {
GUIMain.log(e);
}
}
}
@Override
public void handleLineSave(PrintWriter pw) {
pw.println("p" + GUIMain.instance.getLocationOnScreen().x + "," + GUIMain.instance.getLocationOnScreen().y);
pw.println("s" + GUIMain.instance.getSize().getWidth() + "," + GUIMain.instance.getSize().getHeight());
}
@Override
public File getFile() {
return windowFile;
}
}
//Other methods that don't follow standardization
/**
* Name faces
*/
public static void loadNameFaces(File[] nameFaces) {
try {
for (File nameFace : nameFaces) {
if (nameFace.isDirectory()) continue;
String name = Utils.removeExt(nameFace.getName());
FaceManager.nameFaceMap.put(name, new Face(name, nameFace.getAbsolutePath()));
}
GUIMain.log("Loaded name faces!");
} catch (Exception e) {
GUIMain.log(e);
}
}
private static void doLoadSubSounds() {
loadSubSounds();
loadedSubSounds = true;
GUIMain.log("Loaded sub sounds!");
}
public static void loadSubSounds() {
loadShuffleSet(subSoundDir, SoundEngine.getEngine().getSubStack());
}
private static void doLoadDonationSounds() {
loadDonationSounds();
loadedDonationSounds = true;
GUIMain.log("Loaded donation sounds!");
}
public static void loadDonationSounds() {
loadShuffleSet(donationSoundDir, SoundEngine.getEngine().getDonationStack());
}
private static void loadShuffleSet(File directory, Deque<Sound> stack) {
try {
File[] files = directory.listFiles();
if (files != null && files.length > 0) {
ArrayList<Sound> sounds = new ArrayList<>();
for (File f : files) {
sounds.add(new Sound(5, f.getAbsolutePath()));
}
Collections.shuffle(sounds);
stack.addAll(sounds);
}
} catch (Exception e) {
GUIMain.log(e);
}
}
/**
* Sub icons
*/
public static void loadSubIcons(File[] subIconFiles) {
for (File f : subIconFiles) {
FaceManager.subIconSet.add(new SubscriberIcon(Utils.removeExt(f.getName()), f.getAbsolutePath()));
}
}
/**
* FrankerFaceZ
* <p>
* We can be a little more broad about this saving, since it's a per-channel basis
*/
public static void loadFFZFaces(File[] channels) {
for (File channel : channels) {
if (channel.isDirectory() && channel.length() > 0) {
File[] faces = channel.listFiles();
if (faces != null) {
ArrayList<FrankerFaceZ> loadedFaces = new ArrayList<>();
for (File face : faces) {
if (face != null) {
loadedFaces.add(new FrankerFaceZ(face.getName(), face.getAbsolutePath(), true));
}
}
FaceManager.ffzFaceMap.put(channel.getName(), loadedFaces);
} else {
channel.delete();
}
}
}
}
}