Add voteskip command and update music player

This commit is contained in:
anthorob committed 2022-02-08 18:34:06 -05:00
1 parent dbf6fd549c
commit 048b645e61
41 files changed
+779 -308

No files matched your search

+135 -131
View File
@@ -1,151 +1,155 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.xtremcommunity.discord</groupId> <groupId>com.xtremcommunity.discord</groupId>
<artifactId>netneutrality</artifactId> <artifactId>netneutrality</artifactId>
<version>1.0.0</version> <version>1.0.0</version>
<name>netneutrality-discord-bot</name> <name>netneutrality-discord-bot</name>
<description>Discord bot</description> <description>Discord bot</description>
<packaging>jar</packaging> <packaging>jar</packaging>
<properties> <properties>
<java.version>17</java.version> <java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> <maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<build> <build>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version> <version>3.8.0</version>
<configuration> <configuration>
<release>17</release> <release>17</release>
<encoding>UTF-8</encoding> <encoding>UTF-8</encoding>
<source>17</source> <source>17</source>
<target>17</target> <target>17</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<!-- Build an executable JAR --> <!-- Build an executable JAR -->
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId> <artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version> <version>3.2.0</version>
<configuration> <configuration>
<archive> <archive>
<manifest> <manifest>
<addClasspath>true</addClasspath> <addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix> <classpathPrefix>lib/</classpathPrefix>
<mainClass>netneutrality.MusicBot</mainClass> <mainClass>netneutrality.MusicBot</mainClass>
</manifest> </manifest>
</archive> </archive>
</configuration> </configuration>
</plugin> </plugin>
<plugin> <plugin>
<artifactId>maven-assembly-plugin</artifactId> <artifactId>maven-assembly-plugin</artifactId>
<version>3.3.0</version> <version>3.3.0</version>
<configuration> <configuration>
<archive> <archive>
<manifest> <manifest>
<mainClass>netneutrality.MusicBot</mainClass> <mainClass>netneutrality.MusicBot</mainClass>
<addDefaultImplementationEntries>true</addDefaultImplementationEntries> <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
<addDefaultSpecificationEntries>true</addDefaultSpecificationEntries> <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
</manifest> </manifest>
<manifestEntries> <manifestEntries>
<Multi-Release>true</Multi-Release> <Multi-Release>true</Multi-Release>
</manifestEntries> </manifestEntries>
</archive> </archive>
<descriptorRefs> <descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef> <descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs> </descriptorRefs>
<finalName>netneutrality</finalName> <finalName>netneutrality</finalName>
</configuration> </configuration>
<executions> <executions>
<execution> <execution>
<id>make-assembly</id> <!-- this is used for inheritance merges --> <id>make-assembly</id> <!-- this is used for inheritance merges -->
<phase>package</phase> <!-- bind to the packaging phase --> <phase>package</phase> <!-- bind to the packaging phase -->
<goals> <goals>
<goal>single</goal> <goal>single</goal>
</goals> </goals>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>
<repositories> <repositories>
<repository> <repository>
<id>central</id> <id>central</id>
<url>https://repo.maven.apache.org/maven2</url> <url>https://repo.maven.apache.org/maven2</url>
</repository> </repository>
<repository> <repository>
<id>dv8tion</id> <id>dv8tion</id>
<name>m2-dv8tion</name> <name>m2-dv8tion</name>
<url>https://m2.dv8tion.net/releases</url> <url>https://m2.dv8tion.net/releases</url>
</repository> </repository>
</repositories> </repositories>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>net.dv8tion</groupId> <groupId>net.dv8tion</groupId>
<artifactId>JDA</artifactId> <artifactId>JDA</artifactId>
<version>4.4.0_350</version> <version>4.4.0_350</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.squareup.okhttp3</groupId> <groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId> <artifactId>okhttp</artifactId>
<version>4.9.3</version> <version>4.9.3</version>
</dependency> </dependency>
<!-- https://mvnrepository.com/artifact/org.json/json --> <!-- https://mvnrepository.com/artifact/org.json/json -->
<dependency> <dependency>
<groupId>org.json</groupId> <groupId>org.json</groupId>
<artifactId>json</artifactId> <artifactId>json</artifactId>
<version>20210307</version> <version>20210307</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.reflections</groupId> <groupId>org.reflections</groupId>
<artifactId>reflections</artifactId> <artifactId>reflections</artifactId>
<version>0.10.2</version> <version>0.10.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>mysql-connector-java</artifactId>
<version>8.0.27</version> <version>8.0.27</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId> <artifactId>log4j-api</artifactId>
<version>2.17.0</version> <version>2.17.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId> <artifactId>log4j-core</artifactId>
<version>2.17.0</version> <version>2.17.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.logging.log4j</groupId> <groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId> <artifactId>log4j-slf4j-impl</artifactId>
<version>2.17.0</version> <version>2.17.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.apache.commons</groupId> <groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId> <artifactId>commons-lang3</artifactId>
<version>3.12.0</version> <version>3.12.0</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.sedmelluq</groupId> <groupId>com.sedmelluq</groupId>
<artifactId>lavaplayer</artifactId> <artifactId>lavaplayer</artifactId>
<version>1.3.78</version> <version>1.3.78</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
+51 -6
View File
@@ -10,8 +10,6 @@ import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.TextChannel;
import netneutrality.db.DBController;
import netneutrality.db.controllers.DBMessage;
import netneutrality.db.entities.OGuild; import netneutrality.db.entities.OGuild;
import netneutrality.handler.ratelimiter.IMessageRateLimiter; import netneutrality.handler.ratelimiter.IMessageRateLimiter;
import netneutrality.handler.ratelimiter.MessageRateLimiterImpl; import netneutrality.handler.ratelimiter.MessageRateLimiterImpl;
@@ -24,17 +22,30 @@ import java.util.concurrent.ConcurrentHashMap;
public class GuildManager { public class GuildManager {
public enum GuildSettings { public enum GuildSetting {
MUSIC_ROLE("muisc.role"), MUSIC_CHANNEL("music.channel"), BLACKLIST_CHANNELS("blacklist.exclude.channels"); MUSIC_ROLE("music.role", null), MUSIC_CHANNEL("music.channel", null), BLACKLIST_CHANNELS("blacklist.exclude.channels", null);
private final String setting; private final String setting;
private final String defaultValue;
GuildSettings(String setting) { GuildSetting(String setting, String defaultValue) {
this.setting = setting; this.setting = setting;
this.defaultValue = defaultValue;
}
public String getSetting() {
return setting;
}
public String getDefaultValue() {
return defaultValue;
} }
@Override @Override
public String toString() { public String toString() {
if (setting == null)
return defaultValue;
return setting; return setting;
} }
} }
@@ -61,6 +72,36 @@ public class GuildManager {
} }
} }
public static GuildManager getManager(String guildId) {
return managers.getOrDefault(guildId, null);
}
public static Map<String, GuildManager> getManagers() {
return managers;
}
public static boolean reloadSettingsForGuild(String guildId) {
if (managers.containsKey(guildId)) {
GuildManager manager = managers.get(guildId);
manager.reloadSettings();
return true;
}
return false;
}
public static boolean reloadSettingsForGuild(Guild guild) {
if (managers.containsKey(guild.getId())) {
GuildManager manager = GuildManager.getManager(guild.getId());
manager.reloadSettings();
return true;
}
return false;
}
public void checkAndLogMessage(Message message, Member member, TextChannel channel) { public void checkAndLogMessage(Message message, Member member, TextChannel channel) {
//((DBMessage) DBController.get("message")).log(guild, channel, member, message); //((DBMessage) DBController.get("message")).log(guild, channel, member, message);
} }
@@ -74,10 +115,14 @@ public class GuildManager {
return guildEntity.getSettings().getSettingByKey(key); return guildEntity.getSettings().getSettingByKey(key);
} }
public String getSetting(GuildSettings key) { public String getSetting(GuildSetting key) {
return guildEntity.getSettings().getSettingByKey(key.toString()); return guildEntity.getSettings().getSettingByKey(key.toString());
} }
public Map<String, String> getSettings() {
return guildEntity.getSettings().getSettings();
}
public void reloadSettings() { public void reloadSettings() {
guildEntity.reloadSettings(); guildEntity.reloadSettings();
} }
+8 -17
View File
@@ -7,16 +7,15 @@ import net.dv8tion.jda.api.requests.GatewayIntent;
import netneutrality.command.CommandManager; import netneutrality.command.CommandManager;
import netneutrality.db.DBController; import netneutrality.db.DBController;
import netneutrality.handler.EditHandler; import netneutrality.handler.message.EditHandler;
import netneutrality.handler.MessageReceivedHandler; import netneutrality.handler.message.MessageReceivedHandler;
import netneutrality.handler.ServerJoinHandler; import netneutrality.handler.ServerJoinHandler;
import netneutrality.http.server.WebApi; import netneutrality.http.server.WebApi;
import netneutrality.music.MusicManager; import netneutrality.music.MusicManager;
import netneutrality.thread.BaseThreadFactory;
import netneutrality.thread.ShutdownThread;
import netneutrality.utils.Settings; import netneutrality.utils.Settings;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configurator;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -33,28 +32,20 @@ public class MusicBot {
initJDA(); initJDA();
CommandManager.init(); CommandManager.init();
MusicManager.registerSources();
jda.getPresence().setActivity(Activity.watching("me load...")); jda.getPresence().setActivity(Activity.watching("me load..."));
jda.addEventListener(new MessageReceivedHandler(), new EditHandler(), new ServerJoinHandler()); jda.addEventListener(new MessageReceivedHandler(), new EditHandler(), new ServerJoinHandler());
new WebApi(); new WebApi();
jda.getPresence().setActivity(Activity.watching("you")); jda.getPresence().setActivity(Activity.watching("you"));
Runtime.getRuntime().addShutdownHook(new Thread(() -> { Runtime.getRuntime().addShutdownHook(new BaseThreadFactory("shutdown").newThread(new ShutdownThread()));
if( LogManager.getContext() instanceof LoggerContext) {
logger.debug("Shutting down log4j2");
Configurator.shutdown((LoggerContext)LogManager.getContext());
}
MusicBot.shutdown();
}));
} }
public static void initJDA() { public static void initJDA() {
try { try {
jda = JDABuilder.createDefault(Settings.getInstance().get("BOT_TOKEN")) jda = JDABuilder.createDefault(Settings.getInstance().get("BOT_TOKEN")).enableIntents(GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_MESSAGE_REACTIONS, GatewayIntent.GUILD_VOICE_STATES).build();
.enableIntents(GatewayIntent.GUILD_MEMBERS, GatewayIntent.GUILD_MESSAGE_REACTIONS, GatewayIntent.GUILD_VOICE_STATES)
.build();
jda.awaitReady(); jda.awaitReady();
} catch (LoginException | IllegalArgumentException | InterruptedException e) { } catch (LoginException | IllegalArgumentException | InterruptedException e) {
logger.error(e.getMessage()); logger.error(e.getMessage());
@@ -77,7 +68,7 @@ public class MusicBot {
private static void disconnect() { private static void disconnect() {
if (jda != null) { if (jda != null) {
jda.shutdownNow(); jda.shutdown();
} }
} }
+5 -23
View File
@@ -1,6 +1,6 @@
package netneutrality; package netneutrality;
import netneutrality.thread.BaseThread; import netneutrality.thread.BaseThreadFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -17,7 +17,7 @@ public class ThreadManager {
private final static Logger logger = LoggerFactory.getLogger(ThreadManager.class); private final static Logger logger = LoggerFactory.getLogger(ThreadManager.class);
public static ExecutorService getExecutor(String name) { public static ExecutorService getExecutor(String name) {
return executors.computeIfAbsent(name, s -> Executors.newSingleThreadExecutor(new BaseThread(name))); return executors.computeIfAbsent(name, s -> Executors.newSingleThreadExecutor(new BaseThreadFactory(name)));
} }
@@ -26,36 +26,18 @@ public class ThreadManager {
switch (type) { switch (type) {
case FIXED: case FIXED:
executor = executors.computeIfAbsent(name, s -> Executors.newFixedThreadPool(5, new BaseThread(name))); executor = executors.computeIfAbsent(name, s -> Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new BaseThreadFactory(name)));
break; break;
case SCHEDULED: case SCHEDULED:
executor = executors.computeIfAbsent(name, s -> Executors.newScheduledThreadPool(5, new BaseThread(name))); executor = executors.computeIfAbsent(name, s -> Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors(), new BaseThreadFactory(name)));
break; break;
default: default:
executor = executors.computeIfAbsent(name, s -> Executors.newSingleThreadExecutor(new BaseThread(name))); executor = executors.computeIfAbsent(name, s -> Executors.newSingleThreadExecutor(new BaseThreadFactory(name)));
} }
return executor; return executor;
} }
public static ExecutorService getExecutor(String name, ExecutorType type, Integer poolSize) {
ExecutorService executor;
switch (type) {
case FIXED:
executor = executors.computeIfAbsent(name, s -> Executors.newFixedThreadPool(poolSize, new BaseThread(name)));
break;
case SCHEDULED:
executor = executors.computeIfAbsent(name, s -> Executors.newScheduledThreadPool(poolSize, new BaseThread(name)));
break;
default:
executor = executors.computeIfAbsent(name, s -> Executors.newSingleThreadExecutor(new BaseThread(name)));
}
return executor;
}
public static Future<?> dispatch(ExecutorService executor, Runnable task) { public static Future<?> dispatch(ExecutorService executor, Runnable task) {
return executor.submit(task); return executor.submit(task);
} }
@@ -1,4 +1,5 @@
package netneutrality.command;/* package netneutrality.command;
/*
* Copyright (c) anthorob * Copyright (c) anthorob
* *
* https://xtremcommunity.com/ * https://xtremcommunity.com/
@@ -10,6 +11,8 @@ import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Role; import net.dv8tion.jda.api.entities.Role;
import netneutrality.GuildManager; import netneutrality.GuildManager;
import java.util.List;
public abstract class AbstractMusicCommand extends AbstractCommand { public abstract class AbstractMusicCommand extends AbstractCommand {
@Override @Override
@@ -24,14 +27,15 @@ public abstract class AbstractMusicCommand extends AbstractCommand {
@Override @Override
public boolean canBeExecutedBy(Guild guild, Member member) { public boolean canBeExecutedBy(Guild guild, Member member) {
String roleDb = GuildManager.getManager(guild).getSetting(GuildManager.GuildSettings.MUSIC_ROLE); String roleDb = GuildManager.getManager(guild).getSetting(GuildManager.GuildSetting.MUSIC_ROLE);
boolean can = true;
//System.out.println(roleDb); //System.out.println(roleDb);
if (roleDb != null && !roleDb.isEmpty()) { if (roleDb != null && !roleDb.isEmpty()) {
Role neededRole = guild.getRoleById(roleDb); Role neededRole = guild.getRoleById(roleDb);
return member.getRoles().contains(neededRole); can = member.getRoles().contains(neededRole);
} }
return true; return can;
} }
} }
@@ -15,11 +15,12 @@ import netneutrality.utils.Settings;
import netneutrality.utils.UserFunction; import netneutrality.utils.UserFunction;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
public class CommandContext { public class CommandContext {
private static final ExecutorService commandExecutor = ThreadManager.getExecutor("command-execution", ThreadManager.ExecutorType.FIXED, 10); private static final ExecutorService commandExecutor = ThreadManager.getExecutor("command-execution", ThreadManager.ExecutorType.FIXED);
private AbstractCommand command; private AbstractCommand command;
private final Message message; private final Message message;
@@ -49,11 +50,15 @@ public class CommandContext {
return new CommandContext(message, member, user, guild, channel, jda); return new CommandContext(message, member, user, guild, channel, jda);
} }
public String getArg(int index) {
return getArgumentsAsArray()[index];
}
public String[] getArgumentsAsArray() { public String[] getArgumentsAsArray() {
String msg = message.getContentDisplay().trim(); String msg = message.getContentDisplay().trim();
msg = msg.substring(Settings.getInstance().get("BOT_PREFIX", "$").length()).trim(); msg = msg.substring(Settings.getInstance().get("BOT_PREFIX", "$").length()).trim();
String cmd = msg.split(" ", 2)[0]; String cmd = msg.split(" ", 2)[0];
String[] args = msg.replaceFirst("(" + cmd + ")(\s)?", "").split("\s"); String[] args = msg.replaceFirst("(" + cmd + ")(\\s+)?", "").split("\s");
if (args.length == 1 && args[0].isEmpty()) if (args.length == 1 && args[0].isEmpty())
args = new String[0]; args = new String[0];
@@ -97,6 +102,42 @@ public class CommandContext {
return command; return command;
} }
public void reply(String msg) {
if (channel != null) {
channel.sendMessage(msg).queue();
}
}
public void reply(Message msg) {
if (channel != null) {
channel.sendMessage(msg).queue();
}
}
public void reply(MessageEmbed embed, MessageEmbed... embeds) {
if (channel != null) {
channel.sendMessageEmbeds(embed, embeds).queue();
}
}
public void reply(Collection<? extends MessageEmbed> embeds) {
if (channel != null) {
channel.sendMessageEmbeds(embeds).queue();
}
}
public void replyPrivate(String msg) {
user.openPrivateChannel().flatMap(channel -> channel.sendMessage(msg)).queue();
}
public void replyPrivate(Message msg) {
user.openPrivateChannel().flatMap(channel -> channel.sendMessage(msg)).queue();
}
public void replyPrivate(Collection<? extends MessageEmbed> embeds) {
user.openPrivateChannel().flatMap(channel -> channel.sendMessageEmbeds(embeds)).queue();
}
public void dispatch(AbstractCommand command) { public void dispatch(AbstractCommand command) {
this.command = command; this.command = command;
//TODO: Add usage check //TODO: Add usage check
@@ -0,0 +1,49 @@
package netneutrality.command;/*
* Copyright (c) anthorob
*
* https://xtremcommunity.com/
* https://discord.gg/CdFcyQ
*/
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
public class CommandUsage {
public static class Argument {
public enum Type {
OPTIONAL, REQUIRED
}
private final Type type;
private final String description;
public Argument(Type t, String desc) {
this.type = t;
this.description = desc;
}
public String getDescription() {
return description;
}
public Type getType() {
return type;
}
}
private final Set<Argument> args = new HashSet<>();
public CommandUsage(Argument... arguments) {
Collections.addAll(args, arguments);
}
public static CommandUsage create(Argument... arguments) {
return new CommandUsage(arguments);
}
public Set<Argument> getArguments() {
return args;
}
}
@@ -58,7 +58,7 @@ public class HelpCommand extends AbstractCommand {
public void execute(CommandContext context) { public void execute(CommandContext context) {
List<MessageEmbed> help = MessageFunction.buildHelpMessage(CommandManager.commands, CommandManager.commandsAlias, UserFunction.isAdmin(context.getGuildUser())); List<MessageEmbed> help = MessageFunction.buildHelpMessage(CommandManager.commands, CommandManager.commandsAlias, UserFunction.isAdmin(context.getGuildUser()));
context.getChannel().sendMessage(new MessageBuilder().append(context.getDiscordUser()).append(" help has been sent your way!").build()).queue(); context.reply(new MessageBuilder().append(context.getDiscordUser()).append(" help has been sent your way!").build());
context.getDiscordUser().openPrivateChannel().queue(t -> t.sendMessageEmbeds(help).queue()); context.replyPrivate(help);
} }
} }
@@ -51,7 +51,7 @@ public class PingCommand extends AbstractCommand {
@Override @Override
public void execute(CommandContext context) { public void execute(CommandContext context) {
if (context.getArgumentsAsArray().length == 0) if (context.getArgumentsAsArray().length == 0)
context.getChannel().sendMessage("Pong! [" + MusicBot.getJDA().getGatewayPing() + " ms]").queue(); context.reply(String.format("Pong! [%d ms]", context.getJDA().getGatewayPing()));
} }
} }
@@ -88,7 +88,7 @@ public class BitcoinPriceCommand extends AbstractCommand {
throw new CommandExecutionException("Error during web request", context, e); throw new CommandExecutionException("Error during web request", context, e);
} }
t.editMessage(String.format("The current price of %s in %s is %s$", crypto, finalCurrency, price)).queue(); t.editMessage(String.format("The current price of %s in %s is %s", crypto, finalCurrency, price)).queue();
}); });
} }
@@ -44,7 +44,7 @@ public class CryptoPriceCommand extends AbstractCommand {
@Override @Override
public String getUsage() { public String getUsage() {
return "<crypto> <currency>"; return "<crypto> <currency (optional)>";
} }
@Override @Override
@@ -88,7 +88,7 @@ public class EthereumPriceCommand extends AbstractCommand {
throw new CommandExecutionException("Error during web request", context, e); throw new CommandExecutionException("Error during web request", context, e);
} }
t.editMessage(String.format("The current price of %s in %s is %s$", crypto, finalCurrency, price)).queue(); t.editMessage(String.format("The current price of %s in %s is %s", crypto, finalCurrency, price)).queue();
}); });
} }
@@ -88,7 +88,7 @@ public class LitecoinPriceCommand extends AbstractCommand {
throw new CommandExecutionException("Error during web request", context, e); throw new CommandExecutionException("Error during web request", context, e);
} }
t.editMessage(String.format("The current price of %s in %s is %s$", crypto, finalCurrency, price)).queue(); t.editMessage(String.format("The current price of %s in %s is %s", crypto, finalCurrency, price)).queue();
}); });
} }
@@ -40,7 +40,7 @@ public class DeleteQueueCommand extends AbstractMusicCommand {
@Override @Override
public String getDescription() { public String getDescription() {
return null; return "Deletes the music queue";
} }
@Override @Override
@@ -37,7 +37,7 @@ public class JoinCommand extends AbstractMusicCommand {
@Override @Override
public String getDescription() { public String getDescription() {
return null; return "Make the bot join the current voice channel (if no music is playing, bot will disconnect automatically)";
} }
@Override @Override
@@ -40,7 +40,7 @@ public class LeaveCommand extends AbstractMusicCommand {
@Override @Override
public String getDescription() { public String getDescription() {
return null; return "Make the bot leave the voice channel";
} }
@Override @Override
@@ -35,19 +35,19 @@ public class PlayCommand extends AbstractMusicCommand {
@Override @Override
public String getDescription() { public String getDescription() {
return "Get the party going"; return "Get the party going!";
} }
@Override @Override
public void execute(CommandContext context) { public void execute(CommandContext context) {
MusicManager player = MusicManager.get(context.getGuild()); MusicManager player = MusicManager.get(context.getGuild());
context.getMessage().delete().queue();
if (context.getArgumentsAsArray().length > 0) { if (context.getArgumentsAsArray().length > 0) {
context.getMessage().delete().queue();
player.play(context.getMessageText(), context, false, false); player.play(context.getMessageText(), context, false, false);
} else { } else {
context.getChannel().sendMessage("No url provided").queue(); context.reply("No url provided");
} }
} }
@@ -35,12 +35,12 @@ public class SkipCommand extends AbstractMusicCommand {
@Override @Override
public String getUsage() { public String getUsage() {
return null; return "<number of track to skip (optional)>";
} }
@Override @Override
public String getDescription() { public String getDescription() {
return null; return "Used to skip to the next track in the queue (requires admin privileges)";
} }
@Override @Override
@@ -48,6 +48,12 @@ public class SkipCommand extends AbstractMusicCommand {
MusicManager player = MusicManager.get(context.getGuild()); MusicManager player = MusicManager.get(context.getGuild());
MusicUtils.checkIfConnected(player, context); MusicUtils.checkIfConnected(player, context);
player.skip(); if (context.getArgumentsAsArray().length > 0) {
int skips = Integer.parseInt(context.getArg(0));
player.skip(skips);
} else {
player.skip(1);
}
} }
} }
@@ -11,12 +11,9 @@ import netneutrality.command.AbstractMusicCommand;
import netneutrality.command.CommandContext; import netneutrality.command.CommandContext;
import netneutrality.music.MusicManager; import netneutrality.music.MusicManager;
import netneutrality.utils.MusicUtils; import netneutrality.utils.MusicUtils;
import netneutrality.utils.UserFunction;
public class StopCommand extends AbstractMusicCommand { public class StopCommand extends AbstractMusicCommand {
@Override
public boolean needAdmin() {
return true;
}
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
@@ -40,7 +37,7 @@ public class StopCommand extends AbstractMusicCommand {
@Override @Override
public String getDescription() { public String getDescription() {
return null; return "Stop the music player, deleting the queue in the process";
} }
@Override @Override
@@ -48,6 +45,13 @@ public class StopCommand extends AbstractMusicCommand {
MusicManager player = MusicManager.get(context.getGuild()); MusicManager player = MusicManager.get(context.getGuild());
MusicUtils.checkIfConnected(player, context); MusicUtils.checkIfConnected(player, context);
player.leave(); if (UserFunction.isAdmin(context.getGuildUser())) {
player.leave();
return;
}
if (context.getGuildUser().getId().equals(player.getRequesterOfCurrentTrack().getId())) {
player.leave();
}
} }
} }
@@ -32,7 +32,7 @@ public class ViewQueueCommand extends AbstractMusicCommand {
@Override @Override
public String[] getAlias() { public String[] getAlias() {
return new String[]{"viewq"}; return new String[]{"viewq", "queue"};
} }
@Override @Override
@@ -42,7 +42,7 @@ public class ViewQueueCommand extends AbstractMusicCommand {
@Override @Override
public String getDescription() { public String getDescription() {
return null; return "Get the current queue for the music player";
} }
@Override @Override
@@ -53,9 +53,9 @@ public class ViewQueueCommand extends AbstractMusicCommand {
int queueSize = player.getScheduler().getQueue().size(); int queueSize = player.getScheduler().getQueue().size();
if (queueSize > 0) { if (queueSize > 0) {
context.getChannel().sendMessageEmbeds(MessageFunction.buildViewQueueMessage(player, member)).queue(); context.reply(MessageFunction.buildViewQueueMessage(player, member));
} else { } else {
context.getChannel().sendMessage("Queue is empty!").queue(); context.reply("Queue is empty!");
} }
} }
} }
@@ -9,6 +9,7 @@ package netneutrality.command.music;
import netneutrality.command.AbstractMusicCommand; import netneutrality.command.AbstractMusicCommand;
import netneutrality.command.CommandContext; import netneutrality.command.CommandContext;
import netneutrality.command.CommandUsage;
import netneutrality.exceptions.CommandErrorException; import netneutrality.exceptions.CommandErrorException;
import netneutrality.music.MusicManager; import netneutrality.music.MusicManager;
import netneutrality.utils.MusicUtils; import netneutrality.utils.MusicUtils;
@@ -31,17 +32,17 @@ public class VolumeCommand extends AbstractMusicCommand {
@Override @Override
public String[] getAlias() { public String[] getAlias() {
return new String[0]; return new String[] { "vol" };
} }
@Override @Override
public String getUsage() { public String getUsage() {
return null; return "<integer between 0 and 100 (optional)>";
} }
@Override @Override
public String getDescription() { public String getDescription() {
return null; return "Sets the volume for the music player";
} }
@Override @Override
@@ -8,17 +8,19 @@ package netneutrality.command.music;
import netneutrality.command.AbstractMusicCommand; import netneutrality.command.AbstractMusicCommand;
import netneutrality.command.CommandContext; import netneutrality.command.CommandContext;
import netneutrality.music.MusicManager;
import netneutrality.utils.MusicUtils;
public class VoteSkipCommand extends AbstractMusicCommand { public class VoteSkipCommand extends AbstractMusicCommand {
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
return false; return true;
} }
@Override @Override
public String getCommand() { public String getCommand() {
return null; return "voteskip";
} }
@Override @Override
@@ -33,11 +35,14 @@ public class VoteSkipCommand extends AbstractMusicCommand {
@Override @Override
public String getDescription() { public String getDescription() {
return null; return "Cast a vote to skip the currently playing track";
} }
@Override @Override
public void execute(CommandContext context) { public void execute(CommandContext context) {
MusicManager manager = MusicManager.get(context.getGuild());
MusicUtils.checkIfConnected(manager, context);
manager.voteSkip(context);
} }
} }
@@ -71,7 +71,7 @@ public class DBController {
if (controllers.isEmpty()) if (controllers.isEmpty())
loadControllers(); loadControllers();
((ScheduledExecutorService) ThreadManager.getExecutor("db-keepalive", ThreadManager.ExecutorType.SCHEDULED, 5)).scheduleWithFixedDelay(() -> { ((ScheduledExecutorService) ThreadManager.getExecutor("db-keepalive", ThreadManager.ExecutorType.SCHEDULED)).scheduleWithFixedDelay(() -> {
DBController.get().query("SELECT 1;"); DBController.get().query("SELECT 1;");
}, 0, 120, TimeUnit.SECONDS); }, 0, 120, TimeUnit.SECONDS);
} }
@@ -26,6 +26,8 @@ public class DBBlacklist extends AbstractController {
} }
public void delete(String word, String serverId, Consumer<Boolean> success) { public void delete(String word, String serverId, Consumer<Boolean> success) {
isAlreadyBlacklisted(word, serverId, blacklisted -> { isAlreadyBlacklisted(word, serverId, blacklisted -> {
if (blacklisted >= 1) { if (blacklisted >= 1) {
@@ -119,18 +121,13 @@ public class DBBlacklist extends AbstractController {
return output; return output;
} }
@Deprecated
private boolean isAlreadyBlacklisted(String word, String serverId) { private boolean isAlreadyBlacklisted(String word, String serverId) {
AtomicBoolean result = new AtomicBoolean(); MTableModel table = database.prepareAndExecuteAsync("SELECT COUNT(*) FROM blacklist WHERE word = ? AND guild_id = ?", word, serverId).blocking();
if (!table.isEmpty()) {
return ((int) table.getRowModel(0).getItem(0)) != 0;
}
database.prepareAndExecuteAsync("SELECT COUNT(*) FROM blacklist WHERE word = ? AND guild_id = ?", word, serverId).async(table -> { return false;
if (!table.isEmpty()) {
result.set((int) table.getRowModel(0).getItem(0) != 0);
}
});
return result.get();
} }
private void isAlreadyBlacklisted(String word, String serverId, Consumer<Integer> result) { private void isAlreadyBlacklisted(String word, String serverId, Consumer<Integer> result) {
@@ -30,13 +30,13 @@ public abstract class AbstractEntity implements EntityInterface {
for (String pk : getPrimaryKeys()) { for (String pk : getPrimaryKeys()) {
whereClause.append(pk + " = ?"); whereClause.append(pk + " = ?");
//TODO: Fix last and
if (getPrimaryKeys().length > 1) if (getPrimaryKeys().length > 1)
whereClause.append(" AND "); whereClause.append(" AND ");
} }
String sql = "SELECT * FROM " + getTableName() + " WHERE " + whereClause; String sql = "SELECT * FROM " + getTableName() + " WHERE " + whereClause;
MusicBot.logger.debug(sql + " - pk: " + primaryKey); // MusicBot.logger.debug(sql + " - pk: " + primaryKey);
if (this.primaryKeys.length > 0) if (this.primaryKeys.length > 0)
this.table = DBController.get().prepareAndExecuteAsync(sql, this.primaryKey, this.primaryKeys).blocking(); this.table = DBController.get().prepareAndExecuteAsync(sql, this.primaryKey, this.primaryKeys).blocking();
@@ -32,14 +32,34 @@ public class MRowModel {
return rowIndex; return rowIndex;
} }
/**
* Add an item to the row. No checks are made to match the table itself
* @param obj Object to add to the row
*/
public void addColumnToRow(Object obj) { public void addColumnToRow(Object obj) {
row.add(obj); row.add(obj);
} }
/**
* Get a list of all objects in the row.
* @return List of objects
*/
public List<Object> getRow() { public List<Object> getRow() {
return row; return row;
} }
/**
* <pre>
* Get an item at the specified index.
*
* Example:
* SQL: SELECT first, second, third
* Index: 0 1 2
* </pre>
*
* @param index Index
* @return The object
*/
public Object getItem(int index) { public Object getItem(int index) {
try { try {
return row.get(index); return row.get(index);
@@ -67,6 +67,12 @@ public class MTableModel {
return null; return null;
} }
/**
* Represent a row in a table
*
* @param rowIndex The index of the row
* @return A row model
*/
public MRowModel getRowModel(int rowIndex) { public MRowModel getRowModel(int rowIndex) {
if (getNbRows() != 0) if (getNbRows() != 0)
return rows.getRowModel(rowIndex); return rows.getRowModel(rowIndex);
@@ -45,6 +45,6 @@ public class LogHandler {
private static void sendMessageToChannel(CommandErrorException error) { private static void sendMessageToChannel(CommandErrorException error) {
CommandContext context = error.getCommandContext(); CommandContext context = error.getCommandContext();
context.getChannel().sendMessage(error.getMessage()).queue(); context.reply(error.getMessage());
} }
} }
@@ -1,4 +1,4 @@
package netneutrality.handler; package netneutrality.handler.message;
/* /*
* Copyright (c) anthorob * Copyright (c) anthorob
@@ -25,10 +25,12 @@ public class EditHandler extends ListenerAdapter {
if (event.getAuthor().isBot()) return; if (event.getAuthor().isBot()) return;
MTableModel table = DBController.get().prepareAndExecuteAsync("SELECT channel_id FROM logging WHERE guild_id = ? AND enabled = 1", event.getGuild().getId()).blocking(); MTableModel table = DBController.get().prepareAndExecuteAsync("SELECT channel_id, guild_id FROM logging WHERE guild_id = ? AND enabled = 1", event.getGuild().getId()).blocking();
Object logChannel = table.getRowModel(0).getItem(0); Object logChannel = table.getRowModel(0).getItem(0);
if (logChannel != null) { Object guild = table.getRowModel(0).getItem(1);
if (logChannel != null && guild != null) {
String originalMessage = ((DBMessage) DBController.get("message")).getMessageString(event.getMessage().getId()); String originalMessage = ((DBMessage) DBController.get("message")).getMessageString(event.getMessage().getId());
((DBMessage) DBController.get("message")).logEdit(event.getMessage().getId(), event.getMessage().getContentDisplay()); ((DBMessage) DBController.get("message")).logEdit(event.getMessage().getId(), event.getMessage().getContentDisplay());
@@ -38,18 +40,19 @@ public class EditHandler extends ListenerAdapter {
try { try {
editEmbed.addField("Original message", (originalMessage == null ? "Not available" : originalMessage), false); editEmbed.addField("Original message", (originalMessage == null ? "Not available" : originalMessage), false);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
editEmbed.addField("Original message :", "Message too long. Check db for full message", false); editEmbed.addField("Original message:", "Message too long.", false);
} }
try { try {
editEmbed.addField("Edited to :", event.getMessage().getContentDisplay(), false); editEmbed.addField("Edited to:", event.getMessage().getContentDisplay(), false);
} catch (IllegalArgumentException ex) { } catch (IllegalArgumentException ex) {
editEmbed.addField("Edited to :", "Message too long. Check db for full message", false); editEmbed.addField("Edited to:", "Message too long.", false);
} }
editEmbed.setFooter("ID : " + event.getMessageId(), null); editEmbed.setFooter("ID : " + event.getMessageId(), null);
editEmbed.setTimestamp(event.getMessage().getTimeCreated()); editEmbed.setTimestamp(event.getMessage().getTimeCreated());
event.getJDA().getTextChannelById(logChannel.toString()).sendMessageEmbeds(editEmbed.build()).queue();
event.getJDA().getGuildById((String) guild).getTextChannelById((String) logChannel).sendMessageEmbeds(editEmbed.build()).queue();
} }
@@ -1,4 +1,4 @@
package netneutrality.handler; package netneutrality.handler.message;
/* /*
* Copyright (c) anthorob * Copyright (c) anthorob
* *
@@ -7,6 +7,7 @@ package netneutrality.handler;
*/ */
import net.dv8tion.jda.api.entities.*; import net.dv8tion.jda.api.entities.*;
import net.dv8tion.jda.api.events.guild.voice.GuildVoiceLeaveEvent;
import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent; import net.dv8tion.jda.api.events.message.guild.GuildMessageReceivedEvent;
import net.dv8tion.jda.api.hooks.ListenerAdapter; import net.dv8tion.jda.api.hooks.ListenerAdapter;
import netneutrality.GuildManager; import netneutrality.GuildManager;
@@ -51,5 +52,4 @@ public class MessageReceivedHandler extends ListenerAdapter {
GuildManager.getManager(guild).checkBlacklist(message, member, channel); GuildManager.getManager(guild).checkBlacklist(message, member, channel);
} }
} }
} }
@@ -6,13 +6,13 @@ package netneutrality.http.server;/*
*/ */
import com.sun.net.httpserver.HttpExchange; import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler; import netneutrality.GuildManager;
import netneutrality.MusicBot; import netneutrality.MusicBot;
import netneutrality.http.WebModule; import netneutrality.http.WebModule;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.json.JSONObject;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.util.Map; import java.util.Map;
import java.util.TreeMap; import java.util.TreeMap;
@@ -25,6 +25,8 @@ public class ActionHandler extends AbstractHandler {
if (path.equals("/actions/shutdown")) { if (path.equals("/actions/shutdown")) {
LogManager.getLogger(MusicBot.class).info("Request to shutdown received"); LogManager.getLogger(MusicBot.class).info("Request to shutdown received");
sendStringResponse(exchange, "Shutting down.."); sendStringResponse(exchange, "Shutting down..");
MusicBot.shutdown();
System.exit(0); System.exit(0);
return; return;
} }
@@ -38,6 +40,37 @@ public class ActionHandler extends AbstractHandler {
return; return;
} }
if (path.startsWith("/actions/settings/current/")) {
JSONObject json = new JSONObject();
String[] args = exchange.getRequestURI().toString().split("/");
String guild = args[args.length - 1];
GuildManager manager = GuildManager.getManager(MusicBot.getJDA().getGuildById(guild));
if (manager != null) {
Map<String, String> settings = manager.getSettings();
JSONObject setting = new JSONObject(settings);
json.put("status", "ok");
json.put("settings", setting);
} else {
json.put("status", "error");
}
sendJsonResponse(exchange, json.toString());
return;
}
if (path.startsWith("/actions/settings/reload/")) {
TreeMap<String, String> json = new TreeMap<>();
String[] args = exchange.getRequestURI().toString().split("/");
String guild = args[args.length - 1];
json.put("status", String.valueOf(GuildManager.reloadSettingsForGuild(MusicBot.getJDA().getGuildById(guild))));
sendJsonResponse(exchange, WebModule.createJson(json));
return;
}
sendStringResponse(exchange, path); sendStringResponse(exchange, path);
} }
@@ -14,6 +14,8 @@ import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Member;
import netneutrality.MusicBot; import netneutrality.MusicBot;
import netneutrality.http.WebModule; import netneutrality.http.WebModule;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
@@ -28,15 +30,67 @@ public class GuildHandler extends AbstractHandler {
if (path.equals("/guilds/owner")) { if (path.equals("/guilds/owner")) {
TreeMap<String, String> owners = new TreeMap<>(); JSONArray array = new JSONArray();
JDA jda = MusicBot.getJDA(); JDA jda = MusicBot.getJDA();
jda.getGuilds().forEach(guild -> { jda.getGuilds().forEach(guild -> {
owners.put(guild.getId(), guild.getOwnerId()); JSONObject obj = new JSONObject();
obj.put("guild_id", guild.getId());
obj.put("owner_id", guild.getOwnerId());
array.put(obj);
}); });
sendJsonResponse(exchange, WebModule.createJsonArray(owners)); sendJsonResponse(exchange, array.toString());
return;
}
if (path.startsWith("/guilds/channels/")) {
String[] args = exchange.getRequestURI().toString().split("/");
String guild = args[args.length - 1];
JSONArray array = new JSONArray();
JDA jda = MusicBot.getJDA();
if (jda.getGuildById(guild) == null) {
sendStringResponse(exchange, path);
return;
}
jda.getGuildById(guild).getTextChannels().forEach(channel -> {
JSONObject obj = new JSONObject();
obj.put("channel_id", channel.getId());
obj.put("channel_name", channel.getName());
array.put(obj);
});
sendJsonResponse(exchange, array.toString());
return;
}
if (path.startsWith("/guilds/roles/")) {
String[] args = exchange.getRequestURI().toString().split("/");
String guild = args[args.length - 1];
JSONArray array = new JSONArray();
JDA jda = MusicBot.getJDA();
if (jda.getGuildById(guild) == null) {
sendStringResponse(exchange, path);
return;
}
jda.getGuildById(guild).getRoles().forEach(role -> {
JSONObject obj = new JSONObject();
obj.put("channel_id", role.getId());
obj.put("channel_name", role.getName());
array.put(obj);
});
sendJsonResponse(exchange, array.toString());
return; return;
} }
@@ -8,7 +8,7 @@ package netneutrality.http.server;
*/ */
import com.sun.net.httpserver.HttpServer; import com.sun.net.httpserver.HttpServer;
import netneutrality.thread.BaseThread; import netneutrality.thread.BaseThreadFactory;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -21,7 +21,7 @@ import java.util.concurrent.Executors;
public class WebApi { public class WebApi {
private final static Logger logger = LoggerFactory.getLogger(WebApi.class); private final static Logger logger = LoggerFactory.getLogger(WebApi.class);
private final ExecutorService executor = Executors.newFixedThreadPool(10, new BaseThread("web-api")); private final ExecutorService executor = Executors.newFixedThreadPool(10, new BaseThreadFactory("web-api"));
private final int port = 9000; private final int port = 9000;
private HttpServer server; private HttpServer server;
@@ -24,11 +24,16 @@ public class LoadResultHandler implements AudioLoadResultHandler {
private final boolean silent; private final boolean silent;
public LoadResultHandler(String identifier, TextChannel channel, Member user, TrackScheduler scheduler, boolean silent) { public LoadResultHandler(String identifier, TextChannel channel, Member user, TrackScheduler scheduler, boolean silent) {
if (user == null)
silent = true;
this.identifier = identifier; this.identifier = identifier;
this.channel = channel; this.channel = channel;
this.user = user; this.user = user;
this.scheduler = scheduler; this.scheduler = scheduler;
this.silent = silent; this.silent = silent;
} }
@Override @Override
@@ -37,7 +42,7 @@ public class LoadResultHandler implements AudioLoadResultHandler {
channel.sendMessageEmbeds(MessageFunction.buildAddingTrackMessage(audioTrack, user, identifier)).queue(); channel.sendMessageEmbeds(MessageFunction.buildAddingTrackMessage(audioTrack, user, identifier)).queue();
} }
scheduler.queue(audioTrack); scheduler.queue(audioTrack, user);
} }
@Override @Override
@@ -46,12 +51,15 @@ public class LoadResultHandler implements AudioLoadResultHandler {
if (identifier.startsWith("ytsearch:")) { if (identifier.startsWith("ytsearch:")) {
AudioTrack track = audioPlaylist.getTracks().get(0); AudioTrack track = audioPlaylist.getTracks().get(0);
channel.sendMessageEmbeds(MessageFunction.buildAddingTrackMessage(track, user, String.format("https://youtu.be/%s", track.getIdentifier()))).queue(); if (!silent)
scheduler.queue(track); channel.sendMessageEmbeds(MessageFunction.buildAddingTrackMessage(track, user, String.format("https://youtu.be/%s", track.getIdentifier()))).queue();
scheduler.queue(track, user);
} else { } else {
channel.sendMessageEmbeds(MessageFunction.buildAddingPlaylistMessage(audioPlaylist, user, identifier)).queue(); if (!silent)
channel.sendMessageEmbeds(MessageFunction.buildAddingPlaylistMessage(audioPlaylist, user, identifier)).queue();
for (AudioTrack track : audioPlaylist.getTracks()) { for (AudioTrack track : audioPlaylist.getTracks()) {
scheduler.queue(track); scheduler.queue(track, user);
} }
} }
} }
@@ -23,8 +23,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.BlockingQueue; import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@@ -32,8 +33,9 @@ import java.util.concurrent.TimeUnit;
public class MusicManager { public class MusicManager {
private final static Logger logger = LoggerFactory.getLogger(MusicManager.class); private final static Logger logger = LoggerFactory.getLogger(MusicManager.class);
private final Guild guild;
private final static AudioPlayerManager playerManager = new DefaultAudioPlayerManager(); private final static AudioPlayerManager playerManager = new DefaultAudioPlayerManager();
private final Guild guild;
private final AudioPlayer player; private final AudioPlayer player;
private final TrackScheduler scheduler; private final TrackScheduler scheduler;
private final static Map<String, MusicManager> managers = new ConcurrentHashMap<>(); private final static Map<String, MusicManager> managers = new ConcurrentHashMap<>();
@@ -47,9 +49,6 @@ public class MusicManager {
player.addListener(scheduler); player.addListener(scheduler);
player.setVolume(15); player.setVolume(15);
AudioSourceManagers.registerRemoteSources(playerManager);
AudioSourceManagers.registerLocalSource(playerManager);
managers.put(guild.getId(), this); managers.put(guild.getId(), this);
} }
@@ -61,6 +60,10 @@ public class MusicManager {
} }
} }
public static void registerSources() {
AudioSourceManagers.registerRemoteSources(playerManager);
}
public boolean isConnectedTo(VoiceChannel vChannel) { public boolean isConnectedTo(VoiceChannel vChannel) {
return vChannel != null && vChannel.equals(guild.getAudioManager().getConnectedChannel()); return vChannel != null && vChannel.equals(guild.getAudioManager().getConnectedChannel());
} }
@@ -69,7 +72,7 @@ public class MusicManager {
if (vChannel != null && !isConnectedTo(vChannel)) { if (vChannel != null && !isConnectedTo(vChannel)) {
guild.getAudioManager().openAudioConnection(vChannel); guild.getAudioManager().openAudioConnection(vChannel);
((ScheduledExecutorService) ThreadManager.getExecutor("music", ThreadManager.ExecutorType.SCHEDULED, 5)).scheduleWithFixedDelay(() -> { ((ScheduledExecutorService) ThreadManager.getExecutor("music", ThreadManager.ExecutorType.SCHEDULED)).scheduleWithFixedDelay(() -> {
if ((getCurrentlyPlaying() == null && scheduler.getQueue().isEmpty()) || vChannel.getMembers().size() == 0) { if ((getCurrentlyPlaying() == null && scheduler.getQueue().isEmpty()) || vChannel.getMembers().size() == 0) {
leave(); leave();
} }
@@ -84,18 +87,23 @@ public class MusicManager {
return guild != null && guild.getAudioManager().getConnectedChannel() != null; return guild != null && guild.getAudioManager().getConnectedChannel() != null;
} }
public synchronized AudioTrack getCurrentlyPlaying() { public AudioTrack getCurrentlyPlaying() {
return player.getPlayingTrack(); return player.getPlayingTrack();
} }
public synchronized void leave() { public Member getRequesterOfCurrentTrack() {
TrackScheduler.TrackInfo info = scheduler.getQueue().peek();
return info.getRequester();
}
public void leave() {
if (isConnected()) { if (isConnected()) {
player.stopTrack(); player.stopTrack();
guild.getAudioManager().closeAudioConnection(); guild.getAudioManager().closeAudioConnection();
} }
scheduler.resetTopic(); scheduler.resetTopic();
System.out.println(scheduler.getQueue().size());
} }
public static void disconnectPlayers() { public static void disconnectPlayers() {
@@ -106,6 +114,12 @@ public class MusicManager {
} }
} }
public boolean restoreQueue(List<String> track) {
return false;
}
public void play(String url, CommandContext context, boolean needToBeConnected, boolean silent) { public void play(String url, CommandContext context, boolean needToBeConnected, boolean silent) {
Member member = context.getGuildUser(); Member member = context.getGuildUser();
TextChannel channel = context.getChannel(); TextChannel channel = context.getChannel();
@@ -139,15 +153,49 @@ public class MusicManager {
} }
} }
public synchronized void skip() { public void skip(int nbTrack) {
if (!scheduler.getQueue().isEmpty()) { if (!scheduler.getQueue().isEmpty()) {
scheduler.nextTrack(); if (nbTrack >= scheduler.getQueue().size()) {
nbTrack = 1;
}
scheduler.skipAndPlay(nbTrack);
} else { } else {
leave(); leave();
} }
} }
public synchronized void setVolume(int volume) { public void voteSkip(CommandContext context) {
Queue<TrackScheduler.TrackInfo> queue = scheduler.getQueue();
VoiceChannel voiceChannel = guild.getAudioManager().getConnectedChannel();
if (getCurrentlyPlaying() == null) {
return;
}
// Minus the bot itself
int connected = voiceChannel.getMembers().size() - 1;
if (connected > 2) {
TrackScheduler.TrackInfo info = queue.peek();
double needed = connected / 2.1;
if (info != null) {
info.toggleSkip(context.getGuildUser());
if (info.getSkips().size() > needed) {
skip(1);
} else {
context.reply(String.format("Current skips: %d/%f", info.getSkips().size(), Math.ceil(needed)));
}
}
} else {
skip(1);
}
}
public void setVolume(int volume) {
player.setVolume(volume); player.setVolume(volume);
} }
@@ -155,14 +203,11 @@ public class MusicManager {
return scheduler; return scheduler;
} }
public ArrayList<String> getAllQueuedTrack() { public ArrayList<AudioTrack> getAllQueuedTrack() {
ArrayList<String> queuedTracks = new ArrayList<>(); ArrayList<AudioTrack> tracks = new ArrayList<>();
BlockingQueue<AudioTrack> queue = scheduler.getQueue(); Queue<TrackScheduler.TrackInfo> queue = scheduler.getQueue();
queue.forEach((a) -> { queue.forEach(trackInfo -> tracks.add(trackInfo.getTrack()));
queuedTracks.add(a.getInfo().title); return tracks;
});
return queuedTracks;
} }
} }
@@ -12,68 +12,131 @@ import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack; import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason; import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.TextChannel; import net.dv8tion.jda.api.entities.TextChannel;
import java.util.concurrent.BlockingQueue; import java.util.*;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
public class TrackScheduler extends AudioEventAdapter { public class TrackScheduler extends AudioEventAdapter {
class TrackInfo {
private final AudioTrack track;
private final Member member;
private final Set<String> skips;
public TrackInfo(AudioTrack track, Member member) {
this.track = track;
this.member = member;
this.skips = new HashSet<>();
}
public AudioTrack getTrack() {
return track;
}
public Set<String> getSkips() {
return skips;
}
public void toggleSkip(Member member) {
if (!hasSkip(member))
skips.add(member.getId());
else
skips.remove(member.getId());
}
public boolean hasSkip(Member member) {
return skips.contains(member.getId());
}
public Member getRequester() {
return member;
}
}
private final Object topicLock = new Object();
private final AudioPlayer player; private final AudioPlayer player;
private final Queue<TrackInfo> queue;
private TextChannel channel; private TextChannel channel;
private final BlockingQueue<AudioTrack> queue; private String originalTopic;
public String topic;
public TrackScheduler(AudioPlayer player) { public TrackScheduler(AudioPlayer player) {
this.player = player; this.player = player;
this.queue = new LinkedBlockingQueue<>(); this.queue = new LinkedBlockingQueue<>();
} }
public void queue(AudioTrack track) { public void queue(AudioTrack track, Member member) {
// Calling startTrack with the noInterrupt set to true will start the track only if nothing is currently playing. If // Calling startTrack with the noInterrupt set to true will start the track only if nothing is currently playing. If
// something is playing, it returns false and does nothing. In that case the player was already playing so this // something is playing, it returns false and does nothing. In that case the player was already playing so this
// track goes to the queue instead. // track goes to the queue instead.
boolean start = player.startTrack(track, true); queue.offer(new TrackInfo(track, member));
if (!start) { if (player.getPlayingTrack() == null) {
queue.offer(track); player.startTrack(track, true);
} }
} }
public void setTextChannel(TextChannel channel) { public String getOriginalTopic() {
this.channel = channel; synchronized (topicLock) {
return originalTopic;
if (topic == null) {
topic = channel.getTopic();
} }
} }
public TextChannel getTextChannel() { public TextChannel getTextChannel() {
return channel; return channel;
} }
public BlockingQueue<AudioTrack> getQueue() { public void setTextChannel(TextChannel channel) {
this.channel = channel;
synchronized (topicLock) {
if (originalTopic == null) {
originalTopic = channel.getTopic();
}
}
}
public void resetTextChannel() {
this.channel = null;
}
public Queue<TrackInfo> getQueue() {
return queue; return queue;
} }
public void resetTopic() { public void resetTopic() {
if (channel != null && (topic != null && !topic.equals(""))) { synchronized (topicLock) {
//channel.getManager().setTopic(topic).queue(); if (channel != null && (originalTopic != null && !originalTopic.equals(""))) {
//channel.getManager().setTopic(topic).queue();
}
originalTopic = null;
} }
topic = "";
} }
/** public void nextTrack(AudioPlayer player) {
* Start the next track, stopping the current one if it is playing. TrackInfo last = queue.poll();
*/
public void nextTrack() { if (queue.peek() != null) {
// Start the next track, regardless of if something is already playing or not. In case queue was empty, we are player.startTrack(queue.peek().getTrack(), false);
// giving null to startTrack, which is a valid argument and will simply stop the player. }
AudioTrack track = queue.poll(); }
if (track != null) {
player.startTrack(track, false); public void skipAndPlay(int nb) {
if (nb == 0)
return;
for (int i = 0; i < nb; i++) {
TrackInfo last = queue.poll();
}
TrackInfo current = queue.peek();
if (current != null) {
player.startTrack(current.getTrack(), false);
} }
} }
@@ -99,7 +162,9 @@ public class TrackScheduler extends AudioEventAdapter {
resetTopic(); resetTopic();
if (endReason.mayStartNext) { if (endReason.mayStartNext) {
nextTrack(); nextTrack(player);
} else {
queue.poll();
} }
// endReason == FINISHED: A track finished or died by an exception (mayStartNext = true). // endReason == FINISHED: A track finished or died by an exception (mayStartNext = true).
@@ -119,6 +184,6 @@ public class TrackScheduler extends AudioEventAdapter {
@Override @Override
public void onTrackStuck(AudioPlayer player, AudioTrack track, long thresholdMs) { public void onTrackStuck(AudioPlayer player, AudioTrack track, long thresholdMs) {
resetTopic(); resetTopic();
nextTrack(); nextTrack(player);
} }
} }
@@ -11,12 +11,12 @@ import org.jetbrains.annotations.NotNull;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
public class BaseThread implements ThreadFactory { public class BaseThreadFactory implements ThreadFactory {
private final AtomicLong threadId = new AtomicLong(1); private final AtomicLong threadId = new AtomicLong(1);
private final String name; private final String name;
public BaseThread(String name) { public BaseThreadFactory(String name) {
this.name = name; this.name = name;
} }
@@ -0,0 +1,28 @@
package netneutrality.thread;/*
* Copyright (c) anthorob
*
* https://xtremcommunity.com/
* https://discord.gg/CdFcyQ
*/
import netneutrality.MusicBot;
import netneutrality.utils.UnsafeShutdown;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.core.LoggerContext;
import org.apache.logging.log4j.core.config.Configurator;
public class ShutdownThread implements Runnable {
@Override
public void run() {
if (LogManager.getContext() instanceof LoggerContext) {
System.out.println("Shutting down log4j2");
Configurator.shutdown((LoggerContext) LogManager.getContext());
}
MusicBot.shutdown();
if (!UnsafeShutdown.SAFE) {
//UnsafeShutdown.saveState();
}
}
}
@@ -17,7 +17,7 @@ public class ChannelFunction {
if (toDelete >= 2500) return; if (toDelete >= 2500) return;
ThreadManager.getExecutor("bulk-delete", ThreadManager.ExecutorType.FIXED, 5).submit(() -> { ThreadManager.getExecutor("bulk-delete", ThreadManager.ExecutorType.FIXED).submit(() -> {
int toDeleteFinal = toDelete + 1; int toDeleteFinal = toDelete + 1;
int iteration = toDeleteFinal / 100; int iteration = toDeleteFinal / 100;
@@ -69,6 +69,7 @@ public class MessageFunction {
message.setTitle("Currently playing:"); message.setTitle("Currently playing:");
message.setColor(Color.BLUE); message.setColor(Color.BLUE);
message.addField("Track title: ", current.getInfo().title, false); message.addField("Track title: ", current.getInfo().title, false);
message.addField("Uploaded by: ", current.getInfo().author, false);
message.addField("Duration: ", DurationFormatUtils.formatDuration(current.getPosition(), "**HH:mm:ss**", true) + " / " + DurationFormatUtils.formatDuration(current.getDuration(), "**HH:mm:ss**", true), false); message.addField("Duration: ", DurationFormatUtils.formatDuration(current.getPosition(), "**HH:mm:ss**", true) + " / " + DurationFormatUtils.formatDuration(current.getDuration(), "**HH:mm:ss**", true), false);
message.setFooter("Requested by " + user.getUser().getName(), user.getUser().getAvatarUrl()); message.setFooter("Requested by " + user.getUser().getName(), user.getUser().getAvatarUrl());
message.setTimestamp(OffsetDateTime.now()); message.setTimestamp(OffsetDateTime.now());
@@ -83,6 +84,7 @@ public class MessageFunction {
message.setTitle("Adding track to queue.."); message.setTitle("Adding track to queue..");
message.setColor(Color.BLUE); message.setColor(Color.BLUE);
message.addField("Track title: ", track.getInfo().title, false); message.addField("Track title: ", track.getInfo().title, false);
message.addField("Uploaded by: ", track.getInfo().author, false);
message.addField("Duration: ", DurationFormatUtils.formatDuration(track.getDuration(), "**HH:mm:ss**", true), false); message.addField("Duration: ", DurationFormatUtils.formatDuration(track.getDuration(), "**HH:mm:ss**", true), false);
message.setFooter("Requested by " + user.getUser().getName(), user.getUser().getAvatarUrl()); message.setFooter("Requested by " + user.getUser().getName(), user.getUser().getAvatarUrl());
message.setTimestamp(OffsetDateTime.now()); message.setTimestamp(OffsetDateTime.now());
@@ -105,17 +107,20 @@ public class MessageFunction {
} }
public static MessageEmbed buildViewQueueMessage(MusicManager manager, Member user) { public static MessageEmbed buildViewQueueMessage(MusicManager manager, Member user) {
ArrayList<String> tracks = manager.getAllQueuedTrack(); ArrayList<AudioTrack> tracks = manager.getAllQueuedTrack();
AudioTrack current = manager.getCurrentlyPlaying(); AudioTrack current = manager.getCurrentlyPlaying();
EmbedBuilder message = new EmbedBuilder(); EmbedBuilder message = new EmbedBuilder();
message.setTitle("Current queue (showing max 10 items):"); message.setTitle("Current queue (showing max 10 items):");
message.setColor(Color.BLUE); message.setColor(Color.BLUE);
message.addField("Track #1 (Currently playing)", current.getInfo().title, false);
int index = 1; int index = 0;
for (String track: tracks) { for (AudioTrack track: tracks) {
message.addField("Track #" + ++index, track, false); if (track.getInfo().title.equals(current.getInfo().title)) {
message.addField("Track #" + ++index + " (Currently playing)", track.getInfo().title, false);
} else {
message.addField("Track #" + ++index, track.getInfo().title, false);
}
if (index == 10) break; if (index == 10) break;
} }
@@ -136,8 +141,9 @@ public class MessageFunction {
if (command.getType() == null) if (command.getType() == null)
continue; continue;
String usage = "None"; String usage = "(no arguments required)";
String description = "No description yet"; String description = "No description yet";
StringBuilder availAlias = new StringBuilder();
if (command.isEnabled() && command.getType() != CommandType.UNLISTED) { if (command.isEnabled() && command.getType() != CommandType.UNLISTED) {
if (command.getType() == CommandType.ADMINISTRATION && !admin) if (command.getType() == CommandType.ADMINISTRATION && !admin)
@@ -149,6 +155,18 @@ public class MessageFunction {
if (command.getDescription() != null) if (command.getDescription() != null)
description = command.getDescription(); description = command.getDescription();
if (command.getAlias() != null && command.getAlias().length > 0) {
availAlias.append("(");
for (String str : command.getAlias()) {
availAlias.append(str).append(",");
}
if (!availAlias.isEmpty()) {
availAlias.deleteCharAt(availAlias.lastIndexOf(","));
availAlias.append(")");
}
}
EmbedBuilder message; EmbedBuilder message;
if (help.containsKey(command.getType())) { if (help.containsKey(command.getType())) {
message = help.get(command.getType()); message = help.get(command.getType());
@@ -161,7 +179,7 @@ public class MessageFunction {
message.setColor(Color.GREEN); message.setColor(Color.GREEN);
} }
message.addField(Settings.getInstance().get("BOT_PREFIX") + command.getCommand() + " - Arguments: " + usage, description, false); message.addField(String.format("%s%s %s %s", Settings.getInstance().get("BOT_PREFIX"), command.getCommand(), availAlias, usage), description, false);
} }
} }
@@ -0,0 +1,62 @@
package netneutrality.utils;/*
* Copyright (c) anthorob
*
* https://xtremcommunity.com/
* https://discord.gg/CdFcyQ
*/
import netneutrality.GuildManager;
import netneutrality.music.MusicManager;
import netneutrality.music.TrackScheduler;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Map;
public class UnsafeShutdown {
public static boolean SAFE = false;
//TODO: finish
private static void saveGuildsState() {
Map<String, GuildManager> managers = GuildManager.getManagers();
for (GuildManager manager : managers.values()) {
File state = new File("state.json");
try {
if (!state.exists()) {
state.createNewFile();
}
FileWriter writer = new FileWriter(state);
JSONArray array = new JSONArray();
MusicManager musicManager = MusicManager.get(manager.getGuild());
TrackScheduler trackScheduler = musicManager.getScheduler();
JSONObject music = new JSONObject();
music.put("current_channel", "");
music.put("current_topic", "");
music.put("current_topic", "");
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void saveState() {
saveGuildsState();
}
}