Merge branch 'feat-tasks' into 'develop'

New feature: tasks

See merge request anthorob/netneutrality!2
This commit is contained in:
anthorob committed 2024-09-12 10:50:14 +00:00
commit 5294cb86ca
121 files changed
+3893 -1561

No files matched your search

+7 -1
View File
@@ -10,4 +10,10 @@ MYSQL_PASSWORD=
MYSQL_DATABASE=
MYSQL_PORT=3306
NETNEUTRALITY_SENTRY_DSN=
NETNEUTRALITY_PO_TOKEN=
NETNEUTRALITY_VISITOR_DATA=
NETNEUTRALITY_SENTRY_DSN=
GITLAB_HOST=
GITLAB_TOKEN=
GITLAB_PROJECT_ID=
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JpaPluginProjectSettings">
<option name="lastSelectedLanguage" value="Java" />
</component>
</project>
+7 -1
View File
@@ -10,4 +10,10 @@ MYSQL_PASSWORD=
MYSQL_DATABASE=
MYSQL_PORT=3306
NETNEUTRALITY_SENTRY_DSN=
NETNEUTRALITY_PO_TOKEN=
NETNEUTRALITY_VISITOR_DATA=
NETNEUTRALITY_SENTRY_DSN=
GITLAB_HOST=
GITLAB_TOKEN=
GITLAB_PROJECT_ID=
Regular → Executable
View File
File mode changed.
+1 -1
View File
@@ -13,7 +13,7 @@ services:
MYSQL_ROOT_PASSWORD: 'password'
MARIADB_AUTO_UPGRADE: '1'
ports:
- "3306"
- "3306:3306"
networks:
- internal
+34 -9
View File
@@ -28,6 +28,16 @@
</resources>
<plugins>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>10.17.0</version>
<configuration>
<url>jdbc:mysql://localhost:3306/netneutrality</url>
<user>root</user>
<password></password>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
@@ -91,17 +101,17 @@
<dependency>
<groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId>
<version>5.0.0</version>
<version>5.1.0</version>
</dependency>
<dependency>
<groupId>dev.arbjerg</groupId>
<artifactId>lavaplayer</artifactId>
<version>2.1.2</version>
<version>2.2.1</version>
</dependency>
<dependency>
<groupId>dev.lavalink.youtube</groupId>
<artifactId>v2</artifactId>
<version>1.4.0</version>
<version>1.7.2</version>
</dependency>
<dependency>
@@ -113,23 +123,38 @@
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-core</artifactId>
<version>6.1.7.Final</version>
<version>6.5.2.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.orm</groupId>
<artifactId>hibernate-hikaricp</artifactId>
<version>6.1.7.Final</version>
<version>6.5.2.Final</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.3.0</version>
<version>9.0.0</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>10.17.0</version>
</dependency>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-mysql</artifactId>
<version>10.17.0</version>
</dependency>
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry</artifactId>
<version>7.6.0</version>
<version>7.12.1</version>
</dependency>
<dependency>
<groupId>org.gitlab4j</groupId>
<artifactId>gitlab4j-api</artifactId>
<version>6.0.0-rc.5</version>
</dependency>
<dependency>
@@ -163,12 +188,12 @@
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
<version>3.15.0</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
<version>1.17.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
+55 -54
View File
@@ -11,9 +11,9 @@ SET time_zone = "+00:00";
--
-- Database: `netneutrality`
--
CREATE DATABASE IF NOT EXISTS netneutrality CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE DATABASE IF NOT EXISTS `netneutrality` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE `netneutrality`;
USE netneutrality;
-- --------------------------------------------------------
--
@@ -22,13 +22,13 @@ USE netneutrality;
DROP TABLE IF EXISTS `blacklist`;
CREATE TABLE IF NOT EXISTS `blacklist` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`guild_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`word` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`enabled` tinyint(1) NOT NULL,
`creator_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_blacklist_guilds` (`guild_id`)
`id` int(11) NOT NULL AUTO_INCREMENT,
`guild_id` varchar(255) NOT NULL,
`word` varchar(255) NOT NULL,
`enabled` tinyint(1) NOT NULL,
`creator_id` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_blacklist_guilds` (`guild_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
@@ -39,11 +39,11 @@ CREATE TABLE IF NOT EXISTS `blacklist` (
DROP TABLE IF EXISTS `guilds`;
CREATE TABLE IF NOT EXISTS `guilds` (
`guild_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`logging` tinyint(1) NOT NULL DEFAULT 0,
`debug` tinyint(1) NOT NULL DEFAULT 1,
`log_channel` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`guild_id`)
`guild_id` varchar(255) NOT NULL,
`logging` tinyint(1) NOT NULL DEFAULT 0,
`debug` tinyint(1) NOT NULL DEFAULT 1,
`log_channel` varchar(255) DEFAULT NULL,
PRIMARY KEY (`guild_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
@@ -54,12 +54,12 @@ CREATE TABLE IF NOT EXISTS `guilds` (
DROP TABLE IF EXISTS `guild_settings`;
CREATE TABLE IF NOT EXISTS `guild_settings` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`guild_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`value` text COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_guildsetting_guilds` (`guild_id`)
`id` int(11) NOT NULL AUTO_INCREMENT,
`guild_id` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
`value` text NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_guildsetting_guilds` (`guild_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
@@ -70,10 +70,10 @@ CREATE TABLE IF NOT EXISTS `guild_settings` (
DROP TABLE IF EXISTS `logging`;
CREATE TABLE IF NOT EXISTS `logging` (
`guild_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`channel_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`guild_id`,`channel_id`)
`guild_id` varchar(255) NOT NULL,
`channel_id` varchar(255) NOT NULL,
`enabled` tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (`guild_id`,`channel_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
@@ -84,32 +84,33 @@ CREATE TABLE IF NOT EXISTS `logging` (
DROP TABLE IF EXISTS `log_message`;
CREATE TABLE IF NOT EXISTS `log_message` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`guild_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`channel_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`message_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`user_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`date_sent` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`message` varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
`edited_message` varchar(2000) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `message_id` (`message_id`),
KEY `fk_log_guilds` (`guild_id`)
`id` int(11) NOT NULL AUTO_INCREMENT,
`guild_id` varchar(255) NOT NULL,
`channel_id` varchar(255) NOT NULL,
`message_id` varchar(255) NOT NULL,
`user_id` varchar(255) NOT NULL,
`date_sent` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
`message` varchar(2000) DEFAULT NULL,
`edited_message` varchar(2000) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `message_id` (`message_id`),
KEY `fk_log_guilds` (`guild_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
--
-- Table structure for table `permissions`
-- Table structure for table `tasks`
--
DROP TABLE IF EXISTS `permissions`;
CREATE TABLE IF NOT EXISTS `permissions` (
`guild_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`role_id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`type` int(11) DEFAULT NULL,
`command` int(11) DEFAULT NULL,
PRIMARY KEY (`guild_id`,`role_id`)
DROP TABLE IF EXISTS `tasks`;
CREATE TABLE IF NOT EXISTS `tasks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`task_id` varchar(255) NOT NULL,
`task_class` varchar(255) NOT NULL,
`payload` text DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `tasks_unique_taskid` (`task_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
-- --------------------------------------------------------
@@ -120,13 +121,13 @@ CREATE TABLE IF NOT EXISTS `permissions` (
DROP TABLE IF EXISTS `web_users`;
CREATE TABLE IF NOT EXISTS `web_users` (
`id` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`email` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`expires` timestamp NOT NULL DEFAULT current_timestamp(),
`refresh_token` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`)
`id` varchar(255) NOT NULL,
`email` mediumtext NOT NULL,
`token` varchar(255) NOT NULL,
`expires` timestamp NOT NULL DEFAULT current_timestamp(),
`refresh_token` varchar(255) NOT NULL,
`remember_token` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
--
@@ -137,25 +138,25 @@ CREATE TABLE IF NOT EXISTS `web_users` (
-- Constraints for table `blacklist`
--
ALTER TABLE `blacklist`
ADD CONSTRAINT `fk_blacklist_guilds` FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`guild_id`) ON DELETE CASCADE ON UPDATE CASCADE;
ADD CONSTRAINT `fk_blacklist_guilds` FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`guild_id`) ON DELETE CASCADE ON UPDATE CASCADE;
--
-- Constraints for table `guild_settings`
--
ALTER TABLE `guild_settings`
ADD CONSTRAINT `fk_guildsetting_guilds` FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`guild_id`) ON DELETE CASCADE ON UPDATE CASCADE;
ADD CONSTRAINT `fk_guildsetting_guilds` FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`guild_id`) ON DELETE CASCADE ON UPDATE CASCADE;
--
-- Constraints for table `logging`
--
ALTER TABLE `logging`
ADD CONSTRAINT `fk_logging_guilds` FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`guild_id`) ON DELETE CASCADE ON UPDATE CASCADE;
ADD CONSTRAINT `fk_logging_guilds` FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`guild_id`) ON DELETE CASCADE ON UPDATE CASCADE;
--
-- Constraints for table `log_message`
--
ALTER TABLE `log_message`
ADD CONSTRAINT `fk_log_guilds` FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`guild_id`) ON DELETE CASCADE ON UPDATE CASCADE;
ADD CONSTRAINT `fk_log_guilds` FOREIGN KEY (`guild_id`) REFERENCES `guilds` (`guild_id`) ON DELETE CASCADE ON UPDATE CASCADE;
COMMIT;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
@@ -7,25 +7,28 @@ package me.bobert.netneutrality;
*/
import io.sentry.Sentry;
import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.command.CommandManager;
import me.bobert.netneutrality.core.db.SessionManager;
import me.bobert.netneutrality.core.tasks.TaskManager;
import me.bobert.netneutrality.core.thread.BaseThreadFactory;
import me.bobert.netneutrality.core.thread.ThreadManager;
import me.bobert.netneutrality.hooks.guilds.ServerJoinHook;
import me.bobert.netneutrality.hooks.interaction.ButtonInteractionHook;
import me.bobert.netneutrality.hooks.interaction.ModalInteractionHook;
import me.bobert.netneutrality.hooks.interaction.SlashCommandHook;
import me.bobert.netneutrality.hooks.message.EmojiReactionHook;
import me.bobert.netneutrality.hooks.message.MessageReceivedHook;
import me.bobert.netneutrality.http.server.WebApi;
import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.thread.ShutdownThread;
import me.bobert.netneutrality.utils.Settings;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.JDABuilder;
import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.exceptions.InvalidTokenException;
import net.dv8tion.jda.api.requests.GatewayIntent;
import me.bobert.netneutrality.command.CommandManager;
import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.db.DBController;
import me.bobert.netneutrality.hooks.SlashCommandHooks;
import me.bobert.netneutrality.hooks.guilds.ServerJoinHandler;
import me.bobert.netneutrality.hooks.message.EditHandler;
import me.bobert.netneutrality.hooks.message.MessageReceivedHandler;
import me.bobert.netneutrality.http.server.WebApi;
import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.thread.BaseThreadFactory;
import me.bobert.netneutrality.thread.ShutdownThread;
import me.bobert.netneutrality.thread.ThreadManager;
import me.bobert.netneutrality.utils.Settings;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -34,7 +37,7 @@ import org.apache.logging.log4j.core.config.Configurator;
public class NetNeutrality {
private static JDA jda;
public final static Logger logger = LogManager.getLogger(NetNeutrality.class);
private final static Logger logger = LogManager.getLogger(NetNeutrality.class);
public static final String VERSION = Settings.getInstance().getMavenVersion();
public static void main(String[] args) {
@@ -57,12 +60,15 @@ public class NetNeutrality {
Configurator.setLevel("net.dv8tion.jda", Level.DEBUG);
}
DBController.init();
// DatabaseMigration.migrate();
SessionManager.getSessionFactory();
initJDA();
CommandManager.init();
CommandManager.init(jda);
MusicManager.initPlayerManager();
jda.addEventListener(new MessageReceivedHandler(), new EditHandler(), new ServerJoinHandler());
jda.addEventListener(new SlashCommandHooks());
TaskManager.getInstance().init();
jda.addEventListener(new ServerJoinHook());
jda.addEventListener(new MessageReceivedHook(), new SlashCommandHook());
jda.addEventListener(new ModalInteractionHook(), new ButtonInteractionHook(), new EmojiReactionHook());
new WebApi(Integer.parseInt(Settings.getInstance().get("NETNEUTRALITY_API_PORT", "9000")));
jda.getPresence().setActivity(Activity.watching("you"));
@@ -73,7 +79,7 @@ public class NetNeutrality {
public static void initJDA() {
String token = Settings.getInstance().get("NETNEUTRALITY_BOT_TOKEN");
if (token == null) {
DBController.shutdown();
SessionManager.shutdown();
throw new RuntimeException("No bot token provided, exiting.");
}
@@ -95,7 +101,7 @@ public class NetNeutrality {
MusicManager.disconnectPlayers();
ThreadManager.shutdownExecutor();
disconnect();
DBController.shutdown();
SessionManager.shutdown();
System.out.println("[Net Neutrality] Shutdown complete");
}
@@ -0,0 +1,26 @@
package me.bobert.netneutrality.command;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
public abstract class AbstractAdminCommand extends AbstractCommand {
@Override
public boolean needAdmin() {
return true;
}
@Override
public DefaultMemberPermissions getSlashCommandPermission() {
return DefaultMemberPermissions.DISABLED;
}
@Override
public CommandType getType() {
return CommandType.ADMINISTRATION;
}
}
@@ -6,13 +6,14 @@ package me.bobert.netneutrality.command;
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
public abstract class AbstractCommand implements ICommand {
public abstract class AbstractCommand implements Command {
@Override
public boolean isSlashCommand() {
@@ -25,12 +26,12 @@ public abstract class AbstractCommand implements ICommand {
}
@Override
public boolean canBeExecutedBy(Guild guild, Member member) {
public boolean canBeExecutedBy(Member member) {
return true;
}
@Override
public boolean canBeExecutedFrom(Guild guild, MessageChannel channel) {
public boolean canBeExecutedFrom(MessageChannel channel) {
return true;
}
@@ -12,6 +12,7 @@ import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.channel.ChannelType;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
public abstract class AbstractMusicCommand extends AbstractCommand {
@@ -32,7 +33,12 @@ public abstract class AbstractMusicCommand extends AbstractCommand {
}
@Override
public boolean canBeExecutedBy(Guild guild, Member member) {
public boolean canBeExecutedBy(Member member) {
if (member == null) {
return true;
}
Guild guild = member.getGuild();
String roleDb = GuildManager.getManager(guild).getGuildConfigManager().getSetting(GSettings.MUSIC_ROLE);
if (roleDb != null && !roleDb.isEmpty()) {
@@ -54,14 +60,15 @@ public abstract class AbstractMusicCommand extends AbstractCommand {
}
@Override
public boolean canBeExecutedFrom(Guild guild, MessageChannel channel) {
if (channel.getType() != ChannelType.TEXT)
return false;
public boolean canBeExecutedFrom(MessageChannel channel) {
if (channel instanceof GuildMessageChannel guildMessageChannel) {
if (channel.getType() != ChannelType.TEXT)
return false;
String channelDb = GuildManager.getManager(guild).getGuildConfigManager().getSetting(GSettings.MUSIC_CHANNEL);
if (channelDb != null && !channelDb.isEmpty()) {
return channelDb.equals(channel.getId());
String channelDb = GuildManager.getManager(guildMessageChannel.getGuild()).getGuildConfigManager().getSetting(GSettings.MUSIC_CHANNEL);
if (channelDb != null && !channelDb.isEmpty()) {
return channelDb.equals(channel.getId());
}
}
return true;
@@ -5,15 +5,16 @@ package me.bobert.netneutrality.command;
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
public interface ICommand {
public interface Command {
boolean needAdmin();
@@ -33,9 +34,9 @@ public interface ICommand {
CommandType getType();
boolean canBeExecutedBy(Guild guild, Member member);
boolean canBeExecutedBy(Member member);
boolean canBeExecutedFrom(Guild guild, MessageChannel channel);
boolean canBeExecutedFrom(MessageChannel channel);
@Deprecated
CommandExecutionResult execute(MessageCommandContext context);
@@ -5,29 +5,22 @@ package me.bobert.netneutrality.command.admin;
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.command.AbstractCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.command.CommandType;
import me.bobert.netneutrality.command.AbstractAdminCommand;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.db.DBController;
import me.bobert.netneutrality.db.controllers.DBBlacklist;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.db.entities.OBlacklist;
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
import me.bobert.netneutrality.db.repository.BlacklistRepository;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
import java.util.ArrayList;
import java.util.List;
public class BlacklistCommand extends AbstractCommand {
@Override
public boolean needAdmin() {
return true;
}
public class BlacklistCommand extends AbstractAdminCommand {
@Override
public boolean isEnabled() {
@@ -49,11 +42,6 @@ public class BlacklistCommand extends AbstractCommand {
return "Manages the blacklist of your server.";
}
@Override
public DefaultMemberPermissions getSlashCommandPermission() {
return DefaultMemberPermissions.DISABLED;
}
@Override
public void registerSlashCommandOptions(SlashCommandData data) {
data.addSubcommands(
@@ -65,11 +53,6 @@ public class BlacklistCommand extends AbstractCommand {
);
}
@Override
public CommandType getType() {
return CommandType.ADMINISTRATION;
}
@Override
public CommandExecutionResult execute(MessageCommandContext context) {
return CommandExecutionResult.none();
@@ -78,34 +61,42 @@ public class BlacklistCommand extends AbstractCommand {
@Override
public CommandExecutionResult execute(SlashCommandContext context) {
String sub = context.getEvent().getFullCommandName().split(" ")[1];
DBBlacklist controller = DBController.get(DBBlacklist.class);
String word = context.getEvent().getOption("word", "", OptionMapping::getAsString);
context.ackAndWait(true);
BlacklistRepository repository = new BlacklistRepository();
switch (sub) {
case "get" -> {
case "get" -> context.async("Retrieving list, please wait...", asyncMessage -> {
List<OBlacklist> blacklists = new ArrayList<>();
try {
blacklists = repository.getBlacklistForGuild(context.getGuild());
} catch (Exception e) {
LogHandler.log(e);
}
context.getHook().editOriginal("Retrieving list, please wait...").queue(t -> {
ArrayList<OBlacklist> blacklists = new ArrayList<>();
try {
blacklists = controller.getBlacklistForGuild(context.getGuild());
} catch (Exception e) {
LogHandler.log(e);
}
StringBuilder sb = new StringBuilder();
sb.append("List of blacklisted words for this server [").append(context.getGuild().getName()).append("]: \n");
for (OBlacklist bl : blacklists) {
sb.append(bl.getWord()).append(" -> ").append(bl.getEnabled() ? "enabled" : "disabled").append("\n");
}
StringBuilder sb = new StringBuilder();
for (OBlacklist bl : blacklists) {
sb.append(bl.getWord()).append(" -> ").append(bl.getEnabled() ? "enabled" : "disabled").append("\n");
}
if (blacklists.isEmpty()) {
sb.append("No word found");
}
t.editMessage("List of blacklisted words for this server [" + context.getGuild().getName() + "] :\n" + (blacklists.isEmpty() ? "No word found" : sb.toString())).queue();
});
asyncMessage.edit(sb.toString());
});
case "add" -> {
return repository.add(context.getGuild(), context.getGuildUser(), word);
}
case "remove" -> {
return repository.delete(context.getGuild(), word);
}
case "disable" -> {
return repository.disable(context.getGuild(), word);
}
case "enable" -> {
return repository.enable(context.getGuild(), word);
}
case "add" -> controller.add(word, context);
case "remove" -> controller.delete(word, context);
case "disable" -> controller.disable(word, context);
case "enable" -> controller.enable(word, context);
}
return CommandExecutionResult.none();
@@ -0,0 +1,137 @@
package me.bobert.netneutrality.command.admin;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.command.AbstractAdminCommand;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.tasks.TaskManager;
import me.bobert.netneutrality.tasks.emoji.EmojiReactionRoleTask;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandData;
import net.dv8tion.jda.api.interactions.commands.build.SubcommandGroupData;
public class ReactionCommand extends AbstractAdminCommand {
@Override
public boolean isEnabled() {
return true;
}
@Override
public boolean isGuildOnly() {
return true;
}
@Override
public String getCommandName() {
return "reaction";
}
@Override
public String getDescription() {
return "Manages emoji reactions on this server";
}
@Override
public void registerSlashCommandOptions(SlashCommandData data) {
data.addSubcommandGroups(
new SubcommandGroupData("emoji", "Give roles to users using emojis").addSubcommands(
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, "emoji", "Emoji associated with the role", true),
new OptionData(OptionType.ROLE, "role", "Role to add to the emoji", true)
),
new SubcommandData("add", "Add a role to an existing message").addOptions(
new OptionData(OptionType.STRING, "messageid", "The ID of the message", true),
new OptionData(OptionType.STRING, "emoji", "Emoji associated with the role", true),
new OptionData(OptionType.ROLE, "role", "Role to add to the emoji", true)
),
new SubcommandData("remove", "Remove a role from a message").addOptions(
new OptionData(OptionType.STRING, "messageid", "The ID of the message", true),
new OptionData(OptionType.STRING, "emoji", "Emoji to remove from the message", true)
),
new SubcommandData("delete", "Remove all roles from a message").addOptions(
new OptionData(OptionType.STRING, "messageid", "The ID of the message", true)
)
)
);
}
@Override
public CommandExecutionResult execute(SlashCommandContext context) {
context.ack();
String guildId = context.getGuild().getId();
String messageId = context.getEvent().getOption("messageid", OptionMapping::getAsString);
String sub = context.getSubCommand();
String action = context.getEvent().getFullCommandName().split(" ")[2];
switch (sub) {
case "emoji" -> {
if ("setup".equals(action)) {
String emoji = context.getEvent().getOption("emoji", OptionMapping::getAsString);
String role = context.getEvent().getOption("role", OptionMapping::getAsString);
EmojiReactionRoleTask task = new EmojiReactionRoleTask(guildId, messageId);
if (TaskManager.getInstance().exists(task.getId())) {
return CommandExecutionResult.error("The message ID is already registered, use the add subcommand instead.");
}
task.addEmoji(emoji, role);
TaskManager.getInstance().insert(context, task);
return CommandExecutionResult.success("Added emoji to message successfully!");
}
if ("add".equals(action)) {
String emoji = context.getEvent().getOption("emoji", OptionMapping::getAsString);
String role = context.getEvent().getOption("role", OptionMapping::getAsString);
String id = String.format("%s:%s", guildId, messageId);
EmojiReactionRoleTask task = (EmojiReactionRoleTask) TaskManager.getInstance().getTask(id);
if (task == null) {
return CommandExecutionResult.error("The message ID provided isn't registered with the bot");
}
task.addEmoji(emoji, role);
TaskManager.getInstance().persistTask(task);
return CommandExecutionResult.success("Added emoji to message successfully!");
}
if ("remove".equals(action)) {
String emoji = context.getEvent().getOption("emoji", OptionMapping::getAsString);
String id = String.format("%s:%s", guildId, messageId);
EmojiReactionRoleTask task = (EmojiReactionRoleTask) TaskManager.getInstance().getTask(id);
if (task == null) {
return CommandExecutionResult.error("The message ID provided isn't registered with the bot");
}
task.removeEmoji(emoji);
TaskManager.getInstance().persistTask(task);
return CommandExecutionResult.success("Removed emoji from message successfully!");
}
if ("delete".equals(action)) {
String id = String.format("%s:%s", guildId, messageId);
EmojiReactionRoleTask task = (EmojiReactionRoleTask) TaskManager.getInstance().getTask(id);
if (task == null) {
return CommandExecutionResult.error("The message ID provided isn't registered with the bot");
}
TaskManager.getInstance().unpersistTask(task);
return CommandExecutionResult.success("Unregistered the message ID successfully!");
}
}
}
return CommandExecutionResult.none();
}
}
@@ -6,10 +6,10 @@ package me.bobert.netneutrality.command.basic;
*/
import me.bobert.netneutrality.command.AbstractCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.command.CommandType;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.utils.MessageFunction;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
@@ -57,10 +57,10 @@ public class HelpCommand extends AbstractCommand {
@Override
public CommandExecutionResult execute(MessageCommandContext context) {
if (context.getArgs().length == 0) {
List<MessageEmbed> help = MessageFunction.buildHelpMessage(context.getGuildUser());
List<MessageEmbed> help = MessageFunction.buildHelpMessage();
context.reply(new MessageCreateBuilder().addContent(context.getDiscordUser().getAsMention()).addContent(" help has been sent your way!").build());
context.replyPrivate(help);
context.sendMessageInPrivate(help);
}
return CommandExecutionResult.none();
@@ -68,7 +68,7 @@ public class HelpCommand extends AbstractCommand {
@Override
public CommandExecutionResult execute(SlashCommandContext context) {
List<MessageEmbed> help = MessageFunction.buildHelpMessage(context.getGuildUser());
List<MessageEmbed> help = MessageFunction.buildHelpMessage();
context.sendMessageInPrivate(help);
return CommandExecutionResult.success("Help has been sent in DM!");
@@ -6,10 +6,10 @@ package me.bobert.netneutrality.command.basic;
*/
import me.bobert.netneutrality.command.AbstractCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.command.CommandType;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
public class PingCommand extends AbstractCommand {
@@ -5,18 +5,16 @@ package me.bobert.netneutrality.command.bot;
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.command.AbstractCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.command.CommandType;
import me.bobert.netneutrality.command.AbstractAdminCommand;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.GuildManager;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.db.GSettings;
import me.bobert.netneutrality.music.MusicManager;
import net.dv8tion.jda.api.entities.Role;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.channel.unions.GuildChannelUnion;
import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.OptionType;
import net.dv8tion.jda.api.interactions.commands.build.OptionData;
@@ -27,12 +25,7 @@ import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
import java.util.List;
import java.util.Map;
public class ConfigureCommand extends AbstractCommand {
@Override
public boolean needAdmin() {
return true;
}
public class ConfigureCommand extends AbstractAdminCommand {
@Override
public boolean isEnabled() {
@@ -54,11 +47,6 @@ public class ConfigureCommand extends AbstractCommand {
return "Used to configure the bot on this server";
}
@Override
public DefaultMemberPermissions getSlashCommandPermission() {
return DefaultMemberPermissions.DISABLED;
}
@Override
public void registerSlashCommandOptions(SlashCommandData data) {
data.addSubcommands(
@@ -74,11 +62,6 @@ public class ConfigureCommand extends AbstractCommand {
);
}
@Override
public CommandType getType() {
return CommandType.ADMINISTRATION;
}
@Override
public CommandExecutionResult execute(MessageCommandContext context) {
String[] args = context.getArgs();
@@ -213,7 +196,7 @@ public class ConfigureCommand extends AbstractCommand {
public CommandExecutionResult execute(SlashCommandContext context) {
String sub = context.getEvent().getFullCommandName().split(" ")[1];
GuildManager guildManager = GuildManager.getManager(context.getGuild());
context.ackAndWait(true);
context.ephemeral();
if ("music-role".equals(sub)) {
Role role = context.getEvent().getOption("role", OptionMapping::getAsRole);
@@ -259,11 +242,11 @@ public class ConfigureCommand extends AbstractCommand {
return CommandExecutionResult.success(String.format("Default volume for music player is now %d", volume));
}
if ("debug".equals(sub)) {
if ("error-logging".equals(sub)) {
boolean debug = context.getEvent().getOption("debug", true, OptionMapping::getAsBoolean);
guildManager.setDebug(debug);
return CommandExecutionResult.success(String.format("Error logging is now %s for this guild", (debug ? "enabled" : "disabled")));
return CommandExecutionResult.success("Error logging is now %s for this guild", (debug ? "enabled" : "disabled"));
}
if ("show-all".equals(sub)) {
@@ -284,6 +267,6 @@ public class ConfigureCommand extends AbstractCommand {
context.replyEphemeral(builder.build());
}
return CommandExecutionResult.none();
return CommandExecutionResult.error("Unknown subcommand: " + sub);
}
}
@@ -6,11 +6,11 @@ package me.bobert.netneutrality.command.crypto;
*/
import me.bobert.netneutrality.command.AbstractCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.command.CommandType;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.http.WebModule;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.OptionType;
@@ -79,7 +79,8 @@ public class CryptoPriceCommand extends AbstractCommand {
String crypto = args[0].toUpperCase();
String finalCurrency = currency;
context.getChannel().sendMessage("Retreiving price, please wait...").queue(t -> {
context.async("Retreiving price, please wait...", asyncMessage -> {
String price;
Locale usa = new Locale("en", "US");
NumberFormat dollarFormat = NumberFormat.getCurrencyInstance(usa);
@@ -89,15 +90,15 @@ public class CryptoPriceCommand extends AbstractCommand {
WebModule http = new WebModule(String.format("%s?fsym=%s&tsyms=%s", baseURL, crypto, finalCurrency.toUpperCase())).request();
JSONObject json = http.getAsObject();
if (json.isEmpty() || (json.has("Response") && json.getString("Response").equalsIgnoreCase("Error"))) {
t.editMessage("Crypto doesn't exist.. yet").queue();
asyncMessage.edit("Crypto doesn't exist.. yet");
return;
}
price = dollarFormat.format(json.getDouble(finalCurrency));
t.editMessage(String.format("The current price of %s in %s is %s", crypto, finalCurrency, price)).queue();
asyncMessage.edit("The current price of %s in %s is %s", crypto, finalCurrency, price);
} catch (JSONException | IllegalArgumentException | ArithmeticException e) {
t.editMessage(String.format("Error getting price of %s!", crypto)).queue();
asyncMessage.edit("Error getting price of %s", crypto);
LogHandler.log(e);
}
});
@@ -122,28 +123,26 @@ public class CryptoPriceCommand extends AbstractCommand {
}
String finalCurrency = currency.toUpperCase();
context.getEvent().deferReply().queue(interactionHook -> {
context.async(asyncMessage -> {
try {
WebModule http = new WebModule(String.format("%s?fsym=%s&tsyms=%s", baseURL, symbol, finalCurrency)).request();
JSONObject json = http.getAsObject();
if (json.isEmpty() || (json.has("Response") && json.getString("Response").equalsIgnoreCase("Error"))) {
interactionHook.editOriginal("Crypto doesn't exist.. yet").queue();
asyncMessage.edit("Crypto doesn't exist.. yet");
return;
}
double value = json.getDouble(finalCurrency);
String price = dollarFormat.format(value);
interactionHook.editOriginal(String.format("The current price of %s in %s is %s", symbol, finalCurrency, price)).queue();
asyncMessage.edit("The current price of %s in %s is %s", symbol, finalCurrency, price);
} catch (JSONException | IllegalArgumentException | ArithmeticException e) {
interactionHook.editOriginal(String.format("Error getting price of %s!", symbol)).queue();
asyncMessage.edit("Error getting price of %s!", symbol);
LogHandler.log(e);
}
});
return CommandExecutionResult.none();
@@ -0,0 +1,67 @@
package me.bobert.netneutrality.command.git;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.command.AbstractCommand;
import me.bobert.netneutrality.command.CommandType;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.tasks.TaskManager;
import me.bobert.netneutrality.tasks.git.ReportIssueTask;
import me.bobert.netneutrality.utils.Settings;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
public class ReportIssueCommand extends AbstractCommand {
@Override
public boolean needAdmin() {
return false;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public boolean isGuildOnly() {
return false;
}
@Override
public String getCommandName() {
return "report-bug";
}
@Override
public String getDescription() {
return "Used to report a bug with the bot.";
}
@Override
public void registerSlashCommandOptions(SlashCommandData data) {
}
@Override
public CommandType getType() {
return CommandType.UTILITY;
}
@Override
public CommandExecutionResult execute(SlashCommandContext context) {
String gitlabHost = Settings.getInstance().get("GITLAB_HOST");
String gitlabToken = Settings.getInstance().get("GITLAB_TOKEN");
String projectId = Settings.getInstance().get("GITLAB_PROJECT_ID");
if (gitlabHost == null || gitlabToken == null || projectId == null) {
return CommandExecutionResult.error("Command is not configured, failed to create issue.");
}
TaskManager.getInstance().insert(context, new ReportIssueTask());
return CommandExecutionResult.none();
}
}
@@ -7,10 +7,10 @@ package me.bobert.netneutrality.command.music;
*/
import me.bobert.netneutrality.command.AbstractMusicCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.command.CommandType;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.music.MusicManager;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
@@ -44,7 +44,7 @@ public class BitconnectCommand extends AbstractMusicCommand {
@Override
public CommandExecutionResult execute(MessageCommandContext context) {
MusicManager player = MusicManager.get(context.getGuild());
player.play("https://www.youtube.com/watch?v=e5nyQmaq4k4", context, false, true);
player.play("https://www.youtube.com/watch?v=e5nyQmaq4k4", context, true);
context.deleteMessage();
return CommandExecutionResult.none();
@@ -53,9 +53,9 @@ public class BitconnectCommand extends AbstractMusicCommand {
@Override
public CommandExecutionResult execute(SlashCommandContext context) {
MusicManager player = MusicManager.get(context.getGuild());
context.ackAndWait();
context.ephemeral();
player.play("https://www.youtube.com/watch?v=e5nyQmaq4k4", context, false, true);
player.play("https://www.youtube.com/watch?v=e5nyQmaq4k4", context, true);
return CommandExecutionResult.success("Ready to invest?");
}
}
@@ -7,9 +7,9 @@ package me.bobert.netneutrality.command.music;
*/
import me.bobert.netneutrality.command.AbstractMusicCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MessageFunction;
import me.bobert.netneutrality.utils.MusicUtils;
@@ -46,7 +46,7 @@ public class ClearQueueCommand extends AbstractMusicCommand {
public CommandExecutionResult execute(MessageCommandContext context) {
MusicManager player = MusicManager.get(context.getGuild());
if (!MusicUtils.checkIfConnected(player, context))
if (!MusicUtils.checkIfConnected(context))
return CommandExecutionResult.none();
if (context.getArgs().length == 0) {
@@ -88,9 +88,9 @@ public class ClearQueueCommand extends AbstractMusicCommand {
@Override
public CommandExecutionResult execute(SlashCommandContext context) {
MusicManager manager = MusicManager.get(context.getGuild());
context.ackAndWait();
context.ack();
if (!MusicUtils.checkIfConnected(manager, context))
if (!MusicUtils.checkIfConnected(context))
return CommandExecutionResult.none();
return manager.getScheduler().getQueueManager().clearQueue();
@@ -7,9 +7,9 @@ package me.bobert.netneutrality.command.music;
*/
import me.bobert.netneutrality.command.AbstractMusicCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MessageFunction;
import me.bobert.netneutrality.utils.MusicUtils;
@@ -39,12 +39,14 @@ public class CurrentlyPlayingCommand extends AbstractMusicCommand {
@Override
public CommandExecutionResult execute(MessageCommandContext context) {
MusicManager player = MusicManager.get(context.getGuild());
MusicManager manager = MusicManager.get(context.getGuild());
if (MusicUtils.checkIfConnected(player, context) && player.isCurrentlyPlaying()) {
context.reply(MessageFunction.buildCurrentlyPlayingMessage(player));
} else {
context.reply("Bot isn't playing any tracks right now");
if (MusicUtils.checkIfConnected(context)) {
if (!manager.isCurrentlyPlaying()) {
return CommandExecutionResult.info("Bot isn't playing any tracks right now");
}
context.reply(MessageFunction.buildCurrentlyPlayingMessage(manager));
}
return CommandExecutionResult.none();
@@ -53,9 +55,13 @@ public class CurrentlyPlayingCommand extends AbstractMusicCommand {
@Override
public CommandExecutionResult execute(SlashCommandContext context) {
MusicManager manager = MusicManager.get(context.getGuild());
context.ackAndWait();
context.ack();
if (MusicUtils.checkIfConnected(context)) {
if (!manager.isCurrentlyPlaying()) {
return CommandExecutionResult.info("Bot isn't playing any tracks right now");
}
if (MusicUtils.checkIfConnected(manager, context) && manager.isCurrentlyPlaying()) {
context.reply(MessageFunction.buildCurrentlyPlayingMessage(manager));
}
@@ -7,12 +7,10 @@ package me.bobert.netneutrality.command.music;
*/
import me.bobert.netneutrality.command.AbstractMusicCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.exceptions.CommandExecutionException;
import me.bobert.netneutrality.music.MusicManager;
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.utils.MusicUtils;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
public class JoinCommand extends AbstractMusicCommand {
@@ -39,24 +37,10 @@ public class JoinCommand extends AbstractMusicCommand {
@Override
public CommandExecutionResult execute(MessageCommandContext context) {
MusicManager player = MusicManager.get(context.getGuild());
if (context.getGuildUser().getVoiceState() == null) {
throw new CommandExecutionException("Voice state is null, this should not happen", context, null);
}
if (!context.getGuildUser().getVoiceState().inAudioChannel()) {
return CommandExecutionResult.warn("You are not in a voice channel");
}
if (player.isConnected()) {
return CommandExecutionResult.warn("Player is already in a voice channel");
}
VoiceChannel vChannel = (VoiceChannel) context.getGuildUser().getVoiceState().getChannel();
if (vChannel != null) {
player.connect(vChannel);
if (MusicUtils.checkIfNotConnected(context)) {
context.reply("Joining current voice channel");
MusicUtils.connect(context, true);
}
return CommandExecutionResult.none();
@@ -64,25 +48,10 @@ public class JoinCommand extends AbstractMusicCommand {
@Override
public CommandExecutionResult execute(SlashCommandContext context) {
MusicManager player = MusicManager.get(context.getGuild());
context.ackAndWait();
if (context.getGuildUser().getVoiceState() == null) {
throw new CommandExecutionException("Voice state is null, this should not happen", context, null);
}
if (!context.getGuildUser().getVoiceState().inAudioChannel()) {
return CommandExecutionResult.warn("You are not in a voice channel");
}
if (player.isConnected()) {
return CommandExecutionResult.warn("Player is already in a voice channel");
}
VoiceChannel vChannel = (VoiceChannel) context.getGuildUser().getVoiceState().getChannel();
if (vChannel != null) {
player.connect(vChannel);
if (MusicUtils.checkIfNotConnected(context)) {
context.reply("Joining current voice channel");
MusicUtils.connect(context, true);
}
return CommandExecutionResult.none();
@@ -7,9 +7,9 @@ package me.bobert.netneutrality.command.music;
*/
import me.bobert.netneutrality.command.AbstractMusicCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MusicUtils;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
@@ -40,9 +40,9 @@ public class LeaveCommand extends AbstractMusicCommand {
public CommandExecutionResult execute(MessageCommandContext context) {
MusicManager player = MusicManager.get(context.getGuild());
if (MusicUtils.checkIfConnected(player, context)) {
player.leave();
if (MusicUtils.checkIfConnected(context)) {
context.reply("Leaving voice channel and clearing queue");
player.leave();
}
return CommandExecutionResult.none();
@@ -51,11 +51,10 @@ public class LeaveCommand extends AbstractMusicCommand {
@Override
public CommandExecutionResult execute(SlashCommandContext context) {
MusicManager player = MusicManager.get(context.getGuild());
context.ackAndWait();
if (MusicUtils.checkIfConnected(player, context)) {
player.leave();
if (MusicUtils.checkIfConnected(context)) {
context.reply("Leaving voice channel and clearing queue");
player.leave();
}
return CommandExecutionResult.none();
@@ -7,9 +7,9 @@ package me.bobert.netneutrality.command.music;
*/
import me.bobert.netneutrality.command.AbstractMusicCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.music.MusicManager;
import net.dv8tion.jda.api.interactions.commands.OptionMapping;
import net.dv8tion.jda.api.interactions.commands.OptionType;
@@ -39,12 +39,13 @@ public class PlayCommand extends AbstractMusicCommand {
@Override
public CommandExecutionResult execute(MessageCommandContext context) {
MusicManager player = MusicManager.get(context.getGuild());
MusicManager manager = MusicManager.get(context.getGuild());
if (context.getArgs().length > 0) {
player.play(context.getMessageText(), context, false, false);
if (context.getArgs().length == 0) {
return CommandExecutionResult.warn("Please specify a query");
}
manager.play(context.getMessageText(), context);
return CommandExecutionResult.none();
}
@@ -52,9 +53,9 @@ public class PlayCommand extends AbstractMusicCommand {
public CommandExecutionResult execute(SlashCommandContext context) {
MusicManager manager = MusicManager.get(context.getGuild());
String query = context.getEvent().getOption("query", OptionMapping::getAsString);
context.ackAndWait();
context.ack();
manager.play(query, context, false, false);
manager.play(query, context);
return CommandExecutionResult.none();
}
}
@@ -6,9 +6,9 @@ package me.bobert.netneutrality.command.music;
*/
import me.bobert.netneutrality.command.AbstractMusicCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MessageFunction;
import me.bobert.netneutrality.utils.MusicUtils;
@@ -44,6 +44,7 @@ public class QueueCommand extends AbstractMusicCommand {
@Override
public void registerSlashCommandOptions(SlashCommandData data) {
data.addSubcommands(
new SubcommandData("clear-all", "Clear all queued tracks"),
new SubcommandData("delete-track", "Remove a single track from the queue")
@@ -62,7 +63,7 @@ public class QueueCommand extends AbstractMusicCommand {
public CommandExecutionResult execute(MessageCommandContext context) {
MusicManager player = MusicManager.get(context.getGuild());
if (!MusicUtils.checkIfConnected(player, context))
if (!MusicUtils.checkIfConnected(context))
return CommandExecutionResult.none();
if (context.getArgs().length == 0) {
@@ -104,54 +105,57 @@ public class QueueCommand extends AbstractMusicCommand {
@Override
public CommandExecutionResult execute(SlashCommandContext context) {
MusicManager manager = MusicManager.get(context.getGuild());
context.ackAndWait();
context.ack();
String sub = context.getEvent().getFullCommandName().split(" ")[1];
if (!MusicUtils.checkIfConnected(manager, context)) {
return CommandExecutionResult.none();
}
if (MusicUtils.checkIfConnected(context)) {
switch (sub) {
case "clear-all" -> {
return manager.getScheduler().getQueueManager().clearQueue();
}
case "delete-track" -> {
int position = context.getEvent().getOption("position", 0, OptionMapping::getAsInt);
return manager.getScheduler().getQueueManager().removeFromQueue(position);
}
case "show-queue" -> context.reply(MessageFunction.buildViewQueueMessage(manager, true));
case "shuffle" -> {
return manager.getScheduler().getQueueManager().shuffle();
}
case "from-user" -> {
Member user = context.getEvent().getOption("user", OptionMapping::getAsMember);
if (user == null) {
context.reply("The user isn't from this guild");
break;
switch (sub) {
case "clear-all" -> {
return manager.getScheduler().getQueueManager().clearQueue();
}
context.reply(MessageFunction.buildUserTracksMessage(manager, user));
}
case "remove-from-user" -> {
Member member = context.getEvent().getOption("user", OptionMapping::getAsMember);
if (member == null) {
context.reply("The user isn't from this guild");
break;
case "delete-track" -> {
int position = context.getEvent().getOption("position", 0, OptionMapping::getAsInt);
return manager.getScheduler().getQueueManager().removeFromQueue(position);
}
int size = manager.getScheduler()
.getQueueManager()
.getQueueWithFilter(trackInfo -> member.equals(trackInfo.getRequester()))
.size();
case "show-queue" -> {
if (manager.getQueueSize() == 0) {
return CommandExecutionResult.info("Queue is empty!");
}
context.reply(MessageFunction.buildViewQueueMessage(manager, true));
}
case "shuffle" -> {
return manager.getScheduler().getQueueManager().shuffle();
}
case "from-user" -> {
Member user = context.getEvent().getOption("user", OptionMapping::getAsMember);
if (user == null) {
return CommandExecutionResult.warn("The user isn't from this guild");
}
context.reply(MessageFunction.buildUserTracksMessage(manager, user));
}
case "remove-from-user" -> {
Member member = context.getEvent().getOption("user", OptionMapping::getAsMember);
if (member == null) {
return CommandExecutionResult.warn("The user isn't from this guild");
}
int size = manager.getScheduler()
.getQueueManager()
.getQueueWithFilter(trackInfo -> member.equals(trackInfo.getRequester()))
.size();
if (size == 0) {
return CommandExecutionResult.info(String.format("No tracks added by user %s", member.getEffectiveName()));
}
if (size == 0) {
context.reply("No tracks added by user %s", member.getEffectiveName());
} else {
manager.getScheduler().getQueueManager().removeTracksFromUser(member);
context.reply("Removed all tracks from user %s", member.getEffectiveName());
}
@@ -7,8 +7,8 @@ package me.bobert.netneutrality.command.music;
*/
import me.bobert.netneutrality.command.AbstractMusicCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MusicUtils;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
@@ -37,9 +37,9 @@ public class ShuffleCommand extends AbstractMusicCommand {
@Override
public CommandExecutionResult execute(SlashCommandContext context) {
MusicManager manager = MusicManager.get(context.getGuild());
context.ackAndWait();
context.ack();
if (MusicUtils.checkIfConnected(manager, context)) {
if (MusicUtils.checkIfConnected(context)) {
return manager.getScheduler().getQueueManager().shuffle();
}
@@ -7,9 +7,9 @@ package me.bobert.netneutrality.command.music;
*/
import me.bobert.netneutrality.command.AbstractMusicCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MusicUtils;
import me.bobert.netneutrality.utils.UserFunction;
@@ -45,7 +45,7 @@ public class SkipCommand extends AbstractMusicCommand {
public CommandExecutionResult execute(MessageCommandContext context) {
MusicManager player = MusicManager.get(context.getGuild());
if (!MusicUtils.checkIfConnected(player, context))
if (!MusicUtils.checkIfConnected(context))
return CommandExecutionResult.none();
if (UserFunction.isAdmin(context.getGuildUser()) || MusicUtils.isUserDj(context.getGuildUser())) {
@@ -73,9 +73,9 @@ public class SkipCommand extends AbstractMusicCommand {
public CommandExecutionResult execute(SlashCommandContext context) {
MusicManager manager = MusicManager.get(context.getGuild());
int skips = context.getEvent().getOption("skip", 1, OptionMapping::getAsInt);
context.ackAndWait();
context.ack();
if (MusicUtils.checkIfConnected(manager, context)) {
if (MusicUtils.checkIfConnected(context)) {
return manager.getScheduler().getQueueManager().skip(skips);
}
@@ -7,9 +7,9 @@ package me.bobert.netneutrality.command.music;
*/
import me.bobert.netneutrality.command.AbstractMusicCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MusicUtils;
import me.bobert.netneutrality.utils.UserFunction;
@@ -41,7 +41,7 @@ public class StopCommand extends AbstractMusicCommand {
public CommandExecutionResult execute(MessageCommandContext context) {
MusicManager player = MusicManager.get(context.getGuild());
if (!MusicUtils.checkIfConnected(player, context))
if (!MusicUtils.checkIfConnected(context))
return CommandExecutionResult.none();
// Need a resume command before
@@ -56,9 +56,9 @@ public class StopCommand extends AbstractMusicCommand {
@Override
public CommandExecutionResult execute(SlashCommandContext context) {
MusicManager manager = MusicManager.get(context.getGuild());
context.ackAndWait();
context.ack();
if (MusicUtils.checkIfConnected(manager, context)) {
if (MusicUtils.checkIfConnected(context)) {
manager.stopTrack();
return CommandExecutionResult.success("Stopping music playback");
}
@@ -6,9 +6,9 @@ package me.bobert.netneutrality.command.music;
*/
import me.bobert.netneutrality.command.AbstractMusicCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MessageFunction;
import me.bobert.netneutrality.utils.MusicUtils;
@@ -40,7 +40,7 @@ public class ViewQueueCommand extends AbstractMusicCommand {
public CommandExecutionResult execute(MessageCommandContext context) {
MusicManager player = MusicManager.get(context.getGuild());
if (MusicUtils.checkIfConnected(player, context)) {
if (MusicUtils.checkIfConnected(context)) {
int queueSize = player.getQueueSize();
if (queueSize > 0) {
context.reply(MessageFunction.buildViewQueueMessage(player, true));
@@ -55,16 +55,15 @@ public class ViewQueueCommand extends AbstractMusicCommand {
@Override
public CommandExecutionResult execute(SlashCommandContext context) {
MusicManager manager = MusicManager.get(context.getGuild());
context.ackAndWait();
context.ack();
if (MusicUtils.checkIfConnected(manager, context)) {
if (MusicUtils.checkIfConnected(context)) {
int queueSize = manager.getQueueSize();
if (queueSize > 0) {
context.reply(MessageFunction.buildViewQueueMessage(manager, true));
} else {
if (queueSize == 0) {
return CommandExecutionResult.warn("Queue is empty!");
}
context.reply(MessageFunction.buildViewQueueMessage(manager, true));
}
return CommandExecutionResult.none();
@@ -7,9 +7,9 @@ package me.bobert.netneutrality.command.music;
*/
import me.bobert.netneutrality.command.AbstractMusicCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MusicUtils;
import me.bobert.netneutrality.utils.UserFunction;
@@ -45,7 +45,7 @@ public class VolumeCommand extends AbstractMusicCommand {
public CommandExecutionResult execute(MessageCommandContext context) {
MusicManager player = MusicManager.get(context.getGuild());
if (!MusicUtils.checkIfConnected(player, context))
if (!MusicUtils.checkIfConnected(context))
return CommandExecutionResult.none();
if (UserFunction.isAdmin(context.getGuildUser()) || MusicUtils.isUserDj(context.getGuildUser())) {
@@ -71,7 +71,7 @@ public class VolumeCommand extends AbstractMusicCommand {
MusicManager manager = MusicManager.get(context.getGuild());
int volume = context.getEvent().getOption("volume", 30, OptionMapping::getAsInt);
if (MusicUtils.checkIfConnected(manager, context)) {
if (MusicUtils.checkIfConnected(context)) {
manager.setVolume(volume);
return CommandExecutionResult.success(String.format("Volume set to %s", volume));
}
@@ -6,9 +6,9 @@ package me.bobert.netneutrality.command.music;
*/
import me.bobert.netneutrality.command.AbstractMusicCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.music.MusicManager;
import me.bobert.netneutrality.utils.MusicUtils;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
@@ -39,8 +39,8 @@ public class VoteSkipCommand extends AbstractMusicCommand {
public CommandExecutionResult execute(MessageCommandContext context) {
MusicManager manager = MusicManager.get(context.getGuild());
if (MusicUtils.checkIfConnected(manager, context)) {
manager.voteSkip(context);
if (MusicUtils.checkIfConnected(context)) {
return manager.voteSkip(context);
}
return CommandExecutionResult.none();
@@ -50,7 +50,7 @@ public class VoteSkipCommand extends AbstractMusicCommand {
public CommandExecutionResult execute(SlashCommandContext context) {
MusicManager manager = MusicManager.get(context.getGuild());
if (MusicUtils.checkIfConnected(manager, context)) {
if (MusicUtils.checkIfConnected(context)) {
return manager.voteSkip(context);
}
@@ -0,0 +1,141 @@
package me.bobert.netneutrality.context;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.AsyncMessage;
import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.utils.Settings;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.interactions.InteractionHook;
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.concurrent.TimeUnit;
import java.util.function.Consumer;
public class MessageCommandContext extends CommandContext {
private final MessageReceivedEvent event;
private final Message message;
public MessageCommandContext(MessageReceivedEvent event) {
super(event.getJDA(), event.getGuild(), event.getChannel(), event.getMember(), event.getAuthor());
this.event = event;
this.message = event.getMessage();
}
@Override
public MessageReceivedEvent getEvent() {
return event;
}
public String getArgument(int index) {
return getArgs()[index];
}
public String[] getArgs() {
String msg = message.getContentDisplay().trim();
msg = msg.substring(Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$").length()).trim();
String cmd = msg.split(" ", 2)[0];
String[] args = msg.replaceFirst("(" + cmd + ")(\\s+)?", "").split(" ");
if (args.length == 1 && args[0].isEmpty())
args = new String[0];
return args;
}
public String getMessageText() {
String msg = message.getContentDisplay().trim();
msg = msg.substring(Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$").length()).trim();
String cmd = msg.split(" ", 2)[0];
return msg.replaceFirst("(" + cmd + ")( )?", "");
}
public Message getMessage() {
return message;
}
@Override
public boolean isAcknowledged() {
return true;
}
@Override
public InteractionHook getInteractionHook() {
return null;
}
@Override
public void defer(Consumer<AsyncMessage> consumer) {
reply("Bot is thinking...", consumer);
}
@Override
public void reply(MessageCreateData messageCreateData, boolean ephemeral, Consumer<AsyncMessage> consumer, LayoutComponent... components) {
if (getChannel() != null && getChannel().canTalk()) {
message.reply(messageCreateData).setComponents(components).queue(message1 -> consumer.accept(new AsyncMessage(message1)));
}
}
@Override
public void reply(Modal modal) {
}
@Override
public void replyEphemeral(MessageCreateData message) {
sendMessageInPrivate(message);
}
@Override
public void ack(boolean ephemeral) {
}
@Override
public void deleteMessage() {
if (message != null) {
message.delete().queueAfter(1, TimeUnit.SECONDS);
}
}
public boolean isMessageFromOwner() {
return getGuildUser().isOwner();
}
public boolean isMessageFromCreator() {
String creatorId = Settings.getInstance().get("NETNEUTRALITY_BOT_OWNER");
return creatorId.equals(getDiscordUser().getId());
}
@Override
public String parseCommandName() {
String rawMessage = message.getContentDisplay().trim();
String prefix = Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$");
if (rawMessage.startsWith(prefix)) {
rawMessage = rawMessage.replaceFirst(String.format("^[%s\\s]{1,}", prefix), "").trim();
// rawMessage = rawMessage.substring(prefix.length()).trim();
return rawMessage.split(" ", 2)[0];
}
return null;
}
@Override
public String getSubCommand() {
if (getArgs().length > 0) {
return getArgument(0);
}
return "";
}
}
@@ -0,0 +1,66 @@
package me.bobert.netneutrality.context;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.AsyncMessage;
import me.bobert.netneutrality.core.context.AbstractContext;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
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.modals.Modal;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import java.util.function.Consumer;
public class MusicChannelContext extends AbstractContext {
public MusicChannelContext(GuildMessageChannel channel) {
super(channel.getJDA(), channel.getGuild(), channel, null, null);
}
@Override
public Event getEvent() {
return null;
}
@Override
public InteractionHook getInteractionHook() {
return null;
}
@Override
public boolean isAcknowledged() {
return true;
}
@Override
public void deleteMessage() {
}
@Override
public void reply(Modal modal) {
}
@Override
public void reply(MessageCreateData message, boolean ephemeral, Consumer<AsyncMessage> consumer, LayoutComponent... components) {
if (getChannel().canTalk()) {
getChannel().sendMessage(message).setComponents(components).queue(message1 -> consumer.accept(new AsyncMessage(message1)));
}
}
@Override
public void defer(Consumer<AsyncMessage> consumer) {
reply("Bot is thinking", consumer);
}
@Override
public void ack(boolean ephemeral) {
}
}
@@ -0,0 +1,88 @@
package me.bobert.netneutrality.context;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.AsyncMessage;
import me.bobert.netneutrality.core.context.CommandContext;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.InteractionHook;
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.function.Consumer;
public class SlashCommandContext extends CommandContext {
private final SlashCommandInteractionEvent event;
public SlashCommandContext(SlashCommandInteractionEvent event) {
super(event.getJDA(), event.getGuild(), event.getChannel(), event.getMember(), event.getUser());
this.event = event;
}
@Override
public SlashCommandInteractionEvent getEvent() {
return event;
}
@Override
public boolean isAcknowledged() {
return event.isAcknowledged();
}
@Override
public InteractionHook getInteractionHook() {
if (!event.isAcknowledged())
throw new RuntimeException("Interaction isn't acknowledged yet");
return event.getHook();
}
@Override
public void deleteMessage() {
// not implemented
}
@Override
public void defer(Consumer<AsyncMessage> consumer) {
event.deferReply().queue(hook -> consumer.accept(new AsyncMessage(hook)));
}
@Override
public void reply(MessageCreateData message, boolean ephemeral, Consumer<AsyncMessage> consumer, LayoutComponent... components) {
if (isAcknowledged()) {
getInteractionHook().sendMessage(message).setEphemeral(ephemeral).setComponents(components)
.queue(message1 -> consumer.accept(new AsyncMessage(message1)));
return;
}
event.reply(message).setEphemeral(ephemeral).setComponents(components)
.queue(message1 -> consumer.accept(new AsyncMessage(message1)));
}
@Override
public void reply(Modal modal) {
event.replyModal(modal).queue();
}
@Override
public void ack(boolean ephemeral) {
if (event.isAcknowledged())
throw new RuntimeException("Interaction is already acknowledged, please use getInteractionHook() instead.");
event.deferReply(ephemeral).queue();
}
@Override
public String parseCommandName() {
return event.getName();
}
@Override
public String getSubCommand() {
return event.getFullCommandName().split(" ")[1];
}
}
@@ -0,0 +1,70 @@
package me.bobert.netneutrality.context.tasks;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.AsyncMessage;
import me.bobert.netneutrality.core.context.TaskContext;
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import java.util.function.Consumer;
public class ButtonTaskContext extends TaskContext {
private final ButtonInteractionEvent event;
public ButtonTaskContext(ButtonInteractionEvent event) {
super(event.getJDA(), event.getGuild(), event.getChannel(), event.getMember(), event.getUser());
this.event = event;
}
@Override
public ButtonInteractionEvent getEvent() {
return event;
}
@Override
public String getId() {
return event.getComponentId();
}
@Override
public InteractionHook getInteractionHook() {
return event.getHook();
}
@Override
public void defer(Consumer<AsyncMessage> consumer) {
event.deferReply().queue(hook -> consumer.accept(new AsyncMessage(hook)));
}
@Override
public boolean isAcknowledged() {
return event.isAcknowledged();
}
@Override
public void reply(MessageCreateData message, boolean ephemeral, Consumer<AsyncMessage> consumer, LayoutComponent... components) {
if (isAcknowledged()) {
getInteractionHook().sendMessage(message)
.setEphemeral(ephemeral)
.setComponents(components)
.queue(message1 -> consumer.accept(new AsyncMessage(message1)));
return;
}
event.reply(message)
.setEphemeral(ephemeral)
.setComponents(components)
.queue(message1 -> consumer.accept(new AsyncMessage(message1)));
}
@Override
public void ack(boolean ephemeral) {
event.deferReply(ephemeral).queue();
}
}
@@ -0,0 +1,78 @@
package me.bobert.netneutrality.context.tasks;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.AsyncMessage;
import me.bobert.netneutrality.core.context.TaskContext;
import net.dv8tion.jda.api.entities.MessageReaction;
import net.dv8tion.jda.api.entities.emoji.EmojiUnion;
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import java.util.function.Consumer;
public class EmojiTaskContext extends TaskContext {
private final MessageReactionAddEvent event;
private final EmojiUnion emoji;
public EmojiTaskContext(MessageReactionAddEvent event) {
super(event.getJDA(), event.getGuild(), event.getChannel(), event.getMember(), event.getUser());
this.event = event;
this.emoji = event.getEmoji();
}
public String getEmoji() {
return emoji.getAsReactionCode();
}
public MessageReaction getReaction() {
return event.getReaction();
}
@Override
public MessageReactionAddEvent getEvent() {
return event;
}
@Override
public String getId() {
return String.format("%s:%s", getGuild().getId(), event.getMessageId());
}
@Override
public InteractionHook getInteractionHook() {
return null;
}
@Override
public void defer(Consumer<AsyncMessage> consumer) {
reply("Bot is thinking...", consumer);
}
@Override
public boolean isAcknowledged() {
return true;
}
@Override
public void reply(MessageCreateData message, boolean ephemeral, Consumer<AsyncMessage> consumer, LayoutComponent... components) {
if (getChannel() != null && getChannel().canTalk()) {
getChannel().sendMessage(message).setComponents(components).queue(message1 -> consumer.accept(new AsyncMessage(message1)));
}
}
@Override
public void replyEphemeral(MessageCreateData message) {
sendMessageInPrivate(message);
}
@Override
public void ack(boolean ephemeral) {
}
}
@@ -0,0 +1,66 @@
package me.bobert.netneutrality.context.tasks;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.AsyncMessage;
import me.bobert.netneutrality.core.context.TaskContext;
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.interactions.components.LayoutComponent;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import java.util.function.Consumer;
public class ModalTaskContext extends TaskContext {
private final ModalInteractionEvent event;
public ModalTaskContext(ModalInteractionEvent event) {
super(event.getJDA(), event.getGuild(), event.getChannel(), event.getMember(), event.getUser());
this.event = event;
}
@Override
public ModalInteractionEvent getEvent() {
return event;
}
@Override
public String getId() {
return event.getModalId();
}
@Override
public InteractionHook getInteractionHook() {
return event.getHook();
}
@Override
public void defer(Consumer<AsyncMessage> consumer) {
event.deferReply().queue(hook -> consumer.accept(new AsyncMessage(hook)));
}
@Override
public boolean isAcknowledged() {
return event.isAcknowledged();
}
@Override
public void reply(MessageCreateData message, boolean ephemeral, Consumer<AsyncMessage> consumer, LayoutComponent... components) {
if (isAcknowledged()) {
getInteractionHook().sendMessage(message).setEphemeral(ephemeral).setComponents(components)
.queue(message1 -> consumer.accept(new AsyncMessage(message1)));
return;
}
event.reply(message).setEphemeral(ephemeral).setComponents(components)
.queue(message1 -> consumer.accept(new AsyncMessage(message1)));
}
@Override
public void ack(boolean ephemeral) {
event.deferReply(ephemeral).queue();
}
}
@@ -0,0 +1,97 @@
package me.bobert.netneutrality.core;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.context.AbstractContext;
import net.dv8tion.jda.api.entities.Message;
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.modals.Modal;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import net.dv8tion.jda.api.utils.messages.MessageEditData;
import java.util.function.Consumer;
public class AsyncMessage extends AbstractContext {
private final InteractionHook hook;
private final Message message;
public AsyncMessage(InteractionHook hook) {
super(hook.getJDA(), hook.getInteraction().getGuild(), hook.getInteraction().getMessageChannel(), hook.getInteraction().getMember(), hook.getInteraction().getUser());
this.hook = hook;
this.message = null;
}
public AsyncMessage(Message message) {
super(message.getJDA(), message.getGuild(), message.getChannel(), message.getMember(), message.getAuthor());
this.hook = null;
this.message = message;
}
@Override
public Event getEvent() {
return null;
}
@Override
public InteractionHook getInteractionHook() {
return hook;
}
@Override
public boolean isAcknowledged() {
return true;
}
@Override
public void deleteMessage() {
if (hook != null) {
hook.deleteOriginal().queue();
}
if (message != null) {
message.delete().queue();
}
}
@Override
public void reply(Modal modal) {
}
@Override
public void reply(MessageCreateData messageCreateData, boolean ephemeral, Consumer<AsyncMessage> consumer, LayoutComponent... components) {
if (hook != null) {
hook.sendMessage(messageCreateData).queue();
}
if (message != null) {
message.reply(messageCreateData).queue();
}
}
@Override
public void defer(Consumer<AsyncMessage> consumer) {
}
@Override
public void ack(boolean ephemeral) {
}
@Override
public void edit(MessageEditData messageEditData, boolean ephemeral, Consumer<AsyncMessage> consumer, LayoutComponent... components) {
if (hook != null) {
hook.editOriginal(messageEditData).queue();
}
if (message != null) {
message.editMessage(messageEditData).queue();
}
}
}
@@ -34,12 +34,12 @@ public class BotState {
JSONObject music = new JSONObject();
guild.put("id", manager.getGuild().getId());
MessageChannel currentChannel = trackScheduler.getTextChannel();
if (currentChannel == null) {
music.put("current_channel", "null");
} else {
music.put("current_channel", currentChannel.getId());
}
// MessageChannel currentChannel = trackScheduler.getTextChannel();
// if (currentChannel == null) {
// music.put("current_channel", "null");
// } else {
// music.put("current_channel", currentChannel.getId());
// }
guild.put("music", music);
@@ -6,6 +6,7 @@ package me.bobert.netneutrality.core;
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.db.SessionManager;
import me.bobert.netneutrality.db.GSettings;
import me.bobert.netneutrality.db.entities.OGuild;
import me.bobert.netneutrality.db.entities.OGuildSetting;
@@ -48,7 +49,6 @@ public class GuildConfigManager {
return value;
}
public void setSettingValue(GSettings setting, String value) {
Session session = SessionManager.getSessionFactory().openSession();
JpaUtils.tryLoad(session, guildEntity, guildEntity.getGuildId());
@@ -6,16 +6,13 @@ package me.bobert.netneutrality.core;
*/
import me.bobert.netneutrality.NetNeutrality;
import me.bobert.netneutrality.db.DBController;
import me.bobert.netneutrality.db.controllers.DBMessage;
import me.bobert.netneutrality.core.db.SessionManager;
import me.bobert.netneutrality.db.entities.OBlacklist;
import me.bobert.netneutrality.db.entities.OGuild;
import me.bobert.netneutrality.thread.ThreadManager;
import me.bobert.netneutrality.utils.JpaUtils;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.entities.Role;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.Session;
@@ -36,15 +33,6 @@ public class GuildManager {
private final Guild guild;
private OGuild guildEntity;
private GuildManager(Guild guild) {
this.guild = guild;
this.guildEntity = loadGuildFromDb(guild.getId());
this.guildConfigManager = new GuildConfigManager(guild, this.guildEntity);
managers.put(guild.getId(), this);
reloadBlacklistCache();
}
private GuildManager(String guildId) {
Guild temp = NetNeutrality.getJDA().getGuildById(guildId);
if (temp == null)
@@ -59,11 +47,7 @@ public class GuildManager {
}
public static GuildManager getManager(Guild guild) {
if (managers.containsKey(guild.getId())) {
return managers.get(guild.getId());
} else {
return new GuildManager(guild);
}
return getManager(guild.getId());
}
public static GuildManager getManager(String guildId) {
@@ -104,6 +88,14 @@ public class GuildManager {
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;
@@ -155,12 +147,12 @@ public class GuildManager {
}
public void removeFromDB() {
Session session = sessionFactory.openSession();
session.beginTransaction();
try(Session session = sessionFactory.openSession()) {
session.beginTransaction();
session.remove(guildEntity);
session.getTransaction().commit();
session.close();
session.remove(guildEntity);
session.getTransaction().commit();
}
managers.remove(guild.getId());
}
@@ -7,9 +7,9 @@ package me.bobert.netneutrality.core;
import io.sentry.Sentry;
import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.exceptions.CommandException;
import me.bobert.netneutrality.exceptions.CommandExecutionException;
import me.bobert.netneutrality.core.context.Context;
import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.exceptions.ContextException;
import me.bobert.netneutrality.utils.MessageFunction;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
@@ -18,7 +18,7 @@ import org.apache.logging.log4j.Logger;
public class LogHandler {
private final static Logger logger = LogManager.getLogger(LogHandler.class);
private static final Logger logger = LogManager.getLogger(LogHandler.class);
/**
* Log an exception accordingly
@@ -28,12 +28,10 @@ public class LogHandler {
public static void log(Exception e) {
Sentry.captureException(e);
if (e instanceof CommandExecutionException exception) {
CommandContext context = exception.getCommandContext();
logInternally(exception, context);
tryLogToDebugChannel(exception, context);
tryGracefulSlashContextError(exception, context);
if (e instanceof ContextException exception) {
logInternally(exception);
tryLogToDebugChannel(exception);
tryGracefulContextError(exception);
return;
}
@@ -60,26 +58,35 @@ public class LogHandler {
}
}
private static void logInternally(CommandException exception, CommandContext context) {
logger.error("Exception thrown from command [{}] in guild [{}]: {}", context.getCommand().getCommandName(), context.getGuild().getId(), exception.getMessage(), exception);
private static void logInternally(ContextException exception) {
Context context = exception.getContext();
logger.error("Exception thrown from context in guild {}: {}", context.getGuild() == null ? "PrivateMessage": context.getGuild().getId(), exception.getMessage());
if (context instanceof CommandContext commandContext) {
logger.error("Caused by command: {}", commandContext.getCommand().getCommandName(), exception);
}
if (context instanceof TaskContext taskContext) {
logger.error("Caused by task with ID: {}", taskContext.getId(), exception);
}
}
private static void tryLogToDebugChannel(CommandException exception, CommandContext context) {
private static void tryLogToDebugChannel(ContextException exception) {
Context context = exception.getContext();
if (context.getGuild() != null) {
if (GuildManager.getManager(context.getGuild()).isDebugEnabled()) {
if (context.getGuildManager().isDebugEnabled()) {
context.getGuild()
.getTextChannels()
.stream()
.filter(textChannel -> textChannel.getName().equals("debug") || textChannel.getName().equals("log"))
.filter(TextChannel::canTalk)
.forEach(textChannel -> textChannel.sendMessageEmbeds(MessageFunction.buildDebugMessage(context, exception)).queue());
.forEach(textChannel -> textChannel.sendMessageEmbeds(MessageFunction.buildDebugMessage(exception)).queue());
}
}
}
private static void tryGracefulSlashContextError(CommandException exception, CommandContext context) {
if (context instanceof SlashCommandContext slashCommandContext) {
slashCommandContext.reply(MessageFunction.buildSentryErrorMessage(exception));
}
private static void tryGracefulContextError(ContextException exception) {
exception.getContext().reply(MessageFunction.buildSentryErrorMessage(exception));
}
}
@@ -1,4 +1,4 @@
package me.bobert.netneutrality.command;
package me.bobert.netneutrality.core.command;
/*
* Copyright (c) anthorob
*
@@ -41,6 +41,10 @@ public class CommandExecutionResult {
return new CommandExecutionResult(Result.SUCCESS, message);
}
public static CommandExecutionResult success(String format, Object... args) {
return success(String.format(format, args));
}
public static CommandExecutionResult info(String message) {
return new CommandExecutionResult(Result.INFO, message);
}
@@ -1,16 +1,18 @@
package me.bobert.netneutrality.command;
package me.bobert.netneutrality.core.command;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.NetNeutrality;
import me.bobert.netneutrality.command.Command;
import me.bobert.netneutrality.core.LogHandler;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.interactions.commands.build.CommandData;
import net.dv8tion.jda.api.interactions.commands.build.Commands;
import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData;
import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.reflections.Reflections;
@@ -24,35 +26,33 @@ import java.util.Set;
public class CommandManager {
private final static Logger logger = LogManager.getLogger(CommandManager.class);
public static final HashMap<String, AbstractCommand> commands = new HashMap<>();
private static final Logger logger = LogManager.getLogger(CommandManager.class);
public static final HashMap<String, Command> commands = new HashMap<>();
public static void init(JDA jda) {
logger.info("Loading commands..");
public static void init() {
initCommands();
registerSlashCommands();
registerSlashCommands(jda);
logger.info("Loaded {} commands!", commands.size());
}
public static boolean isValidCommand(String commandString) {
return commands.containsKey(commandString);
}
public static AbstractCommand getCommand(String commandString) {
public static Command getCommand(String commandString) {
return commands.get(commandString);
}
private static void initCommands() {
logger.info("Loading commands..");
Reflections reflections = new Reflections("me.bobert.netneutrality.command");
Set<Class<? extends AbstractCommand>> classes = reflections.getSubTypesOf(AbstractCommand.class);
Set<Class<? extends Command>> classes = reflections.getSubTypesOf(Command.class);
for (Class<? extends AbstractCommand> s : classes) {
for (Class<? extends Command> s : classes) {
try {
if (Modifier.isAbstract(s.getModifiers())) {
continue;
}
AbstractCommand command = s.getConstructor().newInstance();
Command command = s.getConstructor().newInstance();
if (!command.isEnabled())
continue;
@@ -65,25 +65,34 @@ public class CommandManager {
LogHandler.log(e);
}
}
logger.info("Loaded {} commands!", commands.size());
}
private static void registerSlashCommands() {
CommandListUpdateAction commandListUpdateAction = NetNeutrality.getJDA().updateCommands();
private static void registerSlashCommands(JDA jda) {
CommandListUpdateAction commandListUpdateAction = jda.updateCommands();
List<CommandData> slashCommands = new ArrayList<>();
for (AbstractCommand command : commands.values()) {
for (Command command : commands.values()) {
if (!command.isEnabled() || !command.isSlashCommand()) {
continue;
}
SlashCommandData data = Commands.slash(command.getCommandName(), command.getDescription());
data.setDefaultPermissions(command.getSlashCommandPermission());
data.setGuildOnly(command.isGuildOnly());
command.registerSlashCommandOptions(data);
logger.debug("Registering slash command: {}", command.getCommandName());
String description = command.getDescription();
if (StringUtils.isBlank(description)) {
description = "No description provided for this command";
}
try {
SlashCommandData data = Commands.slash(command.getCommandName(), description);
data.setDefaultPermissions(command.getSlashCommandPermission());
data.setGuildOnly(command.isGuildOnly());
command.registerSlashCommandOptions(data);
slashCommands.add(data);
} catch (Exception e) {
logger.error("Error registering slash command: {}", command.getCommandName(), e);
}
slashCommands.add(data);
}
commandListUpdateAction.addCommands(slashCommands).queue();
@@ -1,4 +1,4 @@
package me.bobert.netneutrality.core;
package me.bobert.netneutrality.core.command;
/*
* Copyright (c) anthorob
*
@@ -6,54 +6,51 @@ package me.bobert.netneutrality.core;
*/
import io.sentry.Sentry;
import me.bobert.netneutrality.command.AbstractCommand;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.command.CommandManager;
import me.bobert.netneutrality.command.Command;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.exceptions.CommandExecutionException;
import me.bobert.netneutrality.utils.UserFunction;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import static me.bobert.netneutrality.command.CommandExecutionResult.Result.ERROR;
import static me.bobert.netneutrality.command.CommandExecutionResult.Result.NOACTION;
import static me.bobert.netneutrality.core.command.CommandExecutionResult.Result.ERROR;
import static me.bobert.netneutrality.core.command.CommandExecutionResult.Result.NOACTION;
public class CommandProcessor {
private final static Logger logger = LogManager.getLogger(CommandProcessor.class);
private final String commandName;
private static final Logger logger = LogManager.getLogger(CommandProcessor.class);
private final CommandContext context;
public CommandProcessor(String commandName, CommandContext context) {
this.commandName = commandName;
public CommandProcessor(CommandContext context) {
this.context = context;
}
public void dispatch() {
Command command = CommandManager.getCommand(context.parseCommandName());
context.setCommand(command);
if (!CommandManager.isValidCommand(commandName)) {
return;
if (command == null) {
throw new IllegalArgumentException("Context has no command");
}
AbstractCommand command = CommandManager.getCommand(commandName);
logger.info("Executing command {} on guild {}", command.getCommandName(), context.getGuild().getId());
logger.info("Executing command {} on guild {}", command.getCommandName(), context.getGuild() == null ? "PrivateMessage": context.getGuild().getId());
if (context instanceof SlashCommandContext slashCommandContext)
logger.debug("SlashCommandInteractionId: {}", slashCommandContext.getEvent().getInteraction().getId());
configureSentry(command);
try {
configureSentry(command);
if (context instanceof MessageCommandContext && command.needAdmin() && !UserFunction.isAdmin(context.getGuildUser())) {
context.reply("You are not powerful enough, scrub");
return;
}
if (command.canBeExecutedBy(context.getGuild(), context.getGuildUser())) {
if (command.canBeExecutedFrom(context.getGuild(), context.getChannel())) {
if (command.canBeExecutedBy(context.getGuildUser())) {
if (command.canBeExecutedFrom(context.getChannel())) {
CommandExecutionResult commandExecutionResult = null;
if (context instanceof SlashCommandContext slashCommandContext) {
@@ -65,7 +62,7 @@ public class CommandProcessor {
}
if (commandExecutionResult == null) {
throw new RuntimeException("Command type is not implemented.");
throw new RuntimeException("CommandExecutionResult is null");
}
CommandExecutionResult.Result result = commandExecutionResult.getResult();
@@ -92,21 +89,25 @@ public class CommandProcessor {
}
}
private void configureSentry(AbstractCommand command) {
private void configureSentry(Command command) {
Sentry.configureScope(scope -> {
scope.setContexts("command", command.getCommandName());
scope.setContexts("isAdmin", UserFunction.isAdmin(context.getGuildUser()));
scope.setContexts("canBeExecuted", command.canBeExecutedBy(context.getGuild(), context.getGuildUser()));
scope.setTag("command", command.getCommandName());
scope.setTag("user", context.getGuildUser().getId());
scope.setTag("channel", context.getChannel().getId());
if (context.getGuild() != null) {
scope.setTag("guild", context.getGuild().getId());
scope.setTag("isAdmin", String.valueOf(UserFunction.isAdmin(context.getGuildUser())));
scope.setTag("canBeExecutedBy", String.valueOf(command.canBeExecutedBy(context.getGuildUser())));
}
if (context instanceof SlashCommandContext slashCommandContext)
scope.setTag("interactionId", slashCommandContext.getEvent().getInteraction().getId());
if (context instanceof MessageCommandContext messageCommandContext)
scope.setTag("message", messageCommandContext.getMessage().getId());
scope.setTag("guild", context.getGuild().getId());
});
}
}
@@ -0,0 +1,61 @@
package me.bobert.netneutrality.core.context;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import io.sentry.Sentry;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
public abstract class AbstractContext implements Context {
private final JDA jda;
private final Guild guild;
private final MessageChannel channel;
private final Member member;
private final User user;
public AbstractContext(JDA jda, Guild guild, MessageChannel channel, Member member, User user) {
this.jda = jda;
this.guild = guild;
this.channel = channel;
this.member = member;
this.user = user;
if (user != null) {
io.sentry.protocol.User sentryUser = new io.sentry.protocol.User();
sentryUser.setId(user.getId());
sentryUser.setUsername(user.getEffectiveName());
Sentry.setUser(sentryUser);
}
}
@Override
public JDA getJDA() {
return jda;
}
@Override
public Guild getGuild() {
return guild;
}
@Override
public MessageChannel getChannel() {
return channel;
}
@Override
public Member getGuildUser() {
return member;
}
@Override
public User getDiscordUser() {
return user;
}
}
@@ -1,60 +1,34 @@
package me.bobert.netneutrality.core.context;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.command.AbstractCommand;
import me.bobert.netneutrality.command.Command;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.events.Event;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
public interface CommandContext {
public abstract class CommandContext extends AbstractContext {
private Command command;
AbstractCommand getCommand();
Event getEvent();
Guild getGuild();
MessageChannel getChannel();
Member getGuildUser();
User getDiscordUser();
JDA getJDA();
void deleteMessage();
void reply(MessageCreateData message);
void reply(String message);
void reply(MessageEmbed... embeds);
default void reply(String format, Object... arguments) {
reply(String.format(format, arguments));
public CommandContext(JDA jda, Guild guild, MessageChannel channel, Member member, User user) {
super(jda, guild, channel, member, user);
}
void replyToMessage(String message);
default void replyToMessage(MessageCreateData message) {
reply(message);
public Command getCommand() {
return command;
}
default void replyToMessage(String format, Object... arguments) {
replyToMessage(String.format(format, arguments));
public void setCommand(Command command) {
this.command = command;
}
default void replyToMessage(MessageEmbed... embeds) {
replyToMessage(MessageCreateData.fromEmbeds(embeds));
}
public abstract String parseCommandName();
public abstract String getSubCommand();
}
@@ -0,0 +1,161 @@
package me.bobert.netneutrality.core.context;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.AsyncMessage;
import me.bobert.netneutrality.core.GuildManager;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.events.Event;
import net.dv8tion.jda.api.interactions.InteractionHook;
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.MessageEditData;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public interface Context {
Event getEvent();
JDA getJDA();
InteractionHook getInteractionHook();
Guild getGuild();
MessageChannel getChannel();
Member getGuildUser();
User getDiscordUser();
boolean isAcknowledged();
void deleteMessage();
void reply(Modal modal);
void reply(MessageCreateData message, boolean ephemeral, Consumer<AsyncMessage> consumer, LayoutComponent... components);
void defer(Consumer<AsyncMessage> consumer);
void ack(boolean ephemeral);
default GuildManager getGuildManager() {
return GuildManager.getManager(getGuild());
}
default void ack() {
ack(false);
}
default void ephemeral() {
ack(true);
}
default void reply(MessageCreateData message) {
reply(message, false, asyncMessage -> {});
}
default void reply(String message) {
reply(MessageCreateData.fromContent(message));
}
default void reply(MessageEmbed... embeds) {
reply(MessageCreateData.fromEmbeds(embeds));
}
default void reply(String format, Object... arguments) {
reply(String.format(format, arguments));
}
default void edit(MessageEditData message, boolean ephemeral, Consumer<AsyncMessage> consumer, LayoutComponent... components) {
if (isAcknowledged() && getInteractionHook() != null) {
getInteractionHook().editOriginal(message).setComponents(components).queue(message1 -> consumer.accept(new AsyncMessage(message1)));
return;
}
reply(MessageCreateData.fromEditData(message), ephemeral, consumer, components);
}
default void edit(MessageEditData message, LayoutComponent... components) {
edit(message, false, asyncMessage -> {}, components);
}
default void edit(MessageEditData message) {
edit(message, false, asyncMessage -> {});
}
default void edit(String message) {
edit(MessageEditData.fromContent(message));
}
default void edit(MessageEmbed embed) {
edit(MessageEditData.fromEmbeds(embed));
}
default void edit(MessageEmbed embeds, LayoutComponent... components) {
edit(MessageEditData.fromEmbeds(embeds), components);
}
default void edit(String format, Object... arguments) {
edit(String.format(format, arguments));
}
default void async(MessageCreateData message, Consumer<AsyncMessage> consumer) {
reply(message, false, consumer);
}
default void async(String message, Consumer<AsyncMessage> consumer) {
async(MessageCreateData.fromContent(message), consumer);
}
default void async(Consumer<AsyncMessage> consumer) {
defer(consumer);
}
default void replyEphemeral(MessageCreateData message) {
reply(message, true, asyncMessage -> {});
}
default void replyEphemeral(String message) {
replyEphemeral(MessageCreateData.fromContent(message));
}
default void replyEphemeral(String format, Object... arguments) {
replyEphemeral(String.format(format, arguments));
}
default void replyEphemeral(MessageEmbed... embeds) {
replyEphemeral(MessageCreateData.fromEmbeds(embeds));
}
default void sendMessageInPrivate(MessageCreateData msg) {
User user = getDiscordUser();
if (user != null) {
user.openPrivateChannel().flatMap(channel -> channel.sendMessage(msg)).queue();
}
}
default void sendMessageInPrivate(String message) {
sendMessageInPrivate(MessageCreateData.fromContent(message));
}
default void sendMessageInPrivate(MessageEmbed... embeds) {
sendMessageInPrivate(Arrays.asList(embeds));
}
default void sendMessageInPrivate(List<MessageEmbed> embeds) {
sendMessageInPrivate(MessageCreateData.fromEmbeds(embeds));
}
}
@@ -1,192 +0,0 @@
package me.bobert.netneutrality.core.context;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import io.sentry.Breadcrumb;
import io.sentry.Sentry;
import me.bobert.netneutrality.command.AbstractCommand;
import me.bobert.netneutrality.core.GuildManager;
import me.bobert.netneutrality.thread.ThreadManager;
import me.bobert.netneutrality.utils.Settings;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import java.util.Collection;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
public class MessageCommandContext implements CommandContext {
private static final ExecutorService commandExecutor = ThreadManager.getExecutor();
private AbstractCommand command;
private final MessageReceivedEvent event;
private final Message message;
private final Guild guild;
private final MessageChannel channel;
private final Member member;
private final User user;
private final JDA jda;
public MessageCommandContext(MessageReceivedEvent event, Message message, Member member, User user, Guild guild, MessageChannel channel, JDA jda) {
this.event = event;
this.message = message;
this.member = member;
this.guild = guild;
this.channel = channel;
this.user = user;
this.jda = jda;
io.sentry.protocol.User sentryUser = new io.sentry.protocol.User();
sentryUser.setId(member.getId());
sentryUser.setUsername(member.getEffectiveName());
Sentry.setUser(sentryUser);
Sentry.addBreadcrumb(Breadcrumb.info("New context for message id " + message.getId()));
}
public static MessageCommandContext createContext(MessageReceivedEvent event) {
Message message = event.getMessage();
Member member = event.getMember();
User user = event.getAuthor();
Guild guild = event.getGuild();
MessageChannel channel = event.getChannel();
JDA jda = event.getJDA();
return new MessageCommandContext(event, message, member, user, guild, channel, jda);
}
@Override
public MessageReceivedEvent getEvent() {
return event;
}
public String getArgument(int index) {
return getArgs()[index];
}
public String[] getArgs() {
String msg = message.getContentDisplay().trim();
msg = msg.substring(Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$").length()).trim();
String cmd = msg.split(" ", 2)[0];
String[] args = msg.replaceFirst("(" + cmd + ")(\\s+)?", "").split("\s");
if (args.length == 1 && args[0].isEmpty())
args = new String[0];
return args;
}
public String getMessageText() {
String msg = message.getContentDisplay().trim();
msg = msg.substring(Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$").length()).trim();
String cmd = msg.split(" ", 2)[0];
return msg.replaceFirst("(" + cmd + ")(\s)?", "");
}
public Message getMessage() {
return message;
}
public Guild getGuild() {
return guild;
}
public MessageChannel getChannel() {
return channel;
}
public Member getGuildUser() {
return member;
}
public User getDiscordUser() {
return user;
}
public JDA getJDA() {
return jda;
}
public AbstractCommand getCommand() {
return command;
}
public GuildManager getGuildManager() {
return GuildManager.getManager(guild);
}
public void reply(String message) {
reply(MessageCreateData.fromContent(message));
}
public void reply(String format, Object... arguments) {
reply(String.format(format, arguments));
}
public void reply(MessageEmbed... embeds) {
reply(MessageCreateData.fromEmbeds(embeds));
}
public void reply(MessageCreateData msg) {
if (channel != null && channel.canTalk()) {
channel.sendMessage(msg).queue();
}
}
@Override
public void replyToMessage(String message) {
replyToMessage(MessageCreateData.fromContent(message));
}
@Override
public void replyToMessage(String format, Object... arguments) {
replyToMessage(String.format(format, arguments));
}
public void replyToMessage(MessageCreateData msg) {
if (channel != null && channel.canTalk() && message != null) {
message.reply(msg).queue();
}
}
public void replyPrivate(String message) {
replyPrivate(MessageCreateData.fromContent(message));
}
public void replyPrivate(MessageCreateData msg) {
user.openPrivateChannel().flatMap(channel -> channel.sendMessage(msg)).queue();
}
public void replyPrivate(MessageEmbed... embeds) {
replyPrivate(MessageCreateData.fromEmbeds(embeds));
}
public void replyPrivate(Collection<? extends MessageEmbed> embeds) {
user.openPrivateChannel().flatMap(channel -> channel.sendMessageEmbeds(embeds)).queue();
}
@Override
public void deleteMessage() {
if (message != null) {
message.delete().queueAfter(1, TimeUnit.SECONDS);
}
}
public boolean isMessageFromOwner() {
return member.isOwner();
}
public boolean isMessageFromCreator() {
String creatorId = Settings.getInstance().get("NETNEUTRALITY_BOT_OWNER");
return creatorId.equals(member.getId());
}
}
@@ -1,180 +0,0 @@
package me.bobert.netneutrality.core.context;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.command.AbstractCommand;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.InteractionHook;
import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder;
import net.dv8tion.jda.api.utils.messages.MessageCreateData;
import net.dv8tion.jda.api.utils.messages.MessageEditData;
import java.util.Collection;
public class SlashCommandContext implements CommandContext {
private final SlashCommandInteractionEvent event;
private AbstractCommand command;
private final JDA jda;
private final Guild guild;
private final MessageChannel channel;
private final Member member;
private final User user;
private SlashCommandContext(JDA jda, SlashCommandInteractionEvent event, Guild guild, MessageChannel channel, Member member, User user) {
this.jda = jda;
this.event = event;
this.guild = guild;
this.channel = channel;
this.member = member;
this.user = user;
}
public static SlashCommandContext createContext(SlashCommandInteractionEvent event) {
Member member = event.getMember();
User user = event.getUser();
Guild guild = event.getGuild();
MessageChannel channel = event.getChannel();
JDA jda = event.getJDA();
return new SlashCommandContext(jda, event, guild, channel, member, user);
}
@Override
public SlashCommandInteractionEvent getEvent() {
return event;
}
@Override
public AbstractCommand getCommand() {
return command;
}
@Override
public JDA getJDA() {
return jda;
}
@Override
public Guild getGuild() {
return guild;
}
@Override
public MessageChannel getChannel() {
return channel;
}
@Override
public Member getGuildUser() {
return member;
}
@Override
public User getDiscordUser() {
return user;
}
@Override
public void deleteMessage() {
// not implemented
}
public void sendMessageInPrivate(String msg) {
sendMessageInPrivate(new MessageCreateBuilder().setContent(msg).build());
}
public void sendMessageInPrivate(MessageCreateData msg) {
user.openPrivateChannel().flatMap(channel -> channel.sendMessage(msg)).queue();
}
public void sendMessageInPrivate(MessageEmbed embed, MessageEmbed... embeds) {
user.openPrivateChannel().flatMap(channel -> channel.sendMessageEmbeds(embed, embeds)).queue();
}
public void sendMessageInPrivate(Collection<? extends MessageEmbed> embeds) {
user.openPrivateChannel().flatMap(channel -> channel.sendMessageEmbeds(embeds)).queue();
}
public void reply(MessageCreateData message) {
if (event.isAcknowledged()) {
getHook().editOriginal(MessageEditData.fromCreateData(message)).queue();
//getHook().sendMessage(message).setEphemeral(false).queue();
return;
}
event.reply(message).queue();
}
public void reply(MessageEmbed... embeds) {
reply(MessageCreateData.fromEmbeds(embeds));
}
@Override
public void reply(String message) {
reply(MessageCreateData.fromContent(message));
}
public void replyEphemeral(MessageCreateData message) {
if (event.isAcknowledged()) {
getHook().editOriginal(MessageEditData.fromCreateData(message)).queue();
//getHook().sendMessage(message).setEphemeral(true).queue();
return;
}
event.reply(message).setEphemeral(true).queue();
}
public void replyEphemeral(MessageEmbed... embeds) {
replyEphemeral(MessageCreateData.fromEmbeds(embeds));
}
public void replyEphemeral(String format, Object... arguments) {
replyEphemeral(String.format(format, arguments));
}
public void replyEphemeral(String message) {
replyEphemeral(MessageCreateData.fromContent(message));
}
@Override
public void replyToMessage(String message) {
replyEphemeral(message);
}
public InteractionHook getHook() {
if (!event.isAcknowledged())
throw new RuntimeException("Interaction isn't acknowledged yet");
return event.getHook();
}
public void ackAndWait(boolean ephemeral) {
if (event.isAcknowledged())
throw new RuntimeException("Interaction is already acknowledged, please use getHook() instead.");
event.deferReply(ephemeral).queue();
}
public void ackAndWait() {
ackAndWait(false);
}
public void ack() {
ackAndWait();
}
public void ephemeral() {
ackAndWait(true);
}
}
@@ -0,0 +1,32 @@
package me.bobert.netneutrality.core.context;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.interactions.modals.Modal;
public abstract class TaskContext extends AbstractContext {
public TaskContext(JDA jda, Guild guild, MessageChannel channel, Member member, User user) {
super(jda, guild, channel, member, user);
}
@Override
public void deleteMessage() {
}
@Override
public void reply(Modal modal) {
}
public abstract String getId();
}
@@ -0,0 +1,34 @@
package me.bobert.netneutrality.core.db;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.utils.Settings;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.flywaydb.core.Flyway;
public class DatabaseMigration {
private final static Logger logger = LogManager.getLogger(DatabaseMigration.class);
public static void migrate() {
String dbName = Settings.getInstance().get("MYSQL_DATABASE", "netneutrality");
String dbUser = Settings.getInstance().get("MYSQL_USER", "root");
String dbPass = Settings.getInstance().get("MYSQL_PASSWORD", "");
String dbHost = Settings.getInstance().get("MYSQL_HOST", "localhost");
try {
Flyway flyway = Flyway.configure()
.dataSource(String.format("jdbc:mysql://%s:3306/%s", dbHost, dbName), dbUser, dbPass)
.baselineOnMigrate(true)
.load();
flyway.migrate();
} catch (Exception e) {
logger.error(e);
System.exit(1);
}
}
}
@@ -1,4 +1,4 @@
package me.bobert.netneutrality.core;
package me.bobert.netneutrality.core.db;
/*
* Copyright (c) anthorob
*
@@ -20,7 +20,7 @@ import java.util.HashMap;
import java.util.Map;
public class SessionManager {
private final static Logger logger = LogManager.getLogger(SessionManager.class);
private static final Logger logger = LogManager.getLogger(SessionManager.class);
private static StandardServiceRegistry registry;
private static SessionFactory sessionFactory;
@@ -35,9 +35,9 @@ public class SessionManager {
String dbHost = Settings.getInstance().get("MYSQL_HOST", "localhost");
logger.info(String.format("Connecting to database '%s' at %s:3306 with user '%s'", dbName, dbHost, dbUser));
settings.put(Environment.URL, String.format("jdbc:mysql://%s:3306/%s", dbHost, dbName));
settings.put(Environment.USER, dbUser);
settings.put(Environment.PASS, dbPass);
settings.put(Environment.JAKARTA_JDBC_URL, String.format("jdbc:mysql://%s:3306/%s", dbHost, dbName));
settings.put(Environment.JAKARTA_JDBC_USER, dbUser);
settings.put(Environment.JAKARTA_JDBC_PASSWORD, dbPass);
registryBuilder.applySettings(settings);
registry = registryBuilder.build();
@@ -0,0 +1,39 @@
package me.bobert.netneutrality.core.tasks;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.context.CommandContext;
import org.json.JSONObject;
public abstract class AbstractPersistableTask extends AbstractTask implements PersistableTask {
@Override
public boolean isInsertable() {
return true;
}
@Override
public boolean oneshot() {
return false;
}
@Override
public boolean isExpired() {
return false;
}
@Override
public void replyToInteraction(CommandContext context) {
}
@Override
public String toPayload() {
JSONObject payload = new JSONObject(this);
return payload.toString();
}
}
@@ -0,0 +1,46 @@
package me.bobert.netneutrality.core.tasks;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import java.time.OffsetDateTime;
import java.util.Objects;
public abstract class AbstractTask implements Task {
private final OffsetDateTime creationTime;
public AbstractTask() {
creationTime = OffsetDateTime.now();
}
@Override
public boolean isInsertable() {
return true;
}
@Override
public OffsetDateTime getCreationTime() {
return creationTime;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
AbstractTask that = (AbstractTask) o;
return Objects.equals(getId(), that.getId());
}
@Override
public int hashCode() {
return Objects.hashCode(getId());
}
@Override
public String toString() {
return String.format("Task{id=%s, class=%s, name=%s, oneshot=%b, creationTime=%s}", getId(), getClass().getSimpleName(), getName(), oneshot(), getCreationTime());
}
}
@@ -0,0 +1,14 @@
package me.bobert.netneutrality.core.tasks;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
/**
* Represents a task that will be saved in the database and loaded when the bot is started
*/
public interface PersistableTask extends Task {
String toPayload();
}
@@ -0,0 +1,62 @@
package me.bobert.netneutrality.core.tasks;
/*
* 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.WithChildren;
import java.time.OffsetDateTime;
public interface Task {
/**
* <p>This method should generate a unique ID to differentiate it from other tasks.</p>
* <p>
* For button and modal interaction, this is the ID of the component sent to discord.
* For other types of interaction, for example emoji reaction,
* each reaction should get a unique ID generated by the context.
* </p>
* @return a unique ID for this task
*/
String getId();
/**
* This is only used for debugging purpose.
* @return Name of the task
*/
String getName();
/**
* <p>If this returns false, the task will not get inserted and executed.</p>
*/
boolean isInsertable();
/**
* This determines if the task should be cancelled after execution.
*/
boolean oneshot();
/**
* <p>This method is used to reply to the interaction that triggered this task.</p>
* <p>If the task is implementing {@link WithChildren}, the child tasks must be instanciated in this method.</p>
*/
void replyToInteraction(CommandContext context);
/**
* Executes the task.
* @param context The context of a task containing the event that triggererd it.
*/
void execute(TaskContext context);
/**
* When this returns true, the task will get removed by the cleanup thread.
* @return If the task is expired
*/
boolean isExpired();
OffsetDateTime getCreationTime();
}
@@ -0,0 +1,273 @@
package me.bobert.netneutrality.core.tasks;
/*
* Copyright (c) anthorob
*
* 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.context.CommandContext;
import me.bobert.netneutrality.core.context.TaskContext;
import me.bobert.netneutrality.core.tasks.persistence.TaskRegistry;
import me.bobert.netneutrality.core.tasks.trait.Deletable;
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.thread.ThreadManager;
import me.bobert.netneutrality.db.entities.OTask;
import me.bobert.netneutrality.db.repository.TaskRepository;
import me.bobert.netneutrality.exceptions.TaskExecutionException;
import me.bobert.netneutrality.thread.CleanupTaskThread;
import net.dv8tion.jda.api.interactions.InteractionHook;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
public class TaskManager {
private static final Logger logger = LogManager.getLogger(TaskManager.class);
private static final ScheduledExecutorService executor = ThreadManager.getScheduledExecutor();
private static TaskManager instance;
private final TaskRegistry taskRegistry;
private final Map<String, Task> tasks;
private final Map<String, PersistableTask> persistableTasks;
private final Map<String, CommandContext> deletableTasksContext;
private ScheduledFuture<?> cleanupThread;
private TaskManager() {
this.tasks = new HashMap<>();
this.persistableTasks = new HashMap<>();
this.deletableTasksContext = new HashMap<>();
this.taskRegistry = new TaskRegistry();
}
public static TaskManager getInstance() {
if (instance == null) {
instance = new TaskManager();
}
return instance;
}
public void init() {
taskRegistry.loadTasks();
}
public Task getTask(String id) {
Task task = tasks.get(id);
if (task == null) {
task = persistableTasks.get(id);
}
return task;
}
public Collection<Task> getTasks() {
return tasks.values();
}
public Map<String, PersistableTask> getPersistableTaskMap() {
return persistableTasks;
}
public boolean exists(String id) {
boolean exists = tasks.containsKey(id);
if (!exists) {
exists = persistableTasks.containsKey(id);
}
return exists;
}
public void insert(CommandContext context, Task task) {
String id = task.getId();
if (tasks.containsKey(id) || persistableTasks.containsKey(id)) {
context.reply("A task with the same id already exists!");
return;
}
task.replyToInteraction(context);
if (task instanceof Deletable) {
deletableTasksContext.put(task.getId(), context);
}
if (task.isInsertable()) {
logger.info("Inserting task: {}", task);
if (task instanceof PersistableTask) {
persistTask((PersistableTask) task);
persistableTasks.put(id, (PersistableTask) task);
} else {
tasks.put(id, task);
}
}
if (task instanceof WithChildren taskWithChildren) {
List<Task> childrens = taskWithChildren.getChildTasks();
for (Task child : childrens) {
insert(context, child);
}
}
if (!tasks.isEmpty() && cleanupThread == null) {
cleanupThread = executor.scheduleWithFixedDelay(new CleanupTaskThread(), 1, 5, TimeUnit.MINUTES);
}
}
public void removeTask(Task task) {
String id = task.getId();
if (tasks.containsKey(id)) {
logger.debug("Removing task: {}", task);
tasks.remove(id);
}
if (tasks.isEmpty() && cleanupThread != null) {
cleanupThread.cancel(false);
cleanupThread = null;
}
}
public void handle(TaskContext context) {
Task task = tasks.get(context.getId());
if (task == null) {
task = persistableTasks.get(context.getId());
}
if (task == null && (context instanceof ModalTaskContext || context instanceof ButtonTaskContext)) {
context.replyEphemeral("This task is no longer available, someone might have already used it or it might have expired");
}
if (task != null) {
logger.debug("Executing task: {}", task);
try {
// Cancel task during execution to prevent spam
removeTaskAndChildTasks(task);
task.execute(context);
if (task.isInsertable() && !task.oneshot()) {
reinsertTaskAndChildTasks(task);
}
deleteInteraction(task);
// If interaction is still not acknowledged after execution, send a placeholder response.
if (!context.isAcknowledged() && !(task instanceof NotAcknowledgeable)) {
context.replyEphemeral("Task executed successfully but interaction wasn't acknowledged, report this bug by using the command /report-bug");
}
} catch (TaskExecutionException exception) {
LogHandler.log(exception);
} catch (Exception exception) {
LogHandler.log(new TaskExecutionException("Error during task execution", context, exception));
}
}
}
public void persistTask(PersistableTask task) {
String taskClass = task.getClass().getName();
if (taskRegistry.getTaskCreator(taskClass) == null) {
logger.error("No TaskCreator found for class: {}", taskClass);
return;
}
TaskRepository taskRepository = new TaskRepository();
OTask oTask = taskRepository.findByTaskId(task.getId());
if (oTask == null) {
oTask = new OTask();
}
oTask.setTaskId(task.getId());
oTask.setTaskClass(task.getClass().getName());
oTask.setPayload(task.toPayload());
taskRepository.save(oTask);
}
public void unpersistTask(PersistableTask task) {
logger.debug("Unpersisting task: {}", task);
TaskRepository taskRepository = new TaskRepository();
taskRepository.deleteBy(task.getId());
persistableTasks.remove(task.getId());
}
private void deleteInteraction(Task task) {
if (task instanceof WithParent<?> taskWithParent) {
Task parent = (Task) taskWithParent.getParentTask();
if (parent instanceof Deletable deletable && task.isInsertable()) {
deleteInteraction(parent.getId(), deletable);
return;
}
}
if (task instanceof Deletable deletable && task.isInsertable()) {
deleteInteraction(task.getId(), deletable);
}
}
private void deleteInteraction(String taskId, Deletable deletable) {
logger.debug("Deleting interaction: {}", deletable);
CommandContext context = deletableTasksContext.get(taskId);
if (context != null) {
deletableTasksContext.remove(taskId);
InteractionHook hook = context.getInteractionHook();
if (hook != null) {
if (hook.isExpired()) {
context.replyEphemeral("This interaction has expired, can't remove it");
}
if (!hook.isExpired()) {
hook.deleteOriginal().queue();
}
}
}
deletable.onInteractionDelete();
}
private void removeTaskAndChildTasks(Task task) {
tasks.remove(task.getId());
if (task instanceof WithParent<?> taskWithParent && !taskWithParent.shouldKeepChilds()) {
List<Task> childrens = taskWithParent.getParentTask().getChildTasks();
for (Task child : childrens) {
if (!task.equals(child)) {
tasks.remove(child.getId());
}
}
}
}
private void reinsertTaskAndChildTasks(Task task) {
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,69 @@
package me.bobert.netneutrality.core.tasks.persistence;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
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.JSONObject;
import java.util.Map;
public class TaskFactory {
private static final Logger logger = LogManager.getLogger(TaskFactory.class);
public static PersistableTask createTaskInstance(String className, JSONArray params, JSONArray types, JSONObject payload) throws Exception {
if (params.length() != types.length()) {
throw new RuntimeException("Wrong number of parameters and types for " + className);
}
Class<?> clazz = Class.forName(className);
Class<?>[] classTypes = new Class<?>[types.length()];
Object[] classParams = new Object[params.length()];
for (int i = 0; i < params.length(); i++) {
String type = types.getString(i);
String param = params.getString(i);
Class<?> classType = parseClassTypeFromString(type);
Object classParam = castParamToType(classType, param, type, payload);
classTypes[i] = classType;
classParams[i] = classParam;
}
return (PersistableTask) clazz.getConstructor(classTypes).newInstance(classParams);
}
private static Class<?> parseClassTypeFromString(String param) {
return switch (param) {
case "string" -> String.class;
case "int" -> Integer.class;
case "map" -> Map.class;
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.");
}
}
@@ -0,0 +1,101 @@
package me.bobert.netneutrality.core.tasks.persistence;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.tasks.PersistableTask;
import me.bobert.netneutrality.core.tasks.TaskManager;
import me.bobert.netneutrality.db.entities.OTask;
import me.bobert.netneutrality.db.repository.TaskRepository;
import org.apache.commons.io.IOUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TaskRegistry {
@FunctionalInterface
public interface TaskCreator {
PersistableTask fromPayload(JSONObject payload);
}
private static final Logger logger = LogManager.getLogger(TaskRegistry.class);
private final Map<String, TaskCreator> taskCreators = new HashMap<>();
private final TaskRepository taskRepository;
public TaskRegistry() {
taskRepository = new TaskRepository();
loadTaskConfig();
}
public TaskCreator getTaskCreator(String className) {
return taskCreators.get(className);
}
private void loadTaskConfig() {
String fileName = "persistable-tasks.json";
try (InputStream is = TaskFactory.class.getClassLoader().getResourceAsStream(fileName)) {
if (is == null) {
throw new FileNotFoundException("Could not load: " + fileName);
}
String jsonString = IOUtils.toString(is, StandardCharsets.UTF_8);
JSONObject json = new JSONObject(jsonString);
json.getJSONArray("tasks").forEach(task -> {
JSONObject taskJson = (JSONObject) task;
String className = taskJson.getString("class");
JSONArray params = taskJson.getJSONArray("params");
JSONArray types = taskJson.getJSONArray("types");
taskCreators.put(className, payload -> {
try {
return TaskFactory.createTaskInstance(className, params, types, payload);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
});
});
} catch (Exception e) {
logger.error(e);
}
}
public void loadTasks() {
logger.info("Loading persistable tasks...");
List<OTask> tasks = taskRepository.findAll();
for (OTask oTask : tasks) {
String taskClass = oTask.getTaskClass();
String payload = oTask.getPayload();
try {
TaskCreator creator = getTaskCreator(taskClass);
if (creator == null) {
throw new IllegalArgumentException("No task creator registered for class: " + taskClass);
}
JSONObject json = new JSONObject(payload);
PersistableTask task = creator.fromPayload(json);
logger.info("Loaded task: {}", task);
TaskManager.getInstance().getPersistableTaskMap().put(task.getId(), task);
} catch (Exception e) {
logger.error("Error loading task: {}", payload, e);
}
}
}
}
@@ -0,0 +1,13 @@
package me.bobert.netneutrality.core.tasks.trait;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
/**
* Represents a task that will remove its interaction from the channel after execution
*/
public interface Deletable {
void onInteractionDelete();
}
@@ -0,0 +1,12 @@
package me.bobert.netneutrality.core.tasks.trait;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
/**
* If a task implements this interface, it means that the interaction will not get acknowledged during execution.
*/
public interface NotAcknowledgeable {
}
@@ -0,0 +1,22 @@
package me.bobert.netneutrality.core.tasks.trait;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.tasks.Task;
import me.bobert.netneutrality.core.tasks.TaskManager;
import java.util.List;
public interface WithChildren {
List<Task> getChildTasks();
default void removeAllChildTasks() {
for (Task child : getChildTasks()) {
TaskManager.getInstance().removeTask(child);
}
}
}
@@ -0,0 +1,16 @@
package me.bobert.netneutrality.core.tasks.trait;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
public interface WithParent<E extends WithChildren> {
E getParentTask();
/**
* Whether execution of this task should remove all childs of its parent task during execution
*/
boolean shouldKeepChilds();
}
@@ -1,4 +1,4 @@
package me.bobert.netneutrality.thread;
package me.bobert.netneutrality.core.thread;
/*
* Copyright (c) anthorob
@@ -1,4 +1,4 @@
package me.bobert.netneutrality.thread;
package me.bobert.netneutrality.core.thread;
/*
* Copyright (c) anthorob
*
@@ -10,40 +10,23 @@ import java.util.concurrent.*;
public class ThreadManager {
public enum ExecutorType {
SINGLE, SCHEDULED, FIXED
}
private static final Map<String, ExecutorService> executors = new ConcurrentHashMap<>();
public static ScheduledExecutorService getMusicExecutor() {
public static ScheduledExecutorService getScheduledExecutor() {
return (ScheduledExecutorService) executors.computeIfAbsent(
"music-executor",
"scheduled-executor",
s -> Executors.newScheduledThreadPool(
Runtime.getRuntime().availableProcessors(),
new BaseThreadFactory("music-executor")
new BaseThreadFactory(s)
)
);
}
public static ExecutorService getExecutor() {
return executors.computeIfAbsent("general-executor", s -> Executors.newCachedThreadPool(new BaseThreadFactory("general-executor")));
}
public static ExecutorService getExecutor(String name) {
return executors.computeIfAbsent(name, s -> Executors.newSingleThreadExecutor(new BaseThreadFactory(name)));
}
public static ExecutorService getExecutor(String name, ExecutorType type) {
ExecutorService executor;
switch (type) {
case FIXED -> executor = executors.computeIfAbsent(name, s -> Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new BaseThreadFactory(name)));
case SCHEDULED -> executor = executors.computeIfAbsent(name, s -> Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), new BaseThreadFactory(name)));
default -> executor = executors.computeIfAbsent(name, s -> Executors.newSingleThreadExecutor(new BaseThreadFactory(name)));
}
return executor;
return executors.computeIfAbsent(
"general-executor",
s -> Executors.newCachedThreadPool(new BaseThreadFactory(s))
);
}
public static void shutdownExecutor() {
@@ -1,23 +0,0 @@
package me.bobert.netneutrality.db;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.SessionManager;
import org.hibernate.SessionFactory;
public abstract class AbstractController {
protected SessionFactory sessionFactory;
public AbstractController() {
sessionFactory = SessionManager.getSessionFactory();
}
protected SessionFactory getSessionFactory() {
return sessionFactory;
}
}
@@ -1,38 +0,0 @@
package me.bobert.netneutrality.db;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.SessionManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.reflect.InvocationTargetException;
public class DBController {
private final static Logger logger = LogManager.getLogger(DBController.class);
public static <T> T get(Class<T> returnType) {
if (returnType == null)
throw new IllegalArgumentException("return type is null");
try {
return returnType.getConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
public static void shutdown() {
SessionManager.shutdown();
}
public static void init() {
SessionManager.getSessionFactory();
}
}
@@ -9,7 +9,9 @@ public enum GSettings {
//TODO: description
MUSIC_ROLE("MUSIC_ROLE", ""),
MUSIC_CHANNEL("MUSIC_CHANNEL", ""),
MUSIC_VOLUME("MUSIC_VOLUME", "30");
MUSIC_VOLUME("MUSIC_VOLUME", "30"),
MUSIC_SEARCHRESULTS_LIMIT("MUSIC_SEARCHRESULTS_LIMIT", "5"),
MUSIC_MAXPLAYLISTSIZE("MUSIC_MAXPLAYLISTSIZE", "20");
private final String name;
private final String defaultValue;
@@ -0,0 +1,42 @@
package me.bobert.netneutrality.db;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.db.SessionManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.exception.ConstraintViolationException;
import java.util.List;
public interface Repository<O> {
Logger logger = LogManager.getLogger(Repository.class);
List<O> findAll();
default void save(O entity) {
Transaction transaction = null;
try(Session session = SessionManager.getSessionFactory().openSession()) {
transaction = session.beginTransaction();
session.merge(entity);
transaction.commit();
} catch (ConstraintViolationException e) {
logger.error(e);
throw e;
} catch (Exception e) {
logger.error(e);
if (transaction != null) {
transaction.rollback();
}
}
}
void deleteBy(String id);
}
@@ -1,141 +0,0 @@
package me.bobert.netneutrality.db.controllers;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.GuildManager;
import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.db.AbstractController;
import me.bobert.netneutrality.db.entities.OBlacklist;
import me.bobert.netneutrality.db.entities.OGuild;
import me.bobert.netneutrality.utils.JpaUtils;
import net.dv8tion.jda.api.entities.Guild;
import org.hibernate.Session;
import org.hibernate.query.Query;
import java.util.ArrayList;
public class DBBlacklist extends AbstractController {
public void add(String word, CommandContext context) {
GuildManager guildManager = GuildManager.getManager(context.getGuild());
OBlacklist bl = getWordFromDb(word, context.getGuild().getId());
if (bl == null) {
Session session = getSessionFactory().openSession();
session.beginTransaction();
bl = new OBlacklist();
bl.setGuild(guildManager.getGuildEntity());
bl.setCreatorId(context.getGuildUser().getId());
bl.setWord(word);
bl.setEnabled(true);
session.persist(bl);
session.getTransaction().commit();
session.close();
guildManager.reloadBlacklistCache();
context.reply(String.format("Added the word [%s] to the blacklist.", word));
} else {
context.reply("Word is already blacklisted");
}
}
public void delete(String word, CommandContext context) {
GuildManager guildManager = GuildManager.getManager(context.getGuild());
OBlacklist bl = getWordFromDb(word, context.getGuild().getId());
if (bl != null) {
Session session = getSessionFactory().openSession();
session.beginTransaction();
session.remove(bl);
session.getTransaction().commit();
session.close();
guildManager.reloadBlacklistCache();
context.reply("Removed the word [%s] from the blacklist.", word);
} else {
context.reply("Can't delete word because it doesn't exist");
}
}
public void enable(String word, CommandContext context) {
GuildManager guildManager = GuildManager.getManager(context.getGuild());
OBlacklist bl = getWordFromDb(word, context.getGuild().getId());
if (bl != null && !bl.getEnabled()) {
Session session = getSessionFactory().openSession();
session.beginTransaction();
bl.setEnabled(true);
session.merge(bl);
session.getTransaction().commit();
session.close();
guildManager.reloadBlacklistCache();
context.reply("Enabled the word [%s] for the blacklist.", word);
} else {
context.reply("Either word doesn't exist or is already enabled");
}
}
public void disable(String word, CommandContext context) {
GuildManager guildManager = GuildManager.getManager(context.getGuild());
OBlacklist bl = getWordFromDb(word, context.getGuild().getId());
if (bl != null && bl.getEnabled()) {
Session session = getSessionFactory().openSession();
session.beginTransaction();
bl.setEnabled(false);
session.merge(bl);
session.getTransaction().commit();
session.close();
guildManager.reloadBlacklistCache();
context.reply("Disabled the word [%s] for the blacklist.", word);
} else {
context.reply("Either word doesn't exist or is already disabled");
}
}
public ArrayList<OBlacklist> getBlacklistForGuild(Guild guild) {
GuildManager guildManager = GuildManager.getManager(guild);
Session session = getSessionFactory().openSession();
OGuild guildEntity = guildManager.getGuildEntity();
JpaUtils.tryLoad(session, guildEntity, guildEntity.getGuildId());
ArrayList<OBlacklist> blacklists = new ArrayList<>(guildEntity.getBlacklists());
session.close();
return blacklists;
}
private OBlacklist getWordFromDb(String word, String guildId) {
Session session = getSessionFactory().openSession();
Query<OBlacklist> query = session.createNamedQuery("OBlacklist.findByWordEqualsIgnoreCaseAndGuild_GuildIdEquals", OBlacklist.class);
query.setParameter("guildId", guildId);
query.setParameter("word", word);
OBlacklist blacklist = query.uniqueResult();
session.close();
return blacklist;
}
}
@@ -1,97 +0,0 @@
package me.bobert.netneutrality.db.controllers;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.GuildManager;
import me.bobert.netneutrality.db.AbstractController;
import me.bobert.netneutrality.db.entities.OLogMessage;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.query.Query;
import java.util.List;
public class DBMessage extends AbstractController {
private final static Logger logger = LogManager.getLogger(DBMessage.class);
public void log(GuildManager guildManager, MessageChannel channel, Member author, Message message) {
Session session = getSessionFactory().openSession();
session.beginTransaction();
OLogMessage logMessage = new OLogMessage();
logMessage.setGuild(guildManager.getGuildEntity());
logMessage.setMessageId(message.getId());
logMessage.setMessage(message.getContentRaw().trim());
logMessage.setChannelId(channel.getId());
logMessage.setUserId(author.getId());
logMessage.setDateSent(message.getTimeCreated().toInstant());
session.persist(logMessage);
session.getTransaction().commit();
session.close();
}
public void logEdit(String messageId, String newMessage) {
Session session = getSessionFactory().openSession();
Query<OLogMessage> query = session.createNamedQuery("OLogMessage.findByMessageIdEquals", OLogMessage.class);
query.setParameter("messageId", messageId);
OLogMessage message = query.uniqueResult();
if (message != null) {
session.beginTransaction();
message.setEditedMessage(newMessage);
session.merge(message);
session.getTransaction().commit();
}
session.close();
}
public OLogMessage getMessage(String messageId) {
Session session = getSessionFactory().openSession();
Query<OLogMessage> query = session.createNamedQuery("OLogMessage.findByMessageIdEquals", OLogMessage.class);
query.setParameter("messageId", messageId);
OLogMessage message = query.uniqueResult();
session.close();
return message;
}
public List<OLogMessage> getMessagesFromUser(String userId, String guildId, int max) {
Session session = getSessionFactory().openSession();
Query<OLogMessage> query = session.createNamedQuery("OLogMessage.findByUserIdEquals", OLogMessage.class);
query.setParameter("guildId", guildId);
query.setParameter("userId", userId);
query.setMaxResults(max);
List<OLogMessage> messages = query.getResultList();
session.close();
return messages;
}
public String getMessageString(String messageId) {
Session session = getSessionFactory().openSession();
Query<OLogMessage> query = session.createNamedQuery("OLogMessage.findByMessageIdEquals", OLogMessage.class);
query.setParameter("messageId", messageId);
OLogMessage message = query.uniqueResult();
session.close();
if (message == null)
return null;
else
return message.getMessage();
}
}
@@ -10,9 +10,6 @@ import jakarta.persistence.*;
@Entity
@Table(name = "blacklist")
@NamedQueries({
@NamedQuery(name = "OBlacklist.findByWordEqualsIgnoreCaseAndGuild_GuildIdEquals", query = "select o from OBlacklist o where upper(o.word) = upper(:word) and o.guild.guildId = :guildId")
})
public class OBlacklist {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -12,10 +12,6 @@ import java.time.Instant;
@Entity
@Table(name = "log_message")
@NamedQueries({
@NamedQuery(name = "OLogMessage.findByMessageIdEquals", query = "select o from OLogMessage o where o.messageId = :messageId"),
@NamedQuery(name = "OLogMessage.findByGuild_GuildIdEqualsAndUserIdEquals", query = "select o from OLogMessage o where o.guild.guildId = :guildId and o.userId = :userId")
})
public class OLogMessage {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -0,0 +1,60 @@
package me.bobert.netneutrality.db.entities;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import jakarta.persistence.*;
@Entity
@Table(name = "tasks", uniqueConstraints = {@UniqueConstraint(columnNames = {"task_id"})})
public class OTask {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id", nullable = false)
private Integer id;
@Column(name = "task_id", nullable = false)
private String taskId;
@Column(name = "task_class", nullable = false)
private String taskClass;
@Lob
@Column(name = "payload")
private String payload;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTaskId() {
return taskId;
}
public void setTaskId(String taskId) {
this.taskId = taskId;
}
public String getTaskClass() {
return taskClass;
}
public void setTaskClass(String taskClass) {
this.taskClass = taskClass;
}
public String getPayload() {
return payload;
}
public void setPayload(String payload) {
this.payload = payload;
}
}
@@ -0,0 +1,86 @@
package me.bobert.netneutrality.db.entities;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import jakarta.persistence.*;
import org.hibernate.annotations.ColumnDefault;
import java.time.Instant;
@Entity
@Table(name = "web_users")
public class OWebUser {
@Id
@Column(name = "id", nullable = false)
private String id;
@Lob
@Column(name = "email", nullable = false)
private String email;
@Column(name = "token", nullable = false)
private String token;
@ColumnDefault("current_timestamp()")
@Column(name = "expires", nullable = false)
private Instant expires;
@Column(name = "refresh_token", nullable = false)
private String refreshToken;
@Column(name = "remember_token", length = 100)
private String rememberToken;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public Instant getExpires() {
return expires;
}
public void setExpires(Instant expires) {
this.expires = expires;
}
public String getRefreshToken() {
return refreshToken;
}
public void setRefreshToken(String refreshToken) {
this.refreshToken = refreshToken;
}
public String getRememberToken() {
return rememberToken;
}
public void setRememberToken(String rememberToken) {
this.rememberToken = rememberToken;
}
}
@@ -0,0 +1,160 @@
package me.bobert.netneutrality.db.repository;
/*
* Copyright (c) anthorob
*
* 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.db.Repository;
import me.bobert.netneutrality.db.entities.OBlacklist;
import me.bobert.netneutrality.db.entities.OGuild;
import me.bobert.netneutrality.utils.JpaUtils;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import org.hibernate.Session;
import org.hibernate.query.Query;
import java.util.List;
public class BlacklistRepository implements Repository<OBlacklist> {
@Override
public List<OBlacklist> findAll() {
List<OBlacklist> blacklists;
try(Session session = SessionManager.getSessionFactory().openSession()) {
Query<OBlacklist> query = session.createQuery("FROM OBlacklist", OBlacklist.class);
blacklists = query.list();
}
return blacklists;
}
@Override
public void deleteBy(String id) {
try(Session session = SessionManager.getSessionFactory().openSession()) {
session.beginTransaction();
Query<OBlacklist> query = session.createQuery("SELECT o FROM OBlacklist o WHERE o.id = :blacklistId", OBlacklist.class);
query.setParameter("blacklistId", id);
OBlacklist oTask = query.uniqueResult();
if (oTask != null) {
session.remove(oTask);
}
session.getTransaction().commit();
}
}
public CommandExecutionResult add(Guild guild, Member member, String word) {
GuildManager guildManager = GuildManager.getManager(guild);
OBlacklist bl = getWordFromDb(word, guild.getId());
if (bl == null) {
try(Session session = SessionManager.getSessionFactory().openSession()) {
session.beginTransaction();
bl = new OBlacklist();
bl.setGuild(guildManager.getGuildEntity());
bl.setCreatorId(member.getId());
bl.setWord(word);
bl.setEnabled(true);
session.persist(bl);
session.getTransaction().commit();
}
guildManager.reloadBlacklistCache();
return CommandExecutionResult.success("Added the word [%s] to the blacklist.", word);
}
return CommandExecutionResult.info("Word is already blacklisted");
}
public CommandExecutionResult delete(Guild guild, String word) {
GuildManager guildManager = GuildManager.getManager(guild);
OBlacklist bl = getWordFromDb(word, guild.getId());
if (bl != null) {
try(Session session = SessionManager.getSessionFactory().openSession()) {
session.beginTransaction();
session.remove(bl);
session.getTransaction().commit();
}
guildManager.reloadBlacklistCache();
return CommandExecutionResult.success("Removed the word [%s] to the blacklist.", word);
}
return CommandExecutionResult.info("Word is not blacklisted");
}
public CommandExecutionResult enable(Guild guild, String word) {
GuildManager guildManager = GuildManager.getManager(guild);
OBlacklist bl = getWordFromDb(word, guild.getId());
if (bl != null && !bl.getEnabled()) {
try(Session session = SessionManager.getSessionFactory().openSession()) {
session.beginTransaction();
bl.setEnabled(true);
session.merge(bl);
session.getTransaction().commit();
}
guildManager.reloadBlacklistCache();
return CommandExecutionResult.success("Enabled the word [%s] for the blacklist.", word);
}
return CommandExecutionResult.info("Either word doesn't exist or is already enabled");
}
public CommandExecutionResult disable(Guild guild, String word) {
GuildManager guildManager = GuildManager.getManager(guild);
OBlacklist bl = getWordFromDb(word, guild.getId());
if (bl != null && bl.getEnabled()) {
try(Session session = SessionManager.getSessionFactory().openSession()) {
session.beginTransaction();
bl.setEnabled(false);
session.merge(bl);
session.getTransaction().commit();
}
guildManager.reloadBlacklistCache();
return CommandExecutionResult.success("Disabled the word [%s] for the blacklist.", word);
}
return CommandExecutionResult.info("Either word doesn't exist or is already disabled");
}
public List<OBlacklist> getBlacklistForGuild(Guild guild) {
List<OBlacklist> blacklist;
try(Session session = SessionManager.getSessionFactory().openSession()) {
GuildManager guildManager = GuildManager.getManager(guild);
OGuild guildEntity = guildManager.getGuildEntity();
JpaUtils.tryLoad(session, guildEntity, guildEntity.getGuildId());
blacklist = guildEntity.getBlacklists().stream().toList();
}
return blacklist;
}
private OBlacklist getWordFromDb(String word, String guildId) {
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.setParameter("guildId", guildId);
query.setParameter("word", word);
OBlacklist blacklist = query.uniqueResult();
session.close();
return blacklist;
}
}
}
@@ -0,0 +1,121 @@
package me.bobert.netneutrality.db.repository;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.GuildManager;
import me.bobert.netneutrality.core.db.SessionManager;
import me.bobert.netneutrality.db.Repository;
import me.bobert.netneutrality.db.entities.OLogMessage;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import org.hibernate.Session;
import org.hibernate.query.Query;
import java.util.List;
public class MessageRepository implements Repository<OLogMessage> {
@Override
public List<OLogMessage> findAll() {
List<OLogMessage> messages;
try(Session session = SessionManager.getSessionFactory().openSession()) {
Query<OLogMessage> query = session.createQuery("FROM OLogMessage", OLogMessage.class);
messages = query.list();
}
return messages;
}
@Override
public void deleteBy(String id) {
try(Session session = SessionManager.getSessionFactory().openSession()) {
session.beginTransaction();
Query<OLogMessage> query = session.createQuery("SELECT o FROM OLogMessage o WHERE o.id = :messageId", OLogMessage.class);
query.setParameter("messageId", id);
OLogMessage oLogMessage = query.uniqueResult();
if (oLogMessage != null) {
session.remove(oLogMessage);
}
session.getTransaction().commit();
}
}
public void log(GuildManager guildManager, MessageChannel channel, Member author, Message message) {
try(Session session = SessionManager.getSessionFactory().openSession()) {
session.beginTransaction();
OLogMessage logMessage = new OLogMessage();
logMessage.setGuild(guildManager.getGuildEntity());
logMessage.setMessageId(message.getId());
logMessage.setMessage(message.getContentRaw().trim());
logMessage.setChannelId(channel.getId());
logMessage.setUserId(author.getId());
logMessage.setDateSent(message.getTimeCreated().toInstant());
session.persist(logMessage);
session.getTransaction().commit();
}
}
public void logEdit(String messageId, String newMessage) {
try(Session session = SessionManager.getSessionFactory().openSession()) {
Query<OLogMessage> query = session.createQuery("SELECT o FROM OLogMessage o WHERE o.id = :messageId", OLogMessage.class);
query.setParameter("messageId", messageId);
OLogMessage message = query.uniqueResult();
if (message != null) {
session.beginTransaction();
message.setEditedMessage(newMessage);
session.merge(message);
session.getTransaction().commit();
}
}
}
public OLogMessage getMessage(String messageId) {
try(Session session = SessionManager.getSessionFactory().openSession()) {
Query<OLogMessage> query = session.createQuery("SELECT o FROM OLogMessage o WHERE o.id = :messageId", OLogMessage.class);
query.setParameter("messageId", messageId);
return query.uniqueResult();
}
}
public List<OLogMessage> getMessagesFromUser(String userId, String guildId, int max) {
try(Session session = SessionManager.getSessionFactory().openSession()) {
Query<OLogMessage> query = session.createQuery(
"SELECT o FROM OLogMessage o WHERE o.guild.guildId = :guildId AND o.userId = :userId",
OLogMessage.class
);
query.setParameter("guildId", guildId);
query.setParameter("userId", userId);
query.setMaxResults(max);
return query.getResultList();
}
}
public String getMessageString(String messageId) {
try(Session session = SessionManager.getSessionFactory().openSession()) {
Query<OLogMessage> query = session.createQuery("SELECT o FROM OLogMessage o WHERE o.id = :messageId", OLogMessage.class);
query.setParameter("messageId", messageId);
OLogMessage message = query.uniqueResult();
if (message != null) {
return message.getMessage();
}
return null;
}
}
}
@@ -0,0 +1,60 @@
package me.bobert.netneutrality.db.repository;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.db.SessionManager;
import me.bobert.netneutrality.db.Repository;
import me.bobert.netneutrality.db.entities.OTask;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.query.Query;
import java.util.List;
public class TaskRepository implements Repository<OTask> {
private static final Logger logger = LogManager.getLogger(TaskRepository.class);
@Override
public List<OTask> findAll() {
List<OTask> tasks;
try(Session session = SessionManager.getSessionFactory().openSession()) {
Query<OTask> query = session.createQuery("FROM OTask", OTask.class);
tasks = query.list();
}
return tasks;
}
public OTask findByTaskId(String id) {
try(Session session = SessionManager.getSessionFactory().openSession()) {
Query<OTask> query = session.createQuery("SELECT o FROM OTask o WHERE o.taskId = :taskId", OTask.class);
query.setParameter("taskId", id);
return query.uniqueResult();
}
}
@Override
public void deleteBy(String id) {
try(Session session = SessionManager.getSessionFactory().openSession()) {
session.beginTransaction();
Query<OTask> query = session.createQuery("SELECT o FROM OTask o WHERE o.taskId = :taskId", OTask.class);
query.setParameter("taskId", id);
OTask oTask = query.uniqueResult();
if (oTask != null) {
session.remove(oTask);
}
session.getTransaction().commit();
}
}
}
@@ -1,24 +0,0 @@
package me.bobert.netneutrality.exceptions;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.context.CommandContext;
public abstract class CommandException extends RuntimeException {
public static final String DEFAULT_MESSAGE = "Unknown error during command execution.";
private final CommandContext context;
public CommandException(String message, CommandContext context, Throwable cause) {
super(message == null ? DEFAULT_MESSAGE : message, cause);
this.context = context;
}
public CommandContext getCommandContext() {
return context;
}
}
@@ -11,14 +11,9 @@ import me.bobert.netneutrality.core.context.CommandContext;
/**
* Logs the error internally, without sending a message
*/
public class CommandExecutionException extends CommandException {
public class CommandExecutionException extends ContextException {
/**
* Logs the error internally, without sending a message
* @param context The command context
*/
public CommandExecutionException(String message, CommandContext context, Throwable cause) {
super(message, context, cause);
}
}
@@ -0,0 +1,24 @@
package me.bobert.netneutrality.exceptions;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.context.Context;
public class ContextException extends RuntimeException {
public static final String DEFAULT_MESSAGE = "Unknown error during execution.";
private final Context context;
public ContextException(String message, Context context, Throwable cause) {
super(message == null ? DEFAULT_MESSAGE : message, cause);
this.context = context;
}
public Context getContext() {
return context;
}
}
@@ -0,0 +1,15 @@
package me.bobert.netneutrality.exceptions;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.context.TaskContext;
public class TaskExecutionException extends ContextException {
public TaskExecutionException(String message, TaskContext context, Throwable cause) {
super(message, context, cause);
}
}
@@ -12,7 +12,7 @@ import net.dv8tion.jda.api.events.guild.GuildLeaveEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
public class ServerJoinHandler extends ListenerAdapter {
public class ServerJoinHook extends ListenerAdapter {
@Override
public void onGuildJoin(@NotNull GuildJoinEvent event) {
@@ -0,0 +1,22 @@
package me.bobert.netneutrality.hooks.interaction;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.context.tasks.ButtonTaskContext;
import me.bobert.netneutrality.core.tasks.TaskManager;
import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
public class ButtonInteractionHook extends ListenerAdapter {
@Override
public void onButtonInteraction(@NotNull ButtonInteractionEvent event) {
ButtonTaskContext context = new ButtonTaskContext(event);
TaskManager.getInstance().handle(context);
}
}
@@ -0,0 +1,22 @@
package me.bobert.netneutrality.hooks.interaction;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.context.tasks.ModalTaskContext;
import me.bobert.netneutrality.core.tasks.TaskManager;
import net.dv8tion.jda.api.events.interaction.ModalInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
public class ModalInteractionHook extends ListenerAdapter {
@Override
public void onModalInteraction(@NotNull ModalInteractionEvent event) {
ModalTaskContext context = new ModalTaskContext(event);
TaskManager.getInstance().handle(context);
}
}
@@ -1,25 +1,22 @@
package me.bobert.netneutrality.hooks;
package me.bobert.netneutrality.hooks.interaction;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.CommandProcessor;
import me.bobert.netneutrality.core.context.SlashCommandContext;
import me.bobert.netneutrality.context.SlashCommandContext;
import me.bobert.netneutrality.core.command.CommandProcessor;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
public class SlashCommandHooks extends ListenerAdapter {
public class SlashCommandHook extends ListenerAdapter {
@Override
public void onSlashCommandInteraction(@NotNull SlashCommandInteractionEvent event) {
String commandName = event.getName();
SlashCommandContext commandContext = SlashCommandContext.createContext(event);
CommandProcessor processor = new CommandProcessor(commandName, commandContext);
SlashCommandContext commandContext = new SlashCommandContext(event);
CommandProcessor processor = new CommandProcessor(commandContext);
processor.dispatch();
}
}
@@ -7,10 +7,9 @@ package me.bobert.netneutrality.hooks.message;
*/
import me.bobert.netneutrality.core.GuildManager;
import me.bobert.netneutrality.core.SessionManager;
import me.bobert.netneutrality.db.DBController;
import me.bobert.netneutrality.db.controllers.DBMessage;
import me.bobert.netneutrality.core.db.SessionManager;
import me.bobert.netneutrality.db.entities.OGuild;
import me.bobert.netneutrality.db.repository.MessageRepository;
import me.bobert.netneutrality.utils.JpaUtils;
import me.bobert.netneutrality.utils.MessageFunction;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
@@ -46,8 +45,9 @@ public class EditHandler extends ListenerAdapter {
TextChannel channel = event.getGuild().getTextChannelById(guildEntity.getLogChannel());
if (channel != null) {
String originalMessage = DBController.get(DBMessage.class).getMessageString(event.getMessage().getId());
DBController.get(DBMessage.class).logEdit(event.getMessage().getId(), event.getMessage().getContentDisplay());
MessageRepository repository = new MessageRepository();
String originalMessage = repository.getMessageString(event.getMessage().getId());
repository.logEdit(event.getMessage().getId(), event.getMessage().getContentDisplay());
channel.sendMessageEmbeds(MessageFunction.buildEditMessage(event.getMember(), event.getMessage(), originalMessage)).queue();
}
@@ -0,0 +1,22 @@
package me.bobert.netneutrality.hooks.message;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.context.tasks.EmojiTaskContext;
import me.bobert.netneutrality.core.tasks.TaskManager;
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
public class EmojiReactionHook extends ListenerAdapter {
@Override
public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) {
EmojiTaskContext context = new EmojiTaskContext(event);
TaskManager.getInstance().handle(context);
}
}
@@ -5,16 +5,14 @@ package me.bobert.netneutrality.hooks.message;
* https://discord.gg/qUa6zNFZsd
*/
import me.bobert.netneutrality.core.CommandProcessor;
import me.bobert.netneutrality.core.GuildManager;
import me.bobert.netneutrality.core.context.MessageCommandContext;
import me.bobert.netneutrality.context.MessageCommandContext;
import me.bobert.netneutrality.core.command.CommandProcessor;
import me.bobert.netneutrality.utils.Settings;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
public class MessageReceivedHandler extends ListenerAdapter {
public class MessageReceivedHook extends ListenerAdapter {
@Override
public void onMessageReceived(@NotNull MessageReceivedEvent event) {
@@ -24,19 +22,12 @@ public class MessageReceivedHandler extends ListenerAdapter {
if (!event.isFromGuild())
return;
Message message = event.getMessage();
MessageCommandContext context = MessageCommandContext.createContext(event);
String rawMessage = message.getContentDisplay().trim();
String rawMessage = event.getMessage().getContentDisplay().trim();
String prefix = Settings.getInstance().get("NETNEUTRALITY_BOT_PREFIX", "$");
if (rawMessage.startsWith(prefix)) {
rawMessage = rawMessage.replaceFirst(String.format("^[%s\\s]{1,}", prefix), "").trim();
// rawMessage = rawMessage.substring(prefix.length()).trim();
String commandString = rawMessage.split(" ", 2)[0];
CommandProcessor processor = new CommandProcessor(commandString, context);
MessageCommandContext context = new MessageCommandContext(event);
CommandProcessor processor = new CommandProcessor(context);
processor.dispatch();
}
}
@@ -1,31 +0,0 @@
package me.bobert.netneutrality.hooks.message;
/*
* Copyright (c) anthorob
*
* https://discord.gg/qUa6zNFZsd
*/
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.events.message.MessageUpdateEvent;
import net.dv8tion.jda.api.events.message.react.MessageReactionAddEvent;
import net.dv8tion.jda.api.events.message.react.MessageReactionRemoveEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
public class ReactionHandler extends ListenerAdapter {
@Override
public void onMessageReactionAdd(@NotNull MessageReactionAddEvent event) {
Member member = event.getMember();
}
@Override
public void onMessageReactionRemove(@NotNull MessageReactionRemoveEvent event) {
}
@Override
public void onMessageUpdate(@NotNull MessageUpdateEvent event) {
}
}
@@ -9,7 +9,7 @@ import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter;
import org.jetbrains.annotations.NotNull;
public class VoiceEventHandler extends ListenerAdapter {
public class VoiceEventHook extends ListenerAdapter {
@Override
public void onGuildVoiceUpdate(@NotNull GuildVoiceUpdateEvent event) {
@@ -18,16 +18,20 @@ import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
public class WebModule {
private final String url;
private final OkHttpClient client = new OkHttpClient();
private final OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.build();
private Response response;
//private final String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36 OPR/82.0.4227.43";
private static final String userAgent = String.format("Net Neutrality/%s (Discord bot, [email protected])", NetNeutrality.VERSION);
public WebModule(String url) {
this.url = url;
}
@@ -7,7 +7,7 @@ package me.bobert.netneutrality.http.server;
*/
import com.sun.net.httpserver.HttpServer;
import me.bobert.netneutrality.thread.BaseThreadFactory;
import me.bobert.netneutrality.core.thread.BaseThreadFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -18,7 +18,7 @@ import java.util.concurrent.Executors;
public class WebApi {
private final static Logger logger = LogManager.getLogger(WebApi.class);
private static final Logger logger = LogManager.getLogger(WebApi.class);
private HttpServer server;
@@ -13,19 +13,18 @@ import com.sedmelluq.discord.lavaplayer.source.http.HttpAudioSourceManager;
import com.sedmelluq.discord.lavaplayer.source.soundcloud.SoundCloudAudioSourceManager;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import dev.lavalink.youtube.YoutubeAudioSourceManager;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.core.GuildManager;
import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.thread.ThreadManager;
import me.bobert.netneutrality.db.GSettings;
import me.bobert.netneutrality.exceptions.CommandExecutionException;
import me.bobert.netneutrality.music.source.UdioAudioSourceManager;
import me.bobert.netneutrality.thread.MusicPlayerAutoDisconnectThread;
import me.bobert.netneutrality.thread.ThreadManager;
import me.bobert.netneutrality.utils.MusicUtils;
import me.bobert.netneutrality.utils.Settings;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.entities.channel.middleman.AudioChannel;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -37,16 +36,16 @@ import java.util.concurrent.TimeUnit;
public class MusicManager {
private final static Logger logger = LogManager.getLogger(MusicManager.class);
private final static Map<String, MusicManager> managers = new ConcurrentHashMap<>();
private final static ScheduledExecutorService executor = ThreadManager.getMusicExecutor();
private static final Logger logger = LogManager.getLogger(MusicManager.class);
private static final Map<String, MusicManager> managers = new ConcurrentHashMap<>();
private static final ScheduledExecutorService executor = ThreadManager.getScheduledExecutor();
private final static AudioPlayerManager playerManager = new DefaultAudioPlayerManager();
private static final AudioPlayerManager playerManager = new DefaultAudioPlayerManager();
private final Guild guild;
private final AudioPlayer player;
private final TrackScheduler scheduler;
private ScheduledFuture<?> autoDisconnectThread;
private final int baseVolume;
private ScheduledFuture<?> autoDisconnectThread;
private MusicManager(Guild guild) {
this.guild = guild;
@@ -73,16 +72,20 @@ public class MusicManager {
playerManager.registerSourceManager(SoundCloudAudioSourceManager.createDefault());
playerManager.registerSourceManager(new UdioAudioSourceManager());
playerManager.registerSourceManager(new HttpAudioSourceManager());
String po = Settings.getInstance().get("NETNEUTRALITY_PO_TOKEN");
String visitor = Settings.getInstance().get("NETNEUTRALITY_VISITOR_DATA");
dev.lavalink.youtube.clients.Web.setPoTokenAndVisitorData(po, visitor);
}
public boolean isNotConnectedTo(VoiceChannel vChannel) {
public boolean isNotConnectedTo(AudioChannel vChannel) {
return vChannel == null || !vChannel.equals(guild.getAudioManager().getConnectedChannel());
}
public synchronized void connect(VoiceChannel vChannel) {
if (vChannel != null && isNotConnectedTo(vChannel)) {
logger.debug("Guild {}: connecting to voice channel {}", vChannel.getGuild().getId(), vChannel.getId());
guild.getAudioManager().openAudioConnection(vChannel);
public synchronized void connect(AudioChannel audioChannel) {
if (audioChannel != null && isNotConnectedTo(audioChannel)) {
logger.debug("Guild {}: connecting to voice channel {}", audioChannel.getGuild().getId(), audioChannel.getId());
guild.getAudioManager().openAudioConnection(audioChannel);
guild.getAudioManager().setSelfDeafened(true);
player.setVolume(baseVolume);
@@ -144,42 +147,22 @@ public class MusicManager {
}
}
public synchronized void play(String query, CommandContext context, boolean needToBeConnected, boolean silent) {
public synchronized void play(String query, CommandContext context) {
play(query, context, false);
}
public synchronized void play(String query, CommandContext context, boolean silent) {
logger.debug("Guild {}: request to play '{}' by user {}", guild.getId(), query, context.getGuildUser().getId());
Member member = context.getGuildUser();
MessageChannel channel = context.getChannel();
if (member.getVoiceState() == null) {
logger.error("Guild {}: Voice state is null", guild.getId());
context.replyToMessage("Voice state is null, something has gone terribly wrong.");
LogHandler.log(new CommandExecutionException("Voice state is null", context, null));
return;
}
if (!member.getVoiceState().inAudioChannel()) {
context.replyToMessage("You are not in a voice channel");
return;
}
VoiceChannel vChannel = (VoiceChannel) member.getVoiceState().getChannel();
if (needToBeConnected && isNotConnectedTo(vChannel)) {
context.replyToMessage("The player is not in your voice channel");
return;
}
if (isNotConnectedTo(vChannel)) {
connect(vChannel);
scheduler.attachTextChannel(channel);
}
SourceFinder.AudioSourceType type = SourceFinder.getSource(query);
String identifier = SourceFinder.getIdentifier(query);
if (identifier != null) {
if (identifier == null) {
context.reply("No source found for provided query");
return;
}
if (MusicUtils.connect(context)) {
TrackLoadingHandler loadingHandler = new TrackLoadingHandler(context, this, identifier, type, silent);
playerManager.loadItemOrdered(this, identifier, loadingHandler);
}
}
@@ -189,29 +172,28 @@ public class MusicManager {
}
public synchronized CommandExecutionResult voteSkip(CommandContext context) {
VoiceChannel voiceChannel = (VoiceChannel) guild.getAudioManager().getConnectedChannel();
AudioChannel audioChannel = guild.getAudioManager().getConnectedChannel();
if (voiceChannel == null) {
return CommandExecutionResult.none();
if (audioChannel == null) {
return CommandExecutionResult.warn("Bot is not connected to a voice channel");
}
if (!isCurrentlyPlaying()) {
return CommandExecutionResult.none();
return CommandExecutionResult.warn("No tracks are currently playing");
}
QueueManager.TrackInfo info = scheduler.getQueueManager().peekHeadOfQueue();
if (info == null) {
return CommandExecutionResult.none();
return CommandExecutionResult.warn("Queue is empty");
}
// Minus the bot itself
int connected = voiceChannel.getMembers().size() - 1;
int connected = audioChannel.getMembers().size() - 1;
if (connected < 3) {
context.reply("Skipping current track since not enough people are connected to the voice channel.");
scheduler.getQueueManager().skip();
return CommandExecutionResult.none();
return CommandExecutionResult.info("Skipping current track since not enough people are connected to the voice channel.");
}
double needed = connected / 2.1;
@@ -221,11 +203,12 @@ public class MusicManager {
if (info.getSkips().size() > needed) {
return scheduler.getQueueManager().skip();
} else {
context.reply("Adding your vote to skip the current track. Current votes: %d/%d", info.getSkips().size(), (int) Math.ceil(needed));
}
return CommandExecutionResult.none();
return CommandExecutionResult.success(String.format("Adding your vote to skip the current track. Current votes: %d/%d",
info.getSkips().size(),
(int) Math.ceil(needed))
);
}
public synchronized void setVolume(int volume) {
@@ -7,7 +7,7 @@ package me.bobert.netneutrality.music;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import me.bobert.netneutrality.command.CommandExecutionResult;
import me.bobert.netneutrality.core.command.CommandExecutionResult;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import org.apache.logging.log4j.LogManager;
@@ -24,7 +24,7 @@ public class QueueManager {
private final AudioTrack track;
private final Member member;
private final Set<String> skips;
private final Logger logger = LogManager.getLogger(QueueManager.class);
private static final Logger logger = LogManager.getLogger(QueueManager.class);
public TrackInfo(AudioTrack track, Member member) {
this.track = track;
@@ -186,6 +186,9 @@ public class QueueManager {
public void queue(AudioTrack track, Member member) {
TrackInfo info = new TrackInfo(track, member);
if (queue.contains(info)) {
return;
}
synchronized (queue) {
if (queue.contains(info)) {
@@ -12,13 +12,15 @@ import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.core.context.CommandContext;
import me.bobert.netneutrality.core.tasks.TaskManager;
import me.bobert.netneutrality.tasks.music.LoadPlaylistTask;
import me.bobert.netneutrality.utils.MessageFunction;
import net.dv8tion.jda.api.entities.Guild;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class TrackLoadingHandler implements AudioLoadResultHandler {
private final Logger logger = LogManager.getLogger(TrackLoadingHandler.class);
private static final Logger logger = LogManager.getLogger(TrackLoadingHandler.class);
private final String identifier;
private final SourceFinder.AudioSourceType type;
@@ -44,7 +46,7 @@ public class TrackLoadingHandler implements AudioLoadResultHandler {
scheduler.getQueueManager().queue(audioTrack, context.getGuildUser());
if (!silent) {
context.reply(MessageFunction.buildAddingTrackMessage(audioTrack, type));
context.reply(MessageFunction.buildAddingTrackMessage(audioTrack, context.getGuildUser(), type));
}
}
@@ -52,33 +54,13 @@ public class TrackLoadingHandler implements AudioLoadResultHandler {
public void playlistLoaded(AudioPlaylist playlist) {
logger.debug("Guild {}: playlist loaded: {}", guild.getId(), identifier);
if (!playlist.getTracks().isEmpty()) {
if (playlist.isSearchResult()) {
AudioTrack track = playlist.getTracks().get(0);
scheduler.getQueueManager().queue(track, context.getGuildUser());
if (!silent) {
context.reply(MessageFunction.buildAddingTrackMessage(track, type));
}
} else {
context.deleteMessage();
if (!silent) {
context.reply(MessageFunction.buildAddingPlaylistMessage(playlist, identifier, type));
}
for (AudioTrack track : playlist.getTracks()) {
scheduler.getQueueManager().queue(track, context.getGuildUser());
}
}
}
TaskManager.getInstance().insert(context, new LoadPlaylistTask(playlist));
}
@Override
public void noMatches() {
if (!silent)
context.replyToMessage("No matches found");
context.reply("No matches found");
}
@Override
@@ -87,6 +69,6 @@ public class TrackLoadingHandler implements AudioLoadResultHandler {
LogHandler.log(e);
if (!silent)
context.replyToMessage(MessageFunction.buildSentryErrorMessage(new RuntimeException("Failed to load track: " + e.getMessage())));
context.reply(MessageFunction.buildSentryErrorMessage(new RuntimeException("Failed to load track: " + e.getMessage(), e)));
}
}
@@ -10,22 +10,24 @@ import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
import me.bobert.netneutrality.context.MusicChannelContext;
import me.bobert.netneutrality.core.LogHandler;
import me.bobert.netneutrality.utils.MessageFunction;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
public class TrackScheduler extends AudioEventAdapter {
private final static Logger logger = LogManager.getLogger(TrackScheduler.class);
private static final Logger logger = LogManager.getLogger(TrackScheduler.class);
private final QueueManager queueManager;
private final MusicManager manager;
private final AudioPlayer player;
private MessageChannel currentChannel;
private final Guild guild;
private MusicChannelContext context;
private AudioTrack lastStuckTrack = null;
public TrackScheduler(MusicManager manager) {
this.manager = manager;
@@ -50,28 +52,18 @@ public class TrackScheduler extends AudioEventAdapter {
return guild;
}
public MessageChannel getTextChannel() {
return currentChannel;
public MusicChannelContext getCurrentContext() {
return context;
}
public synchronized void attachTextChannel(MessageChannel channel) {
this.currentChannel = channel;
public synchronized void attachChannel(MessageChannel channel) {
if (channel instanceof GuildMessageChannel guildMessageChannel) {
this.context = new MusicChannelContext(guildMessageChannel);
}
}
public synchronized void detachTextChannel() {
this.currentChannel = null;
}
public void sendMessageToChannel(String msg) {
if (currentChannel != null && currentChannel.canTalk()) {
currentChannel.sendMessage(msg).queue();
}
}
public void sendMessageToChannel(MessageEmbed embed, MessageEmbed... embeds) {
if (currentChannel != null && currentChannel.canTalk()) {
currentChannel.sendMessageEmbeds(embed, embeds).queue();
}
this.context = null;
}
@Override
@@ -118,13 +110,58 @@ public class TrackScheduler extends AudioEventAdapter {
public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception) {
logger.debug("Guild {}: exception while playing track [{}]: {}", guild.getId(), track.getIdentifier(), exception.getMessage());
LogHandler.log(exception);
sendMessageToChannel(MessageFunction.buildSentryErrorMessage(new RuntimeException(String.format("Error while playing track [%s]: %s", track.getInfo().title, exception.getMessage()))));
context.reply(
MessageFunction.buildSentryErrorMessage(
new RuntimeException(String.format("Error while playing track: %s", track.getInfo().title), exception)
)
);
}
@Override
public void onTrackStuck(AudioPlayer player, AudioTrack track, long thresholdMs) {
logger.debug("Guild {}: currently playing track is stuck: {}", guild.getId(), track.getIdentifier());
sendMessageToChannel("Current track is stuck, skipping to next");
queueManager.startNextTrack();
logger.error("Guild {}: currently playing track is stuck: {}", guild.getId(), track.getIdentifier());
if (lastStuckTrack == null || !lastStuckTrack.getIdentifier().equals(track.getIdentifier())) {
lastStuckTrack = track;
logger.debug("Trying to load stuck track again: {}", track.getIdentifier());
context.async("Current track is stuck, trying to load it again", asyncMessage -> {
try {
QueueManager.TrackInfo info = queueManager.peekHeadOfQueue();
if (info != null && info.getTrack().getIdentifier().equals(track.getIdentifier())) {
AudioTrack clone = track.makeClone();
long lastPosition = track.getPosition();
player.stopTrack();
if (queueManager.peekHeadOfQueue() != null && queueManager.peekHeadOfQueue().getTrack().getIdentifier().equals(track.getIdentifier())) {
queueManager.removeHeadOfQueue();
}
queueManager.queue(clone, info.getRequester());
AudioTrack playing = player.getPlayingTrack();
if (playing != null && playing.getIdentifier().equals(track.getIdentifier())) {
playing.setPosition(lastPosition);
lastStuckTrack = null;
} else {
asyncMessage.edit("Failed to seek track to last timestamp, the track will play from the beginning");
}
} else {
asyncMessage.edit("Failed to load last playing track, skipping to next..");
queueManager.startNextTrack();
}
} catch (Exception e) {
LogHandler.log(e);
asyncMessage.edit("Failed to load last playing track, skipping to next..");
queueManager.startNextTrack();
}
});
} else {
context.reply("Current track is stuck, skipping to next..");
queueManager.startNextTrack();
}
}
}
@@ -129,7 +129,7 @@ public class UdioAudioSourceManager implements AudioSourceManager {
}
protected JsonBrowser loadTrackInfoFromPageContent(String content) throws IOException {
String startTag = "self.__next_f.push([1,\"9:";
String startTag = "self.__next_f.push([1,\"d:";
String endTag = "\\n\"])";
String json = DataFormatTools.extractBetween(content, startTag, endTag);
Loaded 100 of 121 files, more files were not shown because too many files have changed in this diff. Show more