refactor: change package path to reflect group id of pom.xml
BREAKING CHANGE:
This commit is contained in:
1 parent
2b08351f6d
commit
f4c7e9c703
94 files changed
+373
-372
No files matched your search
@@ -0,0 +1,118 @@
|
||||
package me.bobert.netneutrality;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import io.sentry.Sentry;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.JDABuilder;
|
||||
import net.dv8tion.jda.api.entities.Activity;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.exceptions.InvalidTokenException;
|
||||
import net.dv8tion.jda.api.requests.GatewayIntent;
|
||||
import me.bobert.netneutrality.command.CommandManager;
|
||||
import me.bobert.netneutrality.core.LogHandler;
|
||||
import me.bobert.netneutrality.db.DBController;
|
||||
import me.bobert.netneutrality.hooks.SlashCommandHooks;
|
||||
import me.bobert.netneutrality.hooks.guilds.ServerJoinHandler;
|
||||
import me.bobert.netneutrality.hooks.message.EditHandler;
|
||||
import me.bobert.netneutrality.hooks.message.MessageReceivedHandler;
|
||||
import me.bobert.netneutrality.http.server.WebApi;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import me.bobert.netneutrality.thread.BaseThreadFactory;
|
||||
import me.bobert.netneutrality.thread.ShutdownThread;
|
||||
import me.bobert.netneutrality.thread.ThreadManager;
|
||||
import me.bobert.netneutrality.utils.Settings;
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.logging.log4j.core.config.Configurator;
|
||||
|
||||
public class NetNeutrality {
|
||||
|
||||
private static JDA jda;
|
||||
public final static Logger logger = LogManager.getLogger(NetNeutrality.class);
|
||||
public static final String VERSION = Settings.getInstance().getMavenVersion();
|
||||
|
||||
public static void main(String[] args) {
|
||||
String env = Settings.getInstance().get("NETNEUTRALITY_BOT_ENV", "dev");
|
||||
|
||||
String sentryDsn = Settings.getInstance().get("NETNEUTRALITY_SENTRY_DSN");
|
||||
if (sentryDsn != null) {
|
||||
logger.info("Found Sentry DSN in config, setting up the SDK..");
|
||||
|
||||
Sentry.init(options -> {
|
||||
options.setDsn(sentryDsn);
|
||||
options.setEnvironment(env);
|
||||
options.setTracesSampleRate(1.0);
|
||||
options.setDebug(false);
|
||||
});
|
||||
}
|
||||
|
||||
if ("dev".equals(env)) {
|
||||
Configurator.setLevel("net.dv8tion.jda", Level.DEBUG);
|
||||
}
|
||||
|
||||
DBController.init();
|
||||
initJDA();
|
||||
CommandManager.init();
|
||||
MusicManager.initPlayerManager();
|
||||
jda.addEventListener(new MessageReceivedHandler(), new EditHandler(), new ServerJoinHandler());
|
||||
jda.addEventListener(new SlashCommandHooks());
|
||||
new WebApi(Integer.parseInt(Settings.getInstance().get("NETNEUTRALITY_API_PORT", "9000")));
|
||||
jda.getPresence().setActivity(Activity.watching("you"));
|
||||
|
||||
Runtime.getRuntime().addShutdownHook(new BaseThreadFactory("shutdown").newThread(new ShutdownThread()));
|
||||
|
||||
}
|
||||
|
||||
public static void initJDA() {
|
||||
String token = Settings.getInstance().get("NETNEUTRALITY_BOT_TOKEN");
|
||||
if (token == null) {
|
||||
DBController.shutdown();
|
||||
throw new RuntimeException("No bot token provided, exiting.");
|
||||
}
|
||||
|
||||
try {
|
||||
jda = JDABuilder
|
||||
.createDefault(token)
|
||||
.enableIntents(GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_MESSAGE_REACTIONS, GatewayIntent.GUILD_VOICE_STATES, GatewayIntent.MESSAGE_CONTENT)
|
||||
.setActivity(Activity.watching("me load..."))
|
||||
.build();
|
||||
jda.awaitReady();
|
||||
} catch (InvalidTokenException | IllegalArgumentException | InterruptedException e) {
|
||||
LogHandler.log(e);
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
System.out.println("[Net Neutrality] Shutting down...");
|
||||
MusicManager.disconnectPlayers();
|
||||
ThreadManager.shutdownExecutor();
|
||||
disconnect();
|
||||
DBController.shutdown();
|
||||
System.out.println("[Net Neutrality] Shutdown complete");
|
||||
}
|
||||
|
||||
public static JDA getJDA() {
|
||||
return jda;
|
||||
}
|
||||
|
||||
public static User getSelf() {
|
||||
return jda.getSelfUser();
|
||||
|
||||
}
|
||||
|
||||
private static void disconnect() {
|
||||
if (jda != null) {
|
||||
jda.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package me.bobert.netneutrality.command;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||
|
||||
public abstract class AbstractCommand implements ICommand {
|
||||
|
||||
@Override
|
||||
public boolean isSlashCommand() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultMemberPermissions getSlashCommandPermission() {
|
||||
return DefaultMemberPermissions.ENABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeExecutedBy(Guild guild, Member member) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeExecutedFrom(Guild guild, MessageChannel channel) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
// Prefer slash commands
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package me.bobert.netneutrality.command;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.core.GuildManager;
|
||||
import me.bobert.netneutrality.db.GSettings;
|
||||
import me.bobert.netneutrality.utils.UserFunction;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Role;
|
||||
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
|
||||
public abstract class AbstractMusicCommand extends AbstractCommand {
|
||||
|
||||
@Override
|
||||
public boolean needAdmin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGuildOnly() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandType getType() {
|
||||
return CommandType.MUSIC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeExecutedBy(Guild guild, Member member) {
|
||||
String roleDb = GuildManager.getManager(guild).getGuildConfigManager().getSetting(GSettings.MUSIC_ROLE);
|
||||
|
||||
if (roleDb != null && !roleDb.isEmpty()) {
|
||||
if (UserFunction.isAdmin(member)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Role neededRole = guild.getRoleById(roleDb);
|
||||
if (neededRole == null) {
|
||||
// Role doesn't exist on guild anymore, remove from db
|
||||
GuildManager.getManager(guild).getGuildConfigManager().setSettingValue(GSettings.MUSIC_ROLE, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
return member.getRoles().contains(neededRole);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeExecutedFrom(Guild guild, MessageChannel channel) {
|
||||
if (channel.getType() != ChannelType.TEXT)
|
||||
return false;
|
||||
|
||||
String channelDb = GuildManager.getManager(guild).getGuildConfigManager().getSetting(GSettings.MUSIC_CHANNEL);
|
||||
|
||||
if (channelDb != null && !channelDb.isEmpty()) {
|
||||
return channelDb.equals(channel.getId());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package me.bobert.netneutrality.command;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
public class CommandExecutionResult {
|
||||
|
||||
public enum Result {
|
||||
SUCCESS, INFO, WARN, ERROR, NOACTION
|
||||
}
|
||||
|
||||
private final Result result;
|
||||
private final String message;
|
||||
private final Exception exception;
|
||||
|
||||
private CommandExecutionResult(Result result, String message, Exception exception) {
|
||||
this.result = result;
|
||||
this.message = message;
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
private CommandExecutionResult(Result result, String message) {
|
||||
this(result, message, null);
|
||||
}
|
||||
|
||||
public Result getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public Exception getException() {
|
||||
return exception;
|
||||
}
|
||||
|
||||
public static CommandExecutionResult success(String message) {
|
||||
return new CommandExecutionResult(Result.SUCCESS, message);
|
||||
}
|
||||
|
||||
public static CommandExecutionResult info(String message) {
|
||||
return new CommandExecutionResult(Result.INFO, message);
|
||||
}
|
||||
|
||||
public static CommandExecutionResult warn(String message) {
|
||||
return new CommandExecutionResult(Result.WARN, message);
|
||||
}
|
||||
|
||||
public static CommandExecutionResult error(String message, Exception exception) {
|
||||
return new CommandExecutionResult(Result.ERROR, message, exception);
|
||||
}
|
||||
|
||||
public static CommandExecutionResult error(String message) {
|
||||
return error(message, null);
|
||||
}
|
||||
|
||||
public static CommandExecutionResult none() {
|
||||
return new CommandExecutionResult(Result.NOACTION, null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
package me.bobert.netneutrality.command;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.NetNeutrality;
|
||||
import me.bobert.netneutrality.core.LogHandler;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.Commands;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.reflections.Reflections;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class CommandManager {
|
||||
|
||||
private final static Logger logger = LogManager.getLogger(CommandManager.class);
|
||||
public static final HashMap<String, AbstractCommand> commands = new HashMap<>();
|
||||
|
||||
public static void init() {
|
||||
initCommands();
|
||||
registerSlashCommands();
|
||||
}
|
||||
|
||||
public static boolean isValidCommand(String commandString) {
|
||||
return commands.containsKey(commandString);
|
||||
}
|
||||
|
||||
public static AbstractCommand getCommand(String commandString) {
|
||||
return commands.get(commandString);
|
||||
}
|
||||
|
||||
private static void initCommands() {
|
||||
logger.info("Loading commands..");
|
||||
|
||||
Reflections reflections = new Reflections("netneutrality.command");
|
||||
Set<Class<? extends AbstractCommand>> classes = reflections.getSubTypesOf(AbstractCommand.class);
|
||||
|
||||
for (Class<? extends AbstractCommand> s : classes) {
|
||||
try {
|
||||
if (Modifier.isAbstract(s.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AbstractCommand command = s.getConstructor().newInstance();
|
||||
|
||||
if (!command.isEnabled())
|
||||
continue;
|
||||
|
||||
if (!commands.containsKey(command.getCommandName())) {
|
||||
commands.put(command.getCommandName(), command);
|
||||
}
|
||||
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
|
||||
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
|
||||
LogHandler.log(e);
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Loaded " + commands.size() + " commands!");
|
||||
}
|
||||
|
||||
private static void registerSlashCommands() {
|
||||
CommandListUpdateAction commandListUpdateAction = NetNeutrality.getJDA().updateCommands();
|
||||
List<CommandData> slashCommands = new ArrayList<>();
|
||||
|
||||
for (AbstractCommand command : commands.values()) {
|
||||
if (!command.isEnabled() || !command.isSlashCommand()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
SlashCommandData data = Commands.slash(command.getCommandName(), command.getDescription());
|
||||
data.setDefaultPermissions(command.getSlashCommandPermission());
|
||||
data.setGuildOnly(command.isGuildOnly());
|
||||
command.registerSlashCommandOptions(data);
|
||||
|
||||
slashCommands.add(data);
|
||||
}
|
||||
|
||||
commandListUpdateAction.addCommands(slashCommands).queue();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package me.bobert.netneutrality.command;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
public enum CommandType {
|
||||
MUSIC("Music", 1), ADMINISTRATION("Administration", 0), UTILITY("Utility", 5), CRYPTO("Crypto", 6), UNLISTED("", -1);
|
||||
|
||||
private final String description;
|
||||
private final int id;
|
||||
|
||||
CommandType(String description, int id) {
|
||||
this.description = description;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package me.bobert.netneutrality.command;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
|
||||
public interface ICommand {
|
||||
|
||||
boolean needAdmin();
|
||||
|
||||
boolean isEnabled();
|
||||
|
||||
boolean isGuildOnly();
|
||||
|
||||
boolean isSlashCommand();
|
||||
|
||||
String getCommandName();
|
||||
|
||||
String getDescription();
|
||||
|
||||
DefaultMemberPermissions getSlashCommandPermission();
|
||||
|
||||
void registerSlashCommandOptions(SlashCommandData data);
|
||||
|
||||
CommandType getType();
|
||||
|
||||
boolean canBeExecutedBy(Guild guild, Member member);
|
||||
|
||||
boolean canBeExecutedFrom(Guild guild, MessageChannel channel);
|
||||
|
||||
@Deprecated
|
||||
CommandExecutionResult execute(MessageCommandContext context);
|
||||
|
||||
CommandExecutionResult execute(SlashCommandContext context);
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package me.bobert.netneutrality.command.admin;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.command.CommandType;
|
||||
import me.bobert.netneutrality.core.LogHandler;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.db.DBController;
|
||||
import me.bobert.netneutrality.db.controllers.DBBlacklist;
|
||||
import me.bobert.netneutrality.db.entities.OBlacklist;
|
||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class BlacklistCommand extends AbstractCommand {
|
||||
|
||||
@Override
|
||||
public boolean needAdmin() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGuildOnly() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "blacklist";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Manages the blacklist of your server.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultMemberPermissions getSlashCommandPermission() {
|
||||
return DefaultMemberPermissions.DISABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
data.addSubcommands(
|
||||
new SubcommandData("get", "Get all blacklisted words for this guild"),
|
||||
new SubcommandData("add", "Add a word to the blacklist")
|
||||
.addOption(OptionType.STRING, "word", "Word to add"),
|
||||
new SubcommandData("remove", "Remove a word from the blacklist")
|
||||
.addOption(OptionType.STRING, "word", "Word to remove")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandType getType() {
|
||||
return CommandType.ADMINISTRATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
String sub = context.getEvent().getFullCommandName().split(" ")[1];
|
||||
DBBlacklist controller = DBController.get(DBBlacklist.class);
|
||||
String word = context.getEvent().getOption("word", "", OptionMapping::getAsString);
|
||||
context.ackAndWait(true);
|
||||
|
||||
switch (sub) {
|
||||
case "get" -> {
|
||||
|
||||
context.getHook().editOriginal("Retrieving list, please wait...").queue(t -> {
|
||||
ArrayList<OBlacklist> blacklists = new ArrayList<>();
|
||||
try {
|
||||
blacklists = controller.getBlacklistForGuild(context.getGuild());
|
||||
} catch (Exception e) {
|
||||
LogHandler.log(e);
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (OBlacklist bl : blacklists) {
|
||||
sb.append(bl.getWord()).append(" -> ").append(bl.getEnabled() ? "enabled" : "disabled").append("\n");
|
||||
}
|
||||
|
||||
t.editMessage("List of blacklisted words for this server [" + context.getGuild().getName() + "] :\n" + (blacklists.isEmpty() ? "No word found" : sb.toString())).queue();
|
||||
|
||||
});
|
||||
}
|
||||
case "add" -> controller.add(word, context);
|
||||
case "remove" -> controller.delete(word, context);
|
||||
case "disable" -> controller.disable(word, context);
|
||||
case "enable" -> controller.enable(word, context);
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package me.bobert.netneutrality.command.basic;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.command.CommandType;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.utils.MessageFunction;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class HelpCommand extends AbstractCommand {
|
||||
|
||||
@Override
|
||||
public boolean needAdmin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGuildOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "help";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Get every command with its usage";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandType getType() {
|
||||
return CommandType.UTILITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
if (context.getArgs().length == 0) {
|
||||
List<MessageEmbed> help = MessageFunction.buildHelpMessage(context.getGuildUser());
|
||||
|
||||
context.reply(new MessageCreateBuilder().addContent(context.getDiscordUser().getAsMention()).addContent(" help has been sent your way!").build());
|
||||
context.replyPrivate(help);
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
List<MessageEmbed> help = MessageFunction.buildHelpMessage(context.getGuildUser());
|
||||
|
||||
context.sendMessageInPrivate(help);
|
||||
return CommandExecutionResult.success("Help has been sent in DM!");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package me.bobert.netneutrality.command.basic;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.command.CommandType;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
|
||||
public class PingCommand extends AbstractCommand {
|
||||
|
||||
@Override
|
||||
public boolean needAdmin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGuildOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "ping";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Pong!";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandType getType() {
|
||||
return CommandType.UTILITY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
if (context.getArgs().length == 0)
|
||||
context.reply(String.format("Pong! [%d ms]", context.getJDA().getGatewayPing()));
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
return CommandExecutionResult.success(String.format("Pong! [%d ms]", context.getJDA().getGatewayPing()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,289 @@
|
||||
package me.bobert.netneutrality.command.bot;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.command.CommandType;
|
||||
import me.bobert.netneutrality.core.GuildManager;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.db.GSettings;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import net.dv8tion.jda.api.entities.Role;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion;
|
||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ConfigureCommand extends AbstractCommand {
|
||||
|
||||
@Override
|
||||
public boolean needAdmin() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGuildOnly() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "config";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Used to configure the bot on this server";
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultMemberPermissions getSlashCommandPermission() {
|
||||
return DefaultMemberPermissions.DISABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
data.addSubcommands(
|
||||
new SubcommandData("music-volume", "Volume for the music player")
|
||||
.addOptions(new OptionData(OptionType.INTEGER, "volume", "Integer between 0 and 100", true)
|
||||
.setRequiredRange(0, 100)),
|
||||
new SubcommandData("error-logging", "Send errors to #error and/or #log channel")
|
||||
.addOption(OptionType.BOOLEAN, "debug", "Enable or disable the fonctionnality", true),
|
||||
new SubcommandData("music-role", "Role needed to use music commands")
|
||||
.addOption(OptionType.ROLE, "role", "Name of role", true),
|
||||
new SubcommandData("music-channel", "Only allow music commands on this channel")
|
||||
.addOption(OptionType.CHANNEL, "channel", "Leave empty to allow everywhere")
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandType getType() {
|
||||
return CommandType.ADMINISTRATION;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
String[] args = context.getArgs();
|
||||
GuildManager guildManager = GuildManager.getManager(context.getGuild());
|
||||
|
||||
if (args.length > 0) {
|
||||
|
||||
// Role for music commands
|
||||
if (context.getArgument(0).equals("music-role")) {
|
||||
if (args.length == 1) {
|
||||
String roleDb = guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_ROLE);
|
||||
Role role = null;
|
||||
if (!roleDb.isEmpty()) {
|
||||
role = context.getGuild().getRoleById(roleDb);
|
||||
}
|
||||
|
||||
context.reply("Current role needed to use music commands (default is everyone): %s", (role == null ? "Everyone" : role.getAsMention()));
|
||||
context.reply("Reset role using 'conf music-role reset' or set it using 'conf musicrole @'");
|
||||
}
|
||||
|
||||
if (args.length == 2) {
|
||||
if (context.getArgument(1).equals("reset")) {
|
||||
if (!guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_ROLE).isEmpty()) {
|
||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_ROLE, null);
|
||||
}
|
||||
|
||||
context.reply("Successful reset of the role");
|
||||
}
|
||||
|
||||
List<Role> roles = context.getMessage().getMentions().getRoles();
|
||||
if (roles.size() == 1) {
|
||||
Role role = roles.get(0);
|
||||
if (role.getGuild().getId().equals(context.getGuild().getId())) {
|
||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_ROLE, role.getId());
|
||||
context.reply("Successfully set %s as the role needed for music command", role.getAsMention());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Role for music commands
|
||||
if (context.getArgument(0).equals("music-channel")) {
|
||||
if (args.length == 1) {
|
||||
String channelDb = guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_CHANNEL);
|
||||
TextChannel channel = null;
|
||||
if (!channelDb.isEmpty()) {
|
||||
channel = context.getGuild().getTextChannelById(channelDb);
|
||||
}
|
||||
|
||||
context.reply("Current channel where music commands can be executed (default is everywhere): %s", (channel == null ? "Everywhere" : channel.getAsMention()));
|
||||
context.reply("Reset channel using 'conf music-channel reset' or set it using 'conf music-channel #'");
|
||||
}
|
||||
|
||||
if (args.length == 2) {
|
||||
if (context.getArgument(1).equals("reset")) {
|
||||
if (!guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_CHANNEL).isEmpty()) {
|
||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_CHANNEL, null);
|
||||
}
|
||||
|
||||
context.reply("Successful reset of the channel");
|
||||
}
|
||||
|
||||
List<TextChannel> channels = context.getMessage().getMentions().getChannels(TextChannel.class);
|
||||
if (channels.size() == 1) {
|
||||
TextChannel channel = channels.get(0);
|
||||
if (channel.getGuild().getId().equals(context.getGuild().getId())) {
|
||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_CHANNEL, channel.getId());
|
||||
context.reply("Successfully set %s as the channel required to execute music command", channel.getAsMention());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default volume for music player
|
||||
if (context.getArgument(0).equals("music-volume")) {
|
||||
if (args.length == 1) {
|
||||
int volume = Integer.parseInt(guildManager.getGuildConfigManager().getSetting(GSettings.MUSIC_VOLUME));
|
||||
|
||||
context.reply("Default volume for music player: %d", volume);
|
||||
context.reply("Set using 'conf music-volume <value between 0 and 100>'");
|
||||
}
|
||||
|
||||
if (args.length == 2) {
|
||||
try {
|
||||
int volume = Integer.parseInt(args[1]);
|
||||
|
||||
if (volume > 0 && volume <= 100) {
|
||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_VOLUME, String.valueOf(volume));
|
||||
MusicManager.get(context.getGuild()).setVolume(volume);
|
||||
|
||||
context.reply("Default volume for music player is now %d", volume);
|
||||
} else
|
||||
throw new NumberFormatException();
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
context.reply("Not a value between 0 and 100");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Debug
|
||||
if (context.getArgument(0).equals("debug")) {
|
||||
if (args.length == 1) {
|
||||
boolean debug = guildManager.isDebugEnabled();
|
||||
|
||||
context.reply("Error logging is currently %s for this guild.", (debug ? "enabled" : "disabled"));
|
||||
context.reply("Use 'conf debug 0/1' to toggle between states (0 = disabled, 1 = enabled).");
|
||||
}
|
||||
|
||||
if (args.length == 2) {
|
||||
String str = args[1];
|
||||
boolean debug = guildManager.isDebugEnabled();
|
||||
if (str.equals("0") || str.equalsIgnoreCase("disable")) {
|
||||
debug = false;
|
||||
}
|
||||
|
||||
if (str.equals("1") || str.equalsIgnoreCase("enable")) {
|
||||
debug = true;
|
||||
}
|
||||
|
||||
guildManager.setDebug(debug);
|
||||
context.reply("Error logging is now %s for this guild", (debug ? "enabled" : "disabled"));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
String sub = context.getEvent().getFullCommandName().split(" ")[1];
|
||||
GuildManager guildManager = GuildManager.getManager(context.getGuild());
|
||||
context.ackAndWait(true);
|
||||
|
||||
if ("music-role".equals(sub)) {
|
||||
Role role = context.getEvent().getOption("role", OptionMapping::getAsRole);
|
||||
assert role != null;
|
||||
|
||||
if (role.getPosition() == -1) {
|
||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_ROLE, null);
|
||||
|
||||
return CommandExecutionResult.success("Everyone can now use music commands");
|
||||
}
|
||||
|
||||
if (role.getGuild().getId().equals(context.getGuild().getId())) {
|
||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_ROLE, role.getId());
|
||||
|
||||
return CommandExecutionResult.success(String.format("Successfully set %s as the role needed for music command", role.getAsMention()));
|
||||
}
|
||||
}
|
||||
|
||||
if ("music-channel".equals(sub)) {
|
||||
GuildChannelUnion channel = context.getEvent().getOption("channel", OptionMapping::getAsChannel);
|
||||
|
||||
if (channel == null) {
|
||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_CHANNEL, null);
|
||||
|
||||
return CommandExecutionResult.success("No channel restrictions for music commands");
|
||||
}
|
||||
|
||||
if (channel instanceof TextChannel) {
|
||||
if (channel.getGuild().getId().equals(context.getGuild().getId())) {
|
||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_CHANNEL, channel.getId());
|
||||
|
||||
return CommandExecutionResult.success(String.format("Successfully set %s as the channel required to execute music command", channel.getAsMention()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ("music-volume".equals(sub)) {
|
||||
int volume = context.getEvent().getOption("volume", 30, OptionMapping::getAsInt);
|
||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_VOLUME, String.valueOf(volume));
|
||||
MusicManager.get(context.getGuild()).setVolume(volume);
|
||||
|
||||
return CommandExecutionResult.success(String.format("Default volume for music player is now %d", volume));
|
||||
}
|
||||
|
||||
if ("debug".equals(sub)) {
|
||||
boolean debug = context.getEvent().getOption("debug", true, OptionMapping::getAsBoolean);
|
||||
guildManager.setDebug(debug);
|
||||
|
||||
return CommandExecutionResult.success(String.format("Error logging is now %s for this guild", (debug ? "enabled" : "disabled")));
|
||||
}
|
||||
|
||||
if ("show-all".equals(sub)) {
|
||||
Map<String, String> settings = guildManager.getGuildConfigManager().getSettings();
|
||||
MessageCreateBuilder builder = new MessageCreateBuilder();
|
||||
|
||||
for (GSettings o : GSettings.values()) {
|
||||
String value = o.getDefaultValue();
|
||||
|
||||
if (settings.containsKey(o.getName())) {
|
||||
value = settings.get(o.getName());
|
||||
}
|
||||
|
||||
builder.addContent(o.getName()).addContent(": ").addContent(value);
|
||||
builder.addContent("\n");
|
||||
}
|
||||
|
||||
context.replyEphemeral(builder.build());
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
package me.bobert.netneutrality.command.crypto;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.command.CommandType;
|
||||
import me.bobert.netneutrality.core.LogHandler;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.http.WebModule;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Currency;
|
||||
import java.util.Locale;
|
||||
|
||||
public class CryptoPriceCommand extends AbstractCommand {
|
||||
|
||||
private final String baseURL = "https://min-api.cryptocompare.com/data/price";
|
||||
|
||||
@Override
|
||||
public boolean needAdmin() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGuildOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "crypto";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Get the price of cryptocurrencies";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
data.addOption(OptionType.STRING, "symbol", "The crypto symbol to fetch price for", true);
|
||||
data.addOption(OptionType.STRING, "currency", "In which currency should the price be displayed");
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandType getType() {
|
||||
return CommandType.CRYPTO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
String[] args = context.getArgs();
|
||||
String currency = "USD";
|
||||
|
||||
if (args.length > 0) {
|
||||
if (args.length == 2) {
|
||||
try {
|
||||
currency = Currency.getInstance(args[1].toUpperCase()).getCurrencyCode();
|
||||
} catch (IllegalArgumentException exception) {
|
||||
|
||||
return CommandExecutionResult.warn("Please enter a valid currency");
|
||||
}
|
||||
}
|
||||
|
||||
String crypto = args[0].toUpperCase();
|
||||
String finalCurrency = currency;
|
||||
context.getChannel().sendMessage("Retreiving price, please wait...").queue(t -> {
|
||||
String price;
|
||||
Locale usa = new Locale("en", "US");
|
||||
NumberFormat dollarFormat = NumberFormat.getCurrencyInstance(usa);
|
||||
dollarFormat.setMaximumFractionDigits(24);
|
||||
|
||||
try {
|
||||
WebModule http = new WebModule(String.format("%s?fsym=%s&tsyms=%s", baseURL, crypto, finalCurrency.toUpperCase())).request();
|
||||
JSONObject json = http.getAsObject();
|
||||
if (json.isEmpty() || (json.has("Response") && json.getString("Response").equalsIgnoreCase("Error"))) {
|
||||
t.editMessage("Crypto doesn't exist.. yet").queue();
|
||||
return;
|
||||
}
|
||||
|
||||
price = dollarFormat.format(json.getDouble(finalCurrency));
|
||||
t.editMessage(String.format("The current price of %s in %s is %s", crypto, finalCurrency, price)).queue();
|
||||
|
||||
} catch (JSONException | IllegalArgumentException | ArithmeticException e) {
|
||||
t.editMessage(String.format("Error getting price of %s!", crypto)).queue();
|
||||
LogHandler.log(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
Locale usa = new Locale("en", "US");
|
||||
NumberFormat dollarFormat = NumberFormat.getCurrencyInstance(usa);
|
||||
dollarFormat.setMaximumFractionDigits(24);
|
||||
|
||||
String symbol = context.getEvent().getOption("symbol", OptionMapping::getAsString);
|
||||
String currency = context.getEvent().getOption("currency", "USD", OptionMapping::getAsString);
|
||||
|
||||
try {
|
||||
currency = Currency.getInstance(currency).getCurrencyCode();
|
||||
} catch (IllegalArgumentException exception) {
|
||||
return CommandExecutionResult.error("Please enter a valid currency");
|
||||
}
|
||||
|
||||
String finalCurrency = currency.toUpperCase();
|
||||
context.getEvent().deferReply().queue(interactionHook -> {
|
||||
try {
|
||||
|
||||
WebModule http = new WebModule(String.format("%s?fsym=%s&tsyms=%s", baseURL, symbol, finalCurrency)).request();
|
||||
JSONObject json = http.getAsObject();
|
||||
|
||||
if (json.isEmpty() || (json.has("Response") && json.getString("Response").equalsIgnoreCase("Error"))) {
|
||||
interactionHook.editOriginal("Crypto doesn't exist.. yet").queue();
|
||||
return;
|
||||
}
|
||||
|
||||
double value = json.getDouble(finalCurrency);
|
||||
String price = dollarFormat.format(value);
|
||||
|
||||
interactionHook.editOriginal(String.format("The current price of %s in %s is %s", symbol, finalCurrency, price)).queue();
|
||||
|
||||
} catch (JSONException | IllegalArgumentException | ArithmeticException e) {
|
||||
interactionHook.editOriginal(String.format("Error getting price of %s!", symbol)).queue();
|
||||
LogHandler.log(e);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
*.java
|
||||
@@ -0,0 +1,61 @@
|
||||
package me.bobert.netneutrality.command.music;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.command.CommandType;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
|
||||
public class BitconnectCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "bitconnect";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Ready to invest?";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandType getType() {
|
||||
return CommandType.UNLISTED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
player.play("https://www.youtube.com/watch?v=e5nyQmaq4k4", context, false, true);
|
||||
|
||||
context.deleteMessage();
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
context.ackAndWait();
|
||||
|
||||
player.play("https://www.youtube.com/watch?v=e5nyQmaq4k4", context, false, true);
|
||||
return CommandExecutionResult.success("Ready to invest?");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package me.bobert.netneutrality.command.music;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import me.bobert.netneutrality.utils.MessageFunction;
|
||||
import me.bobert.netneutrality.utils.MusicUtils;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
|
||||
public class ClearQueueCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
public boolean needAdmin() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "clearqueue";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Empties the music queue";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
|
||||
if (!MusicUtils.checkIfConnected(player, context))
|
||||
return CommandExecutionResult.none();
|
||||
|
||||
if (context.getArgs().length == 0) {
|
||||
context.reply("Select the track you want to delete from the queue (track# or all)");
|
||||
context.reply(MessageFunction.buildViewQueueMessage(player, true));
|
||||
}
|
||||
|
||||
if (context.getArgs().length > 0) {
|
||||
|
||||
int queueSize = player.getQueueSize();
|
||||
if (context.getArgument(0).equals("all")) {
|
||||
|
||||
if (queueSize > 0) {
|
||||
player.getScheduler().getQueueManager().clearQueue();
|
||||
context.reply(String.format("Removed %d track%s from the queue", queueSize--, (queueSize > 1 ? "s" : "")));
|
||||
} else {
|
||||
context.reply("Queue is empty!");
|
||||
}
|
||||
} else {
|
||||
|
||||
try {
|
||||
int num = Integer.parseInt(context.getArgument(0));
|
||||
|
||||
if (num > 0) {
|
||||
return player.getScheduler().getQueueManager().removeFromQueue(num);
|
||||
} else {
|
||||
context.reply("Number out of range");
|
||||
}
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
context.reply("Please enter a number");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
MusicManager manager = MusicManager.get(context.getGuild());
|
||||
context.ackAndWait();
|
||||
|
||||
if (!MusicUtils.checkIfConnected(manager, context))
|
||||
return CommandExecutionResult.none();
|
||||
|
||||
return manager.getScheduler().getQueueManager().clearQueue();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package me.bobert.netneutrality.command.music;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import me.bobert.netneutrality.utils.MessageFunction;
|
||||
import me.bobert.netneutrality.utils.MusicUtils;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
|
||||
public class CurrentlyPlayingCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "nowplaying";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "View currently playing track";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
// no arguments
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
|
||||
if (MusicUtils.checkIfConnected(player, context) && player.isCurrentlyPlaying()) {
|
||||
context.reply(MessageFunction.buildCurrentlyPlayingMessage(player));
|
||||
} else {
|
||||
context.reply("Bot isn't playing any tracks right now");
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
MusicManager manager = MusicManager.get(context.getGuild());
|
||||
context.ackAndWait();
|
||||
|
||||
if (MusicUtils.checkIfConnected(manager, context) && manager.isCurrentlyPlaying()) {
|
||||
context.reply(MessageFunction.buildCurrentlyPlayingMessage(manager));
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package me.bobert.netneutrality.command.music;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.exceptions.CommandExecutionException;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
|
||||
public class JoinCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "join";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Make the bot join your current voice channel";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
|
||||
if (context.getGuildUser().getVoiceState() == null) {
|
||||
throw new CommandExecutionException("Voice state is null, this should not happen", context, null);
|
||||
}
|
||||
|
||||
if (!context.getGuildUser().getVoiceState().inAudioChannel()) {
|
||||
return CommandExecutionResult.warn("You are not in a voice channel");
|
||||
}
|
||||
|
||||
if (player.isConnected()) {
|
||||
return CommandExecutionResult.warn("Player is already in a voice channel");
|
||||
}
|
||||
|
||||
VoiceChannel vChannel = (VoiceChannel) context.getGuildUser().getVoiceState().getChannel();
|
||||
if (vChannel != null) {
|
||||
player.connect(vChannel);
|
||||
context.reply("Joining current voice channel");
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
context.ackAndWait();
|
||||
|
||||
if (context.getGuildUser().getVoiceState() == null) {
|
||||
throw new CommandExecutionException("Voice state is null, this should not happen", context, null);
|
||||
}
|
||||
|
||||
if (!context.getGuildUser().getVoiceState().inAudioChannel()) {
|
||||
return CommandExecutionResult.warn("You are not in a voice channel");
|
||||
}
|
||||
|
||||
if (player.isConnected()) {
|
||||
return CommandExecutionResult.warn("Player is already in a voice channel");
|
||||
}
|
||||
|
||||
VoiceChannel vChannel = (VoiceChannel) context.getGuildUser().getVoiceState().getChannel();
|
||||
if (vChannel != null) {
|
||||
player.connect(vChannel);
|
||||
context.reply("Joining current voice channel");
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package me.bobert.netneutrality.command.music;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import me.bobert.netneutrality.utils.MusicUtils;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
|
||||
public class LeaveCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "leave";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Make the bot leave the voice channel";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
|
||||
if (MusicUtils.checkIfConnected(player, context)) {
|
||||
player.leave();
|
||||
context.reply("Leaving voice channel and clearing queue");
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
context.ackAndWait();
|
||||
|
||||
if (MusicUtils.checkIfConnected(player, context)) {
|
||||
player.leave();
|
||||
context.reply("Leaving voice channel and clearing queue");
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package me.bobert.netneutrality.command.music;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
|
||||
public class PlayCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "play";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Get the party going!";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
data.addOption(OptionType.STRING, "query", "URL or search query", true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
|
||||
if (context.getArgs().length > 0) {
|
||||
player.play(context.getMessageText(), context, false, false);
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
MusicManager manager = MusicManager.get(context.getGuild());
|
||||
String query = context.getEvent().getOption("query", OptionMapping::getAsString);
|
||||
context.ackAndWait();
|
||||
|
||||
manager.play(query, context, false, false);
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package me.bobert.netneutrality.command.music;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import me.bobert.netneutrality.utils.MessageFunction;
|
||||
import me.bobert.netneutrality.utils.MusicUtils;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
||||
|
||||
public class QueueCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "queue";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Manage the music queue";
|
||||
}
|
||||
|
||||
@Override
|
||||
public DefaultMemberPermissions getSlashCommandPermission() {
|
||||
return DefaultMemberPermissions.DISABLED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
data.addSubcommands(
|
||||
new SubcommandData("clear-all", "Clear all queued tracks"),
|
||||
new SubcommandData("delete-track", "Remove a single track from the queue")
|
||||
.addOptions(new OptionData(OptionType.INTEGER, "position", "Position of track", true)
|
||||
.setMinValue(1)),
|
||||
new SubcommandData("show-queue", "Display current queue"),
|
||||
new SubcommandData("shuffle", "Shuffle the queue"),
|
||||
new SubcommandData("from-user", "View tracks added by a user")
|
||||
.addOption(OptionType.USER, "user", "Which user?", true),
|
||||
new SubcommandData("remove-from-user", "Remove all tracks added by a user")
|
||||
.addOption(OptionType.USER, "user", "Which user?", true)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
|
||||
if (!MusicUtils.checkIfConnected(player, context))
|
||||
return CommandExecutionResult.none();
|
||||
|
||||
if (context.getArgs().length == 0) {
|
||||
context.reply("Select the track you want to delete from the queue (track# or all)");
|
||||
context.reply(MessageFunction.buildViewQueueMessage(player, true));
|
||||
}
|
||||
|
||||
if (context.getArgs().length > 0) {
|
||||
|
||||
int queueSize = player.getQueueSize();
|
||||
if (context.getArgument(0).equals("all")) {
|
||||
|
||||
if (queueSize > 0) {
|
||||
player.getScheduler().getQueueManager().clearQueue();
|
||||
context.reply(String.format("Removed %d track%s from the queue", queueSize--, (queueSize > 1 ? "s" : "")));
|
||||
} else {
|
||||
context.reply("Queue is empty!");
|
||||
}
|
||||
} else {
|
||||
|
||||
try {
|
||||
int num = Integer.parseInt(context.getArgument(0));
|
||||
|
||||
if (num > 0) {
|
||||
return player.getScheduler().getQueueManager().removeFromQueue(num);
|
||||
} else {
|
||||
context.reply("Number out of range");
|
||||
}
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
context.reply("Please enter a number");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
MusicManager manager = MusicManager.get(context.getGuild());
|
||||
context.ackAndWait();
|
||||
String sub = context.getEvent().getFullCommandName().split(" ")[1];
|
||||
|
||||
if (!MusicUtils.checkIfConnected(manager, context)) {
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
switch (sub) {
|
||||
case "clear-all" -> {
|
||||
return manager.getScheduler().getQueueManager().clearQueue();
|
||||
}
|
||||
|
||||
case "delete-track" -> {
|
||||
int position = context.getEvent().getOption("position", 0, OptionMapping::getAsInt);
|
||||
return manager.getScheduler().getQueueManager().removeFromQueue(position);
|
||||
}
|
||||
|
||||
case "show-queue" -> context.reply(MessageFunction.buildViewQueueMessage(manager, true));
|
||||
|
||||
case "shuffle" -> {
|
||||
return manager.getScheduler().getQueueManager().shuffle();
|
||||
}
|
||||
|
||||
case "from-user" -> {
|
||||
Member user = context.getEvent().getOption("user", OptionMapping::getAsMember);
|
||||
if (user == null) {
|
||||
context.reply("The user isn't from this guild");
|
||||
break;
|
||||
}
|
||||
|
||||
context.reply(MessageFunction.buildUserTracksMessage(manager, user));
|
||||
}
|
||||
|
||||
case "remove-from-user" -> {
|
||||
Member member = context.getEvent().getOption("user", OptionMapping::getAsMember);
|
||||
if (member == null) {
|
||||
context.reply("The user isn't from this guild");
|
||||
break;
|
||||
}
|
||||
|
||||
int size = manager.getScheduler()
|
||||
.getQueueManager()
|
||||
.getQueueWithFilter(trackInfo -> member.equals(trackInfo.getRequester()))
|
||||
.size();
|
||||
|
||||
if (size == 0) {
|
||||
context.reply("No tracks added by user %s", member.getEffectiveName());
|
||||
} else {
|
||||
manager.getScheduler().getQueueManager().removeTracksFromUser(member);
|
||||
context.reply("Removed all tracks from user %s", member.getEffectiveName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package me.bobert.netneutrality.command.music;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import me.bobert.netneutrality.utils.MusicUtils;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
|
||||
public class ShuffleCommand extends AbstractMusicCommand {
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "shuffle";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Shuffle the music queue";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
MusicManager manager = MusicManager.get(context.getGuild());
|
||||
context.ackAndWait();
|
||||
|
||||
if (MusicUtils.checkIfConnected(manager, context)) {
|
||||
return manager.getScheduler().getQueueManager().shuffle();
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package me.bobert.netneutrality.command.music;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import me.bobert.netneutrality.utils.MusicUtils;
|
||||
import me.bobert.netneutrality.utils.UserFunction;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
|
||||
public class SkipCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "skip";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Skip to the next track in the queue";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
data.addOptions(new OptionData(OptionType.INTEGER, "skip", "Number of tracks to skip")
|
||||
.setMinValue(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
|
||||
if (!MusicUtils.checkIfConnected(player, context))
|
||||
return CommandExecutionResult.none();
|
||||
|
||||
if (UserFunction.isAdmin(context.getGuildUser()) || MusicUtils.isUserDj(context.getGuildUser())) {
|
||||
if (UserFunction.isAdmin(context.getGuildUser()) && context.getArgs().length > 0) {
|
||||
try {
|
||||
int skips = Integer.parseInt(context.getArgument(0));
|
||||
player.getScheduler().getQueueManager().skip(skips);
|
||||
|
||||
context.reply("Skipping %s track%s", skips, (skips > 1 ? "s" : ""));
|
||||
} catch (NumberFormatException e) {
|
||||
context.reply("Please enter how many track(s) to skip");
|
||||
}
|
||||
} else {
|
||||
player.getScheduler().getQueueManager().skip(1);
|
||||
context.reply("Skipping current track");
|
||||
}
|
||||
} else {
|
||||
player.voteSkip(context);
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
MusicManager manager = MusicManager.get(context.getGuild());
|
||||
int skips = context.getEvent().getOption("skip", 1, OptionMapping::getAsInt);
|
||||
context.ackAndWait();
|
||||
|
||||
if (MusicUtils.checkIfConnected(manager, context)) {
|
||||
return manager.getScheduler().getQueueManager().skip(skips);
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package me.bobert.netneutrality.command.music;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import me.bobert.netneutrality.utils.MusicUtils;
|
||||
import me.bobert.netneutrality.utils.UserFunction;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
|
||||
public class StopCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "stop";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Stop the music player, keeping the current queue.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
|
||||
if (!MusicUtils.checkIfConnected(player, context))
|
||||
return CommandExecutionResult.none();
|
||||
|
||||
// Need a resume command before
|
||||
if (UserFunction.isAdmin(context.getGuildUser())) {
|
||||
player.stopTrack();
|
||||
context.reply("Stopping current track");
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
MusicManager manager = MusicManager.get(context.getGuild());
|
||||
context.ackAndWait();
|
||||
|
||||
if (MusicUtils.checkIfConnected(manager, context)) {
|
||||
manager.stopTrack();
|
||||
return CommandExecutionResult.success("Stopping music playback");
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package me.bobert.netneutrality.command.music;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import me.bobert.netneutrality.utils.MessageFunction;
|
||||
import me.bobert.netneutrality.utils.MusicUtils;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
|
||||
public class ViewQueueCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "viewqueue";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Get the current queue for the music player";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
|
||||
if (MusicUtils.checkIfConnected(player, context)) {
|
||||
int queueSize = player.getQueueSize();
|
||||
if (queueSize > 0) {
|
||||
context.reply(MessageFunction.buildViewQueueMessage(player, true));
|
||||
} else {
|
||||
context.reply("Queue is empty!");
|
||||
}
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
MusicManager manager = MusicManager.get(context.getGuild());
|
||||
context.ackAndWait();
|
||||
|
||||
if (MusicUtils.checkIfConnected(manager, context)) {
|
||||
int queueSize = manager.getQueueSize();
|
||||
|
||||
if (queueSize > 0) {
|
||||
context.reply(MessageFunction.buildViewQueueMessage(manager, true));
|
||||
} else {
|
||||
return CommandExecutionResult.warn("Queue is empty!");
|
||||
}
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package me.bobert.netneutrality.command.music;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import me.bobert.netneutrality.utils.MusicUtils;
|
||||
import me.bobert.netneutrality.utils.UserFunction;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
|
||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
|
||||
public class VolumeCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "volume";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Sets the volume for the music player";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
data.addOptions(new OptionData(OptionType.INTEGER, "volume", "Playback volume for this session", true)
|
||||
.setRequiredRange(0, 100));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
MusicManager player = MusicManager.get(context.getGuild());
|
||||
|
||||
if (!MusicUtils.checkIfConnected(player, context))
|
||||
return CommandExecutionResult.none();
|
||||
|
||||
if (UserFunction.isAdmin(context.getGuildUser()) || MusicUtils.isUserDj(context.getGuildUser())) {
|
||||
try {
|
||||
int volume = Integer.parseInt(context.getArgument(0));
|
||||
|
||||
if (volume > 0 && volume <= 100) {
|
||||
player.setVolume(volume);
|
||||
context.reply("Volume set to %s", volume);
|
||||
} else
|
||||
throw new NumberFormatException();
|
||||
|
||||
} catch (NumberFormatException e) {
|
||||
context.reply("Please enter a value from 0 to 100");
|
||||
}
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
MusicManager manager = MusicManager.get(context.getGuild());
|
||||
int volume = context.getEvent().getOption("volume", 30, OptionMapping::getAsInt);
|
||||
|
||||
if (MusicUtils.checkIfConnected(manager, context)) {
|
||||
manager.setVolume(volume);
|
||||
return CommandExecutionResult.success(String.format("Volume set to %s", volume));
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package me.bobert.netneutrality.command.music;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractMusicCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import me.bobert.netneutrality.utils.MusicUtils;
|
||||
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
|
||||
|
||||
public class VoteSkipCommand extends AbstractMusicCommand {
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommandName() {
|
||||
return "voteskip";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return "Cast a vote to skip the currently playing track";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerSlashCommandOptions(SlashCommandData data) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(MessageCommandContext context) {
|
||||
MusicManager manager = MusicManager.get(context.getGuild());
|
||||
|
||||
if (MusicUtils.checkIfConnected(manager, context)) {
|
||||
manager.voteSkip(context);
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandExecutionResult execute(SlashCommandContext context) {
|
||||
MusicManager manager = MusicManager.get(context.getGuild());
|
||||
|
||||
if (MusicUtils.checkIfConnected(manager, context)) {
|
||||
return manager.voteSkip(context);
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package me.bobert.netneutrality.core;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import me.bobert.netneutrality.music.TrackScheduler;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public class BotState {
|
||||
public static boolean SAFE = false;
|
||||
|
||||
//TODO: is it needed?
|
||||
private static void saveGuildsState() {
|
||||
Map<String, GuildManager> managers = GuildManager.getManagers();
|
||||
|
||||
JSONObject main = new JSONObject();
|
||||
JSONArray guildsArray = new JSONArray();
|
||||
|
||||
for (GuildManager manager : managers.values()) {
|
||||
MusicManager musicManager = MusicManager.get(manager.getGuild());
|
||||
TrackScheduler trackScheduler = musicManager.getScheduler();
|
||||
|
||||
JSONObject guild = new JSONObject();
|
||||
JSONObject music = new JSONObject();
|
||||
guild.put("id", manager.getGuild().getId());
|
||||
|
||||
MessageChannel currentChannel = trackScheduler.getTextChannel();
|
||||
if (currentChannel == null) {
|
||||
music.put("current_channel", "null");
|
||||
} else {
|
||||
music.put("current_channel", currentChannel.getId());
|
||||
}
|
||||
|
||||
guild.put("music", music);
|
||||
|
||||
guildsArray.put(guild);
|
||||
}
|
||||
|
||||
main.put("guilds", guildsArray);
|
||||
|
||||
File state = new File("state.json");
|
||||
try {
|
||||
if (!state.exists()) {
|
||||
state.createNewFile();
|
||||
}
|
||||
|
||||
try (FileWriter writer = new FileWriter(state)) {
|
||||
writer.write(main.toString());
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static void dump() {
|
||||
if (!SAFE) {
|
||||
saveGuildsState();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package me.bobert.netneutrality.core;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import io.sentry.Sentry;
|
||||
import me.bobert.netneutrality.command.AbstractCommand;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.command.CommandManager;
|
||||
import me.bobert.netneutrality.core.context.CommandContext;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.exceptions.CommandExecutionException;
|
||||
import me.bobert.netneutrality.utils.UserFunction;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import static me.bobert.netneutrality.command.CommandExecutionResult.Result.ERROR;
|
||||
import static me.bobert.netneutrality.command.CommandExecutionResult.Result.NOACTION;
|
||||
|
||||
public class CommandProcessor {
|
||||
|
||||
private final static Logger logger = LogManager.getLogger(CommandProcessor.class);
|
||||
private final String commandName;
|
||||
private final CommandContext context;
|
||||
|
||||
public CommandProcessor(String commandName, CommandContext context) {
|
||||
this.commandName = commandName;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public void dispatch() {
|
||||
|
||||
if (!CommandManager.isValidCommand(commandName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
AbstractCommand command = CommandManager.getCommand(commandName);
|
||||
logger.info("Executing command {} on guild {}", command.getCommandName(), context.getGuild().getId());
|
||||
|
||||
if (context instanceof SlashCommandContext slashCommandContext)
|
||||
logger.debug("SlashCommandInteractionId: {}", slashCommandContext.getEvent().getInteraction().getId());
|
||||
|
||||
configureSentry(command);
|
||||
|
||||
try {
|
||||
|
||||
if (context instanceof MessageCommandContext && command.needAdmin() && !UserFunction.isAdmin(context.getGuildUser())) {
|
||||
context.reply("You are not powerful enough, scrub");
|
||||
return;
|
||||
}
|
||||
|
||||
if (command.canBeExecutedBy(context.getGuild(), context.getGuildUser())) {
|
||||
if (command.canBeExecutedFrom(context.getGuild(), context.getChannel())) {
|
||||
CommandExecutionResult commandExecutionResult = null;
|
||||
|
||||
if (context instanceof SlashCommandContext slashCommandContext) {
|
||||
commandExecutionResult = command.execute(slashCommandContext);
|
||||
}
|
||||
|
||||
if (context instanceof MessageCommandContext messageCommandContext) {
|
||||
commandExecutionResult = command.execute(messageCommandContext);
|
||||
}
|
||||
|
||||
if (commandExecutionResult == null) {
|
||||
throw new RuntimeException("Command type is not implemented.");
|
||||
}
|
||||
|
||||
CommandExecutionResult.Result result = commandExecutionResult.getResult();
|
||||
String message = commandExecutionResult.getMessage();
|
||||
if (result == NOACTION) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (result == ERROR) {
|
||||
Exception exception = commandExecutionResult.getException();
|
||||
|
||||
logger.error("Error from command {}: {}", command.getCommandName(), message, exception);
|
||||
}
|
||||
|
||||
context.reply(message);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: make better
|
||||
} catch (CommandExecutionException exception) {
|
||||
LogHandler.log(exception);
|
||||
} catch (Exception exception) {
|
||||
LogHandler.log(new CommandExecutionException("Error during command execution", context, exception));
|
||||
}
|
||||
}
|
||||
|
||||
private void configureSentry(AbstractCommand command) {
|
||||
|
||||
Sentry.configureScope(scope -> {
|
||||
scope.setContexts("command", command.getCommandName());
|
||||
scope.setContexts("isAdmin", UserFunction.isAdmin(context.getGuildUser()));
|
||||
scope.setContexts("canBeExecuted", command.canBeExecutedBy(context.getGuild(), context.getGuildUser()));
|
||||
scope.setTag("channel", context.getChannel().getId());
|
||||
|
||||
if (context instanceof SlashCommandContext slashCommandContext)
|
||||
scope.setTag("interactionId", slashCommandContext.getEvent().getInteraction().getId());
|
||||
|
||||
if (context instanceof MessageCommandContext messageCommandContext)
|
||||
scope.setTag("message", messageCommandContext.getMessage().getId());
|
||||
|
||||
scope.setTag("guild", context.getGuild().getId());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package me.bobert.netneutrality.core;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.db.GSettings;
|
||||
import me.bobert.netneutrality.db.entities.OGuild;
|
||||
import me.bobert.netneutrality.db.entities.OGuildSetting;
|
||||
import me.bobert.netneutrality.utils.JpaUtils;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.hibernate.Session;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class GuildConfigManager {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(GuildConfigManager.class);
|
||||
private final Guild guild;
|
||||
private final OGuild guildEntity;
|
||||
|
||||
private final Map<String, String> settingsCache = new HashMap<>();
|
||||
|
||||
public GuildConfigManager(Guild guild, OGuild guildEntity) {
|
||||
this.guild = guild;
|
||||
this.guildEntity = guildEntity;
|
||||
|
||||
reloadSettingsCache();
|
||||
}
|
||||
|
||||
public Map<String, String> getSettings() {
|
||||
return settingsCache;
|
||||
}
|
||||
|
||||
public String getSetting(GSettings setting) {
|
||||
String value = settingsCache.get(setting.getName());
|
||||
|
||||
if (value == null) {
|
||||
value = setting.getDefaultValue();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
public void setSettingValue(GSettings setting, String value) {
|
||||
Session session = SessionManager.getSessionFactory().openSession();
|
||||
JpaUtils.tryLoad(session, guildEntity, guildEntity.getGuildId());
|
||||
|
||||
OGuildSetting oGuildSetting = guildEntity.getSettingByName(setting.getName());
|
||||
|
||||
session.beginTransaction();
|
||||
if (value == null || setting.getDefaultValue().equals(value)) {
|
||||
if (oGuildSetting != null) {
|
||||
session.delete(oGuildSetting);
|
||||
}
|
||||
} else {
|
||||
if (oGuildSetting == null) {
|
||||
oGuildSetting = new OGuildSetting();
|
||||
oGuildSetting.setGuild(guildEntity);
|
||||
oGuildSetting.setName(setting.getName());
|
||||
oGuildSetting.setValue(value);
|
||||
|
||||
session.save(oGuildSetting);
|
||||
} else {
|
||||
oGuildSetting.setValue(value);
|
||||
|
||||
session.update(oGuildSetting);
|
||||
}
|
||||
}
|
||||
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
reloadSettingsCache();
|
||||
}
|
||||
|
||||
public void reloadSettingsCache() {
|
||||
logger.debug("Reloading settings cache for guild: " + guild.getId());
|
||||
settingsCache.clear();
|
||||
|
||||
JpaUtils.getWithSession(guildEntity, session -> {
|
||||
Set<OGuildSetting> settingSet = guildEntity.getGuildSettings();
|
||||
|
||||
for (OGuildSetting setting : settingSet) {
|
||||
settingsCache.put(setting.getName(), setting.getValue());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package me.bobert.netneutrality.core;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.NetNeutrality;
|
||||
import me.bobert.netneutrality.db.DBController;
|
||||
import me.bobert.netneutrality.db.controllers.DBMessage;
|
||||
import me.bobert.netneutrality.db.entities.OBlacklist;
|
||||
import me.bobert.netneutrality.db.entities.OGuild;
|
||||
import me.bobert.netneutrality.thread.ThreadManager;
|
||||
import me.bobert.netneutrality.utils.JpaUtils;
|
||||
import me.bobert.netneutrality.utils.MessageFunction;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class GuildManager {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(GuildManager.class);
|
||||
private static final Map<String, GuildManager> managers = new ConcurrentHashMap<>();
|
||||
private static final SessionFactory sessionFactory = SessionManager.getSessionFactory();
|
||||
private final Set<String> blacklistCache = new HashSet<>();
|
||||
private final GuildConfigManager guildConfigManager;
|
||||
private final Guild guild;
|
||||
private OGuild guildEntity;
|
||||
|
||||
private GuildManager(Guild guild) {
|
||||
this.guild = guild;
|
||||
this.guildEntity = loadGuildFromDb(guild.getId());
|
||||
this.guildConfigManager = new GuildConfigManager(guild, this.guildEntity);
|
||||
|
||||
managers.put(guild.getId(), this);
|
||||
reloadBlacklistCache();
|
||||
}
|
||||
|
||||
private GuildManager(String guildId) {
|
||||
Guild temp = NetNeutrality.getJDA().getGuildById(guildId);
|
||||
if (temp == null)
|
||||
throw new IllegalArgumentException("Guild does not exist in current cache");
|
||||
|
||||
this.guild = temp;
|
||||
this.guildEntity = loadGuildFromDb(guild.getId());
|
||||
this.guildConfigManager = new GuildConfigManager(guild, this.guildEntity);
|
||||
|
||||
managers.put(guild.getId(), this);
|
||||
reloadBlacklistCache();
|
||||
}
|
||||
|
||||
public static GuildManager getManager(Guild guild) {
|
||||
if (managers.containsKey(guild.getId())) {
|
||||
return managers.get(guild.getId());
|
||||
} else {
|
||||
return new GuildManager(guild);
|
||||
}
|
||||
}
|
||||
|
||||
public static GuildManager getManager(String guildId) {
|
||||
if (managers.containsKey(guildId)) {
|
||||
return managers.get(guildId);
|
||||
} else {
|
||||
return new GuildManager(guildId);
|
||||
}
|
||||
}
|
||||
|
||||
// Getter
|
||||
public static Map<String, GuildManager> getManagers() {
|
||||
return managers;
|
||||
}
|
||||
|
||||
public Guild getGuild() {
|
||||
return guild;
|
||||
}
|
||||
|
||||
public OGuild getGuildEntity() {
|
||||
return guildEntity;
|
||||
}
|
||||
|
||||
public Set<String> getBlacklistCache() {
|
||||
return blacklistCache;
|
||||
}
|
||||
|
||||
public GuildConfigManager getGuildConfigManager() {
|
||||
return guildConfigManager;
|
||||
}
|
||||
|
||||
// Methods
|
||||
public boolean isLoggingEnabled() {
|
||||
return guildEntity.getLogging();
|
||||
}
|
||||
|
||||
public boolean isDebugEnabled() {
|
||||
return guildEntity.getDebug();
|
||||
}
|
||||
|
||||
public void setDebug(boolean toggle) {
|
||||
if (toggle == isDebugEnabled())
|
||||
return;
|
||||
|
||||
JpaUtils.updateWithTransaction(guildEntity, () -> guildEntity.setDebug(toggle));
|
||||
}
|
||||
|
||||
public void reloadBlacklistCache() {
|
||||
logger.debug("Reloading blacklist cache for guild: " + guild.getId());
|
||||
blacklistCache.clear();
|
||||
|
||||
JpaUtils.getWithSession(guildEntity, session -> {
|
||||
Set<OBlacklist> oBlacklistSet = guildEntity.getBlacklists();
|
||||
|
||||
for (OBlacklist bl : oBlacklistSet) {
|
||||
if (bl.getEnabled()) {
|
||||
blacklistCache.add(bl.getWord());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void checkAndLogMessage(Message message, Member member, MessageChannel channel) {
|
||||
if (isLoggingEnabled()) {
|
||||
ThreadManager.getExecutor().submit(() -> DBController.get(DBMessage.class).log(this, channel, member, message));
|
||||
}
|
||||
}
|
||||
|
||||
public void checkBlacklist(Message message, MessageChannel channel) {
|
||||
MessageFunction.testMessageAgainstBlacklist(message, channel, blacklistCache);
|
||||
}
|
||||
|
||||
private OGuild loadGuildFromDb(String guildId) {
|
||||
Session session = sessionFactory.openSession();
|
||||
OGuild entity = session.get(OGuild.class, guildId);
|
||||
|
||||
if (entity == null) {
|
||||
logger.debug("Guild[{}] doesn't exist in DB, creating entity..", guildId);
|
||||
session.beginTransaction();
|
||||
entity = new OGuild();
|
||||
|
||||
entity.setGuildId(guildId);
|
||||
|
||||
session.save(entity);
|
||||
session.getTransaction().commit();
|
||||
|
||||
guild.getTextChannels()
|
||||
.stream()
|
||||
.filter(textChannel -> textChannel.getName().equals("debug") || textChannel.getName().equals("log"))
|
||||
.forEach(textChannel -> textChannel.sendMessage("Error logging enabled on channel " + textChannel.getAsMention()).queue());
|
||||
}
|
||||
|
||||
session.close();
|
||||
return entity;
|
||||
}
|
||||
|
||||
public void reloadEntity() {
|
||||
this.guildEntity = loadGuildFromDb(guild.getId());
|
||||
}
|
||||
|
||||
public void removeFromDB() {
|
||||
Session session = sessionFactory.openSession();
|
||||
session.beginTransaction();
|
||||
|
||||
session.delete(guildEntity);
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
managers.remove(guild.getId());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package me.bobert.netneutrality.core;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import io.sentry.Sentry;
|
||||
import me.bobert.netneutrality.core.context.CommandContext;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import me.bobert.netneutrality.exceptions.CommandException;
|
||||
import me.bobert.netneutrality.exceptions.CommandExecutionException;
|
||||
import me.bobert.netneutrality.utils.MessageFunction;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class LogHandler {
|
||||
|
||||
private final static Logger logger = LogManager.getLogger(LogHandler.class);
|
||||
|
||||
/**
|
||||
* Log an exception accordingly
|
||||
*
|
||||
* @param e The exceptions to log
|
||||
*/
|
||||
public static void log(Exception e) {
|
||||
Sentry.captureException(e);
|
||||
|
||||
if (e instanceof CommandExecutionException exception) {
|
||||
CommandContext context = exception.getCommandContext();
|
||||
|
||||
logInternally(exception, context);
|
||||
tryLogToDebugChannel(exception, context);
|
||||
tryGracefulSlashContextError(exception, context);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
|
||||
public static void log(String s, Exception e) {
|
||||
logger.error(s, e);
|
||||
}
|
||||
|
||||
public static void logThread(Thread t, Throwable e) {
|
||||
logger.error("Uncaught exception thrown in thread {}", t);
|
||||
log((Exception) e);
|
||||
}
|
||||
|
||||
public static void sendMessageToDebugChannel(Guild guild, String message) {
|
||||
if (GuildManager.getManager(guild).isDebugEnabled()) {
|
||||
guild.getTextChannels()
|
||||
.stream()
|
||||
.filter(textChannel -> textChannel.getName().equals("debug") || textChannel.getName().equals("log"))
|
||||
.filter(TextChannel::canTalk)
|
||||
.forEach(textChannel -> textChannel.sendMessage(message).queue());
|
||||
}
|
||||
}
|
||||
|
||||
private static void logInternally(CommandException exception, CommandContext context) {
|
||||
logger.error("Exception thrown from command [{}] in guild [{}]: {}", context.getCommand().getCommandName(), context.getGuild().getId(), exception.getMessage(), exception);
|
||||
}
|
||||
|
||||
private static void tryLogToDebugChannel(CommandException exception, CommandContext context) {
|
||||
if (context.getGuild() != null) {
|
||||
if (GuildManager.getManager(context.getGuild()).isDebugEnabled()) {
|
||||
context.getGuild()
|
||||
.getTextChannels()
|
||||
.stream()
|
||||
.filter(textChannel -> textChannel.getName().equals("debug") || textChannel.getName().equals("log"))
|
||||
.filter(TextChannel::canTalk)
|
||||
.forEach(textChannel -> textChannel.sendMessageEmbeds(MessageFunction.buildDebugMessage(context, exception)).queue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void tryGracefulSlashContextError(CommandException exception, CommandContext context) {
|
||||
if (context instanceof SlashCommandContext slashCommandContext) {
|
||||
slashCommandContext.reply(MessageFunction.buildSentryErrorMessage(exception.getMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package me.bobert.netneutrality.core;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import io.sentry.Sentry;
|
||||
import me.bobert.netneutrality.utils.Settings;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.Environment;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SessionManager {
|
||||
private final static Logger logger = LogManager.getLogger(SessionManager.class);
|
||||
private static StandardServiceRegistry registry;
|
||||
private static SessionFactory sessionFactory;
|
||||
|
||||
public static SessionFactory getSessionFactory() {
|
||||
if (sessionFactory == null) {
|
||||
try {
|
||||
StandardServiceRegistryBuilder registryBuilder = new StandardServiceRegistryBuilder().configure();
|
||||
Map<String, Object> settings = new HashMap<>();
|
||||
String dbName = Settings.getInstance().get("NETNEUTRALITY_MYSQL_DATABASE", "netneutrality");
|
||||
String dbUser = Settings.getInstance().get("NETNEUTRALITY_MYSQL_USER", "root");
|
||||
String dbPass = Settings.getInstance().get("NETNEUTRALITY_MYSQL_PASSWORD", "");
|
||||
String dbHost = Settings.getInstance().get("NETNEUTRALITY_MYSQL_HOST", "localhost");
|
||||
logger.info(String.format("Connecting to database '%s' at %s:3306 with user '%s'", dbName, dbHost, dbUser));
|
||||
|
||||
settings.put(Environment.URL, String.format("jdbc:mysql://%s:3306/%s", dbHost, dbName));
|
||||
settings.put(Environment.USER, dbUser);
|
||||
settings.put(Environment.PASS, dbPass);
|
||||
registryBuilder.applySettings(settings);
|
||||
|
||||
registry = registryBuilder.build();
|
||||
MetadataSources sources = new MetadataSources(registry);
|
||||
Metadata metadata = sources.getMetadataBuilder().build();
|
||||
sessionFactory = metadata.getSessionFactoryBuilder().build();
|
||||
|
||||
} catch (Exception e) {
|
||||
String env = Settings.getInstance().get("NETNEUTRALITY_BOT_ENV", "dev");
|
||||
if (!"dev".equals(env)) {
|
||||
Sentry.captureException(e);
|
||||
}
|
||||
|
||||
shutdown();
|
||||
logger.error("Error connecting to database. Shutting down..");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
return sessionFactory;
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
if (registry != null) {
|
||||
StandardServiceRegistryBuilder.destroy(registry);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package me.bobert.netneutrality.core.context;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractCommand;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.events.Event;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||
|
||||
public interface CommandContext {
|
||||
|
||||
AbstractCommand getCommand();
|
||||
|
||||
Event getEvent();
|
||||
|
||||
Guild getGuild();
|
||||
|
||||
MessageChannel getChannel();
|
||||
|
||||
Member getGuildUser();
|
||||
|
||||
User getDiscordUser();
|
||||
|
||||
JDA getJDA();
|
||||
|
||||
void deleteMessage();
|
||||
|
||||
void reply(MessageCreateData message);
|
||||
|
||||
void reply(String message);
|
||||
|
||||
void reply(MessageEmbed... embeds);
|
||||
|
||||
default void reply(String format, Object... arguments) {
|
||||
reply(String.format(format, arguments));
|
||||
}
|
||||
|
||||
void replyToMessage(String message);
|
||||
|
||||
default void replyToMessage(MessageCreateData message) {
|
||||
reply(message);
|
||||
}
|
||||
|
||||
default void replyToMessage(String format, Object... arguments) {
|
||||
replyToMessage(String.format(format, arguments));
|
||||
}
|
||||
|
||||
default void replyToMessage(MessageEmbed... embeds) {
|
||||
replyToMessage(MessageCreateData.fromEmbeds(embeds));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package me.bobert.netneutrality.core.context;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import io.sentry.Breadcrumb;
|
||||
import io.sentry.Sentry;
|
||||
import me.bobert.netneutrality.command.AbstractCommand;
|
||||
import me.bobert.netneutrality.core.GuildManager;
|
||||
import me.bobert.netneutrality.thread.ThreadManager;
|
||||
import me.bobert.netneutrality.utils.Settings;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.*;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class MessageCommandContext implements CommandContext {
|
||||
|
||||
private static final ExecutorService commandExecutor = ThreadManager.getExecutor();
|
||||
|
||||
private AbstractCommand command;
|
||||
private final MessageReceivedEvent event;
|
||||
private final Message message;
|
||||
private final Guild guild;
|
||||
private final MessageChannel channel;
|
||||
private final Member member;
|
||||
private final User user;
|
||||
private final JDA jda;
|
||||
|
||||
public MessageCommandContext(MessageReceivedEvent event, Message message, Member member, User user, Guild guild, MessageChannel channel, JDA jda) {
|
||||
this.event = event;
|
||||
this.message = message;
|
||||
this.member = member;
|
||||
this.guild = guild;
|
||||
this.channel = channel;
|
||||
this.user = user;
|
||||
this.jda = jda;
|
||||
|
||||
io.sentry.protocol.User sentryUser = new io.sentry.protocol.User();
|
||||
sentryUser.setId(member.getId());
|
||||
sentryUser.setUsername(member.getEffectiveName());
|
||||
Sentry.setUser(sentryUser);
|
||||
Sentry.addBreadcrumb(Breadcrumb.info("New context for message id " + message.getId()));
|
||||
}
|
||||
|
||||
public static MessageCommandContext createContext(MessageReceivedEvent event) {
|
||||
Message message = event.getMessage();
|
||||
Member member = event.getMember();
|
||||
User user = event.getAuthor();
|
||||
Guild guild = event.getGuild();
|
||||
MessageChannel channel = event.getChannel();
|
||||
JDA jda = event.getJDA();
|
||||
|
||||
return new MessageCommandContext(event, message, member, user, guild, channel, jda);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageReceivedEvent getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
public String getArgument(int index) {
|
||||
return getArgs()[index];
|
||||
}
|
||||
|
||||
public String[] getArgs() {
|
||||
String msg = message.getContentDisplay().trim();
|
||||
msg = msg.substring(Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$").length()).trim();
|
||||
String cmd = msg.split(" ", 2)[0];
|
||||
String[] args = msg.replaceFirst("(" + cmd + ")(\\s+)?", "").split("\s");
|
||||
|
||||
if (args.length == 1 && args[0].isEmpty())
|
||||
args = new String[0];
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
public String getMessageText() {
|
||||
String msg = message.getContentDisplay().trim();
|
||||
msg = msg.substring(Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$").length()).trim();
|
||||
String cmd = msg.split(" ", 2)[0];
|
||||
|
||||
return msg.replaceFirst("(" + cmd + ")(\s)?", "");
|
||||
}
|
||||
|
||||
public Message getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public Guild getGuild() {
|
||||
return guild;
|
||||
}
|
||||
|
||||
public MessageChannel getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public Member getGuildUser() {
|
||||
return member;
|
||||
}
|
||||
|
||||
public User getDiscordUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
public JDA getJDA() {
|
||||
return jda;
|
||||
}
|
||||
|
||||
public AbstractCommand getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
public GuildManager getGuildManager() {
|
||||
return GuildManager.getManager(guild);
|
||||
}
|
||||
|
||||
public void reply(String message) {
|
||||
reply(MessageCreateData.fromContent(message));
|
||||
}
|
||||
|
||||
public void reply(String format, Object... arguments) {
|
||||
reply(String.format(format, arguments));
|
||||
}
|
||||
|
||||
public void reply(MessageEmbed... embeds) {
|
||||
reply(MessageCreateData.fromEmbeds(embeds));
|
||||
}
|
||||
|
||||
public void reply(MessageCreateData msg) {
|
||||
if (channel != null && channel.canTalk()) {
|
||||
channel.sendMessage(msg).queue();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replyToMessage(String message) {
|
||||
replyToMessage(MessageCreateData.fromContent(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replyToMessage(String format, Object... arguments) {
|
||||
replyToMessage(String.format(format, arguments));
|
||||
}
|
||||
|
||||
public void replyToMessage(MessageCreateData msg) {
|
||||
if (channel != null && channel.canTalk() && message != null) {
|
||||
message.reply(msg).queue();
|
||||
}
|
||||
}
|
||||
|
||||
public void replyPrivate(String message) {
|
||||
replyPrivate(MessageCreateData.fromContent(message));
|
||||
}
|
||||
|
||||
public void replyPrivate(MessageCreateData msg) {
|
||||
user.openPrivateChannel().flatMap(channel -> channel.sendMessage(msg)).queue();
|
||||
}
|
||||
|
||||
public void replyPrivate(MessageEmbed... embeds) {
|
||||
replyPrivate(MessageCreateData.fromEmbeds(embeds));
|
||||
}
|
||||
|
||||
public void replyPrivate(Collection<? extends MessageEmbed> embeds) {
|
||||
user.openPrivateChannel().flatMap(channel -> channel.sendMessageEmbeds(embeds)).queue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteMessage() {
|
||||
if (message != null) {
|
||||
message.delete().queueAfter(1, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMessageFromOwner() {
|
||||
return member.isOwner();
|
||||
}
|
||||
|
||||
public boolean isMessageFromCreator() {
|
||||
String creatorId = Settings.getInstance().get("NETNEUTRALITY_BOT_OWNER");
|
||||
|
||||
return creatorId.equals(member.getId());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
package me.bobert.netneutrality.core.context;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.command.AbstractCommand;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.entities.User;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.interactions.InteractionHook;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageEditData;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class SlashCommandContext implements CommandContext {
|
||||
|
||||
private final SlashCommandInteractionEvent event;
|
||||
private AbstractCommand command;
|
||||
private final JDA jda;
|
||||
private final Guild guild;
|
||||
private final MessageChannel channel;
|
||||
private final Member member;
|
||||
private final User user;
|
||||
|
||||
private SlashCommandContext(JDA jda, SlashCommandInteractionEvent event, Guild guild, MessageChannel channel, Member member, User user) {
|
||||
this.jda = jda;
|
||||
this.event = event;
|
||||
this.guild = guild;
|
||||
this.channel = channel;
|
||||
this.member = member;
|
||||
this.user = user;
|
||||
}
|
||||
|
||||
public static SlashCommandContext createContext(SlashCommandInteractionEvent event) {
|
||||
Member member = event.getMember();
|
||||
User user = event.getUser();
|
||||
Guild guild = event.getGuild();
|
||||
MessageChannel channel = event.getChannel();
|
||||
JDA jda = event.getJDA();
|
||||
|
||||
return new SlashCommandContext(jda, event, guild, channel, member, user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SlashCommandInteractionEvent getEvent() {
|
||||
return event;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractCommand getCommand() {
|
||||
return command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JDA getJDA() {
|
||||
return jda;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Guild getGuild() {
|
||||
return guild;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MessageChannel getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Member getGuildUser() {
|
||||
return member;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getDiscordUser() {
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteMessage() {
|
||||
// not implemented
|
||||
}
|
||||
|
||||
public void sendMessageInPrivate(String msg) {
|
||||
sendMessageInPrivate(new MessageCreateBuilder().setContent(msg).build());
|
||||
}
|
||||
|
||||
public void sendMessageInPrivate(MessageCreateData msg) {
|
||||
user.openPrivateChannel().flatMap(channel -> channel.sendMessage(msg)).queue();
|
||||
}
|
||||
|
||||
public void sendMessageInPrivate(MessageEmbed embed, MessageEmbed... embeds) {
|
||||
user.openPrivateChannel().flatMap(channel -> channel.sendMessageEmbeds(embed, embeds)).queue();
|
||||
}
|
||||
|
||||
public void sendMessageInPrivate(Collection<? extends MessageEmbed> embeds) {
|
||||
user.openPrivateChannel().flatMap(channel -> channel.sendMessageEmbeds(embeds)).queue();
|
||||
}
|
||||
|
||||
public void reply(MessageCreateData message) {
|
||||
if (event.isAcknowledged()) {
|
||||
getHook().editOriginal(MessageEditData.fromCreateData(message)).queue();
|
||||
//getHook().sendMessage(message).setEphemeral(false).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
event.reply(message).queue();
|
||||
}
|
||||
|
||||
public void reply(MessageEmbed... embeds) {
|
||||
reply(MessageCreateData.fromEmbeds(embeds));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reply(String message) {
|
||||
reply(MessageCreateData.fromContent(message));
|
||||
}
|
||||
|
||||
public void replyEphemeral(MessageCreateData message) {
|
||||
if (event.isAcknowledged()) {
|
||||
getHook().editOriginal(MessageEditData.fromCreateData(message)).queue();
|
||||
//getHook().sendMessage(message).setEphemeral(true).queue();
|
||||
return;
|
||||
}
|
||||
|
||||
event.reply(message).setEphemeral(true).queue();
|
||||
}
|
||||
|
||||
public void replyEphemeral(MessageEmbed... embeds) {
|
||||
replyEphemeral(MessageCreateData.fromEmbeds(embeds));
|
||||
}
|
||||
|
||||
public void replyEphemeral(String format, Object... arguments) {
|
||||
replyEphemeral(String.format(format, arguments));
|
||||
}
|
||||
|
||||
public void replyEphemeral(String message) {
|
||||
replyEphemeral(MessageCreateData.fromContent(message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replyToMessage(String message) {
|
||||
replyEphemeral(message);
|
||||
}
|
||||
|
||||
public InteractionHook getHook() {
|
||||
if (!event.isAcknowledged())
|
||||
throw new RuntimeException("Interaction isn't acknowledged yet");
|
||||
|
||||
return event.getHook();
|
||||
}
|
||||
|
||||
public void ackAndWait(boolean ephemeral) {
|
||||
if (event.isAcknowledged())
|
||||
throw new RuntimeException("Interaction is already acknowledged, please use getHook() instead.");
|
||||
|
||||
event.deferReply(ephemeral).queue();
|
||||
}
|
||||
|
||||
public void ackAndWait() {
|
||||
ackAndWait(false);
|
||||
}
|
||||
|
||||
public void ack() {
|
||||
ackAndWait();
|
||||
}
|
||||
|
||||
public void ephemeral() {
|
||||
ackAndWait(true);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package me.bobert.netneutrality.db;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.core.SessionManager;
|
||||
import org.hibernate.SessionFactory;
|
||||
|
||||
public abstract class AbstractController {
|
||||
|
||||
protected SessionFactory sessionFactory;
|
||||
|
||||
public AbstractController() {
|
||||
sessionFactory = SessionManager.getSessionFactory();
|
||||
}
|
||||
|
||||
protected SessionFactory getSessionFactory() {
|
||||
return sessionFactory;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package me.bobert.netneutrality.db;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.core.SessionManager;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public class DBController {
|
||||
|
||||
private final static Logger logger = LogManager.getLogger(DBController.class);
|
||||
|
||||
public static <T> T get(Class<T> returnType) {
|
||||
if (returnType == null)
|
||||
throw new IllegalArgumentException("return type is null");
|
||||
|
||||
try {
|
||||
return returnType.getConstructor().newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void shutdown() {
|
||||
SessionManager.shutdown();
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
SessionManager.getSessionFactory();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package me.bobert.netneutrality.db;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
public enum GSettings {
|
||||
//TODO: description
|
||||
MUSIC_ROLE("MUSIC_ROLE", ""),
|
||||
MUSIC_CHANNEL("MUSIC_CHANNEL", ""),
|
||||
MUSIC_VOLUME("MUSIC_VOLUME", "30");
|
||||
|
||||
private final String name;
|
||||
private final String defaultValue;
|
||||
|
||||
GSettings(String name, String defaultValue) {
|
||||
this.name = name;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getDefaultValue() {
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package me.bobert.netneutrality.db.controllers;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.core.GuildManager;
|
||||
import me.bobert.netneutrality.core.context.CommandContext;
|
||||
import me.bobert.netneutrality.db.AbstractController;
|
||||
import me.bobert.netneutrality.db.entities.OBlacklist;
|
||||
import me.bobert.netneutrality.db.entities.OGuild;
|
||||
import me.bobert.netneutrality.utils.JpaUtils;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.query.Query;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class DBBlacklist extends AbstractController {
|
||||
|
||||
public void add(String word, CommandContext context) {
|
||||
GuildManager guildManager = GuildManager.getManager(context.getGuild());
|
||||
|
||||
|
||||
OBlacklist bl = getWordFromDb(word, context.getGuild().getId());
|
||||
|
||||
if (bl == null) {
|
||||
Session session = getSessionFactory().openSession();
|
||||
|
||||
session.beginTransaction();
|
||||
bl = new OBlacklist();
|
||||
bl.setGuild(guildManager.getGuildEntity());
|
||||
bl.setCreatorId(context.getGuildUser().getId());
|
||||
bl.setWord(word);
|
||||
bl.setEnabled(true);
|
||||
|
||||
session.save(bl);
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
guildManager.reloadBlacklistCache();
|
||||
context.reply(String.format("Added the word [%s] to the blacklist.", word));
|
||||
} else {
|
||||
context.reply("Word is already blacklisted");
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void delete(String word, CommandContext context) {
|
||||
GuildManager guildManager = GuildManager.getManager(context.getGuild());
|
||||
OBlacklist bl = getWordFromDb(word, context.getGuild().getId());
|
||||
|
||||
if (bl != null) {
|
||||
Session session = getSessionFactory().openSession();
|
||||
session.beginTransaction();
|
||||
|
||||
session.delete(bl);
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
guildManager.reloadBlacklistCache();
|
||||
context.reply("Removed the word [%s] from the blacklist.", word);
|
||||
} else {
|
||||
context.reply("Can't delete word because it doesn't exist");
|
||||
}
|
||||
}
|
||||
|
||||
public void enable(String word, CommandContext context) {
|
||||
GuildManager guildManager = GuildManager.getManager(context.getGuild());
|
||||
|
||||
|
||||
OBlacklist bl = getWordFromDb(word, context.getGuild().getId());
|
||||
if (bl != null && !bl.getEnabled()) {
|
||||
Session session = getSessionFactory().openSession();
|
||||
session.beginTransaction();
|
||||
|
||||
bl.setEnabled(true);
|
||||
|
||||
session.update(bl);
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
guildManager.reloadBlacklistCache();
|
||||
context.reply("Enabled the word [%s] for the blacklist.", word);
|
||||
} else {
|
||||
context.reply("Either word doesn't exist or is already enabled");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void disable(String word, CommandContext context) {
|
||||
GuildManager guildManager = GuildManager.getManager(context.getGuild());
|
||||
|
||||
|
||||
OBlacklist bl = getWordFromDb(word, context.getGuild().getId());
|
||||
if (bl != null && bl.getEnabled()) {
|
||||
Session session = getSessionFactory().openSession();
|
||||
session.beginTransaction();
|
||||
|
||||
bl.setEnabled(false);
|
||||
|
||||
session.update(bl);
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
|
||||
guildManager.reloadBlacklistCache();
|
||||
context.reply("Disabled the word [%s] for the blacklist.", word);
|
||||
} else {
|
||||
context.reply("Either word doesn't exist or is already disabled");
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<OBlacklist> getBlacklistForGuild(Guild guild) {
|
||||
GuildManager guildManager = GuildManager.getManager(guild);
|
||||
Session session = getSessionFactory().openSession();
|
||||
|
||||
OGuild guildEntity = guildManager.getGuildEntity();
|
||||
JpaUtils.tryLoad(session, guildEntity, guildEntity.getGuildId());
|
||||
|
||||
ArrayList<OBlacklist> blacklists = new ArrayList<>(guildEntity.getBlacklists());
|
||||
session.close();
|
||||
|
||||
return blacklists;
|
||||
}
|
||||
|
||||
private OBlacklist getWordFromDb(String word, String guildId) {
|
||||
Session session = getSessionFactory().openSession();
|
||||
|
||||
Query<OBlacklist> query = session.createNamedQuery("OBlacklist.findByWordEqualsIgnoreCaseAndGuild_GuildIdEquals", OBlacklist.class);
|
||||
query.setParameter("guildId", guildId);
|
||||
query.setParameter("word", word);
|
||||
OBlacklist blacklist = query.uniqueResult();
|
||||
|
||||
session.close();
|
||||
return blacklist;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package me.bobert.netneutrality.db.controllers;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.core.GuildManager;
|
||||
import me.bobert.netneutrality.db.AbstractController;
|
||||
import me.bobert.netneutrality.db.entities.OLogMessage;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.query.Query;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class DBMessage extends AbstractController {
|
||||
|
||||
private final static Logger logger = LogManager.getLogger(DBMessage.class);
|
||||
|
||||
public void log(GuildManager guildManager, MessageChannel channel, Member author, Message message) {
|
||||
Session session = getSessionFactory().openSession();
|
||||
session.beginTransaction();
|
||||
|
||||
OLogMessage logMessage = new OLogMessage();
|
||||
logMessage.setGuild(guildManager.getGuildEntity());
|
||||
logMessage.setMessageId(message.getId());
|
||||
logMessage.setMessage(message.getContentRaw().trim());
|
||||
logMessage.setChannelId(channel.getId());
|
||||
logMessage.setUserId(author.getId());
|
||||
logMessage.setDateSent(message.getTimeCreated().toInstant());
|
||||
|
||||
session.save(logMessage);
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
public void logEdit(String messageId, String newMessage) {
|
||||
Session session = getSessionFactory().openSession();
|
||||
Query<OLogMessage> query = session.createNamedQuery("OLogMessage.findByMessageIdEquals", OLogMessage.class);
|
||||
query.setParameter("messageId", messageId);
|
||||
|
||||
OLogMessage message = query.uniqueResult();
|
||||
if (message != null) {
|
||||
session.beginTransaction();
|
||||
message.setEditedMessage(newMessage);
|
||||
|
||||
session.update(message);
|
||||
session.getTransaction().commit();
|
||||
}
|
||||
|
||||
session.close();
|
||||
}
|
||||
|
||||
public OLogMessage getMessage(String messageId) {
|
||||
Session session = getSessionFactory().openSession();
|
||||
Query<OLogMessage> query = session.createNamedQuery("OLogMessage.findByMessageIdEquals", OLogMessage.class);
|
||||
query.setParameter("messageId", messageId);
|
||||
|
||||
OLogMessage message = query.uniqueResult();
|
||||
session.close();
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
public List<OLogMessage> getMessagesFromUser(String userId, String guildId, int max) {
|
||||
Session session = getSessionFactory().openSession();
|
||||
Query<OLogMessage> query = session.createNamedQuery("OLogMessage.findByUserIdEquals", OLogMessage.class);
|
||||
query.setParameter("guildId", guildId);
|
||||
query.setParameter("userId", userId);
|
||||
query.setMaxResults(max);
|
||||
|
||||
List<OLogMessage> messages = query.getResultList();
|
||||
session.close();
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
public String getMessageString(String messageId) {
|
||||
Session session = getSessionFactory().openSession();
|
||||
Query<OLogMessage> query = session.createNamedQuery("OLogMessage.findByMessageIdEquals", OLogMessage.class);
|
||||
query.setParameter("messageId", messageId);
|
||||
|
||||
OLogMessage message = query.uniqueResult();
|
||||
session.close();
|
||||
|
||||
if (message == null)
|
||||
return null;
|
||||
else
|
||||
return message.getMessage();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package me.bobert.netneutrality.db.entities;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "blacklist")
|
||||
@NamedQueries({
|
||||
@NamedQuery(name = "OBlacklist.findByWordEqualsIgnoreCaseAndGuild_GuildIdEquals", query = "select o from OBlacklist o where upper(o.word) = upper(:word) and o.guild.guildId = :guildId")
|
||||
})
|
||||
public class OBlacklist {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id", nullable = false)
|
||||
private Integer id;
|
||||
|
||||
@Column(name = "word", nullable = false)
|
||||
private String word;
|
||||
|
||||
@Column(name = "enabled", nullable = false)
|
||||
private Boolean enabled = false;
|
||||
|
||||
@Column(name = "creator_id", nullable = false)
|
||||
private String creatorId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "guild_id", nullable = false)
|
||||
private OGuild guild;
|
||||
|
||||
public OGuild getGuild() {
|
||||
return guild;
|
||||
}
|
||||
|
||||
public void setGuild(OGuild guild) {
|
||||
this.guild = guild;
|
||||
}
|
||||
|
||||
public String getCreatorId() {
|
||||
return creatorId;
|
||||
}
|
||||
|
||||
public void setCreatorId(String creatorId) {
|
||||
this.creatorId = creatorId;
|
||||
}
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getWord() {
|
||||
return word;
|
||||
}
|
||||
|
||||
public void setWord(String word) {
|
||||
this.word = word;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package me.bobert.netneutrality.db.entities;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
@Entity
|
||||
@Table(name = "guilds")
|
||||
public class OGuild {
|
||||
|
||||
@Id
|
||||
@Column(name = "guild_id", nullable = false)
|
||||
private String guildId;
|
||||
|
||||
@Column(name = "logging", nullable = false)
|
||||
private Boolean logging = false;
|
||||
|
||||
@Column(name = "debug", nullable = false)
|
||||
private Boolean debug = true;
|
||||
|
||||
@Column(name = "log_channel")
|
||||
private String logChannel;
|
||||
|
||||
@OneToMany(mappedBy = "guild")
|
||||
private Set<OGuildSetting> guildSettings = new LinkedHashSet<>();
|
||||
|
||||
@OneToMany(mappedBy = "guild")
|
||||
private Set<OBlacklist> blacklists = new LinkedHashSet<>();
|
||||
|
||||
@OneToMany(mappedBy = "guild")
|
||||
private Set<OLogMessage> logMessages = new LinkedHashSet<>();
|
||||
|
||||
@OneToMany(mappedBy = "guild")
|
||||
private Set<OLogging> OLoggings = new LinkedHashSet<>();
|
||||
|
||||
public Set<OLogging> getLoggings() {
|
||||
return OLoggings;
|
||||
}
|
||||
|
||||
public void setLoggings(Set<OLogging> OLoggings) {
|
||||
this.OLoggings = OLoggings;
|
||||
}
|
||||
|
||||
public Set<OLogMessage> getLogMessages() {
|
||||
return logMessages;
|
||||
}
|
||||
|
||||
public void setLogMessages(Set<OLogMessage> logMessages) {
|
||||
this.logMessages = logMessages;
|
||||
}
|
||||
|
||||
public Set<OBlacklist> getBlacklists() {
|
||||
return blacklists;
|
||||
}
|
||||
|
||||
public void setBlacklists(Set<OBlacklist> blacklists) {
|
||||
this.blacklists = blacklists;
|
||||
}
|
||||
|
||||
public Set<OGuildSetting> getGuildSettings() {
|
||||
return guildSettings;
|
||||
}
|
||||
|
||||
public void setGuildSettings(Set<OGuildSetting> guildSettings) {
|
||||
this.guildSettings = guildSettings;
|
||||
}
|
||||
|
||||
public String getLogChannel() {
|
||||
return logChannel;
|
||||
}
|
||||
|
||||
public void setLogChannel(String logChannel) {
|
||||
this.logChannel = logChannel;
|
||||
}
|
||||
|
||||
public Boolean getLogging() {
|
||||
return logging;
|
||||
}
|
||||
|
||||
public void setLogging(Boolean logging) {
|
||||
this.logging = logging;
|
||||
}
|
||||
|
||||
public Boolean getDebug() {
|
||||
return debug;
|
||||
}
|
||||
|
||||
public void setDebug(Boolean debug) {
|
||||
this.debug = debug;
|
||||
}
|
||||
|
||||
|
||||
public String getGuildId() {
|
||||
return guildId;
|
||||
}
|
||||
|
||||
public void setGuildId(String guildId) {
|
||||
this.guildId = guildId;
|
||||
}
|
||||
|
||||
public OGuildSetting getSettingByName(String name) {
|
||||
Optional<OGuildSetting> setting = getGuildSettings()
|
||||
.stream()
|
||||
.filter(setting1 -> setting1.getName().equalsIgnoreCase(name))
|
||||
.findFirst();
|
||||
|
||||
return setting.orElse(null);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package me.bobert.netneutrality.db.entities;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "guild_settings")
|
||||
@NamedQueries({
|
||||
@NamedQuery(name = "settings_findByName", query = "select o from OGuildSetting o where o.guild.id = :id and upper(o.name) = upper(:name)")
|
||||
})
|
||||
public class OGuildSetting {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id", nullable = false)
|
||||
private Integer id;
|
||||
|
||||
@Column(name = "name", nullable = false)
|
||||
private String name;
|
||||
|
||||
@Lob
|
||||
@Column(name = "value", nullable = false)
|
||||
private String value;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "guild_id", nullable = false)
|
||||
private OGuild guild;
|
||||
|
||||
public OGuild getGuild() {
|
||||
return guild;
|
||||
}
|
||||
|
||||
public void setGuild(OGuild guild) {
|
||||
this.guild = guild;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
public void setValue(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package me.bobert.netneutrality.db.entities;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
@Entity
|
||||
@Table(name = "log_message")
|
||||
@NamedQueries({
|
||||
@NamedQuery(name = "OLogMessage.findByMessageIdEquals", query = "select o from OLogMessage o where o.messageId = :messageId"),
|
||||
@NamedQuery(name = "OLogMessage.findByGuild_GuildIdEqualsAndUserIdEquals", query = "select o from OLogMessage o where o.guild.guildId = :guildId and o.userId = :userId")
|
||||
})
|
||||
public class OLogMessage {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Column(name = "id", nullable = false)
|
||||
private Integer id;
|
||||
|
||||
@Column(name = "channel_id", nullable = false)
|
||||
private String channelId;
|
||||
|
||||
@Column(name = "message_id", nullable = false)
|
||||
private String messageId;
|
||||
|
||||
@Column(name = "user_id", nullable = false)
|
||||
private String userId;
|
||||
|
||||
@Convert(disableConversion = true)
|
||||
@Column(name = "date_sent", nullable = false)
|
||||
private Instant dateSent;
|
||||
|
||||
@Column(name = "message", length = 2000)
|
||||
private String message;
|
||||
|
||||
@Column(name = "edited_message", length = 2000)
|
||||
private String editedMessage;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "guild_id", nullable = false)
|
||||
private OGuild guild;
|
||||
|
||||
public OGuild getGuild() {
|
||||
return guild;
|
||||
}
|
||||
|
||||
public void setGuild(OGuild guild) {
|
||||
this.guild = guild;
|
||||
}
|
||||
|
||||
public String getEditedMessage() {
|
||||
return editedMessage;
|
||||
}
|
||||
|
||||
public void setEditedMessage(String editedMessage) {
|
||||
this.editedMessage = editedMessage;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Instant getDateSent() {
|
||||
return dateSent;
|
||||
}
|
||||
|
||||
public void setDateSent(Instant dateSent) {
|
||||
this.dateSent = dateSent;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(String userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getMessageId() {
|
||||
return messageId;
|
||||
}
|
||||
|
||||
public void setMessageId(String messageId) {
|
||||
this.messageId = messageId;
|
||||
}
|
||||
|
||||
public String getChannelId() {
|
||||
return channelId;
|
||||
}
|
||||
|
||||
public void setChannelId(String channelId) {
|
||||
this.channelId = channelId;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package me.bobert.netneutrality.db.entities;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "logging")
|
||||
public class OLogging {
|
||||
@EmbeddedId
|
||||
private OLoggingId id;
|
||||
|
||||
@MapsId("guildId")
|
||||
@ManyToOne(fetch = FetchType.LAZY, optional = false)
|
||||
@JoinColumn(name = "guild_id", nullable = false)
|
||||
private OGuild guild;
|
||||
|
||||
@Column(name = "enabled", nullable = false)
|
||||
private Boolean enabled = false;
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public OGuild getGuild() {
|
||||
return guild;
|
||||
}
|
||||
|
||||
public void setGuild(OGuild guild) {
|
||||
this.guild = guild;
|
||||
}
|
||||
|
||||
public OLoggingId getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(OLoggingId id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package me.bobert.netneutrality.db.entities;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Embeddable;
|
||||
import org.hibernate.Hibernate;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
@Embeddable
|
||||
public class OLoggingId implements Serializable {
|
||||
@Serial
|
||||
private static final long serialVersionUID = 8216581265618800768L;
|
||||
@Column(name = "guild_id", nullable = false)
|
||||
private String guildId;
|
||||
@Column(name = "channel_id", nullable = false)
|
||||
private String channelId;
|
||||
|
||||
public String getChannelId() {
|
||||
return channelId;
|
||||
}
|
||||
|
||||
public void setChannelId(String channelId) {
|
||||
this.channelId = channelId;
|
||||
}
|
||||
|
||||
public String getGuildId() {
|
||||
return guildId;
|
||||
}
|
||||
|
||||
public void setGuildId(String guildId) {
|
||||
this.guildId = guildId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(guildId, channelId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
|
||||
OLoggingId entity = (OLoggingId) o;
|
||||
return Objects.equals(this.guildId, entity.guildId) &&
|
||||
Objects.equals(this.channelId, entity.channelId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package me.bobert.netneutrality.exceptions;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.core.context.CommandContext;
|
||||
|
||||
public abstract class CommandException extends RuntimeException {
|
||||
|
||||
public static final String DEFAULT_MESSAGE = "Unknown error during command execution.";
|
||||
private final CommandContext context;
|
||||
|
||||
public CommandException(String message, CommandContext context, Throwable cause) {
|
||||
super(message == null ? DEFAULT_MESSAGE : message, cause);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public CommandContext getCommandContext() {
|
||||
return context;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package me.bobert.netneutrality.exceptions;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.core.context.CommandContext;
|
||||
|
||||
/**
|
||||
* Logs the error internally, without sending a message
|
||||
*/
|
||||
public class CommandExecutionException extends CommandException {
|
||||
|
||||
/**
|
||||
* Logs the error internally, without sending a message
|
||||
* @param context The command context
|
||||
*/
|
||||
public CommandExecutionException(String message, CommandContext context, Throwable cause) {
|
||||
super(message, context, cause);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package me.bobert.netneutrality.exceptions;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.core.LogHandler;
|
||||
|
||||
public class ExecutorException implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
LogHandler.logThread(t, e);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package me.bobert.netneutrality.hooks;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.core.CommandProcessor;
|
||||
import me.bobert.netneutrality.core.context.SlashCommandContext;
|
||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SlashCommandHooks extends ListenerAdapter {
|
||||
|
||||
@Override
|
||||
public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent event) {
|
||||
String commandName = event.getName();
|
||||
|
||||
SlashCommandContext commandContext = SlashCommandContext.createContext(event);
|
||||
CommandProcessor processor = new CommandProcessor(commandName, commandContext);
|
||||
processor.dispatch();
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package me.bobert.netneutrality.hooks.guilds;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.core.GuildManager;
|
||||
import net.dv8tion.jda.api.events.guild.GuildJoinEvent;
|
||||
import net.dv8tion.jda.api.events.guild.GuildLeaveEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ServerJoinHandler extends ListenerAdapter {
|
||||
|
||||
@Override
|
||||
public void onGuildJoin(@NotNull GuildJoinEvent event) {
|
||||
GuildManager.getManager(event.getGuild());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGuildLeave(@NotNull GuildLeaveEvent event) {
|
||||
GuildManager.getManager(event.getGuild()).removeFromDB();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package me.bobert.netneutrality.hooks.message;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.core.GuildManager;
|
||||
import me.bobert.netneutrality.core.SessionManager;
|
||||
import me.bobert.netneutrality.db.DBController;
|
||||
import me.bobert.netneutrality.db.controllers.DBMessage;
|
||||
import me.bobert.netneutrality.db.entities.OGuild;
|
||||
import me.bobert.netneutrality.utils.JpaUtils;
|
||||
import me.bobert.netneutrality.utils.MessageFunction;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||
import net.dv8tion.jda.api.events.message.MessageUpdateEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
import org.hibernate.Session;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class EditHandler extends ListenerAdapter {
|
||||
|
||||
@Override
|
||||
public void onMessageUpdate(MessageUpdateEvent event) {
|
||||
|
||||
if (event.getAuthor().isBot()) return;
|
||||
|
||||
GuildManager guildManager = GuildManager.getManager(event.getGuild());
|
||||
|
||||
if (guildManager.getGuildEntity().getLogging()) {
|
||||
OGuild guildEntity = guildManager.getGuildEntity();
|
||||
|
||||
if (guildEntity.getLogChannel() != null && !guildEntity.getLogChannel().isEmpty()) {
|
||||
Session session = SessionManager.getSessionFactory().openSession();
|
||||
JpaUtils.tryLoad(session, guildEntity, guildEntity.getGuildId());
|
||||
|
||||
int size = guildEntity.getLoggings()
|
||||
.stream()
|
||||
.filter(oLogging -> oLogging.getId().getChannelId().equals(event.getChannel().getId()) && oLogging.getEnabled())
|
||||
.collect(Collectors.toSet())
|
||||
.size();
|
||||
|
||||
if (size > 0) {
|
||||
TextChannel channel = event.getGuild().getTextChannelById(guildEntity.getLogChannel());
|
||||
|
||||
if (channel != null) {
|
||||
String originalMessage = DBController.get(DBMessage.class).getMessageString(event.getMessage().getId());
|
||||
DBController.get(DBMessage.class).logEdit(event.getMessage().getId(), event.getMessage().getContentDisplay());
|
||||
|
||||
channel.sendMessageEmbeds(MessageFunction.buildEditMessage(event.getMember(), event.getMessage(), originalMessage)).queue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package me.bobert.netneutrality.hooks.message;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.core.CommandProcessor;
|
||||
import me.bobert.netneutrality.core.GuildManager;
|
||||
import me.bobert.netneutrality.core.context.MessageCommandContext;
|
||||
import me.bobert.netneutrality.utils.Settings;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class MessageReceivedHandler extends ListenerAdapter {
|
||||
|
||||
@Override
|
||||
public void onMessageReceived(@NotNull MessageReceivedEvent event) {
|
||||
if (event.getAuthor().isBot()) return;
|
||||
if (event.isWebhookMessage()) return;
|
||||
|
||||
if (!event.isFromGuild())
|
||||
return;
|
||||
|
||||
Message message = event.getMessage();
|
||||
|
||||
MessageCommandContext context = MessageCommandContext.createContext(event);
|
||||
|
||||
GuildManager.getManager(context.getGuild()).checkAndLogMessage(message, context.getGuildUser(), context.getChannel());
|
||||
|
||||
String rawMessage = message.getContentDisplay().trim();
|
||||
String prefix = Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$");
|
||||
|
||||
if (rawMessage.startsWith(prefix)) {
|
||||
rawMessage = rawMessage.replaceFirst(String.format("^[%s\\s]{1,}", prefix), "").trim();
|
||||
// rawMessage = rawMessage.substring(prefix.length()).trim();
|
||||
String commandString = rawMessage.split(" ", 2)[0];
|
||||
|
||||
CommandProcessor processor = new CommandProcessor(commandString, context);
|
||||
processor.dispatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package me.bobert.netneutrality.hooks.message;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.events.message.MessageUpdateEvent;
|
||||
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
|
||||
import net.dv8tion.jda.api.events.message.react.MessageReactionRemoveEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ReactionHandler extends ListenerAdapter {
|
||||
|
||||
@Override
|
||||
public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) {
|
||||
Member member = event.getMember();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageReactionRemove(@NotNull MessageReactionRemoveEvent event) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessageUpdate(@NotNull MessageUpdateEvent event) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package me.bobert.netneutrality.hooks.voice;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent;
|
||||
import net.dv8tion.jda.api.hooks.ListenerAdapter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class VoiceEventHandler extends ListenerAdapter {
|
||||
|
||||
@Override
|
||||
public void onGuildVoiceUpdate(@NotNull GuildVoiceUpdateEvent event) {
|
||||
super.onGuildVoiceUpdate(event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
package me.bobert.netneutrality.http;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import io.sentry.Breadcrumb;
|
||||
import io.sentry.Sentry;
|
||||
import me.bobert.netneutrality.NetNeutrality;
|
||||
import me.bobert.netneutrality.core.LogHandler;
|
||||
import okhttp3.*;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class WebModule {
|
||||
|
||||
private final String url;
|
||||
private final OkHttpClient client = new OkHttpClient();
|
||||
private Response response;
|
||||
//private final String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 OPR/82.0.4227.43";
|
||||
private static final String userAgent = String.format("Net Neutrality/%s (Discord bot, [email protected])", NetNeutrality.VERSION);
|
||||
|
||||
|
||||
public WebModule(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a request to the specified URL in the constructor.
|
||||
* <br>Needs to be executed before any other method call
|
||||
*
|
||||
* @return an instance of the WebModule class with the Response of the Web Request
|
||||
*/
|
||||
public WebModule request() {
|
||||
Sentry.addBreadcrumb(Breadcrumb.http(url, "get"));
|
||||
Request request = new Request.Builder().url(this.url)
|
||||
.addHeader("User-Agent", userAgent)
|
||||
.build();
|
||||
try {
|
||||
response = client.newCall(request).execute();
|
||||
Sentry.addBreadcrumb(Breadcrumb.http(url, "get", response.code()));
|
||||
} catch (IOException e) {
|
||||
Sentry.captureException(e);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public WebModule request(Map<String, String> headers) throws IOException {
|
||||
Sentry.addBreadcrumb(Breadcrumb.http(url, "get"));
|
||||
Request.Builder builder = new Request.Builder().url(this.url)
|
||||
.addHeader("User-Agent", userAgent);
|
||||
|
||||
for (Map.Entry<String, String> header : headers.entrySet()) {
|
||||
builder.addHeader(header.getKey(), header.getValue());
|
||||
}
|
||||
|
||||
Request request = builder.build();
|
||||
|
||||
try {
|
||||
response = client.newCall(request).execute();
|
||||
Sentry.addBreadcrumb(Breadcrumb.http(url, "get", response.code()));
|
||||
} catch (IOException e) {
|
||||
Sentry.captureException(e);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public WebModule post(TreeMap<String, String> params, Map<String, String> headers) {
|
||||
Sentry.addBreadcrumb(Breadcrumb.http(url, "post"));
|
||||
RequestBody body = RequestBody.create(createJson(params), MediaType.parse("application/json; charset=utf-8"));
|
||||
Request.Builder builder = new Request.Builder().url(this.url)
|
||||
.addHeader("User-Agent", userAgent);
|
||||
|
||||
for (Map.Entry<String, String> header : headers.entrySet()) {
|
||||
builder.addHeader(header.getKey(), header.getValue());
|
||||
}
|
||||
Request request = builder.post(body).build();
|
||||
try {
|
||||
response = client.newCall(request).execute();
|
||||
Sentry.addBreadcrumb(Breadcrumb.http(url, "post", response.code()));
|
||||
} catch (IOException e) {
|
||||
Sentry.captureException(e);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public static String createJson(TreeMap<String, String> params) {
|
||||
StringBuilder json = new StringBuilder();
|
||||
|
||||
for (Map.Entry<String, String> param : params.entrySet()) {
|
||||
String str = "\"" + param.getKey() + "\":\"" + param.getValue() + "\"";
|
||||
json.append(str);
|
||||
json.append(",");
|
||||
}
|
||||
json.insert(0, "{");
|
||||
|
||||
return (json.length() > 1 ? json.substring(0, json.length() - 1) + "}" : "");
|
||||
}
|
||||
|
||||
public static String createJsonObject(List<String> key, List<String> value) {
|
||||
if (key.size() != value.size())
|
||||
throw new IllegalArgumentException("Key list size not equal to value list size");
|
||||
|
||||
StringBuilder json = new StringBuilder();
|
||||
|
||||
for (int index = 0; index < key.size();) {
|
||||
json.append(String.format("\"%s\":\"%s\"", key.get(index), value.get(index)));
|
||||
json.append(",");
|
||||
}
|
||||
json.insert(0, "{");
|
||||
|
||||
return (json.length() > 1 ? json.substring(0, json.length() - 1) + "}" : "");
|
||||
}
|
||||
|
||||
public static String createJsonArray(List<String> list) {
|
||||
StringBuilder json = new StringBuilder();
|
||||
|
||||
for (String str : list) {
|
||||
json.append("\"").append(str).append("\"");
|
||||
json.append(",");
|
||||
}
|
||||
json.insert(0, "[");
|
||||
|
||||
return (json.length() > 1 ? json.substring(0, json.length() - 1) + "]" : "");
|
||||
}
|
||||
|
||||
public static String createJsonArray(TreeMap<String, String> users) {
|
||||
StringBuilder json = new StringBuilder();
|
||||
|
||||
for (Map.Entry<String, String> param : users.entrySet()) {
|
||||
String str = "{\"" + param.getKey() + "\":\"" + param.getValue() + "\"}";
|
||||
json.append(str);
|
||||
json.append(",");
|
||||
}
|
||||
json.insert(0, "[");
|
||||
|
||||
return (json.length() > 1 ? json.substring(0, json.length() - 1) + "]" : "[]");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the response as a JSONObject
|
||||
*/
|
||||
public JSONObject getAsObject() {
|
||||
if (response == null)
|
||||
throw new RuntimeException("Request has not been sent");
|
||||
|
||||
try {
|
||||
ResponseBody body = response.body();
|
||||
return new JSONObject(body.string());
|
||||
} catch (IOException | NullPointerException e) {
|
||||
LogHandler.log(e);
|
||||
return new JSONObject();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use if your response start with an array : [ ]
|
||||
*
|
||||
* @return the response as a JSONArray
|
||||
*/
|
||||
public JSONArray getAsArray() {
|
||||
if (response == null)
|
||||
throw new RuntimeException("Request has not been sent");
|
||||
|
||||
try {
|
||||
ResponseBody body = response.body();
|
||||
return new JSONArray(body.string());
|
||||
} catch (IOException | NullPointerException e) {
|
||||
LogHandler.log(e);
|
||||
return new JSONArray();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the response as a String
|
||||
*/
|
||||
public String getRawData() throws IOException {
|
||||
if (response == null)
|
||||
throw new RuntimeException("Request has not been sent");
|
||||
|
||||
return response.body().string();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package me.bobert.netneutrality.http.server;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import com.sun.net.httpserver.HttpHandler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public abstract class AbstractHandler implements HttpHandler {
|
||||
|
||||
public abstract void handle(HttpExchange exchange) throws IOException;
|
||||
|
||||
public void sendStringResponse(HttpExchange exchange, String str) throws IOException {
|
||||
exchange.sendResponseHeaders(200, str.length());
|
||||
OutputStream os = exchange.getResponseBody();
|
||||
os.write(str.getBytes());
|
||||
os.close();
|
||||
}
|
||||
|
||||
public void sendJsonResponse(HttpExchange exchange, String str) throws IOException {
|
||||
exchange.getResponseHeaders().add("Content-Type", "application/json");
|
||||
exchange.sendResponseHeaders(200, str.length());
|
||||
OutputStream os = exchange.getResponseBody();
|
||||
os.write(str.getBytes());
|
||||
os.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package me.bobert.netneutrality.http.server;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import me.bobert.netneutrality.NetNeutrality;
|
||||
import me.bobert.netneutrality.core.GuildManager;
|
||||
import me.bobert.netneutrality.http.WebModule;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class ActionHandler extends AbstractHandler {
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
|
||||
String path = exchange.getRequestURI().getRawPath();
|
||||
|
||||
if (path.equals("/actions/shutdown")) {
|
||||
LogManager.getLogger(NetNeutrality.class).info("Request to shutdown received");
|
||||
NetNeutrality.shutdown();
|
||||
sendStringResponse(exchange, "Shutting down..");
|
||||
|
||||
System.exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.equals("/actions/status")) {
|
||||
TreeMap<String, String> json = new TreeMap<>();
|
||||
json.put("health", String.valueOf(NetNeutrality.getJDA().getStatus()));
|
||||
json.put("ping", String.valueOf(NetNeutrality.getJDA().getGatewayPing()));
|
||||
|
||||
sendJsonResponse(exchange, WebModule.createJson(json));
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.startsWith("/actions/settings/current/")) {
|
||||
JSONObject json = new JSONObject();
|
||||
String[] args = exchange.getRequestURI().toString().split("/");
|
||||
String guildId = args[args.length - 1];
|
||||
|
||||
Guild guild = NetNeutrality.getJDA().getGuildById(guildId);
|
||||
if (guild != null) {
|
||||
GuildManager manager = GuildManager.getManager(guild);
|
||||
Map<String, String> settings = manager.getGuildConfigManager().getSettings();
|
||||
JSONObject setting = new JSONObject(settings);
|
||||
|
||||
json.put("status", "ok");
|
||||
json.put("settings", setting);
|
||||
} else {
|
||||
json.put("status", "error");
|
||||
}
|
||||
|
||||
sendJsonResponse(exchange, json.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
if (path.startsWith("/actions/settings/reload/")) {
|
||||
TreeMap<String, String> json = new TreeMap<>();
|
||||
String[] args = exchange.getRequestURI().toString().split("/");
|
||||
String guildId = args[args.length - 1];
|
||||
|
||||
Guild guild = NetNeutrality.getJDA().getGuildById(guildId);
|
||||
boolean ok = true; //TODO: add bool
|
||||
if (guild == null)
|
||||
GuildManager.getManager(guildId).getGuildConfigManager().reloadSettingsCache();
|
||||
else
|
||||
GuildManager.getManager(guild).getGuildConfigManager().reloadSettingsCache();
|
||||
|
||||
|
||||
json.put("status", String.valueOf(ok));
|
||||
sendJsonResponse(exchange, WebModule.createJson(json));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
sendStringResponse(exchange, path);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package me.bobert.netneutrality.http.server;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import me.bobert.netneutrality.NetNeutrality;
|
||||
import net.dv8tion.jda.api.JDA;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class GuildHandler extends AbstractHandler {
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
|
||||
String path = exchange.getRequestURI().getRawPath();
|
||||
|
||||
if (path.equals("/guilds/owner")) {
|
||||
|
||||
JSONArray array = new JSONArray();
|
||||
|
||||
JDA jda = NetNeutrality.getJDA();
|
||||
|
||||
jda.getGuilds().forEach(guild -> {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("guild_id", guild.getId());
|
||||
obj.put("owner_id", guild.getOwnerId());
|
||||
array.put(obj);
|
||||
});
|
||||
|
||||
sendJsonResponse(exchange, array.toString());
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (path.startsWith("/guilds/channels/")) {
|
||||
|
||||
String[] args = exchange.getRequestURI().toString().split("/");
|
||||
String guild = args[args.length - 1];
|
||||
JSONArray array = new JSONArray();
|
||||
|
||||
JDA jda = NetNeutrality.getJDA();
|
||||
|
||||
if (jda.getGuildById(guild) == null) {
|
||||
sendStringResponse(exchange, path);
|
||||
return;
|
||||
}
|
||||
|
||||
jda.getGuildById(guild).getTextChannels().forEach(channel -> {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("channel_id", channel.getId());
|
||||
obj.put("channel_name", channel.getName());
|
||||
array.put(obj);
|
||||
});
|
||||
|
||||
sendJsonResponse(exchange, array.toString());
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (path.startsWith("/guilds/roles/")) {
|
||||
|
||||
String[] args = exchange.getRequestURI().toString().split("/");
|
||||
String guild = args[args.length - 1];
|
||||
JSONArray array = new JSONArray();
|
||||
JDA jda = NetNeutrality.getJDA();
|
||||
|
||||
if (jda.getGuildById(guild) == null) {
|
||||
sendStringResponse(exchange, path);
|
||||
return;
|
||||
}
|
||||
|
||||
jda.getGuildById(guild).getRoles().forEach(role -> {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("channel_id", role.getId());
|
||||
obj.put("channel_name", role.getName());
|
||||
array.put(obj);
|
||||
});
|
||||
|
||||
sendJsonResponse(exchange, array.toString());
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
sendStringResponse(exchange, path);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package me.bobert.netneutrality.http.server;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class RootHandler extends AbstractHandler {
|
||||
|
||||
@Override
|
||||
public void handle(HttpExchange exchange) throws IOException {
|
||||
|
||||
String response = "<h1>Server start success if you see this message</h1>";
|
||||
|
||||
sendStringResponse(exchange, response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package me.bobert.netneutrality.http.server;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
import me.bobert.netneutrality.thread.BaseThreadFactory;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
public class WebApi {
|
||||
|
||||
private final static Logger logger = LogManager.getLogger(WebApi.class);
|
||||
|
||||
private HttpServer server;
|
||||
|
||||
public WebApi(int port) {
|
||||
logger.info("Starting web server...");
|
||||
|
||||
try {
|
||||
InetSocketAddress sockAddr = new InetSocketAddress(port);
|
||||
server = HttpServer.create(sockAddr, 0);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
server.createContext("/", new RootHandler());
|
||||
server.createContext("/actions", new ActionHandler());
|
||||
server.createContext("/guilds", new GuildHandler());
|
||||
ExecutorService executor = Executors.newFixedThreadPool(10, new BaseThreadFactory("web-api"));
|
||||
server.setExecutor(executor);
|
||||
server.start();
|
||||
logger.info("Web server started at [" + server.getAddress() + "]");
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
package me.bobert.netneutrality.music;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
|
||||
import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame;
|
||||
import net.dv8tion.jda.api.audio.AudioSendHandler;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* This is a wrapper around AudioPlayer which makes it behave as an AudioSendHandler for JDA. As JDA calls canProvide
|
||||
* before every call to provide20MsAudio(), we pull the frame in canProvide() and use the frame we already pulled in
|
||||
* provide20MsAudio().
|
||||
*/
|
||||
public class AudioPlayerSendHandler implements AudioSendHandler {
|
||||
private final AudioPlayer audioPlayer;
|
||||
private AudioFrame lastFrame;
|
||||
|
||||
/**
|
||||
* @param audioPlayer Audio player to wrap.
|
||||
*/
|
||||
public AudioPlayerSendHandler(AudioPlayer audioPlayer) {
|
||||
this.audioPlayer = audioPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canProvide() {
|
||||
lastFrame = audioPlayer.provide();
|
||||
return lastFrame != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer provide20MsAudio() {
|
||||
return ByteBuffer.wrap(lastFrame.getData());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOpus() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
package me.bobert.netneutrality.music;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
|
||||
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
|
||||
import com.sedmelluq.discord.lavaplayer.source.http.HttpAudioSourceManager;
|
||||
import com.sedmelluq.discord.lavaplayer.source.soundcloud.SoundCloudAudioSourceManager;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import dev.lavalink.youtube.YoutubeAudioSourceManager;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import me.bobert.netneutrality.core.GuildManager;
|
||||
import me.bobert.netneutrality.core.LogHandler;
|
||||
import me.bobert.netneutrality.core.context.CommandContext;
|
||||
import me.bobert.netneutrality.db.GSettings;
|
||||
import me.bobert.netneutrality.exceptions.CommandExecutionException;
|
||||
import me.bobert.netneutrality.music.loadable.LoadableSong;
|
||||
import me.bobert.netneutrality.music.source.UdioAudioSourceManager;
|
||||
import me.bobert.netneutrality.thread.MusicPlayerAutoDisconnectThread;
|
||||
import me.bobert.netneutrality.thread.ThreadManager;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class MusicManager {
|
||||
|
||||
private final static Logger logger = LogManager.getLogger(MusicManager.class);
|
||||
private final static Map<String, MusicManager> managers = new ConcurrentHashMap<>();
|
||||
private final static ScheduledExecutorService executor = ThreadManager.getMusicExecutor();
|
||||
|
||||
private final static AudioPlayerManager playerManager = new DefaultAudioPlayerManager();
|
||||
private final Guild guild;
|
||||
private final AudioPlayer player;
|
||||
private final TrackScheduler scheduler;
|
||||
private TrackLoader lastTrackLoader;
|
||||
private ScheduledFuture<?> autoDisconnectThread;
|
||||
private final int baseVolume;
|
||||
|
||||
private MusicManager(Guild guild) {
|
||||
this.guild = guild;
|
||||
this.player = playerManager.createPlayer();
|
||||
this.scheduler = new TrackScheduler(this);
|
||||
this.baseVolume = Integer.parseInt(GuildManager.getManager(guild).getGuildConfigManager().getSetting(GSettings.MUSIC_VOLUME));
|
||||
|
||||
guild.getAudioManager().setSendingHandler(new AudioPlayerSendHandler(player));
|
||||
player.addListener(scheduler);
|
||||
|
||||
managers.put(guild.getId(), this);
|
||||
}
|
||||
|
||||
public static MusicManager get(Guild guild) {
|
||||
if (managers.containsKey(guild.getId())) {
|
||||
return managers.get(guild.getId());
|
||||
} else {
|
||||
return new MusicManager(guild);
|
||||
}
|
||||
}
|
||||
|
||||
public static void initPlayerManager() {
|
||||
playerManager.registerSourceManager(new YoutubeAudioSourceManager());
|
||||
playerManager.registerSourceManager(SoundCloudAudioSourceManager.createDefault());
|
||||
playerManager.registerSourceManager(new UdioAudioSourceManager());
|
||||
playerManager.registerSourceManager(new HttpAudioSourceManager());
|
||||
}
|
||||
|
||||
public boolean isNotConnectedTo(VoiceChannel vChannel) {
|
||||
return vChannel == null || !vChannel.equals(guild.getAudioManager().getConnectedChannel());
|
||||
}
|
||||
|
||||
public synchronized void connect(VoiceChannel vChannel) {
|
||||
if (vChannel != null && isNotConnectedTo(vChannel)) {
|
||||
logger.debug("Guild {}: connecting to voice channel {}", vChannel.getGuild().getId(), vChannel.getId());
|
||||
guild.getAudioManager().openAudioConnection(vChannel);
|
||||
guild.getAudioManager().setSelfDeafened(true);
|
||||
player.setVolume(baseVolume);
|
||||
|
||||
if (autoDisconnectThread == null)
|
||||
autoDisconnectThread = executor.scheduleWithFixedDelay(new MusicPlayerAutoDisconnectThread(this), 240, 240, TimeUnit.SECONDS);
|
||||
} else {
|
||||
throw new RuntimeException("Error connecting to voice channel");
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return guild != null && guild.getAudioManager().getConnectedChannel() != null;
|
||||
}
|
||||
|
||||
public synchronized AudioTrack getCurrentlyPlaying() {
|
||||
return player.getPlayingTrack();
|
||||
}
|
||||
|
||||
public synchronized boolean isCurrentlyPlaying() {
|
||||
return player.getPlayingTrack() != null;
|
||||
}
|
||||
|
||||
public synchronized Member getRequesterOfCurrentTrack() {
|
||||
QueueManager.TrackInfo info = scheduler.getQueueManager().peekHeadOfQueue();
|
||||
|
||||
if (info == null)
|
||||
return null;
|
||||
|
||||
return info.getRequester();
|
||||
}
|
||||
|
||||
protected AudioPlayer getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public Guild getGuild() {
|
||||
return guild;
|
||||
}
|
||||
|
||||
public void stopLoading() {
|
||||
lastTrackLoader.stopLoading();
|
||||
}
|
||||
|
||||
public synchronized void leave() {
|
||||
if (guild != null && guild.getAudioManager().getConnectedChannel() != null) {
|
||||
logger.debug("Guild {}: leaving voice channel {}", guild.getId(), guild.getAudioManager().getConnectedChannel().getId());
|
||||
player.stopTrack();
|
||||
guild.getAudioManager().closeAudioConnection();
|
||||
}
|
||||
|
||||
scheduler.detachTextChannel();
|
||||
|
||||
if (autoDisconnectThread != null) {
|
||||
autoDisconnectThread.cancel(true);
|
||||
autoDisconnectThread = null;
|
||||
}
|
||||
}
|
||||
|
||||
public static void disconnectPlayers() {
|
||||
for (MusicManager mm : managers.values()) {
|
||||
mm.getScheduler().getQueueManager().clearQueue();
|
||||
mm.player.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void play(String url, CommandContext context, boolean needToBeConnected, boolean silent) {
|
||||
logger.debug("Guild {}: request to play '{}' by user {}", guild.getId(), url, context.getGuildUser().getId());
|
||||
|
||||
List<LoadableSong> toLoad = new ArrayList<>();
|
||||
toLoad.add(new LoadableSong(null, url, null));
|
||||
|
||||
play(toLoad, context, needToBeConnected, silent);
|
||||
}
|
||||
|
||||
public void play(List<LoadableSong> songs, CommandContext context, boolean needToBeConnected, boolean silent) {
|
||||
Member member = context.getGuildUser();
|
||||
MessageChannel channel = context.getChannel();
|
||||
|
||||
if (member.getVoiceState() == null) {
|
||||
logger.error("Guild {}: Voice state is null", guild.getId());
|
||||
context.replyToMessage("Voice state is null, something has gone terribly wrong.");
|
||||
LogHandler.log(new CommandExecutionException("Voice state is null", context, null));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!member.getVoiceState().inAudioChannel()) {
|
||||
context.replyToMessage("You are not in a voice channel");
|
||||
return;
|
||||
}
|
||||
|
||||
VoiceChannel vChannel = (VoiceChannel) member.getVoiceState().getChannel();
|
||||
if (needToBeConnected && isNotConnectedTo(vChannel)) {
|
||||
context.replyToMessage("The player is not in your voice channel");
|
||||
return;
|
||||
}
|
||||
|
||||
if (isNotConnectedTo(vChannel)) {
|
||||
connect(vChannel);
|
||||
scheduler.attachTextChannel(channel);
|
||||
}
|
||||
|
||||
TrackLoader loader = new TrackLoader(this, context, playerManager, silent);
|
||||
lastTrackLoader = loader;
|
||||
|
||||
loader.loadAllSongs(songs);
|
||||
}
|
||||
|
||||
public synchronized void stopTrack() {
|
||||
player.stopTrack();
|
||||
}
|
||||
|
||||
public synchronized CommandExecutionResult voteSkip(CommandContext context) {
|
||||
VoiceChannel voiceChannel = (VoiceChannel) guild.getAudioManager().getConnectedChannel();
|
||||
|
||||
if (voiceChannel == null) {
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
if (!isCurrentlyPlaying()) {
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
QueueManager.TrackInfo info = scheduler.getQueueManager().peekHeadOfQueue();
|
||||
if (info == null) {
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
// Minus the bot itself
|
||||
int connected = voiceChannel.getMembers().size() - 1;
|
||||
|
||||
if (connected < 3) {
|
||||
context.reply("Skipping current track since not enough people are connected to the voice channel.");
|
||||
scheduler.getQueueManager().skip();
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
double needed = connected / 2.1;
|
||||
|
||||
info.addSkip(context.getGuildUser());
|
||||
logger.debug("VoteSkip: guild={}, connected={}, needed={}, skips={}, user={}", context.getGuild().getId(), connected, needed, info.getSkips().size(), context.getGuildUser().getId());
|
||||
|
||||
if (info.getSkips().size() > needed) {
|
||||
return scheduler.getQueueManager().skip();
|
||||
} else {
|
||||
context.reply("Adding your vote to skip the current track. Current votes: %d/%d", info.getSkips().size(), (int) Math.ceil(needed));
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
public synchronized void setVolume(int volume) {
|
||||
player.setVolume(volume);
|
||||
}
|
||||
|
||||
public int getQueueSize() {
|
||||
return scheduler.getQueueManager().getQueueSize();
|
||||
}
|
||||
|
||||
public TrackScheduler getScheduler() {
|
||||
return scheduler;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
package me.bobert.netneutrality.music;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import me.bobert.netneutrality.command.CommandExecutionResult;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
public class QueueManager {
|
||||
|
||||
public static class TrackInfo {
|
||||
|
||||
private final AudioTrack track;
|
||||
private final Member member;
|
||||
private final Set<String> skips;
|
||||
private final Logger logger = LogManager.getLogger(QueueManager.class);
|
||||
|
||||
public TrackInfo(AudioTrack track, Member member) {
|
||||
this.track = track;
|
||||
this.member = member;
|
||||
this.skips = new HashSet<>();
|
||||
}
|
||||
|
||||
public AudioTrack getTrack() {
|
||||
return track;
|
||||
}
|
||||
|
||||
public Set<String> getSkips() {
|
||||
return skips;
|
||||
}
|
||||
|
||||
public void addSkip(Member member) {
|
||||
if (!hasSkip(member)) {
|
||||
logger.debug("VoteSkip: user={}, skip doesn't exist, before={}, after={}", member.getId(), skips.size(), skips.size() + 1);
|
||||
skips.add(member.getId());
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasSkip(Member member) {
|
||||
return skips.contains(member.getId());
|
||||
}
|
||||
|
||||
public Member getRequester() {
|
||||
return member;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TrackInfo trackInfo = (TrackInfo) o;
|
||||
return track.equals(trackInfo.track);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(track);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TrackInfo{" +
|
||||
"track=" + track.getInfo().title +
|
||||
", member=" + member.getEffectiveName() +
|
||||
", skips=" + skips +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
|
||||
private final Logger logger = LogManager.getLogger(QueueManager.class);
|
||||
|
||||
private final TrackScheduler scheduler;
|
||||
private final AudioPlayer player;
|
||||
private final Guild guild;
|
||||
private final Deque<TrackInfo> queue;
|
||||
|
||||
public QueueManager(TrackScheduler scheduler) {
|
||||
this.scheduler = scheduler;
|
||||
this.player = scheduler.getPlayer();
|
||||
this.guild = scheduler.getGuild();
|
||||
|
||||
this.queue = new LinkedBlockingDeque<>();
|
||||
}
|
||||
|
||||
public List<TrackInfo> getQueue() {
|
||||
synchronized (queue) {
|
||||
return queue.stream().toList();
|
||||
}
|
||||
}
|
||||
|
||||
public TrackScheduler getScheduler() {
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
public Guild getGuild() {
|
||||
return guild;
|
||||
}
|
||||
|
||||
public int getQueueSize() {
|
||||
synchronized (queue) {
|
||||
return queue.size();
|
||||
}
|
||||
}
|
||||
|
||||
public List<TrackInfo> getQueueWithFilter(Predicate<? super TrackInfo> filter) {
|
||||
synchronized (queue) {
|
||||
return queue.stream().filter(filter).toList();
|
||||
}
|
||||
}
|
||||
|
||||
public TrackInfo peekHeadOfQueue() {
|
||||
synchronized (queue) {
|
||||
return queue.peek();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeHeadOfQueue() {
|
||||
synchronized (queue) {
|
||||
queue.poll();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
synchronized (queue) {
|
||||
return queue.isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
public CommandExecutionResult clearQueue() {
|
||||
scheduler.getMusicManager().stopLoading();
|
||||
scheduler.getMusicManager().leave();
|
||||
|
||||
if (queue.isEmpty()) {
|
||||
return CommandExecutionResult.warn("Queue is empty!");
|
||||
}
|
||||
|
||||
synchronized (queue) {
|
||||
int queueSize = queue.size();
|
||||
if (queueSize == 0) {
|
||||
return CommandExecutionResult.warn("Queue is empty!");
|
||||
}
|
||||
|
||||
queue.clear();
|
||||
return CommandExecutionResult.success(String.format("Removed %d track%s from the queue", queueSize--, (queueSize > 1 ? "s" : "")));
|
||||
}
|
||||
}
|
||||
|
||||
public void removeTracksFromUser(Member member) {
|
||||
synchronized (queue) {
|
||||
int size = queue.stream().filter(trackInfo -> member.equals(trackInfo.getRequester())).toList().size();
|
||||
|
||||
if (size > 0) {
|
||||
if (size == queue.size()) {
|
||||
clearQueue();
|
||||
return;
|
||||
}
|
||||
|
||||
TrackInfo first = queue.peek();
|
||||
queue.removeIf(trackInfo -> member.equals(trackInfo.getRequester()));
|
||||
|
||||
if (first != null && member.equals(first.getRequester())) {
|
||||
skip(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<AudioTrack> getAllQueuedTrack() {
|
||||
synchronized (queue) {
|
||||
ArrayList<AudioTrack> tracks = new ArrayList<>();
|
||||
|
||||
queue.forEach(trackInfo -> tracks.add(trackInfo.getTrack()));
|
||||
return tracks;
|
||||
}
|
||||
}
|
||||
|
||||
public void queue(AudioTrack track, Member member) {
|
||||
TrackInfo info = new TrackInfo(track, member);
|
||||
|
||||
synchronized (queue) {
|
||||
if (queue.contains(info)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.getPlayingTrack() == null && !queue.isEmpty()) {
|
||||
queue.addFirst(info);
|
||||
}
|
||||
|
||||
if ((player.getPlayingTrack() == null && queue.isEmpty()) || player.getPlayingTrack() != null) {
|
||||
queue.add(info);
|
||||
}
|
||||
|
||||
if (player.getPlayingTrack() == null) {
|
||||
AudioTrack toPlay = (queue.peek() == null ? null : queue.peek().getTrack());
|
||||
player.startTrack(toPlay, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void startNextTrack() {
|
||||
synchronized (queue) {
|
||||
TrackInfo last = queue.poll();
|
||||
TrackInfo next = queue.peek();
|
||||
|
||||
if (next != null) {
|
||||
player.startTrack(next.getTrack().makeClone(), false);
|
||||
} else {
|
||||
player.stopTrack();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public CommandExecutionResult skip() {
|
||||
return skip(1);
|
||||
}
|
||||
|
||||
public CommandExecutionResult skip(int nbTrack) {
|
||||
if (nbTrack == 0) {
|
||||
nbTrack = 1;
|
||||
}
|
||||
|
||||
synchronized (queue) {
|
||||
if (queue.isEmpty()) {
|
||||
scheduler.getMusicManager().leave();
|
||||
return CommandExecutionResult.info("Queue is empty, leaving voice channel");
|
||||
}
|
||||
|
||||
if (nbTrack > queue.size()) {
|
||||
nbTrack = 1;
|
||||
}
|
||||
|
||||
logger.debug("Guild {}: skipping {} tracks", guild.getId(), nbTrack);
|
||||
for (int i = 1; i < nbTrack; i++) {
|
||||
queue.poll();
|
||||
}
|
||||
|
||||
if (queue.isEmpty()) {
|
||||
scheduler.getMusicManager().leave();
|
||||
return CommandExecutionResult.info("Queue is empty, leaving voice channel");
|
||||
}
|
||||
|
||||
startNextTrack();
|
||||
|
||||
return CommandExecutionResult.success(String.format("Skipping %s track%s..", nbTrack, (nbTrack > 1 ? "s" : "")));
|
||||
}
|
||||
}
|
||||
|
||||
public CommandExecutionResult shuffle() {
|
||||
if (queue.size() > 3) {
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
synchronized (queue) {
|
||||
if (queue.size() > 3) {
|
||||
logger.debug("Guild {}: shuffling queue", guild.getId());
|
||||
TrackInfo current = queue.poll();
|
||||
|
||||
ArrayList<TrackInfo> newQueue = new ArrayList<>(queue);
|
||||
|
||||
Collections.shuffle(newQueue);
|
||||
|
||||
queue.clear();
|
||||
queue.add(current);
|
||||
queue.addAll(newQueue);
|
||||
|
||||
return CommandExecutionResult.success("Queue is now shuffled.");
|
||||
}
|
||||
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
}
|
||||
|
||||
public CommandExecutionResult removeFromQueue(int index) {
|
||||
if (queue.isEmpty() || index < 1) {
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
synchronized (queue) {
|
||||
if (queue.isEmpty()) {
|
||||
return CommandExecutionResult.none();
|
||||
}
|
||||
|
||||
if (index > queue.size()) {
|
||||
return CommandExecutionResult.error("Position is out of bounds");
|
||||
}
|
||||
|
||||
logger.debug("Guild {}: removing index {} from queue", guild.getId(), index - 1);
|
||||
|
||||
if (index > 1) {
|
||||
List<TrackInfo> newQueue = new ArrayList<>(queue);
|
||||
newQueue.remove(index - 1);
|
||||
|
||||
queue.clear();
|
||||
queue.addAll(newQueue);
|
||||
|
||||
return CommandExecutionResult.success(String.format("Removed track #%d from the queue", index));
|
||||
}
|
||||
|
||||
skip();
|
||||
return CommandExecutionResult.none();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
package me.bobert.netneutrality.music;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class SourceFinder {
|
||||
|
||||
public enum AudioSourceType {
|
||||
Youtube,
|
||||
YTMusic,
|
||||
Soundcloud,
|
||||
Bandcamp,
|
||||
Vimeo,
|
||||
YTSearch,
|
||||
Udio,
|
||||
DirectLinkMp3,
|
||||
Nothing
|
||||
|
||||
}
|
||||
|
||||
private static final Pattern youtubePattern = Pattern.compile("^(?:https?://)?(?:www\\.)?(?:youtube\\.com|youtu\\.be)/.*?(?:embed|e|v|watch.*?v=)?/?([-_a-zA-Z0-9]{10,})?(?:&?index=\\d+)?(?>(?:playlist\\?|&)?list=([^#\\\\&?]{12,}))?", Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern youtubeMusicPattern = Pattern.compile("^(?:https?://)?(?:www\\.)?music\\.(?:youtube\\.com|youtu\\.be)/.*?(?:embed|e|v|watch.*?v=)?/?([-_a-zA-Z0-9]{10,})?(?:&?index=\\d+)?(?>(?:playlist\\?|&)?list=([^#\\\\&?]{12,}))?", Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern soundcloudPattern = Pattern.compile("^(?:https?://)?(?:www\\.)?(?:m\\.)?soundcloud\\.com/([a-zA-Z0-9-_]+)/([a-zA-Z0-9-_]+)(?:\\\\?.*|)$", Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern udioPattern = Pattern.compile("^https://www.udio.com/songs/[0-9A-Za-z]+$", Pattern.CASE_INSENSITIVE);
|
||||
private static final Pattern mp3Pattern = Pattern.compile("^https?://(?:.*)(?:.mp3)$", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
public static AudioSourceType getSource(String url) {
|
||||
Matcher youtubeMatcher = youtubePattern.matcher(url);
|
||||
Matcher youtubeMusicMatcher = youtubeMusicPattern.matcher(url);
|
||||
Matcher soundcloudMatcher = soundcloudPattern.matcher(url);
|
||||
Matcher udioMatcher = udioPattern.matcher(url);
|
||||
Matcher mp3Matcher = mp3Pattern.matcher(url);
|
||||
|
||||
if (youtubeMatcher.find())
|
||||
return AudioSourceType.Youtube;
|
||||
|
||||
if (youtubeMusicMatcher.find())
|
||||
return AudioSourceType.YTMusic;
|
||||
|
||||
if (soundcloudMatcher.find())
|
||||
return AudioSourceType.Soundcloud;
|
||||
|
||||
if (udioMatcher.find())
|
||||
return AudioSourceType.Udio;
|
||||
|
||||
if (mp3Matcher.find())
|
||||
return AudioSourceType.DirectLinkMp3;
|
||||
|
||||
if (!(url.startsWith("http://") || url.startsWith("https://")))
|
||||
return AudioSourceType.YTSearch;
|
||||
|
||||
return AudioSourceType.Nothing;
|
||||
}
|
||||
|
||||
public static String getIdentifier(String url) {
|
||||
String identifier;
|
||||
|
||||
identifier = switch (getSource(url)) {
|
||||
case Youtube, YTMusic, Soundcloud, Udio, DirectLinkMp3 -> url;
|
||||
case YTSearch -> "ytsearch:" + url;
|
||||
default -> null;
|
||||
};
|
||||
|
||||
return identifier;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package me.bobert.netneutrality.music;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
|
||||
import me.bobert.netneutrality.core.LogHandler;
|
||||
import me.bobert.netneutrality.core.context.CommandContext;
|
||||
import me.bobert.netneutrality.music.loadable.CachedSource;
|
||||
import me.bobert.netneutrality.music.loadable.LoadableMusicSource;
|
||||
import me.bobert.netneutrality.music.loadable.LoadableSong;
|
||||
import me.bobert.netneutrality.thread.ThreadManager;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class TrackLoader implements Runnable {
|
||||
|
||||
private final Logger logger = LogManager.getLogger(TrackLoader.class);
|
||||
private final CommandContext context;
|
||||
private final MusicManager musicManager;
|
||||
private final AudioPlayerManager audioPlayerManager;
|
||||
private TrackLoadingHandler lastLoadingHandler;
|
||||
private List<LoadableSong> songs;
|
||||
private LoadableMusicSource source;
|
||||
private int numberOfTracksLoaded;
|
||||
private int totalNumberOfTracks;
|
||||
private boolean loadingDisabled = false;
|
||||
private boolean silent;
|
||||
private final Object sync = new Object();
|
||||
|
||||
public TrackLoader(MusicManager musicManager, CommandContext context, AudioPlayerManager playerManager, boolean silent) {
|
||||
this.musicManager = musicManager;
|
||||
this.context = context;
|
||||
this.audioPlayerManager = playerManager;
|
||||
this.silent = silent;
|
||||
}
|
||||
|
||||
public void loadAllSongs(List<LoadableSong> songs) {
|
||||
this.songs = songs;
|
||||
if (songs.size() > 1) {
|
||||
this.silent = true;
|
||||
}
|
||||
|
||||
ThreadManager.getMusicExecutor().submit(this);
|
||||
}
|
||||
|
||||
public void loadAllSongs(CachedSource cachedSource) {
|
||||
this.source = cachedSource.getSource();
|
||||
|
||||
loadAllSongs(cachedSource.getSongs());
|
||||
}
|
||||
|
||||
public void stopLoading() {
|
||||
loadingDisabled = true;
|
||||
|
||||
if (lastLoadingHandler != null) {
|
||||
lastLoadingHandler.stopLoading();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
List<LoadableSong> copy = new ArrayList<>(songs);
|
||||
|
||||
for (LoadableSong song : songs) {
|
||||
if (loadingDisabled) {
|
||||
copy.remove(song);
|
||||
break;
|
||||
}
|
||||
|
||||
SourceFinder.AudioSourceType type = SourceFinder.getSource(song.name());
|
||||
String identifier = SourceFinder.getIdentifier(song.name());
|
||||
|
||||
if (identifier != null) {
|
||||
TrackLoadingHandler loadingHandler = new TrackLoadingHandler(context, musicManager, identifier, type, silent);
|
||||
loadingHandler.useCallbackOnLoad(success -> {
|
||||
|
||||
synchronized (sync) {
|
||||
if (success) {
|
||||
numberOfTracksLoaded++;
|
||||
}
|
||||
|
||||
totalNumberOfTracks++;
|
||||
|
||||
logger.debug("callback notify; total={}, loaded={}", totalNumberOfTracks, numberOfTracksLoaded);
|
||||
sync.notify();
|
||||
}
|
||||
|
||||
});
|
||||
lastLoadingHandler = loadingHandler;
|
||||
|
||||
audioPlayerManager.loadItemOrdered(this, identifier, loadingHandler);
|
||||
} else {
|
||||
copy.remove(song);
|
||||
|
||||
if (songs.size() == 1) {
|
||||
context.replyToMessage("No source found to play the given url");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
synchronized (sync) {
|
||||
while (!(copy.size() == totalNumberOfTracks)) {
|
||||
logger.debug("waiting for load; size={}, total={}, loaded={}", songs.size(), totalNumberOfTracks, numberOfTracksLoaded);
|
||||
sync.wait();
|
||||
}
|
||||
|
||||
//TODO: logic
|
||||
if (copy.size() > 1 && source != null && !loadingDisabled) {
|
||||
context.replyToMessage("Loaded %d of %d tracks from online source", numberOfTracksLoaded, totalNumberOfTracks);
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException exception) {
|
||||
stopLoading();
|
||||
context.replyToMessage("Loading of tracks was interrupted due to an exception");
|
||||
LogHandler.log(exception);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package me.bobert.netneutrality.music;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import me.bobert.netneutrality.core.LogHandler;
|
||||
import me.bobert.netneutrality.core.context.CommandContext;
|
||||
import me.bobert.netneutrality.utils.MessageFunction;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class TrackLoadingHandler implements AudioLoadResultHandler {
|
||||
private final Logger logger = LogManager.getLogger(TrackLoadingHandler.class);
|
||||
|
||||
private final String identifier;
|
||||
private final SourceFinder.AudioSourceType type;
|
||||
private final boolean silent;
|
||||
private Consumer<Boolean> loadConsumer;
|
||||
private boolean loadingDisabled = false;
|
||||
private final Guild guild;
|
||||
private final TrackScheduler scheduler;
|
||||
private final CommandContext context;
|
||||
|
||||
public TrackLoadingHandler(CommandContext context, MusicManager manager, String identifier, SourceFinder.AudioSourceType type, boolean silent) {
|
||||
this.identifier = identifier;
|
||||
this.type = type;
|
||||
this.silent = silent;
|
||||
this.guild = manager.getGuild();
|
||||
this.scheduler = manager.getScheduler();
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
public synchronized void stopLoading() {
|
||||
logger.debug("Guild {}: stopped loading of new tracks", guild.getId());
|
||||
loadingDisabled = true;
|
||||
}
|
||||
|
||||
public void useCallbackOnLoad(Consumer<Boolean> consumer) {
|
||||
this.loadConsumer = consumer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trackLoaded(AudioTrack audioTrack) {
|
||||
if (loadConsumer != null) {
|
||||
loadConsumer.accept(true);
|
||||
}
|
||||
|
||||
if (loadingDisabled)
|
||||
return;
|
||||
|
||||
context.deleteMessage();
|
||||
|
||||
logger.debug("Guild {}: track loaded: {}", guild.getId(), audioTrack.getIdentifier());
|
||||
scheduler.getQueueManager().queue(audioTrack, context.getGuildUser());
|
||||
|
||||
if (!silent) {
|
||||
context.reply(MessageFunction.buildAddingTrackMessage(audioTrack, type));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void playlistLoaded(AudioPlaylist playlist) {
|
||||
if (loadConsumer != null) {
|
||||
loadConsumer.accept(true);
|
||||
}
|
||||
|
||||
if (loadingDisabled)
|
||||
return;
|
||||
|
||||
logger.debug("Guild {}: playlist loaded: {}", guild.getId(), identifier);
|
||||
|
||||
if (!playlist.getTracks().isEmpty()) {
|
||||
if (playlist.isSearchResult()) {
|
||||
|
||||
AudioTrack track = playlist.getTracks().get(0);
|
||||
scheduler.getQueueManager().queue(track, context.getGuildUser());
|
||||
|
||||
if (!silent) {
|
||||
context.reply(MessageFunction.buildAddingTrackMessage(track, type));
|
||||
}
|
||||
|
||||
} else {
|
||||
context.deleteMessage();
|
||||
if (!silent) {
|
||||
context.reply(MessageFunction.buildAddingPlaylistMessage(playlist, identifier, type));
|
||||
}
|
||||
|
||||
for (AudioTrack track : playlist.getTracks()) {
|
||||
if (loadingDisabled) {
|
||||
break;
|
||||
}
|
||||
|
||||
scheduler.getQueueManager().queue(track, context.getGuildUser());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void noMatches() {
|
||||
if (loadConsumer != null)
|
||||
loadConsumer.accept(false);
|
||||
|
||||
if (!silent)
|
||||
context.replyToMessage("No matches found");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFailed(FriendlyException e) {
|
||||
if (loadConsumer != null)
|
||||
loadConsumer.accept(false);
|
||||
|
||||
logger.debug("Guild {}: load failed for identifier {}", guild.getId(), identifier, e);
|
||||
LogHandler.log(e);
|
||||
|
||||
if (!silent)
|
||||
context.replyToMessage(MessageFunction.buildSentryErrorMessage("Failed to load track: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package me.bobert.netneutrality.music;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
|
||||
import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
|
||||
import me.bobert.netneutrality.core.LogHandler;
|
||||
import me.bobert.netneutrality.utils.MessageFunction;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class TrackScheduler extends AudioEventAdapter {
|
||||
private final static Logger logger = LogManager.getLogger(TrackScheduler.class);
|
||||
|
||||
private final QueueManager queueManager;
|
||||
private final MusicManager manager;
|
||||
private final AudioPlayer player;
|
||||
private MessageChannel currentChannel;
|
||||
private final Guild guild;
|
||||
|
||||
public TrackScheduler(MusicManager manager) {
|
||||
this.manager = manager;
|
||||
this.player = manager.getPlayer();
|
||||
this.guild = manager.getGuild();
|
||||
this.queueManager = new QueueManager(this);
|
||||
}
|
||||
|
||||
public QueueManager getQueueManager() {
|
||||
return queueManager;
|
||||
}
|
||||
|
||||
public MusicManager getMusicManager() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
public AudioPlayer getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
public Guild getGuild() {
|
||||
return guild;
|
||||
}
|
||||
|
||||
public MessageChannel getTextChannel() {
|
||||
return currentChannel;
|
||||
}
|
||||
|
||||
public synchronized void attachTextChannel(MessageChannel channel) {
|
||||
this.currentChannel = channel;
|
||||
}
|
||||
|
||||
public synchronized void detachTextChannel() {
|
||||
this.currentChannel = null;
|
||||
}
|
||||
|
||||
public void sendMessageToChannel(String msg) {
|
||||
if (currentChannel != null && currentChannel.canTalk()) {
|
||||
currentChannel.sendMessage(msg).queue();
|
||||
}
|
||||
}
|
||||
|
||||
public void sendMessageToChannel(MessageEmbed embed, MessageEmbed... embeds) {
|
||||
if (currentChannel != null && currentChannel.canTalk()) {
|
||||
currentChannel.sendMessageEmbeds(embed, embeds).queue();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerPause(AudioPlayer player) {
|
||||
// Player was paused
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerResume(AudioPlayer player) {
|
||||
// Player was resumed
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackStart(AudioPlayer player, AudioTrack track) {
|
||||
logger.debug("Guild {}: starting track: {}", guild.getId(), track.getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {
|
||||
logger.debug("Guild {}: currently playing track ended: {}", guild.getId(), track.getIdentifier());
|
||||
|
||||
if (endReason == AudioTrackEndReason.LOAD_FAILED) {
|
||||
logger.debug("Guild {}: failed loading track: {}", guild.getId(), track.getIdentifier());
|
||||
}
|
||||
|
||||
// check if mayStartNext
|
||||
if (endReason == AudioTrackEndReason.FINISHED || endReason == AudioTrackEndReason.LOAD_FAILED) {
|
||||
queueManager.startNextTrack();
|
||||
}
|
||||
|
||||
if (endReason == AudioTrackEndReason.STOPPED) {
|
||||
queueManager.removeHeadOfQueue();
|
||||
}
|
||||
|
||||
// endReason == FINISHED: A track finished or died by an exception (mayStartNext = true).
|
||||
// endReason == LOAD_FAILED: Loading of a track failed (mayStartNext = true).
|
||||
// endReason == STOPPED: The player was stopped.
|
||||
// endReason == REPLACED: Another track started playing while this had not finished
|
||||
// endReason == CLEANUP: Player hasn't been queried for a while, if you want you can put a
|
||||
// clone of this back to your queue
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception) {
|
||||
logger.debug("Guild {}: exception while playing track [{}]: {}", guild.getId(), track.getIdentifier(), exception.getMessage());
|
||||
LogHandler.log(exception);
|
||||
sendMessageToChannel(MessageFunction.buildSentryErrorMessage(String.format("Error while playing track [%s]: %s", track.getInfo().title, exception.getMessage())));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrackStuck(AudioPlayer player, AudioTrack track, long thresholdMs) {
|
||||
logger.debug("Guild {}: currently playing track is stuck: {}", guild.getId(), track.getIdentifier());
|
||||
sendMessageToChannel("Current track is stuck, skipping to next");
|
||||
queueManager.startNextTrack();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package me.bobert.netneutrality.music.loadable;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AppleMostPlayed implements LoadableMusicSource {
|
||||
@Override
|
||||
public String url() {
|
||||
return "https://rss.applemarketingtools.com/api/v2/ca/music/most-played/25/songs.json";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<LoadableSong> parse(JSONObject obj) {
|
||||
List<LoadableSong> songs = new ArrayList<>();
|
||||
JSONArray results = obj.getJSONObject("feed").getJSONArray("results");
|
||||
|
||||
results.forEach(o -> {
|
||||
JSONObject song = (JSONObject) o;
|
||||
String artist = song.getString("artistName");
|
||||
String songName = song.getString("name");
|
||||
String artwork = song.getString("artworkUrl100");
|
||||
|
||||
songs.add(new LoadableSong(artist, songName, artwork));
|
||||
});
|
||||
|
||||
return songs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifier() {
|
||||
return "apple";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package me.bobert.netneutrality.music.loadable;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.List;
|
||||
|
||||
public class CachedSource {
|
||||
private final LoadableMusicSource source;
|
||||
private OffsetDateTime lastAccess;
|
||||
private final List<LoadableSong> songs;
|
||||
|
||||
protected CachedSource(LoadableMusicSource source, List<LoadableSong> songs) {
|
||||
this.source = source;
|
||||
this.lastAccess = OffsetDateTime.now();
|
||||
this.songs = songs;
|
||||
}
|
||||
|
||||
public List<LoadableSong> getSongs() {
|
||||
return songs;
|
||||
}
|
||||
|
||||
public OffsetDateTime getLastAccess() {
|
||||
return lastAccess;
|
||||
}
|
||||
|
||||
public LoadableMusicSource getSource() {
|
||||
return source;
|
||||
}
|
||||
|
||||
public void updateAccessTime() {
|
||||
this.lastAccess = OffsetDateTime.now();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package me.bobert.netneutrality.music.loadable;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface LoadableMusicSource {
|
||||
String url();
|
||||
|
||||
List<LoadableSong> parse(JSONObject obj);
|
||||
|
||||
String getIdentifier();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package me.bobert.netneutrality.music.loadable;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
public record LoadableSong(String artist, String name, String artworkUrl) {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LoadableSong{" +
|
||||
"artist='" + artist + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
", artworkUrl='" + artworkUrl + '\'' +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package me.bobert.netneutrality.music.loadable;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.http.WebModule;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class LoadableSourceManager {
|
||||
|
||||
private final Logger logger = LogManager.getLogger(LoadableSourceManager.class);
|
||||
private static final Map<String, CachedSource> cache = new HashMap<>();
|
||||
|
||||
public CachedSource get(LoadableMusicSource source) {
|
||||
|
||||
if (cache.containsKey(source.getIdentifier())) {
|
||||
CachedSource cachedSource = cache.get(source.getIdentifier());
|
||||
|
||||
if (OffsetDateTime.now().isAfter(cachedSource.getLastAccess().plusHours(4))) {
|
||||
cachedSource.getSongs().clear();
|
||||
cachedSource.getSongs().addAll(loadFromUrl(source));
|
||||
cachedSource.updateAccessTime();
|
||||
}
|
||||
|
||||
return cachedSource;
|
||||
}
|
||||
|
||||
List<LoadableSong> songs = loadFromUrl(source);
|
||||
CachedSource newCachedSource = new CachedSource(source, songs);
|
||||
|
||||
cache.put(source.getIdentifier(), newCachedSource);
|
||||
return newCachedSource;
|
||||
}
|
||||
|
||||
private List<LoadableSong> loadFromUrl(LoadableMusicSource source) {
|
||||
WebModule web = new WebModule(source.url());
|
||||
JSONObject obj = web.request().getAsObject();
|
||||
|
||||
if (obj == null)
|
||||
return Collections.emptyList();
|
||||
|
||||
List<LoadableSong> songs = source.parse(obj);
|
||||
Collections.shuffle(songs);
|
||||
|
||||
return songs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package me.bobert.netneutrality.music.source;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
|
||||
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManager;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.DataFormatTools;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.ExceptionTools;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.JsonBrowser;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.io.HttpClientTools;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterfaceManager;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioItem;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioReference;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity.SUSPICIOUS;
|
||||
|
||||
public class UdioAudioSourceManager implements AudioSourceManager {
|
||||
|
||||
private static final Logger log = LogManager.getLogger(UdioAudioSourceManager.class);
|
||||
|
||||
|
||||
private static final String TRACK_URL_REGEX = "^https://www.udio.com/songs/[0-9A-Za-z]+$";
|
||||
private static final Pattern trackUrlPattern = Pattern.compile(TRACK_URL_REGEX);
|
||||
|
||||
private final HttpInterfaceManager httpInterfaceManager;
|
||||
|
||||
public UdioAudioSourceManager() {
|
||||
httpInterfaceManager = HttpClientTools.createDefaultThreadLocalManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSourceName() {
|
||||
return "udio";
|
||||
}
|
||||
|
||||
@Override
|
||||
public AudioItem loadItem(AudioPlayerManager manager, AudioReference reference) {
|
||||
if (!trackUrlPattern.matcher(reference.identifier).matches()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try (HttpInterface httpInterface = httpInterfaceManager.getInterface()) {
|
||||
return loadFromTrackPage(httpInterface, reference.identifier);
|
||||
} catch (IOException e) {
|
||||
throw new FriendlyException("Loading Udio track information failed.", SUSPICIOUS, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isTrackEncodable(AudioTrack track) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encodeTrack(AudioTrack track, DataOutput output) throws IOException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public AudioTrack decodeTrack(AudioTrackInfo trackInfo, DataInput input) throws IOException {
|
||||
return new UdioAudioTrack(trackInfo, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
ExceptionTools.closeWithWarnings(httpInterfaceManager);
|
||||
}
|
||||
|
||||
public HttpInterface getHttpInterface() {
|
||||
return httpInterfaceManager.getInterface();
|
||||
}
|
||||
|
||||
private AudioItem loadFromTrackPage(HttpInterface httpInterface, String trackUrl) throws IOException {
|
||||
|
||||
try (CloseableHttpResponse response = httpInterface.execute(new HttpGet(trackUrl))) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
|
||||
if (statusCode == HttpStatus.SC_NOT_FOUND) {
|
||||
return AudioReference.NO_TRACK;
|
||||
} else if (!HttpClientTools.isSuccessWithContent(statusCode)) {
|
||||
throw new FriendlyException("Server responded with an error.", SUSPICIOUS,
|
||||
new IllegalStateException("Response code is " + statusCode));
|
||||
}
|
||||
|
||||
return loadTrackFromPageContent(trackUrl, IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
|
||||
private AudioTrack loadTrackFromPageContent(String trackUrl, String content) throws IOException {
|
||||
JsonBrowser trackInfo = loadTrackInfoFromPageContent(content);
|
||||
|
||||
if (trackInfo == null) {
|
||||
log.debug("Udio HTML: {}", content);
|
||||
throw new FriendlyException("Udio: Failed to extract track information from web page.", SUSPICIOUS, null);
|
||||
}
|
||||
|
||||
String title = trackInfo.get("title").text();
|
||||
String artist = trackInfo.get("artist").text();
|
||||
long duration = (long) (trackInfo.get("duration").as(Double.class) * 1000.0);
|
||||
String playbackUrl = trackInfo.get("song_path").text();
|
||||
String artworkUrl = getArtworkUrlFromImagePath(trackInfo);
|
||||
|
||||
AudioTrackInfo audioTrackInfo = new AudioTrackInfo(title, artist, duration, trackUrl, false, playbackUrl, artworkUrl, null);
|
||||
|
||||
return new UdioAudioTrack(audioTrackInfo, this);
|
||||
}
|
||||
|
||||
protected JsonBrowser loadTrackInfoFromPageContent(String content) throws IOException {
|
||||
String startTag = "self.__next_f.push([1,\"9:";
|
||||
String endTag = "\\n\"])";
|
||||
|
||||
String json = DataFormatTools.extractBetween(content, startTag, endTag);
|
||||
|
||||
if (json == null) {
|
||||
log.error("Track JSON not found in page");
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
String unescapedJson = json.replace("\\", "");
|
||||
JsonBrowser parsedJson = JsonBrowser.parse(unescapedJson);
|
||||
|
||||
return parsedJson.index(3).get("children").index(3).get("children").index(3).get("track");
|
||||
} catch (JsonParseException e) {
|
||||
log.error("Failed to parse track JSON: {}", json);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String getArtworkUrlFromImagePath(JsonBrowser trackInfo) {
|
||||
String imagePath = trackInfo.get("image_path").text();
|
||||
String artworkOptions = "width=3840,quality=75";
|
||||
|
||||
return imagePath.replace("public", artworkOptions);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package me.bobert.netneutrality.music.source;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.container.mp3.Mp3AudioTrack;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.JsonBrowser;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.io.HttpClientTools;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface;
|
||||
import com.sedmelluq.discord.lavaplayer.tools.io.PersistentHttpStream;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo;
|
||||
import com.sedmelluq.discord.lavaplayer.track.DelegatedAudioTrack;
|
||||
import com.sedmelluq.discord.lavaplayer.track.playback.LocalAudioTrackExecutor;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity.SUSPICIOUS;
|
||||
|
||||
public class UdioAudioTrack extends DelegatedAudioTrack {
|
||||
|
||||
private static final Logger log = LogManager.getLogger(UdioAudioTrack.class);
|
||||
|
||||
private final UdioAudioSourceManager sourceManager;
|
||||
|
||||
/**
|
||||
* @param trackInfo Track info
|
||||
* @param sourceManager Source manager which was used to find this track
|
||||
*/
|
||||
public UdioAudioTrack(AudioTrackInfo trackInfo, UdioAudioSourceManager sourceManager) {
|
||||
super(trackInfo);
|
||||
|
||||
this.sourceManager = sourceManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void process(LocalAudioTrackExecutor localExecutor) throws Exception {
|
||||
try (HttpInterface httpInterface = sourceManager.getHttpInterface()) {
|
||||
String playbackUrl = loadPlaybackUrl(httpInterface);
|
||||
|
||||
log.debug("Starting Udio track from URL: {}", playbackUrl);
|
||||
|
||||
try (PersistentHttpStream stream = new PersistentHttpStream(httpInterface, new URI(playbackUrl), null)) {
|
||||
processDelegate(new Mp3AudioTrack(trackInfo, stream), localExecutor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String loadPlaybackUrl(HttpInterface httpInterface) throws IOException {
|
||||
JsonBrowser trackInfo = loadTrackInfo(httpInterface);
|
||||
|
||||
if (trackInfo == null) {
|
||||
throw new FriendlyException("Udio: Failed to extract track information from web page.", SUSPICIOUS, null);
|
||||
}
|
||||
|
||||
return trackInfo.get("song_path").text();
|
||||
}
|
||||
|
||||
private JsonBrowser loadTrackInfo(HttpInterface httpInterface) throws IOException {
|
||||
try (CloseableHttpResponse response = httpInterface.execute(new HttpGet(trackInfo.identifier))) {
|
||||
int statusCode = response.getStatusLine().getStatusCode();
|
||||
|
||||
if (!HttpClientTools.isSuccessWithContent(statusCode)) {
|
||||
throw new FriendlyException("Server responded with an error.", SUSPICIOUS,
|
||||
new IllegalStateException("Response code for player config is " + statusCode));
|
||||
}
|
||||
|
||||
return sourceManager.loadTrackInfoFromPageContent(IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package me.bobert.netneutrality.thread;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.exceptions.ExecutorException;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class BaseThreadFactory implements ThreadFactory {
|
||||
|
||||
private final AtomicLong threadId = new AtomicLong(1);
|
||||
private final String name;
|
||||
|
||||
public BaseThreadFactory(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Thread newThread(@NotNull Runnable r) {
|
||||
Thread thread = new Thread(r);
|
||||
thread.setName(name + "-" + threadId.getAndIncrement());
|
||||
thread.setUncaughtExceptionHandler(new ExecutorException());
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package me.bobert.netneutrality.thread;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.NetNeutrality;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import net.dv8tion.jda.api.entities.Guild;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MusicPlayerAutoDisconnectThread implements Runnable {
|
||||
|
||||
private final Logger logger = LogManager.getLogger(MusicPlayerAutoDisconnectThread.class);
|
||||
private final MusicManager manager;
|
||||
|
||||
//TODO: move to scheduler
|
||||
public MusicPlayerAutoDisconnectThread(MusicManager musicManager) {
|
||||
manager = musicManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Guild guild = manager.getGuild();
|
||||
VoiceChannel channel = (VoiceChannel) guild.getAudioManager().getConnectedChannel();
|
||||
|
||||
try {
|
||||
if (channel != null) {
|
||||
if (conditionsMetToDisconnect(channel)) {
|
||||
// Sleep 30 seconds then check again (prevents disconnecting right after a track ends)
|
||||
Thread.sleep(30000);
|
||||
|
||||
if (conditionsMetToDisconnect(channel)) {
|
||||
logger.debug("Guild {}: auto-disconnect player from voice channel: {}", guild.getId(), channel.getId());
|
||||
manager.leave();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean conditionsMetToDisconnect(VoiceChannel channel) {
|
||||
List<Member> members = channel.getMembers().stream().filter(member -> !member.getId().equals(NetNeutrality.getSelf().getId())).toList();
|
||||
|
||||
return members.isEmpty() || manager.getScheduler().getQueueManager().isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package me.bobert.netneutrality.thread;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.NetNeutrality;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.core.LoggerContext;
|
||||
import org.apache.logging.log4j.core.config.Configurator;
|
||||
|
||||
public class ShutdownThread implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (LogManager.getContext() instanceof LoggerContext) {
|
||||
System.out.println("Shutting down log4j2");
|
||||
Configurator.shutdown((LoggerContext) LogManager.getContext());
|
||||
}
|
||||
|
||||
if (NetNeutrality.getJDA() != null) {
|
||||
NetNeutrality.shutdown();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package me.bobert.netneutrality.thread;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class ThreadManager {
|
||||
|
||||
public enum ExecutorType {
|
||||
SINGLE, SCHEDULED, FIXED
|
||||
}
|
||||
|
||||
private static final Map<String, ExecutorService> executors = new ConcurrentHashMap<>();
|
||||
|
||||
public static ScheduledExecutorService getMusicExecutor() {
|
||||
return (ScheduledExecutorService) executors.computeIfAbsent(
|
||||
"music-executor",
|
||||
s -> Executors.newScheduledThreadPool(
|
||||
Runtime.getRuntime().availableProcessors(),
|
||||
new BaseThreadFactory("music-executor")
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static ExecutorService getExecutor() {
|
||||
return executors.computeIfAbsent("general-executor", s -> Executors.newCachedThreadPool(new BaseThreadFactory("general-executor")));
|
||||
}
|
||||
|
||||
public static ExecutorService getExecutor(String name) {
|
||||
return executors.computeIfAbsent(name, s -> Executors.newSingleThreadExecutor(new BaseThreadFactory(name)));
|
||||
}
|
||||
|
||||
public static ExecutorService getExecutor(String name, ExecutorType type) {
|
||||
ExecutorService executor;
|
||||
|
||||
switch (type) {
|
||||
case FIXED -> executor = executors.computeIfAbsent(name, s -> Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new BaseThreadFactory(name)));
|
||||
case SCHEDULED -> executor = executors.computeIfAbsent(name, s -> Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), new BaseThreadFactory(name)));
|
||||
default -> executor = executors.computeIfAbsent(name, s -> Executors.newSingleThreadExecutor(new BaseThreadFactory(name)));
|
||||
}
|
||||
|
||||
return executor;
|
||||
}
|
||||
|
||||
public static void shutdownExecutor() {
|
||||
System.out.println("[Executor] Shutting down...");
|
||||
|
||||
try {
|
||||
for (ExecutorService ex : executors.values()) {
|
||||
ex.shutdown();
|
||||
ex.awaitTermination(1, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
System.err.println("[Executor] Running tasks interrupted");
|
||||
}
|
||||
finally {
|
||||
executors.forEach((name, ex) -> {
|
||||
if (!ex.isTerminated()) {
|
||||
System.err.println("[Executor: " + name + "] Forcing shutdown of running tasks..");
|
||||
ex.shutdownNow();
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
System.out.println("[Executor] Shutdown finished!");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
package me.bobert.netneutrality.utils;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import io.sentry.ISpan;
|
||||
import io.sentry.ITransaction;
|
||||
import io.sentry.Sentry;
|
||||
import io.sentry.SpanStatus;
|
||||
import me.bobert.netneutrality.core.context.CommandContext;
|
||||
import me.bobert.netneutrality.thread.ThreadManager;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.MessageHistory;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
|
||||
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
public class ChannelMessagePurger {
|
||||
|
||||
private static final Logger logger = LogManager.getLogger(ChannelMessagePurger.class);
|
||||
private static final Map<GuildMessageChannel, ChannelMessagePurger> instances = new HashMap<>();
|
||||
private final GuildMessageChannel channel;
|
||||
private final Lock lock;
|
||||
|
||||
private ChannelMessagePurger(GuildMessageChannel channel) {
|
||||
this.channel = channel;
|
||||
this.lock = new ReentrantLock();
|
||||
}
|
||||
|
||||
public static ChannelMessagePurger getPurger(GuildMessageChannel channel) {
|
||||
return instances.computeIfAbsent(channel, ChannelMessagePurger::new);
|
||||
}
|
||||
|
||||
public boolean canLockBeAcquired() {
|
||||
boolean acquired = lock.tryLock();
|
||||
if (acquired)
|
||||
lock.unlock();
|
||||
|
||||
return acquired;
|
||||
}
|
||||
|
||||
public void purgeMessages(CommandContext context, int toPurge) {
|
||||
ThreadManager.getExecutor().submit(() -> {
|
||||
if (lock.tryLock()) {
|
||||
try {
|
||||
int total = countAllChannelMessages(channel);
|
||||
int finalToPurge = toPurge;
|
||||
|
||||
if (finalToPurge > total) {
|
||||
finalToPurge = total;
|
||||
}
|
||||
|
||||
safePurge(context, finalToPurge);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void purgeChannel(CommandContext context) {
|
||||
ThreadManager.getExecutor().submit(() -> {
|
||||
if (lock.tryLock()) {
|
||||
try {
|
||||
int total = countAllChannelMessages(channel);
|
||||
context.reply("Number of messages to purge: %d", total);
|
||||
|
||||
safePurge(context, total);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private synchronized int countAllChannelMessages(GuildMessageChannel channel) {
|
||||
logger.info("Guild {}: counting all messages from channel {}", channel.getGuild().getId(), channel.getId());
|
||||
MessageHistory history = channel.getHistory();
|
||||
int totalCount = 0;
|
||||
int iterationCount = 0;
|
||||
|
||||
while (totalCount == iterationCount) {
|
||||
history.retrievePast(100).complete();
|
||||
|
||||
iterationCount += 100;
|
||||
totalCount = history.size();
|
||||
}
|
||||
logger.info("Guild {}: Counted {} messages from channel {}", channel.getGuild().getId(), totalCount, channel.getId());
|
||||
|
||||
return totalCount;
|
||||
}
|
||||
|
||||
private synchronized void safePurge(CommandContext context, int toPurge) {
|
||||
ITransaction transaction = Sentry.startTransaction(String.format("purge messages from guild %s)", channel.getGuild().getId()), "task");
|
||||
transaction.setData("channel", channel.getId());
|
||||
transaction.setData("toPurge", toPurge);
|
||||
|
||||
if (toPurge == 1) {
|
||||
toPurge += 1;
|
||||
}
|
||||
|
||||
int tracker = toPurge;
|
||||
int iteration = tracker / 100;
|
||||
boolean olderThan2Weeks = false;
|
||||
context.replyToMessage("Purging iterations to do: %d", iteration);
|
||||
|
||||
try {
|
||||
for (int i = 0; i < iteration; i++) {
|
||||
ISpan innerSpan = transaction.startChild("retrieve-messages", "Processing iteration " + (i + 1) + "/" + iteration);
|
||||
try {
|
||||
List<Message> messages = channel.getHistory().retrievePast(100).complete();
|
||||
channel.deleteMessages(messages).complete();
|
||||
tracker -= 100;
|
||||
innerSpan.setStatus(SpanStatus.OK);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
olderThan2Weeks = true;
|
||||
} catch (Exception e) {
|
||||
innerSpan.setThrowable(e);
|
||||
innerSpan.setStatus(SpanStatus.INTERNAL_ERROR);
|
||||
} finally {
|
||||
innerSpan.finish();
|
||||
}
|
||||
}
|
||||
|
||||
if (tracker > 0) {
|
||||
ISpan innerSpan = transaction.startChild("retrieve-messages", String.format("Processing final iteration (%d messages)", tracker));
|
||||
try {
|
||||
List<Message> messages = channel.getHistory().retrievePast(tracker).complete();
|
||||
channel.deleteMessages(messages).complete();
|
||||
innerSpan.setStatus(SpanStatus.OK);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
olderThan2Weeks = true;
|
||||
} catch (Exception e) {
|
||||
innerSpan.setThrowable(e);
|
||||
innerSpan.setStatus(SpanStatus.INTERNAL_ERROR);
|
||||
} finally {
|
||||
innerSpan.finish();
|
||||
}
|
||||
|
||||
|
||||
innerSpan.finish();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
transaction.setThrowable(e);
|
||||
transaction.setStatus(SpanStatus.INTERNAL_ERROR);
|
||||
} finally {
|
||||
transaction.finish();
|
||||
MessageCreateBuilder builder = new MessageCreateBuilder()
|
||||
.addContent("Finished purging ")
|
||||
.addContent(String.valueOf(toPurge))
|
||||
.addContent(" messages.");
|
||||
|
||||
if (olderThan2Weeks) {
|
||||
builder.addContent("\n").addContent("Messages older than two weeks weren't deleted.");
|
||||
}
|
||||
context.reply(builder.build());
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package me.bobert.netneutrality.utils;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import io.sentry.Breadcrumb;
|
||||
import io.sentry.Sentry;
|
||||
import io.sentry.SentryLevel;
|
||||
import me.bobert.netneutrality.core.GuildManager;
|
||||
import me.bobert.netneutrality.core.SessionManager;
|
||||
import me.bobert.netneutrality.db.entities.OGuild;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.ObjectNotFoundException;
|
||||
import org.hibernate.Session;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class JpaUtils {
|
||||
|
||||
/**
|
||||
* Tries to read the persistent state into the object.
|
||||
*
|
||||
* If the object is not found in the database and is of type {@link OGuild}, tries to recreate entity and throws
|
||||
* an exception to block execution.
|
||||
*
|
||||
* The session is not closed
|
||||
*
|
||||
* @param session The session in which to load the persistent object
|
||||
* @param object The persistent object
|
||||
* @param id Identifier
|
||||
*/
|
||||
public static void tryLoad(Session session, Object object, Serializable id) {
|
||||
try {
|
||||
session.load(object, id);
|
||||
} catch (HibernateException exception) {
|
||||
if (exception instanceof ObjectNotFoundException) {
|
||||
Sentry.addBreadcrumb(Breadcrumb.error("ObjectNotFoundException"));
|
||||
|
||||
if (object instanceof OGuild oGuild) {
|
||||
String cachedId = oGuild.getGuildId();
|
||||
Sentry.captureMessage("Trying to load Guild[" + cachedId + "] while entity doesn't exist in database, reloading..", SentryLevel.ERROR);
|
||||
|
||||
GuildManager guildManager = GuildManager.getManager(cachedId);
|
||||
guildManager.reloadEntity();
|
||||
guildManager.getBlacklistCache().clear();
|
||||
Sentry.captureMessage("Guild[" + cachedId + "] reloaded", SentryLevel.INFO);
|
||||
}
|
||||
}
|
||||
|
||||
session.close();
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateWithTransaction(Object persistantEntity, Runnable runnable) {
|
||||
Session session = SessionManager.getSessionFactory().openSession();
|
||||
session.beginTransaction();
|
||||
|
||||
runnable.run();
|
||||
|
||||
session.update(persistantEntity);
|
||||
session.getTransaction().commit();
|
||||
session.close();
|
||||
}
|
||||
|
||||
public static void getWithSession(OGuild oGuild, Consumer<Session> consumer) {
|
||||
getWithSession(oGuild, oGuild.getGuildId(), consumer);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param persistantEntity The entity to load
|
||||
* @param id The identifier for the entity
|
||||
* @param close If the session should be closed after consumer is executed
|
||||
* @param consumer The comsumer, which exposes the session
|
||||
*/
|
||||
public static void getWithSession(Object persistantEntity, Serializable id, Consumer<Session> consumer) {
|
||||
Session session = SessionManager.getSessionFactory().openSession();
|
||||
tryLoad(session, persistantEntity, id);
|
||||
|
||||
consumer.accept(session);
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,285 @@
|
||||
package me.bobert.netneutrality.utils;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo;
|
||||
import io.sentry.Sentry;
|
||||
import me.bobert.netneutrality.command.AbstractCommand;
|
||||
import me.bobert.netneutrality.command.CommandManager;
|
||||
import me.bobert.netneutrality.command.CommandType;
|
||||
import me.bobert.netneutrality.core.context.CommandContext;
|
||||
import me.bobert.netneutrality.exceptions.CommandException;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import me.bobert.netneutrality.music.QueueManager;
|
||||
import me.bobert.netneutrality.music.SourceFinder;
|
||||
import net.dv8tion.jda.api.EmbedBuilder;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Message;
|
||||
import net.dv8tion.jda.api.entities.MessageEmbed;
|
||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||
import org.apache.commons.lang3.time.DurationFormatUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.awt.*;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class MessageFunction {
|
||||
|
||||
private final static Logger logger = LogManager.getLogger(MessageFunction.class);
|
||||
|
||||
public static MessageEmbed buildCurrentlyPlayingMessage(MusicManager manager) {
|
||||
AudioTrack current = manager.getCurrentlyPlaying();
|
||||
|
||||
EmbedBuilder message = new EmbedBuilder();
|
||||
message.setAuthor("Link to track", String.format("https://youtu.be/%s", current.getIdentifier()));
|
||||
message.setThumbnail(String.format("https://img.youtube.com/vi/%s/default.jpg", current.getIdentifier()));
|
||||
message.setTitle("Currently playing:");
|
||||
message.setColor(Color.BLUE);
|
||||
message.addField("Track title: ", current.getInfo().title, false);
|
||||
message.addField("Duration: ", DurationFormatUtils.formatDuration(current.getPosition(), "**HH:mm:ss**", true) + " / " + DurationFormatUtils.formatDuration(current.getDuration(), "**HH:mm:ss**", true), false);
|
||||
message.setTimestamp(OffsetDateTime.now());
|
||||
|
||||
return message.build();
|
||||
}
|
||||
|
||||
public static MessageEmbed buildAddingTrackMessage(AudioTrack track, SourceFinder.AudioSourceType type) {
|
||||
EmbedBuilder message = new EmbedBuilder();
|
||||
String url = track.getIdentifier();
|
||||
String thumbnail = null;
|
||||
|
||||
if (type == SourceFinder.AudioSourceType.Youtube || type == SourceFinder.AudioSourceType.YTSearch || type == SourceFinder.AudioSourceType.YTMusic) {
|
||||
url = String.format("https://youtu.be/%s", track.getIdentifier());
|
||||
thumbnail = String.format("https://i3.ytimg.com/vi/%s/hqdefault.jpg", track.getIdentifier());
|
||||
}
|
||||
|
||||
if (type == SourceFinder.AudioSourceType.Udio) {
|
||||
thumbnail = track.getInfo().artworkUrl;
|
||||
}
|
||||
|
||||
if (thumbnail != null) {
|
||||
message.setThumbnail(thumbnail);
|
||||
}
|
||||
message.setTitle("Adding track to queue..");
|
||||
message.setColor(Color.BLUE);
|
||||
message.addField("Track title: ", String.format("[**`%s`**](%s)", track.getInfo().title, url), false);
|
||||
message.addField("Duration: ", DurationFormatUtils.formatDuration(track.getDuration(), "**HH:mm:ss**", true), false);
|
||||
message.setTimestamp(OffsetDateTime.now());
|
||||
|
||||
return message.build();
|
||||
}
|
||||
|
||||
public static MessageEmbed buildSearchResultsMessage(List<AudioTrack> tracks) {
|
||||
EmbedBuilder message = new EmbedBuilder();
|
||||
|
||||
message.setTitle("Select the track to add to the queue");
|
||||
message.setColor(Color.BLUE);
|
||||
|
||||
int i = 0;
|
||||
for (AudioTrack track : tracks) {
|
||||
message.addField(++i + ": ", String.format("**`%s`**", track.getInfo().title), false);
|
||||
}
|
||||
|
||||
return message.build();
|
||||
}
|
||||
|
||||
public static MessageEmbed buildAddingPlaylistMessage(AudioPlaylist playlist, String identifier, SourceFinder.AudioSourceType type) {
|
||||
EmbedBuilder message = new EmbedBuilder();
|
||||
String url = identifier;
|
||||
String thumbnail = null;
|
||||
|
||||
if (type == SourceFinder.AudioSourceType.Youtube || type == SourceFinder.AudioSourceType.YTSearch) {
|
||||
url = String.format("https://youtu.be/%s", playlist.getTracks().get(0).getIdentifier());
|
||||
thumbnail = String.format("https://i3.ytimg.com/vi/%s/hqdefault.jpg", playlist.getTracks().get(0).getIdentifier());
|
||||
}
|
||||
|
||||
if (type == SourceFinder.AudioSourceType.YTMusic) {
|
||||
thumbnail = String.format("https://i3.ytimg.com/vi/%s/hqdefault.jpg", playlist.getTracks().get(0).getIdentifier());
|
||||
}
|
||||
|
||||
if (thumbnail != null) {
|
||||
message.setThumbnail(thumbnail);
|
||||
}
|
||||
message.setTitle("Adding playlist to queue..");
|
||||
message.setColor(Color.BLUE);
|
||||
message.addField("Playlist: ", String.format("[**`%s`**](%s)", playlist.getName(), url), false);
|
||||
message.addField("Number of tracks: ", String.valueOf(playlist.getTracks().size()), false);
|
||||
message.setTimestamp(OffsetDateTime.now());
|
||||
|
||||
return message.build();
|
||||
}
|
||||
|
||||
public static MessageEmbed buildViewQueueMessage(MusicManager manager, boolean currentlyPlaying) {
|
||||
ArrayList<AudioTrack> tracks = manager.getScheduler().getQueueManager().getAllQueuedTrack();
|
||||
AudioTrack current = manager.getCurrentlyPlaying();
|
||||
int numberofTracksShown = Math.min(tracks.size(), 10);
|
||||
|
||||
EmbedBuilder message = new EmbedBuilder();
|
||||
message.setTitle(String.format("Current queue (showing %d out of %d items):", numberofTracksShown, tracks.size()));
|
||||
message.setColor(Color.BLUE);
|
||||
|
||||
int index = 0;
|
||||
for (AudioTrack track : tracks) {
|
||||
AudioTrackInfo info = track.getInfo();
|
||||
|
||||
if (current != null && current.getInfo().title.equals(info.title) && currentlyPlaying) {
|
||||
message.addField("Track #" + ++index + " (Currently playing)", info.title, false);
|
||||
} else {
|
||||
message.addField("Track #" + ++index, info.title, false);
|
||||
}
|
||||
|
||||
if (index == 10) break;
|
||||
}
|
||||
|
||||
message.setTimestamp(OffsetDateTime.now());
|
||||
|
||||
return message.build();
|
||||
}
|
||||
|
||||
public static MessageEmbed buildUserTracksMessage(MusicManager manager, Member member) {
|
||||
List<QueueManager.TrackInfo> tracks = manager.getScheduler()
|
||||
.getQueueManager()
|
||||
.getQueueWithFilter(trackInfo -> member.equals(trackInfo.getRequester()));
|
||||
AudioTrack current = manager.getCurrentlyPlaying();
|
||||
int numberofTracksShown = Math.min(tracks.size(), 10);
|
||||
|
||||
EmbedBuilder message = new EmbedBuilder();
|
||||
message.setTitle(String.format("Tracks added by %s (showing %d out of %d items):", member.getAsMention(), numberofTracksShown, tracks.size()));
|
||||
message.setColor(Color.BLUE);
|
||||
|
||||
int index = 0;
|
||||
for (QueueManager.TrackInfo track : tracks) {
|
||||
AudioTrackInfo info = track.getTrack().getInfo();
|
||||
|
||||
if (current != null && current.getInfo().title.equals(info.title)) {
|
||||
message.addField("Track #" + ++index + " (Currently playing)", info.title, false);
|
||||
} else {
|
||||
message.addField("Track #" + ++index, info.title, false);
|
||||
}
|
||||
|
||||
if (index == 10) break;
|
||||
}
|
||||
message.setTimestamp(OffsetDateTime.now());
|
||||
|
||||
return message.build();
|
||||
}
|
||||
|
||||
public static ArrayList<MessageEmbed> buildHelpMessage(Member member) {
|
||||
HashMap<CommandType, EmbedBuilder> help = new HashMap<>();
|
||||
|
||||
for (AbstractCommand command : CommandManager.commands.values()) {
|
||||
if (command.getType() == null)
|
||||
continue;
|
||||
|
||||
String description = "No description yet";
|
||||
|
||||
if (command.isEnabled() && command.getType() != CommandType.UNLISTED) {
|
||||
if (command.getType() == CommandType.ADMINISTRATION && !UserFunction.isAdmin(member))
|
||||
continue;
|
||||
|
||||
if (command.getDescription() != null)
|
||||
description = command.getDescription();
|
||||
|
||||
EmbedBuilder message;
|
||||
if (help.containsKey(command.getType())) {
|
||||
message = help.get(command.getType());
|
||||
} else {
|
||||
help.put(command.getType(), message = new EmbedBuilder());
|
||||
}
|
||||
|
||||
if (message.isEmpty()) {
|
||||
message.setTitle(command.getType().getDescription() + " commands for NetNeutrality");
|
||||
message.setColor(Color.BLUE);
|
||||
}
|
||||
|
||||
message.addField(String.format("%s", command.getCommandName()), description, false);
|
||||
}
|
||||
}
|
||||
|
||||
ArrayList<MessageEmbed> messages = new ArrayList<>();
|
||||
help.values().forEach(embedBuilder -> messages.add(embedBuilder.build()));
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
public static MessageEmbed buildSentryErrorMessage(String error) {
|
||||
EmbedBuilder message = new EmbedBuilder();
|
||||
message.setColor(Color.RED);
|
||||
message.setTitle(":warning: Error during execution :warning:");
|
||||
message.addField("Error message:", error, false);
|
||||
message.addField("Error ID:", Sentry.getLastEventId().toString(), false);
|
||||
message.setTimestamp(OffsetDateTime.now());
|
||||
|
||||
return message.build();
|
||||
}
|
||||
|
||||
public static MessageEmbed buildDebugMessage(CommandContext context, CommandException error) {
|
||||
EmbedBuilder message = new EmbedBuilder();
|
||||
message.setColor(Color.RED);
|
||||
message.setTitle(":warning: Exception in command execution");
|
||||
message.addField("Caused by command:", context.getCommand().getCommandName(), false);
|
||||
message.addField("Exception:", error.getMessage(), false);
|
||||
if (error.getCause() != null) {
|
||||
String cause;
|
||||
if (error.getCause().getMessage() != null && !error.getCause().getMessage().isEmpty()) {
|
||||
cause = error.getCause().getMessage();
|
||||
} else {
|
||||
cause = error.getCause().getClass().getName();
|
||||
}
|
||||
message.addField("Caused by:", cause, false);
|
||||
}
|
||||
|
||||
message.setFooter("Requested by " + context.getGuildUser().getEffectiveName(), context.getGuildUser().getAvatarUrl());
|
||||
message.setTimestamp(OffsetDateTime.now());
|
||||
|
||||
return message.build();
|
||||
}
|
||||
|
||||
public static MessageEmbed buildEditMessage(Member member, Message message, String originalMessage) {
|
||||
EmbedBuilder editEmbed = new EmbedBuilder();
|
||||
editEmbed.setColor(Color.RED);
|
||||
editEmbed.setDescription(":pencil2: **Message edited by " + member.getEffectiveName() + "**");
|
||||
try {
|
||||
editEmbed.addField("Original message", (originalMessage == null ? "Not available" : originalMessage), false);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
editEmbed.addField("Original message:", "Message too long.", false);
|
||||
}
|
||||
|
||||
try {
|
||||
editEmbed.addField("Edited to:", message.getContentDisplay(), false);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
editEmbed.addField("Edited to:", "Message too long.", false);
|
||||
}
|
||||
|
||||
editEmbed.setFooter("ID : " + message.getId(), null);
|
||||
editEmbed.setTimestamp(message.getTimeCreated());
|
||||
|
||||
return editEmbed.build();
|
||||
}
|
||||
|
||||
public static void testMessageAgainstBlacklist(Message message, MessageChannel channel, Set<String> cache) {
|
||||
boolean found = false;
|
||||
for (String word : cache) {
|
||||
if (message.getContentRaw().toLowerCase().contains(word)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
message.delete().queue();
|
||||
channel.sendMessage("Not in my christian server").queue();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package me.bobert.netneutrality.utils;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
|
||||
public class MiscUtils {
|
||||
|
||||
public static boolean isValidUrl(String url) {
|
||||
try {
|
||||
URL object = new URL(url);
|
||||
object.toURI();
|
||||
|
||||
return true;
|
||||
} catch (MalformedURLException | URISyntaxException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package me.bobert.netneutrality.utils;
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.core.context.CommandContext;
|
||||
import me.bobert.netneutrality.exceptions.CommandExecutionException;
|
||||
import me.bobert.netneutrality.music.MusicManager;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
|
||||
|
||||
public class MusicUtils {
|
||||
|
||||
public static boolean checkIfConnected(MusicManager manager, CommandContext context) {
|
||||
if (context.getGuildUser().getVoiceState() == null) {
|
||||
throw new CommandExecutionException("Voice state is null, this should not happen", context, null);
|
||||
}
|
||||
|
||||
if (!context.getGuildUser().getVoiceState().inAudioChannel()) {
|
||||
context.reply("You are not in a voice channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!manager.isConnected()) {
|
||||
context.reply("The bot isn't connected to a voice channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
VoiceChannel vChannel = (VoiceChannel) context.getGuildUser().getVoiceState().getChannel();
|
||||
if (manager.isNotConnectedTo(vChannel)) {
|
||||
context.reply("The bot isn't in your voice channel");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isUserDj(Member member) {
|
||||
MusicManager manager = MusicManager.get(member.getGuild());
|
||||
|
||||
if (manager.isCurrentlyPlaying()) {
|
||||
return manager.getRequesterOfCurrentTrack().equals(member);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package me.bobert.netneutrality.utils;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import me.bobert.netneutrality.http.WebModule;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.json.JSONArray;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
public class Settings {
|
||||
|
||||
private static Settings INSTANCE;
|
||||
private final File propFile = new File("bot.properties");
|
||||
private final Properties prop = new Properties();
|
||||
public final String DATETIMEFORMAT = "yyyy-MM-dd HH:mm:ss";
|
||||
|
||||
private Settings() {
|
||||
checkAndLoadPropertiesFile();
|
||||
loadEnvironmentVariables();
|
||||
}
|
||||
|
||||
private void checkAndLoadPropertiesFile() {
|
||||
try (FileInputStream fis = new FileInputStream(propFile)) {
|
||||
prop.load(fis);
|
||||
} catch (IOException e) {
|
||||
LogManager.getLogger(Settings.class).info("Can't load settings from bot.properties, bot is most likely running in docker. Using environment variables instead.");
|
||||
}
|
||||
}
|
||||
|
||||
private void loadEnvironmentVariables() {
|
||||
Map<String, String> envs = System.getenv();
|
||||
|
||||
for (Map.Entry<String, String> env : envs.entrySet()) {
|
||||
prop.setProperty(env.getKey(), env.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
public static Settings getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new Settings();
|
||||
}
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
public String get(String key) {
|
||||
String property = prop.getProperty(key);
|
||||
|
||||
if (property == null) {
|
||||
LogManager.getLogger(Settings.class).error("Can't find property with key {}", key);
|
||||
}
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
public String get(String key, String def) {
|
||||
String property = get(key);
|
||||
|
||||
if (property == null || property.isEmpty())
|
||||
return def;
|
||||
|
||||
return property;
|
||||
}
|
||||
|
||||
public String getMavenVersion() {
|
||||
String version = null;
|
||||
|
||||
try {
|
||||
Properties p = new Properties();
|
||||
InputStream is = getClass().getResourceAsStream("/app.properties");
|
||||
if (is != null) {
|
||||
p.load(is);
|
||||
version = p.getProperty("app.version", "");
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
|
||||
if (version == null) {
|
||||
version = "0.0.0";
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
public String getGitVersion() {
|
||||
String gitlabHost = "https://git.bobert.me";
|
||||
String gitlabProjectId = "22";
|
||||
String latestReleaseUrl = String.format("%s/api/v4/projects/%s/releases/", gitlabHost, gitlabProjectId);
|
||||
|
||||
JSONArray json = new WebModule(latestReleaseUrl).request().getAsArray();
|
||||
if (json.isNull(0)) {
|
||||
return "0.0.0";
|
||||
}
|
||||
|
||||
return json.getJSONObject(0).getString("tag_name");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package me.bobert.netneutrality.utils;
|
||||
|
||||
/*
|
||||
* Copyright (c) anthorob
|
||||
*
|
||||
* https://discord.gg/qUa6zNFZsd
|
||||
*/
|
||||
|
||||
import net.dv8tion.jda.api.Permission;
|
||||
import net.dv8tion.jda.api.entities.Member;
|
||||
import net.dv8tion.jda.api.entities.Role;
|
||||
|
||||
public class UserFunction {
|
||||
|
||||
public static boolean isAdmin(Member user) {
|
||||
boolean isAdmin = false;
|
||||
for(Role role : user.getRoles()) {
|
||||
if (role.getPermissions().contains(Permission.MANAGE_SERVER) || role.getPermissions().contains(Permission.ADMINISTRATOR))
|
||||
isAdmin = true;
|
||||
}
|
||||
|
||||
return isAdmin;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user