Done #3 (ACL), still need testing though
parent
0074f64537
commit
86ab3f8490
@ -0,0 +1,56 @@
|
|||||||
|
package net.pingex.discordbot;
|
||||||
|
|
||||||
|
import sx.blah.discord.handle.impl.events.MessageReceivedEvent;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enum describing the default behavior of a command when no specific permission override is given.
|
||||||
|
* @version 0.1-dev
|
||||||
|
* @author Raphael "Pingex" NAAS
|
||||||
|
*/
|
||||||
|
public enum DefaultPermission
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Everyone can call the command
|
||||||
|
*/
|
||||||
|
EVERYONE(e -> true),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only the owner of the guild where command was run can run the command
|
||||||
|
*/
|
||||||
|
GUILD_OWNER(e -> e.getMessage().getGuild().getOwnerID().equals(e.getMessage().getAuthor().getID())),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only the bot owner can run the command. Bot owner can be defined in the configuration file
|
||||||
|
*/
|
||||||
|
BOT_OWNER(e -> e.getMessage().getAuthor().getID().equals(Configuration.getValue("general", "owner"))),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nobody can run the command
|
||||||
|
*/
|
||||||
|
NONE(e -> false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The predicate for each enum value defined above
|
||||||
|
*/
|
||||||
|
private Predicate<MessageReceivedEvent> predicate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quick constructor, no further explanation needed
|
||||||
|
* @param p Input Predicate
|
||||||
|
*/
|
||||||
|
DefaultPermission(Predicate<MessageReceivedEvent> p)
|
||||||
|
{
|
||||||
|
predicate = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates the input MRE against the predicate of the said enum value
|
||||||
|
* @param e The input event, containing all needed methods to evaluate the predicate
|
||||||
|
* @return What Predicate.test() returns
|
||||||
|
*/
|
||||||
|
boolean eval(MessageReceivedEvent e)
|
||||||
|
{
|
||||||
|
return predicate.test(e);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,360 @@
|
|||||||
|
package net.pingex.discordbot;
|
||||||
|
|
||||||
|
import com.google.gson.Gson;
|
||||||
|
import com.google.gson.GsonBuilder;
|
||||||
|
import com.google.gson.reflect.TypeToken;
|
||||||
|
import net.pingex.discordbot.json.permissions.Guild;
|
||||||
|
import sx.blah.discord.api.IDiscordClient;
|
||||||
|
import sx.blah.discord.handle.impl.events.MessageReceivedEvent;
|
||||||
|
import sx.blah.discord.handle.obj.IGuild;
|
||||||
|
import sx.blah.discord.handle.obj.IRole;
|
||||||
|
import sx.blah.discord.handle.obj.IUser;
|
||||||
|
import sx.blah.discord.util.DiscordException;
|
||||||
|
import sx.blah.discord.util.HTTP429Exception;
|
||||||
|
import sx.blah.discord.util.MissingPermissionsException;
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Permissions modules.
|
||||||
|
* @version 0.1-dev
|
||||||
|
* @author Raphael "Pingex" NAAS
|
||||||
|
*/
|
||||||
|
@Controllable(name="perm")
|
||||||
|
class PermissionsModule extends AbstractModule
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Main permissions table
|
||||||
|
*/
|
||||||
|
private HashMap<String, Guild> permissions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gson instance for json (de)serialisation
|
||||||
|
*/
|
||||||
|
private static Gson gson = new GsonBuilder().setPrettyPrinting().create();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current instance of this module, for easier access
|
||||||
|
*/
|
||||||
|
private static PermissionsModule INSTANCE = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current instance of this module
|
||||||
|
* @return This instance
|
||||||
|
*/
|
||||||
|
public static PermissionsModule getInstance()
|
||||||
|
{
|
||||||
|
return INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor doing all the basic stuff, like registering as a Listener to Discord, getting a logger, etc.
|
||||||
|
* @param client Discord Client instance used to register self.
|
||||||
|
*/
|
||||||
|
public PermissionsModule(IDiscordClient client)
|
||||||
|
{
|
||||||
|
super(client);
|
||||||
|
INSTANCE = this;
|
||||||
|
|
||||||
|
if(!Configuration.exists("permissions", "file") || Configuration.getValue("permissions", "file").isEmpty())
|
||||||
|
{
|
||||||
|
logger.warning("Permissions file location not specified, defaulting to `permissions.json`");
|
||||||
|
Configuration.setValue("permissions", "file", "permissions.json");
|
||||||
|
}
|
||||||
|
|
||||||
|
reloadPermissions();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gives the UID of the User passed as arguments
|
||||||
|
* @param name The current name of the user
|
||||||
|
* @param discriminator His discriminator
|
||||||
|
* @return The ID of the target user
|
||||||
|
*/
|
||||||
|
@Command
|
||||||
|
public String getUID(MessageReceivedEvent event, String name, String discriminator)
|
||||||
|
{
|
||||||
|
for(IUser i : event.getMessage().getGuild().getUsers())
|
||||||
|
if(i.getName().equals(name) && i.getDiscriminator().equals(discriminator))
|
||||||
|
return i.getID();
|
||||||
|
return "User not found";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets caller's UID
|
||||||
|
* @return Caller's UID
|
||||||
|
*/
|
||||||
|
@Command
|
||||||
|
public String getMyUID(MessageReceivedEvent event)
|
||||||
|
{
|
||||||
|
return event.getMessage().getAuthor().getID();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Give GID of the named role
|
||||||
|
* @param name The role current name
|
||||||
|
* @return The GID of the role
|
||||||
|
*/
|
||||||
|
@Command
|
||||||
|
public String getGID(MessageReceivedEvent event, String name)
|
||||||
|
{
|
||||||
|
for(IRole i : event.getMessage().getGuild().getRoles())
|
||||||
|
if(i.getName().equals(name))
|
||||||
|
return i.getID();
|
||||||
|
return "Role not found";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump all Roles and their GID
|
||||||
|
* Note: this commands answers the dump via PM to avoid spam and the @everyone role to be publicly displayed thus notify everyone in the guild.
|
||||||
|
* @return The whole dump, formatted as a displayable String
|
||||||
|
*/
|
||||||
|
@Command
|
||||||
|
public String dumpGID(MessageReceivedEvent event)
|
||||||
|
{
|
||||||
|
StringBuffer buffer = new StringBuffer("Dumping Roles for Guild ").append(event.getMessage().getGuild().getName()).append("\n");
|
||||||
|
for(IRole i : event.getMessage().getGuild().getRoles())
|
||||||
|
buffer.append(i.getName()).append("\t").append(i.getID()).append("\n");
|
||||||
|
|
||||||
|
// PM the IDs
|
||||||
|
// TODO: Remove when PM answers are implemented
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client.getOrCreatePMChannel(event.getMessage().getAuthor()).sendMessage(buffer.toString());
|
||||||
|
} catch (MissingPermissionsException | HTTP429Exception | DiscordException e)
|
||||||
|
{
|
||||||
|
logger.warning("Fail to send the result");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "Check your PM for the command result";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump all Users and their UID
|
||||||
|
* Note: this commands answers the dump via PM to avoid spam.
|
||||||
|
* @return The whole dump, formatted as a displayable String
|
||||||
|
*/
|
||||||
|
@Command
|
||||||
|
public String dumpUID(MessageReceivedEvent event)
|
||||||
|
{
|
||||||
|
StringBuffer buffer = new StringBuffer("Dumping all users for Guild ").append(event.getMessage().getGuild().getName()).append("\n");
|
||||||
|
for(IUser i : event.getMessage().getGuild().getUsers())
|
||||||
|
buffer.append(i.getName()).append("#").append(i.getDiscriminator()).append("\t").append(i.getID()).append("\n");
|
||||||
|
|
||||||
|
// PM the IDs
|
||||||
|
// TODO: Remove when PM answers are implemented
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client.getOrCreatePMChannel(event.getMessage().getAuthor()).sendMessage(buffer.toString());
|
||||||
|
} catch (MissingPermissionsException | HTTP429Exception | DiscordException e)
|
||||||
|
{
|
||||||
|
logger.warning("Fail to send the result");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return "Check your PM for the command result";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload permissions from file
|
||||||
|
* @return Nothing
|
||||||
|
*/
|
||||||
|
@Command
|
||||||
|
public String reload(MessageReceivedEvent event)
|
||||||
|
{
|
||||||
|
reloadPermissions();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save permissions to file
|
||||||
|
* @return Nothing
|
||||||
|
*/
|
||||||
|
@Command
|
||||||
|
public String save(MessageReceivedEvent event)
|
||||||
|
{
|
||||||
|
writePermissions();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets an user permission
|
||||||
|
* @param command The target *full* command, shorthands doesn't work
|
||||||
|
* @param user The user's UID
|
||||||
|
* @param target `true` to allow execution of the command, `false` to deny it, `null` to remove the rule
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Command
|
||||||
|
public String setUser(MessageReceivedEvent event, String command, String user, Boolean target)
|
||||||
|
{
|
||||||
|
if(!permissions.containsKey(event.getMessage().getGuild().getID()))
|
||||||
|
permissions.put(event.getMessage().getGuild().getID(), new Guild(new HashMap<>()));
|
||||||
|
Guild g = permissions.get(event.getMessage().getGuild().getID());
|
||||||
|
|
||||||
|
if(!g.commands.containsKey(command))
|
||||||
|
g.commands.put(command, new net.pingex.discordbot.json.permissions.Command(new HashMap<>(), new HashMap<>()));
|
||||||
|
net.pingex.discordbot.json.permissions.Command c = g.commands.get(command);
|
||||||
|
|
||||||
|
if(target == null) c.users.remove(user);
|
||||||
|
else c.users.put(user, target);
|
||||||
|
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a role permission
|
||||||
|
* @param command The target *full* command, shorthands doesn't work
|
||||||
|
* @param group The role's GID
|
||||||
|
* @param target `true` to allow execution of the command, `false` to deny it, `null` to remove the rule
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Command
|
||||||
|
public String setRole(MessageReceivedEvent event, String command, String group, Boolean target)
|
||||||
|
{
|
||||||
|
if(!permissions.containsKey(event.getMessage().getGuild().getID()))
|
||||||
|
permissions.put(event.getMessage().getGuild().getID(), new Guild(new HashMap<>()));
|
||||||
|
Guild g = permissions.get(event.getMessage().getGuild().getID());
|
||||||
|
|
||||||
|
if(!g.commands.containsKey(command))
|
||||||
|
g.commands.put(command, new net.pingex.discordbot.json.permissions.Command(new HashMap<>(), new HashMap<>()));
|
||||||
|
net.pingex.discordbot.json.permissions.Command c = g.commands.get(command);
|
||||||
|
|
||||||
|
if(target == null) c.roles.remove(group);
|
||||||
|
else c.roles.put(group, target);
|
||||||
|
|
||||||
|
return "OK";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dump all permissions defined for the current guild
|
||||||
|
* @return Displayable String of the permissions
|
||||||
|
*/
|
||||||
|
@Command(shorthand = "dumpPerm")
|
||||||
|
public String dumpPermissions(MessageReceivedEvent event)
|
||||||
|
{
|
||||||
|
StringBuffer buffer = new StringBuffer("Dumping all permissions for Guild ").append(event.getMessage().getGuild().getName()).append("\n\n```");
|
||||||
|
|
||||||
|
if(!permissions.containsKey(event.getMessage().getGuild().getID()))
|
||||||
|
return "No permissions set for Guild";
|
||||||
|
Guild g = permissions.get(event.getMessage().getGuild().getID());
|
||||||
|
|
||||||
|
for(Map.Entry<String, net.pingex.discordbot.json.permissions.Command> i : g.commands.entrySet())
|
||||||
|
{
|
||||||
|
buffer.append("========== COMMAND ").append(i.getKey()).append(" ==========\n");
|
||||||
|
|
||||||
|
// USERS
|
||||||
|
buffer.append("----- Users -----\n");
|
||||||
|
for(Map.Entry<String, Boolean> j : i.getValue().users.entrySet())
|
||||||
|
{
|
||||||
|
buffer.append(j.getKey()).append("\t").append(j.getValue()).append("\n");
|
||||||
|
}
|
||||||
|
buffer.append("\n");
|
||||||
|
|
||||||
|
// GROUPS
|
||||||
|
buffer.append("----- Roles -----\n");
|
||||||
|
for(Map.Entry<String, Boolean> j : i.getValue().roles.entrySet())
|
||||||
|
{
|
||||||
|
buffer.append(j.getKey()).append("\t").append(j.getValue()).append("\n");
|
||||||
|
}
|
||||||
|
buffer.append("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.append("```");
|
||||||
|
return buffer.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Says if the permissions overrider will allow the target user to run the target command
|
||||||
|
* @param user User name
|
||||||
|
* @param discriminator His discriminator
|
||||||
|
* @param command The target command
|
||||||
|
* @return `YES` if he can run it, `NO` if he can't, `N/A` if the decision relies to the default behavior
|
||||||
|
*/
|
||||||
|
@Command
|
||||||
|
public String canRun(MessageReceivedEvent event, String user, String discriminator, String command)
|
||||||
|
{
|
||||||
|
IUser target = null;
|
||||||
|
|
||||||
|
for(IUser i : event.getMessage().getGuild().getUsers())
|
||||||
|
if(i.getName().equals(user) && i.getDiscriminator().equals(discriminator))
|
||||||
|
{
|
||||||
|
target = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(target == null)
|
||||||
|
return "User not found in this Guild.";
|
||||||
|
|
||||||
|
Boolean toReturn = canRun(event.getMessage().getGuild(), target, command);
|
||||||
|
return toReturn == null ? "N/A" : (toReturn ? "YES" : "NO");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Says if the permissions overrider will allow the target user to run the target command. To be run internally
|
||||||
|
* @param guild The current guild
|
||||||
|
* @param user The target user
|
||||||
|
* @param command The target command
|
||||||
|
* @return `true` is he can run the command, `false` if he can't, `null` if the overrider has nothing to say
|
||||||
|
*/
|
||||||
|
public Boolean canRun(IGuild guild, IUser user, String command)
|
||||||
|
{
|
||||||
|
// Return null if command or guild aren't mentioned in the permissions file
|
||||||
|
if(!permissions.containsKey(guild.getID()) || !permissions.get(guild.getID()).commands.containsKey(command)) return null;
|
||||||
|
net.pingex.discordbot.json.permissions.Command c = permissions.get(guild.getID()).commands.get(command);
|
||||||
|
|
||||||
|
// User matching
|
||||||
|
if(c.users.containsKey(user.getID()))
|
||||||
|
return c.users.get(user.getID());
|
||||||
|
|
||||||
|
// Role matching
|
||||||
|
for(IRole i : user.getRolesForGuild(guild))
|
||||||
|
{
|
||||||
|
if(c.roles.containsKey(i.getID()))
|
||||||
|
return c.roles.get(i.getID());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reloads the permissions file. Discards the current permissions cache
|
||||||
|
*/
|
||||||
|
private void reloadPermissions()
|
||||||
|
{
|
||||||
|
logger.info("Reloading permissions");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileReader reader = new FileReader(Configuration.getValue("permissions", "file"));
|
||||||
|
permissions = gson.fromJson(reader, new TypeToken<HashMap<String, Guild>>(){}.getType());
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
catch (FileNotFoundException e)
|
||||||
|
{
|
||||||
|
logger.info("File doesn't exist, using default permissions");
|
||||||
|
permissions = new HashMap<>();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
logger.severe("I/O error while reading permissions file");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the current HashMap to file.
|
||||||
|
*/
|
||||||
|
private void writePermissions()
|
||||||
|
{
|
||||||
|
logger.info("Writing permissions");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
FileWriter writer = new FileWriter(Configuration.getValue("permissions", "file"));
|
||||||
|
writer.write(gson.toJson(permissions));
|
||||||
|
writer.close();
|
||||||
|
}
|
||||||
|
catch (IOException e)
|
||||||
|
{
|
||||||
|
logger.severe("I/O error while writing permissions file");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package net.pingex.discordbot.json.permissions;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A command's permissions list
|
||||||
|
*/
|
||||||
|
public class Command
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Users
|
||||||
|
*/
|
||||||
|
public HashMap<String, Boolean> users;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Roles
|
||||||
|
*/
|
||||||
|
public HashMap<String, Boolean> roles;
|
||||||
|
|
||||||
|
public Command(HashMap<String, Boolean> users, HashMap<String, Boolean> roles)
|
||||||
|
{
|
||||||
|
this.users = users;
|
||||||
|
this.roles = roles;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,19 @@
|
|||||||
|
package net.pingex.discordbot.json.permissions;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Json representation of a guild, contains an HashMap of commands
|
||||||
|
*/
|
||||||
|
public class Guild
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The list of commands which have permission overrides defined.
|
||||||
|
*/
|
||||||
|
public HashMap<String, Command> commands;
|
||||||
|
|
||||||
|
public Guild(HashMap<String, Command> commands)
|
||||||
|
{
|
||||||
|
this.commands = commands;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue