0

Made LogalBot open source.

This commit is contained in:
Logan Fick
2019-05-25 16:06:57 -04:00
commit b40dd8b9b0
49 changed files with 3633 additions and 0 deletions

View File

@@ -0,0 +1,141 @@
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 javax.security.auth.login.LoginException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dev.logal.logalbot.commands.administration.*;
import dev.logal.logalbot.commands.audio.*;
import dev.logal.logalbot.commands.fun.*;
import dev.logal.logalbot.commands.general.*;
import dev.logal.logalbot.events.*;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.CommandManager;
import dev.logal.logalbot.utils.DataManager;
import net.dv8tion.jda.core.AccountType;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.JDABuilder;
public final class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
private static final String token = System.getenv("TOKEN");
private static JDA jda;
private Main() {
// Static access only.
}
public static final void main(final String[] arguments) {
logger.info("Beginning setup of LogalBot...");
logger.info("Verifying connection to Redis...");
try {
DataManager.verifyConnection();
} catch (final Throwable exception) {
logger.error("An error occurred while attempting to verify the connection to Redis!", exception);
System.exit(1);
}
logger.info("Running any needed schema migrations...");
try {
DataManager.runMigrations();
} catch (final Throwable exception) {
logger.error("An error occurred while attempting to migrate the database!", exception);
System.exit(1);
}
logger.info("Attempting to log into Discord...");
try {
final JDABuilder builder = new JDABuilder(AccountType.BOT);
builder.setAutoReconnect(true);
builder.setAudioEnabled(true);
builder.setToken(token);
builder.addEventListener(new GuildReady());
jda = builder.build().awaitReady();
} catch (final LoginException exception) {
logger.error("The token specified is not valid.");
System.exit(1);
} catch (final Throwable exception) {
logger.error("An error occurred while attempting to set up JDA!", exception);
System.exit(1);
}
logger.info("Successfully logged into Discord as bot user '" + jda.getSelfUser().getName() + "'.");
logger.info("Initializing...");
AudioUtil.initializePlayerManager();
jda.addEventListener(new GuildVoiceLeave());
jda.addEventListener(new GuildVoiceMove());
jda.addEventListener(new GuildMessageReactionAdd());
// General Commands
CommandManager.registerCommand("about", new About(), false);
CommandManager.registerCommand("help", new Help(), false);
// Fun Commands
CommandManager.registerCommand("8ball", new EightBall(), false);
// Audio Commands
CommandManager.registerCommand("forceskip", new ForceSkip(), true);
CommandManager.registerCommandAlias("fs", "forceskip");
CommandManager.registerCommandAlias("fskip", "forceskip");
CommandManager.registerCommand("lock", new Lock(), true);
CommandManager.registerCommandAlias("l", "lock");
CommandManager.registerCommand("nowplaying", new NowPlaying(), false);
CommandManager.registerCommandAlias("np", "nowplaying");
CommandManager.registerCommand("pause", new Pause(), true);
CommandManager.registerCommand("play", new Play(), false);
CommandManager.registerCommandAlias("p", "play");
CommandManager.registerCommandAlias("pl", "play");
CommandManager.registerCommandAlias("add", "play");
CommandManager.registerCommandAlias("a", "play");
CommandManager.registerCommand("queue", new Queue(), false);
CommandManager.registerCommandAlias("q", "queue");
CommandManager.registerCommand("remove", new Remove(), true);
CommandManager.registerCommandAlias("r", "remove");
CommandManager.registerCommandAlias("x", "remove");
CommandManager.registerCommandAlias("rem", "remove");
CommandManager.registerCommandAlias("rm", "remove");
CommandManager.registerCommand("reset", new Reset(), true);
CommandManager.registerCommandAlias("rst", "reset");
CommandManager.registerCommand("skip", new Skip(), false);
CommandManager.registerCommandAlias("s", "skip");
CommandManager.registerCommand("volume", new Volume(), true);
CommandManager.registerCommandAlias("v", "volume");
CommandManager.registerCommandAlias("vol", "volume");
CommandManager.registerCommand("shuffle", new Shuffle(), true);
CommandManager.registerCommandAlias("shuf", "shuffle");
CommandManager.registerCommandAlias("shuff", "shuffle");
CommandManager.registerCommandAlias("shfl", "shuffle");
// Administration Commands
CommandManager.registerCommand("whitelist", new Whitelist(), true);
CommandManager.registerCommandAlias("wl", "whitelist");
CommandManager.registerCommand("settings", new Settings(), true);
CommandManager.registerCommandAlias("set", "settings");
CommandManager.registerCommandAlias("setting", "settings");
CommandManager.registerCommandAlias("configure", "settings");
CommandManager.registerCommandAlias("config", "settings");
CommandManager.registerCommandAlias("conf", "settings");
logger.info("Everything seems to be ready! Enabling command listener...");
jda.addEventListener(new GuildMessageReceived());
logger.info("Initialization complete!");
}
}

View File

@@ -0,0 +1,48 @@
package dev.logal.logalbot.audio;
// 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 com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame;
import net.dv8tion.jda.core.audio.AudioSendHandler;
import net.dv8tion.jda.core.utils.Checks;
public final class AudioPlayerSendHandler implements AudioSendHandler {
private final AudioPlayer audioPlayer;
private AudioFrame lastFrame;
public AudioPlayerSendHandler(final AudioPlayer audioPlayer) {
Checks.notNull(audioPlayer, "Audio Player");
this.audioPlayer = audioPlayer;
}
@Override
public final boolean canProvide() {
lastFrame = audioPlayer.provide();
return lastFrame != null;
}
@Override
public final byte[] provide20MsAudio() {
return lastFrame.getData();
}
@Override
public final boolean isOpus() {
return true;
}
}

View File

@@ -0,0 +1,201 @@
package dev.logal.logalbot.audio;
// 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.ArrayList;
import java.util.concurrent.TimeUnit;
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.PermissionManager;
import dev.logal.logalbot.utils.TrackUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.utils.Checks;
public final class TrackLoadHandler implements AudioLoadResultHandler {
private final Logger logger = LoggerFactory.getLogger(TrackLoadHandler.class);
private final Member requester;
private final TextChannel channel;
public TrackLoadHandler(final Member requester, final TextChannel channel) {
Checks.notNull(requester, "Requester");
Checks.notNull(channel, "Channel");
this.requester = requester;
this.channel = channel;
}
@Override
public final void trackLoaded(final AudioTrack track) {
Checks.notNull(track, "Track");
final CommandResponse response;
final TrackScheduler scheduler = AudioUtil.getTrackScheduler(this.channel.getGuild());
final AudioTrackInfo info = track.getInfo();
if (info.isStream) {
response = new CommandResponse("no_entry_sign",
"Sorry " + this.requester.getAsMention() + ", but streams cannot be added to the queue.")
.setDeletionDelay(10, TimeUnit.SECONDS);
response.sendResponse(this.channel);
return;
}
if ((info.length >= 60000 && info.length <= 900000) || PermissionManager.isWhitelisted(this.requester)) {
response = new CommandResponse("no_entry_sign",
"Sorry " + this.requester.getAsMention()
+ ", but you can only add tracks between 1 and 15 minutes in length.").setDeletionDelay(10,
TimeUnit.SECONDS);
response.sendResponse(this.channel);
return;
}
if (scheduler.isQueued(track)) {
response = new CommandResponse("no_entry_sign",
"Sorry " + this.requester.getAsMention() + ", but that track is already queued.")
.setDeletionDelay(10, TimeUnit.SECONDS);
response.sendResponse(this.channel);
return;
}
scheduler.addToQueue(track, requester);
response = new CommandResponse("notes",
this.requester.getAsMention() + " added the following track to the queue:");
response.attachEmbed(TrackUtil.generateTrackInfoEmbed(track));
response.sendResponse(this.channel);
}
@Override
public final void playlistLoaded(final AudioPlaylist playlist) {
Checks.notNull(playlist, "Playlist");
CommandResponse response;
final TrackScheduler scheduler = AudioUtil.getTrackScheduler(this.channel.getGuild());
final AudioTrack selectedTrack = playlist.getSelectedTrack();
AudioTrack track = null;
if (!playlist.isSearchResult() && selectedTrack != null) {
track = selectedTrack;
} else if (playlist.isSearchResult()) {
track = playlist.getTracks().get(0);
}
if (track != null) {
if (scheduler.isQueued(track)) {
response = new CommandResponse("no_entry_sign",
"Sorry " + this.requester.getAsMention() + ", but that track is already queued.")
.setDeletionDelay(10, TimeUnit.SECONDS);
response.sendResponse(this.channel);
return;
}
final AudioTrackInfo info = track.getInfo();
if (info.isStream) {
response = new CommandResponse("no_entry_sign",
"Sorry " + this.requester.getAsMention() + ", but streams cannot be added to the queue.")
.setDeletionDelay(10, TimeUnit.SECONDS);
response.sendResponse(this.channel);
return;
}
if ((info.length >= 60000 && info.length <= 900000) || PermissionManager.isWhitelisted(this.requester)) {
scheduler.addToQueue(track, this.requester);
response = new CommandResponse("notes",
this.requester.getAsMention() + " added the following track to the queue:");
response.attachEmbed(TrackUtil.generateTrackInfoEmbed(track));
response.sendResponse(this.channel);
} else {
response = new CommandResponse("no_entry_sign", "Sorry " + this.requester.getAsMention()
+ ", but you are not allowed to add tracks less than 1 minute or greater than 15 minutes in length.")
.setDeletionDelay(10, TimeUnit.SECONDS);
response.sendResponse(this.channel);
}
} else {
if (PermissionManager.isWhitelisted(this.requester)) {
final ArrayList<AudioTrack> addedTracks = new ArrayList<>();
for (final AudioTrack playlistTrack : playlist.getTracks()) {
if (!scheduler.isQueueFull()) {
if (!scheduler.isQueued(playlistTrack) && !playlistTrack.getInfo().isStream) {
scheduler.addToQueue(playlistTrack, this.requester);
addedTracks.add(playlistTrack);
}
} else {
break;
}
}
if (addedTracks.size() == 0) {
response = new CommandResponse("no_entry_sign",
"Sorry " + this.requester.getAsMention()
+ ", but none of the tracks in that playlist could be added.").setDeletionDelay(10,
TimeUnit.SECONDS);
response.sendResponse(this.channel);
}
response = new CommandResponse("notes",
this.requester.getAsMention() + " added the following tracks to the queue:");
response.attachEmbed(TrackUtil.generateTrackListInfoEmbed(addedTracks, false));
response.sendResponse(this.channel);
} else {
response = new CommandResponse("no_entry_sign",
"Sorry " + this.requester.getAsMention()
+ ", but you are not allowed to add playlists to the queue.").setDeletionDelay(10,
TimeUnit.SECONDS);
response.sendResponse(this.channel);
}
}
}
@Override
public final void noMatches() {
final CommandResponse response = new CommandResponse("map",
"Sorry " + this.requester.getAsMention() + ", but I was not able to find that track.")
.setDeletionDelay(10, TimeUnit.SECONDS);
response.sendResponse(this.channel);
}
@Override
public final void loadFailed(final FriendlyException exception) {
Checks.notNull(exception, "Exception");
final CommandResponse response;
if (exception.getMessage().equals("Unknown file format.")) {
response = new CommandResponse("question",
"Sorry " + this.requester.getAsMention() + ", but I do not recognize the format of that track.")
.setDeletionDelay(10, TimeUnit.SECONDS);
} else {
final Guild guild = this.channel.getGuild();
logger.error("An error occurred for " + guild.getName() + " (" + guild.getId()
+ ") while trying to load a track!", exception);
response = new CommandResponse("sos",
"Sorry " + this.requester.getAsMention()
+ ", but an error occurred while trying to get that track!").setDeletionDelay(10,
TimeUnit.SECONDS);
}
response.sendResponse(this.channel);
}
}

View File

@@ -0,0 +1,176 @@
package dev.logal.logalbot.audio;
// 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.ArrayList;
import java.util.Collections;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
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.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;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.User;
import net.dv8tion.jda.core.entities.VoiceChannel;
import net.dv8tion.jda.core.utils.Checks;
public final class TrackScheduler extends AudioEventAdapter {
private static final Logger logger = LoggerFactory.getLogger(TrackScheduler.class);
private final Guild guild;
private final ArrayList<AudioTrack> queue = new ArrayList<>(250);
private boolean queueLocked = false;
private ScheduledFuture<?> idleLogoutTask;
public TrackScheduler(final Guild guild) {
Checks.notNull(guild, "Guild");
this.guild = guild;
}
public final void addToQueue(final AudioTrack track, final Member requester) {
Checks.notNull(track, "Track");
Checks.notNull(requester, "Requester");
if (this.queueLocked && !PermissionManager.isWhitelisted(requester)) {
return;
}
if (this.isQueueFull()) {
return;
}
final User user = requester.getUser();
logger.info(user.getName() + " (" + user.getId() + ") added '" + track.getInfo().title + "' to the queue in "
+ this.guild.getName() + " (" + this.guild.getId() + ").");
this.queue.add(track);
if (!AudioUtil.isTrackLoaded(this.guild)) {
AudioUtil.openAudioConnection(VoiceChannelUtil.getVoiceChannelMemberIsConnectedTo(requester));
AudioUtil.playTrack(this.guild, this.queue.remove(0));
}
}
public final void removeFromQueue(final int index) {
Checks.notNull(index, "Index");
logger.info("Track '" + queue.remove(index).getInfo().title + "' has been removed from the queue in "
+ this.guild.getName() + " (" + this.guild.getId() + ").");
}
public final boolean isQueued(final AudioTrack track) {
Checks.notNull(track, "Track");
for (final AudioTrack queuedTrack : queue) {
if (track.getInfo().identifier.equals(queuedTrack.getInfo().identifier)) {
return true;
}
}
return false;
}
public final boolean isQueueFull() {
return this.queue.size() >= 250;
}
public final boolean isQueueEmpty() {
return this.queue.isEmpty();
}
public final boolean isQueueLocked() {
return this.queueLocked;
}
public final void setQueueLocked(final boolean locked) {
Checks.notNull(locked, "Locked");
this.queueLocked = locked;
}
public final void clearQueue() {
this.queue.clear();
}
public final void shuffleQueue() {
Collections.shuffle(this.queue);
}
public final ArrayList<AudioTrack> getQueue() {
return this.queue;
}
public final void skipCurrentTrack() {
if (AudioUtil.isTrackLoaded(this.guild)) {
logger.info("Track '" + AudioUtil.getLoadedTrack(this.guild).getInfo().title + "' in "
+ this.guild.getName() + " (" + this.guild.getId() + ") been skipped.");
AudioUtil.stopTrack(this.guild);
}
}
@Override
public final void onTrackStart(final AudioPlayer player, final AudioTrack track) {
Checks.notNull(player, "Player");
Checks.notNull(track, "Track");
logger.info("Track '" + track.getInfo().title + "' in " + this.guild.getName() + " (" + this.guild.getId()
+ ") has started.");
if (this.idleLogoutTask != null && !this.idleLogoutTask.isDone()) {
logger.info("A track has started in " + this.guild.getName() + " (" + this.guild.getId()
+ "). Cancelling scheduled disconnect.");
this.idleLogoutTask.cancel(true);
}
SkipTracker.resetVotes(this.guild);
}
@Override
public final void onTrackEnd(final AudioPlayer player, final AudioTrack track,
final AudioTrackEndReason endReason) {
Checks.notNull(player, "Player");
Checks.notNull(track, "Track");
Checks.notNull(endReason, "End reason");
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));
} else {
try {
AudioUtil.setVolume(this.guild, Integer.parseInt(DataManager.getGuildValue(guild, "defaultVolume")));
} catch (final NumberFormatException exception) {
AudioUtil.setVolume(this.guild, 10);
}
this.queueLocked = false;
AudioUtil.setPausedState(this.guild, false);
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);
}
SkipTracker.resetVotes(this.guild);
}
}

View File

@@ -0,0 +1,22 @@
package dev.logal.logalbot.commands;
// 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 net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
public interface Command {
CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel);
}

View File

@@ -0,0 +1,92 @@
package dev.logal.logalbot.commands;
// 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.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.vdurmont.emoji.Emoji;
import com.vdurmont.emoji.EmojiManager;
import dev.logal.logalbot.tasks.MessageDeleteTask;
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;
import net.dv8tion.jda.core.entities.MessageEmbed;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.entities.User;
public final class CommandResponse {
private final Emoji emoji;
private final String response;
private final LinkedHashMap<Emoji, ReactionCallback> callbacks = new LinkedHashMap<>();
private MessageEmbed responseEmbed;
private User callbacksTarget;
private long deletionDelay = 0;
private TimeUnit deletionDelayUnit;
public CommandResponse(final String emoji, final String response) {
this.emoji = EmojiManager.getForAlias(emoji);
this.response = response;
}
public final CommandResponse attachEmbed(final MessageEmbed embed) {
this.responseEmbed = embed;
return this;
}
public final CommandResponse setDeletionDelay(final long delay, final TimeUnit unit) {
this.deletionDelay = delay;
this.deletionDelayUnit = unit;
return this;
}
public final CommandResponse addReactionCallback(final Emoji emoji, final ReactionCallback callback) {
this.callbacks.put(emoji, callback);
return this;
}
public final CommandResponse setReactionCallbackTarget(final Member member) {
this.callbacksTarget = member.getUser();
return this;
}
public final void sendResponse(final TextChannel channel) {
final MessageBuilder builder = new MessageBuilder();
builder.setContent(this.emoji.getUnicode() + " " + this.response);
if (this.responseEmbed != null) {
builder.setEmbed(this.responseEmbed);
}
channel.sendMessage(builder.build()).queue(this::handleResponseCreation);
}
private final void handleResponseCreation(final Message message) {
if ((this.deletionDelay != 0) && (this.deletionDelayUnit != null)) {
Scheduler.schedule(new MessageDeleteTask(message), this.deletionDelay, this.deletionDelayUnit);
}
for (final Map.Entry<Emoji, ReactionCallback> callback : callbacks.entrySet()) {
ReactionCallbackManager.registerCallback(message, callback.getKey(), callback.getValue());
ReactionCallbackManager.setCallbackTarget(this.callbacksTarget, message);
message.addReaction(callback.getKey().getUnicode()).queue();
}
}
}

View File

@@ -0,0 +1,21 @@
package dev.logal.logalbot.commands;
// 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 net.dv8tion.jda.core.entities.Member;
public interface ReactionCallback {
void run(final Member reactor, final long messageID);
}

View File

@@ -0,0 +1,123 @@
package dev.logal.logalbot.commands.administration;
// 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.TimeUnit;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.DataManager;
import dev.logal.logalbot.utils.StringUtil;
import net.dv8tion.jda.core.EmbedBuilder;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
public final class Settings implements Command {
@Override
public final CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel) {
final Guild guild = executor.getGuild();
if (arguments.length == 0) {
final CommandResponse response = new CommandResponse("tools",
executor.getAsMention() + ", these are the current settings for this guild:");
final EmbedBuilder builder = new EmbedBuilder();
builder.setTitle("**Current Settings for " + StringUtil.sanitize(guild.getName()) + "**");
final String commandCharacter = DataManager.getGuildValue(guild, "commandCharacter");
if (commandCharacter == null) {
builder.addField("Command Character", "Not Set", true);
} else {
builder.addField("Command Character", commandCharacter, true);
}
final String defaultVolume = DataManager.getGuildValue(guild, "defaultVolume");
if (defaultVolume == null) {
builder.addField("Default Volume", "10%", true);
} else {
builder.addField("Default Volume", defaultVolume + "%", true);
}
response.attachEmbed(builder.build());
return response;
}
if (arguments[0].equalsIgnoreCase("commandcharacter") || arguments[0].equalsIgnoreCase("cmdchar")) {
if (arguments.length == 1) {
DataManager.deleteGuildKey(guild, "commandCharacter");
return new CommandResponse("white_check_mark",
executor.getAsMention() + ", the command character has been disabled.");
} else {
final char[] input = arguments[1].replaceAll("\n", "").toCharArray();
if (input.length > 1) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention()
+ ", but the command character must be a single character.").setDeletionDelay(10,
TimeUnit.SECONDS);
} else if (input.length == 0) {
DataManager.deleteGuildKey(guild, "commandCharacter");
return new CommandResponse("white_check_mark",
executor.getAsMention() + ", the command character has been disabled.");
} else {
DataManager.setGuildValue(guild, "commandCharacter", "" + input[0]);
return new CommandResponse("white_check_mark",
executor.getAsMention() + ", the command character has been set to `" + input[0] + "`.");
}
}
} else if (arguments[0].equalsIgnoreCase("defaultvolume") || arguments[0].equalsIgnoreCase("volume")) {
if (arguments.length == 1) {
DataManager.deleteGuildKey(guild, "defaultVolume");
if (!AudioUtil.isTrackLoaded(guild)) {
AudioUtil.setVolume(guild, 10);
}
return new CommandResponse("white_check_mark",
executor.getAsMention() + ", the default volume has been reset to `10%`.");
} else {
final int volume;
try {
volume = Integer.parseInt(arguments[1]);
} catch (final NumberFormatException exception) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but the default volume must be an integer.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
if (volume == 10) {
DataManager.deleteGuildKey(guild, "defaultVolume");
return new CommandResponse("white_check_mark",
executor.getAsMention() + ", the default volume has been reset to `10%`.");
}
if (volume <= 150 && volume >= 1) {
if (!AudioUtil.isTrackLoaded(guild)) {
AudioUtil.setVolume(guild, volume);
}
DataManager.setGuildValue(guild, "defaultVolume", "" + volume);
return new CommandResponse("white_check_mark",
executor.getAsMention() + ", the default volume has been set to `" + volume + "%`.");
} else {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention()
+ ", but the default volume must be between 1% and 150%.").setDeletionDelay(10,
TimeUnit.SECONDS);
}
}
} else {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but I do not know what that setting is.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
}
}

View File

@@ -0,0 +1,82 @@
package dev.logal.logalbot.commands.administration;
// 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.TimeUnit;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.PermissionManager;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
public final class Whitelist implements Command {
@Override
public final CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel) {
final Guild guild = channel.getGuild();
if (!executor.hasPermission(Permission.ADMINISTRATOR)) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but you are not allowed to use this command.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
if (arguments.length == 0) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention()
+ ", but you need to specify a user to add or remove from the whitelist.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
final String userID = arguments[0].replaceFirst("<@[!]?([0-9]*)>", "$1");
final Member member;
try {
member = guild.getMemberById(userID);
} catch (final Throwable exception) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but that doesn't appear to be a valid user.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
if (member == null) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but that doesn't appear to be a valid user.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
if (member.hasPermission(Permission.ADMINISTRATOR)) {
return new CommandResponse("no_entry_sign", "Sorry " + executor.getAsMention()
+ ", but you cannot remove that user from the whitelist due to them being a guild administrator.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
if (member.getUser().isBot()) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but you cannot whitelist bots.").setDeletionDelay(10,
TimeUnit.SECONDS);
}
if (PermissionManager.isWhitelisted(member)) {
PermissionManager.removeFromWhitelist(member);
return new CommandResponse("heavy_multiplication_x",
executor.getAsMention() + " has removed " + member.getAsMention() + " from the whitelist.");
} else {
PermissionManager.addToWhitelist(member);
return new CommandResponse("heavy_check_mark",
executor.getAsMention() + " has added " + member.getAsMention() + " to the whitelist.");
}
}
}

View File

@@ -0,0 +1,56 @@
package dev.logal.logalbot.commands.audio;
// 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.TimeUnit;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.TrackUtil;
import dev.logal.logalbot.utils.VoiceChannelUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
public final class ForceSkip implements Command {
@Override
public final CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel) {
final Guild guild = channel.getGuild();
if (!AudioUtil.isTrackLoaded(guild)) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention()
+ ", but there must be a track playing in order to force skip it.").setDeletionDelay(10,
TimeUnit.SECONDS);
}
if (!VoiceChannelUtil.isInCurrentVoiceChannel(executor)) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but you must be in voice channel `"
+ AudioUtil.getVoiceChannelConnectedTo(guild).getName()
+ "` in order to force skip tracks.").setDeletionDelay(10, TimeUnit.SECONDS);
}
final AudioTrack skippedTrack = AudioUtil.getLoadedTrack(guild);
AudioUtil.getTrackScheduler(guild).skipCurrentTrack();
final CommandResponse response = new CommandResponse("gun",
executor.getAsMention() + " force skipped the following track:");
response.attachEmbed(TrackUtil.generateTrackInfoEmbed(skippedTrack));
return response;
}
}

View File

@@ -0,0 +1,55 @@
package dev.logal.logalbot.commands.audio;
// 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.TimeUnit;
import dev.logal.logalbot.audio.TrackScheduler;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.VoiceChannelUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
public final class Lock implements Command {
@Override
public final CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel) {
final Guild guild = channel.getGuild();
if (!AudioUtil.isTrackLoaded(guild)) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention()
+ ", but there must be a track playing in order to lock or unlock the queue.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
if (!VoiceChannelUtil.isInCurrentVoiceChannel(executor)) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but you must be in voice channel `"
+ AudioUtil.getVoiceChannelConnectedTo(guild).getName()
+ "` in order to lock or unlock the queue.").setDeletionDelay(10, TimeUnit.SECONDS);
}
final TrackScheduler scheduler = AudioUtil.getTrackScheduler(guild);
if (scheduler.isQueueLocked()) {
scheduler.setQueueLocked(false);
return new CommandResponse("unlock", executor.getAsMention() + " unlocked the queue.");
} else {
scheduler.setQueueLocked(true);
return new CommandResponse("lock", executor.getAsMention() + " locked the queue.");
}
}
}

View File

@@ -0,0 +1,38 @@
package dev.logal.logalbot.commands.audio;
// 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.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.TrackUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
public final class NowPlaying implements Command {
@Override
public final CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel) {
final Guild guild = channel.getGuild();
if (!AudioUtil.isTrackLoaded(guild)) {
return new CommandResponse("mute", executor.getAsMention() + ", there is nothing currently playing.");
}
final CommandResponse response = new CommandResponse("dancer",
executor.getAsMention() + ", this is the track currently playing:");
response.attachEmbed(TrackUtil.generateCurrentTrackInfoEmbed(guild));
return response;
}
}

View File

@@ -0,0 +1,53 @@
package dev.logal.logalbot.commands.audio;
// 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.TimeUnit;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.VoiceChannelUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
public final class Pause implements Command {
@Override
public final CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel) {
final Guild guild = channel.getGuild();
if (!AudioUtil.isTrackLoaded(guild)) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention()
+ ", but there must be a track playing in order to pause or resume the track player.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
if (!VoiceChannelUtil.isInCurrentVoiceChannel(executor)) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but you must be in voice channel `"
+ AudioUtil.getVoiceChannelConnectedTo(guild).getName()
+ "` in order to pause or resume the track player.").setDeletionDelay(10, TimeUnit.SECONDS);
}
if (AudioUtil.isPlayerPaused(guild)) {
AudioUtil.setPausedState(guild, false);
return new CommandResponse("arrow_forward", executor.getAsMention() + " resumed the track player.");
} else {
AudioUtil.setPausedState(guild, true);
return new CommandResponse("pause_button", executor.getAsMention() + " paused the track player.");
}
}
}

View File

@@ -0,0 +1,100 @@
package dev.logal.logalbot.commands.audio;
// 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.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.TimeUnit;
import dev.logal.logalbot.audio.TrackScheduler;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.PermissionManager;
import dev.logal.logalbot.utils.VoiceChannelUtil;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.entities.VoiceChannel;
public final class Play implements Command {
@Override
public final CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel) {
final Guild guild = channel.getGuild();
if (AudioUtil.isTrackLoaded(guild) && !VoiceChannelUtil.isInCurrentVoiceChannel(executor)) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but you need to be in voice channel `"
+ AudioUtil.getVoiceChannelConnectedTo(guild).getName()
+ "` in order to add songs to the queue.").setDeletionDelay(10, TimeUnit.SECONDS);
}
final VoiceChannel targetChannel = VoiceChannelUtil.getVoiceChannelMemberIsConnectedTo(executor);
if (targetChannel == null) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention()
+ ", but you need to be in a voice channel in order to add songs to the queue.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
final Member selfMember = guild.getSelfMember();
if (!selfMember.hasPermission(targetChannel, Permission.VOICE_CONNECT)
|| !selfMember.hasPermission(targetChannel, Permission.VOICE_SPEAK)) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention()
+ ", but I do not have the required permissions to use your current voice channel.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
if (arguments.length == 0) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention()
+ ", but you need to provide a search query or a link to a specific track or playlist.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
final TrackScheduler scheduler = AudioUtil.getTrackScheduler(guild);
if (scheduler.isQueueLocked() && !PermissionManager.isWhitelisted(executor)) {
return new CommandResponse("lock", "Sorry " + executor.getAsMention() + ", but the queue is locked.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
if (scheduler.isQueueFull()) {
return new CommandResponse("card_box", "Sorry " + executor.getAsMention() + ", but the queue is full.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
boolean isLink;
try {
new URL(arguments[0]);
isLink = true;
} catch (final MalformedURLException exception) {
isLink = false;
}
final StringBuilder query;
if (isLink) {
query = new StringBuilder(arguments[0]);
} else {
query = new StringBuilder("ytsearch:");
for (final String part : arguments) {
query.append(part).append(" ");
}
}
AudioUtil.findTrack(query.toString(), executor, channel);
return null;
}
}

View File

@@ -0,0 +1,55 @@
package dev.logal.logalbot.commands.audio;
// 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.TimeUnit;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.TrackUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
public final class Queue implements Command {
@Override
public final CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel) {
final Guild guild = channel.getGuild();
if (AudioUtil.getTrackScheduler(guild).isQueueEmpty()) {
return new CommandResponse("information_source", executor.getAsMention() + ", the queue is empty.");
}
final CommandResponse response = new CommandResponse("bookmark_tabs",
executor.getAsMention() + ", the following tracks are in the queue:");
final int page;
if (arguments.length == 0) {
page = 1;
} else {
try {
page = Integer.parseInt(arguments[0]);
} catch (final NumberFormatException exception) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but the page number must be an integer.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
}
response.attachEmbed(
TrackUtil.generatePaginatedTrackListInfoEmbed(AudioUtil.getTrackScheduler(guild).getQueue(), page));
return response;
}
}

View File

@@ -0,0 +1,114 @@
package dev.logal.logalbot.commands.audio;
// 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.TimeUnit;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.vdurmont.emoji.EmojiManager;
import dev.logal.logalbot.audio.TrackScheduler;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.commands.ReactionCallback;
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 dev.logal.logalbot.utils.VoiceChannelUtil;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.Message;
import net.dv8tion.jda.core.entities.TextChannel;
public final class Remove implements Command {
@Override
public final CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel) {
final TrackScheduler scheduler = AudioUtil.getTrackScheduler(channel.getGuild());
if (scheduler.isQueueEmpty()) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but there are no tracks in the queue.").setDeletionDelay(10,
TimeUnit.SECONDS);
}
final Guild guild = channel.getGuild();
if (AudioUtil.isTrackLoaded(guild) && !VoiceChannelUtil.isInCurrentVoiceChannel(executor)) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but you need to be in voice channel `"
+ AudioUtil.getVoiceChannelConnectedTo(guild).getName()
+ "` in order to remove tracks from the queue.").setDeletionDelay(10, TimeUnit.SECONDS);
}
if (arguments.length == 0) {
if (!guild.getSelfMember().hasPermission(channel, Permission.MESSAGE_ADD_REACTION)) {
return new CommandResponse("no_entry_sign", "Sorry " + executor.getAsMention()
+ ", but I do not have the required permissions to create a reaction selection dialog in this text channel.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
final CommandResponse response = new CommandResponse("question",
executor.getAsMention() + ", which track would you like to remove from the top of the queue?");
response.attachEmbed(TrackUtil.generateTrackListInfoEmbed(scheduler.getQueue(), true));
for (int i = 0; i < scheduler.getQueue().size(); i++) {
final int trackNumber = i + 1;
if (trackNumber == 11) {
break;
}
response.addReactionCallback(EmojiManager.getForAlias("" + trackNumber), new ReactionCallback() {
@Override
public final void run(final Member reactor, final long messageID) {
ReactionCallbackManager.unregisterMessage(messageID);
channel.getMessageById(messageID).queue(this::deleteMessage);
CommandManager.executeCommand(("remove " + trackNumber).split(" "), reactor, channel);
}
private void deleteMessage(final Message message) {
message.delete().queue();
}
});
}
response.setReactionCallbackTarget(executor);
response.setDeletionDelay(30, TimeUnit.SECONDS);
return response;
}
final int index;
try {
index = Integer.parseInt(arguments[0]);
} catch (final NumberFormatException exception) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but the index must be an integer.").setDeletionDelay(10,
TimeUnit.SECONDS);
}
try {
final AudioTrack removedTrack = scheduler.getQueue().get(index - 1);
scheduler.removeFromQueue(index - 1);
final CommandResponse response = new CommandResponse("scissors",
executor.getAsMention() + " removed the following track from the queue:");
response.attachEmbed(TrackUtil.generateTrackInfoEmbed(removedTrack));
return response;
} catch (final IndexOutOfBoundsException exception) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but that index is outside the bounds of the queue.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
}
}

View File

@@ -0,0 +1,46 @@
package dev.logal.logalbot.commands.audio;
// 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.TimeUnit;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.VoiceChannelUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
public final class Reset implements Command {
@Override
public final CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel) {
final Guild guild = channel.getGuild();
if (AudioUtil.isTrackLoaded(guild) && !VoiceChannelUtil.isInCurrentVoiceChannel(executor)) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but you need to be in voice channel `"
+ AudioUtil.getVoiceChannelConnectedTo(guild).getName()
+ "` in order to reset the audio player.").setDeletionDelay(10, TimeUnit.SECONDS);
}
AudioUtil.getTrackScheduler(guild).clearQueue();
if (AudioUtil.isTrackLoaded(guild)) {
AudioUtil.stopTrack(guild);
}
return new CommandResponse("recycle",
executor.getAsMention() + " has stopped the current track and reset the queue.");
}
}

View File

@@ -0,0 +1,49 @@
package dev.logal.logalbot.commands.audio;
// 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.TimeUnit;
import dev.logal.logalbot.audio.TrackScheduler;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.VoiceChannelUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
public final class Shuffle implements Command {
@Override
public final CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel) {
final Guild guild = channel.getGuild();
final TrackScheduler scheduler = AudioUtil.getTrackScheduler(guild);
if (scheduler.isQueueEmpty()) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but there are no tracks in the queue.").setDeletionDelay(10,
TimeUnit.SECONDS);
}
if (AudioUtil.isTrackLoaded(guild) && !VoiceChannelUtil.isInCurrentVoiceChannel(executor)) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but you need to be in voice channel `"
+ AudioUtil.getVoiceChannelConnectedTo(guild).getName()
+ "` in order to shuffle the queue.").setDeletionDelay(10, TimeUnit.SECONDS);
}
scheduler.shuffleQueue();
return new CommandResponse("salad", executor.getAsMention() + " shuffled the queue.");
}
}

View File

@@ -0,0 +1,73 @@
package dev.logal.logalbot.commands.audio;
// 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.TimeUnit;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.SkipTracker;
import dev.logal.logalbot.utils.TrackUtil;
import dev.logal.logalbot.utils.VoiceChannelUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
public final class Skip implements Command {
@Override
public final CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel) {
final Guild guild = channel.getGuild();
if (!AudioUtil.isTrackLoaded(guild)) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention()
+ ", but there must be a track playing in order to vote to skip it.").setDeletionDelay(10,
TimeUnit.SECONDS);
}
if (!VoiceChannelUtil.isInCurrentVoiceChannel(executor)) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but you must be in voice channel `"
+ AudioUtil.getVoiceChannelConnectedTo(channel.getGuild()).getName()
+ "` in order to vote to skip the current track.").setDeletionDelay(10, TimeUnit.SECONDS);
}
if (SkipTracker.hasVoted(executor)) {
return new CommandResponse("no_entry_sign",
"You have already voted to skip the current track " + executor.getAsMention() + ".")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
SkipTracker.registerVote(executor);
if (SkipTracker.shouldSkip(guild)) {
final AudioTrack skippedTrack = AudioUtil.getLoadedTrack(guild);
AudioUtil.getTrackScheduler(guild).skipCurrentTrack();
final CommandResponse response = new CommandResponse("gun",
executor.getAsMention() + " was the last required vote. The following track has been skipped:");
response.attachEmbed(TrackUtil.generateTrackInfoEmbed(skippedTrack));
return response;
} else {
if (SkipTracker.getRemainingRequired(guild) == 1) {
return new CommandResponse("x", executor.getAsMention() + " has voted to skip the current track. "
+ SkipTracker.getRemainingRequired(guild) + " more vote is needed.");
} else {
return new CommandResponse("x", executor.getAsMention() + " has voted to skip the current track. "
+ SkipTracker.getRemainingRequired(guild) + " more votes are needed.");
}
}
}
}

View File

@@ -0,0 +1,81 @@
package dev.logal.logalbot.commands.audio;
// 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.TimeUnit;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.AudioUtil;
import dev.logal.logalbot.utils.VoiceChannelUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
public final class Volume implements Command {
@Override
public final CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel) {
final Guild guild = channel.getGuild();
if (!AudioUtil.isTrackLoaded(guild)) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention()
+ ", but there must be a track playing in order to change the volume of the track player.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
if (!VoiceChannelUtil.isInCurrentVoiceChannel(executor)) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but you must be in voice channel `"
+ AudioUtil.getVoiceChannelConnectedTo(guild).getName()
+ "` in order to change the volume of the track player.").setDeletionDelay(10,
TimeUnit.SECONDS);
}
if (arguments.length == 0) {
if (AudioUtil.getVolume(guild) >= 75) {
return new CommandResponse("loud_sound", executor.getAsMention() + ", the volume is currently set to `"
+ AudioUtil.getVolume(guild) + "%`.");
} else {
return new CommandResponse("sound", executor.getAsMention() + ", the volume is currently set to `"
+ AudioUtil.getVolume(guild) + "%`.");
}
}
final int volume;
try {
volume = Integer.parseInt(arguments[0]);
} catch (final NumberFormatException exception) {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but the volume must be an integer.").setDeletionDelay(10,
TimeUnit.SECONDS);
}
if (volume <= 150 && volume >= 1) {
final int oldVolume = AudioUtil.getVolume(guild);
AudioUtil.setVolume(guild, volume);
if (volume >= 75) {
return new CommandResponse("loud_sound",
executor.getAsMention() + " set the volume from `" + oldVolume + "%` to `" + volume + "%`.");
} else {
return new CommandResponse("sound",
executor.getAsMention() + " set the volume from `" + oldVolume + "%` to `" + volume + "%`.");
}
} else {
return new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but the volume must be between 1% and 150%.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
}
}

View File

@@ -0,0 +1,73 @@
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.util.ArrayList;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import com.vdurmont.emoji.EmojiManager;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import dev.logal.logalbot.utils.StringUtil;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
public final class EightBall implements Command {
private final ArrayList<String> responses = new ArrayList<>(20);
private final Random rng = new Random();
public EightBall() {
responses.add("It is certain");
responses.add("It is decidedly so");
responses.add("Without a doubt");
responses.add("Yes - definitely");
responses.add("You may rely on it");
responses.add("As I see it, yes");
responses.add("Most likely");
responses.add("Outlook good");
responses.add("Yes");
responses.add("Signs point to yes");
responses.add("Reply hazy, try again");
responses.add("Ask again later");
responses.add("Better not tell you now");
responses.add("Cannot predict now");
responses.add("Concentrate and ask again");
responses.add("Don't count on it");
responses.add("My reply is no");
responses.add("My sources say no");
responses.add("Outlook not so good");
responses.add("Very doubtful");
}
@Override
public final CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel) {
if (arguments.length == 0) {
return new CommandResponse("no_entry_sign",
"Sorry, " + executor.getAsMention() + ", but you need to supply a question for the Magic 8 Ball.")
.setDeletionDelay(10, TimeUnit.SECONDS);
}
final String question = StringUtil.sanitizeCodeBlock(String.join(" ", arguments));
return new CommandResponse("question",
executor.getAsMention() + " asked the Magic 8 Ball: `" + question + "`\n"
+ EmojiManager.getForAlias("8ball").getUnicode() + " The Magic 8 Ball responds: *"
+ responses.get(rng.nextInt(responses.size())) + "*.");
}
}

View File

@@ -0,0 +1,28 @@
package dev.logal.logalbot.commands.general;
// 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.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 About implements Command {
@Override
public CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel) {
return new CommandResponse("wave", "Hello " + executor.getAsMention()
+ "! I'm LogalBot, a bot created by LogalDeveloper. https://logal.dev/");
}
}

View File

@@ -0,0 +1,34 @@
package dev.logal.logalbot.commands.general;
// 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.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import net.dv8tion.jda.core.EmbedBuilder;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
public final class Help implements Command {
@Override
public CommandResponse execute(final String[] arguments, final Member executor, final TextChannel channel) {
final CommandResponse response = new CommandResponse("directory",
executor.getAsMention() + ", here are some helpful links:");
final EmbedBuilder builder = new EmbedBuilder();
builder.addField("Command Reference", "https://logal.dev/projects/logalbot/command-reference/", false);
builder.addField("Git Repository", "https://git.logal.dev/LogalDeveloper/LogalBot", false);
response.attachEmbed(builder.build());
return response;
}
}

View File

@@ -0,0 +1,32 @@
package dev.logal.logalbot.events;
// 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.events.message.guild.react.GuildMessageReactionAddEvent;
import net.dv8tion.jda.core.hooks.ListenerAdapter;
import net.dv8tion.jda.core.utils.Checks;
public final class GuildMessageReactionAdd extends ListenerAdapter {
@Override
public final void onGuildMessageReactionAdd(final GuildMessageReactionAddEvent event) {
Checks.notNull(event, "Event");
if (!event.getUser().equals(event.getJDA().getSelfUser())) {
ReactionCallbackManager.executeCallback(event.getMessageIdLong(), event.getMember(),
event.getReactionEmote().getName());
}
}
}

View File

@@ -0,0 +1,77 @@
package dev.logal.logalbot.events;
// 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.Arrays;
import java.util.List;
import dev.logal.logalbot.utils.CommandManager;
import dev.logal.logalbot.utils.DataManager;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.Message;
import net.dv8tion.jda.core.entities.SelfUser;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.events.message.guild.GuildMessageReceivedEvent;
import net.dv8tion.jda.core.hooks.ListenerAdapter;
import net.dv8tion.jda.core.utils.Checks;
public final class GuildMessageReceived extends ListenerAdapter {
@Override
public final void onGuildMessageReceived(final GuildMessageReceivedEvent event) {
Checks.notNull(event, "Event");
final Guild guild = event.getGuild();
final Member self = guild.getSelfMember();
final TextChannel channel = event.getChannel();
final Message message = event.getMessage();
if (event.getAuthor().isBot() || message.isTTS() || !self.hasPermission(channel, Permission.MESSAGE_WRITE)
|| !self.hasPermission(channel, Permission.MESSAGE_EMBED_LINKS)) {
return;
}
final String content = message.getContentRaw();
final SelfUser selfUser = event.getJDA().getSelfUser();
final List<Member> mentionedMembers = message.getMentionedMembers();
final Member author = event.getMember();
if (mentionedMembers.size() >= 1 && mentionedMembers.get(0).getUser().getIdLong() == selfUser.getIdLong()
&& (content.startsWith(self.getAsMention()) || content.startsWith(selfUser.getAsMention()))) {
final String[] rawCommand = content.split(" ");
final String[] command = Arrays.copyOfRange(rawCommand, 1, rawCommand.length);
if (command.length >= 1) {
if (self.hasPermission(channel, Permission.MESSAGE_MANAGE)) {
message.delete().reason("LogalBot Command Execution").queue();
}
CommandManager.executeCommand(command, author, channel);
}
} else {
final String commandCharacter = DataManager.getGuildValue(guild, "commandCharacter");
if (commandCharacter == null) {
return;
}
final char commandChar = commandCharacter.toCharArray()[0];
if (content.length() > 1 && content.charAt(0) == commandChar) {
final String[] command = content.substring(1).split(" ");
if (self.hasPermission(channel, Permission.MESSAGE_MANAGE)) {
message.delete().reason("LogalBot Command Execution").queue();
}
CommandManager.executeCommand(command, author, channel);
}
}
}
}

View File

@@ -0,0 +1,29 @@
package dev.logal.logalbot.events;
// 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.events.guild.GuildReadyEvent;
import net.dv8tion.jda.core.hooks.ListenerAdapter;
import net.dv8tion.jda.core.utils.Checks;
public final class GuildReady extends ListenerAdapter {
@Override
public final void onGuildReady(final GuildReadyEvent event) {
Checks.notNull(event, "Event");
AudioUtil.initialize(event.getGuild());
}
}

View File

@@ -0,0 +1,65 @@
package dev.logal.logalbot.events;
// 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.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dev.logal.logalbot.utils.AudioUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.VoiceChannel;
import net.dv8tion.jda.core.events.guild.voice.GuildVoiceLeaveEvent;
import net.dv8tion.jda.core.hooks.ListenerAdapter;
import net.dv8tion.jda.core.utils.Checks;
public final class GuildVoiceLeave extends ListenerAdapter {
private static final Logger logger = LoggerFactory.getLogger(GuildVoiceLeave.class);
@Override
public final void onGuildVoiceLeave(final GuildVoiceLeaveEvent event) {
Checks.notNull(event, "Event");
final Guild guild = event.getGuild();
if (!AudioUtil.isAudioConnectionOpen(guild)) {
return;
}
if (!AudioUtil.isTrackLoaded(guild)) {
return;
}
final Member member = event.getMember();
if (member.getUser().equals(event.getJDA().getSelfUser())) {
return;
}
final VoiceChannel leftChannel = event.getChannelLeft();
if (leftChannel.equals(AudioUtil.getVoiceChannelConnectedTo(guild))) {
final List<Member> members = leftChannel.getMembers();
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);
}
}
}
}
}

View File

@@ -0,0 +1,65 @@
package dev.logal.logalbot.events;
// 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.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dev.logal.logalbot.utils.AudioUtil;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.VoiceChannel;
import net.dv8tion.jda.core.events.guild.voice.GuildVoiceMoveEvent;
import net.dv8tion.jda.core.hooks.ListenerAdapter;
import net.dv8tion.jda.core.utils.Checks;
public final class GuildVoiceMove extends ListenerAdapter {
private static final Logger logger = LoggerFactory.getLogger(GuildVoiceMove.class);
@Override
public final void onGuildVoiceMove(final GuildVoiceMoveEvent event) {
Checks.notNull(event, "Event");
final Guild guild = event.getGuild();
if (!AudioUtil.isAudioConnectionOpen(guild)) {
return;
}
if (!AudioUtil.isTrackLoaded(guild)) {
return;
}
final Member member = event.getMember();
if (member.getUser().equals(event.getJDA().getSelfUser())) {
return;
}
final VoiceChannel leftChannel = event.getChannelLeft();
if (leftChannel.equals(AudioUtil.getVoiceChannelConnectedTo(guild))) {
final List<Member> members = leftChannel.getMembers();
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);
}
}
}
}
}

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 IdleDisconnectTask implements Runnable {
private final Guild guild;
public IdleDisconnectTask(final Guild guild) {
Checks.notNull(guild, "Guild");
this.guild = guild;
}
@Override
public final void run() {
AudioUtil.closeAudioConnection(guild);
}
}

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.ReactionCallbackManager;
import net.dv8tion.jda.core.entities.Message;
import net.dv8tion.jda.core.utils.Checks;
public final class MessageDeleteTask implements Runnable {
private final Message messageToDelete;
public MessageDeleteTask(final Message message) {
Checks.notNull(message, "Message");
this.messageToDelete = message;
}
@Override
public final void run() {
ReactionCallbackManager.unregisterMessage(messageToDelete.getIdLong());
messageToDelete.delete().queue();
}
}

View File

@@ -0,0 +1,173 @@
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.HashMap;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dev.logal.logalbot.audio.AudioPlayerSendHandler;
import dev.logal.logalbot.audio.TrackLoadHandler;
import dev.logal.logalbot.audio.TrackScheduler;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.entities.VoiceChannel;
import net.dv8tion.jda.core.managers.AudioManager;
import net.dv8tion.jda.core.utils.Checks;
public final class AudioUtil {
private static final Logger logger = LoggerFactory.getLogger(AudioUtil.class);
private static final AudioPlayerManager playerManager = new DefaultAudioPlayerManager();
private static final HashMap<String, AudioPlayer> players = new HashMap<>();
private static final HashMap<String, TrackScheduler> schedulers = new HashMap<>();
private AudioUtil() {
// Static access only.
}
public static final void initializePlayerManager() {
AudioSourceManagers.registerRemoteSources(playerManager);
}
public static final void initialize(final Guild guild) {
Checks.notNull(guild, "Guild");
players.put(guild.getId(), playerManager.createPlayer());
schedulers.put(guild.getId(), new TrackScheduler(guild));
players.get(guild.getId()).addListener(schedulers.get(guild.getId()));
try {
setVolume(guild, Integer.parseInt(DataManager.getGuildValue(guild, "defaultVolume")));
} catch (final NumberFormatException exception) {
setVolume(guild, 10);
}
getTrackScheduler(guild).setQueueLocked(false);
setPausedState(guild, false);
logger.info("Audio environment initialized for " + guild.getName() + " (" + guild.getId() + ").");
}
public static final void openAudioConnection(final VoiceChannel channel) {
Checks.notNull(channel, "Channel");
final Guild guild = channel.getGuild();
final AudioManager audioManager = guild.getAudioManager();
audioManager.setSendingHandler(new AudioPlayerSendHandler(players.get(guild.getId())));
audioManager.openAudioConnection(channel);
}
public static final void closeAudioConnection(final Guild guild) {
Checks.notNull(guild, "Guild");
guild.getAudioManager().closeAudioConnection();
}
public static final VoiceChannel getVoiceChannelConnectedTo(final Guild guild) {
Checks.notNull(guild, "Guild");
return guild.getAudioManager().getConnectedChannel();
}
public static final boolean isAudioConnectionOpen(final Guild guild) {
Checks.notNull(guild, "Guild");
return guild.getAudioManager().isConnected();
}
public static final void playTrack(final Guild guild, final AudioTrack track) {
Checks.notNull(guild, "Guild");
Checks.notNull(track, "Track");
players.get(guild.getId()).playTrack(track);
}
public static final void stopTrack(final Guild guild) {
Checks.notNull(guild, "Guild");
players.get(guild.getId()).stopTrack();
}
public static final boolean isTrackLoaded(final Guild guild) {
Checks.notNull(guild, "Guild");
return !(getLoadedTrack(guild) == null);
}
public static final AudioTrack getLoadedTrack(final Guild guild) {
Checks.notNull(guild, "Guild");
return players.get(guild.getId()).getPlayingTrack();
}
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.getId()).setPaused(pausedState);
if (pausedState) {
logger.info("The audio player was paused in " + guild.getName() + " (" + guild.getId() + ").");
} else {
logger.info("The audio player was resumed in " + guild.getName() + " (" + guild.getId() + ").");
}
}
public static final boolean isPlayerPaused(final Guild guild) {
Checks.notNull(guild, "Guild");
return players.get(guild.getId()).isPaused();
}
public static final void setVolume(final Guild guild, final int volume) {
Checks.notNull(guild, "Guild");
Checks.positive(volume, "Volume");
players.get(guild.getId()).setVolume(volume);
logger.info("The audio player's volume was set to " + getVolume(guild) + "% in " + guild.getName() + " ("
+ guild.getId() + ").");
}
public static final int getVolume(final Guild guild) {
Checks.notNull(guild, "Guild");
return players.get(guild.getId()).getVolume();
}
public static final void findTrack(final String query, final Member requester, final TextChannel channel) {
Checks.notNull(query, "Query");
Checks.notNull(requester, "Requester");
Checks.notNull(channel, "Channel");
playerManager.loadItem(query, new TrackLoadHandler(requester, channel));
}
public static final TrackScheduler getTrackScheduler(final Guild guild) {
Checks.notNull(guild, "Guild");
return schedulers.get(guild.getId());
}
}

View File

@@ -0,0 +1,127 @@
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.Arrays;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import dev.logal.logalbot.commands.Command;
import dev.logal.logalbot.commands.CommandResponse;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.utils.Checks;
public final class CommandManager {
private static final Logger logger = LoggerFactory.getLogger(CommandManager.class);
private static final HashMap<String, Command> commandMap = new HashMap<>();
private static final HashMap<String, Boolean> permissionMap = new HashMap<>();
private static final HashMap<String, String> aliasMap = new HashMap<>();
private CommandManager() {
// Static access only.
}
public static final void executeCommand(final String[] command, final Member executor, final TextChannel channel) {
Checks.notEmpty(command, "Command");
Checks.notNull(executor, "Executor");
Checks.notNull(channel, "Channel");
String commandName = command[0].toLowerCase();
final String[] arguments = Arrays.copyOfRange(command, 1, command.length);
final Guild guild = channel.getGuild();
CommandResponse response;
if (aliasMap.containsKey(commandName)) {
commandName = aliasMap.get(commandName);
}
logger.info(executor.getEffectiveName() + " (" + executor.getUser().getId() + ") executed command '"
+ commandName + "' with arguments '" + String.join(" ", arguments) + "' in " + guild.getName() + " ("
+ guild.getId() + ").");
if (!commandMap.containsKey(commandName)) {
response = new CommandResponse("question",
"Sorry " + executor.getAsMention() + ", but I do not know what that command is.");
response.setDeletionDelay(10, TimeUnit.SECONDS);
response.sendResponse(channel);
return;
}
if (permissionMap.get(commandName) && !PermissionManager.isWhitelisted(executor)) {
logger.info(executor.getEffectiveName() + " (" + executor.getUser().getId()
+ ") was denied access to a command due to not being whitelisted in " + guild.getName() + " ("
+ guild.getId() + ").");
response = new CommandResponse("no_entry_sign",
"Sorry " + executor.getAsMention() + ", but you are not allowed to use this command.");
response.setDeletionDelay(10, TimeUnit.SECONDS);
response.sendResponse(channel);
return;
}
try {
response = commandMap.get(commandName).execute(arguments, executor, channel);
} catch (final Throwable exception) {
logger.error(
"An error occurred while executing a command for " + executor.getEffectiveName() + " ("
+ executor.getUser().getId() + ") in " + guild.getName() + " (" + guild.getId() + ").",
exception);
response = new CommandResponse("sos",
"Sorry " + executor.getAsMention() + ", but an error occurred while executing your command!!");
response.setDeletionDelay(10, TimeUnit.SECONDS);
response.sendResponse(channel);
return;
}
if (response != null) {
response.sendResponse(channel);
}
}
public static final void registerCommand(final String command, final Command commandObject,
final boolean mustBeWhitelisted) {
Checks.notEmpty(command, "Command");
Checks.notNull(commandObject, "Command object");
Checks.notNull(mustBeWhitelisted, "Whitelist requirement");
commandMap.put(command, commandObject);
permissionMap.put(command, mustBeWhitelisted);
}
public static final void registerCommandAlias(final String alias, final String command) {
Checks.notEmpty(alias, "Alias");
Checks.notEmpty(command, "Command");
aliasMap.put(alias, command);
}
public static final void unregisterCommand(final String command) {
Checks.notEmpty(command, "Command");
commandMap.remove(command);
permissionMap.remove(command);
}
public static final void unregisterCommandAlias(final String command) {
Checks.notEmpty(command, "Command");
aliasMap.remove(command);
}
}

View File

@@ -0,0 +1,126 @@
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.User;
import net.dv8tion.jda.core.utils.Checks;
import redis.clients.jedis.Jedis;
public final class DataManager {
private static final Logger logger = LoggerFactory.getLogger(DataManager.class);
private static final String host = System.getenv("REDIS_HOST");
private static final String password = System.getenv("REDIS_PASSWORD");
private static final String databaseNumber = System.getenv("REDIS_DATABASE");
private static Jedis jedis = new Jedis();
private DataManager() {
// Static access only.
}
public static final void verifyConnection() {
if (!jedis.isConnected()) {
jedis = new Jedis(host);
if (password != null) {
jedis.auth(password);
}
if (databaseNumber != null) {
final int num = Integer.parseInt(databaseNumber);
jedis.select(num);
}
}
}
public static final void runMigrations() {
if (jedis.get("schemaVersion") == null) {
logger.info("Migrating schema to version 1...");
jedis.set("schemaVersion", "1");
logger.info("Migration to schema version 1 complete.");
}
}
public static final String getUserValue(final Member member, final String key) {
Checks.notNull(member, "Member");
Checks.notEmpty(key, "Key");
return jedis.get("g" + member.getGuild().getId() + ":u" + member.getUser().getId() + ":" + key);
}
public static final void setUserValue(final Member member, final String key, final String value) {
Checks.notNull(member, "Member");
Checks.notEmpty(key, "Key");
Checks.notEmpty(value, "Value");
jedis.set("g" + member.getGuild().getId() + ":u" + member.getUser().getId() + ":" + key, value);
}
public static final void deleteUserKey(final Member member, final String key) {
Checks.notNull(member, "Member");
Checks.notEmpty(key, "Key");
jedis.del("g" + member.getGuild().getId() + ":u" + member.getUser().getId() + ":" + key);
}
public static final String getGlobalUserValue(final User user, final String key) {
Checks.notNull(user, "User");
Checks.notEmpty(key, "Key");
return jedis.get("u" + user.getId() + ":" + key);
}
public static final void setGlobalUserValue(final User user, final String key, final String value) {
Checks.notNull(user, "User");
Checks.notEmpty(key, "Key");
Checks.notEmpty(value, "Value");
jedis.set("u" + user.getId() + ":" + key, value);
}
public static final void deleteGlobalUserKey(final User user, final String key) {
Checks.notNull(user, "User");
Checks.notEmpty(key, "Key");
jedis.del("u" + user.getId() + ":" + key);
}
public static final String getGuildValue(final Guild guild, final String key) {
Checks.notNull(guild, "Guild");
Checks.notEmpty(key, "Key");
return jedis.get("g" + guild.getId() + ":" + key);
}
public static final void setGuildValue(final Guild guild, final String key, final String value) {
Checks.notNull(guild, "Guild");
Checks.notEmpty(key, "Key");
Checks.notEmpty(value, "Value");
jedis.set("g" + guild.getId() + ":" + key, value);
}
public static final void deleteGuildKey(final Guild guild, final String key) {
Checks.notNull(guild, "Guild");
Checks.notEmpty(key, "Key");
jedis.del("g" + guild.getId() + ":" + key);
}
}

View File

@@ -0,0 +1,60 @@
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.utils.Checks;
public final class PermissionManager {
private static final Logger logger = LoggerFactory.getLogger(PermissionManager.class);
private PermissionManager() {
// Static access only.
}
public static final boolean isWhitelisted(final Member member) {
Checks.notNull(member, "Member");
if (member.hasPermission(Permission.ADMINISTRATOR)) {
return true;
}
if (DataManager.getUserValue(member, "whitelisted") == null) {
DataManager.setUserValue(member, "whitelisted", "false");
}
return DataManager.getUserValue(member, "whitelisted").equals("true");
}
public static final void addToWhitelist(final Member member) {
Checks.notNull(member, "Member");
DataManager.setUserValue(member, "whitelisted", "true");
logger.info(member.getEffectiveName() + " (" + member.getUser().getId() + ") was added to the whitelist in "
+ member.getGuild().getName() + " (" + member.getGuild().getId() + ").");
}
public static final void removeFromWhitelist(final Member member) {
Checks.notNull(member, "Member");
DataManager.setUserValue(member, "whitelisted", "false");
logger.info(member.getEffectiveName() + " (" + member.getUser().getId() + ") was removed from the whitelist in "
+ member.getGuild().getName() + " (" + member.getGuild().getId() + ").");
}
}

View File

@@ -0,0 +1,80 @@
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.HashMap;
import com.vdurmont.emoji.Emoji;
import com.vdurmont.emoji.EmojiManager;
import dev.logal.logalbot.commands.ReactionCallback;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.Message;
import net.dv8tion.jda.core.entities.User;
import net.dv8tion.jda.core.utils.Checks;
public final class ReactionCallbackManager {
private static final HashMap<Long, HashMap<Emoji, ReactionCallback>> callbackDictionary = new HashMap<>();
private static final HashMap<Long, Long> targetDictionary = new HashMap<>();
private ReactionCallbackManager() {
// Static access only.
}
public static final void registerCallback(final Message message, final Emoji emoji,
final ReactionCallback callback) {
Checks.notNull(message, "Message");
Checks.notNull(emoji, "Emoji");
Checks.notNull(callback, "Callback");
if (!callbackDictionary.containsKey(message.getIdLong())) {
callbackDictionary.put(message.getIdLong(), new HashMap<>());
}
callbackDictionary.get(message.getIdLong()).put(emoji, callback);
}
public static final void setCallbackTarget(final User user, final Message message) {
Checks.notNull(user, "User");
Checks.notNull(message, "Messsage");
targetDictionary.put(message.getIdLong(), user.getIdLong());
}
public static final void unregisterMessage(final long messageID) {
Checks.notNull(messageID, "Message ID");
callbackDictionary.remove(messageID);
targetDictionary.remove(messageID);
}
public static final void executeCallback(final long messageID, final Member reactor, final String emoji) {
Checks.notNull(messageID, "Message ID");
Checks.notNull(reactor, "Reactor");
Checks.notEmpty(emoji, "Emoji");
if (callbackDictionary.containsKey(messageID)) {
if (targetDictionary.containsKey(messageID)
&& !targetDictionary.get(messageID).equals(reactor.getUser().getIdLong())) {
return;
}
final Emoji parsedEmoji = EmojiManager.getByUnicode(emoji);
if (callbackDictionary.get(messageID).containsKey(parsedEmoji)) {
callbackDictionary.get(messageID).get(parsedEmoji).run(reactor, messageID);
}
}
}
}

View File

@@ -0,0 +1,48 @@
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

@@ -0,0 +1,77 @@
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.ArrayList;
import java.util.HashMap;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.utils.Checks;
public final class SkipTracker {
private static final HashMap<Long, ArrayList<Long>> skipVotesDictionary = new HashMap<>();
private SkipTracker() {
// Static access only.
}
public static final void registerVote(final Member member) {
Checks.notNull(member, "Member");
final Guild guild = member.getGuild();
if (!skipVotesDictionary.containsKey(guild.getIdLong())) {
resetVotes(guild);
}
final ArrayList<Long> registeredVotes = skipVotesDictionary.get(guild.getIdLong());
if (!registeredVotes.contains(member.getUser().getIdLong())) {
registeredVotes.add(member.getUser().getIdLong());
}
}
public static final boolean hasVoted(final Member member) {
Checks.notNull(member, "Member");
return skipVotesDictionary.get(member.getGuild().getIdLong()).contains(member.getUser().getIdLong());
}
public static final void resetVotes(final Guild guild) {
Checks.notNull(guild, "Guild");
final long guildID = guild.getIdLong();
if (skipVotesDictionary.containsKey(guildID)) {
skipVotesDictionary.get(guildID).clear();
} else {
skipVotesDictionary.put(guildID, new ArrayList<>());
}
}
public static final int getRemainingRequired(final Guild guild) {
Checks.notNull(guild, "Guild");
final int listeners = (int) AudioUtil.getVoiceChannelConnectedTo(guild).getMembers().stream()
.filter(member -> !member.getUser().isBot()).count();
final int required = (int) Math.ceil(listeners * .55);
return (required - skipVotesDictionary.get(guild.getIdLong()).size());
}
public static final boolean shouldSkip(final Guild guild) {
Checks.notNull(guild, "Guild");
return getRemainingRequired(guild) <= 0;
}
}

View File

@@ -0,0 +1,45 @@
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 net.dv8tion.jda.core.utils.Checks;
public final class StringUtil {
private StringUtil() {
// Static access only.
}
public static final String sanitize(final String string) {
Checks.notNull(string, "String");
return string.replaceAll("([_*`<@>~|])", "\\\\$1").replaceAll("[\r\n]", "");
}
public static final String sanitizeCodeBlock(final String string) {
Checks.notNull(string, "String");
return string.replaceAll("[`]", "'").replaceAll("[\r\n]", "");
}
public static final String formatTime(final long milliseconds) {
Checks.notNull(milliseconds, "Milliseconds");
final long second = (milliseconds / 1000) % 60;
final long minute = (milliseconds / (1000 * 60)) % 60;
final long hour = (milliseconds / (1000 * 60 * 60)) % 24;
return String.format("%02d:%02d:%02d", hour, minute, second);
}
}

View File

@@ -0,0 +1,110 @@
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.List;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import net.dv8tion.jda.core.EmbedBuilder;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.MessageEmbed;
import net.dv8tion.jda.core.utils.Checks;
public final class TrackUtil {
private TrackUtil() {
// Static access only.
}
public static final MessageEmbed generateTrackInfoEmbed(final AudioTrack track) {
Checks.notNull(track, "Track");
final EmbedBuilder builder = new EmbedBuilder();
builder.addField(StringUtil.sanitize(track.getInfo().title),
StringUtil.sanitize(track.getInfo().author) + " - " + StringUtil.formatTime(track.getDuration()),
false);
return builder.build();
}
public static final MessageEmbed generateCurrentTrackInfoEmbed(final Guild guild) {
Checks.notNull(guild, "Guild");
final EmbedBuilder builder = new EmbedBuilder();
final AudioTrack track = AudioUtil.getLoadedTrack(guild);
builder.addField(
StringUtil.sanitize(track.getInfo().title), StringUtil.sanitize(track.getInfo().author) + " - "
+ StringUtil.formatTime(track.getPosition()) + "/" + StringUtil.formatTime(track.getDuration()),
false);
return builder.build();
}
public static final MessageEmbed generateTrackListInfoEmbed(final List<AudioTrack> 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++) {
if (i == 10) {
break;
}
final AudioTrack track = tracks.get(i);
if (numbered) {
builder.addField("**" + (i + 1) + ":** " + StringUtil.sanitize(track.getInfo().title),
StringUtil.sanitize(track.getInfo().author) + " - "
+ StringUtil.formatTime(track.getDuration()),
false);
} else {
builder.addField(StringUtil.sanitize(track.getInfo().title), StringUtil.sanitize(track.getInfo().author)
+ " - " + StringUtil.formatTime(track.getDuration()), false);
}
}
if (tracks.size() > 10) {
builder.setTitle("**Top 10 Tracks - " + (tracks.size() - 10) + " Not Shown**");
}
return builder.build();
}
public static final MessageEmbed generatePaginatedTrackListInfoEmbed(final List<AudioTrack> tracks, int page) {
Checks.notNull(tracks, "Tracks");
Checks.notNull(page, "Page");
final EmbedBuilder builder = new EmbedBuilder();
if (page < 1) {
page = 1;
}
final int pages = (int) Math.ceil(tracks.size() / 10d);
if (page > pages) {
page = pages;
}
page = page - 1;
final int start = page * 10;
final int end = start + 10;
for (int i = start; i < end && i < tracks.size(); i++) {
final AudioTrack track = tracks.get(i);
builder.addField("**" + (i + 1) + ":** " + StringUtil.sanitize(track.getInfo().title),
StringUtil.sanitize(track.getInfo().author) + " - " + StringUtil.formatTime(track.getDuration()),
false);
}
builder.setTitle("**" + tracks.size() + " Total Tracks - Page " + (page + 1) + "/" + pages + "**");
return builder.build();
}
}

View File

@@ -0,0 +1,42 @@
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 net.dv8tion.jda.core.entities.Member;
import net.dv8tion.jda.core.entities.VoiceChannel;
import net.dv8tion.jda.core.utils.Checks;
public final class VoiceChannelUtil {
private VoiceChannelUtil() {
// Static access only.
}
public static final VoiceChannel getVoiceChannelMemberIsConnectedTo(final Member member) {
Checks.notNull(member, "Member");
for (final VoiceChannel channel : member.getGuild().getVoiceChannels()) {
if (channel.getMembers().contains(member)) {
return channel;
}
}
return null;
}
public static final boolean isInCurrentVoiceChannel(final Member member) {
Checks.notNull(member, "Member");
return AudioUtil.getVoiceChannelConnectedTo(member.getGuild()).getMembers().contains(member);
}
}