Bunch of improvements to tasks and DB logic
Use locks when executing tasks Decouple GuildManager into services Add more logging when failing to load a persistable task Add ping method to WebModule Improve Task interface and implementations Start work on step tasks
This commit is contained in:
1 parent
72d8bcef20
commit
6429e42384
73 files changed
+1551
-1005
No files matched your search
@@ -45,7 +45,7 @@ public class NetNeutrality {
|
|||||||
options.setEnvironment(env);
|
options.setEnvironment(env);
|
||||||
options.setTracesSampleRate(1.0);
|
options.setTracesSampleRate(1.0);
|
||||||
options.setDebug(false);
|
options.setDebug(false);
|
||||||
options.setRelease(VERSION);
|
options.setRelease(Settings.getVersion());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ import me.bobert.netneutrality.core.command.options.CommandOption;
|
|||||||
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
|
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
import me.bobert.netneutrality.db.entities.OBlacklist;
|
import me.bobert.netneutrality.db.entities.OBlacklist;
|
||||||
import me.bobert.netneutrality.db.repository.BlacklistRepository;
|
import me.bobert.netneutrality.db.service.BlacklistService;
|
||||||
|
import me.bobert.netneutrality.db.service.GuildService;
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
||||||
|
|
||||||
@@ -54,14 +55,14 @@ public class BlacklistCommand extends AbstractAdminCommand {
|
|||||||
public CommandExecutionResult execute(CommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
String sub = context.getSubcommand();
|
String sub = context.getSubcommand();
|
||||||
String word = context.getOption("word", "", CommandOption::getAsString);
|
String word = context.getOption("word", "", CommandOption::getAsString);
|
||||||
BlacklistRepository repository = new BlacklistRepository();
|
BlacklistService service = GuildService.get(context.getGuild()).getBlacklistService();
|
||||||
|
|
||||||
switch (sub) {
|
switch (sub) {
|
||||||
case "get" -> context.async("Retrieving list, please wait...", asyncMessage -> {
|
case "get" -> context.async("Retrieving list, please wait...", asyncMessage -> {
|
||||||
List<OBlacklist> blacklists = repository.getBlacklistForGuild(context.getGuild());
|
List<OBlacklist> blacklists = service.getBlacklistForGuild();
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("List of blacklisted words for this server [").append(context.getGuild().getName()).append("]: \n");
|
sb.append("List of blacklisted words for this server: \n");
|
||||||
for (OBlacklist bl : blacklists) {
|
for (OBlacklist bl : blacklists) {
|
||||||
sb.append(bl.getWord()).append(" -> ").append(bl.getEnabled() ? "enabled" : "disabled").append("\n");
|
sb.append(bl.getWord()).append(" -> ").append(bl.getEnabled() ? "enabled" : "disabled").append("\n");
|
||||||
}
|
}
|
||||||
@@ -73,10 +74,10 @@ public class BlacklistCommand extends AbstractAdminCommand {
|
|||||||
asyncMessage.edit(sb.toString());
|
asyncMessage.edit(sb.toString());
|
||||||
});
|
});
|
||||||
case "add" -> {
|
case "add" -> {
|
||||||
return repository.add(context.getGuild(), context.getGuildUser(), word);
|
return service.add(context.getGuildUser(), word);
|
||||||
}
|
}
|
||||||
case "remove" -> {
|
case "remove" -> {
|
||||||
return repository.delete(context.getGuild(), word);
|
return service.delete(word);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ import net.dv8tion.jda.api.interactions.commands.build.OptionData;
|
|||||||
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
|
||||||
import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData;
|
import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class ReactionCommand extends AbstractAdminCommand {
|
public class ReactionCommand extends AbstractAdminCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -43,6 +46,7 @@ public class ReactionCommand extends AbstractAdminCommand {
|
|||||||
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
public void registerCommandOptions(CommandOptionsBuilder builder) {
|
||||||
builder.addSubcommandGroups(
|
builder.addSubcommandGroups(
|
||||||
new SubcommandGroupData("emoji", "Give roles to users using emojis").addSubcommands(
|
new SubcommandGroupData("emoji", "Give roles to users using emojis").addSubcommands(
|
||||||
|
new SubcommandData("list", "Lists all registered tasks for this guild"),
|
||||||
new SubcommandData("setup", "Add a role to a message").addOptions(
|
new SubcommandData("setup", "Add a role to a message").addOptions(
|
||||||
new OptionData(OptionType.STRING, "messageid", "The ID of the message", true)
|
new OptionData(OptionType.STRING, "messageid", "The ID of the message", true)
|
||||||
.setRequiredLength(19, 19),
|
.setRequiredLength(19, 19),
|
||||||
@@ -77,65 +81,78 @@ public class ReactionCommand extends AbstractAdminCommand {
|
|||||||
String group = context.getSubcommandGroup();
|
String group = context.getSubcommandGroup();
|
||||||
String sub = context.getSubcommand();
|
String sub = context.getSubcommand();
|
||||||
|
|
||||||
switch (group) {
|
return switch (group) {
|
||||||
case "emoji" -> {
|
case "emoji" -> executeEmoji(context, guildId, messageId, sub);
|
||||||
if ("setup".equals(sub)) {
|
default -> CommandExecutionResult.none();
|
||||||
String emoji = context.getOption("emoji", CommandOption::getAsString);
|
};
|
||||||
String role = context.getOption("role", CommandOption::getAsString);
|
}
|
||||||
|
|
||||||
EmojiReactionRoleTask task = new EmojiReactionRoleTask(guildId, messageId);
|
private CommandExecutionResult executeEmoji(CommandContext context, String guildId, String messageId, String sub) {
|
||||||
if (TaskManager.getInstance().exists(task.getId())) {
|
String id = String.format("%s:%s", guildId, messageId);
|
||||||
return CommandExecutionResult.error("The message ID is already registered, use the add subcommand instead.");
|
EmojiReactionRoleTask task = (EmojiReactionRoleTask) TaskManager.getInstance().getTask(id);
|
||||||
}
|
|
||||||
task.addEmoji(emoji, role);
|
|
||||||
|
|
||||||
TaskManager.getInstance().insert(context, task);
|
switch (sub) {
|
||||||
return CommandExecutionResult.success("Added emoji to message successfully!");
|
case "list" -> {
|
||||||
|
List<EmojiReactionRoleTask> tasks = TaskManager.getInstance().findTasksByPartialId(EmojiReactionRoleTask.class, guildId);
|
||||||
|
if (tasks.isEmpty()) {
|
||||||
|
return CommandExecutionResult.info("No tasks found for this guild");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("add".equals(sub)) {
|
StringBuilder builder = new StringBuilder();
|
||||||
String emoji = context.getOption("emoji", CommandOption::getAsString);
|
builder.append("I have found the following tasks for this guild:");
|
||||||
String role = context.getOption("role", CommandOption::getAsString);
|
|
||||||
|
|
||||||
String id = String.format("%s:%s", guildId, messageId);
|
for (EmojiReactionRoleTask item : tasks) {
|
||||||
EmojiReactionRoleTask task = (EmojiReactionRoleTask) TaskManager.getInstance().getTask(id);
|
builder.append("\n- Task ID: ").append(item.getId()).append("\n")
|
||||||
if (task == null) {
|
.append(" Message ID: ").append(item.getMessageId()).append("\n")
|
||||||
return CommandExecutionResult.error("The message ID provided isn't registered with the bot");
|
.append(" Emoji Mapping: ").append(item.getEmojiMapping()).append("\n");
|
||||||
}
|
|
||||||
|
|
||||||
task.addEmoji(emoji, role);
|
|
||||||
TaskManager.getInstance().getTaskService().persistTask(task);
|
|
||||||
return CommandExecutionResult.success("Added emoji to message successfully!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("remove".equals(sub)) {
|
return CommandExecutionResult.success(builder.toString());
|
||||||
String emoji = context.getOption("emoji", CommandOption::getAsString);
|
}
|
||||||
|
case "setup" -> {
|
||||||
String id = String.format("%s:%s", guildId, messageId);
|
if (task != null) {
|
||||||
EmojiReactionRoleTask task = (EmojiReactionRoleTask) TaskManager.getInstance().getTask(id);
|
return CommandExecutionResult.error("The message ID is already registered, use the add subcommand instead.");
|
||||||
if (task == null) {
|
|
||||||
return CommandExecutionResult.error("The message ID provided isn't registered with the bot");
|
|
||||||
}
|
|
||||||
|
|
||||||
task.removeEmoji(emoji);
|
|
||||||
TaskManager.getInstance().getTaskService().persistTask(task);
|
|
||||||
return CommandExecutionResult.success("Removed emoji from message successfully!");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("delete".equals(sub)) {
|
String emoji = context.getOption("emoji", CommandOption::getAsString);
|
||||||
String id = String.format("%s:%s", guildId, messageId);
|
String role = context.getOption("role", CommandOption::getAsString);
|
||||||
EmojiReactionRoleTask task = (EmojiReactionRoleTask) TaskManager.getInstance().getTask(id);
|
task = new EmojiReactionRoleTask(guildId, messageId, Map.of(emoji, role));
|
||||||
if (task == null) {
|
task.insert(context);
|
||||||
return CommandExecutionResult.error("The message ID provided isn't registered with the bot");
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskManager.getInstance().getTaskService().unpersistTask(task);
|
return CommandExecutionResult.success("Added emoji to message successfully!");
|
||||||
return CommandExecutionResult.success("Unregistered the message ID successfully!");
|
}
|
||||||
|
case "add" -> {
|
||||||
|
if (task == null) {
|
||||||
|
return CommandExecutionResult.error("The message ID provided isn't registered with the bot");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String emoji = context.getOption("emoji", CommandOption::getAsString);
|
||||||
|
String role = context.getOption("role", CommandOption::getAsString);
|
||||||
|
task.addEmoji(emoji, role);
|
||||||
|
task.save();
|
||||||
|
return CommandExecutionResult.success("Added emoji to message successfully!");
|
||||||
|
}
|
||||||
|
case "remove" -> {
|
||||||
|
String emoji = context.getOption("emoji", CommandOption::getAsString);
|
||||||
|
|
||||||
|
if (task == null) {
|
||||||
|
return CommandExecutionResult.error("The message ID provided isn't registered with the bot");
|
||||||
|
}
|
||||||
|
|
||||||
|
task.removeEmoji(emoji);
|
||||||
|
task.save();
|
||||||
|
return CommandExecutionResult.success("Removed emoji from message successfully!");
|
||||||
|
}
|
||||||
|
case "delete" -> {
|
||||||
|
if (task == null) {
|
||||||
|
return CommandExecutionResult.error("The message ID provided isn't registered with the bot");
|
||||||
|
}
|
||||||
|
|
||||||
|
task.delete();
|
||||||
|
return CommandExecutionResult.success("Unregistered the message ID successfully!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
return CommandExecutionResult.none();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -61,9 +61,9 @@ public class HelpCommand extends AbstractCommand {
|
|||||||
public CommandExecutionResult execute(CommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
String command = context.getOption("command", null, CommandOption::getAsString);
|
String command = context.getOption("command", null, CommandOption::getAsString);
|
||||||
if (command == null) {
|
if (command == null) {
|
||||||
Button commands = BuiltInTask.createButton(CommandHelp.class).primary("Commands");
|
Button commands = BuiltInTask.createButton(CommandHelp.class);
|
||||||
Button info = BuiltInTask.createButton(BotInfo.class).primary("Info");
|
Button info = BuiltInTask.createButton(BotInfo.class);
|
||||||
Button reportbug = BuiltInTask.createButton(ReportBug.class).danger("Report bug");
|
Button reportbug = BuiltInTask.createButton(ReportBug.class);
|
||||||
ActionRow row = ActionRow.of(commands, info, reportbug);
|
ActionRow row = ActionRow.of(commands, info, reportbug);
|
||||||
context.replyEphemeral("Thanks for using the Net Neutrality Bot! What do you need help with?", List.of(row));
|
context.replyEphemeral("Thanks for using the Net Neutrality Bot! What do you need help with?", List.of(row));
|
||||||
return CommandExecutionResult.none();
|
return CommandExecutionResult.none();
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ package me.bobert.netneutrality.command.bot;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.GuildManager;
|
|
||||||
import me.bobert.netneutrality.core.command.AbstractAdminCommand;
|
import me.bobert.netneutrality.core.command.AbstractAdminCommand;
|
||||||
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.core.command.options.CommandOption;
|
import me.bobert.netneutrality.core.command.options.CommandOption;
|
||||||
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
|
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
import me.bobert.netneutrality.db.GSettings;
|
import me.bobert.netneutrality.db.GSettings;
|
||||||
|
import me.bobert.netneutrality.db.service.GuildService;
|
||||||
import me.bobert.netneutrality.music.MusicManager;
|
import me.bobert.netneutrality.music.MusicManager;
|
||||||
import net.dv8tion.jda.api.entities.Role;
|
import net.dv8tion.jda.api.entities.Role;
|
||||||
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
|
||||||
@@ -63,7 +63,7 @@ public class ConfigureCommand extends AbstractAdminCommand {
|
|||||||
@Override
|
@Override
|
||||||
public CommandExecutionResult execute(CommandContext context) {
|
public CommandExecutionResult execute(CommandContext context) {
|
||||||
String sub = context.getSubcommand();
|
String sub = context.getSubcommand();
|
||||||
GuildManager guildManager = GuildManager.getManager(context.getGuild());
|
GuildService guildService = GuildService.get(context.getGuild().getId());
|
||||||
context.ephemeral();
|
context.ephemeral();
|
||||||
|
|
||||||
if ("music-role".equals(sub)) {
|
if ("music-role".equals(sub)) {
|
||||||
@@ -71,13 +71,13 @@ public class ConfigureCommand extends AbstractAdminCommand {
|
|||||||
assert role != null;
|
assert role != null;
|
||||||
|
|
||||||
if (role.getPosition() == -1) {
|
if (role.getPosition() == -1) {
|
||||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_ROLE, null);
|
guildService.getGuildSettingService().setSettingValue(GSettings.MUSIC_ROLE, null);
|
||||||
|
|
||||||
return CommandExecutionResult.success("Everyone can now use music commands");
|
return CommandExecutionResult.success("Everyone can now use music commands");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role.getGuild().getId().equals(context.getGuild().getId())) {
|
if (role.getGuild().getId().equals(context.getGuild().getId())) {
|
||||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_ROLE, role.getId());
|
guildService.getGuildSettingService().setSettingValue(GSettings.MUSIC_ROLE, role.getId());
|
||||||
|
|
||||||
return CommandExecutionResult.success(String.format("Successfully set %s as the role needed for music command", role.getAsMention()));
|
return CommandExecutionResult.success(String.format("Successfully set %s as the role needed for music command", role.getAsMention()));
|
||||||
}
|
}
|
||||||
@@ -87,14 +87,14 @@ public class ConfigureCommand extends AbstractAdminCommand {
|
|||||||
GuildChannelUnion channel = context.getOption("channel", CommandOption::getAsChannel);
|
GuildChannelUnion channel = context.getOption("channel", CommandOption::getAsChannel);
|
||||||
|
|
||||||
if (channel == null) {
|
if (channel == null) {
|
||||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_CHANNEL, null);
|
guildService.getGuildSettingService().setSettingValue(GSettings.MUSIC_CHANNEL, null);
|
||||||
|
|
||||||
return CommandExecutionResult.success("No channel restrictions for music commands");
|
return CommandExecutionResult.success("No channel restrictions for music commands");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channel instanceof TextChannel) {
|
if (channel instanceof TextChannel) {
|
||||||
if (channel.getGuild().getId().equals(context.getGuild().getId())) {
|
if (channel.getGuild().getId().equals(context.getGuild().getId())) {
|
||||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_CHANNEL, channel.getId());
|
guildService.getGuildSettingService().setSettingValue(GSettings.MUSIC_CHANNEL, channel.getId());
|
||||||
|
|
||||||
return CommandExecutionResult.success(String.format("Successfully set %s as the channel required to execute music command", channel.getAsMention()));
|
return CommandExecutionResult.success(String.format("Successfully set %s as the channel required to execute music command", channel.getAsMention()));
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@ public class ConfigureCommand extends AbstractAdminCommand {
|
|||||||
|
|
||||||
if ("music-volume".equals(sub)) {
|
if ("music-volume".equals(sub)) {
|
||||||
int volume = context.getOption("volume", 30, CommandOption::getAsInt);
|
int volume = context.getOption("volume", 30, CommandOption::getAsInt);
|
||||||
guildManager.getGuildConfigManager().setSettingValue(GSettings.MUSIC_VOLUME, String.valueOf(volume));
|
guildService.getGuildSettingService().setSettingValue(GSettings.MUSIC_VOLUME, String.valueOf(volume));
|
||||||
MusicManager.get(context.getGuild()).setVolume(volume);
|
MusicManager.get(context.getGuild()).setVolume(volume);
|
||||||
|
|
||||||
return CommandExecutionResult.success(String.format("Default volume for music player is now %d", volume));
|
return CommandExecutionResult.success(String.format("Default volume for music player is now %d", volume));
|
||||||
@@ -112,13 +112,13 @@ public class ConfigureCommand extends AbstractAdminCommand {
|
|||||||
|
|
||||||
if ("error-logging".equals(sub)) {
|
if ("error-logging".equals(sub)) {
|
||||||
boolean debug = context.getOption("debug", true, CommandOption::getAsBoolean);
|
boolean debug = context.getOption("debug", true, CommandOption::getAsBoolean);
|
||||||
guildManager.setDebug(debug);
|
guildService.updateDebugMode(debug);
|
||||||
|
|
||||||
return CommandExecutionResult.success("Error logging is now %s for this guild", (debug ? "enabled" : "disabled"));
|
return CommandExecutionResult.success("Error logging is now %s for this guild", (debug ? "enabled" : "disabled"));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("show-all".equals(sub)) {
|
if ("show-all".equals(sub)) {
|
||||||
Map<String, String> settings = guildManager.getGuildConfigManager().getSettings();
|
Map<String, String> settings = guildService.getGuildSettingService().getSettings();
|
||||||
MessageCreateBuilder builder = new MessageCreateBuilder();
|
MessageCreateBuilder builder = new MessageCreateBuilder();
|
||||||
|
|
||||||
for (GSettings o : GSettings.values()) {
|
for (GSettings o : GSettings.values()) {
|
||||||
|
|||||||
@@ -11,10 +11,12 @@ import me.bobert.netneutrality.core.command.CommandCategory;
|
|||||||
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
|
import me.bobert.netneutrality.core.command.options.CommandOptionsBuilder;
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
import me.bobert.netneutrality.core.tasks.TaskManager;
|
import me.bobert.netneutrality.http.WebModule;
|
||||||
import me.bobert.netneutrality.tasks.git.ReportIssueTask;
|
import me.bobert.netneutrality.tasks.git.ReportIssueTask;
|
||||||
import me.bobert.netneutrality.utils.Settings;
|
import me.bobert.netneutrality.utils.Settings;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class ReportIssueCommand extends AbstractCommand {
|
public class ReportIssueCommand extends AbstractCommand {
|
||||||
@Override
|
@Override
|
||||||
public boolean needAdmin() {
|
public boolean needAdmin() {
|
||||||
@@ -66,8 +68,10 @@ public class ReportIssueCommand extends AbstractCommand {
|
|||||||
return CommandExecutionResult.error("Command is not configured, failed to create issue.");
|
return CommandExecutionResult.error("Command is not configured, failed to create issue.");
|
||||||
}
|
}
|
||||||
|
|
||||||
TaskManager.getInstance().insert(context, new ReportIssueTask(context instanceof SlashCommandContext));
|
if (new WebModule.Builder(gitlabHost).ping().timeout(2, TimeUnit.SECONDS).build().request().isSuccess()) {
|
||||||
|
return CommandExecutionResult.task(new ReportIssueTask(context));
|
||||||
|
}
|
||||||
|
|
||||||
return CommandExecutionResult.none();
|
return CommandExecutionResult.error("Unable to create bug reports at this time, please try again later.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -34,6 +34,14 @@ public class AsyncContext extends AbstractContext {
|
|||||||
this.message = message;
|
this.message = message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getMessageId() {
|
||||||
|
if (hook != null) {
|
||||||
|
return hook.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
return message.getId();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Event getEvent() {
|
public Event getEvent() {
|
||||||
return null;
|
return null;
|
||||||
@@ -60,6 +68,10 @@ public class AsyncContext extends AbstractContext {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isAcknowledged() {
|
public boolean isAcknowledged() {
|
||||||
|
if (hook != null) {
|
||||||
|
return hook.getInteraction().isAcknowledged();
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,19 +6,17 @@ package me.bobert.netneutrality.context;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.context.AbstractContext;
|
import me.bobert.netneutrality.core.context.AbstractContext;
|
||||||
|
import me.bobert.netneutrality.core.context.NoMessageContext;
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
|
||||||
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
|
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
|
||||||
import net.dv8tion.jda.api.events.Event;
|
import net.dv8tion.jda.api.events.Event;
|
||||||
import net.dv8tion.jda.api.interactions.InteractionHook;
|
|
||||||
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
|
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
|
||||||
import net.dv8tion.jda.api.interactions.modals.Modal;
|
|
||||||
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class MusicChannelContext extends AbstractContext {
|
public class MusicChannelContext extends AbstractContext implements NoMessageContext {
|
||||||
|
|
||||||
public MusicChannelContext(GuildMessageChannel channel, Member member) {
|
public MusicChannelContext(GuildMessageChannel channel, Member member) {
|
||||||
super(channel.getJDA(), channel.getGuild(), channel, member, member.getUser());
|
super(channel.getJDA(), channel.getGuild(), channel, member, member.getUser());
|
||||||
@@ -34,21 +32,6 @@ public class MusicChannelContext extends AbstractContext {
|
|||||||
return String.format("music:%s", getChannel().getId());
|
return String.format("music:%s", getChannel().getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChannelType getType() {
|
|
||||||
return getChannel().getType();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InteractionHook getInteractionHook() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAcknowledged() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFromGuild() {
|
public boolean isFromGuild() {
|
||||||
return true;
|
return true;
|
||||||
@@ -59,25 +42,10 @@ public class MusicChannelContext extends AbstractContext {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reply(Modal modal) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
|
public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
|
||||||
if (getChannel().canTalk()) {
|
if (getChannel().canTalk()) {
|
||||||
getChannel().sendMessage(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message)));
|
getChannel().sendMessage(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void defer(boolean ephemeral, Consumer<AsyncContext> consumer) {
|
|
||||||
reply("Bot is thinking", consumer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void ack(boolean ephemeral) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -5,31 +5,25 @@ package me.bobert.netneutrality.context.command;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.context.AsyncContext;
|
|
||||||
import me.bobert.netneutrality.core.command.options.CommandOption;
|
import me.bobert.netneutrality.core.command.options.CommandOption;
|
||||||
import me.bobert.netneutrality.core.command.options.MessageCommandOption;
|
import me.bobert.netneutrality.core.command.options.MessageCommandOption;
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.MessageContext;
|
||||||
import me.bobert.netneutrality.utils.Settings;
|
import me.bobert.netneutrality.utils.Settings;
|
||||||
import net.dv8tion.jda.api.entities.*;
|
import net.dv8tion.jda.api.entities.*;
|
||||||
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||||
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
|
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
|
||||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
import net.dv8tion.jda.api.interactions.InteractionHook;
|
|
||||||
import net.dv8tion.jda.api.interactions.commands.Command;
|
import net.dv8tion.jda.api.interactions.commands.Command;
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
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.OptionData;
|
||||||
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
|
|
||||||
import net.dv8tion.jda.api.interactions.modals.Modal;
|
|
||||||
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.StringJoiner;
|
import java.util.StringJoiner;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
public class MessageCommandContext extends CommandContext {
|
public class MessageCommandContext extends CommandContext implements MessageContext {
|
||||||
private final MessageReceivedEvent event;
|
private final MessageReceivedEvent event;
|
||||||
private final Message message;
|
private final Message message;
|
||||||
|
|
||||||
@@ -54,63 +48,10 @@ public class MessageCommandContext extends CommandContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChannelType getType() {
|
|
||||||
return event.getChannelType();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Message getMessage() {
|
public Message getMessage() {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAcknowledged() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFromGuild() {
|
|
||||||
return event.isFromGuild();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InteractionHook getInteractionHook() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void defer(boolean ephemeral, Consumer<AsyncContext> consumer) {
|
|
||||||
reply("Bot is thinking...", consumer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
|
|
||||||
if (ephemeral) {
|
|
||||||
sendMessageInPrivate(messageCreateData, components);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getChannel() != null && getChannel().canTalk()) {
|
|
||||||
message.reply(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reply(Modal modal) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void ack(boolean ephemeral) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deleteMessage() {
|
|
||||||
if (message != null && getType() != ChannelType.PRIVATE) {
|
|
||||||
message.delete().queueAfter(1, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<CommandOption> parseOptions(String[] args, List<OptionData> commandOptions) {
|
public List<CommandOption> parseOptions(String[] args, List<OptionData> commandOptions) {
|
||||||
List<CommandOption> options = new ArrayList<>();
|
List<CommandOption> options = new ArrayList<>();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package me.bobert.netneutrality.context.tasks;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.context.AsyncContext;
|
import me.bobert.netneutrality.context.AsyncContext;
|
||||||
|
import me.bobert.netneutrality.core.context.NoMessageContext;
|
||||||
import me.bobert.netneutrality.core.context.TaskContext;
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||||
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
|
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
|
||||||
@@ -17,7 +18,8 @@ import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class EmojiTaskContext extends TaskContext {
|
public class EmojiTaskContext extends TaskContext implements NoMessageContext {
|
||||||
|
public static final String ID = "et:%s:%s";
|
||||||
private final MessageReactionAddEvent event;
|
private final MessageReactionAddEvent event;
|
||||||
|
|
||||||
public EmojiTaskContext(MessageReactionAddEvent event) {
|
public EmojiTaskContext(MessageReactionAddEvent event) {
|
||||||
@@ -32,29 +34,9 @@ public class EmojiTaskContext extends TaskContext {
|
|||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public ChannelType getType() {
|
|
||||||
return event.getChannelType();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
return String.format("%s:%s", getGuild().getId(), event.getMessageId());
|
return String.format(ID, getGuild().getId(), event.getMessageId());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public InteractionHook getInteractionHook() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void defer(boolean ephemeral, Consumer<AsyncContext> consumer) {
|
|
||||||
reply("Bot is thinking...", consumer);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isAcknowledged() {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -62,29 +44,10 @@ public class EmojiTaskContext extends TaskContext {
|
|||||||
return event.isFromGuild();
|
return event.isFromGuild();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reply(Modal modal) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
|
public void reply(MessageCreateData messageCreateData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
|
||||||
if (getChannel() != null && getChannel().canTalk()) {
|
if (getChannel() != null && getChannel().canTalk()) {
|
||||||
getChannel().sendMessage(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message)));
|
getChannel().sendMessage(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void replyEphemeral(MessageCreateData messageCreateData) {
|
|
||||||
if (getType() == ChannelType.PRIVATE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sendMessageInPrivate(messageCreateData);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void ack(boolean ephemeral) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
package me.bobert.netneutrality.context.tasks;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.MessageContext;
|
||||||
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.MessageReference;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||||
|
import net.dv8tion.jda.api.events.Event;
|
||||||
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
|
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||||
|
|
||||||
|
public class MessageTaskContext extends TaskContext implements MessageContext {
|
||||||
|
private final MessageReceivedEvent event;
|
||||||
|
private final Message message;
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
public MessageTaskContext(MessageReceivedEvent event) {
|
||||||
|
super(event.getJDA(), event.isFromGuild() ? event.getGuild() : null, event.getChannel(), event.getMember(), event.getAuthor());
|
||||||
|
this.event = event;
|
||||||
|
this.message = event.getMessage();
|
||||||
|
|
||||||
|
MessageReference reference = message.getMessageReference();
|
||||||
|
this.id = reference == null ? event.getMessageId() : reference.getMessageId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Event getEvent() {
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void replyEphemeral(MessageCreateData messageCreateData) {
|
||||||
|
if (getType() == ChannelType.PRIVATE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessageInPrivate(messageCreateData);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Message getMessage() {
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,9 +5,9 @@ package me.bobert.netneutrality.core;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.db.service.GuildService;
|
||||||
import me.bobert.netneutrality.music.MusicManager;
|
import me.bobert.netneutrality.music.MusicManager;
|
||||||
import me.bobert.netneutrality.music.TrackScheduler;
|
import me.bobert.netneutrality.music.TrackScheduler;
|
||||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
@@ -21,18 +21,21 @@ public class BotState {
|
|||||||
|
|
||||||
//TODO: is it needed?
|
//TODO: is it needed?
|
||||||
private static void saveGuildsState() {
|
private static void saveGuildsState() {
|
||||||
Map<String, GuildManager> managers = GuildManager.getManagers();
|
Map<String, GuildService> services = GuildService.getServices();
|
||||||
|
|
||||||
JSONObject main = new JSONObject();
|
JSONObject main = new JSONObject();
|
||||||
JSONArray guildsArray = new JSONArray();
|
JSONArray guildsArray = new JSONArray();
|
||||||
|
|
||||||
for (GuildManager manager : managers.values()) {
|
for (MusicManager manager : MusicManager.getManagers()) {
|
||||||
MusicManager musicManager = MusicManager.get(manager.getGuild());
|
TrackScheduler trackScheduler = manager.getScheduler();
|
||||||
TrackScheduler trackScheduler = musicManager.getScheduler();
|
}
|
||||||
|
|
||||||
|
for (GuildService service : services.values()) {
|
||||||
|
|
||||||
|
|
||||||
JSONObject guild = new JSONObject();
|
JSONObject guild = new JSONObject();
|
||||||
JSONObject music = new JSONObject();
|
JSONObject music = new JSONObject();
|
||||||
guild.put("id", manager.getGuild().getId());
|
guild.put("id", service.getEntity().getGuildId());
|
||||||
|
|
||||||
// MessageChannel currentChannel = trackScheduler.getTextChannel();
|
// MessageChannel currentChannel = trackScheduler.getTextChannel();
|
||||||
// if (currentChannel == null) {
|
// if (currentChannel == null) {
|
||||||
|
|||||||
@@ -1,157 +0,0 @@
|
|||||||
package me.bobert.netneutrality.core;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.NetNeutrality;
|
|
||||||
import me.bobert.netneutrality.db.entities.OBlacklist;
|
|
||||||
import me.bobert.netneutrality.db.entities.OGuild;
|
|
||||||
import me.bobert.netneutrality.db.repository.GuildRepository;
|
|
||||||
import me.bobert.netneutrality.utils.JpaUtils;
|
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
|
||||||
import net.dv8tion.jda.api.entities.Role;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
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 final GuildRepository guildRepository = new GuildRepository();
|
|
||||||
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;
|
|
||||||
loadGuildFromDb(guild.getId());
|
|
||||||
this.guildConfigManager = new GuildConfigManager(guild, this.guildEntity);
|
|
||||||
|
|
||||||
reloadBlacklistCache();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GuildManager getManager(Guild guild) {
|
|
||||||
if (managers.containsKey(guild.getId())) {
|
|
||||||
return managers.get(guild.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
GuildManager manager = new GuildManager(guild);
|
|
||||||
managers.put(guild.getId(), manager);
|
|
||||||
|
|
||||||
return manager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GuildManager getManager(String guildId) {
|
|
||||||
if (managers.containsKey(guildId)) {
|
|
||||||
return managers.get(guildId);
|
|
||||||
}
|
|
||||||
|
|
||||||
Guild temp = NetNeutrality.getInstance().getJDA().getGuildById(guildId);
|
|
||||||
if (temp == null) {
|
|
||||||
throw new IllegalArgumentException("Guild does not exist in current cache");
|
|
||||||
}
|
|
||||||
|
|
||||||
return getManager(temp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 addRolesToMember(Member member, Role role) {
|
|
||||||
Member self = guild.getSelfMember();
|
|
||||||
if (!self.canInteract(role)) {
|
|
||||||
logger.error("Missing permissions to interact with role {} in guild {}", role.getId(), guild.getId());
|
|
||||||
}
|
|
||||||
guild.addRoleToMember(member, role).queue();
|
|
||||||
}
|
|
||||||
|
|
||||||
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, guildEntity.getGuildId(), session -> {
|
|
||||||
Set<OBlacklist> oBlacklistSet = guildEntity.getBlacklists();
|
|
||||||
|
|
||||||
for (OBlacklist bl : oBlacklistSet) {
|
|
||||||
if (bl.getEnabled()) {
|
|
||||||
blacklistCache.add(bl.getWord());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadGuildFromDb(String guildId) {
|
|
||||||
JpaUtils.runWithTransaction(session -> {
|
|
||||||
OGuild entity = guildRepository.findById(guildId);
|
|
||||||
|
|
||||||
if (entity == null) {
|
|
||||||
logger.debug("Guild[{}] doesn't exist in DB, creating entity..", guildId);
|
|
||||||
|
|
||||||
entity = new OGuild();
|
|
||||||
entity.setGuildId(guildId);
|
|
||||||
session.persist(entity);
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
guildEntity = entity;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reloadEntity() {
|
|
||||||
loadGuildFromDb(guild.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeFromDB() {
|
|
||||||
guildRepository.deleteBy(guild.getId());
|
|
||||||
|
|
||||||
managers.remove(guild.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -9,6 +9,7 @@ import io.sentry.Sentry;
|
|||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
import me.bobert.netneutrality.core.context.Context;
|
import me.bobert.netneutrality.core.context.Context;
|
||||||
import me.bobert.netneutrality.core.context.TaskContext;
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
import me.bobert.netneutrality.db.service.GuildService;
|
||||||
import me.bobert.netneutrality.exceptions.ContextException;
|
import me.bobert.netneutrality.exceptions.ContextException;
|
||||||
import me.bobert.netneutrality.utils.MessageFunction;
|
import me.bobert.netneutrality.utils.MessageFunction;
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
@@ -49,13 +50,12 @@ public class LogHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void sendMessageToDebugChannel(Guild guild, String message) {
|
public static void sendMessageToDebugChannel(Guild guild, String message) {
|
||||||
if (GuildManager.getManager(guild).isDebugEnabled()) {
|
guild.getTextChannels()
|
||||||
guild.getTextChannels()
|
.stream()
|
||||||
.stream()
|
.filter(textChannel -> textChannel.getName().equals("debug") || textChannel.getName().equals("log"))
|
||||||
.filter(textChannel -> textChannel.getName().equals("debug") || textChannel.getName().equals("log"))
|
.filter(TextChannel::canTalk)
|
||||||
.filter(TextChannel::canTalk)
|
.forEach(textChannel -> textChannel.sendMessage(message).queue());
|
||||||
.forEach(textChannel -> textChannel.sendMessage(message).queue());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void logInternally(ContextException exception) {
|
private static void logInternally(ContextException exception) {
|
||||||
@@ -80,7 +80,7 @@ public class LogHandler {
|
|||||||
Context context = exception.getContext();
|
Context context = exception.getContext();
|
||||||
|
|
||||||
if (context.getGuild() != null) {
|
if (context.getGuild() != null) {
|
||||||
if (context.getGuildManager().isDebugEnabled()) {
|
if (GuildService.get(context.getGuild()).isDebugEnabled()) {
|
||||||
context.getGuild()
|
context.getGuild()
|
||||||
.getTextChannels()
|
.getTextChannels()
|
||||||
.stream()
|
.stream()
|
||||||
|
|||||||
@@ -17,9 +17,14 @@ public abstract class AbstractMessageMenuCommand extends AbstractCommand {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGuildOnly() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSlashCommandOnly() {
|
public boolean isSlashCommandOnly() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -27,6 +32,11 @@ public abstract class AbstractMessageMenuCommand extends AbstractCommand {
|
|||||||
// no options for context commands
|
// no options for context commands
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CommandCategory getCategory() {
|
||||||
|
return CommandCategory.UNLISTED;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Type getType() {
|
public Type getType() {
|
||||||
return Type.MESSAGE;
|
return Type.MESSAGE;
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ package me.bobert.netneutrality.core.command;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.GuildManager;
|
|
||||||
import me.bobert.netneutrality.db.GSettings;
|
import me.bobert.netneutrality.db.GSettings;
|
||||||
|
import me.bobert.netneutrality.db.service.GuildService;
|
||||||
import me.bobert.netneutrality.utils.UserFunction;
|
import me.bobert.netneutrality.utils.UserFunction;
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
@@ -39,7 +39,8 @@ public abstract class AbstractMusicCommand extends AbstractCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Guild guild = member.getGuild();
|
Guild guild = member.getGuild();
|
||||||
String roleDb = GuildManager.getManager(guild).getGuildConfigManager().getSetting(GSettings.MUSIC_ROLE);
|
|
||||||
|
String roleDb = GuildService.get(guild.getId()).getGuildSettingService().getSetting(GSettings.MUSIC_ROLE);
|
||||||
|
|
||||||
if (roleDb != null && !roleDb.isEmpty()) {
|
if (roleDb != null && !roleDb.isEmpty()) {
|
||||||
if (UserFunction.isAdmin(member)) {
|
if (UserFunction.isAdmin(member)) {
|
||||||
@@ -49,7 +50,7 @@ public abstract class AbstractMusicCommand extends AbstractCommand {
|
|||||||
Role neededRole = guild.getRoleById(roleDb);
|
Role neededRole = guild.getRoleById(roleDb);
|
||||||
if (neededRole == null) {
|
if (neededRole == null) {
|
||||||
// Role doesn't exist on guild anymore, remove from db
|
// Role doesn't exist on guild anymore, remove from db
|
||||||
GuildManager.getManager(guild).getGuildConfigManager().setSettingValue(GSettings.MUSIC_ROLE, null);
|
GuildService.get(guild.getId()).getGuildSettingService().setSettingValue(GSettings.MUSIC_ROLE, null);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +66,7 @@ public abstract class AbstractMusicCommand extends AbstractCommand {
|
|||||||
if (channel.getType() != ChannelType.TEXT)
|
if (channel.getType() != ChannelType.TEXT)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
String channelDb = GuildManager.getManager(guildMessageChannel.getGuild()).getGuildConfigManager().getSetting(GSettings.MUSIC_CHANNEL);
|
String channelDb = GuildService.get(guildMessageChannel.getGuild()).getGuildSettingService().getSetting(GSettings.MUSIC_CHANNEL);
|
||||||
if (channelDb != null && !channelDb.isEmpty()) {
|
if (channelDb != null && !channelDb.isEmpty()) {
|
||||||
return channelDb.equals(channel.getId());
|
return channelDb.equals(channel.getId());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,26 +5,38 @@ package me.bobert.netneutrality.core.command;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.tasks.Task;
|
||||||
|
|
||||||
public class CommandExecutionResult {
|
public class CommandExecutionResult {
|
||||||
|
|
||||||
public enum Result {
|
public enum Result {
|
||||||
SUCCESS, INFO, WARN, ERROR, NOACTION
|
SUCCESS, INFO, WARN, ERROR, NOACTION, TASK
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Result result;
|
private final Result result;
|
||||||
private final String message;
|
private final String message;
|
||||||
|
private final Object data;
|
||||||
private final Exception exception;
|
private final Exception exception;
|
||||||
|
|
||||||
private CommandExecutionResult(Result result, String message, Exception exception) {
|
private CommandExecutionResult(Result result, String message, Object data, Exception exception) {
|
||||||
this.result = result;
|
this.result = result;
|
||||||
this.message = message;
|
this.message = message;
|
||||||
|
this.data = data;
|
||||||
this.exception = exception;
|
this.exception = exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CommandExecutionResult(Result result, String message, Exception exception) {
|
||||||
|
this(result, message, null, exception);
|
||||||
|
}
|
||||||
|
|
||||||
private CommandExecutionResult(Result result, String message) {
|
private CommandExecutionResult(Result result, String message) {
|
||||||
this(result, message, null);
|
this(result, message, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private CommandExecutionResult(Result result, Object data) {
|
||||||
|
this(result, null, data, null);
|
||||||
|
}
|
||||||
|
|
||||||
public Result getResult() {
|
public Result getResult() {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -33,6 +45,10 @@ public class CommandExecutionResult {
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Object getData() {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
public Exception getException() {
|
public Exception getException() {
|
||||||
return exception;
|
return exception;
|
||||||
}
|
}
|
||||||
@@ -65,4 +81,7 @@ public class CommandExecutionResult {
|
|||||||
return new CommandExecutionResult(Result.NOACTION, null);
|
return new CommandExecutionResult(Result.NOACTION, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static CommandExecutionResult task(Task task) {
|
||||||
|
return new CommandExecutionResult(Result.TASK, null, task, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -14,15 +14,13 @@ import org.apache.logging.log4j.Logger;
|
|||||||
import org.reflections.Reflections;
|
import org.reflections.Reflections;
|
||||||
|
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public class CommandManager {
|
public class CommandManager {
|
||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(CommandManager.class);
|
private static final Logger logger = LogManager.getLogger(CommandManager.class);
|
||||||
public static final HashMap<String, CommandContainer> commands = new HashMap<>();
|
public static final Map<String, CommandContainer> commands = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
public static void init() {
|
public static void init() {
|
||||||
logger.info("Loading commands..");
|
logger.info("Loading commands..");
|
||||||
|
|||||||
@@ -11,7 +11,10 @@ import me.bobert.netneutrality.core.LogHandler;
|
|||||||
import me.bobert.netneutrality.core.command.options.CommandOptions;
|
import me.bobert.netneutrality.core.command.options.CommandOptions;
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
import me.bobert.netneutrality.core.context.InteractionContext;
|
import me.bobert.netneutrality.core.context.InteractionContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.Task;
|
||||||
|
import me.bobert.netneutrality.core.tasks.TaskManager;
|
||||||
import me.bobert.netneutrality.exceptions.CommandExecutionException;
|
import me.bobert.netneutrality.exceptions.CommandExecutionException;
|
||||||
|
import me.bobert.netneutrality.exceptions.GracefulContextError;
|
||||||
import me.bobert.netneutrality.utils.UserFunction;
|
import me.bobert.netneutrality.utils.UserFunction;
|
||||||
import net.dv8tion.jda.api.interactions.commands.OptionType;
|
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.OptionData;
|
||||||
@@ -26,8 +29,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static java.util.function.Predicate.not;
|
import static java.util.function.Predicate.not;
|
||||||
import static me.bobert.netneutrality.core.command.CommandExecutionResult.Result.ERROR;
|
import static me.bobert.netneutrality.core.command.CommandExecutionResult.Result.*;
|
||||||
import static me.bobert.netneutrality.core.command.CommandExecutionResult.Result.NOACTION;
|
|
||||||
|
|
||||||
public class CommandProcessor {
|
public class CommandProcessor {
|
||||||
|
|
||||||
@@ -56,13 +58,13 @@ public class CommandProcessor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command.isSlashCommandOnly() && !(context instanceof SlashCommandContext)) {
|
if (command.isSlashCommandOnly() && !(context instanceof InteractionContext)) {
|
||||||
context.replyEphemeral("This command is only available as a slash command");
|
context.replyEphemeral("This command is only available as a slash command");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.info("{} ID: {}", context.getClass().getSimpleName(), context.getId());
|
logger.info("Context ID: {}", context.getId());
|
||||||
logger.info("Executing command {} on guild {}", command.getCommandName(), context.getGuild() == null ? "PrivateMessage" : context.getGuild().getId());
|
logger.info("Executing command {} on guild {}", command.getCommandName(), context.getGuild() == null ? "PrivateMessage" : context.getGuild().getId());
|
||||||
configureSentry(command);
|
configureSentry(command);
|
||||||
|
|
||||||
@@ -95,17 +97,28 @@ public class CommandProcessor {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (result == TASK) {
|
||||||
|
TaskManager.getInstance().insert(context, (Task) commandExecutionResult.getData());
|
||||||
|
}
|
||||||
|
|
||||||
String message = commandExecutionResult.getMessage();
|
String message = commandExecutionResult.getMessage();
|
||||||
if (result == ERROR) {
|
if (result == ERROR) {
|
||||||
logger.error("Context[{}]: Error from command: {}", context.getId(), message, commandExecutionResult.getException());
|
logger.error("Context[{}]: Error from command: {}", context.getId(), message, commandExecutionResult.getException());
|
||||||
}
|
}
|
||||||
|
|
||||||
context.reply(message);
|
if (result == WARN) {
|
||||||
} catch (Exception exception) {
|
logger.warn("Context[{}]: Warning from command: {}", context.getId(), message);
|
||||||
if (exception instanceof CommandExecutionException) {
|
|
||||||
LogHandler.log(exception);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (message != null) {
|
||||||
|
context.reply(message);
|
||||||
|
}
|
||||||
|
} catch (GracefulContextError exception) {
|
||||||
|
logger.error(exception.getMessage());
|
||||||
|
context.replyEphemeral(exception.getMessage());
|
||||||
|
} catch (CommandExecutionException exception) {
|
||||||
|
LogHandler.log(exception);
|
||||||
|
} catch (Exception exception) {
|
||||||
LogHandler.log(new CommandExecutionException("Error during command execution", context, exception));
|
LogHandler.log(new CommandExecutionException("Error during command execution", context, exception));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package me.bobert.netneutrality.core.context;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import io.sentry.Sentry;
|
import io.sentry.Sentry;
|
||||||
|
import me.bobert.netneutrality.context.command.SlashCommandContext;
|
||||||
import net.dv8tion.jda.api.JDA;
|
import net.dv8tion.jda.api.JDA;
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ package me.bobert.netneutrality.core.context;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.context.AsyncContext;
|
import me.bobert.netneutrality.context.AsyncContext;
|
||||||
import me.bobert.netneutrality.core.GuildManager;
|
|
||||||
import me.bobert.netneutrality.utils.ContextUtils;
|
import me.bobert.netneutrality.utils.ContextUtils;
|
||||||
import net.dv8tion.jda.api.JDA;
|
import net.dv8tion.jda.api.JDA;
|
||||||
import net.dv8tion.jda.api.entities.*;
|
import net.dv8tion.jda.api.entities.*;
|
||||||
@@ -55,10 +54,6 @@ public interface Context {
|
|||||||
|
|
||||||
void ack(boolean ephemeral);
|
void ack(boolean ephemeral);
|
||||||
|
|
||||||
default GuildManager getGuildManager() {
|
|
||||||
return GuildManager.getManager(getGuild());
|
|
||||||
}
|
|
||||||
|
|
||||||
default void ack() {
|
default void ack() {
|
||||||
ack(false);
|
ack(false);
|
||||||
}
|
}
|
||||||
@@ -160,33 +155,39 @@ public interface Context {
|
|||||||
replyEphemeral(MessageCreateData.fromEmbeds(embeds));
|
replyEphemeral(MessageCreateData.fromEmbeds(embeds));
|
||||||
}
|
}
|
||||||
|
|
||||||
default void sendMessageInPrivate(MessageCreateData messageCreateData, List<LayoutComponent> components) {
|
default void sendMessageInPrivate(MessageCreateData messageCreateData, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
|
||||||
User user = getDiscordUser();
|
User user = getDiscordUser();
|
||||||
if (user != null) {
|
if (user != null) {
|
||||||
user.openPrivateChannel().flatMap(channel -> channel.sendMessage(messageCreateData).setComponents(components)).queue();
|
user.openPrivateChannel()
|
||||||
|
.flatMap(channel -> channel.sendMessage(messageCreateData).setComponents(components))
|
||||||
|
.queue(message -> consumer.accept(new AsyncContext(message)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void sendMessageInPrivate(String message, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
|
||||||
|
sendMessageInPrivate(MessageCreateData.fromContent(message), components, consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
default void sendMessageInPrivate(MessageCreateData messageCreateData, List<LayoutComponent> components) {
|
||||||
|
sendMessageInPrivate(messageCreateData, components, asyncContext -> {});
|
||||||
|
}
|
||||||
|
|
||||||
|
default void sendMessageInPrivate(String message, Consumer<AsyncContext> consumer) {
|
||||||
|
sendMessageInPrivate(message, List.of(), consumer);
|
||||||
|
}
|
||||||
|
|
||||||
default void sendMessageInPrivate(MessageCreateData messageCreateData) {
|
default void sendMessageInPrivate(MessageCreateData messageCreateData) {
|
||||||
sendMessageInPrivate(messageCreateData, List.of());
|
sendMessageInPrivate(messageCreateData, List.of());
|
||||||
}
|
}
|
||||||
|
|
||||||
default void sendMessageInPrivate(String message) {
|
|
||||||
sendMessageInPrivateWithLimit(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
default void sendMessageInPrivate(String message, List<LayoutComponent> components) {
|
|
||||||
sendMessageInPrivate(MessageCreateData.fromContent(message), components);
|
|
||||||
}
|
|
||||||
|
|
||||||
default void sendMessageInPrivate(MessageEmbed embed) {
|
|
||||||
sendMessageInPrivate(List.of(embed));
|
|
||||||
}
|
|
||||||
|
|
||||||
default void sendMessageInPrivate(List<MessageEmbed> embeds) {
|
default void sendMessageInPrivate(List<MessageEmbed> embeds) {
|
||||||
sendMessageInPrivate(MessageCreateData.fromEmbeds(embeds));
|
sendMessageInPrivate(MessageCreateData.fromEmbeds(embeds));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default void sendMessageInPrivate(String message) {
|
||||||
|
sendMessageInPrivateWithLimit(message);
|
||||||
|
}
|
||||||
|
|
||||||
private void replyWithLimit(String message) {
|
private void replyWithLimit(String message) {
|
||||||
for (String chunk : ContextUtils.splitMessage(message, Message.MAX_CONTENT_LENGTH)) {
|
for (String chunk : ContextUtils.splitMessage(message, Message.MAX_CONTENT_LENGTH)) {
|
||||||
reply(MessageCreateData.fromContent(chunk));
|
reply(MessageCreateData.fromContent(chunk));
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
package me.bobert.netneutrality.core.context;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.context.AsyncContext;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
|
||||||
|
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public interface MessageContext extends NoMessageContext {
|
||||||
|
Message getMessage();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean isFromGuild() {
|
||||||
|
return getMessage().isFromGuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void deleteMessage() {
|
||||||
|
if (getMessage() != null && getType() != ChannelType.PRIVATE) {
|
||||||
|
getMessage().delete().queueAfter(1, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void reply(MessageCreateData messageCreateData, boolean ephemeral, List<LayoutComponent> components, Consumer<AsyncContext> consumer) {
|
||||||
|
if (ephemeral && getType() != ChannelType.PRIVATE) {
|
||||||
|
sendMessageInPrivate(messageCreateData, components);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getChannel() != null && getChannel().canTalk()) {
|
||||||
|
getMessage().reply(messageCreateData).setComponents(components).queue(message -> consumer.accept(new AsyncContext(message)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package me.bobert.netneutrality.core.context;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.context.AsyncContext;
|
||||||
|
import net.dv8tion.jda.api.entities.channel.ChannelType;
|
||||||
|
import net.dv8tion.jda.api.interactions.InteractionHook;
|
||||||
|
import net.dv8tion.jda.api.interactions.modals.Modal;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
public interface NoMessageContext extends Context {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default InteractionHook getInteractionHook() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default boolean isAcknowledged() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default ChannelType getType() {
|
||||||
|
return getChannel().getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void reply(Modal modal) {
|
||||||
|
// only available for interactions
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void defer(boolean ephemeral, Consumer<AsyncContext> consumer) {
|
||||||
|
reply("Bot is thinking...", consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void ack(boolean ephemeral) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,15 +11,15 @@ import net.dv8tion.jda.api.entities.Member;
|
|||||||
import net.dv8tion.jda.api.entities.User;
|
import net.dv8tion.jda.api.entities.User;
|
||||||
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public abstract class TaskContext extends AbstractContext {
|
public abstract class TaskContext extends AbstractContext {
|
||||||
private final Map<String, Object> contextData;
|
private final Map<String, Object> contextData;
|
||||||
|
|
||||||
public TaskContext(JDA jda, Guild guild, MessageChannel channel, Member member, User user) {
|
public TaskContext(JDA jda, Guild guild, MessageChannel channel, Member member, User user) {
|
||||||
super(jda, guild, channel, member, user);
|
super(jda, guild, channel, member, user);
|
||||||
this.contextData = new HashMap<>();
|
this.contextData = new ConcurrentHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package me.bobert.netneutrality.core.tasks;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
|
||||||
public abstract class AbstractBuiltInTask extends AbstractTask implements BuiltInTask {
|
public abstract class AbstractBuiltInTask extends AbstractTask implements BuiltInTask {
|
||||||
|
|
||||||
@@ -19,11 +20,6 @@ public abstract class AbstractBuiltInTask extends AbstractTask implements BuiltI
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean oneshot() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isExpired() {
|
public boolean isExpired() {
|
||||||
return false;
|
return false;
|
||||||
@@ -33,4 +29,12 @@ public abstract class AbstractBuiltInTask extends AbstractTask implements BuiltI
|
|||||||
public void replyToInteraction(CommandContext context) {
|
public void replyToInteraction(CommandContext context) {
|
||||||
// Task is loaded at runtime, this will never be called
|
// Task is loaded at runtime, this will never be called
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Lifecycle execute(TaskContext context) {
|
||||||
|
executeTask(context);
|
||||||
|
return Lifecycle.BUILTIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void executeTask(TaskContext context);
|
||||||
}
|
}
|
||||||
@@ -15,11 +15,6 @@ public abstract class AbstractPersistableTask extends AbstractTask implements Pe
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean oneshot() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isExpired() {
|
public boolean isExpired() {
|
||||||
return false;
|
return false;
|
||||||
@@ -30,6 +25,30 @@ public abstract class AbstractPersistableTask extends AbstractTask implements Pe
|
|||||||
// Task loaded from database, this will never be called
|
// Task loaded from database, this will never be called
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void insert(CommandContext context) {
|
||||||
|
if (TaskManager.getInstance().hasTask(getId())) {
|
||||||
|
throw new RuntimeException("Cannot insert a task that already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskManager.getInstance().insert(context, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save() {
|
||||||
|
if (!TaskManager.getInstance().hasTask(getId())) {
|
||||||
|
throw new RuntimeException("Cannot save a task that doesn't exist yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
TaskManager.getInstance().getTaskService().persistTask(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void delete() {
|
||||||
|
TaskManager.getInstance().getTaskService().unpersistTask(this);
|
||||||
|
TaskManager.getInstance().removeTask(this);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toPayload() {
|
public String toPayload() {
|
||||||
JSONObject payload = new JSONObject(this);
|
JSONObject payload = new JSONObject(this);
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ package me.bobert.netneutrality.core.tasks;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
@@ -26,8 +28,8 @@ public abstract class AbstractTask implements Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TaskButtonCreator createButton() {
|
public void insert(CommandContext context) {
|
||||||
return new TaskButtonCreator(getId());
|
TaskManager.getInstance().insert(context, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -46,6 +48,6 @@ public abstract class AbstractTask implements Task {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("Task{id=%s, class=%s, name=%s, oneshot=%b, creationTime=%s}", getId(), getClass().getSimpleName(), getName(), oneshot(), getCreationTime());
|
return String.format("Task{id=%s, lockId=%s, class=%s, name=%s, creationTime=%s}", getId(), getLockId(), getClass().getSimpleName(), getName(), getCreationTime());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,18 +5,21 @@ package me.bobert.netneutrality.core.tasks;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a task that has a known ID and is loaded at runtime.
|
* Represents a task that has a known ID and is loaded at runtime.
|
||||||
* This can be any task and is not necessarily a button.
|
* This can be any task and is not necessarily a button.
|
||||||
*/
|
*/
|
||||||
public interface BuiltInTask extends Task {
|
public interface BuiltInTask extends Task {
|
||||||
|
|
||||||
static TaskButtonCreator createButton(Class<? extends BuiltInTask> clazz) {
|
static Button createButton(Class<? extends BuiltInTask> clazz) {
|
||||||
BuiltInTask builtInTask = TaskManager.getInstance().getTaskMap().values().stream().filter(clazz::isInstance).map(task -> (BuiltInTask) task).findFirst().orElse(null);
|
BuiltInTask builtInTask = TaskManager.getInstance().findBuiltInTaskByClass(clazz);
|
||||||
|
|
||||||
if (builtInTask == null) {
|
if (builtInTask == null) {
|
||||||
throw new IllegalArgumentException("Task class " + clazz.getName() + " was not registered by the task manager.");
|
throw new IllegalArgumentException("Task class " + clazz.getName() + " was not registered by the task manager.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TaskButtonCreator(builtInTask.getId());
|
return builtInTask.createButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
package me.bobert.netneutrality.core.tasks;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||||
|
|
||||||
|
public abstract class GenericCustomIdTask extends AbstractTask {
|
||||||
|
private final String customId;
|
||||||
|
|
||||||
|
public GenericCustomIdTask(String id) {
|
||||||
|
this.customId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return customId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Button createButton() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package me.bobert.netneutrality.core.tasks;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public abstract class GenericRandomIdTask extends GenericCustomIdTask {
|
||||||
|
public GenericRandomIdTask() {
|
||||||
|
super(UUID.randomUUID().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,10 +5,19 @@ package me.bobert.netneutrality.core.tasks;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a task that will be saved in the database and loaded when the bot is started
|
* Represents a task that can be persisted and restored later.
|
||||||
|
* The persistable task provides functionality to serialize its state
|
||||||
|
* so that it can be stored in a database.
|
||||||
*/
|
*/
|
||||||
public interface PersistableTask extends Task {
|
public interface PersistableTask extends Task {
|
||||||
|
void insert(CommandContext context);
|
||||||
|
|
||||||
|
void save();
|
||||||
|
|
||||||
|
void delete();
|
||||||
|
|
||||||
String toPayload();
|
String toPayload();
|
||||||
}
|
}
|
||||||
@@ -13,17 +13,35 @@ import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
|||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
public interface Task {
|
public interface Task {
|
||||||
|
enum Lifecycle {
|
||||||
|
BUILTIN,
|
||||||
|
PARENT,
|
||||||
|
KEEP,
|
||||||
|
REMOVE_SELF,
|
||||||
|
REMOVE_SELF_AND_CHILDREN
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* <p>This method should generate a unique ID to differentiate it from other tasks.</p>
|
* This method should generate a unique ID to differentiate it from other tasks.
|
||||||
* <p>
|
* <p>
|
||||||
* This determines what triggers a task to be executed.
|
* This determines what triggers a task to be executed.
|
||||||
* For example, a modal or button would use the component id.
|
* For example, a modal or button would use the component id.
|
||||||
* For an emoji reaction, the guild and message id would be combined.
|
* For an emoji reaction, the guild and message id would be combined.
|
||||||
* </p>
|
* </p>
|
||||||
|
*
|
||||||
* @return a unique ID for this task
|
* @return a unique ID for this task
|
||||||
*/
|
*/
|
||||||
String getId();
|
String getId();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the lock ID that is used to synchronize tasks
|
||||||
|
* that require a locking mechanism.
|
||||||
|
*
|
||||||
|
* @return the lock ID associated with this task
|
||||||
|
*/
|
||||||
|
default String getLockId() {
|
||||||
|
return getId();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is only used for debugging purpose.
|
* This is only used for debugging purpose.
|
||||||
* @return Name of the task
|
* @return Name of the task
|
||||||
@@ -36,9 +54,14 @@ public interface Task {
|
|||||||
boolean isInsertable();
|
boolean isInsertable();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This determines if the task should be cancelled after execution.
|
* When this returns true, the task will get removed by the cleanup thread.
|
||||||
|
* @return If the task is expired
|
||||||
*/
|
*/
|
||||||
boolean oneshot();
|
boolean isExpired();
|
||||||
|
|
||||||
|
OffsetDateTime getCreationTime();
|
||||||
|
|
||||||
|
Button createButton();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>This method is used to reply to the interaction that triggered this task.</p>
|
* <p>This method is used to reply to the interaction that triggered this task.</p>
|
||||||
@@ -48,41 +71,9 @@ public interface Task {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the task.
|
* Executes the task.
|
||||||
* @param context The context of a task containing the event that triggererd it.
|
* @param context The context for this task.
|
||||||
*/
|
*/
|
||||||
void execute(TaskContext context);
|
Lifecycle execute(TaskContext context);
|
||||||
|
|
||||||
/**
|
void insert(CommandContext context);
|
||||||
* When this returns true, the task will get removed by the cleanup thread.
|
|
||||||
* @return If the task is expired
|
|
||||||
*/
|
|
||||||
boolean isExpired();
|
|
||||||
|
|
||||||
OffsetDateTime getCreationTime();
|
|
||||||
|
|
||||||
TaskButtonCreator createButton();
|
|
||||||
|
|
||||||
class TaskButtonCreator {
|
|
||||||
private final String id;
|
|
||||||
|
|
||||||
public TaskButtonCreator(String id) {
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Button primary(String label) {
|
|
||||||
return Button.primary(id, label);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Button secondary(String label) {
|
|
||||||
return Button.secondary(id, label);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Button success(String label) {
|
|
||||||
return Button.success(id, label);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Button danger(String label) {
|
|
||||||
return Button.danger(id, label);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -5,17 +5,18 @@ package me.bobert.netneutrality.core.tasks;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.context.tasks.ButtonTaskContext;
|
|
||||||
import me.bobert.netneutrality.context.tasks.ModalTaskContext;
|
|
||||||
import me.bobert.netneutrality.core.LogHandler;
|
import me.bobert.netneutrality.core.LogHandler;
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.InteractionContext;
|
||||||
import me.bobert.netneutrality.core.context.TaskContext;
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.impl.StepTask;
|
||||||
import me.bobert.netneutrality.core.tasks.trait.Deletable;
|
import me.bobert.netneutrality.core.tasks.trait.Deletable;
|
||||||
import me.bobert.netneutrality.core.tasks.trait.NotAcknowledgeable;
|
import me.bobert.netneutrality.core.tasks.trait.NotAcknowledgeable;
|
||||||
import me.bobert.netneutrality.core.tasks.trait.WithChildren;
|
import me.bobert.netneutrality.core.tasks.trait.WithChildren;
|
||||||
import me.bobert.netneutrality.core.tasks.trait.WithParent;
|
import me.bobert.netneutrality.core.tasks.trait.WithParent;
|
||||||
import me.bobert.netneutrality.core.thread.ThreadManager;
|
import me.bobert.netneutrality.core.thread.ThreadManager;
|
||||||
import me.bobert.netneutrality.db.service.TaskService;
|
import me.bobert.netneutrality.db.service.TaskService;
|
||||||
|
import me.bobert.netneutrality.exceptions.GracefulContextError;
|
||||||
import me.bobert.netneutrality.exceptions.TaskExecutionException;
|
import me.bobert.netneutrality.exceptions.TaskExecutionException;
|
||||||
import me.bobert.netneutrality.thread.CleanupTaskThread;
|
import me.bobert.netneutrality.thread.CleanupTaskThread;
|
||||||
import net.dv8tion.jda.api.interactions.InteractionHook;
|
import net.dv8tion.jda.api.interactions.InteractionHook;
|
||||||
@@ -26,19 +27,27 @@ import org.reflections.Reflections;
|
|||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class TaskManager {
|
public class TaskManager {
|
||||||
private static final Logger logger = LogManager.getLogger(TaskManager.class);
|
private static final Logger logger = LogManager.getLogger(TaskManager.class);
|
||||||
private static TaskManager instance;
|
private static TaskManager instance;
|
||||||
private final TaskService taskService;
|
private final TaskService taskService;
|
||||||
private final Map<String, Task> tasks;
|
private final Map<String, Task> tasks;
|
||||||
|
private final List<Task> pendingTasks;
|
||||||
private final Map<String, CommandContext> deletableTasksContext;
|
private final Map<String, CommandContext> deletableTasksContext;
|
||||||
|
private final Map<String, ReentrantLock> tasksLock;
|
||||||
|
|
||||||
private TaskManager() {
|
private TaskManager() {
|
||||||
this.tasks = new HashMap<>();
|
this.tasks = new ConcurrentHashMap<>();
|
||||||
this.deletableTasksContext = new HashMap<>();
|
this.pendingTasks = new CopyOnWriteArrayList<>();
|
||||||
this.taskService = new TaskService(this);
|
this.deletableTasksContext = new ConcurrentHashMap<>();
|
||||||
|
this.tasksLock = new ConcurrentHashMap<>();
|
||||||
|
this.taskService = new TaskService();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TaskManager getInstance() {
|
public static TaskManager getInstance() {
|
||||||
@@ -56,7 +65,7 @@ public class TaskManager {
|
|||||||
ThreadManager.getScheduledExecutor().scheduleWithFixedDelay(new CleanupTaskThread(), 1, 5, TimeUnit.MINUTES);
|
ThreadManager.getScheduledExecutor().scheduleWithFixedDelay(new CleanupTaskThread(), 1, 5, TimeUnit.MINUTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TaskService getTaskService() {
|
protected TaskService getTaskService() {
|
||||||
return taskService;
|
return taskService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,22 +73,22 @@ public class TaskManager {
|
|||||||
return tasks.get(id);
|
return tasks.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<Task> getTasks() {
|
public boolean hasTask(String id) {
|
||||||
|
return tasks.containsKey(id) || pendingTasks.stream().anyMatch(task -> task.getId().equals(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized Collection<Task> getTasks() {
|
||||||
return tasks.values();
|
return tasks.values();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Task> getTaskMap() {
|
public synchronized Collection<Task> getPendingTasks() {
|
||||||
return tasks;
|
return pendingTasks;
|
||||||
}
|
|
||||||
|
|
||||||
public boolean exists(String id) {
|
|
||||||
return tasks.containsKey(id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void insert(CommandContext context, Task task) {
|
public void insert(CommandContext context, Task task) {
|
||||||
String id = task.getId();
|
String id = task.getId();
|
||||||
|
|
||||||
if (tasks.containsKey(id)) {
|
if (hasTask(id)) {
|
||||||
context.reply("A task with the same ID already exists!");
|
context.reply("A task with the same ID already exists!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -93,7 +102,11 @@ public class TaskManager {
|
|||||||
taskService.persistTask(persistableTask);
|
taskService.persistTask(persistableTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.put(id, task);
|
if (task instanceof StepTask) {
|
||||||
|
pendingTasks.add(task);
|
||||||
|
} else {
|
||||||
|
tasks.put(id, task);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task instanceof Deletable) {
|
if (task instanceof Deletable) {
|
||||||
@@ -102,6 +115,10 @@ public class TaskManager {
|
|||||||
|
|
||||||
if (task instanceof WithChildren taskWithChildren) {
|
if (task instanceof WithChildren taskWithChildren) {
|
||||||
List<Task> childrens = taskWithChildren.getChildTasks();
|
List<Task> childrens = taskWithChildren.getChildTasks();
|
||||||
|
if (childrens == null || childrens.isEmpty()) {
|
||||||
|
logger.warn("Task [{}]: parent task doesn't create any childs", task.getId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (Task child : childrens) {
|
for (Task child : childrens) {
|
||||||
insert(context, child);
|
insert(context, child);
|
||||||
@@ -109,51 +126,107 @@ public class TaskManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeTask(Task task) {
|
public synchronized void activateTask(Task task) {
|
||||||
|
if (pendingTasks.remove(task)) {
|
||||||
|
tasks.put(task.getId(), task);
|
||||||
|
logger.info("Updating task {} with new id: {}", task.getName(), task.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void removeTask(Task task) {
|
||||||
String id = task.getId();
|
String id = task.getId();
|
||||||
|
|
||||||
if (tasks.containsKey(id)) {
|
if (tasks.containsKey(id)) {
|
||||||
logger.debug("Removing task: {}", task);
|
logger.debug("Removing task: {}", task);
|
||||||
tasks.remove(id);
|
tasks.remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tasksLock.remove(task.getLockId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handle(TaskContext context) {
|
public void handle(TaskContext context) {
|
||||||
String taskId = context.getId();
|
String taskId = context.getId();
|
||||||
Task task = tasks.get(taskId);
|
Task task = getTask(taskId);
|
||||||
|
|
||||||
if (task == null && (context instanceof ModalTaskContext || context instanceof ButtonTaskContext)) {
|
if (task == null && context instanceof InteractionContext) {
|
||||||
context.replyEphemeral("This task is no longer available, someone might have already used it or it might have expired");
|
context.replyEphemeral("This task is no longer available, someone might have already used it or it might have expired");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task != null) {
|
if (task != null) {
|
||||||
logger.debug("Executing task: {}", task);
|
String lockId = task.getLockId();
|
||||||
|
ReentrantLock lock = tasksLock.computeIfAbsent(lockId, k -> {
|
||||||
|
if (task instanceof PersistableTask || task instanceof BuiltInTask) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ReentrantLock();
|
||||||
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Cancel task during execution to prevent spam
|
if (lock != null) {
|
||||||
removeTaskAndChildTasks(task);
|
logger.debug("Aquiring lock for task: {}", taskId);
|
||||||
|
lock.lock();
|
||||||
|
logger.debug("Aquired lock for: {}", lockId);
|
||||||
|
|
||||||
task.execute(context);
|
if (!tasks.containsKey(taskId)) {
|
||||||
|
logger.debug("Task {} is no longer available after aquiring lock", taskId);
|
||||||
if (task.isInsertable() && !task.oneshot()) {
|
return;
|
||||||
reinsertTaskAndChildTasks(task);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.debug("Executing task: {}", task);
|
||||||
|
Task.Lifecycle status = task.execute(context);
|
||||||
|
handleTaskLifecycle(task, status);
|
||||||
|
|
||||||
deleteInteraction(task);
|
deleteInteraction(task);
|
||||||
|
|
||||||
// If interaction is still not acknowledged after execution, send a placeholder response.
|
// If interaction is still not acknowledged after execution, send a placeholder response.
|
||||||
if (!context.isAcknowledged() && !(task instanceof NotAcknowledgeable)) {
|
if (!context.isAcknowledged() && !(task instanceof NotAcknowledgeable)) {
|
||||||
|
logger.warn("Interaction not acknowledged");
|
||||||
context.replyEphemeral("Task executed successfully but interaction wasn't acknowledged, report this bug by using the command /report-bug");
|
context.replyEphemeral("Task executed successfully but interaction wasn't acknowledged, report this bug by using the command /report-bug");
|
||||||
}
|
}
|
||||||
|
} catch (GracefulContextError exception) {
|
||||||
|
logger.error(exception.getMessage());
|
||||||
|
context.replyEphemeral(exception.getMessage());
|
||||||
} catch (TaskExecutionException exception) {
|
} catch (TaskExecutionException exception) {
|
||||||
LogHandler.log(exception);
|
LogHandler.log(exception);
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
LogHandler.log(new TaskExecutionException("Error during task execution", context, exception));
|
LogHandler.log(new TaskExecutionException("Error during task execution", context, exception));
|
||||||
|
} finally {
|
||||||
|
if (lock != null) {
|
||||||
|
logger.debug("Releasing lock for task: {}", taskId);
|
||||||
|
lock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <E extends Task> List<E> findTasksByPartialId(Class<E> clazz, String partialId) {
|
||||||
|
return tasks.values().stream()
|
||||||
|
.filter(clazz::isInstance)
|
||||||
|
.map(clazz::cast)
|
||||||
|
.filter(task -> task.getId().contains(partialId))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public BuiltInTask findBuiltInTaskByClass(Class<? extends BuiltInTask> clazz) {
|
||||||
|
return tasks.values().stream()
|
||||||
|
.filter(clazz::isInstance)
|
||||||
|
.map(task -> (BuiltInTask) task)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void insertPersistableTask(PersistableTask task) {
|
||||||
|
if (tasks.containsKey(task.getId())) {
|
||||||
|
logger.error("Trying to load persistable task but ID already exists: {}", task.getId());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Loaded persistable task: {}", task);
|
||||||
|
tasks.put(task.getId(), task);
|
||||||
|
}
|
||||||
|
|
||||||
private void loadBuiltInTasks() {
|
private void loadBuiltInTasks() {
|
||||||
logger.info("Loading built-in tasks...");
|
logger.info("Loading built-in tasks...");
|
||||||
|
|
||||||
@@ -177,7 +250,7 @@ public class TaskManager {
|
|||||||
|
|
||||||
private void deleteInteraction(Task task) {
|
private void deleteInteraction(Task task) {
|
||||||
if (task instanceof WithParent<?> taskWithParent) {
|
if (task instanceof WithParent<?> taskWithParent) {
|
||||||
Task parent = (Task) taskWithParent.getParentTask();
|
Task parent = taskWithParent.getParentTask();
|
||||||
|
|
||||||
if (parent instanceof Deletable deletable && task.isInsertable()) {
|
if (parent instanceof Deletable deletable && task.isInsertable()) {
|
||||||
deleteInteraction(parent.getId(), deletable);
|
deleteInteraction(parent.getId(), deletable);
|
||||||
@@ -212,37 +285,28 @@ public class TaskManager {
|
|||||||
deletable.onInteractionDelete();
|
deletable.onInteractionDelete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeTaskAndChildTasks(Task task) {
|
private void handleTaskLifecycle(Task task, Task.Lifecycle status) {
|
||||||
if (task instanceof BuiltInTask) {
|
if (status == null) {
|
||||||
return;
|
throw new IllegalArgumentException("Task lifecycle status cannot be null");
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.remove(task.getId());
|
switch (status) {
|
||||||
|
case REMOVE_SELF:
|
||||||
|
removeTask(task);
|
||||||
|
break;
|
||||||
|
|
||||||
if (task instanceof WithParent<?> taskWithParent && !taskWithParent.shouldKeepChilds()) {
|
case REMOVE_SELF_AND_CHILDREN:
|
||||||
List<Task> childrens = taskWithParent.getParentTask().getChildTasks();
|
if (task instanceof WithChildren taskWithChildren) {
|
||||||
for (Task child : childrens) {
|
for (Task child : taskWithChildren.getChildTasks()) {
|
||||||
if (!task.equals(child)) {
|
removeTask(child);
|
||||||
tasks.remove(child.getId());
|
}
|
||||||
}
|
}
|
||||||
}
|
removeTask(task);
|
||||||
}
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
private void reinsertTaskAndChildTasks(Task task) {
|
case KEEP:
|
||||||
if (task instanceof BuiltInTask) {
|
default:
|
||||||
return;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
tasks.put(task.getId(), task);
|
|
||||||
|
|
||||||
if (task instanceof WithParent<?> taskWithParent && taskWithParent.shouldKeepChilds()) {
|
|
||||||
List<Task> childrens = taskWithParent.getParentTask().getChildTasks();
|
|
||||||
for (Task child : childrens) {
|
|
||||||
if (!task.equals(child)) {
|
|
||||||
tasks.put(child.getId(), child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package me.bobert.netneutrality.core.tasks.impl;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.tasks.GenericRandomIdTask;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
|
||||||
|
public abstract class ButtonTask extends GenericRandomIdTask {
|
||||||
|
private final ButtonStyle buttonStyle;
|
||||||
|
private final String buttonLabel;
|
||||||
|
|
||||||
|
public ButtonTask(ButtonStyle buttonStyle, String buttonLabel) {
|
||||||
|
this.buttonStyle = buttonStyle;
|
||||||
|
this.buttonLabel = buttonLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Button createButton() {
|
||||||
|
if (buttonStyle == null || buttonLabel == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Button.of(buttonStyle, getId(), buttonLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isExpired() {
|
||||||
|
return getCreationTime().plusMinutes(15).isBefore(OffsetDateTime.now());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
package me.bobert.netneutrality.core.tasks.impl;
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.context.tasks.EmojiTaskContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.AbstractPersistableTask;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public abstract class EmojiTask extends AbstractPersistableTask {
|
||||||
|
private final Map<String, String> emojiMapping = new ConcurrentHashMap<>();
|
||||||
|
private final String guildId;
|
||||||
|
private final String messageId;
|
||||||
|
|
||||||
|
public EmojiTask(String guildId, String messageId) {
|
||||||
|
this.guildId = guildId;
|
||||||
|
this.messageId = messageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGuildId() {
|
||||||
|
return guildId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMessageId() {
|
||||||
|
return messageId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addEmoji(String emoji, String snowflake) {
|
||||||
|
emojiMapping.put(emoji, snowflake);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeEmoji(String emoji) {
|
||||||
|
emojiMapping.remove(emoji);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> getEmojiMapping() {
|
||||||
|
return emojiMapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return String.format(EmojiTaskContext.ID, getGuildId(), getMessageId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Button createButton() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-15
@@ -1,4 +1,4 @@
|
|||||||
package me.bobert.netneutrality.tasks;
|
package me.bobert.netneutrality.core.tasks.impl;
|
||||||
/*
|
/*
|
||||||
* Copyright (c) anthorob
|
* Copyright (c) anthorob
|
||||||
*
|
*
|
||||||
@@ -6,25 +6,13 @@ package me.bobert.netneutrality.tasks;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.context.TaskContext;
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
import me.bobert.netneutrality.core.tasks.AbstractTask;
|
import me.bobert.netneutrality.core.tasks.GenericRandomIdTask;
|
||||||
import net.dv8tion.jda.api.interactions.modals.ModalMapping;
|
import net.dv8tion.jda.api.interactions.modals.ModalMapping;
|
||||||
|
|
||||||
import java.time.OffsetDateTime;
|
import java.time.OffsetDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public abstract class ModalTask extends AbstractTask {
|
public abstract class ModalTask extends GenericRandomIdTask {
|
||||||
private final UUID id = UUID.randomUUID();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return id.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean oneshot() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isExpired() {
|
public boolean isExpired() {
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package me.bobert.netneutrality.core.tasks.impl;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.context.MessageContext;
|
||||||
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.AbstractTask;
|
||||||
|
import me.bobert.netneutrality.core.tasks.TaskManager;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public abstract class StepTask extends AbstractTask {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(StepTask.class);
|
||||||
|
|
||||||
|
public enum StepType {
|
||||||
|
STRING, ROLE, EMOJI
|
||||||
|
}
|
||||||
|
|
||||||
|
private Guild guild;
|
||||||
|
private String id = UUID.randomUUID().toString();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void replyToInteraction(CommandContext context) {
|
||||||
|
if (context.isFromGuild()) {
|
||||||
|
guild = context.getGuild();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (guild == null && getStepType() == StepType.ROLE) {
|
||||||
|
throw new IllegalArgumentException("Step task if of type role but it was inserted in a non-guild context");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inDM()) {
|
||||||
|
context.replyEphemeral("Please follow the instructions sent in DM.");
|
||||||
|
context.sendMessageInPrivate(getStepDescription() + "\n" + getStepQuestion(), asyncContext -> assignId(asyncContext.getMessageId()));
|
||||||
|
} else {
|
||||||
|
context.reply(getStepQuestion(), asyncContext -> id = asyncContext.getMessageId());
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("step id : " + id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Lifecycle execute(TaskContext context) {
|
||||||
|
if (context instanceof MessageContext messageContext) {
|
||||||
|
Message message = messageContext.getMessage();
|
||||||
|
String answer = message.getContentRaw();
|
||||||
|
|
||||||
|
if (validateAnswer(answer)) {
|
||||||
|
StepTask nextStep = executeStep(answer);
|
||||||
|
|
||||||
|
//TODO: to finish
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lifecycle.REMOVE_SELF;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isExpired() {
|
||||||
|
return getCreationTime().plusMinutes(30).isBefore(OffsetDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Button createButton() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validateAnswer(String answer) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assignId(String newId) {
|
||||||
|
if (newId == null || newId.isBlank()) {
|
||||||
|
throw new IllegalArgumentException("Task ID cannot be null or empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (this) {
|
||||||
|
this.id = newId;
|
||||||
|
TaskManager.getInstance().activateTask(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.id = newId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract boolean inDM();
|
||||||
|
|
||||||
|
public abstract StepType getStepType();
|
||||||
|
|
||||||
|
public abstract String getStepDescription();
|
||||||
|
|
||||||
|
public abstract String getStepQuestion();
|
||||||
|
|
||||||
|
public abstract StepTask executeStep(String answer);
|
||||||
|
}
|
||||||
@@ -6,64 +6,76 @@ package me.bobert.netneutrality.core.tasks.persistence;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.tasks.PersistableTask;
|
import me.bobert.netneutrality.core.tasks.PersistableTask;
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class TaskFactory {
|
public class TaskFactory {
|
||||||
private static final Logger logger = LogManager.getLogger(TaskFactory.class);
|
public enum ParamType {
|
||||||
|
STRING(String.class),
|
||||||
|
INT(Integer.class),
|
||||||
|
MAP(Map.class);
|
||||||
|
|
||||||
|
private final Class<?> typeClass;
|
||||||
|
|
||||||
|
ParamType(Class<?> typeClass) {
|
||||||
|
this.typeClass = typeClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> getTypeClass() {
|
||||||
|
return typeClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ParamType fromString(String type) {
|
||||||
|
return switch (type.toLowerCase()) {
|
||||||
|
case "string" -> STRING;
|
||||||
|
case "int" -> INT;
|
||||||
|
case "map" -> MAP;
|
||||||
|
default -> throw new IllegalArgumentException("Unsupported type: " + type);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static PersistableTask createTaskInstance(String className, JSONArray params, JSONArray types, JSONObject payload) throws Exception {
|
public static PersistableTask createTaskInstance(String className, JSONArray params, JSONArray types, JSONObject payload) throws Exception {
|
||||||
if (params.length() != types.length()) {
|
if (params.length() != types.length()) {
|
||||||
throw new RuntimeException("Wrong number of parameters and types for " + className);
|
throw new RuntimeException("Wrong number of parameters and types.");
|
||||||
}
|
}
|
||||||
|
|
||||||
Class<?> clazz = Class.forName(className);
|
Class<?> clazz = Class.forName(className);
|
||||||
|
if (!PersistableTask.class.isAssignableFrom(clazz)) {
|
||||||
|
throw new IllegalArgumentException("Class does not implement PersistableTask.");
|
||||||
|
}
|
||||||
|
|
||||||
Class<?>[] classTypes = new Class<?>[types.length()];
|
Class<?>[] classTypes = new Class<?>[types.length()];
|
||||||
Object[] classParams = new Object[params.length()];
|
Object[] classParams = new Object[params.length()];
|
||||||
|
|
||||||
for (int i = 0; i < params.length(); i++) {
|
for (int i = 0; i < params.length(); i++) {
|
||||||
String type = types.getString(i);
|
ParamType paramType = ParamType.fromString(types.getString(i));
|
||||||
String param = params.getString(i);
|
String paramName = params.getString(i);
|
||||||
|
|
||||||
Class<?> classType = parseClassTypeFromString(type);
|
classTypes[i] = paramType.getTypeClass();
|
||||||
Object classParam = castParamToType(classType, param, type, payload);
|
classParams[i] = extractValue(paramType, paramName, payload);
|
||||||
|
|
||||||
classTypes[i] = classType;
|
|
||||||
classParams[i] = classParam;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (PersistableTask) clazz.getConstructor(classTypes).newInstance(classParams);
|
try {
|
||||||
|
return (PersistableTask) clazz.getConstructor(classTypes).newInstance(classParams);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
throw new NoSuchMethodException("No matching constructor found with parameters: " + Arrays.toString(classTypes));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new IllegalArgumentException("Invalid arguments for constructor. Expected: " + Arrays.toString(classParams), e);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
throw new RuntimeException("Constructor threw an exception", e.getCause());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Class<?> parseClassTypeFromString(String param) {
|
private static Object extractValue(ParamType type, String paramName, JSONObject payload) {
|
||||||
return switch (param) {
|
return switch (type) {
|
||||||
case "string" -> String.class;
|
case STRING -> payload.getString(paramName);
|
||||||
case "int" -> Integer.class;
|
case INT -> payload.getInt(paramName);
|
||||||
case "map" -> Map.class;
|
case MAP -> payload.getJSONObject(paramName).toMap();
|
||||||
default -> throw new IllegalArgumentException("Param " + param + " is not implemented yet.");
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Object castParamToType(Class<?> type, String paramName, String typeString, JSONObject payload) {
|
|
||||||
if (type == String.class) {
|
|
||||||
return payload.getString(paramName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == Integer.class) {
|
|
||||||
return payload.getInt(paramName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type == Map.class) {
|
|
||||||
JSONObject mapJson = payload.getJSONObject(paramName);
|
|
||||||
|
|
||||||
return mapJson.toMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalArgumentException("Type " + typeString + " is not implemented yet.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -26,43 +26,67 @@ public class TaskRegistry {
|
|||||||
|
|
||||||
private static final Logger logger = LogManager.getLogger(TaskRegistry.class);
|
private static final Logger logger = LogManager.getLogger(TaskRegistry.class);
|
||||||
private static final Map<String, TaskCreator> taskCreators = new HashMap<>();
|
private static final Map<String, TaskCreator> taskCreators = new HashMap<>();
|
||||||
|
private static final String FILE_NAME = "persistable-tasks.json";
|
||||||
|
|
||||||
public TaskRegistry() {
|
// JSON keys as constants
|
||||||
loadTaskConfig();
|
private static final String KEY_TASKS = "tasks";
|
||||||
}
|
private static final String KEY_CLASS = "class";
|
||||||
|
private static final String KEY_PARAMS = "params";
|
||||||
|
private static final String KEY_TYPES = "types";
|
||||||
|
|
||||||
public static TaskCreator getTaskCreator(String className) {
|
public static TaskCreator getTaskCreator(String className) {
|
||||||
return taskCreators.get(className);
|
return taskCreators.get(className);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void loadTaskConfig() {
|
public static void loadTaskConfig() {
|
||||||
String fileName = "persistable-tasks.json";
|
logger.info("Reading task mappings from {}..", FILE_NAME);
|
||||||
|
|
||||||
try (InputStream is = TaskFactory.class.getClassLoader().getResourceAsStream(fileName)) {
|
try (InputStream is = TaskFactory.class.getClassLoader().getResourceAsStream(FILE_NAME)) {
|
||||||
if (is == null) {
|
if (is == null) {
|
||||||
throw new FileNotFoundException("Could not load: " + fileName);
|
throw new FileNotFoundException("Could not load: " + FILE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
String jsonString = IOUtils.toString(is, StandardCharsets.UTF_8);
|
String jsonString = IOUtils.toString(is, StandardCharsets.UTF_8);
|
||||||
JSONObject json = new JSONObject(jsonString);
|
JSONObject json = new JSONObject(jsonString);
|
||||||
|
JSONArray tasksArray = json.optJSONArray(KEY_TASKS);
|
||||||
|
|
||||||
json.getJSONArray("tasks").forEach(task -> {
|
if (tasksArray == null) {
|
||||||
JSONObject taskJson = (JSONObject) task;
|
logger.warn("No tasks found in JSON: {}", FILE_NAME);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String className = taskJson.getString("class");
|
for (int i = 0; i < tasksArray.length(); i++) {
|
||||||
JSONArray params = taskJson.getJSONArray("params");
|
JSONObject taskJson = tasksArray.getJSONObject(i);
|
||||||
JSONArray types = taskJson.getJSONArray("types");
|
|
||||||
|
String className = taskJson.optString(KEY_CLASS, "").trim();
|
||||||
|
if (className.isEmpty()) {
|
||||||
|
logger.warn("Missing JSON key '{}' at index {}", KEY_CLASS, i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONArray params = taskJson.optJSONArray(KEY_PARAMS);
|
||||||
|
if (params == null) {
|
||||||
|
logger.warn("Missing JSON key '{}' for class: {}", KEY_PARAMS, className);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONArray types = taskJson.optJSONArray(KEY_TYPES);
|
||||||
|
if (types == null) {
|
||||||
|
logger.warn("Missing JSON key '{}' for class: {}", KEY_TYPES, className);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
taskCreators.put(className, payload -> {
|
taskCreators.put(className, payload -> {
|
||||||
try {
|
try {
|
||||||
return TaskFactory.createTaskInstance(className, params, types, payload);
|
return TaskFactory.createTaskInstance(className, params, types, payload);
|
||||||
} catch (RuntimeException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
logger.error("Failed to create instance of '{}': {}", className, e.getMessage());
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
|
||||||
|
logger.info("{} mappings registered", taskCreators.size());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error(e);
|
logger.error(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,5 +9,11 @@ package me.bobert.netneutrality.core.tasks.trait;
|
|||||||
* Represents a task that will remove its interaction from the channel after execution
|
* Represents a task that will remove its interaction from the channel after execution
|
||||||
*/
|
*/
|
||||||
public interface Deletable {
|
public interface Deletable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called after the interaction associated with a deletable task
|
||||||
|
* has been deleted. Tasks that implement the {@link Deletable} interface can provide
|
||||||
|
* their own cleanup logic in this method.
|
||||||
|
*/
|
||||||
void onInteractionDelete();
|
void onInteractionDelete();
|
||||||
}
|
}
|
||||||
@@ -6,7 +6,7 @@ package me.bobert.netneutrality.core.tasks.trait;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a task implements this interface, it means that the interaction will not get acknowledged during execution.
|
* Represents a task that will not acknowledge the interaction when executed.
|
||||||
*/
|
*/
|
||||||
public interface NotAcknowledgeable {
|
public interface NotAcknowledgeable {
|
||||||
}
|
}
|
||||||
@@ -5,17 +5,21 @@ package me.bobert.netneutrality.core.tasks.trait;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
import me.bobert.netneutrality.core.tasks.Task;
|
import me.bobert.netneutrality.core.tasks.Task;
|
||||||
import me.bobert.netneutrality.core.tasks.TaskManager;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface WithChildren {
|
public interface WithChildren extends Task {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default Lifecycle execute(TaskContext context) {
|
||||||
|
return Lifecycle.PARENT;
|
||||||
|
}
|
||||||
|
|
||||||
List<Task> getChildTasks();
|
List<Task> getChildTasks();
|
||||||
|
|
||||||
default void removeAllChildTasks() {
|
default void addChild(Task... tasks) {
|
||||||
for (Task child : getChildTasks()) {
|
getChildTasks().addAll(List.of(tasks));
|
||||||
TaskManager.getInstance().removeTask(child);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,12 +5,30 @@ package me.bobert.netneutrality.core.tasks.trait;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public interface WithParent<E extends WithChildren> {
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.Task;
|
||||||
|
|
||||||
E getParentTask();
|
public interface WithParent<E extends WithChildren> extends Task {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether execution of this task should remove all childs of its parent task during execution
|
* Retrieves the lock ID associated with the parent task.
|
||||||
|
*
|
||||||
|
* @return the lock ID of the parent task
|
||||||
*/
|
*/
|
||||||
boolean shouldKeepChilds();
|
@Override
|
||||||
|
default String getLockId() {
|
||||||
|
return getParentTask().getLockId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
default void replyToInteraction(CommandContext context) {
|
||||||
|
// Child tasks shouldn't reply to the interaction, this is the job of the parent task
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the parent task associated with this task.
|
||||||
|
*
|
||||||
|
* @return the parent task of type E
|
||||||
|
*/
|
||||||
|
E getParentTask();
|
||||||
}
|
}
|
||||||
@@ -22,11 +22,12 @@ public class ThreadManager {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ExecutorService getExecutor(String name) {
|
||||||
|
return executors.computeIfAbsent(name, s -> Executors.newCachedThreadPool(new BaseThreadFactory(s)));
|
||||||
|
}
|
||||||
|
|
||||||
public static ExecutorService getExecutor() {
|
public static ExecutorService getExecutor() {
|
||||||
return executors.computeIfAbsent(
|
return getExecutor("general-executor");
|
||||||
"general-executor",
|
|
||||||
s -> Executors.newCachedThreadPool(new BaseThreadFactory(s))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void shutdownExecutor() {
|
public static void shutdownExecutor() {
|
||||||
|
|||||||
@@ -5,14 +5,11 @@ package me.bobert.netneutrality.db.repository;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.GuildManager;
|
|
||||||
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
|
||||||
import me.bobert.netneutrality.core.db.SessionManager;
|
import me.bobert.netneutrality.core.db.SessionManager;
|
||||||
import me.bobert.netneutrality.db.Repository;
|
import me.bobert.netneutrality.db.Repository;
|
||||||
import me.bobert.netneutrality.db.entities.OBlacklist;
|
import me.bobert.netneutrality.db.entities.OBlacklist;
|
||||||
import me.bobert.netneutrality.db.entities.OGuild;
|
import me.bobert.netneutrality.db.entities.OGuild;
|
||||||
import me.bobert.netneutrality.utils.JpaUtils;
|
import me.bobert.netneutrality.utils.JpaUtils;
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
import org.hibernate.Session;
|
import org.hibernate.Session;
|
||||||
import org.hibernate.query.Query;
|
import org.hibernate.query.Query;
|
||||||
@@ -53,15 +50,26 @@ public class BlacklistRepository implements Repository<OBlacklist> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommandExecutionResult add(Guild guild, Member member, String word) {
|
public List<OBlacklist> findByGuildId(String guildId) {
|
||||||
GuildManager guildManager = GuildManager.getManager(guild);
|
List<OBlacklist> blacklists = new ArrayList<>();
|
||||||
|
|
||||||
|
JpaUtils.runWithSession(session -> {
|
||||||
|
Query<OBlacklist> query = session.createQuery("SELECT o FROM OBlacklist o WHERE o.guild.guildId = :guildId", OBlacklist.class);
|
||||||
|
query.setParameter("guildId", guildId);
|
||||||
|
|
||||||
|
blacklists.addAll(query.list());
|
||||||
|
});
|
||||||
|
|
||||||
|
return blacklists;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(OGuild oGuild, Member member, String word) {
|
||||||
JpaUtils.runWithTransaction(session -> {
|
JpaUtils.runWithTransaction(session -> {
|
||||||
OBlacklist bl = getWordFromDb(word, guild.getId());
|
OBlacklist bl = getWordFromDb(oGuild.getGuildId(), word);
|
||||||
|
|
||||||
if (bl == null) {
|
if (bl == null) {
|
||||||
bl = new OBlacklist();
|
bl = new OBlacklist();
|
||||||
bl.setGuild(guildManager.getGuildEntity());
|
bl.setGuild(oGuild);
|
||||||
bl.setCreatorId(member.getId());
|
bl.setCreatorId(member.getId());
|
||||||
bl.setWord(word);
|
bl.setWord(word);
|
||||||
bl.setEnabled(true);
|
bl.setEnabled(true);
|
||||||
@@ -72,37 +80,18 @@ public class BlacklistRepository implements Repository<OBlacklist> {
|
|||||||
session.merge(bl);
|
session.merge(bl);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
guildManager.reloadBlacklistCache();
|
|
||||||
return CommandExecutionResult.success("Added the word [%s] to the blacklist.", word);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommandExecutionResult delete(Guild guild, String word) {
|
public void delete(OGuild oGuild, String word) {
|
||||||
GuildManager guildManager = GuildManager.getManager(guild);
|
|
||||||
|
|
||||||
JpaUtils.runWithTransaction(session -> {
|
JpaUtils.runWithTransaction(session -> {
|
||||||
OBlacklist bl = getWordFromDb(word, guild.getId());
|
OBlacklist bl = getWordFromDb(oGuild.getGuildId(), word);
|
||||||
if (bl != null) {
|
if (bl != null) {
|
||||||
session.remove(bl);
|
session.remove(bl);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
guildManager.reloadBlacklistCache();
|
|
||||||
return CommandExecutionResult.success("Removed the word [%s] to the blacklist.", word);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<OBlacklist> getBlacklistForGuild(Guild guild) {
|
private OBlacklist getWordFromDb(String guildId, String word) {
|
||||||
GuildManager manager = GuildManager.getManager(guild);
|
|
||||||
OGuild guildEntity = manager.getGuildEntity();
|
|
||||||
List<OBlacklist> blacklist = new ArrayList<>();
|
|
||||||
|
|
||||||
JpaUtils.getWithSession(guildEntity, guildEntity.getGuildId(), session -> blacklist.addAll(guildEntity.getBlacklists()));
|
|
||||||
|
|
||||||
return blacklist;
|
|
||||||
}
|
|
||||||
|
|
||||||
private OBlacklist getWordFromDb(String word, String guildId) {
|
|
||||||
try(Session session = SessionManager.getSessionFactory().openSession()) {
|
try(Session session = SessionManager.getSessionFactory().openSession()) {
|
||||||
Query<OBlacklist> query = session.createQuery("SELECT o FROM OBlacklist o WHERE upper(o.word) = upper(:word) AND o.guild.guildId = :guildId", OBlacklist.class);
|
Query<OBlacklist> query = session.createQuery("SELECT o FROM OBlacklist o WHERE upper(o.word) = upper(:word) AND o.guild.guildId = :guildId", OBlacklist.class);
|
||||||
query.setParameter("guildId", guildId);
|
query.setParameter("guildId", guildId);
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ package me.bobert.netneutrality.db.repository;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.GuildManager;
|
|
||||||
import me.bobert.netneutrality.core.db.SessionManager;
|
import me.bobert.netneutrality.core.db.SessionManager;
|
||||||
import me.bobert.netneutrality.db.Repository;
|
import me.bobert.netneutrality.db.Repository;
|
||||||
|
import me.bobert.netneutrality.db.entities.OGuild;
|
||||||
import me.bobert.netneutrality.db.entities.OLogMessage;
|
import me.bobert.netneutrality.db.entities.OLogMessage;
|
||||||
import me.bobert.netneutrality.utils.JpaUtils;
|
import me.bobert.netneutrality.utils.JpaUtils;
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
@@ -56,10 +56,11 @@ public class MessageRepository implements Repository<OLogMessage> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void log(GuildManager guildManager, MessageChannel channel, Member author, Message message) {
|
//TODO: move to a service
|
||||||
|
public void log(OGuild oGuild, MessageChannel channel, Member author, Message message) {
|
||||||
JpaUtils.runWithTransaction(session -> {
|
JpaUtils.runWithTransaction(session -> {
|
||||||
OLogMessage logMessage = new OLogMessage();
|
OLogMessage logMessage = new OLogMessage();
|
||||||
logMessage.setGuild(guildManager.getGuildEntity());
|
logMessage.setGuild(oGuild);
|
||||||
logMessage.setMessageId(message.getId());
|
logMessage.setMessageId(message.getId());
|
||||||
logMessage.setMessage(message.getContentRaw().trim());
|
logMessage.setMessage(message.getContentRaw().trim());
|
||||||
logMessage.setChannelId(channel.getId());
|
logMessage.setChannelId(channel.getId());
|
||||||
|
|||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package me.bobert.netneutrality.db.service;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
|
import me.bobert.netneutrality.db.entities.OBlacklist;
|
||||||
|
import me.bobert.netneutrality.db.entities.OGuild;
|
||||||
|
import me.bobert.netneutrality.db.repository.BlacklistRepository;
|
||||||
|
import me.bobert.netneutrality.utils.JpaUtils;
|
||||||
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class BlacklistService {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(BlacklistService.class);
|
||||||
|
private static final Map<String, Set<String>> blacklistCache = new ConcurrentHashMap<>();
|
||||||
|
private final BlacklistRepository blacklistRepository;
|
||||||
|
private final GuildService guildService;
|
||||||
|
|
||||||
|
public BlacklistService(GuildService guildService) {
|
||||||
|
this.blacklistRepository = new BlacklistRepository();
|
||||||
|
this.guildService = guildService;
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
reloadBlacklistCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reloadBlacklistCache() {
|
||||||
|
OGuild oGuild = guildService.getEntity();
|
||||||
|
logger.debug("Reloading blacklist cache for guild: {}", oGuild.getGuildId());
|
||||||
|
|
||||||
|
JpaUtils.getWithSession(oGuild, oGuild.getGuildId(), session -> {
|
||||||
|
Set<String> blacklistWords = oGuild.getBlacklists().stream()
|
||||||
|
.filter(OBlacklist::getEnabled)
|
||||||
|
.map(OBlacklist::getWord)
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
blacklistCache.put(oGuild.getGuildId(), blacklistWords);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandExecutionResult add(Member member, String word) {
|
||||||
|
try {
|
||||||
|
blacklistRepository.add(guildService.getEntity(), member, word);
|
||||||
|
reloadBlacklistCache();
|
||||||
|
|
||||||
|
return CommandExecutionResult.success("Added the word [%s] to the blacklist.", word);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return CommandExecutionResult.warn("Failed to add word to blacklist: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandExecutionResult delete(String word) {
|
||||||
|
try {
|
||||||
|
blacklistRepository.delete(guildService.getEntity(), word);
|
||||||
|
reloadBlacklistCache();
|
||||||
|
|
||||||
|
return CommandExecutionResult.success("Removed the word [%s] to the blacklist.", word);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return CommandExecutionResult.warn("Failed to remove word from blacklist: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<OBlacklist> getBlacklistForGuild() {
|
||||||
|
try {
|
||||||
|
return blacklistRepository.findByGuildId(guildService.getEntity().getGuildId());
|
||||||
|
} catch (Exception e) {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeGuildBlacklist() {
|
||||||
|
logger.info("Clearing blacklist cache for guild: {}", guildService.getEntity().getGuildId());
|
||||||
|
blacklistCache.remove(guildService.getEntity().getGuildId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isWordBlacklisted(String guildId, String word) {
|
||||||
|
Set<String> blacklistWords = blacklistCache.get(guildId);
|
||||||
|
return blacklistWords != null && blacklistWords.contains(word);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<String> getBlacklist(String guildId) {
|
||||||
|
return blacklistCache.getOrDefault(guildId, Collections.emptySet());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package me.bobert.netneutrality.db.service;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.db.entities.OGuild;
|
||||||
|
import me.bobert.netneutrality.db.repository.GuildRepository;
|
||||||
|
import me.bobert.netneutrality.utils.JpaUtils;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
public class GuildService {
|
||||||
|
private static final Map<String, GuildService> services = new ConcurrentHashMap<>();
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(GuildService.class);
|
||||||
|
private final GuildRepository guildRepository = new GuildRepository();
|
||||||
|
private final BlacklistService blacklistService;
|
||||||
|
private final GuildSettingService guildSettingService;
|
||||||
|
private OGuild oGuild;
|
||||||
|
|
||||||
|
private GuildService(String guildId) {
|
||||||
|
this.oGuild = findOrCreateGuild(guildId);
|
||||||
|
this.blacklistService = new BlacklistService(this);
|
||||||
|
this.guildSettingService = new GuildSettingService(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GuildService get(String guildId) {
|
||||||
|
return services.computeIfAbsent(guildId, GuildService::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GuildService get(Guild guild) {
|
||||||
|
return get(guild.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, GuildService> getServices() {
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OGuild getEntity() {
|
||||||
|
return oGuild;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlacklistService getBlacklistService() {
|
||||||
|
return blacklistService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GuildSettingService getGuildSettingService() {
|
||||||
|
return guildSettingService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public OGuild findOrCreateGuild(String guildId) {
|
||||||
|
OGuild guildEntity = guildRepository.findById(guildId);
|
||||||
|
if (guildEntity == null) {
|
||||||
|
logger.debug("Guild[{}] doesn't exist in DB, creating entity..", guildId);
|
||||||
|
|
||||||
|
guildEntity = new OGuild();
|
||||||
|
guildEntity.setGuildId(guildId);
|
||||||
|
guildRepository.save(guildEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return guildEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDebugEnabled() {
|
||||||
|
return oGuild.getDebug();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateDebugMode(boolean debug) {
|
||||||
|
JpaUtils.updateWithTransaction(oGuild, () -> oGuild.setDebug(debug));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deleteGuild() {
|
||||||
|
guildRepository.deleteBy(oGuild.getGuildId());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reloadEntity() {
|
||||||
|
this.oGuild = findOrCreateGuild(oGuild.getGuildId());
|
||||||
|
}
|
||||||
|
}
|
||||||
+14
-21
@@ -1,4 +1,4 @@
|
|||||||
package me.bobert.netneutrality.core;
|
package me.bobert.netneutrality.db.service;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) anthorob
|
* Copyright (c) anthorob
|
||||||
@@ -7,30 +7,23 @@ package me.bobert.netneutrality.core;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.db.GSettings;
|
import me.bobert.netneutrality.db.GSettings;
|
||||||
import me.bobert.netneutrality.db.entities.OGuild;
|
|
||||||
import me.bobert.netneutrality.db.entities.OGuildSetting;
|
import me.bobert.netneutrality.db.entities.OGuildSetting;
|
||||||
import me.bobert.netneutrality.db.repository.GuildSettingRepository;
|
import me.bobert.netneutrality.db.repository.GuildSettingRepository;
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
import org.slf4j.Logger;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
public class GuildConfigManager {
|
public class GuildSettingService {
|
||||||
|
private static final Logger logger = LoggerFactory.getLogger(GuildSettingService.class);
|
||||||
private static final Logger logger = LogManager.getLogger(GuildConfigManager.class);
|
|
||||||
private final Guild guild;
|
|
||||||
private final OGuild guildEntity;
|
|
||||||
private final GuildSettingRepository guildSettingRepository = new GuildSettingRepository();
|
private final GuildSettingRepository guildSettingRepository = new GuildSettingRepository();
|
||||||
|
private final GuildService guildService;
|
||||||
|
private final Map<String, String> settingsCache = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private final Map<String, String> settingsCache = new HashMap<>();
|
public GuildSettingService(GuildService guildService) {
|
||||||
|
this.guildService = guildService;
|
||||||
public GuildConfigManager(Guild guild, OGuild guildEntity) {
|
|
||||||
this.guild = guild;
|
|
||||||
this.guildEntity = guildEntity;
|
|
||||||
|
|
||||||
reloadSettingsCache();
|
reloadSettingsCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,19 +42,19 @@ public class GuildConfigManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setSettingValue(GSettings setting, String value) {
|
public void setSettingValue(GSettings setting, String value) {
|
||||||
guildSettingRepository.setSettingValue(guildEntity, setting, value);
|
guildSettingRepository.setSettingValue(guildService.getEntity(), setting, value);
|
||||||
|
|
||||||
reloadSettingsCache();
|
reloadSettingsCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reloadSettingsCache() {
|
public void reloadSettingsCache() {
|
||||||
logger.debug("Reloading settings cache for guild: {}", guild.getId());
|
logger.debug("Reloading settings cache for guild: {}", guildService.getEntity().getGuildId());
|
||||||
settingsCache.clear();
|
settingsCache.clear();
|
||||||
|
|
||||||
List<OGuildSetting> settings = guildSettingRepository.findByGuild(guild.getId());
|
|
||||||
|
List<OGuildSetting> settings = guildSettingRepository.findByGuild(guildService.getEntity().getGuildId());
|
||||||
for (OGuildSetting setting : settings) {
|
for (OGuildSetting setting : settings) {
|
||||||
settingsCache.put(setting.getName(), setting.getValue());
|
settingsCache.put(setting.getName(), setting.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -15,23 +15,27 @@ import org.apache.logging.log4j.Logger;
|
|||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
public class TaskService {
|
public class TaskService {
|
||||||
private static final Logger logger = LogManager.getLogger(TaskService.class);
|
private static final Logger logger = LogManager.getLogger(TaskService.class);
|
||||||
private final TaskManager taskManager;
|
|
||||||
private final TaskRepository taskRepository;
|
private final TaskRepository taskRepository;
|
||||||
|
|
||||||
public TaskService(TaskManager taskManager) {
|
public TaskService() {
|
||||||
this.taskManager = taskManager;
|
|
||||||
this.taskRepository = new TaskRepository();
|
this.taskRepository = new TaskRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadTasks() {
|
public void loadTasks() {
|
||||||
|
TaskRegistry.loadTaskConfig();
|
||||||
logger.info("Loading persistable tasks...");
|
logger.info("Loading persistable tasks...");
|
||||||
|
|
||||||
List<OTask> tasks = taskRepository.findAll();
|
List<OTask> tasks = taskRepository.findAll();
|
||||||
|
|
||||||
for (OTask oTask : tasks) {
|
for (OTask oTask : tasks) {
|
||||||
|
String taskId = oTask.getTaskId();
|
||||||
String taskClass = oTask.getTaskClass();
|
String taskClass = oTask.getTaskClass();
|
||||||
|
logger.debug("Trying to load task with class: {}", taskClass);
|
||||||
|
|
||||||
String payload = oTask.getPayload();
|
String payload = oTask.getPayload();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -42,20 +46,38 @@ public class TaskService {
|
|||||||
|
|
||||||
JSONObject json = new JSONObject(payload);
|
JSONObject json = new JSONObject(payload);
|
||||||
PersistableTask task = creator.fromPayload(json);
|
PersistableTask task = creator.fromPayload(json);
|
||||||
|
if (task == null) {
|
||||||
if (TaskManager.getInstance().exists(task.getId())) {
|
|
||||||
logger.error("Task with id {} already exists", task.getId());
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info("Loaded task: {}", task);
|
validateTask(task, oTask);
|
||||||
TaskManager.getInstance().getTaskMap().put(task.getId(), task);
|
TaskManager.getInstance().insertPersistableTask(task);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("Error loading task: {}", payload, e);
|
logger.error("Error loading task with ID {}: {}", taskId, e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void validateTask(PersistableTask task, OTask oTask) throws IllegalArgumentException {
|
||||||
|
String taskId = oTask.getTaskId();
|
||||||
|
String payload = oTask.getPayload();
|
||||||
|
|
||||||
|
if (!Objects.equals(taskId, task.getId())) {
|
||||||
|
throw new IllegalArgumentException("Task ID mismatch, expected: " + task.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject json = new JSONObject(payload);
|
||||||
|
String payloadId = json.getString("id");
|
||||||
|
if (!Objects.equals(payloadId, task.getId())) {
|
||||||
|
throw new IllegalArgumentException("Task payload ID mismatch, expected: " + task.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
String payloadLockId = json.getString("lockId");
|
||||||
|
if (!Objects.equals(payloadLockId, task.getLockId())) {
|
||||||
|
throw new IllegalArgumentException("Task lock ID mismatch, expected: " + task.getLockId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void persistTask(PersistableTask task) {
|
public void persistTask(PersistableTask task) {
|
||||||
String taskClass = task.getClass().getName();
|
String taskClass = task.getClass().getName();
|
||||||
if (TaskRegistry.getTaskCreator(taskClass) == null) {
|
if (TaskRegistry.getTaskCreator(taskClass) == null) {
|
||||||
@@ -67,10 +89,10 @@ public class TaskService {
|
|||||||
OTask oTask = taskRepository.findById(task.getId());
|
OTask oTask = taskRepository.findById(task.getId());
|
||||||
if (oTask == null) {
|
if (oTask == null) {
|
||||||
oTask = new OTask();
|
oTask = new OTask();
|
||||||
|
oTask.setTaskId(task.getId());
|
||||||
|
oTask.setTaskClass(task.getClass().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
oTask.setTaskId(task.getId());
|
|
||||||
oTask.setTaskClass(task.getClass().getName());
|
|
||||||
oTask.setPayload(task.toPayload());
|
oTask.setPayload(task.toPayload());
|
||||||
|
|
||||||
taskRepository.save(oTask);
|
taskRepository.save(oTask);
|
||||||
@@ -81,6 +103,5 @@ public class TaskService {
|
|||||||
TaskRepository taskRepository = new TaskRepository();
|
TaskRepository taskRepository = new TaskRepository();
|
||||||
|
|
||||||
taskRepository.deleteBy(task.getId());
|
taskRepository.deleteBy(task.getId());
|
||||||
taskManager.getTaskMap().remove(task.getId());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
package me.bobert.netneutrality.exceptions;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.Context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When thrown, this will call {@link Context#replyEphemeral(String)} with the specified message
|
||||||
|
*/
|
||||||
|
public class GracefulContextError extends RuntimeException {
|
||||||
|
|
||||||
|
public GracefulContextError(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,22 +7,30 @@ package me.bobert.netneutrality.hooks;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.context.command.MessageCommandContext;
|
import me.bobert.netneutrality.context.command.MessageCommandContext;
|
||||||
|
import me.bobert.netneutrality.context.command.MessageMenuContext;
|
||||||
import me.bobert.netneutrality.context.command.SlashCommandContext;
|
import me.bobert.netneutrality.context.command.SlashCommandContext;
|
||||||
|
import me.bobert.netneutrality.context.command.UserMenuContext;
|
||||||
import me.bobert.netneutrality.context.tasks.ButtonTaskContext;
|
import me.bobert.netneutrality.context.tasks.ButtonTaskContext;
|
||||||
import me.bobert.netneutrality.context.tasks.EmojiTaskContext;
|
import me.bobert.netneutrality.context.tasks.EmojiTaskContext;
|
||||||
|
import me.bobert.netneutrality.context.tasks.MessageTaskContext;
|
||||||
import me.bobert.netneutrality.context.tasks.ModalTaskContext;
|
import me.bobert.netneutrality.context.tasks.ModalTaskContext;
|
||||||
import me.bobert.netneutrality.core.GuildManager;
|
import me.bobert.netneutrality.core.LogHandler;
|
||||||
import me.bobert.netneutrality.core.command.CommandProcessor;
|
import me.bobert.netneutrality.core.command.CommandProcessor;
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
import me.bobert.netneutrality.core.context.Context;
|
import me.bobert.netneutrality.core.context.Context;
|
||||||
import me.bobert.netneutrality.core.context.TaskContext;
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
import me.bobert.netneutrality.core.tasks.TaskManager;
|
import me.bobert.netneutrality.core.tasks.TaskManager;
|
||||||
|
import me.bobert.netneutrality.db.service.GuildService;
|
||||||
import me.bobert.netneutrality.utils.Settings;
|
import me.bobert.netneutrality.utils.Settings;
|
||||||
|
import net.dv8tion.jda.api.entities.Message;
|
||||||
import net.dv8tion.jda.api.events.Event;
|
import net.dv8tion.jda.api.events.Event;
|
||||||
import net.dv8tion.jda.api.events.GenericEvent;
|
import net.dv8tion.jda.api.events.GenericEvent;
|
||||||
import net.dv8tion.jda.api.events.guild.GuildJoinEvent;
|
import net.dv8tion.jda.api.events.guild.GuildJoinEvent;
|
||||||
|
import net.dv8tion.jda.api.events.guild.GuildLeaveEvent;
|
||||||
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.MessageContextInteractionEvent;
|
||||||
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
|
||||||
|
import net.dv8tion.jda.api.events.interaction.command.UserContextInteractionEvent;
|
||||||
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
|
||||||
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
|
||||||
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
|
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
|
||||||
@@ -45,21 +53,30 @@ public class EventsHandler implements EventListener {
|
|||||||
if (event.getAuthor().isBot()) return null;
|
if (event.getAuthor().isBot()) return null;
|
||||||
if (event.isWebhookMessage()) return null;
|
if (event.isWebhookMessage()) return null;
|
||||||
|
|
||||||
String rawMessage = event.getMessage().getContentDisplay().trim();
|
Message message = event.getMessage();
|
||||||
|
String rawMessage = message.getContentDisplay().trim();
|
||||||
String prefix = Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$");
|
String prefix = Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$");
|
||||||
|
|
||||||
if (rawMessage.startsWith(prefix)) {
|
if (rawMessage.startsWith(prefix)) {
|
||||||
return new MessageCommandContext(event);
|
return new MessageCommandContext(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return new MessageTaskContext(event);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addListener(UserContextInteractionEvent.class, UserMenuContext::new);
|
||||||
|
addListener(MessageContextInteractionEvent.class, MessageMenuContext::new);
|
||||||
addListener(SlashCommandInteractionEvent.class, SlashCommandContext::new);
|
addListener(SlashCommandInteractionEvent.class, SlashCommandContext::new);
|
||||||
addListener(ModalInteractionEvent.class, ModalTaskContext::new);
|
addListener(ModalInteractionEvent.class, ModalTaskContext::new);
|
||||||
addListener(ButtonInteractionEvent.class, ButtonTaskContext::new);
|
addListener(ButtonInteractionEvent.class, ButtonTaskContext::new);
|
||||||
addListener(MessageReactionAddEvent.class, EmojiTaskContext::new);
|
addListener(MessageReactionAddEvent.class, EmojiTaskContext::new);
|
||||||
addGenericListener(GuildJoinEvent.class, event -> GuildManager.getManager(event.getGuild()).removeFromDB());
|
addGenericListener(GuildJoinEvent.class, event -> {
|
||||||
|
GuildService service = GuildService.get(event.getGuild());
|
||||||
|
|
||||||
|
if (service.isDebugEnabled()) {
|
||||||
|
LogHandler.sendMessageToDebugChannel(event.getGuild(), "Error logging enabled on this channel");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
addGenericListener(GuildLeaveEvent.class, event -> GuildService.get(event.getGuild()).deleteGuild());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -104,11 +121,6 @@ public class EventsHandler implements EventListener {
|
|||||||
Function<Event, Context> function = (Function<Event, Context>) listeners.get(eventClass);
|
Function<Event, Context> function = (Function<Event, Context>) listeners.get(eventClass);
|
||||||
Context context = function.apply(event);
|
Context context = function.apply(event);
|
||||||
|
|
||||||
if (context == null) {
|
|
||||||
logger.debug("Handling event {} by generic listener", event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context instanceof CommandContext commandContext) {
|
if (context instanceof CommandContext commandContext) {
|
||||||
handleCommand(commandContext);
|
handleCommand(commandContext);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -9,8 +9,7 @@ package me.bobert.netneutrality.http;
|
|||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import io.sentry.Breadcrumb;
|
import io.sentry.Breadcrumb;
|
||||||
import io.sentry.Sentry;
|
import io.sentry.Sentry;
|
||||||
import me.bobert.netneutrality.NetNeutrality;
|
import me.bobert.netneutrality.utils.Settings;
|
||||||
import me.bobert.netneutrality.core.LogHandler;
|
|
||||||
import okhttp3.*;
|
import okhttp3.*;
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@@ -18,15 +17,12 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Collections;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class WebModule {
|
public class WebModule {
|
||||||
private enum Method {
|
private enum Method {
|
||||||
GET, POST
|
PING, GET, POST
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String USER_AGENT = "Net Neutrality/%s (Discord bot, discord.gg/trAqhQ5Wsr)";
|
private static final String USER_AGENT = "Net Neutrality/%s (Discord bot, discord.gg/trAqhQ5Wsr)";
|
||||||
@@ -45,7 +41,6 @@ public class WebModule {
|
|||||||
private final long cacheExpirationMillis;
|
private final long cacheExpirationMillis;
|
||||||
private final OkHttpClient client;
|
private final OkHttpClient client;
|
||||||
private final Map<String, String> headers;
|
private final Map<String, String> headers;
|
||||||
private final Map<String, String> params;
|
|
||||||
private final Request request;
|
private final Request request;
|
||||||
private CachedResponse cachedResponse;
|
private CachedResponse cachedResponse;
|
||||||
|
|
||||||
@@ -54,9 +49,8 @@ public class WebModule {
|
|||||||
this.url = builder.url;
|
this.url = builder.url;
|
||||||
this.client = builder.client;
|
this.client = builder.client;
|
||||||
this.headers = builder.headers;
|
this.headers = builder.headers;
|
||||||
this.params = builder.params;
|
|
||||||
this.cacheExpirationMillis = builder.timeUnit.toMillis(builder.timeout);
|
this.cacheExpirationMillis = builder.timeUnit.toMillis(builder.timeout);
|
||||||
this.request = createRequest();
|
this.request = builder.request;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,19 +64,6 @@ public class WebModule {
|
|||||||
cache.keySet().removeIf(s -> s.contains(url));
|
cache.keySet().removeIf(s -> s.contains(url));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Request createRequest() {
|
|
||||||
Request.Builder builder = new Request.Builder().url(url);
|
|
||||||
builder.addHeader("User-Agent", String.format(USER_AGENT, NetNeutrality.VERSION));
|
|
||||||
headers.forEach(builder::addHeader);
|
|
||||||
|
|
||||||
if (method == Method.POST && !params.isEmpty()) {
|
|
||||||
RequestBody body = RequestBody.create(new Gson().toJson(params), MediaType.parse("application/json; charset=utf-8"));
|
|
||||||
builder.post(body);
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends the HTTP request based on the configuration and retrieves the response.
|
* Sends the HTTP request based on the configuration and retrieves the response.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -107,24 +88,37 @@ public class WebModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long startTime = System.currentTimeMillis();
|
||||||
try (Response response = client.newCall(request).execute()) {
|
try (Response response = client.newCall(request).execute()) {
|
||||||
|
long elapsedTime = System.currentTimeMillis() - startTime;
|
||||||
Sentry.addBreadcrumb(Breadcrumb.http(url, method.name(), response.code()));
|
Sentry.addBreadcrumb(Breadcrumb.http(url, method.name(), response.code()));
|
||||||
String body = response.body() != null ? response.body().string() : null;
|
String body = response.body() != null ? response.body().string() : null;
|
||||||
|
|
||||||
this.cachedResponse = new CachedResponse(
|
this.cachedResponse = new CachedResponse(
|
||||||
response.code(),
|
response.code(),
|
||||||
response.headers(),
|
response.headers(),
|
||||||
|
elapsedTime,
|
||||||
body,
|
body,
|
||||||
System.currentTimeMillis() + cacheExpirationMillis
|
System.currentTimeMillis() + cacheExpirationMillis,
|
||||||
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
if (method == Method.GET && cacheKey != null) {
|
|
||||||
log.debug("Caching request: {}", cacheKey);
|
|
||||||
cache.put(cacheKey, this.cachedResponse);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
long elapsedTime = System.currentTimeMillis() - startTime;
|
||||||
|
log.error("Request failed: {}", e.getMessage(), e);
|
||||||
Sentry.captureException(e);
|
Sentry.captureException(e);
|
||||||
throw new RuntimeException("Failed to execute request", e);
|
|
||||||
|
this.cachedResponse = new CachedResponse(
|
||||||
|
0, // Status code 0 indicates a failure
|
||||||
|
new Headers.Builder().build(),
|
||||||
|
elapsedTime,
|
||||||
|
null,
|
||||||
|
System.currentTimeMillis() + cacheExpirationMillis,
|
||||||
|
e.getMessage()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method == Method.GET && cacheKey != null) {
|
||||||
|
cache.put(cacheKey, this.cachedResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
@@ -145,28 +139,27 @@ public class WebModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public JSONObject getAsObject() {
|
public JSONObject getAsObject() {
|
||||||
try {
|
return cachedResponse.getBody()
|
||||||
return new JSONObject(cachedResponse.getBody());
|
.map(JSONObject::new)
|
||||||
} catch (NullPointerException e) {
|
.orElseThrow(() -> new IllegalStateException("No body available for this response"));
|
||||||
LogHandler.log(e);
|
|
||||||
return new JSONObject();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSONArray getAsArray() {
|
public JSONArray getAsArray() {
|
||||||
try {
|
return cachedResponse.getBody()
|
||||||
return new JSONArray(cachedResponse.getBody());
|
.map(JSONArray::new)
|
||||||
} catch (NullPointerException e) {
|
.orElseThrow(() -> new IllegalStateException("No body available for this response"));
|
||||||
LogHandler.log(e);
|
|
||||||
return new JSONArray();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRawData() {
|
public String getRawData() {
|
||||||
return cachedResponse.getBody();
|
return cachedResponse.getBody().orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return cachedResponse != null && cachedResponse.getStatusCode() >= 200 && cachedResponse.getStatusCode() < 400;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
private Request request;
|
||||||
private final String url;
|
private final String url;
|
||||||
private Method method = Method.GET;
|
private Method method = Method.GET;
|
||||||
private long timeout = 5;
|
private long timeout = 5;
|
||||||
@@ -187,6 +180,11 @@ public class WebModule {
|
|||||||
this.url = url;
|
this.url = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder ping() {
|
||||||
|
this.method = Method.PING;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the request to use the HTTP GET method.
|
* Configures the request to use the HTTP GET method.
|
||||||
*
|
*
|
||||||
@@ -224,7 +222,7 @@ public class WebModule {
|
|||||||
/**
|
/**
|
||||||
* Configures caching for the request.
|
* Configures caching for the request.
|
||||||
*
|
*
|
||||||
* @param timeout the cache expiration duration
|
* @param timeout the cache expiration duration
|
||||||
* @param timeUnit the unit of time for the cache expiration
|
* @param timeUnit the unit of time for the cache expiration
|
||||||
* @return the current {@code Builder} instance for chaining
|
* @return the current {@code Builder} instance for chaining
|
||||||
*/
|
*/
|
||||||
@@ -234,10 +232,17 @@ public class WebModule {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder timeout(long timeout, TimeUnit timeUnit) {
|
||||||
|
this.connectTimeout = timeUnit.toMillis(timeout);
|
||||||
|
this.readTimeout = timeUnit.toMillis(timeout);
|
||||||
|
this.writeTimeout = timeUnit.toMillis(timeout);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the connection timeout for the HTTP client.
|
* Sets the connection timeout for the HTTP client.
|
||||||
*
|
*
|
||||||
* @param timeout the timeout duration
|
* @param timeout the timeout duration
|
||||||
* @param timeUnit the unit of time for the timeout
|
* @param timeUnit the unit of time for the timeout
|
||||||
* @return the current {@code Builder} instance for chaining
|
* @return the current {@code Builder} instance for chaining
|
||||||
*/
|
*/
|
||||||
@@ -249,7 +254,7 @@ public class WebModule {
|
|||||||
/**
|
/**
|
||||||
* Sets the read timeout for the HTTP client.
|
* Sets the read timeout for the HTTP client.
|
||||||
*
|
*
|
||||||
* @param timeout the timeout duration
|
* @param timeout the timeout duration
|
||||||
* @param timeUnit the unit of time for the timeout
|
* @param timeUnit the unit of time for the timeout
|
||||||
* @return the current {@code Builder} instance for chaining
|
* @return the current {@code Builder} instance for chaining
|
||||||
*/
|
*/
|
||||||
@@ -261,7 +266,7 @@ public class WebModule {
|
|||||||
/**
|
/**
|
||||||
* Sets the write timeout for the HTTP client.
|
* Sets the write timeout for the HTTP client.
|
||||||
*
|
*
|
||||||
* @param timeout the timeout duration
|
* @param timeout the timeout duration
|
||||||
* @param timeUnit the unit of time for the timeout
|
* @param timeUnit the unit of time for the timeout
|
||||||
* @return the current {@code Builder} instance for chaining
|
* @return the current {@code Builder} instance for chaining
|
||||||
*/
|
*/
|
||||||
@@ -270,6 +275,27 @@ public class WebModule {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Request createRequest() {
|
||||||
|
Request.Builder builder = new Request.Builder().url(url);
|
||||||
|
builder.addHeader("User-Agent", String.format(USER_AGENT, Settings.getVersion()));
|
||||||
|
headers.forEach(builder::addHeader);
|
||||||
|
|
||||||
|
if (method == Method.PING) {
|
||||||
|
builder.head();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method == Method.GET) {
|
||||||
|
builder.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method == Method.POST && !params.isEmpty()) {
|
||||||
|
RequestBody body = RequestBody.create(new Gson().toJson(params), MediaType.parse("application/json; charset=utf-8"));
|
||||||
|
builder.post(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Builds and returns a configured {@link WebModule} instance.
|
* Builds and returns a configured {@link WebModule} instance.
|
||||||
*
|
*
|
||||||
@@ -281,6 +307,7 @@ public class WebModule {
|
|||||||
.writeTimeout(writeTimeout, TimeUnit.MILLISECONDS)
|
.writeTimeout(writeTimeout, TimeUnit.MILLISECONDS)
|
||||||
.readTimeout(readTimeout, TimeUnit.MILLISECONDS)
|
.readTimeout(readTimeout, TimeUnit.MILLISECONDS)
|
||||||
.build();
|
.build();
|
||||||
|
this.request = createRequest();
|
||||||
|
|
||||||
return new WebModule(this);
|
return new WebModule(this);
|
||||||
}
|
}
|
||||||
@@ -289,14 +316,18 @@ public class WebModule {
|
|||||||
private static class CachedResponse {
|
private static class CachedResponse {
|
||||||
private final int statusCode;
|
private final int statusCode;
|
||||||
private final Headers headers;
|
private final Headers headers;
|
||||||
|
private final long elapsedTime;
|
||||||
private final String body;
|
private final String body;
|
||||||
private final long expirationTime;
|
private final long expirationTime;
|
||||||
|
private final String errorMessage;
|
||||||
|
|
||||||
public CachedResponse(int statusCode, Headers headers, String body, long expirationTime) {
|
public CachedResponse(int statusCode, Headers headers, long elapsedTime, String body, long expirationTime, String errorMessage) {
|
||||||
this.statusCode = statusCode;
|
this.statusCode = statusCode;
|
||||||
this.headers = headers;
|
this.headers = headers;
|
||||||
|
this.elapsedTime = elapsedTime;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
this.expirationTime = expirationTime;
|
this.expirationTime = expirationTime;
|
||||||
|
this.errorMessage = errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isExpired() {
|
public boolean isExpired() {
|
||||||
@@ -311,8 +342,16 @@ public class WebModule {
|
|||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getBody() {
|
public long getElapsedTime() {
|
||||||
return body;
|
return elapsedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getBody() {
|
||||||
|
return Optional.ofNullable(body);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<String> getErrorMessage() {
|
||||||
|
return Optional.ofNullable(errorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ package me.bobert.netneutrality.http.server;
|
|||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.sun.net.httpserver.HttpExchange;
|
import com.sun.net.httpserver.HttpExchange;
|
||||||
import me.bobert.netneutrality.NetNeutrality;
|
import me.bobert.netneutrality.NetNeutrality;
|
||||||
import me.bobert.netneutrality.core.GuildManager;
|
import me.bobert.netneutrality.db.service.GuildService;
|
||||||
import net.dv8tion.jda.api.entities.Guild;
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@@ -49,8 +49,8 @@ public class ActionHandler extends AbstractHandler {
|
|||||||
|
|
||||||
Guild guild = NetNeutrality.getInstance().getJDA().getGuildById(guildId);
|
Guild guild = NetNeutrality.getInstance().getJDA().getGuildById(guildId);
|
||||||
if (guild != null) {
|
if (guild != null) {
|
||||||
GuildManager manager = GuildManager.getManager(guild);
|
GuildService service = GuildService.get(guild);
|
||||||
Map<String, String> settings = manager.getGuildConfigManager().getSettings();
|
Map<String, String> settings = service.getGuildSettingService().getSettings();
|
||||||
JSONObject setting = new JSONObject(settings);
|
JSONObject setting = new JSONObject(settings);
|
||||||
|
|
||||||
json.put("status", "ok");
|
json.put("status", "ok");
|
||||||
@@ -68,13 +68,8 @@ public class ActionHandler extends AbstractHandler {
|
|||||||
String[] args = exchange.getRequestURI().toString().split("/");
|
String[] args = exchange.getRequestURI().toString().split("/");
|
||||||
String guildId = args[args.length - 1];
|
String guildId = args[args.length - 1];
|
||||||
|
|
||||||
Guild guild = NetNeutrality.getInstance().getJDA().getGuildById(guildId);
|
|
||||||
boolean ok = true; //TODO: add bool
|
boolean ok = true; //TODO: add bool
|
||||||
if (guild == null)
|
GuildService.get(guildId).getGuildSettingService().reloadSettingsCache();
|
||||||
GuildManager.getManager(guildId).getGuildConfigManager().reloadSettingsCache();
|
|
||||||
else
|
|
||||||
GuildManager.getManager(guild).getGuildConfigManager().reloadSettingsCache();
|
|
||||||
|
|
||||||
|
|
||||||
json.put("status", String.valueOf(ok));
|
json.put("status", String.valueOf(ok));
|
||||||
sendJsonResponse(exchange, new Gson().toJson(json));
|
sendJsonResponse(exchange, new Gson().toJson(json));
|
||||||
|
|||||||
@@ -13,11 +13,11 @@ import com.sedmelluq.discord.lavaplayer.source.http.HttpAudioSourceManager;
|
|||||||
import com.sedmelluq.discord.lavaplayer.source.soundcloud.SoundCloudAudioSourceManager;
|
import com.sedmelluq.discord.lavaplayer.source.soundcloud.SoundCloudAudioSourceManager;
|
||||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||||
import dev.lavalink.youtube.YoutubeAudioSourceManager;
|
import dev.lavalink.youtube.YoutubeAudioSourceManager;
|
||||||
import me.bobert.netneutrality.core.GuildManager;
|
|
||||||
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
import me.bobert.netneutrality.core.command.CommandExecutionResult;
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
import me.bobert.netneutrality.core.thread.ThreadManager;
|
import me.bobert.netneutrality.core.thread.ThreadManager;
|
||||||
import me.bobert.netneutrality.db.GSettings;
|
import me.bobert.netneutrality.db.GSettings;
|
||||||
|
import me.bobert.netneutrality.db.service.GuildService;
|
||||||
import me.bobert.netneutrality.music.source.UdioAudioSourceManager;
|
import me.bobert.netneutrality.music.source.UdioAudioSourceManager;
|
||||||
import me.bobert.netneutrality.thread.MusicManagerCleanupThread;
|
import me.bobert.netneutrality.thread.MusicManagerCleanupThread;
|
||||||
import me.bobert.netneutrality.thread.MusicPlayerAutoDisconnectThread;
|
import me.bobert.netneutrality.thread.MusicPlayerAutoDisconnectThread;
|
||||||
@@ -47,7 +47,7 @@ public class MusicManager {
|
|||||||
this.guild = guild;
|
this.guild = guild;
|
||||||
this.player = playerManager.createPlayer();
|
this.player = playerManager.createPlayer();
|
||||||
this.scheduler = new TrackScheduler(this);
|
this.scheduler = new TrackScheduler(this);
|
||||||
this.baseVolume = Integer.parseInt(GuildManager.getManager(guild).getGuildConfigManager().getSetting(GSettings.MUSIC_VOLUME));
|
this.baseVolume = Integer.parseInt(GuildService.get(guild).getGuildSettingService().getSetting(GSettings.MUSIC_VOLUME));
|
||||||
|
|
||||||
guild.getAudioManager().setSendingHandler(new AudioPlayerSendHandler(player));
|
guild.getAudioManager().setSendingHandler(new AudioPlayerSendHandler(player));
|
||||||
player.addListener(scheduler);
|
player.addListener(scheduler);
|
||||||
@@ -104,8 +104,9 @@ public class MusicManager {
|
|||||||
return guild;
|
return guild;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isNotConnectedTo(AudioChannel vChannel) {
|
public synchronized boolean isNotConnectedTo(AudioChannel vChannel) {
|
||||||
return vChannel == null || !vChannel.equals(guild.getAudioManager().getConnectedChannel());
|
AudioChannel current = guild.getAudioManager().getConnectedChannel();
|
||||||
|
return vChannel == null || current == null || !vChannel.getId().equals(current.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void connect(AudioChannel audioChannel) {
|
public synchronized void connect(AudioChannel audioChannel) {
|
||||||
@@ -120,7 +121,7 @@ public class MusicManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void disconnect() {
|
public synchronized void disconnect() {
|
||||||
if (guild != null && guild.getAudioManager().getConnectedChannel() != null) {
|
if (guild.getAudioManager().getConnectedChannel() != null) {
|
||||||
logger.debug("Guild {}: disconnecting from voice channel {}", guild.getId(), guild.getAudioManager().getConnectedChannel().getId());
|
logger.debug("Guild {}: disconnecting from voice channel {}", guild.getId(), guild.getAudioManager().getConnectedChannel().getId());
|
||||||
player.stopTrack();
|
player.stopTrack();
|
||||||
guild.getAudioManager().closeAudioConnection();
|
guild.getAudioManager().closeAudioConnection();
|
||||||
@@ -129,8 +130,8 @@ public class MusicManager {
|
|||||||
scheduler.detachContext();
|
scheduler.detachContext();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConnected() {
|
public synchronized boolean isConnected() {
|
||||||
return guild != null && guild.getAudioManager().getConnectedChannel() != null;
|
return guild.getAudioManager().getConnectedChannel() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized AudioTrack getCurrentlyPlaying() {
|
public synchronized AudioTrack getCurrentlyPlaying() {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ public class TrackLoadingHandler implements AudioLoadResultHandler {
|
|||||||
public void playlistLoaded(AudioPlaylist playlist) {
|
public void playlistLoaded(AudioPlaylist playlist) {
|
||||||
logger.debug("Guild {}: playlist loaded: {}", guild.getId(), identifier);
|
logger.debug("Guild {}: playlist loaded: {}", guild.getId(), identifier);
|
||||||
|
|
||||||
TaskManager.getInstance().insert(context, new LoadPlaylistTask(playlist, type));
|
new LoadPlaylistTask(playlist, type).insert(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
package me.bobert.netneutrality.tasks;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.tasks.AbstractTask;
|
|
||||||
|
|
||||||
import java.time.OffsetDateTime;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public abstract class ButtonTask extends AbstractTask {
|
|
||||||
private final UUID id = UUID.randomUUID();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return id.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean oneshot() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isExpired() {
|
|
||||||
return getCreationTime().plusMinutes(15).isBefore(OffsetDateTime.now());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
package me.bobert.netneutrality.tasks;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.tasks.AbstractPersistableTask;
|
|
||||||
|
|
||||||
public abstract class EmojiTask extends AbstractPersistableTask {
|
|
||||||
private final String guildId;
|
|
||||||
private final String messageId;
|
|
||||||
|
|
||||||
public EmojiTask(String guildId, String messageId) {
|
|
||||||
this.guildId = guildId;
|
|
||||||
this.messageId = messageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getGuildId() {
|
|
||||||
return guildId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMessageId() {
|
|
||||||
return messageId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getId() {
|
|
||||||
return String.format("%s:%s", getGuildId(), getMessageId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,35 +6,21 @@ package me.bobert.netneutrality.tasks.emoji;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.context.TaskContext;
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
import me.bobert.netneutrality.tasks.EmojiTask;
|
import me.bobert.netneutrality.core.tasks.impl.EmojiTask;
|
||||||
|
import me.bobert.netneutrality.utils.GuildUtils;
|
||||||
import net.dv8tion.jda.api.entities.Role;
|
import net.dv8tion.jda.api.entities.Role;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class EmojiReactionRoleTask extends EmojiTask {
|
public class EmojiReactionRoleTask extends EmojiTask {
|
||||||
private final Map<String, String> emojiMapping;
|
public EmojiReactionRoleTask(String guildId, String channelId, String messageId) {
|
||||||
|
|
||||||
public EmojiReactionRoleTask(String guildId, String messageId) {
|
|
||||||
super(guildId, messageId);
|
super(guildId, messageId);
|
||||||
this.emojiMapping = new HashMap<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// constructor for auto loading
|
||||||
public EmojiReactionRoleTask(String guildId, String messageId, Map<String, String> emojiMapping) {
|
public EmojiReactionRoleTask(String guildId, String messageId, Map<String, String> emojiMapping) {
|
||||||
super(guildId, messageId);
|
super(guildId, messageId);
|
||||||
this.emojiMapping = emojiMapping;
|
getEmojiMapping().putAll(emojiMapping);
|
||||||
}
|
|
||||||
|
|
||||||
public void addEmoji(String emoji, String role) {
|
|
||||||
emojiMapping.put(emoji, role);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeEmoji(String emoji) {
|
|
||||||
emojiMapping.remove(emoji);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, String> getEmojiMapping() {
|
|
||||||
return emojiMapping;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -43,15 +29,17 @@ public class EmojiReactionRoleTask extends EmojiTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(TaskContext context) {
|
public Lifecycle execute(TaskContext context) {
|
||||||
String emoji = (String) context.getContextData("emoji-code");
|
String emoji = (String) context.getContextData("emoji-code");
|
||||||
|
|
||||||
if (getEmojiMapping().containsKey(emoji)) {
|
if (getEmojiMapping().containsKey(emoji)) {
|
||||||
Role role = context.getGuild().getRoleById(getEmojiMapping().get(emoji));
|
Role role = context.getGuild().getRoleById(getEmojiMapping().get(emoji));
|
||||||
|
|
||||||
if (role != null) {
|
if (role != null) {
|
||||||
context.getGuildManager().addRolesToMember(context.getGuildUser(), role);
|
GuildUtils.addRolesToMember(context, role);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Lifecycle.KEEP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package me.bobert.netneutrality.tasks.generic;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.trait.WithChildren;
|
||||||
|
import me.bobert.netneutrality.core.tasks.trait.WithParent;
|
||||||
|
import me.bobert.netneutrality.core.tasks.impl.ButtonTask;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A generic button task that represents a button to accept a choice.
|
||||||
|
* @param <P> the type of the parent task, which must implement WithChildren
|
||||||
|
*/
|
||||||
|
public class AcceptButton<P extends WithChildren> extends ButtonTask implements WithParent<P> {
|
||||||
|
private final P parentTask;
|
||||||
|
private final Function<TaskContext, Lifecycle> function;
|
||||||
|
|
||||||
|
protected AcceptButton(P parentTask, ButtonStyle buttonStyle, String buttonLabel, Function<TaskContext, Lifecycle> function) {
|
||||||
|
super(buttonStyle, buttonLabel);
|
||||||
|
this.parentTask = parentTask;
|
||||||
|
this.function = function;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <P extends WithChildren> AcceptButton<P> createPrimaryButton(P parentTask, String buttonLabel, Function<TaskContext, Lifecycle> function) {
|
||||||
|
return new AcceptButton<>(parentTask, ButtonStyle.PRIMARY, buttonLabel, function);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <P extends WithChildren> AcceptButton<P> createSuccessButton(P parentTask, String buttonLabel, Function<TaskContext, Lifecycle> function) {
|
||||||
|
return new AcceptButton<>(parentTask, ButtonStyle.SUCCESS, buttonLabel, function);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public P getParentTask() {
|
||||||
|
return parentTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "accept-" + parentTask.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Lifecycle execute(TaskContext context) {
|
||||||
|
return function.apply(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package me.bobert.netneutrality.tasks.generic;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
|
import me.bobert.netneutrality.core.tasks.trait.NotAcknowledgeable;
|
||||||
|
import me.bobert.netneutrality.core.tasks.trait.WithChildren;
|
||||||
|
import me.bobert.netneutrality.core.tasks.trait.WithParent;
|
||||||
|
import me.bobert.netneutrality.core.tasks.impl.ButtonTask;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class CancelButton<P extends WithChildren> extends ButtonTask implements WithParent<P>, NotAcknowledgeable {
|
||||||
|
private final P parentTask;
|
||||||
|
private final Function<TaskContext, Lifecycle> function;
|
||||||
|
|
||||||
|
protected CancelButton(P parentTask, ButtonStyle buttonStyle, String buttonLabel, Function<TaskContext, Lifecycle> function) {
|
||||||
|
super(buttonStyle, buttonLabel);
|
||||||
|
this.parentTask = parentTask;
|
||||||
|
this.function = function;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <P extends WithChildren> CancelButton<P> create(P parentTask, String buttonLabel, Function<TaskContext, Lifecycle> function) {
|
||||||
|
return new CancelButton<>(parentTask, ButtonStyle.DANGER, buttonLabel, function);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public P getParentTask() {
|
||||||
|
return parentTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "cancel-" + parentTask.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Lifecycle execute(TaskContext context) {
|
||||||
|
return function.apply(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,10 +6,11 @@ package me.bobert.netneutrality.tasks.git;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import io.sentry.Sentry;
|
import io.sentry.Sentry;
|
||||||
|
import me.bobert.netneutrality.context.command.SlashCommandContext;
|
||||||
import me.bobert.netneutrality.core.LogHandler;
|
import me.bobert.netneutrality.core.LogHandler;
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
import me.bobert.netneutrality.core.context.TaskContext;
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
import me.bobert.netneutrality.tasks.ModalTask;
|
import me.bobert.netneutrality.core.tasks.impl.ModalTask;
|
||||||
import me.bobert.netneutrality.utils.Settings;
|
import me.bobert.netneutrality.utils.Settings;
|
||||||
import net.dv8tion.jda.api.entities.User;
|
import net.dv8tion.jda.api.entities.User;
|
||||||
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
||||||
@@ -27,8 +28,8 @@ public class ReportIssueTask extends ModalTask {
|
|||||||
private static final Logger logger = LogManager.getLogger(ReportIssueTask.class);
|
private static final Logger logger = LogManager.getLogger(ReportIssueTask.class);
|
||||||
private final boolean insertable;
|
private final boolean insertable;
|
||||||
|
|
||||||
public ReportIssueTask(boolean insertable) {
|
public ReportIssueTask(CommandContext context) {
|
||||||
this.insertable = insertable;
|
this.insertable = context instanceof SlashCommandContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -63,7 +64,7 @@ public class ReportIssueTask extends ModalTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(TaskContext context) {
|
public Lifecycle execute(TaskContext context) {
|
||||||
context.ephemeral();
|
context.ephemeral();
|
||||||
|
|
||||||
String gitlabHost = Settings.getInstance().get("GITLAB_HOST");
|
String gitlabHost = Settings.getInstance().get("GITLAB_HOST");
|
||||||
@@ -73,7 +74,7 @@ public class ReportIssueTask extends ModalTask {
|
|||||||
if (gitlabHost.isBlank() || gitlabToken.isBlank() || projectId.isBlank()) {
|
if (gitlabHost.isBlank() || gitlabToken.isBlank() || projectId.isBlank()) {
|
||||||
logger.error("Gitlab is not configured in the bot settings, can't execute task");
|
logger.error("Gitlab is not configured in the bot settings, can't execute task");
|
||||||
context.reply("Command is not configured, failed to create issue.");
|
context.reply("Command is not configured, failed to create issue.");
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModalMapping modalTitle = getValue("title", context);
|
ModalMapping modalTitle = getValue("title", context);
|
||||||
@@ -103,5 +104,7 @@ public class ReportIssueTask extends ModalTask {
|
|||||||
context.reply("Failed to create issue.");
|
context.reply("Failed to create issue.");
|
||||||
LogHandler.log(e);
|
LogHandler.log(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Lifecycle.REMOVE_SELF;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@ package me.bobert.netneutrality.tasks.help;
|
|||||||
|
|
||||||
import me.bobert.netneutrality.core.context.TaskContext;
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
import me.bobert.netneutrality.core.tasks.AbstractBuiltInTask;
|
import me.bobert.netneutrality.core.tasks.AbstractBuiltInTask;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||||
|
|
||||||
public class BotInfo extends AbstractBuiltInTask {
|
public class BotInfo extends AbstractBuiltInTask {
|
||||||
|
|
||||||
@@ -16,7 +17,12 @@ public class BotInfo extends AbstractBuiltInTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(TaskContext context) {
|
public Button createButton() {
|
||||||
|
return Button.primary(getId(), "Info");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void executeTask(TaskContext context) {
|
||||||
context.sendMessageInPrivate("For information about the bot and to follow the development, " +
|
context.sendMessageInPrivate("For information about the bot and to follow the development, " +
|
||||||
"you can join the discord here: https://discord.gg/udVJwTgV");
|
"you can join the discord here: https://discord.gg/udVJwTgV");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import me.bobert.netneutrality.core.context.TaskContext;
|
|||||||
import me.bobert.netneutrality.core.tasks.AbstractBuiltInTask;
|
import me.bobert.netneutrality.core.tasks.AbstractBuiltInTask;
|
||||||
import me.bobert.netneutrality.utils.MessageFunction;
|
import me.bobert.netneutrality.utils.MessageFunction;
|
||||||
import me.bobert.netneutrality.utils.Settings;
|
import me.bobert.netneutrality.utils.Settings;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||||
|
|
||||||
public class CommandHelp extends AbstractBuiltInTask {
|
public class CommandHelp extends AbstractBuiltInTask {
|
||||||
|
|
||||||
@@ -18,7 +19,12 @@ public class CommandHelp extends AbstractBuiltInTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(TaskContext context) {
|
public Button createButton() {
|
||||||
|
return Button.primary(getId(), "Commands");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void executeTask(TaskContext context) {
|
||||||
context.replyEphemeral("The prefix for the bot is " + Settings.getInstance().getPrefix());
|
context.replyEphemeral("The prefix for the bot is " + Settings.getInstance().getPrefix());
|
||||||
context.replyEphemeral(MessageFunction.buildHelpMessage());
|
context.replyEphemeral(MessageFunction.buildHelpMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ package me.bobert.netneutrality.tasks.help;
|
|||||||
|
|
||||||
import me.bobert.netneutrality.core.context.TaskContext;
|
import me.bobert.netneutrality.core.context.TaskContext;
|
||||||
import me.bobert.netneutrality.core.tasks.AbstractBuiltInTask;
|
import me.bobert.netneutrality.core.tasks.AbstractBuiltInTask;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.Button;
|
||||||
|
|
||||||
public class ReportBug extends AbstractBuiltInTask {
|
public class ReportBug extends AbstractBuiltInTask {
|
||||||
|
|
||||||
@@ -16,7 +17,12 @@ public class ReportBug extends AbstractBuiltInTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(TaskContext context) {
|
public Button createButton() {
|
||||||
|
return Button.danger(getId(), "Report bug");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void executeTask(TaskContext context) {
|
||||||
context.sendMessageInPrivate("To report a bug with the bot, you can either use the /report-bug command " +
|
context.sendMessageInPrivate("To report a bug with the bot, you can either use the /report-bug command " +
|
||||||
"or join the discord here: https://discord.gg/udVJwTgV");
|
"or join the discord here: https://discord.gg/udVJwTgV");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,61 +0,0 @@
|
|||||||
package me.bobert.netneutrality.tasks.music;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
|
||||||
import me.bobert.netneutrality.core.context.TaskContext;
|
|
||||||
import me.bobert.netneutrality.core.tasks.trait.WithParent;
|
|
||||||
import me.bobert.netneutrality.music.MusicManager;
|
|
||||||
import me.bobert.netneutrality.music.SourceFinder;
|
|
||||||
import me.bobert.netneutrality.tasks.ButtonTask;
|
|
||||||
import me.bobert.netneutrality.utils.MessageFunction;
|
|
||||||
import me.bobert.netneutrality.utils.MusicUtils;
|
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class AddAllTracksTask extends ButtonTask implements WithParent<LoadPlaylistTask> {
|
|
||||||
private final LoadPlaylistTask parentTask;
|
|
||||||
List<AudioTrack> tracks;
|
|
||||||
|
|
||||||
public AddAllTracksTask(LoadPlaylistTask parentTask, List<AudioTrack> tracks) {
|
|
||||||
this.parentTask = parentTask;
|
|
||||||
this.tracks = tracks;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "add-all-tracks";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void replyToInteraction(CommandContext context) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(TaskContext context) {
|
|
||||||
MusicManager manager = MusicManager.get(context.getGuild());
|
|
||||||
Member member = context.getGuildUser();
|
|
||||||
|
|
||||||
if (MusicUtils.connect(context)) {
|
|
||||||
manager.getQueueManager().queue(tracks, member);
|
|
||||||
|
|
||||||
context.reply(MessageFunction.buildAddingPlaylistMessage(tracks, parentTask.getPlaylist().getName(), member, SourceFinder.AudioSourceType.Youtube));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LoadPlaylistTask getParentTask() {
|
|
||||||
return parentTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldKeepChilds() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
package me.bobert.netneutrality.tasks.music;
|
|
||||||
/*
|
|
||||||
* Copyright (c) anthorob
|
|
||||||
*
|
|
||||||
* https://discord.gg/qUa6zNFZsd
|
|
||||||
*/
|
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
|
||||||
import me.bobert.netneutrality.core.context.TaskContext;
|
|
||||||
import me.bobert.netneutrality.core.tasks.trait.NotAcknowledgeable;
|
|
||||||
import me.bobert.netneutrality.core.tasks.trait.WithParent;
|
|
||||||
import me.bobert.netneutrality.tasks.ButtonTask;
|
|
||||||
|
|
||||||
public class CancelLoadTask extends ButtonTask implements WithParent<LoadPlaylistTask>, NotAcknowledgeable {
|
|
||||||
private final LoadPlaylistTask parentTask;
|
|
||||||
|
|
||||||
public CancelLoadTask(LoadPlaylistTask parentTask) {
|
|
||||||
this.parentTask = parentTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getName() {
|
|
||||||
return "cancel-playlist-loading";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void replyToInteraction(CommandContext context) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LoadPlaylistTask getParentTask() {
|
|
||||||
return parentTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldKeepChilds() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(TaskContext context) {
|
|
||||||
// Interaction will get removed after execution
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,16 +8,20 @@ package me.bobert.netneutrality.tasks.music;
|
|||||||
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
|
||||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
import me.bobert.netneutrality.core.context.CommandContext;
|
||||||
import me.bobert.netneutrality.core.context.TaskContext;
|
|
||||||
import me.bobert.netneutrality.core.tasks.Task;
|
import me.bobert.netneutrality.core.tasks.Task;
|
||||||
|
import me.bobert.netneutrality.core.tasks.TaskManager;
|
||||||
import me.bobert.netneutrality.core.tasks.trait.Deletable;
|
import me.bobert.netneutrality.core.tasks.trait.Deletable;
|
||||||
import me.bobert.netneutrality.core.tasks.trait.WithChildren;
|
import me.bobert.netneutrality.core.tasks.trait.WithChildren;
|
||||||
import me.bobert.netneutrality.db.GSettings;
|
import me.bobert.netneutrality.db.GSettings;
|
||||||
|
import me.bobert.netneutrality.db.service.GuildService;
|
||||||
import me.bobert.netneutrality.music.MusicManager;
|
import me.bobert.netneutrality.music.MusicManager;
|
||||||
import me.bobert.netneutrality.music.SourceFinder;
|
import me.bobert.netneutrality.music.SourceFinder;
|
||||||
import me.bobert.netneutrality.tasks.ButtonTask;
|
import me.bobert.netneutrality.core.tasks.impl.ButtonTask;
|
||||||
|
import me.bobert.netneutrality.tasks.generic.AcceptButton;
|
||||||
|
import me.bobert.netneutrality.tasks.generic.CancelButton;
|
||||||
import me.bobert.netneutrality.utils.MessageFunction;
|
import me.bobert.netneutrality.utils.MessageFunction;
|
||||||
import me.bobert.netneutrality.utils.MusicUtils;
|
import me.bobert.netneutrality.utils.MusicUtils;
|
||||||
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
import net.dv8tion.jda.api.entities.emoji.Emoji;
|
import net.dv8tion.jda.api.entities.emoji.Emoji;
|
||||||
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
import net.dv8tion.jda.api.interactions.components.ActionRow;
|
||||||
import net.dv8tion.jda.api.interactions.components.ItemComponent;
|
import net.dv8tion.jda.api.interactions.components.ItemComponent;
|
||||||
@@ -31,6 +35,7 @@ public class LoadPlaylistTask extends ButtonTask implements WithChildren, Deleta
|
|||||||
private final SourceFinder.AudioSourceType sourceType;
|
private final SourceFinder.AudioSourceType sourceType;
|
||||||
|
|
||||||
public LoadPlaylistTask(AudioPlaylist playlist, SourceFinder.AudioSourceType sourceType) {
|
public LoadPlaylistTask(AudioPlaylist playlist, SourceFinder.AudioSourceType sourceType) {
|
||||||
|
super(null, null);
|
||||||
this.playlist = playlist;
|
this.playlist = playlist;
|
||||||
this.sourceType = sourceType;
|
this.sourceType = sourceType;
|
||||||
}
|
}
|
||||||
@@ -57,8 +62,8 @@ public class LoadPlaylistTask extends ButtonTask implements WithChildren, Deleta
|
|||||||
}
|
}
|
||||||
|
|
||||||
int searchResultsLimit = 5; // Max number of buttons per row
|
int searchResultsLimit = 5; // Max number of buttons per row
|
||||||
int maxPlaylistSizeBeforeAsking = Integer.parseInt(context.getGuildManager()
|
int maxPlaylistSizeBeforeAsking = Integer.parseInt(GuildService.get(context.getGuild())
|
||||||
.getGuildConfigManager()
|
.getGuildSettingService()
|
||||||
.getSetting(GSettings.MUSIC_MAXPLAYLISTSIZE));
|
.getSetting(GSettings.MUSIC_MAXPLAYLISTSIZE));
|
||||||
|
|
||||||
// Search results
|
// Search results
|
||||||
@@ -68,10 +73,10 @@ public class LoadPlaylistTask extends ButtonTask implements WithChildren, Deleta
|
|||||||
|
|
||||||
for (int i = 0; i < searchResults.size(); i++) {
|
for (int i = 0; i < searchResults.size(); i++) {
|
||||||
AudioTrack track = searchResults.get(i);
|
AudioTrack track = searchResults.get(i);
|
||||||
Task buttonTask = new LoadTrackFromPlaylistTask(this, track);
|
Task buttonTask = new LoadTrackFromPlaylistTask(this, String.valueOf(i + 1), track);
|
||||||
|
|
||||||
buttonList.add(buttonTask.createButton().primary(String.valueOf(i + 1)));
|
buttonList.add(buttonTask.createButton());
|
||||||
childrens.add(buttonTask);
|
addChild(buttonTask);
|
||||||
}
|
}
|
||||||
|
|
||||||
ActionRow firstRow = ActionRow.of(buttonList);
|
ActionRow firstRow = ActionRow.of(buttonList);
|
||||||
@@ -99,21 +104,27 @@ public class LoadPlaylistTask extends ButtonTask implements WithChildren, Deleta
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(TaskContext context) {
|
|
||||||
// Only the buttons will get executed, this task is only used to insert them
|
|
||||||
}
|
|
||||||
|
|
||||||
private ActionRow commonButtons(List<AudioTrack> tracks, String addAllText, String cancelText) {
|
private ActionRow commonButtons(List<AudioTrack> tracks, String addAllText, String cancelText) {
|
||||||
Task addAllTracksTask = new AddAllTracksTask(this, tracks);
|
Task accept = AcceptButton.createSuccessButton(this, addAllText, context -> {
|
||||||
childrens.add(addAllTracksTask);
|
MusicManager manager = MusicManager.get(context.getGuild());
|
||||||
|
Member member = context.getGuildUser();
|
||||||
|
|
||||||
Task cancelTask = new CancelLoadTask(this);
|
if (MusicUtils.connect(context)) {
|
||||||
childrens.add(cancelTask);
|
manager.getQueueManager().queue(tracks, member);
|
||||||
|
context.reply(MessageFunction.buildAddingPlaylistMessage(tracks, playlist.getName(), member, SourceFinder.AudioSourceType.Youtube));
|
||||||
|
|
||||||
|
return Lifecycle.REMOVE_SELF_AND_CHILDREN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lifecycle.KEEP;
|
||||||
|
});
|
||||||
|
|
||||||
|
Task cancel = CancelButton.create(this, cancelText, context -> Lifecycle.REMOVE_SELF_AND_CHILDREN);
|
||||||
|
addChild(accept, cancel);
|
||||||
|
|
||||||
return ActionRow.of(
|
return ActionRow.of(
|
||||||
addAllTracksTask.createButton().success(addAllText).withEmoji(Emoji.fromFormatted("U+1F60E")),
|
accept.createButton().withEmoji(Emoji.fromFormatted("U+1F60E")),
|
||||||
cancelTask.createButton().danger(cancelText).withEmoji(Emoji.fromFormatted("U+1F92E"))
|
cancel.createButton().withEmoji(Emoji.fromFormatted("U+1F92E"))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,6 +135,8 @@ public class LoadPlaylistTask extends ButtonTask implements WithChildren, Deleta
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInteractionDelete() {
|
public void onInteractionDelete() {
|
||||||
removeAllChildTasks();
|
for (Task child : getChildTasks()) {
|
||||||
|
TaskManager.getInstance().removeTask(child);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -6,22 +6,32 @@ package me.bobert.netneutrality.tasks.music;
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
|
||||||
import me.bobert.netneutrality.core.context.CommandContext;
|
|
||||||
import me.bobert.netneutrality.core.context.TaskContext;
|
|
||||||
import me.bobert.netneutrality.core.tasks.trait.WithParent;
|
|
||||||
import me.bobert.netneutrality.music.MusicManager;
|
import me.bobert.netneutrality.music.MusicManager;
|
||||||
import me.bobert.netneutrality.music.SourceFinder;
|
import me.bobert.netneutrality.music.SourceFinder;
|
||||||
import me.bobert.netneutrality.tasks.ButtonTask;
|
import me.bobert.netneutrality.tasks.generic.AcceptButton;
|
||||||
import me.bobert.netneutrality.utils.MessageFunction;
|
import me.bobert.netneutrality.utils.MessageFunction;
|
||||||
import me.bobert.netneutrality.utils.MusicUtils;
|
import me.bobert.netneutrality.utils.MusicUtils;
|
||||||
import net.dv8tion.jda.api.entities.Member;
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
import net.dv8tion.jda.api.interactions.components.buttons.ButtonStyle;
|
||||||
|
|
||||||
public class LoadTrackFromPlaylistTask extends ButtonTask implements WithParent<LoadPlaylistTask> {
|
public class LoadTrackFromPlaylistTask extends AcceptButton<LoadPlaylistTask> {
|
||||||
private final LoadPlaylistTask parentTask;
|
|
||||||
private final AudioTrack track;
|
private final AudioTrack track;
|
||||||
|
|
||||||
public LoadTrackFromPlaylistTask(LoadPlaylistTask parentTask, AudioTrack track) {
|
public LoadTrackFromPlaylistTask(LoadPlaylistTask parentTask, String buttonLabel, AudioTrack track) {
|
||||||
this.parentTask = parentTask;
|
super(parentTask, ButtonStyle.PRIMARY, buttonLabel, context -> {
|
||||||
|
MusicManager manager = MusicManager.get(context.getGuild());
|
||||||
|
Member member = context.getGuildUser();
|
||||||
|
|
||||||
|
if (MusicUtils.connect(context)) {
|
||||||
|
manager.getQueueManager().queue(track, member);
|
||||||
|
context.reply(MessageFunction.buildAddingTrackMessage(track, member, SourceFinder.AudioSourceType.Youtube));
|
||||||
|
|
||||||
|
return Lifecycle.REMOVE_SELF_AND_CHILDREN;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Lifecycle.KEEP;
|
||||||
|
});
|
||||||
|
|
||||||
this.track = track;
|
this.track = track;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,31 +39,4 @@ public class LoadTrackFromPlaylistTask extends ButtonTask implements WithParent<
|
|||||||
public String getName() {
|
public String getName() {
|
||||||
return String.format("load-track-%s", track.getIdentifier());
|
return String.format("load-track-%s", track.getIdentifier());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void replyToInteraction(CommandContext context) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(TaskContext context) {
|
|
||||||
MusicManager manager = MusicManager.get(context.getGuild());
|
|
||||||
Member member = context.getGuildUser();
|
|
||||||
|
|
||||||
if (MusicUtils.connect(context)) {
|
|
||||||
manager.getQueueManager().queue(track, member);
|
|
||||||
|
|
||||||
context.reply(MessageFunction.buildAddingTrackMessage(track, member, SourceFinder.AudioSourceType.Youtube));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LoadPlaylistTask getParentTask() {
|
|
||||||
return parentTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldKeepChilds() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -26,13 +26,11 @@ public class CleanupTaskThread implements Runnable {
|
|||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
if (!tasks.isEmpty()) {
|
if (!tasks.isEmpty()) {
|
||||||
logger.info("Cleaning up tasks..");
|
logger.info("Cleaning up {} expired tasks..", tasks.size());
|
||||||
|
|
||||||
for (Task task : tasks) {
|
for (Task task : tasks) {
|
||||||
TaskManager.getInstance().removeTask(task);
|
TaskManager.getInstance().removeTask(task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package me.bobert.netneutrality.utils;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) anthorob
|
||||||
|
*
|
||||||
|
* https://discord.gg/qUa6zNFZsd
|
||||||
|
*/
|
||||||
|
|
||||||
|
import me.bobert.netneutrality.core.context.Context;
|
||||||
|
import net.dv8tion.jda.api.entities.Guild;
|
||||||
|
import net.dv8tion.jda.api.entities.Member;
|
||||||
|
import net.dv8tion.jda.api.entities.Role;
|
||||||
|
|
||||||
|
public class GuildUtils {
|
||||||
|
|
||||||
|
public static void addRolesToMember(Context context, Role role) {
|
||||||
|
addRolesToMember(context.getGuild(), context.getGuildUser(), role);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void addRolesToMember(Guild guild, Member member, Role role) {
|
||||||
|
Member self = guild.getSelfMember();
|
||||||
|
if (self.canInteract(role)) {
|
||||||
|
guild.addRoleToMember(member, role).queue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,9 +6,9 @@ package me.bobert.netneutrality.utils;
|
|||||||
* https://discord.gg/qUa6zNFZsd
|
* https://discord.gg/qUa6zNFZsd
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import me.bobert.netneutrality.core.GuildManager;
|
|
||||||
import me.bobert.netneutrality.core.db.SessionManager;
|
import me.bobert.netneutrality.core.db.SessionManager;
|
||||||
import me.bobert.netneutrality.db.entities.OGuild;
|
import me.bobert.netneutrality.db.entities.OGuild;
|
||||||
|
import me.bobert.netneutrality.db.service.GuildService;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.hibernate.HibernateException;
|
import org.hibernate.HibernateException;
|
||||||
@@ -30,7 +30,6 @@ public class JpaUtils {
|
|||||||
* If the object is not found in the database and is of type {@link OGuild}, tries to recreate entity and throws
|
* 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.
|
* an exception to block execution.
|
||||||
* <p>
|
* <p>
|
||||||
* The session is not closed
|
|
||||||
*
|
*
|
||||||
* @param session The session in which to load the persistent object
|
* @param session The session in which to load the persistent object
|
||||||
* @param object The persistent object
|
* @param object The persistent object
|
||||||
@@ -45,9 +44,9 @@ public class JpaUtils {
|
|||||||
String cachedId = oGuild.getGuildId();
|
String cachedId = oGuild.getGuildId();
|
||||||
logger.error("Trying to load Guild[{}] while entity doesn't exist in database, reloading..", cachedId);
|
logger.error("Trying to load Guild[{}] while entity doesn't exist in database, reloading..", cachedId);
|
||||||
|
|
||||||
GuildManager guildManager = GuildManager.getManager(cachedId);
|
GuildService service = GuildService.get(cachedId);
|
||||||
guildManager.reloadEntity();
|
service.reloadEntity();
|
||||||
guildManager.getBlacklistCache().clear();
|
service.getBlacklistService().removeGuildBlacklist();
|
||||||
logger.info("Guild[{}] reloaded", cachedId);
|
logger.info("Guild[{}] reloaded", cachedId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,6 +54,10 @@ public class Settings {
|
|||||||
return INSTANCE;
|
return INSTANCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getVersion() {
|
||||||
|
return Settings.getInstance().getMavenVersion();
|
||||||
|
}
|
||||||
|
|
||||||
public String get(String key) {
|
public String get(String key) {
|
||||||
String property = prop.getProperty(key);
|
String property = prop.getProperty(key);
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user