Page MenuHomedesp's stash

Settings.java
No OneTemporary

Settings.java

package me.despawningbone.discordbot.command.admin;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
import org.apache.commons.lang3.exception.ExceptionUtils;
import me.despawningbone.discordbot.DiscordBot;
import me.despawningbone.discordbot.command.Command;
import me.despawningbone.discordbot.command.CommandResult;
import me.despawningbone.discordbot.command.CommandResult.CommandResultType;
import me.despawningbone.discordbot.utils.MiscUtils;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.Permission;
public class Settings extends Command {
public static final int DISABLED = 0x80000; //even if i use 0x80000000 and it overflows it should still work //DONE 0x200 is a new permission; JDA 3 will probably not recognize it so i have to circumvent over it manually //changed to 0x80000
private static final List<String> disableExempt = Arrays.asList("admin", "admin.settings", "admin.settings.perms");
public Settings() {
this.alias = Arrays.asList("setting", "set");
this.desc = "Change the settings of the bot for this guild!";
this.usage = "<subcommands>";
this.botUserLevel = -BotUserLevel.BOT_OWNER.ordinal();
this.perms = EnumSet.of(Permission.MANAGE_SERVER);
//PATCHED? SQL INJECTION PRONE (eg node: games/**/WHERE1--.",*," - returns numberformatexception; games/**/WHERE1--.osu",*,"osu returns N/A; can even drop tables)
registerSubCommand("perms", Arrays.asList("permissions", "perm", "permission"), (channel, user, msg, words) -> { //DONE disable commands; use negative? //TODO list all edited permission nodes, clear command?
int opType = 3;
try {
if(words[0].equalsIgnoreCase("list")) {
if(words.length != 2) throw new ArrayIndexOutOfBoundsException(); //invalid argument count
opType = 0;
} else if(words[0].equalsIgnoreCase("wipe") || words[0].equalsIgnoreCase("wipeall")){
opType = 1;
} else {
if(words.length != 3) throw new ArrayIndexOutOfBoundsException(); //invalid argument count
opType = words[0].equalsIgnoreCase("guild") ? 2 : 3;
}
} catch(ArrayIndexOutOfBoundsException e) {
return new CommandResult(CommandResultType.INVALIDARGS, "Invalid argument count specified.");
}
if(!words[1].matches("[a-zA-Z0-9.]+")) return new CommandResult(CommandResultType.INVALIDARGS, "Invalid node specified."); //prevent SQL injection; might be restricting though, but then again all command names are alphanumeric
try(Connection con = DiscordBot.db.getConnection(); Statement s = con.createStatement()){
String node = words[1].contains(".") ? words[1].substring(words[1].indexOf(".") + 1).toLowerCase() : "_GLOBAL_";
String cat = node.equals("_GLOBAL_") ? words[1].toLowerCase() : words[1].substring(0, words[1].indexOf(".")).toLowerCase();
ResultSet rs = null;
if(opType == 3) try {
if(channel.getGuild().getTextChannelById(words[0]) == null) throw new NumberFormatException();
} catch(NumberFormatException e) {
if(words[0].startsWith("#")) {
if(!msg.getMentions().getChannels().isEmpty()) {
words[0] = msg.getMentions().getChannels().get(0).getId(); //assuming its to order from left to right; but tbh if they try to trick the command with 2 channels it wont work anyways with the range check and node check
}
} else {
return new CommandResult(CommandResultType.INVALIDARGS, "Invalid channel specified.");
}
}
try {
rs = s.executeQuery("SELECT \"" + node + "\" FROM perms_" + cat + " WHERE id = " + channel.getGuild().getId() + ";"); //hopefully this will be pretty fast
} catch(SQLException x) {
return new CommandResult(CommandResultType.INVALIDARGS, "Invalid node specified.");
}
EnumSet<Permission> def = null;
if(node.contains(".")) { //path must be valid coz or else SQL wouldve thrown an exception
String[] split = node.split("\\.");
Command subCmd = DiscordBot.commands.get(split[0]);
for(int i = 1; i < split.length; i++) {
subCmd = subCmd.getSubCommand(split[i]);
}
def = subCmd.getDefaultPerms();
} else {
def = DiscordBot.commands.containsKey(node) ? DiscordBot.commands.get(node).getDefaultPerms() : EnumSet.noneOf(Permission.class);
}
if(opType == 0) { //list perms
EmbedBuilder eb = new EmbedBuilder();
eb.setTitle("Permissions for node: " + words[1]);
eb.appendDescription("*Default: " + (def.isEmpty() ? "N/A" : def.stream().map(p -> p.name()).collect(Collectors.joining(", "))) + "*\n\n");
if(rs.next()) {
String sOrig = rs.getString(1);
if(sOrig.equals(node)) return new CommandResult(CommandResultType.INVALIDARGS, "Invalid node specified."); //SQL returns the actual string if the table doesnt have the column
for(String line : sOrig.split("\n")) {
String[] split = line.split(":");
int index = 0;
if(split.length < 2) { //channels would have colons
eb.appendDescription("**Global permissions:** \n");
} else {
eb.appendDescription("<#" + split[0] + ">:\n"); //TODO low priority: old permissions set for deleted channels will still be there
index = 1;
}
long orig = Long.parseLong(split[index]);
if(orig != 0) {
long deny = (int) (orig >> 32), allow = (int) orig;
if(MiscUtils.hasDisabled(deny)) eb.appendDescription("**-DISABLED**\n");
for(Permission p : Permission.getPermissions(deny)) eb.appendDescription("-" + p.name() + "\n");
if(MiscUtils.hasDisabled(allow)) eb.appendDescription("**DISABLED**\n");
for(Permission p : Permission.getPermissions(allow)) eb.appendDescription(p.name() + "\n");
} else {
eb.appendDescription("N/A\n");
}
eb.appendDescription("\n");
}
} else { //edit perms
eb.appendDescription("Global permissions: \n");
if(!def.isEmpty()) for(Permission p : def) eb.appendDescription(p.name() + "\n");
else eb.appendDescription("N/A\n");
}
eb.setFooter("To see the permissions in effect, do the help command in an affected channel!", null);
channel.sendMessageEmbeds(eb.build()).queue();
return new CommandResult(CommandResultType.SUCCESS);
} else if(opType == 1) { //wipe
String actualNode = words[1].toLowerCase();
if(words[0].equalsIgnoreCase("wipeall")) {
s.execute("DELETE FROM perms_" + cat + " WHERE id = " + channel.getGuild().getId() + ";");
actualNode = cat;
} else {
s.execute("UPDATE perms_" + cat + " SET \"" + node + "\" = '" + ((0L << 32) | (Permission.getRaw(def) & 0xffffffffL)) + "\n'");
}
channel.sendMessage("Successfully reset `" + actualNode + "` to default permissions.").queue();
return new CommandResult(CommandResultType.SUCCESS);
} else {
boolean allows = words[2].indexOf("-") == -1;
Permission perm = null;
try {
String p = allows ? words[2].toUpperCase() : words[2].substring(1).toUpperCase();
if(!p.equals("DISABLED")) perm = Permission.valueOf(p);
if(perm == null && disableExempt.contains(words[1].toLowerCase())) return new CommandResult(CommandResultType.INVALIDARGS, "You cannot disable this node."); //temporary patch
} catch(IllegalArgumentException e) {
return new CommandResult(CommandResultType.INVALIDARGS, "Invalid permission specified.");
}
String[] lines = {""};
String type = "";
if(rs.next()) { //considerations: rs.next(); guild vs channel; allows; add or remove;
lines = rs.getString(1).split("\n");
if(lines[0].equals(node)) return new CommandResult(CommandResultType.INVALIDARGS, "Invalid node specified."); //SQL returns the actual string if the table doesnt have the column
int i = 0;
long orig = 0;
if(opType == 2) {
orig = Long.parseLong(lines[0]);
} else {
try {
for(i = 0; i <= lines.length; i++) {
if(lines[i].startsWith(words[0] + ":")) {
orig = Long.parseLong(lines[i].split(":")[1]);
break;
}
}
} catch(ArrayIndexOutOfBoundsException e) {
lines = Arrays.copyOf(lines, lines.length + 1); //to extend it; should be the same method as using arraylist
//orig = Permission.getRaw(def); //use default //actually should already have imposed global default if rs has next; adding default to this will only give channel the global perm, which makes no sense
}
}
String[] split = imposePerms(words[0].equalsIgnoreCase("guild") ? null : words[0], orig, perm, allows);
type = split[0]; lines[i] = split[1]; //at this stage theres only 2 options: guild and a valid channel id
} else {
if(opType == 2) {
String[] split = imposePerms(null, Permission.getRaw(def), perm, allows); //initiate guild by imposing perm to default; since allow is in the lower 32 bit, i can just put def as the base
type = split[0]; lines[0] = split[1];
} else {
lines[0] = String.valueOf(Permission.getRaw(def)); //initiate guild with the default permission of the command
lines = Arrays.copyOf(lines, lines.length + 1);
String[] split = imposePerms(words[0], 0, perm, allows); //impose with perm base = 0
type = split[0]; lines[1] = split[1];
}
}
String fin = String.join("\n", lines) + "\n"; //last line must have \n
s.execute("INSERT INTO perms_" + cat + "(id, \"" + node + "\") VALUES (" + channel.getGuild().getId() + ", '" + fin + "') ON CONFLICT(id) DO UPDATE SET \"" + node + "\" = '" + fin + "';"); //no need blank update check, because it will never be the same
channel.sendMessage("Successfully " + type + " " + (words[0].equalsIgnoreCase("guild") ? "guild-wide" : "channel-specific (<#" + words[0] + ">)") + " permission `" + (allows ? "" : "-") + (perm == null ? "DISABLED" : perm.name()) + "` for `" + words[1].toLowerCase() + "`.").queue();
return new CommandResult(CommandResultType.SUCCESS);
}
} catch(SQLException e) {
return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e));
}
}, "<channel|guild|list|wipe|wipeall> <command node> [-][permission]", Arrays.asList("list admin", "list games.osu.pp", "wipeall admin", "wipe games.osu", "419464253574086656 games MESSAGE_EMBED_LINKS", "guild games.osu -MESSAGE_EMBED_LINKS", "#general music VOICE_CONNECT", "guild admin ADMINISTRATOR"),
"Edit default perms required for commands for this guild!", Arrays.asList(
"Adding a `-` sign before a permission overrides the parent permission set,",
"Where `cat(guild > channel) > cmd(guild > channel) > subcmd(guild > channel)` (parent > child, where subcmd channel is of highest priority).",
"*For example, specifying `admin.settings.prefix` with `-MANAGE_SERVER` will override the requirement of `admin.settings`, and allow those without `MANAGE_SERVER` to use `admin.settings.prefix`*.\n",
"The permission nodes are as such: `<cat>[.cmd][.subcmd]`.",
"For permission names, please refer to https://ci.dv8tion.net/job/JDA/javadoc/net/dv8tion/jda/core/Permission.html,",
"Along with a special permission `DISABLED` for disabling the node.",
"Specifying the permission twice will remove it.\n",
"Use `wipe` if you want to reset the permissions for the node, and `wipeall` for resetting all perms in the whole category.",
"`list` and `wipe`/`wipeall` only accepts the first 2 parameters."), EnumSet.of(Permission.ADMINISTRATOR), -BotUserLevel.BOT_OWNER.ordinal());
registerSubCommand("prefix", Arrays.asList("pre"), (channel, user, msg, words) -> { //dont allow prefixes with markdown for now
String prefix;
if(words.length < 1) {
prefix = DiscordBot.prefix;
channel.sendMessage("No prefix entered. Using default prefix...").queue();
} else {
prefix = msg.getContentStripped().substring(msg.getContentDisplay().lastIndexOf(String.join(" ", words))).replaceAll("\\\\", ""); //if prefix/shortcut contains markdown it breaks
}
if(prefix.length() > 30) return new CommandResult(CommandResultType.INVALIDARGS, "Please do not enter prefixes that are too long."); //arbitrarily set; can change anytime
try (Connection con = DiscordBot.db.getConnection()) {
PreparedStatement s = con.prepareStatement("INSERT INTO settings(id, prefix) VALUES (" + channel.getGuild().getId() + ", ?) ON CONFLICT(id) DO UPDATE SET prefix = ? WHERE prefix <> ?");
s.setString(1, prefix); s.setString(2, prefix); s.setString(3, prefix); //using prep statement to prevent injection
int update = s.executeUpdate();
s.close();
if(update != 0) { //should ever only be 1 or 0
channel.sendMessage("Successfully changed prefix to `" + prefix + "`. Do " + prefix + "help to see the new command syntaxes.").queue();
} else {
return new CommandResult(CommandResultType.INVALIDARGS, "You are setting the same prefix!");
}
return new CommandResult(CommandResultType.SUCCESS);
} catch(SQLException e) {
return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e));
}
}, "[new prefix]", Arrays.asList("`!desp `", "!", "-"),
"Change the prefix for this guild!", Arrays.asList("*You can also include spaces with the use of `code block`s.*", "Leave blank to use the default (`" + DiscordBot.prefix + "`)."));
}
private String[] imposePerms(String cId, long base, Permission perm, boolean allows) {
long allow = (int) base;
long deny = base >> 32;
String type; long newRaw = allows ? allow : deny;
if(perm == null) { //only disabled is null; since Permission doesnt have DISABLED mapped, i have to do it manually
type = MiscUtils.hasDisabled(newRaw) ? "removed" : "added";
if(type.equals("removed")) {
newRaw &= ~DISABLED;
} else {
newRaw |= DISABLED;
}
} else {
List<Permission> perms = new ArrayList<>(Permission.getPermissions(newRaw));
if(perms.contains(perm)) {
perms.remove(perm);
type = "removed";
} else {
perms.add(perm);
type = "added";
}
newRaw = Permission.getRaw(perms) | (MiscUtils.hasDisabled(newRaw) ? DISABLED : 0); //add back as perms remove it
}
if(allows) allow = newRaw; else deny = newRaw;
return new String[]{type, (cId == null ? "" : cId + ":") + ((deny << 32) | (allow & 0xffffffffL))};
}
}

File Metadata

Mime Type
text/x-java
Expires
Sun, Aug 3, 4:09 AM (49 m, 31 s)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
3c/e9/264aafed5f8c5aa401141f0fecb5

Event Timeline