0

Compare commits

...

10 Commits

27 changed files with 497 additions and 119 deletions

View File

@@ -31,7 +31,7 @@ import net.dv8tion.jda.core.AccountType;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.JDABuilder;
public final class Main {
public final class Main implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
private static final String token = System.getenv("TOKEN");
@@ -42,6 +42,11 @@ public final class Main {
}
public static final void main(final String[] arguments) {
MainThread.scheduleImmediately(new Main());
}
@Override
public final void run() {
logger.info("Beginning setup of LogalBot...");
logger.info("Verifying connection to Redis...");
@@ -89,6 +94,11 @@ public final class Main {
CommandManager.registerCommand("help", new Help(), false);
// Fun Commands
CommandManager.registerCommand("dice", new Dice(), false);
CommandManager.registerCommandAlias("die", "dice");
CommandManager.registerCommandAlias("random", "dice");
CommandManager.registerCommandAlias("rng", "dice");
CommandManager.registerCommandAlias("roll", "dice");
CommandManager.registerCommand("8ball", new EightBall(), false);
// Audio Commands

View File

@@ -0,0 +1,61 @@
package dev.logal.logalbot;
// Copyright 2019 Logan Fick
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import net.dv8tion.jda.core.utils.Checks;
public final class MainThread {
private static final ScheduledExecutorService mainThread = Executors
.newSingleThreadScheduledExecutor(new MainThreadFactory());
public static final void scheduleImmediately(final Runnable task) {
Checks.notNull(task, "Task");
mainThread.schedule(task, 0, TimeUnit.MICROSECONDS);
}
public static final ScheduledFuture<?> scheduleLater(final Runnable task, final long delay, final TimeUnit unit) {
Checks.notNull(task, "Task");
Checks.notNegative(delay, "Delay");
Checks.notNull(unit, "Unit");
return mainThread.schedule(task, delay, unit);
}
public static final ScheduledFuture<?> scheduleRepeatedlyAtFixedRate(final Runnable task, final long initialDelay,
final long period, final TimeUnit unit) {
Checks.notNull(task, "Task");
Checks.notNegative(initialDelay, "Initial Delay");
Checks.notNegative(period, "Period");
Checks.notNull(unit, "Unit");
return mainThread.scheduleAtFixedRate(task, initialDelay, period, unit);
}
public static final ScheduledFuture<?> scheduleRepeatedlyWithFixedDelay(final Runnable task,
final long initialDelay, final long period, final TimeUnit unit) {
Checks.notNull(task, "Task");
Checks.notNegative(initialDelay, "Initial Delay");
Checks.notNegative(period, "Period");
Checks.notNull(unit, "Unit");
return mainThread.scheduleWithFixedDelay(task, initialDelay, period, unit);
}
}

View File

@@ -0,0 +1,30 @@
package dev.logal.logalbot;
// Copyright 2019 Logan Fick
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import java.util.concurrent.ThreadFactory;
import net.dv8tion.jda.core.utils.Checks;
public final class MainThreadFactory implements ThreadFactory {
@Override
public final Thread newThread(final Runnable runnable) {
Checks.notNull(runnable, "Runnable");
final Thread newThread = new Thread(runnable);
newThread.setName("Main Thread");
return newThread;
}
}

View File

@@ -26,7 +26,9 @@ import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dev.logal.logalbot.MainThread;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.tasks.TrackAdditionTask;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.PermissionManager;
import dev.logal.logalbot.utils.TrackUtil;
@@ -54,7 +56,8 @@ public final class TrackLoadHandler implements AudioLoadResultHandler {
Checks.notNull(track, "Track");
final CommandResponse response;
final TrackScheduler scheduler = AudioUtil.getTrackScheduler(this.channel.getGuild());
final Guild guild = this.channel.getGuild();
final TrackScheduler scheduler = AudioUtil.getTrackScheduler(guild);
final AudioTrackInfo info = track.getInfo();
if (info.isStream) {
@@ -83,7 +86,7 @@ public final class TrackLoadHandler implements AudioLoadResultHandler {
}
final RequestedTrack requestedTrack = new RequestedTrack(track, requester);
scheduler.addToQueue(requestedTrack);
MainThread.scheduleImmediately(new TrackAdditionTask(guild, requestedTrack));
response = new CommandResponse("notes",
this.requester.getAsMention() + " added the following track to the queue:");
response.attachEmbed(TrackUtil.trackInfoEmbed(requestedTrack));
@@ -95,7 +98,8 @@ public final class TrackLoadHandler implements AudioLoadResultHandler {
Checks.notNull(playlist, "Playlist");
CommandResponse response;
final TrackScheduler scheduler = AudioUtil.getTrackScheduler(this.channel.getGuild());
final Guild guild = this.channel.getGuild();
final TrackScheduler scheduler = AudioUtil.getTrackScheduler(guild);
final AudioTrack selectedTrack = playlist.getSelectedTrack();
final AudioTrack track;
@@ -127,7 +131,7 @@ public final class TrackLoadHandler implements AudioLoadResultHandler {
if ((info.length >= 60000 && info.length <= 900000) || PermissionManager.isWhitelisted(this.requester)) {
final RequestedTrack requestedTrack = new RequestedTrack(track, requester);
scheduler.addToQueue(requestedTrack);
MainThread.scheduleImmediately(new TrackAdditionTask(guild, requestedTrack));
response = new CommandResponse("notes",
this.requester.getAsMention() + " added the following track to the queue:");
response.attachEmbed(TrackUtil.trackInfoEmbed(requestedTrack));
@@ -141,20 +145,19 @@ public final class TrackLoadHandler implements AudioLoadResultHandler {
}
} else {
if (PermissionManager.isWhitelisted(this.requester)) {
final LinkedList<RequestedTrack> addedTracks = new LinkedList<>();
final LinkedList<RequestedTrack> tracksToAdd = new LinkedList<>();
int count = 0;
for (final AudioTrack playlistTrack : playlist.getTracks()) {
if (!scheduler.isQueueFull()) {
if (count++ != 250) {
if (!scheduler.isQueued(playlistTrack) && !playlistTrack.getInfo().isStream) {
final RequestedTrack requestedTrack = new RequestedTrack(track, requester);
scheduler.addToQueue(requestedTrack);
addedTracks.add(requestedTrack);
tracksToAdd.add(new RequestedTrack(playlistTrack, requester));
}
} else {
break;
}
}
if (addedTracks.size() == 0) {
if (tracksToAdd.size() == 0) {
response = new CommandResponse("x",
"Sorry " + this.requester.getAsMention()
+ ", but none of the tracks in that playlist could be added to the queue.")
@@ -162,9 +165,10 @@ public final class TrackLoadHandler implements AudioLoadResultHandler {
response.sendResponse(this.channel);
}
MainThread.scheduleImmediately(new TrackAdditionTask(guild, tracksToAdd));
response = new CommandResponse("notes",
this.requester.getAsMention() + " added the following tracks from a playlist to the queue:");
response.attachEmbed(TrackUtil.trackListInfoEmbed(addedTracks, false));
response.attachEmbed(TrackUtil.trackListInfoEmbed(tracksToAdd, false));
response.sendResponse(this.channel);
} else {
response = new CommandResponse("x",

View File

@@ -27,11 +27,13 @@ import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dev.logal.logalbot.tasks.IdleDisconnectTask;
import dev.logal.logalbot.MainThread;
import dev.logal.logalbot.tasks.CloseAudioConnectionTask;
import dev.logal.logalbot.tasks.OpenAudioConnectionTask;
import dev.logal.logalbot.tasks.PlayNextTrackTask;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.DataManager;
import dev.logal.logalbot.utils.PermissionManager;
import dev.logal.logalbot.utils.Scheduler;
import dev.logal.logalbot.utils.SkipTracker;
import dev.logal.logalbot.utils.VoiceChannelUtil;
import net.dv8tion.jda.core.entities.Guild;
@@ -46,7 +48,7 @@ public final class TrackScheduler extends AudioEventAdapter {
private final Guild guild;
private final LinkedList<RequestedTrack> queue = new LinkedList<>();
private boolean queueLocked = false;
private ScheduledFuture<?> idleLogoutTask;
private ScheduledFuture<?> closeAudioConnectionTask;
public TrackScheduler(final Guild guild) {
Checks.notNull(guild, "Guild");
@@ -71,16 +73,21 @@ public final class TrackScheduler extends AudioEventAdapter {
logger.info(user.getName() + " (" + user.getId() + ") added '" + track.getInfo().title + "' to the queue in "
+ this.guild.getName() + " (" + this.guild.getId() + ").");
if (!AudioUtil.isTrackLoaded(this.guild)) {
AudioUtil.openAudioConnection(VoiceChannelUtil.getVoiceChannelMemberIsConnectedTo(requester));
AudioUtil.playTrack(this.guild, requestedTrack);
} else {
this.queue.add(requestedTrack);
final VoiceChannel targetChannel = VoiceChannelUtil.getVoiceChannelMemberIsConnectedTo(requester);
if (!AudioUtil.isAudioConnectionOpen(this.guild)
|| targetChannel != AudioUtil.getVoiceChannelConnectedTo(this.guild)) {
MainThread.scheduleImmediately(
new OpenAudioConnectionTask(VoiceChannelUtil.getVoiceChannelMemberIsConnectedTo(requester)));
}
if (!AudioUtil.isTrackLoaded(this.guild) && this.queue.size() == 0) {
MainThread.scheduleImmediately(new PlayNextTrackTask(this.guild));
}
this.queue.add(requestedTrack);
}
public final void removeFromQueue(final int index) {
Checks.notNull(index, "Index");
Checks.notNegative(index, "Index");
logger.info("Track '" + queue.remove(index).getTrack().getInfo().title + "' has been removed from the queue in "
+ this.guild.getName() + " (" + this.guild.getId() + ").");
@@ -110,8 +117,6 @@ public final class TrackScheduler extends AudioEventAdapter {
}
public final void setQueueLocked(final boolean locked) {
Checks.notNull(locked, "Locked");
this.queueLocked = locked;
}
@@ -123,7 +128,13 @@ public final class TrackScheduler extends AudioEventAdapter {
Collections.shuffle(this.queue);
}
public final RequestedTrack popFromQueue() {
return this.queue.remove(0);
}
public final int occupiedSlotCount(Member member) {
Checks.notNull(member, "Member");
int count = 0;
for (RequestedTrack requestedTrack : this.queue) {
if (requestedTrack.getRequester().equals(member)) {
@@ -152,10 +163,10 @@ public final class TrackScheduler extends AudioEventAdapter {
logger.info("Track '" + track.getInfo().title + "' in " + this.guild.getName() + " (" + this.guild.getId()
+ ") has started.");
if (this.idleLogoutTask != null && !this.idleLogoutTask.isDone()) {
if (this.closeAudioConnectionTask != null && !this.closeAudioConnectionTask.isDone()) {
logger.info("A track has started in " + this.guild.getName() + " (" + this.guild.getId()
+ "). Cancelling scheduled disconnect.");
this.idleLogoutTask.cancel(true);
this.closeAudioConnectionTask.cancel(true);
}
SkipTracker.resetVotes(this.guild);
}
@@ -170,7 +181,7 @@ public final class TrackScheduler extends AudioEventAdapter {
logger.info("Track '" + track.getInfo().title + "' in " + this.guild.getName() + " (" + this.guild.getId()
+ ") has stopped.");
if ((endReason.mayStartNext || endReason == AudioTrackEndReason.STOPPED) && this.queue.size() >= 1) {
AudioUtil.playTrack(this.guild, this.queue.remove(0));
MainThread.scheduleImmediately(new PlayNextTrackTask(this.guild));
} else {
try {
AudioUtil.setVolume(this.guild, Integer.parseInt(DataManager.getGuildValue(guild, "defaultVolume")));
@@ -182,7 +193,8 @@ public final class TrackScheduler extends AudioEventAdapter {
final VoiceChannel currentChannel = AudioUtil.getVoiceChannelConnectedTo(this.guild);
logger.info("Disconnecting from " + currentChannel.getName() + " (" + currentChannel.getId() + ") in "
+ this.guild.getName() + " (" + this.guild.getId() + ") in 1 minute...");
this.idleLogoutTask = Scheduler.schedule(new IdleDisconnectTask(this.guild), 1, TimeUnit.MINUTES);
this.closeAudioConnectionTask = MainThread.scheduleLater(new CloseAudioConnectionTask(this.guild), 1,
TimeUnit.MINUTES);
}
SkipTracker.resetVotes(this.guild);
}

View File

@@ -21,10 +21,10 @@ import java.util.concurrent.TimeUnit;
import com.vdurmont.emoji.Emoji;
import com.vdurmont.emoji.EmojiManager;
import dev.logal.logalbot.MainThread;
import dev.logal.logalbot.tasks.MessageDeleteTask;
import dev.logal.logalbot.tasks.ReactionCallbackExpireTask;
import dev.logal.logalbot.utils.ReactionCallbackManager;
import dev.logal.logalbot.utils.Scheduler;
import net.dv8tion.jda.core.MessageBuilder;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.Message;
@@ -44,10 +44,11 @@ public final class CommandResponse {
private TimeUnit deletionDelayUnit, expireDelayUnit;
public CommandResponse(final String emoji, final String response) {
Checks.notNull(emoji, "Emoji");
Checks.notNull(response, "Response");
Checks.notEmpty(emoji, "Emoji");
Checks.notEmpty(response, "Response");
this.emoji = EmojiManager.getForAlias(emoji);
Checks.notNull(this.emoji, "Valid Emoji");
this.response = response;
}
@@ -59,7 +60,7 @@ public final class CommandResponse {
}
public final CommandResponse setDeletionDelay(final long delay, final TimeUnit unit) {
Checks.notNull(delay, "Delay");
Checks.notNegative(delay, "Delay");
Checks.notNull(unit, "Unit");
this.deletionDelay = delay;
@@ -68,7 +69,7 @@ public final class CommandResponse {
}
public final CommandResponse addReactionCallback(final String emoji, final ReactionCallback callback) {
Checks.notNull(emoji, "Emoji");
Checks.notEmpty(emoji, "Emoji");
Checks.notNull(callback, "Callback");
this.callbacks.put(EmojiManager.getForAlias(emoji), callback);
@@ -83,7 +84,7 @@ public final class CommandResponse {
}
public final CommandResponse setReactionCallbackExpireDelay(final long delay, final TimeUnit unit) {
Checks.notNull(delay, "Delay");
Checks.notNegative(delay, "Delay");
Checks.notNull(unit, "Unit");
this.expireDelay = delay;
@@ -106,9 +107,9 @@ public final class CommandResponse {
private final void handleResponseCreation(final Message message) {
if ((this.deletionDelay != 0) && (this.deletionDelayUnit != null)) {
Scheduler.schedule(new MessageDeleteTask(message), this.deletionDelay, this.deletionDelayUnit);
MainThread.scheduleLater(new MessageDeleteTask(message), this.deletionDelay, this.deletionDelayUnit);
} else if ((this.expireDelay != 0) && (this.expireDelayUnit != null)) {
Scheduler.schedule(new ReactionCallbackExpireTask(message), this.expireDelay, this.expireDelayUnit);
MainThread.scheduleLater(new ReactionCallbackExpireTask(message), this.expireDelay, this.expireDelayUnit);
}
for (final Map.Entry<Emoji, ReactionCallback> callback : callbacks.entrySet()) {

View File

@@ -17,11 +17,12 @@ package dev.logal.logalbot.commands.audio;
import java.util.List;
import java.util.concurrent.TimeUnit;
import dev.logal.logalbot.MainThread;
import dev.logal.logalbot.audio.RequestedTrack;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.tasks.CommandExecutionTask;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.CommandManager;
import dev.logal.logalbot.utils.ReactionCallbackManager;
import dev.logal.logalbot.utils.TrackUtil;
import net.dv8tion.jda.core.entities.Guild;
@@ -56,14 +57,16 @@ public final class Queue implements Command {
if (page != 1) {
response.addReactionCallback("arrow_left", (reactor, message) -> {
ReactionCallbackManager.unregisterMessage(message, true);
CommandManager.executeCommand(("queue " + (page - 1)).split(" "), reactor, channel);
MainThread.scheduleImmediately(
new CommandExecutionTask(("queue " + (page - 1)).split(" "), reactor, channel));
});
}
if (TrackUtil.doesGreaterPageExist(queue, page)) {
response.addReactionCallback("arrow_right", (reactor, message) -> {
ReactionCallbackManager.unregisterMessage(message, true);
CommandManager.executeCommand(("queue " + (page + 1)).split(" "), reactor, channel);
MainThread.scheduleImmediately(
new CommandExecutionTask(("queue " + (page + 1)).split(" "), reactor, channel));
});
}

View File

@@ -16,12 +16,13 @@ package dev.logal.logalbot.commands.audio;
import java.util.concurrent.TimeUnit;
import dev.logal.logalbot.MainThread;
import dev.logal.logalbot.audio.RequestedTrack;
import dev.logal.logalbot.audio.TrackScheduler;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.tasks.CommandExecutionTask;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.CommandManager;
import dev.logal.logalbot.utils.ReactionCallbackManager;
import dev.logal.logalbot.utils.StringUtil;
import dev.logal.logalbot.utils.TrackUtil;
@@ -69,7 +70,8 @@ public final class Remove implements Command {
response.addReactionCallback(StringUtil.intToKeycapEmoji(trackNumber).getAliases().get(0),
(reactor, message) -> {
ReactionCallbackManager.unregisterMessage(message, true);
CommandManager.executeCommand(("remove " + trackNumber).split(" "), reactor, channel);
MainThread.scheduleImmediately(
new CommandExecutionTask(("remove " + trackNumber).split(" "), reactor, channel));
});
}

View File

@@ -44,6 +44,6 @@ public final class Shuffle implements Command {
}
scheduler.shuffleQueue();
return new CommandResponse("salad", executor.getAsMention() + " shuffled the queue.");
return new CommandResponse("green_salad", executor.getAsMention() + " shuffled the queue.");
}
}

View File

@@ -0,0 +1,53 @@
package dev.logal.logalbot.commands.fun;
// Copyright 2019 Logan Fick
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import java.security.SecureRandom;
import java.util.concurrent.TimeUnit;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
public final class Dice implements Command {
private final SecureRandom rng = new SecureRandom();
@Override
public CommandResponse execute(String[] arguments, Member executor, TextChannel channel) {
if (arguments.length == 0) {
return new CommandResponse("game_die",
executor.getAsMention() + ", the dice rolled **" + (rng.nextInt(6) + 1) + "**.");
} else {
final int maximumRange;
try {
maximumRange = Integer.parseInt(arguments[0]);
} catch (final NumberFormatException exception) {
return new CommandResponse("x",
"Sorry " + executor.getAsMention() + ", but the maximum range must be an integer.")
.setDeletionDelay(30, TimeUnit.SECONDS);
}
if (maximumRange < 1) {
return new CommandResponse("x",
"Sorry " + executor.getAsMention() + ", but the maximum range must be at least 1.")
.setDeletionDelay(30, TimeUnit.SECONDS);
}
return new CommandResponse("game_die",
executor.getAsMention() + ", the dice rolled **" + (rng.nextInt(maximumRange) + 1) + "**.");
}
}
}

View File

@@ -14,7 +14,8 @@ package dev.logal.logalbot.events;
// See the License for the specific language governing permissions and
// limitations under the License.
import dev.logal.logalbot.utils.ReactionCallbackManager;
import dev.logal.logalbot.MainThread;
import dev.logal.logalbot.tasks.ReactionCallbackExecutionTask;
import net.dv8tion.jda.core.events.message.guild.react.GuildMessageReactionAddEvent;
import net.dv8tion.jda.core.hooks.ListenerAdapter;
import net.dv8tion.jda.core.utils.Checks;
@@ -25,8 +26,8 @@ public final class GuildMessageReactionAdd extends ListenerAdapter {
Checks.notNull(event, "Event");
if (!event.getUser().equals(event.getJDA().getSelfUser())) {
ReactionCallbackManager.executeCallback(event.getMessageIdLong(), event.getChannel(), event.getMember(),
event.getReactionEmote().getName());
MainThread.scheduleImmediately(new ReactionCallbackExecutionTask(event.getMessageIdLong(),
event.getChannel(), event.getMember(), event.getReactionEmote().getName()));
}
}
}

View File

@@ -17,7 +17,8 @@ package dev.logal.logalbot.events;
import java.util.Arrays;
import java.util.List;
import dev.logal.logalbot.utils.CommandManager;
import dev.logal.logalbot.MainThread;
import dev.logal.logalbot.tasks.CommandExecutionTask;
import dev.logal.logalbot.utils.DataManager;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Guild;
@@ -55,7 +56,7 @@ public final class GuildMessageReceived extends ListenerAdapter {
if (self.hasPermission(channel, Permission.MESSAGE_MANAGE)) {
message.delete().reason("LogalBot Command Execution").queue();
}
CommandManager.executeCommand(command, author, channel);
MainThread.scheduleImmediately(new CommandExecutionTask(command, author, channel));
}
} else {
final String commandCharacter = DataManager.getGuildValue(guild, "commandCharacter");
@@ -70,7 +71,7 @@ public final class GuildMessageReceived extends ListenerAdapter {
if (self.hasPermission(channel, Permission.MESSAGE_MANAGE)) {
message.delete().reason("LogalBot Command Execution").queue();
}
CommandManager.executeCommand(command, author, channel);
MainThread.scheduleImmediately(new CommandExecutionTask(command, author, channel));
}
}
}

View File

@@ -19,6 +19,8 @@ import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dev.logal.logalbot.MainThread;
import dev.logal.logalbot.tasks.ResetAudioPlayerTask;
import dev.logal.logalbot.utils.AudioUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
@@ -55,10 +57,7 @@ public final class GuildVoiceLeave extends ListenerAdapter {
if (members.size() == 1 && members.get(0).getUser().equals(event.getJDA().getSelfUser())) {
logger.info("All listeners left " + leftChannel.getName() + " (" + leftChannel.getId() + ") in "
+ guild.getName() + " (" + guild.getId() + ").");
AudioUtil.getTrackScheduler(guild).clearQueue();
if (AudioUtil.isTrackLoaded(guild)) {
AudioUtil.stopTrack(guild);
}
MainThread.scheduleImmediately(new ResetAudioPlayerTask(guild));
}
}
}

View File

@@ -19,6 +19,8 @@ import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dev.logal.logalbot.MainThread;
import dev.logal.logalbot.tasks.ResetAudioPlayerTask;
import dev.logal.logalbot.utils.AudioUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
@@ -55,10 +57,7 @@ public final class GuildVoiceMove extends ListenerAdapter {
if (members.size() == 1 && members.get(0).getUser().equals(event.getJDA().getSelfUser())) {
logger.info("All listeners left " + leftChannel.getName() + " (" + leftChannel.getId() + ") in "
+ guild.getName() + " (" + guild.getId() + ").");
AudioUtil.getTrackScheduler(guild).clearQueue();
if (AudioUtil.isTrackLoaded(guild)) {
AudioUtil.stopTrack(guild);
}
MainThread.scheduleImmediately(new ResetAudioPlayerTask(guild));
}
}
}

View File

@@ -18,10 +18,10 @@ import dev.logal.logalbot.utils.AudioUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.utils.Checks;
public final class IdleDisconnectTask implements Runnable {
public final class CloseAudioConnectionTask implements Runnable {
private final Guild guild;
public IdleDisconnectTask(final Guild guild) {
public CloseAudioConnectionTask(final Guild guild) {
Checks.notNull(guild, "Guild");
this.guild = guild;

View File

@@ -0,0 +1,41 @@
package dev.logal.logalbot.tasks;
// Copyright 2019 Logan Fick
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import dev.logal.logalbot.utils.CommandManager;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.utils.Checks;
public final class CommandExecutionTask implements Runnable {
private final String[] command;
private final Member executor;
private final TextChannel channel;
public CommandExecutionTask(final String[] command, final Member executor, final TextChannel channel) {
Checks.noneNull(command, "Command");
Checks.notNull(executor, "Executor");
Checks.notNull(channel, "Channel");
this.command = command;
this.executor = executor;
this.channel = channel;
}
@Override
public final void run() {
CommandManager.executeCommand(this.command, this.executor, this.channel);
}
}

View File

@@ -30,6 +30,5 @@ public final class MessageDeleteTask implements Runnable {
@Override
public final void run() {
ReactionCallbackManager.unregisterMessage(messageToDelete, true);
messageToDelete.delete().queue();
}
}

View File

@@ -0,0 +1,34 @@
package dev.logal.logalbot.tasks;
// Copyright 2019 Logan Fick
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import dev.logal.logalbot.utils.AudioUtil;
import net.dv8tion.jda.core.entities.VoiceChannel;
import net.dv8tion.jda.core.utils.Checks;
public final class OpenAudioConnectionTask implements Runnable {
private final VoiceChannel channel;
public OpenAudioConnectionTask(final VoiceChannel channel) {
Checks.notNull(channel, "Channel");
this.channel = channel;
}
@Override
public void run() {
AudioUtil.openAudioConnection(channel);
}
}

View File

@@ -0,0 +1,34 @@
package dev.logal.logalbot.tasks;
// Copyright 2019 Logan Fick
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import dev.logal.logalbot.utils.AudioUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.utils.Checks;
public final class PlayNextTrackTask implements Runnable {
private final Guild guild;
public PlayNextTrackTask(final Guild guild) {
Checks.notNull(guild, "Guild");
this.guild = guild;
}
@Override
public void run() {
AudioUtil.playNextTrack(this.guild);
}
}

View File

@@ -0,0 +1,45 @@
package dev.logal.logalbot.tasks;
// Copyright 2019 Logan Fick
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import dev.logal.logalbot.utils.ReactionCallbackManager;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.utils.Checks;
public final class ReactionCallbackExecutionTask implements Runnable {
private final long messageID;
private final TextChannel channel;
private final Member executor;
private final String emoji;
public ReactionCallbackExecutionTask(final long messageID, final TextChannel channel, final Member executor,
final String emoji) {
Checks.notNegative(messageID, "Message ID");
Checks.notNull(channel, "Channel");
Checks.notNull(executor, "Executor");
Checks.notEmpty(emoji, "Emoji");
this.messageID = messageID;
this.channel = channel;
this.executor = executor;
this.emoji = emoji;
}
@Override
public void run() {
ReactionCallbackManager.executeCallback(this.messageID, this.channel, this.executor, this.emoji);
}
}

View File

@@ -0,0 +1,35 @@
package dev.logal.logalbot.tasks;
// Copyright 2019 Logan Fick
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import dev.logal.logalbot.utils.AudioUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.utils.Checks;
public final class ResetAudioPlayerTask implements Runnable {
private final Guild guild;
public ResetAudioPlayerTask(final Guild guild) {
Checks.notNull(guild, "Guild");
this.guild = guild;
}
@Override
public void run() {
AudioUtil.getTrackScheduler(guild).clearQueue();
AudioUtil.stopTrack(guild);
}
}

View File

@@ -0,0 +1,54 @@
package dev.logal.logalbot.tasks;
// Copyright 2019 Logan Fick
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import java.util.LinkedList;
import java.util.List;
import dev.logal.logalbot.audio.RequestedTrack;
import dev.logal.logalbot.audio.TrackScheduler;
import dev.logal.logalbot.utils.AudioUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.utils.Checks;
public final class TrackAdditionTask implements Runnable {
private final Guild guild;
private final LinkedList<RequestedTrack> tracks;
public TrackAdditionTask(final Guild guild, final RequestedTrack track) {
Checks.notNull(guild, "Guild");
Checks.notNull(track, "Track");
this.guild = guild;
tracks = new LinkedList<RequestedTrack>();
tracks.add(track);
}
public TrackAdditionTask(final Guild guild, final List<RequestedTrack> tracks) {
Checks.notNull(guild, "Guild");
Checks.noneNull(tracks, "Tracks");
this.guild = guild;
this.tracks = (LinkedList<RequestedTrack>) tracks;
}
@Override
public final void run() {
final TrackScheduler scheduler = AudioUtil.getTrackScheduler(this.guild);
for (final RequestedTrack track : this.tracks) {
scheduler.addToQueue(track);
}
}
}

View File

@@ -106,11 +106,20 @@ public final class AudioUtil {
loadedTracks.put(guild.getIdLong(), track);
}
public static final void playNextTrack(final Guild guild) {
Checks.notNull(guild, "Guild");
final RequestedTrack track = schedulers.get(guild.getIdLong()).popFromQueue();
if (track != null) {
playTrack(guild, track);
}
}
public static final void stopTrack(final Guild guild) {
Checks.notNull(guild, "Guild");
players.get(guild.getIdLong()).stopTrack();
loadedTracks.remove(guild.getIdLong());
players.get(guild.getIdLong()).stopTrack();
}
public static final boolean isTrackLoaded(final Guild guild) {
@@ -132,7 +141,6 @@ public final class AudioUtil {
public static final void setPausedState(final Guild guild, final boolean pausedState) {
Checks.notNull(guild, "Guild");
Checks.notNull(pausedState, "Paused state");
guild.getAudioManager().setSelfMuted(pausedState);
players.get(guild.getIdLong()).setPaused(pausedState);
@@ -166,7 +174,7 @@ public final class AudioUtil {
}
public static final void findTrack(final String query, final Member requester, final TextChannel channel) {
Checks.notNull(query, "Query");
Checks.notEmpty(query, "Query");
Checks.notNull(requester, "Requester");
Checks.notNull(channel, "Channel");

View File

@@ -49,8 +49,8 @@ public final class ReactionCallbackManager {
}
public static final void setCallbackTarget(final Message message, final User user) {
Checks.notNull(user, "User");
Checks.notNull(message, "Messsage");
Checks.notNull(user, "User");
targetDictionary.put(message.getIdLong(), user.getIdLong());
}
@@ -58,19 +58,22 @@ public final class ReactionCallbackManager {
public static final void unregisterMessage(final Message message, final boolean delete) {
Checks.notNull(message, "Message");
callbackDictionary.remove(message.getIdLong());
targetDictionary.remove(message.getIdLong());
if (delete) {
message.delete().queue();
} else {
message.addReaction(EmojiManager.getForAlias("no_entry").getUnicode()).queue();
if (callbackDictionary.containsKey(message.getIdLong())) {
message.addReaction(EmojiManager.getForAlias("no_entry").getUnicode()).queue();
}
}
callbackDictionary.remove(message.getIdLong());
targetDictionary.remove(message.getIdLong());
}
public static final void executeCallback(final long messageID, final TextChannel channel, final Member reactor,
final String emoji) {
Checks.notNull(messageID, "Message ID");
Checks.notNegative(messageID, "Message ID");
Checks.notNull(channel, "Channel");
Checks.notNull(reactor, "Reactor");
Checks.notEmpty(emoji, "Emoji");

View File

@@ -1,48 +0,0 @@
package dev.logal.logalbot.utils;
// Copyright 2019 Logan Fick
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// https://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import net.dv8tion.jda.core.utils.Checks;
public final class Scheduler {
private static final ScheduledExecutorService pool = Executors.newScheduledThreadPool(1);
private Scheduler() {
// Static access only.
}
public static final ScheduledFuture<?> schedule(final Runnable runnable, final long delay, final TimeUnit unit) {
Checks.notNull(runnable, "Runnable");
Checks.notNull(delay, "Delay");
Checks.notNull(unit, "Unit");
return pool.schedule(runnable, delay, unit);
}
public static final ScheduledFuture<?> scheduleRepeating(final Runnable runnable, final long initialDelay,
final long period, final TimeUnit unit) {
Checks.notNull(runnable, "Runnable");
Checks.notNull(initialDelay, "Initial delay");
Checks.notNull(period, "Period");
Checks.notNull(unit, "Unit");
return pool.scheduleAtFixedRate(runnable, initialDelay, period, unit);
}
}

View File

@@ -81,7 +81,7 @@ public final class StringUtil {
}
public static final String formatTime(final long milliseconds) {
Checks.notNull(milliseconds, "Milliseconds");
Checks.notNegative(milliseconds, "Milliseconds");
final long second = (milliseconds / 1000) % 60;
final long minute = (milliseconds / (1000 * 60)) % 60;

View File

@@ -65,7 +65,6 @@ public final class TrackUtil {
public static final MessageEmbed trackListInfoEmbed(final List<RequestedTrack> tracks, final boolean numbered) {
Checks.notNull(tracks, "Tracks");
Checks.notNull(numbered, "Numbered");
final EmbedBuilder builder = new EmbedBuilder();
for (int i = 0; i < tracks.size(); i++) {
@@ -94,7 +93,6 @@ public final class TrackUtil {
public static final MessageEmbed pagedTrackListInfoEmbed(final List<RequestedTrack> tracks, int page) {
Checks.notNull(tracks, "Tracks");
Checks.notNull(page, "Page");
final EmbedBuilder builder = new EmbedBuilder();
if (page < 1) {
@@ -124,7 +122,6 @@ public final class TrackUtil {
public static final boolean doesGreaterPageExist(final List<RequestedTrack> tracks, int page) {
Checks.notNull(tracks, "Tracks");
Checks.notNull(page, "Page");
final int pages = (int) Math.ceil(tracks.size() / 10d);
return (page < pages);