//public static List<String> BannedID = new ArrayList<String>(); //DONE store in DB
public static List<String> ModID = new ArrayList<String>();
public static List<String> logExcemptID = new ArrayList<String>();
//TODO synchronize below or change to concurrent //but why? its thread safe since its read only
public static HashMap<String, Command> commands = new HashMap<String, Command>();
public static HashMap<String, Command> aliases = new HashMap<String, Command>(); //TODO temporary solution
public static TreeMap<String, List<Command>> catCmds = new TreeMap<String, List<Command>>(); //order base on categories?
public static ConcurrentHashMap<String, TextChannel> lastMusicCmd = new ConcurrentHashMap<String, TextChannel>(); //TODO store in guild configs //nah its fine
//public static HashMap<String, Game> guildMemberPresence = new HashMap<String, Game>();
public static final String prefix = "!desp "; //DONE allow guild change prefix?
public static JDA mainJDA = null;
public static final String BotID = "311086271642599424";
public static final String OwnerID = "237881229876133888";
public static Properties tokens = new Properties();
static final Logger logger = LoggerFactory.getLogger(DiscordBot.class); //package private
public static HikariDataSource db;
/*public static FirefoxDriver driver;*/
// DONE SQLite integration; check if program termination will screw up the connection
// TODO SHARDING
// DONE on shutdown alert those playing music or await? //alerted
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> { //no need to be a global variable since i dont need to access it
try {
FileOutputStream out = new FileOutputStream(System.getProperty("user.dir") + File.separator + "testAnimePicCache.bin");
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(EventListener.testAnimePicCache);
oos.flush();
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}, 1, 5, TimeUnit.MINUTES);*/
// DEPRECATED //or is it? make unmodifiable instead
- ModID.add("165403578133905408");
- ModID.add("187714189672841216");
- ModID.add("204587986724192257"); // hyper
- ModID.add("194112515859283968"); // bond
- ModID.add("214270819793108993"); // emma
- ModID.add("209883960522702848"); // M4
- ModID.add("237546287388426242"); // haoiscoll
- ModID.add("264612287048843264"); // kanade
- ModID.add("254509414600409088"); // shii
+ for(String id : tokens.getProperty("botmod").split(",")) {
+ ModID.add(id);
+ }
ModID.add(OwnerID);
logExcemptID.add(BotID);
}
private static void initDB() { //init guild settings when do !desp settings for the first time? //dont really need coz im doing upsert for all values anyways
HikariConfig dbConf = new HikariConfig();
dbConf.setJdbcUrl("jdbc:sqlite:data.db");
dbConf.setIdleTimeout(45000);
dbConf.setMaxLifetime(60000);
dbConf.setMaximumPoolSize(10);
//dbConf.setMaximumPoolSize(25); //for deployment in server
db = new HikariDataSource(dbConf);
try (Connection con = db.getConnection()){
Statement s = con.createStatement();
s.execute("CREATE TABLE IF NOT EXISTS settings"
+ "(id INTEGER PRIMARY KEY," //performance problem using text; integer can handle long anyways
+ "prefix TEXT DEFAULT '!desp ',"
+ "premium INTEGER DEFAULT 0,"
+ "mchannel TEXT," //allow people to set this manually? By default, use last music cmd place
+ "locale TEXT DEFAULT 'EN'," //or int? //user specific or guild specific, or user override guild?
+ "shortcuts TEXT," //use another method?
+ "votepct INTEGER DEFAULT 50,"
+ "looplimit INTEGER DEFAULT 1,"
+ "volume INTEGER DEFAULT 100," //premium?, is default actually 100?
+ "helpdm INTEGER DEFAULT 0);"); //send help to dm or not, excluding cmd help(?)
//add logexempt?
PreparedStatement pragma = con.prepareStatement("SELECT name FROM pragma_table_info('perms_' || ?);"); //NOTE: sqlite does not support changing default values, beware when adding new commands
String create = "CREATE TABLE perms_" + entry.getKey().toLowerCase() + " (id INTEGER PRIMARY KEY, _GLOBAL_ TEXT DEFAULT '0\n', "; //perms for a category are always default no restraints
join.add("\"" + node.getKey() + "\" TEXT DEFAULT '" + ((0L << 32) | (Permission.getRaw(node.getValue()) & 0xffffffffL)) + "\n'"); //initialize with no permission deny override, permissions allow //needs to be text so that channel overrides delimiter can be stored
}
create += join.toString() + ");";
s.execute(create);
//DONE? create table
} else {
if(!nodes.isEmpty()) { //which means there is new subCmd/cmd
s.execute("ALTER TABLE perms_" + entry.getKey().toLowerCase() + " ADD COLUMN \"" + node.getKey() + "\" TEXT DEFAULT '" + ((0L << 32) | (Permission.getRaw(node.getValue()) & 0xffffffffL)) + "\n';"); //not able to prep statement coz its column name
s.close();
}
}
}
}
pragma.close();
//DONE TEST generate the nodes, retrieve default from fields? //what about additional commands added, use ALTER TABLE ADD COLUMN DEFAULT? I dont need to sort it according to category at all, i am not and will not be bulk printing permissions anywhere //actually i might be coz of list edited perms
//fit subnodes into sub tables, split by category so its more balanced (category as table name)?
//fields are gonna be sth like <channelid>:<perm>|<perm> etc?
//DONE store the permissions with long merged; <32bit = deny, >32bit = allow
s.execute("CREATE TABLE IF NOT EXISTS users"
+ "(id INTEGER PRIMARY KEY,"
+ "reports TEXT DEFAULT '',"
+ "game TEXT);"); //disposable, i can change it anytime
//add botmod field? if so, i can deprecate the hard coded list, but it would require querying to this table on perm checks //i need to query it to check if the user is banned anyways //subcmd need to query again, so i didnt do it; besides the modlist is gonna be too small to be significant anyways
//add user specific locale? might be confusing
s.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
private static void initCmds() {
Reflections reflections = new Reflections("me.despawningbone.discordbot.command");
pos.add(pod.getInt("position")); //update to prevent dupes
}
}
break;
+ case "futureTopic":
+ result.complete(new CommandResult(CommandResultType.FAILURE, "This query was identified under the topic of `" + resp.getJSONObject("futureTopic").getString("topic") + "`; Development of this topic is under investigation..."));
+ break;
case "didyoumean":
- result.complete(new CommandResult(CommandResultType.INVALIDARGS, "No results found :cry:\nDid you mean `" + resp.getJSONArray("didyoumean").getJSONObject(0).getString("val") + "`?"));
+ result.complete(new CommandResult(CommandResultType.INVALIDARGS, "No good results found :cry:\nDid you mean `" + resp.getJSONArray("didyoumean").getJSONObject(0).getString("val") + "`?"));
registerSubCommand("removetrack", Arrays.asList("rt", "r", "skiptrack", "st"), (c, u, m, a) -> {
if(a.length > 0) {
try {
int num = Integer.parseInt(a[0]);
if(num == 1) return new CommandResult(CommandResultType.INVALIDARGS, "You cannot remove the current track from the queue with this command. Use !desp music skip instead.");
if(num < 1) return new CommandResult(CommandResultType.INVALIDARGS, "The index you entered is invalid.");
return new CommandResult(CommandResultType.INVALIDARGS, "Please valid indices.");
}
}, "<fromindex> <toindex>", Arrays.asList("2 3"), "Move a track to the specified index.", Arrays.asList(" * you can also run this command regardless of perms if you are alone with the bot.")
c.sendMessage("Successfully shuffled the queue.").queue();
return new CommandResult(CommandResultType.SUCCESS);
} else {
return new CommandResult(CommandResultType.INVALIDARGS, "You are currently not alone in the music channel, therefore this feature is disabled.");
}
}, "", null, "Shuffle the tracks in the queue when you are alone!", null);
//TODO make vote system for this
registerSubCommand("clear", Arrays.asList("disconnect", "dc", "stop", "clearqueue"), (c, u, m, a) -> {
handler.stopAndClearQueue(c.getGuild());
c.sendMessage("The queue has been cleared.").queue();
return new CommandResult(CommandResultType.SUCCESS);
}, "", null, "Clear the queue and stop the player.", Arrays.asList(" * you can also run this command regardless of perms if you are alone with the bot.")
return new CommandResult(CommandResultType.FAILURE, erMsg);
} catch(NullPointerException e) {
return new CommandResult(CommandResultType.INVALIDARGS, "There is nothing playing currently! Please specify a song title to search the lyrics up.");
}
}, "[search words]", Arrays.asList(""), "Search some lyrics up!", Arrays.asList(" * if you did not specify any search words, the bot will try to fetch the lyrics of the current track playing, if any."));
//TODO equalizer persistence?
//make this premium?
registerSubCommand("equalizer", Arrays.asList("eq"), (c, u, m, a) -> {
GuildMusicManager mm = handler.getGuildMusicManager(c.getGuild());
Float[] vals;
if(a.length == 0) {
vals = mm.getCurrentGain();
} else {
try {
try {
if(a.length != 15) throw new NumberFormatException();
vals = new Float[15];
for(int i = 0; i < a.length; i++) {
vals[i] = Float.parseFloat(a[i]);
}
mm.setGain(0, vals);
} catch(NumberFormatException e) {
vals = mm.setPresetGain(a[0]);
}
} catch(IllegalArgumentException e2) {
return new CommandResult(CommandResultType.INVALIDARGS, e2.getMessage());
}
}
//formatting
DecimalFormat df = new DecimalFormat("0.00");
df.setPositivePrefix("+");
EmbedBuilder eb = new EmbedBuilder();
eb.setTitle("Current equalizer graph");
eb.appendDescription("```\n");
for(double line = 0.25; line >= -0.25; line -= 0.05) {
return new CommandResult(CommandResultType.SUCCESS);
}, "[bandvalues/presetname]", Arrays.asList("", "0.09 0.07 0.07 0.01 0 0 -0.02 -0.02 0.03 0.03 0.05 0.07 0.09 0.1 0.1", "bassboost"), "Sets the equalizer for the music player in this guild!",
Arrays.asList("Accepts 15 bands with values ranging from -0.25 to 0.25, where -0.25 is muted and 0.25 is double volume.", "* presets include: `bassboost`, `default`, `rock`.", "Input nothing to return the current settings.", "",
"Note: you might experience some audio cracking for band values >0.1, since amplifying volumes remotely does not work well.", "It is recommended to use values from -0.25 to 0.1, and turning discord volume up instead.")
if(Arrays.asList("move", "clear").contains(sub.getName())) { //should always be connected to vc
if(channel.getGuild().getAudioManager().getConnectedChannel().getMembers().size() == 2) { //alone; no need to check if the other member is requester, since its checked before
CommandResult res = super.execute(channel, author, msg, args); //pass to subcommand handler for perms checking
if(res.getResultType() == CommandResultType.NOPERMS && Arrays.asList("forceskip", "skip", "setposition", "removetrack").contains(sub.getName())) { //add back info to command result
res = new CommandResult(CommandResultType.INVALIDARGS, res.getMessage().getContentRaw().split(" to execute")[0] + ", or have requested the track to execute this command.");
}
return res;
}
private final String geniusAuth = DiscordBot.tokens.getProperty("genius");
//private ExecutorService executor = Executors.newCachedThreadPool(); //unfortunately i need to finish the api search to get the url to start the html scrape, which means i cannot use future for multithread here
public List<MessageEmbed> getLyrics(String search) { //DONE dont splice words between embeds; make whole sentence to be spliced instead
//System.out.println(search);
List<MessageEmbed> em = new ArrayList<MessageEmbed>();
private void setFinalLyricsEmbed(EmbedBuilder eb, JSONObject main, Element href) { //change the format for the supplementary info in artists and albums from italic to sth else?
//System.out.println("test");
try {
String name = main.getJSONObject("primary_artist").getString("name");
JSONObject preload = new JSONObject(StringEscapeUtils.unescapeJson(temp));
JSONObject song = preload.getJSONObject("entities").getJSONObject("songs").getJSONObject(Integer.toString(preload.getJSONObject("songPage").getInt("song")));
StringBuilder sb = new StringBuilder();
try {
String album = IntStream.range(0, song.getJSONArray("trackingData").length()).mapToObj(i -> song.getJSONArray("trackingData").getJSONObject(i)).filter(obj -> obj.getString("key").equals("Primary Album")).findFirst().get().getString("value").trim();