updated package name

fixed soundcloud search url bug
fixed playlist permission bug
added info to playlists command
bumped versions
This commit is contained in:
John Grosh
2017-04-24 23:49:57 -04:00
parent 61c62032f2
commit 0f52bbd170
41 changed files with 173 additions and 166 deletions
@@ -0,0 +1,388 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot;
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 java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import com.jagrosh.jdautilities.commandclient.Command.Category;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jdautilities.waiter.EventWaiter;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.gui.GUI;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Role;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.entities.VoiceChannel;
import net.dv8tion.jda.core.events.ReadyEvent;
import net.dv8tion.jda.core.events.ShutdownEvent;
import net.dv8tion.jda.core.hooks.ListenerAdapter;
import net.dv8tion.jda.core.utils.PermissionUtil;
import net.dv8tion.jda.core.utils.SimpleLog;
import org.json.JSONException;
import org.json.JSONObject;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class Bot extends ListenerAdapter {
private final HashMap<String,Settings> settings;
private final AudioPlayerManager manager;
private final EventWaiter waiter;
private final ScheduledExecutorService threadpool;
private JDA jda;
private GUI gui;
//private GuildsPanel panel;
public final Category MUSIC = new Category("Music");
public final Category DJ = new Category("DJ", event ->
{
if(event.getAuthor().getId().equals(event.getClient().getOwnerId()))
return true;
if(event.getGuild()==null)
return true;
if(PermissionUtil.checkPermission(event.getGuild(), event.getMember(), Permission.MANAGE_SERVER))
return true;
Role dj = event.getGuild().getRoleById(getSettings(event.getGuild()).getRoleId());
return event.getMember().getRoles().contains(dj);
});
public final Category ADMIN = new Category("Admin", event ->
{
if(event.getAuthor().getId().equals(event.getClient().getOwnerId()))
return true;
if(event.getGuild()==null)
return true;
return PermissionUtil.checkPermission(event.getGuild(), event.getMember(), Permission.MANAGE_SERVER);
});
public final Category OWNER = new Category("Owner");
public Bot(EventWaiter waiter)
{
this.waiter = waiter;
this.settings = new HashMap<>();
manager = new DefaultAudioPlayerManager();
threadpool = Executors.newSingleThreadScheduledExecutor();
AudioSourceManagers.registerRemoteSources(manager);
try {
JSONObject loadedSettings = new JSONObject(new String(Files.readAllBytes(Paths.get("serversettings.json"))));
loadedSettings.keySet().forEach((id) -> {
JSONObject o = loadedSettings.getJSONObject(id);
settings.put(id, new Settings(
o.has("text_channel_id") ? o.getString("text_channel_id") : null,
o.has("voice_channel_id")? o.getString("voice_channel_id"): null,
o.has("dj_role_id") ? o.getString("dj_role_id") : null,
o.has("volume") ? o.getInt("volume") : 100,
o.has("default_playlist")? o.getString("default_playlist"): null));
});
} catch(IOException | JSONException e) {
SimpleLog.getLog("Settings").warn("Failed to load server settings: "+e);
}
}
public EventWaiter getWaiter()
{
return waiter;
}
public AudioPlayerManager getAudioManager()
{
return manager;
}
public int queueTrack(CommandEvent event, AudioTrack track)
{
return setUpHandler(event).addTrack(track, event.getAuthor());
}
public AudioHandler setUpHandler(CommandEvent event)
{
return setUpHandler(event.getGuild());
}
public AudioHandler setUpHandler(Guild guild)
{
AudioHandler handler;
if(guild.getAudioManager().getSendingHandler()==null)
{
AudioPlayer player = manager.createPlayer();
if(settings.containsKey(guild.getId()))
player.setVolume(settings.get(guild.getId()).getVolume());
handler = new AudioHandler(player, guild, this);
player.addListener(handler);
guild.getAudioManager().setSendingHandler(handler);
threadpool.scheduleWithFixedDelay(() -> updateTopic(guild,handler), 0, 5, TimeUnit.SECONDS);
}
else
handler = (AudioHandler)guild.getAudioManager().getSendingHandler();
return handler;
}
private void updateTopic(Guild guild, AudioHandler handler)
{
TextChannel tchan = guild.getTextChannelById(getSettings(guild).getTextId());
if(tchan!=null && PermissionUtil.checkPermission(tchan, guild.getSelfMember(), Permission.MANAGE_CHANNEL))
{
String otherText;
if(tchan.getTopic()==null || tchan.getTopic().isEmpty())
otherText = "\u200B";
else if(tchan.getTopic().contains("\u200B"))
otherText = tchan.getTopic().substring(tchan.getTopic().indexOf("\u200B"));
else
otherText = "\u200B\n "+tchan.getTopic();
String text = FormatUtil.formattedAudio(handler, guild.getJDA(), true)+otherText;
if(!text.equals(tchan.getTopic()))
tchan.getManager().setTopic(text).queue();
}
}
public void shutdown(){
manager.shutdown();
threadpool.shutdownNow();
jda.getGuilds().stream().forEach(g -> {
g.getAudioManager().closeAudioConnection();
AudioHandler ah = (AudioHandler)g.getAudioManager().getSendingHandler();
if(ah!=null)
{
ah.getQueue().clear();
ah.getPlayer().destroy();
updateTopic(g, ah);
}
});
jda.shutdown();
}
public void setGUI(GUI gui)
{
this.gui = gui;
}
@Override
public void onShutdown(ShutdownEvent event) {
if(gui!=null)
gui.dispose();
}
@Override
public void onReady(ReadyEvent event) {
this.jda = event.getJDA();
if(jda.getGuilds().isEmpty())
{
SimpleLog.getLog("MusicBot").warn("This bot is not on any guilds! Use the following link to add the bot to your guilds!");
SimpleLog.getLog("MusicBot").warn(event.getJDA().asBot().getInviteUrl(JMusicBot.RECOMMENDED_PERMS));
}
jda.getGuilds().forEach((guild) -> {
try
{
String defpl = getSettings(guild).getDefaultPlaylist();
if(defpl!=null)
{
if(setUpHandler(guild).playFromDefault())
guild.getAudioManager().openAudioConnection(guild.getVoiceChannelById(getSettings(guild).getVoiceId()));
}
}
catch(Exception ex) {System.err.println(ex);}
});
}
// settings
public Settings getSettings(Guild guild)
{
return settings.getOrDefault(guild.getId(), Settings.DEFAULT_SETTINGS);
}
public void setTextChannel(TextChannel channel)
{
Settings s = settings.get(channel.getGuild().getId());
if(s==null)
{
settings.put(channel.getGuild().getId(), new Settings(channel.getId(),null,null,100,null));
}
else
{
s.setTextId(channel.getId());
}
writeSettings();
}
public void setVoiceChannel(VoiceChannel channel)
{
Settings s = settings.get(channel.getGuild().getId());
if(s==null)
{
settings.put(channel.getGuild().getId(), new Settings(null,channel.getId(),null,100,null));
}
else
{
s.setVoiceId(channel.getId());
}
writeSettings();
}
public void setRole(Role role)
{
Settings s = settings.get(role.getGuild().getId());
if(s==null)
{
settings.put(role.getGuild().getId(), new Settings(null,null,role.getId(),100,null));
}
else
{
s.setRoleId(role.getId());
}
writeSettings();
}
public void setDefaultPlaylist(Guild guild, String playlist)
{
Settings s = settings.get(guild.getId());
if(s==null)
{
settings.put(guild.getId(), new Settings(null,null,null,100,playlist));
}
else
{
s.setDefaultPlaylist(playlist);
}
writeSettings();
}
public void setVolume(Guild guild, int volume)
{
Settings s = settings.get(guild.getId());
if(s==null)
{
settings.put(guild.getId(), new Settings(null,null,null,volume,null));
}
else
{
s.setVolume(volume);
}
writeSettings();
}
public void clearTextChannel(Guild guild)
{
Settings s = getSettings(guild);
if(s!=Settings.DEFAULT_SETTINGS)
{
if(s.getVoiceId()==null && s.getRoleId()==null)
settings.remove(guild.getId());
else
s.setTextId(null);
writeSettings();
}
}
public void clearVoiceChannel(Guild guild)
{
Settings s = getSettings(guild);
if(s!=Settings.DEFAULT_SETTINGS)
{
if(s.getTextId()==null && s.getRoleId()==null)
settings.remove(guild.getId());
else
s.setVoiceId(null);
writeSettings();
}
}
public void clearRole(Guild guild)
{
Settings s = getSettings(guild);
if(s!=Settings.DEFAULT_SETTINGS)
{
if(s.getVoiceId()==null && s.getTextId()==null)
settings.remove(guild.getId());
else
s.setRoleId(null);
writeSettings();
}
}
private void writeSettings()
{
JSONObject obj = new JSONObject();
settings.keySet().stream().forEach(key -> {
JSONObject o = new JSONObject();
Settings s = settings.get(key);
if(s.getTextId()!=null)
o.put("text_channel_id", s.getTextId());
if(s.getVoiceId()!=null)
o.put("voice_channel_id", s.getVoiceId());
if(s.getRoleId()!=null)
o.put("dj_role_id", s.getRoleId());
if(s.getVolume()!=100)
o.put("volume",s.getVolume());
if(s.getDefaultPlaylist()!=null)
o.put("default_playlist", s.getDefaultPlaylist());
obj.put(key, o);
});
try {
Files.write(Paths.get("serversettings.json"), obj.toString(4).getBytes());
} catch(IOException ex){
SimpleLog.getLog("Settings").warn("Failed to write to file: "+ex);
}
}
//gui stuff
/*public void registerPanel(GuildsPanel panel)
{
this.panel = panel;
threadpool.scheduleWithFixedDelay(() -> updatePanel(), 0, 5, TimeUnit.SECONDS);
}
public void updatePanel()
{
System.out.println("updating...");
Guild guild = jda.getGuilds().get(panel.getIndex());
panel.updatePanel((AudioHandler)guild.getAudioManager().getSendingHandler());
}
@Override
public void onGuildJoin(GuildJoinEvent event) {
if(panel!=null)
panel.updateList(event.getJDA().getGuilds());
}
@Override
public void onGuildLeave(GuildLeaveEvent event) {
if(panel!=null)
panel.updateList(event.getJDA().getGuilds());
}
@Override
public void onShutdown(ShutdownEvent event) {
((GUI)panel.getTopLevelAncestor()).dispose();
}*/
}
@@ -0,0 +1,213 @@
/*
* Copyright 2016 John Grosh (jagrosh)
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
import javax.swing.JOptionPane;
/**
*
* @author John Grosh (jagrosh)
*/
public class Config {
private boolean nogui;
private String prefix;
private String token;
private String owner;
private String success;
private String warning;
private String error;
private String game;
private String help;
public Config(boolean nogui)
{
this.nogui = nogui;
List<String> lines;
try {
lines = Files.readAllLines(Paths.get("config.txt"));
for(String line: lines)
{
String[] parts = line.split("=",2);
String key = parts[0].trim().toLowerCase();
String value = parts.length>1 ? parts[1].trim() : null;
switch(key)
{
case "token":
token = value;
break;
case "prefix":
prefix = value;
break;
case "owner":
owner = value;
break;
case "success":
success = value;
break;
case "warning":
warning = value;
break;
case "error":
error = value;
break;
case "game":
game = value;
break;
case "help":
help = value;
break;
}
}
} catch (IOException ex) {
alert("'config.txt' was not found!");
lines = new LinkedList<>();
}
boolean write = false;
if(token==null || token.isEmpty())
{
token = prompt("Please provide a bot token."
+ "\nInstructions for obtaining a token can be found here:"
+ "\nhttps://github.com/jagrosh/MusicBot/wiki/Getting-a-Bot-Token."
+ "\nBot Token: ");
if(token==null)
{
alert("No token provided! Exiting.");
System.exit(0);
}
else
{
lines.add("token="+token);
write = true;
}
}
if(owner==null || !owner.matches("\\d{17,20}"))
{
owner = prompt("Owner ID was missing, or the provided owner ID is not valid."
+ "\nPlease provide the User ID of the bot's owner."
+ "\nInstructions for obtaining your User ID can be found here:"
+ "\nhttps://github.com/jagrosh/MusicBot/wiki/Finding-Your-User-ID"
+ "\nOwner User ID: ");
if(owner==null || !owner.matches("\\d{17,20}"))
{
alert("Invalid User ID! Exiting.");
System.exit(0);
}
else
{
lines.add("owner="+owner);
write = true;
}
}
if(write)
{
StringBuilder builder = new StringBuilder();
lines.stream().forEach(s -> builder.append(s).append("\r\n"));
try {
Files.write(Paths.get("config.txt"), builder.toString().trim().getBytes());
} catch(IOException ex) {
alert("Failed to write new config options to config.txt: "+ex
+ "\nPlease make sure that the files are not on your desktop or some other restricted area.");
}
}
}
public String getPrefix()
{
return prefix;
}
public String getToken()
{
return token;
}
public String getOwnerId()
{
return owner;
}
public String getSuccess()
{
return success==null ? "\uD83C\uDFB6" : success;
}
public String getWarning()
{
return warning==null ? "\uD83D\uDCA1" : warning;
}
public String getError()
{
return error==null ? "\uD83D\uDEAB" : error;
}
public String getGame()
{
return game;
}
public String getHelp()
{
return help==null ? "help" : help;
}
public boolean getNoGui()
{
return nogui;
}
private void alert(String message)
{
if(nogui)
System.out.println("[WARNING] "+message);
else
{
try {
JOptionPane.showMessageDialog(null, message, "JMusicBot", JOptionPane.WARNING_MESSAGE);
} catch(Exception e) {
nogui = true;
alert("Switching to nogui mode. You can manually start in nogui mode by including the -nogui flag.");
alert(message);
}
}
}
private String prompt(String content)
{
if(nogui)
{
Scanner scanner = new Scanner(System.in);
System.out.println(content);
return scanner.next();
}
else
{
try {
return JOptionPane.showInputDialog(null, content, "JMusicBot", JOptionPane.WARNING_MESSAGE);
} catch(Exception e) {
nogui = true;
alert("Switching to nogui mode. You can manually start in nogui mode by including the -nogui flag.");
return prompt(content);
}
}
}
}
@@ -0,0 +1,137 @@
/*
* Copyright 2016 John Grosh (jagrosh).
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot;
import java.awt.Color;
import javax.security.auth.login.LoginException;
import com.jagrosh.jdautilities.commandclient.CommandClient;
import com.jagrosh.jdautilities.commandclient.CommandClientBuilder;
import com.jagrosh.jdautilities.commandclient.examples.*;
import com.jagrosh.jdautilities.waiter.EventWaiter;
import com.jagrosh.jmusicbot.commands.*;
import com.jagrosh.jmusicbot.gui.GUI;
import net.dv8tion.jda.core.AccountType;
import net.dv8tion.jda.core.JDABuilder;
import net.dv8tion.jda.core.OnlineStatus;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Game;
import net.dv8tion.jda.core.exceptions.RateLimitedException;
import net.dv8tion.jda.core.utils.SimpleLog;
/**
*
* @author John Grosh (jagrosh)
*/
public class JMusicBot {
public static Permission[] RECOMMENDED_PERMS = new Permission[]{Permission.MESSAGE_READ, Permission.MESSAGE_WRITE, Permission.MESSAGE_HISTORY, Permission.MESSAGE_ADD_REACTION,
Permission.MESSAGE_EMBED_LINKS, Permission.MESSAGE_ATTACH_FILES, Permission.MESSAGE_MANAGE, Permission.MESSAGE_EXT_EMOJI,
Permission.MANAGE_CHANNEL, Permission.VOICE_CONNECT, Permission.VOICE_SPEAK, Permission.NICKNAME_CHANGE};
/**
* @param args the command line arguments
*/
public static void main(String[] args){
// check run mode(s)
boolean nogui = false;
for(String arg: args)
if("-nogui".equalsIgnoreCase(arg))
nogui = true;
// load config
Config config = new Config(nogui);
// set up the listener
EventWaiter waiter = new EventWaiter();
Bot bot = new Bot(waiter);
AboutCommand.IS_AUTHOR = false;
// set up the command client
CommandClientBuilder cb = new CommandClientBuilder()
.setPrefix(config.getPrefix())
.setOwnerId(config.getOwnerId())
.setEmojis(config.getSuccess(), config.getWarning(), config.getError())
.setHelpWord(config.getHelp())
.addCommands(
new AboutCommand(Color.BLUE.brighter(),
"a music bot that is [easy to host yourself!](https://github.com/jagrosh/MusicBot)",
new String[]{"High-quality music playback", "FairQueue™ Technology", "Easy to host yourself"},
RECOMMENDED_PERMS),
new PingCommand(),
new SettingsCmd(bot),
new NowplayingCmd(bot),
new PlayCmd(bot),
new PlaylistsCmd(bot),
new QueueCmd(bot),
new RemoveCmd(bot),
new SearchCmd(bot),
new SCSearchCmd(bot),
new ShuffleCmd(bot),
new SkipCmd(bot),
new ForceskipCmd(bot),
new SkiptoCmd(bot),
new StopCmd(bot),
new VolumeCmd(bot),
new SetdjCmd(bot),
new SettcCmd(bot),
new SetvcCmd(bot),
//new GuildlistCommand(waiter),
new PlaylistCmd(bot),
new SetavatarCmd(bot),
new SetgameCmd(bot),
new SetnameCmd(bot),
new ShutdownCmd(bot)
);
if(config.getGame()==null)
cb.useDefaultGame();
else
cb.setGame(Game.of(config.getGame()));
CommandClient client = cb.build();
if(!config.getNoGui())
{
try {
GUI gui = new GUI(bot);
bot.setGUI(gui);
gui.init();
} catch(Exception e) {
SimpleLog.getLog("Startup").fatal("Could not start GUI. If you are "
+ "running on a server or in a location where you cannot display a "
+ "window, please run in nogui mode using the -nogui flag.");
}
}
// attempt to log in and start
try {
new JDABuilder(AccountType.BOT)
.setToken(config.getToken())
.setAudioEnabled(true)
.setGame(Game.of("loading..."))
.setStatus(OnlineStatus.DO_NOT_DISTURB)
.addListener(client)
.addListener(waiter)
.addListener(bot)
.buildAsync();
} catch (LoginException | IllegalArgumentException | RateLimitedException ex) {
SimpleLog.getLog("Login").fatal(ex);
}
}
}
@@ -0,0 +1,91 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class Settings {
public final static Settings DEFAULT_SETTINGS = new Settings(null, null, null, 100, null);
private String textId;
private String voiceId;
private String roleId;
private int volume;
private String defaultPlaylist;
public Settings(String textId, String voiceId, String roleId, int volume, String defaultPlaylist)
{
this.textId = textId;
this.voiceId = voiceId;
this.roleId = roleId;
this.volume = volume;
this.defaultPlaylist = defaultPlaylist;
}
public String getTextId()
{
return textId;
}
public String getVoiceId()
{
return voiceId;
}
public String getRoleId()
{
return roleId;
}
public int getVolume()
{
return volume;
}
public String getDefaultPlaylist()
{
return defaultPlaylist;
}
public void setTextId(String id)
{
this.textId = id;
}
public void setVoiceId(String id)
{
this.voiceId = id;
}
public void setRoleId(String id)
{
this.roleId = id;
}
public void setVolume(int volume)
{
this.volume = volume;
}
public void setDefaultPlaylist(String defaultPlaylist)
{
this.defaultPlaylist = defaultPlaylist;
}
}
@@ -0,0 +1,157 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.audio;
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 com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.playlist.Playlist;
import com.jagrosh.jmusicbot.queue.FairQueue;
import net.dv8tion.jda.core.audio.AudioSendHandler;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.User;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class AudioHandler extends AudioEventAdapter implements AudioSendHandler {
private final AudioPlayer audioPlayer;
private final Guild guild;
private final FairQueue<QueuedTrack> queue;
private final Set<String> votes;
private final List<AudioTrack> defaultQueue;
private final Bot bot;
private AudioFrame lastFrame;
private QueuedTrack current;
public AudioHandler(AudioPlayer audioPlayer, Guild guild, Bot bot) {
this.audioPlayer = audioPlayer;
this.guild = guild;
this.bot = bot;
queue = new FairQueue<>();
votes = new HashSet<>();
defaultQueue = new LinkedList<>();
}
public int addTrack(AudioTrack track, User user)
{
QueuedTrack qt = new QueuedTrack(track, user.getId());
if(current==null)
{
current = qt;
audioPlayer.playTrack(track);
return -1;
}
else
{
return queue.add(qt);
}
}
public QueuedTrack getCurrentTrack()
{
return current;
}
public FairQueue<QueuedTrack> getQueue()
{
return queue;
}
public Set<String> getVotes()
{
return votes;
}
public AudioPlayer getPlayer()
{
return audioPlayer;
}
public boolean playFromDefault()
{
if(!defaultQueue.isEmpty())
{
current = new QueuedTrack(defaultQueue.remove(0), null);
audioPlayer.playTrack(current.getTrack());
return true;
}
if(bot.getSettings(guild)==null || bot.getSettings(guild).getDefaultPlaylist()==null)
return false;
Playlist pl = Playlist.loadPlaylist(bot.getSettings(guild).getDefaultPlaylist());
if(pl==null || pl.getItems().isEmpty())
return false;
pl.loadTracks(bot.getAudioManager(), () -> {
if(pl.getTracks().isEmpty())
{
current = null;
guild.getAudioManager().closeAudioConnection();
}
else
{
defaultQueue.addAll(pl.getTracks());
playFromDefault();
}
});
return true;
}
@Override
public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason endReason) {
if(queue.isEmpty())
{
if(!playFromDefault())
{
current = null;
guild.getAudioManager().closeAudioConnection();
}
}
else
{
current = queue.pull();
player.playTrack(current.getTrack());
}
}
@Override
public void onTrackStart(AudioPlayer player, AudioTrack track) {
votes.clear();
}
@Override
public boolean canProvide() {
lastFrame = audioPlayer.provide();
return lastFrame != null;
}
@Override
public byte[] provide20MsAudio() {
return lastFrame.data;
}
@Override
public boolean isOpus() {
return true;
}
}
@@ -0,0 +1,52 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.audio;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.jagrosh.jmusicbot.queue.Queueable;
import com.jagrosh.jmusicbot.utils.FormatUtil;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class QueuedTrack implements Queueable {
private final AudioTrack track;
private final String owner;
public QueuedTrack(AudioTrack track, String owner)
{
this.track = track;
this.owner = owner;
}
@Override
public String getIdentifier() {
return owner;
}
public AudioTrack getTrack()
{
return track;
}
@Override
public String toString() {
return "`["+FormatUtil.formatTime(track.getDuration())+"]` **" + track.getInfo().title +"** - <@"+owner+">";
}
}
@@ -0,0 +1,48 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import net.dv8tion.jda.core.entities.User;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class ForceskipCmd extends MusicCommand {
public ForceskipCmd(Bot bot)
{
super(bot);
this.name = "forceskip";
this.help = "skips the current song";
this.aliases = new String[]{"modskip"};
this.bePlaying = true;
this.category = bot.DJ;
}
@Override
public void doCommand(CommandEvent event) {
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
User u = event.getJDA().getUserById(handler.getCurrentTrack().getIdentifier());
event.reply(event.getClient().getSuccess()+" Skipped **"+handler.getCurrentTrack().getTrack().getInfo().title
+"** (requested by "+(u==null ? "someone" : "**"+u.getName()+"**")+")");
handler.getPlayer().stopTrack();
}
}
@@ -0,0 +1,89 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import com.jagrosh.jdautilities.commandclient.Command;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.Settings;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import net.dv8tion.jda.core.entities.GuildVoiceState;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.entities.VoiceChannel;
import net.dv8tion.jda.core.exceptions.PermissionException;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public abstract class MusicCommand extends Command {
protected final Bot bot;
protected boolean bePlaying;
protected boolean beListening;
public MusicCommand(Bot bot)
{
this.bot = bot;
this.guildOnly = true;
this.category = bot.MUSIC;
}
@Override
protected void execute(CommandEvent event) {
Settings settings = bot.getSettings(event.getGuild());
TextChannel tchannel = event.getGuild().getTextChannelById(settings.getTextId());
if(tchannel!=null && !event.getTextChannel().equals(tchannel))
{
try {
event.getMessage().delete().queue();
} catch(PermissionException e){}
event.replyInDM(event.getClient().getError()+" You can only use that command in <#"+settings.getTextId()+">!");
return;
}
if(bePlaying
&& (event.getGuild().getAudioManager().getSendingHandler()==null
|| ((AudioHandler)event.getGuild().getAudioManager().getSendingHandler()).getCurrentTrack()==null))
{
event.reply(event.getClient().getError()+" There must be music playing to use that!");
return;
}
if(beListening)
{
VoiceChannel current = event.getGuild().getSelfMember().getVoiceState().getChannel();
if(current==null)
current = event.getGuild().getVoiceChannelById(settings.getVoiceId());
GuildVoiceState userState = event.getMember().getVoiceState();
if(!userState.inVoiceChannel() || userState.isDeafened() || (current!=null && !userState.getChannel().equals(current)))
{
event.reply(event.getClient().getError()
+" You must be listening in "+(current==null ? "a voice channel" : "**"+current.getName()+"**")
+" to use that!");
return;
}
if(!event.getGuild().getSelfMember().getVoiceState().inVoiceChannel())
try {
event.getGuild().getAudioManager().openAudioConnection(userState.getChannel());
}catch(PermissionException ex) {
event.reply(event.getClient().getError()+" I am unable to connect to **"+userState.getChannel().getName()+"**!");
return;
}
}
doCommand(event);
}
public abstract void doCommand(CommandEvent event);
}
@@ -0,0 +1,42 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.utils.FormatUtil;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class NowplayingCmd extends MusicCommand {
public NowplayingCmd(Bot bot)
{
super(bot);
this.name = "nowplaying";
this.help = "shows the song that is currently playing";
this.aliases = new String[]{"np","current"};
}
@Override
public void doCommand(CommandEvent event) {
event.reply(FormatUtil.formattedAudio((AudioHandler)event.getGuild().getAudioManager().getSendingHandler(), event.getJDA(), false));
}
}
@@ -0,0 +1,167 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity;
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.jagrosh.jdautilities.commandclient.Command;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.playlist.Playlist;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.core.entities.Message;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class PlayCmd extends MusicCommand {
public PlayCmd(Bot bot)
{
super(bot);
this.name = "play";
this.arguments = "<title|URL|subcommand>";
this.help = "plays the provided song";
this.beListening = true;
this.bePlaying = false;
this.children = new Command[]{new PlaylistCmd(bot)};
}
@Override
public void doCommand(CommandEvent event) {
if(event.getArgs().isEmpty())
{
StringBuilder builder = new StringBuilder(event.getClient().getWarning()+" Play Commands:\n");
builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" <song title>` - plays the first result from Youtube");
builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" <URL>` - plays the provided song, playlist, or stream");
for(Command cmd: children)
builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" ").append(cmd.getName()).append(" ").append(cmd.getArguments()).append("` - ").append(cmd.getHelp());
event.reply(builder.toString());
return;
}
String args = event.getArgs().startsWith("<") && event.getArgs().endsWith(">")
? event.getArgs().substring(1,event.getArgs().length()-1)
: event.getArgs();
event.getChannel().sendMessage("\u231A Loading... `["+args+"]`").queue(m -> {
bot.getAudioManager().loadItemOrdered(event.getGuild(), args, new ResultHandler(m,event,false));
});
}
private class ResultHandler implements AudioLoadResultHandler {
final Message m;
final CommandEvent event;
final boolean ytsearch;
private ResultHandler(Message m, CommandEvent event, boolean ytsearch)
{
this.m = m;
this.event = event;
this.ytsearch = ytsearch;
}
@Override
public void trackLoaded(AudioTrack track) {
int pos = bot.queueTrack(event, track)+1;
m.editMessage(event.getClient().getSuccess()+" Added **"+track.getInfo().title
+"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0 ? "to begin playing"
: " to the queue at position "+pos)).queue();
}
@Override
public void playlistLoaded(AudioPlaylist playlist) {
if(playlist.getTracks().size()==1 || playlist.isSearchResult() || playlist.getSelectedTrack()!=null)
{
AudioTrack single = playlist.getSelectedTrack()==null?playlist.getTracks().get(0):playlist.getSelectedTrack();
int pos = bot.queueTrack(event, single)+1;
m.editMessage(event.getClient().getSuccess()+" Added **"+single.getInfo().title
+"** (`"+FormatUtil.formatTime(single.getDuration())+"`) "+(pos==0 ? "to begin playing"
: " to the queue at position "+pos)).queue();
}
else
{
m.editMessage(event.getClient().getSuccess()+" Found "
+(playlist.getName()==null?"a playlist":"playlist **"+playlist.getName()+"**")+" with `"
+playlist.getTracks().size()+"` entries; added to the queue!").queue();
playlist.getTracks().stream().forEach((track) -> {
bot.queueTrack(event, track);
});
}
}
@Override
public void noMatches() {
if(ytsearch)
m.editMessage(event.getClient().getWarning()+" No results found for `"+event.getArgs()+"`.").queue();
else
bot.getAudioManager().loadItemOrdered(event.getGuild(), "ytsearch:"+event.getArgs(), new ResultHandler(m,event,true));
}
@Override
public void loadFailed(FriendlyException throwable) {
if(throwable.severity==Severity.COMMON)
m.editMessage(event.getClient().getError()+" Error loading: "+throwable.getMessage()).queue();
else
m.editMessage(event.getClient().getError()+" Error loading track.").queue();
}
}
public class PlaylistCmd extends MusicCommand {
public PlaylistCmd(Bot bot)
{
super(bot);
this.name = "playlist";
this.aliases = new String[]{"pl"};
this.arguments = "<name>";
this.help = "plays the provided playlist";
this.beListening = true;
this.bePlaying = false;
}
@Override
public void doCommand(CommandEvent event) {
if(event.getArgs().isEmpty())
{
event.reply(event.getClient().getError()+" Please include a playlist name.");
return;
}
Playlist playlist = Playlist.loadPlaylist(event.getArgs());
if(playlist==null)
{
event.reply(event.getClient().getError()+" I could not find `"+event.getArgs()+".txt` in the Playlists folder.");
return;
}
event.getChannel().sendMessage("\u231A Loading playlist **"+event.getArgs()+"**...").queue(m -> {
playlist.loadTracks(bot.getAudioManager(), () -> {
StringBuilder builder = new StringBuilder(playlist.getTracks().isEmpty()
? event.getClient().getWarning()+" No tracks were loaded!"
: event.getClient().getSuccess()+" Loaded **"+playlist.getTracks().size()+"** tracks!");
if(!playlist.getErrors().isEmpty())
builder.append("\nThe following tracks failed to load:");
playlist.getErrors().forEach(err -> builder.append("\n`[").append(err.getIndex()+1).append("]` **").append(err.getItem()).append("**: ").append(err.getReason()));
String str = builder.toString();
if(str.length()>2000)
str = str.substring(0,1994)+" (...)";
m.editMessage(str).queue();
playlist.getTracks().forEach(track -> bot.queueTrack(event, track));
});
});
}
}
}
@@ -0,0 +1,250 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import com.jagrosh.jdautilities.commandclient.Command;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.playlist.Playlist;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class PlaylistCmd extends Command {
private final Bot bot;
public PlaylistCmd(Bot bot)
{
this.bot = bot;
this.category = bot.OWNER;
this.ownerCommand = true;
this.guildOnly = false;
this.name = "playlist";
this.arguments = "<append|delete|make|setdefault>";
this.help = "playlist management";
this.children = new Command[]{
new ListCmd(),
new AppendlistCmd(),
new DeletelistCmd(),
new MakelistCmd(),
new DefaultlistCmd()
};
}
@Override
public void execute(CommandEvent event) {
StringBuilder builder = new StringBuilder(event.getClient().getWarning()+" Playlist Management Commands:\n");
for(Command cmd: this.children)
builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" ").append(cmd.getName())
.append(" ").append(cmd.getArguments()==null ? "" : cmd.getArguments()).append("` - ").append(cmd.getHelp());
event.reply(builder.toString());
}
public class MakelistCmd extends Command {
public MakelistCmd()
{
this.name = "make";
this.aliases = new String[]{"create"};
this.help = "makes a new playlist";
this.arguments = "<name>";
this.category = bot.OWNER;
this.ownerCommand = true;
this.guildOnly = false;
}
@Override
protected void execute(CommandEvent event) {
String pname = event.getArgs().replaceAll("\\s+", "_");
if(Playlist.loadPlaylist(pname)==null)
{
try
{
Files.createFile(Paths.get("Playlists"+File.separator+pname+".txt"));
event.reply(event.getClient().getSuccess()+" Successfully created playlist `"+pname+"`!");
}
catch(IOException e)
{
event.reply(event.getClient().getError()+" I was unable to create the playlist: "+e.getLocalizedMessage());
}
}
else
event.reply(event.getClient().getError()+" Playlist `"+pname+"` already exists!");
}
}
public class DeletelistCmd extends Command {
public DeletelistCmd()
{
this.name = "delete";
this.aliases = new String[]{"remove"};
this.help = "deletes an existing playlist";
this.arguments = "<name>";
this.guildOnly = false;
this.ownerCommand = true;
this.category = bot.OWNER;
}
@Override
protected void execute(CommandEvent event) {
String pname = event.getArgs().replaceAll("\\s+", "_");
if(Playlist.loadPlaylist(pname)==null)
event.reply(event.getClient().getError()+" Playlist `"+pname+"` doesn't exist!");
else
{
try
{
Files.delete(Paths.get("Playlists"+File.separator+pname+".txt"));
event.reply(event.getClient().getSuccess()+" Successfully deleted playlist `"+pname+"`!");
}
catch(IOException e)
{
event.reply(event.getClient().getError()+" I was unable to delete the playlist: "+e.getLocalizedMessage());
}
}
}
}
public class AppendlistCmd extends Command {
public AppendlistCmd()
{
this.name = "append";
this.aliases = new String[]{"add"};
this.help = "appends songs to an existing playlist";
this.arguments = "<name> <URL> | <URL> | ...";
this.guildOnly = false;
this.ownerCommand = true;
this.category = bot.OWNER;
}
@Override
protected void execute(CommandEvent event) {
String[] parts = event.getArgs().split("\\s+", 2);
if(parts.length<2)
{
event.reply(event.getClient().getError()+" Please include a playlist name and URLs to add!");
return;
}
String pname = parts[0];
Playlist playlist = Playlist.loadPlaylist(pname);
if(playlist==null)
event.reply(event.getClient().getError()+" Playlist `"+pname+"` doesn't exist!");
else
{
StringBuilder builder = new StringBuilder();
playlist.getItems().forEach(item -> builder.append("\r\n").append(item));
String[] urls = parts[1].split("\\|");
for(String url: urls)
{
String u = url.trim();
if(u.startsWith("<") && u.endsWith(">"))
u = u.substring(1, u.length()-1);
builder.append("\r\n").append(u);
}
try
{
Files.write(Paths.get("Playlists"+File.separator+pname+".txt"), builder.toString().trim().getBytes());
event.reply(event.getClient().getSuccess()+" Successfully added "+urls.length+" songs to playlist `"+pname+"`!");
}
catch(IOException e)
{
event.reply(event.getClient().getError()+" I was unable to append to the playlist: "+e.getLocalizedMessage());
}
}
}
}
public class DefaultlistCmd extends Command {
public DefaultlistCmd()
{
this.name = "setdefault";
this.aliases = new String[]{"default"};
this.help = "sets the default playlist for the server";
this.arguments = "<playlistname|NONE>";
this.guildOnly = true;
this.ownerCommand = true;
this.category = bot.OWNER;
}
@Override
protected void execute(CommandEvent event) {
if(event.getArgs().isEmpty())
{
event.reply(event.getClient().getError()+" Please include a playlist name or NONE");
}
if(event.getArgs().equalsIgnoreCase("none"))
{
bot.setDefaultPlaylist(event.getGuild(), null);
event.reply(event.getClient().getSuccess()+" Cleared the default playlist for **"+event.getGuild().getName()+"**");
return;
}
String pname = event.getArgs().replaceAll("\\s+", "_");
if(Playlist.loadPlaylist(pname)==null)
{
event.reply(event.getClient().getError()+" Could not find `"+pname+".txt`!");
}
else
{
bot.setDefaultPlaylist(event.getGuild(), pname);
event.reply(event.getClient().getSuccess()+" The default playlist for **"+event.getGuild().getName()+"** is now `"+pname+"`");
}
}
}
public class ListCmd extends Command {
public ListCmd()
{
this.name = "all";
this.aliases = new String[]{"available","list"};
this.help = "lists all available playlists";
this.guildOnly = true;
this.ownerCommand = true;
this.category = bot.OWNER;
}
@Override
protected void execute(CommandEvent event) {
if(!Playlist.folderExists())
Playlist.createFolder();
if(!Playlist.folderExists())
{
event.reply(event.getClient().getWarning()+" Playlists folder does not exist and could not be created!");
return;
}
List<String> list = Playlist.getPlaylists();
if(list==null)
event.reply(event.getClient().getError()+" Failed to load available playlists!");
else if(list.isEmpty())
event.reply(event.getClient().getWarning()+" There are no playlists in the Playlists folder!");
else
{
StringBuilder builder = new StringBuilder(event.getClient().getSuccess()+" Available playlists:\n");
list.forEach(str -> builder.append("`").append(str).append("` "));
event.reply(builder.toString());
}
}
}
}
@@ -0,0 +1,63 @@
/*
* Copyright 2017 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import java.util.List;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.playlist.Playlist;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class PlaylistsCmd extends MusicCommand {
public PlaylistsCmd(Bot bot)
{
super(bot);
this.name = "playlists";
this.help = "shows the available playlists";
this.aliases = new String[]{"pls"};
this.guildOnly = true;
this.beListening = false;
this.beListening = false;
}
@Override
public void doCommand(CommandEvent event) {
if(!Playlist.folderExists())
Playlist.createFolder();
if(!Playlist.folderExists())
{
event.reply(event.getClient().getWarning()+" Playlists folder does not exist and could not be created!");
return;
}
List<String> list = Playlist.getPlaylists();
if(list==null)
event.reply(event.getClient().getError()+" Failed to load available playlists!");
else if(list.isEmpty())
event.reply(event.getClient().getWarning()+" There are no playlists in the Playlists folder!");
else
{
StringBuilder builder = new StringBuilder(event.getClient().getSuccess()+" Available playlists:\n");
list.forEach(str -> builder.append("`").append(str).append("` "));
builder.append("\nType `").append(event.getClient().getTextualPrefix()).append("play playlist <name>` to play a playlist");
event.reply(builder.toString());
}
}
}
@@ -0,0 +1,84 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import java.util.List;
import java.util.concurrent.TimeUnit;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jdautilities.menu.pagination.PaginatorBuilder;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.audio.QueuedTrack;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.exceptions.PermissionException;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class QueueCmd extends MusicCommand {
private final PaginatorBuilder builder;
public QueueCmd(Bot bot)
{
super(bot);
this.name = "queue";
this.help = "shows the current queue";
this.arguments = "[pagenum]";
this.aliases = new String[]{"list"};
this.bePlaying = true;
this.botPermissions = new Permission[]{Permission.MESSAGE_ADD_REACTION,Permission.MESSAGE_EMBED_LINKS};
builder = new PaginatorBuilder()
.setColumns(1)
.setFinalAction(m -> {try{m.clearReactions().queue();}catch(PermissionException e){}})
.setItemsPerPage(10)
.waitOnSinglePage(false)
.useNumberedItems(true)
.showPageNumbers(true)
.setEventWaiter(bot.getWaiter())
.setTimeout(1, TimeUnit.MINUTES)
;
}
@Override
public void doCommand(CommandEvent event) {
int pagenum = 1;
try{
pagenum = Integer.parseInt(event.getArgs());
}catch(NumberFormatException e){}
List<QueuedTrack> list = ((AudioHandler)event.getGuild().getAudioManager().getSendingHandler()).getQueue().getList();
if(list.isEmpty())
{
event.reply(event.getClient().getWarning()+" There is no music in the queue!");
return;
}
String[] songs = new String[list.size()];
long total = 0;
for(int i=0; i<list.size(); i++)
{
total += list.get(i).getTrack().getDuration();
songs[i] = list.get(i).toString();
}
builder.setText(event.getClient().getSuccess()+" Current Queue | "+songs.length+" entries | `"+FormatUtil.formatTime(total)+"` ")
.setItems(songs)
.setUsers(event.getAuthor())
.setColor(event.getSelfMember().getColor())
;
builder.build().paginate(event.getChannel(), pagenum);
}
}
@@ -0,0 +1,93 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.audio.QueuedTrack;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.User;
import net.dv8tion.jda.core.utils.PermissionUtil;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class RemoveCmd extends MusicCommand {
public RemoveCmd(Bot bot)
{
super(bot);
this.name = "remove";
this.help = "removes a song from the queue";
this.arguments = "<position|ALL>";
this.aliases = new String[]{"delete"};
this.beListening = true;
this.bePlaying = true;
}
@Override
public void doCommand(CommandEvent event) {
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
if(handler.getQueue().isEmpty())
{
event.reply(event.getClient().getError()+" There is nothing in the queue!");
return;
}
if(event.getArgs().equalsIgnoreCase("all"))
{
int count = handler.getQueue().removeAll(event.getAuthor().getId());
if(count==0)
event.reply(event.getClient().getWarning()+" You don't have any songs in the queue!");
else
event.reply(event.getClient().getSuccess()+" Successfully removed your "+count+" entries.");
return;
}
int pos;
try {
pos = Integer.parseInt(event.getArgs());
} catch(NumberFormatException e) {
pos = 0;
}
if(pos<1 || pos>handler.getQueue().size())
{
event.reply(event.getClient().getError()+" Position must be a valid integer between 1 and "+handler.getQueue().size()+"!");
return;
}
boolean isDJ = PermissionUtil.checkPermission(event.getGuild(), event.getMember(), Permission.MANAGE_SERVER);
if(!isDJ)
isDJ = event.getMember().getRoles().contains(event.getGuild().getRoleById(bot.getSettings(event.getGuild()).getRoleId()));
QueuedTrack qt = handler.getQueue().get(pos-1);
if(qt.getIdentifier().equals(event.getAuthor().getId()))
{
handler.getQueue().remove(pos-1);
event.reply(event.getClient().getSuccess()+" Removed **"+qt.getTrack().getInfo().title+"** from the queue");
}
else if(isDJ)
{
handler.getQueue().remove(pos-1);
User u = event.getJDA().getUserById(qt.getIdentifier());
event.reply(event.getClient().getSuccess()+" Removed **"+qt.getTrack().getInfo().title
+"** from the queue (requested by "+(u==null ? "someone" : "**"+u.getName()+"**")+")");
}
else
{
event.reply(event.getClient().getError()+" You cannot remove **"+qt.getTrack().getInfo().title+"** because you didn't add it!");
}
}
}
@@ -0,0 +1,121 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity;
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import java.util.concurrent.TimeUnit;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jdautilities.menu.orderedmenu.OrderedMenuBuilder;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Message;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class SCSearchCmd extends MusicCommand {
private final OrderedMenuBuilder builder;
public SCSearchCmd(Bot bot)
{
super(bot);
this.name = "scsearch";
this.arguments = "<query>";
this.help = "searches Soundcloud for a provided query";
this.beListening = true;
this.bePlaying = false;
this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
builder = new OrderedMenuBuilder()
.allowTextInput(true)
.useNumbers()
.useCancelButton(true)
.setEventWaiter(bot.getWaiter())
.setTimeout(1, TimeUnit.MINUTES)
;
}
@Override
public void doCommand(CommandEvent event) {
if(event.getArgs().isEmpty())
{
event.reply(event.getClient().getError()+" Please include a query.");
return;
}
event.getChannel().sendMessage("\uD83D\uDD0E Searching... `["+event.getArgs()+"]`").queue(m -> {
bot.getAudioManager().loadItemOrdered(event.getGuild(), "scsearch:"+event.getArgs(), new ResultHandler(m,event));
});
}
private class ResultHandler implements AudioLoadResultHandler {
final Message m;
final CommandEvent event;
private ResultHandler(Message m, CommandEvent event)
{
this.m = m;
this.event = event;
}
@Override
public void trackLoaded(AudioTrack track) {
int pos = bot.queueTrack(event, track)+1;
m.editMessage(event.getClient().getSuccess()+" Added **"+track.getInfo().title
+"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0 ? "to begin playing"
: " to the queue at position "+pos)).queue();
}
@Override
public void playlistLoaded(AudioPlaylist playlist) {
builder.setColor(event.getSelfMember().getColor())
.setText(event.getClient().getSuccess()+" Search results for `"+event.getArgs()+"`:")
.setChoices(new String[0])
.setAction(i -> {
AudioTrack track = playlist.getTracks().get(i-1);
int pos = bot.queueTrack(event, track)+1;
event.getChannel().sendMessage(event.getClient().getSuccess()+" Added **"+track.getInfo().title
+"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0 ? "to begin playing"
: " to the queue at position "+pos)).queue();
})
.setCancel(() -> m.delete().queue())
.setUsers(event.getAuthor())
;
for(int i=0; i<4&&i<playlist.getTracks().size(); i++)
{
AudioTrack track = playlist.getTracks().get(i);
builder.addChoices("`["+FormatUtil.formatTime(track.getDuration())+"]` [**"
+track.getInfo().title+"**]("+track.getInfo().uri+")");
}
builder.build().display(m);
}
@Override
public void noMatches() {
m.editMessage(event.getClient().getWarning()+" No results found for `"+event.getArgs()+"`.").queue();
}
@Override
public void loadFailed(FriendlyException throwable) {
if(throwable.severity==Severity.COMMON)
m.editMessage(event.getClient().getError()+" Error loading: "+throwable.getMessage()).queue();
else
m.editMessage(event.getClient().getError()+" Error loading track.").queue();
}
}
}
@@ -0,0 +1,122 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity;
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import java.util.concurrent.TimeUnit;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jdautilities.menu.orderedmenu.OrderedMenuBuilder;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.Message;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class SearchCmd extends MusicCommand {
private final OrderedMenuBuilder builder;
public SearchCmd(Bot bot)
{
super(bot);
this.name = "search";
this.aliases = new String[]{"ytsearch"};
this.arguments = "<query>";
this.help = "searches Youtube for a provided query";
this.beListening = true;
this.bePlaying = false;
this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS};
builder = new OrderedMenuBuilder()
.allowTextInput(true)
.useNumbers()
.useCancelButton(true)
.setEventWaiter(bot.getWaiter())
.setTimeout(1, TimeUnit.MINUTES)
;
}
@Override
public void doCommand(CommandEvent event) {
if(event.getArgs().isEmpty())
{
event.reply(event.getClient().getError()+" Please include a query.");
return;
}
event.getChannel().sendMessage("\uD83D\uDD0E Searching... `["+event.getArgs()+"]`").queue(m -> {
bot.getAudioManager().loadItemOrdered(event.getGuild(), "ytsearch:"+event.getArgs(), new ResultHandler(m,event));
});
}
private class ResultHandler implements AudioLoadResultHandler {
final Message m;
final CommandEvent event;
private ResultHandler(Message m, CommandEvent event)
{
this.m = m;
this.event = event;
}
@Override
public void trackLoaded(AudioTrack track) {
int pos = bot.queueTrack(event, track)+1;
m.editMessage(event.getClient().getSuccess()+" Added **"+track.getInfo().title
+"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0 ? "to begin playing"
: " to the queue at position "+pos)).queue();
}
@Override
public void playlistLoaded(AudioPlaylist playlist) {
builder.setColor(event.getSelfMember().getColor())
.setText(event.getClient().getSuccess()+" Search results for `"+event.getArgs()+"`:")
.setChoices(new String[0])
.setAction(i -> {
AudioTrack track = playlist.getTracks().get(i-1);
int pos = bot.queueTrack(event, track)+1;
event.getChannel().sendMessage(event.getClient().getSuccess()+" Added **"+track.getInfo().title
+"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0 ? "to begin playing"
: " to the queue at position "+pos)).queue();
})
.setCancel(() -> m.delete().queue())
.setUsers(event.getAuthor())
;
for(int i=0; i<4&&i<playlist.getTracks().size(); i++)
{
AudioTrack track = playlist.getTracks().get(i);
builder.addChoices("`["+FormatUtil.formatTime(track.getDuration())+"]` [**"
+track.getInfo().title+"**](https://youtu.be/"+track.getIdentifier()+")");
}
builder.build().display(m);
}
@Override
public void noMatches() {
m.editMessage(event.getClient().getWarning()+" No results found for `"+event.getArgs()+"`.").queue();
}
@Override
public void loadFailed(FriendlyException throwable) {
if(throwable.severity==Severity.COMMON)
m.editMessage(event.getClient().getError()+" Error loading: "+throwable.getMessage()).queue();
else
m.editMessage(event.getClient().getError()+" Error loading track.").queue();
}
}
}
@@ -0,0 +1,69 @@
/*
* Copyright 2017 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import com.jagrosh.jdautilities.commandclient.Command;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.utils.OtherUtil;
import net.dv8tion.jda.core.entities.Icon;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class SetavatarCmd extends Command {
public SetavatarCmd(Bot bot)
{
this.name = "setavatar";
this.help = "sets the avatar of the bot";
this.arguments = "<url>";
this.ownerCommand = true;
this.category = bot.OWNER;
}
@Override
protected void execute(CommandEvent event) {
String url;
if(event.getArgs().isEmpty())
if(!event.getMessage().getAttachments().isEmpty() && event.getMessage().getAttachments().get(0).isImage())
url = event.getMessage().getAttachments().get(0).getUrl();
else
url = null;
else
url = event.getArgs();
InputStream s = OtherUtil.imageFromUrl(url);
if(s==null)
{
event.reply(event.getClient().getError()+" Invalid or missing URL");
}
else
{
try {
event.getSelfUser().getManager().setAvatar(Icon.from(s)).queue(
v -> event.reply(event.getClient().getSuccess()+" Successfully changed avatar."),
t -> event.reply(event.getClient().getError()+" Failed to set avatar."));
} catch(IOException e) {
event.reply(event.getClient().getError()+" Could not load from provided URL.");
}
}
}
}
@@ -0,0 +1,69 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import java.util.List;
import com.jagrosh.jdautilities.commandclient.Command;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.utils.FinderUtil;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.core.entities.Role;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class SetdjCmd extends Command {
private final Bot bot;
public SetdjCmd(Bot bot)
{
this.bot = bot;
this.name = "setdj";
this.help = "sets the DJ role for certain music commands";
this.arguments = "<rolename|NONE>";
this.guildOnly = true;
this.category = bot.ADMIN;
}
@Override
protected void execute(CommandEvent event) {
if(event.getArgs().isEmpty())
{
event.reply(event.getClient().getError()+" Please include a role name or NONE");
}
else if(event.getArgs().equalsIgnoreCase("none"))
{
bot.clearTextChannel(event.getGuild());
event.reply(event.getClient().getSuccess()+" DJ role cleared; Only Admins can use the DJ commands.");
}
else
{
List<Role> list = FinderUtil.findRole(event.getArgs(), event.getGuild());
if(list.isEmpty())
event.reply(event.getClient().getWarning()+" No Roles found matching \""+event.getArgs()+"\"");
else if (list.size()>1)
event.reply(event.getClient().getWarning()+FormatUtil.listOfRoles(list, event.getArgs()));
else
{
bot.setRole(list.get(0));
event.reply(event.getClient().getSuccess()+" DJ commands can now be used by users with the **"+list.get(0).getName()+"** role.");
}
}
}
}
@@ -0,0 +1,49 @@
/*
* Copyright 2017 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import com.jagrosh.jdautilities.commandclient.Command;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import net.dv8tion.jda.core.entities.Game;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class SetgameCmd extends Command {
public SetgameCmd(Bot bot)
{
this.name = "setgame";
this.help = "sets the game the bot is playing";
this.arguments = "[game]";
this.ownerCommand = true;
this.category = bot.OWNER;
}
@Override
protected void execute(CommandEvent event) {
try {
event.getJDA().getPresence().setGame(event.getArgs().isEmpty() ? null : Game.of(event.getArgs()));
event.reply(event.getClient().getSuccess()+" **"+event.getSelfUser().getName()
+"** is "+(event.getArgs().isEmpty() ? "no longer playing anything." : "now playing `"+event.getArgs()+"`"));
} catch(Exception e) {
event.reply(event.getClient().getError()+" The game could not be set!");
}
}
}
@@ -0,0 +1,51 @@
/*
* Copyright 2017 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import com.jagrosh.jdautilities.commandclient.Command;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import net.dv8tion.jda.core.exceptions.RateLimitedException;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class SetnameCmd extends Command {
public SetnameCmd(Bot bot)
{
this.name = "setname";
this.help = "sets the name of the bot";
this.arguments = "<name>";
this.ownerCommand = true;
this.category = bot.OWNER;
}
@Override
protected void execute(CommandEvent event) {
try {
String oldname = event.getSelfUser().getName();
event.getSelfUser().getManager().setName(event.getArgs()).complete(false);
event.reply(event.getClient().getSuccess()+" Name changed from `"+oldname+"` to `"+event.getArgs()+"`");
} catch(RateLimitedException e) {
event.reply(event.getClient().getError()+" Name can only be changed twice per hour!");
} catch(Exception e) {
event.reply(event.getClient().getError()+" That name is not valid!");
}
}
}
@@ -0,0 +1,69 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import java.util.List;
import com.jagrosh.jdautilities.commandclient.Command;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.utils.FinderUtil;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.core.entities.TextChannel;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class SettcCmd extends Command {
private final Bot bot;
public SettcCmd(Bot bot)
{
this.bot = bot;
this.name = "settc";
this.help = "sets the text channel for music commands";
this.arguments = "<channel|NONE>";
this.guildOnly = true;
this.category = bot.ADMIN;
}
@Override
protected void execute(CommandEvent event) {
if(event.getArgs().isEmpty())
{
event.reply(event.getClient().getError()+" Please include a text channel or NONE");
}
else if(event.getArgs().equalsIgnoreCase("none"))
{
bot.clearTextChannel(event.getGuild());
event.reply(event.getClient().getSuccess()+" Music commands can now be used in any channel");
}
else
{
List<TextChannel> list = FinderUtil.findTextChannel(event.getArgs(), event.getGuild());
if(list.isEmpty())
event.reply(event.getClient().getWarning()+" No Text Channels found matching \""+event.getArgs()+"\"");
else if (list.size()>1)
event.reply(event.getClient().getWarning()+FormatUtil.listOfTChannels(list, event.getArgs()));
else
{
bot.setTextChannel(list.get(0));
event.reply(event.getClient().getSuccess()+" Music commands can now only be used in <#"+list.get(0).getId()+">");
}
}
}
}
@@ -0,0 +1,67 @@
/*
* Copyright 2017 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import com.jagrosh.jdautilities.commandclient.Command;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.Settings;
import net.dv8tion.jda.core.EmbedBuilder;
import net.dv8tion.jda.core.MessageBuilder;
import net.dv8tion.jda.core.entities.Role;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.entities.VoiceChannel;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class SettingsCmd extends Command {
private final Bot bot;
public SettingsCmd(Bot bot)
{
this.bot = bot;
this.name = "settings";
this.help = "shows the bots settings";
this.aliases = new String[]{"status"};
this.guildOnly = true;
}
@Override
protected void execute(CommandEvent event) {
Settings s = bot.getSettings(event.getGuild());
MessageBuilder builder = new MessageBuilder()
.append("\uD83C\uDFA7 **")
.append(event.getSelfUser().getName())
.append("** settings:");
TextChannel tchan = event.getGuild().getTextChannelById(s.getTextId());
VoiceChannel vchan = event.getGuild().getVoiceChannelById(s.getVoiceId());
Role role = event.getGuild().getRoleById(s.getRoleId());
EmbedBuilder ebuilder = new EmbedBuilder()
.setColor(event.getSelfMember().getColor())
.setDescription("Text Channel: "+(tchan==null ? "Any" : "**#"+tchan.getName()+"**")
+ "\nVoice Channel: "+(vchan==null ? "Any" : "**"+vchan.getName()+"**")
+ "\nDJ Role: "+(role==null ? "None" : "**"+role.getName()+"**")
+ "\nDefault Playlist: "+(s.getDefaultPlaylist()==null ? "None" : "**"+s.getDefaultPlaylist()+"**")
)
.setFooter(event.getJDA().getGuilds().size()+" servers | "
+event.getJDA().getGuilds().stream().filter(g -> g.getSelfMember().getVoiceState().inVoiceChannel()).count()
+" audio connections", null);
event.getChannel().sendMessage(builder.setEmbed(ebuilder.build()).build()).queue();
}
}
@@ -0,0 +1,69 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import java.util.List;
import com.jagrosh.jdautilities.commandclient.Command;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.utils.FinderUtil;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.core.entities.VoiceChannel;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class SetvcCmd extends Command {
private final Bot bot;
public SetvcCmd(Bot bot)
{
this.bot = bot;
this.name = "setvc";
this.help = "sets the voice channel for playing music";
this.arguments = "<channel|NONE>";
this.guildOnly = true;
this.category = bot.ADMIN;
}
@Override
protected void execute(CommandEvent event) {
if(event.getArgs().isEmpty())
{
event.reply(event.getClient().getError()+" Please include a voice channel or NONE");
}
else if(event.getArgs().equalsIgnoreCase("none"))
{
bot.clearVoiceChannel(event.getGuild());
event.reply(event.getClient().getSuccess()+" Music can now be played in any channel");
}
else
{
List<VoiceChannel> list = FinderUtil.findVoiceChannel(event.getArgs(), event.getGuild());
if(list.isEmpty())
event.reply(event.getClient().getWarning()+" No Voice Channels found matching \""+event.getArgs()+"\"");
else if (list.size()>1)
event.reply(event.getClient().getWarning()+FormatUtil.listOfVChannels(list, event.getArgs()));
else
{
bot.setVoiceChannel(list.get(0));
event.reply(event.getClient().getSuccess()+" Music can now only be played in **"+list.get(0).getName()+"**");
}
}
}
}
@@ -0,0 +1,54 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class ShuffleCmd extends MusicCommand {
public ShuffleCmd(Bot bot)
{
super(bot);
this.name = "shuffle";
this.help = "shuffles songs you have added";
this.beListening = true;
this.bePlaying = true;
}
@Override
public void doCommand(CommandEvent event) {
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
int s = handler.getQueue().shuffle(event.getAuthor().getId());
switch (s) {
case 0:
event.reply(event.getClient().getError()+" You don't have any music in the queue to shuffle!");
break;
case 1:
event.reply(event.getClient().getWarning()+" You only have one song in the queue!");
break;
default:
event.reply(event.getClient().getSuccess()+" You successfully shuffled your "+s+" entries.");
break;
}
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2017 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import com.jagrosh.jdautilities.commandclient.Command;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class ShutdownCmd extends Command {
private final Bot bot;
public ShutdownCmd(Bot bot)
{
this.bot = bot;
this.name = "shutdown";
this.help = "safely shuts down";
this.ownerCommand = true;
this.category = bot.OWNER;
}
@Override
protected void execute(CommandEvent event) {
event.reply(event.getClient().getWarning()+" Shutting down...");
bot.shutdown();
}
}
@@ -0,0 +1,75 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import net.dv8tion.jda.core.entities.User;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class SkipCmd extends MusicCommand {
public SkipCmd(Bot bot)
{
super(bot);
this.name = "skip";
this.help = "votes to skip the current song";
this.aliases = new String[]{"voteskip"};
this.beListening = true;
this.bePlaying = true;
}
@Override
public void doCommand(CommandEvent event) {
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
if(event.getAuthor().getId().equals(handler.getCurrentTrack().getIdentifier()))
{
event.reply(event.getClient().getSuccess()+" Skipped **"+handler.getCurrentTrack().getTrack().getInfo().title
+"**");
handler.getPlayer().stopTrack();
}
else
{
int listeners = (int)event.getSelfMember().getVoiceState().getChannel().getMembers().stream()
.filter(m -> !m.getUser().isBot() && !m.getVoiceState().isDeafened()).count();
String msg;
if(handler.getVotes().contains(event.getAuthor().getId()))
msg = event.getClient().getWarning()+" You already voted to skip this song `[";
else
{
msg = event.getClient().getSuccess()+" You voted to skip the song `[";
handler.getVotes().add(event.getAuthor().getId());
}
int skippers = (int)event.getSelfMember().getVoiceState().getChannel().getMembers().stream()
.filter(m -> handler.getVotes().contains(m.getUser().getId())).count();
int required = (int)Math.ceil(listeners * .55);
msg+= skippers+" votes, "+required+"/"+listeners+" needed]`";
if(skippers>=required)
{
User u = event.getJDA().getUserById(handler.getCurrentTrack().getIdentifier());
msg+="\n"+event.getClient().getSuccess()+" Skipped **"+handler.getCurrentTrack().getTrack().getInfo().title
+"**"+(handler.getCurrentTrack().getIdentifier()==null ? "" : " (requested by "+(u==null ? "someone" : "**"+u.getName()+"**")+")");
handler.getPlayer().stopTrack();
}
event.reply(msg);
}
}
}
@@ -0,0 +1,63 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import net.dv8tion.jda.core.entities.User;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class SkiptoCmd extends MusicCommand {
public SkiptoCmd(Bot bot)
{
super(bot);
this.name = "skipto";
this.help = "skips to the specified song";
this.arguments = "<position>";
this.aliases = new String[]{"jumpto"};
this.bePlaying = true;
this.category = bot.DJ;
}
@Override
public void doCommand(CommandEvent event) {
int index = 0;
try
{
index = Integer.parseInt(event.getArgs());
}
catch(NumberFormatException e)
{
event.reply(event.getClient().getError()+" `"+event.getArgs()+"` is not a valid integer!");
return;
}
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
if(index<1 || index>handler.getQueue().size())
{
event.reply(event.getClient().getError()+" Position must be a valid integer between 1 and "+handler.getQueue().size()+"!");
return;
}
handler.getQueue().skip(index-1);
event.reply(event.getClient().getSuccess()+" Skipped to **"+handler.getQueue().get(0).getTrack().getInfo().title+"**");
handler.getPlayer().stopTrack();
}
}
@@ -0,0 +1,46 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class StopCmd extends MusicCommand {
public StopCmd(Bot bot)
{
super(bot);
this.name = "stop";
this.help = "stops the current song and clears the queue";
this.bePlaying = true;
this.category = bot.DJ;
}
@Override
public void doCommand(CommandEvent event) {
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
handler.getQueue().clear();
handler.getPlayer().stopTrack();
event.getGuild().getAudioManager().closeAudioConnection();
event.reply(event.getClient().getSuccess()+" The player has stopped and the queue has been cleared.");
}
}
@@ -0,0 +1,66 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.commands;
import com.jagrosh.jdautilities.commandclient.CommandEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.utils.FormatUtil;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class VolumeCmd extends MusicCommand {
public VolumeCmd(Bot bot)
{
super(bot);
this.name = "volume";
this.aliases = new String[]{"vol"};
this.help = "sets or shows volume";
this.arguments = "[0-150]";
this.category = bot.DJ;
}
@Override
public void doCommand(CommandEvent event) {
AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler();
int volume = handler==null && bot.getSettings(event.getGuild())==null ? 100 : (handler==null ? bot.getSettings(event.getGuild()).getVolume() : handler.getPlayer().getVolume());
if(event.getArgs().isEmpty())
{
event.reply(FormatUtil.volumeIcon(volume)+" Current volume is `"+volume+"`");
}
else
{
int nvolume;
try{
nvolume = Integer.parseInt(event.getArgs());
}catch(NumberFormatException e){
nvolume = -1;
}
if(nvolume<0 || nvolume>150)
event.reply(event.getClient().getError()+" Volume must be a valid integer between 0 and 150!");
else
{
bot.setUpHandler(event).getPlayer().setVolume(nvolume);
bot.setVolume(event.getGuild(), nvolume);
event.reply(FormatUtil.volumeIcon(nvolume)+" Volume changed from `"+volume+"` to `"+nvolume+"`");
}
}
}
}
@@ -0,0 +1,49 @@
/*
* Copyright 2017 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.gui;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.io.PrintStream;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class ConsolePanel extends JPanel {
public ConsolePanel()
{
super();
JTextArea text = new JTextArea();
text.setLineWrap(true);
text.setWrapStyleWord(true);
text.setEditable(false);
PrintStream con=new PrintStream(new TextAreaOutputStream(text));
System.setOut(con);
System.setErr(con);
JScrollPane pane = new JScrollPane();
pane.setViewportView(text);
super.setLayout(new GridLayout(1,1));
super.add(pane);
super.setPreferredSize(new Dimension(400,300));
}
}
@@ -0,0 +1,64 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.gui;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import javax.swing.JFrame;
import javax.swing.JTabbedPane;
import javax.swing.WindowConstants;
import com.jagrosh.jmusicbot.Bot;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class GUI extends JFrame {
private final ConsolePanel console;
private final GuildsPanel guilds;
private final Bot bot;
public GUI(Bot bot) {
super();
this.bot = bot;
console = new ConsolePanel();
guilds = new GuildsPanel(bot);
}
public void init()
{
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setTitle("JMusicBot");
JTabbedPane tabs = new JTabbedPane();
//tabs.add("Guilds", guilds);
tabs.add("Console", console);
getContentPane().add(tabs);
pack();
setLocationRelativeTo(null);
setVisible(true);
addWindowListener(new WindowListener() {
@Override public void windowOpened(WindowEvent e) {}
@Override public void windowClosing(WindowEvent e) {bot.shutdown();}
@Override public void windowClosed(WindowEvent e) {}
@Override public void windowIconified(WindowEvent e) {}
@Override public void windowDeiconified(WindowEvent e) {}
@Override public void windowActivated(WindowEvent e) {}
@Override public void windowDeactivated(WindowEvent e) {}
});
}
}
@@ -0,0 +1,111 @@
/*
* Copyright 2017 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.gui;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent;
import com.jagrosh.jmusicbot.Bot;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import com.jagrosh.jmusicbot.utils.FormatUtil;
import net.dv8tion.jda.core.entities.Guild;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class GuildsPanel extends JPanel {
private final Bot bot;
private final JList guildList;
private final JTextArea guildQueue;
private int index = -1;
public GuildsPanel(Bot bot)
{
super();
super.setLayout(new GridBagLayout());
this.bot = bot;
guildList = new JList();
guildQueue = new JTextArea();
guildList.setModel(new DefaultListModel());
guildList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
guildList.setFixedCellHeight(20);
guildList.setPreferredSize(new Dimension(100,300));
guildQueue.setPreferredSize(new Dimension(300,300));
guildQueue.setEditable(false);
JScrollPane pane = new JScrollPane();
JScrollPane pane2 = new JScrollPane();
pane.setViewportView(guildList);
pane2.setViewportView(guildQueue);
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.LINE_START;
c.gridx = 0;
c.gridwidth = 1;
super.add(pane, c);
c.gridx = 1;
c.gridwidth = 3;
super.add(pane2, c);
//bot.registerPanel(this);
guildList.addListSelectionListener((ListSelectionEvent e) -> {
index = guildList.getSelectedIndex();
//bot.updatePanel();
});
}
public void updateList(List<Guild> guilds)
{
String[] strs = new String[guilds.size()];
for(int i=0; i<guilds.size(); i++)
strs[i] = guilds.get(i).getName();
guildList.setListData(strs);
}
public int getIndex()
{
return guildList.getSelectedIndex();
}
public void updatePanel(AudioHandler handler) {
StringBuilder builder = new StringBuilder("Now Playing: ");
if(handler==null || handler.getCurrentTrack()==null)
{
builder.append("nothing");
}
else
{
builder.append(handler.getCurrentTrack().getTrack().getInfo().title)
.append(" [")
.append(FormatUtil.formatTime(handler.getCurrentTrack().getTrack().getDuration()))
.append("]\n");
for(int i=0; i<handler.getQueue().size(); i++)
builder.append("\n").append(i+1).append(". ").append(handler.getQueue().get(i).getTrack().getInfo().title);
}
guildQueue.setText(builder.toString());
guildQueue.updateUI();
}
}
@@ -0,0 +1,147 @@
/*
* Copyright 2017 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.gui;
import java.awt.*;
import java.io.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
/**
*
* @author Lawrence Dol
*/
public class TextAreaOutputStream extends OutputStream {
// *************************************************************************************************
// INSTANCE MEMBERS
// *************************************************************************************************
private byte[] oneByte; // array for write(int val);
private Appender appender; // most recent action
public TextAreaOutputStream(JTextArea txtara) {
this(txtara,1000);
}
public TextAreaOutputStream(JTextArea txtara, int maxlin) {
if(maxlin<1) { throw new IllegalArgumentException("TextAreaOutputStream maximum lines must be positive (value="+maxlin+")"); }
oneByte=new byte[1];
appender=new Appender(txtara,maxlin);
}
/** Clear the current console text area. */
public synchronized void clear() {
if(appender!=null) { appender.clear(); }
}
@Override
public synchronized void close() {
appender=null;
}
@Override
public synchronized void flush() {
}
@Override
public synchronized void write(int val) {
oneByte[0]=(byte)val;
write(oneByte,0,1);
}
@Override
public synchronized void write(byte[] ba) {
write(ba,0,ba.length);
}
@Override
public synchronized void write(byte[] ba,int str,int len) {
if(appender!=null) { appender.append(bytesToString(ba,str,len)); }
}
//@edu.umd.cs.findbugs.annotations.SuppressWarnings("DM_DEFAULT_ENCODING")
static private String bytesToString(byte[] ba, int str, int len) {
try { return new String(ba,str,len,"UTF-8"); } catch(UnsupportedEncodingException thr) { return new String(ba,str,len); } // all JVMs are required to support UTF-8
}
// *************************************************************************************************
// STATIC MEMBERS
// *************************************************************************************************
static class Appender
implements Runnable
{
private final JTextArea textArea;
private final int maxLines; // maximum lines allowed in text area
private final LinkedList<Integer> lengths; // length of lines within text area
private final List<String> values; // values waiting to be appended
private int curLength; // length of current line
private boolean clear;
private boolean queue;
Appender(JTextArea txtara, int maxlin) {
textArea =txtara;
maxLines =maxlin;
lengths =new LinkedList<>();
values =new ArrayList<>();
curLength=0;
clear =false;
queue =true;
}
synchronized void append(String val) {
values.add(val);
if(queue) { queue=false; EventQueue.invokeLater(this); }
}
synchronized void clear() {
clear=true;
curLength=0;
lengths.clear();
values.clear();
if(queue) { queue=false; EventQueue.invokeLater(this); }
}
// MUST BE THE ONLY METHOD THAT TOUCHES textArea!
@Override
public synchronized void run() {
if(clear) { textArea.setText(""); }
values.stream().map((val) -> {
curLength+=val.length();
return val;
}).map((val) -> {
if(val.endsWith(EOL1) || val.endsWith(EOL2)) {
if(lengths.size()>=maxLines) { textArea.replaceRange("",0,lengths.removeFirst()); }
lengths.addLast(curLength);
curLength=0;
}
return val;
}).forEach((val) -> {
textArea.append(val);
});
values.clear();
clear =false;
queue =true;
}
static private final String EOL1="\n";
static private final String EOL2=System.getProperty("line.separator",EOL1);
}
} /* END PUBLIC CLASS */
@@ -0,0 +1,186 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.jagrosh.jmusicbot.playlist;
import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
/**
*
* @author John Grosh (john.a.grosh@gmail.com)
*/
public class Playlist {
private final String name;
private final List<String> items;
private List<AudioTrack> tracks;
private List<PlaylistLoadError> errors;
private Playlist(String name, List<String> items)
{
this.name = name;
this.items = items;
}
public void loadTracks(AudioPlayerManager manager, Runnable callback)
{
if(tracks==null)
{
tracks = new LinkedList<>();
errors = new LinkedList<>();
for(int i=0; i<items.size(); i++)
{
boolean last = i+1==items.size();
int index = i;
manager.loadItemOrdered(name, items.get(i), new AudioLoadResultHandler() {
@Override
public void trackLoaded(AudioTrack at) {
tracks.add(at);
if(last && callback!=null)
callback.run();
}
@Override
public void playlistLoaded(AudioPlaylist ap) {
if(ap.isSearchResult())
tracks.add(ap.getTracks().get(0));
else if(ap.getSelectedTrack()!=null)
tracks.add(ap.getSelectedTrack());
else
tracks.addAll(ap.getTracks());
if(last && callback!=null)
callback.run();
}
@Override
public void noMatches() {
errors.add(new PlaylistLoadError(index, items.get(index), "No matches found."));
if(last && callback!=null)
callback.run();
}
@Override
public void loadFailed(FriendlyException fe) {
errors.add(new PlaylistLoadError(index, items.get(index), "Failed to load track: "+fe.getLocalizedMessage()));
if(last && callback!=null)
callback.run();
}
});
}
}
}
public String getName()
{
return name;
}
public List<String> getItems()
{
return items;
}
public List<AudioTrack> getTracks()
{
return tracks;
}
public List<PlaylistLoadError> getErrors()
{
return errors;
}
public static void createFolder()
{
try
{
Files.createDirectory(Paths.get("Playlists"));
} catch (IOException ex)
{}
}
public static boolean folderExists()
{
return Files.exists(Paths.get("Playlists"));
}
public static List<String> getPlaylists()
{
if(folderExists())
{
File folder = new File("Playlists");
return Arrays.asList(folder.listFiles((pathname) -> pathname.getName().endsWith(".txt")))
.stream().map(f -> f.getName().substring(0,f.getName().length()-4)).collect(Collectors.toList());
}
else
{
createFolder();
return null;
}
}
public static Playlist loadPlaylist(String name)
{
try
{
if(folderExists())
{
return new Playlist(name, Files.readAllLines(Paths.get("Playlists"+File.separator+name+".txt"))
.stream()
.map((str) -> str.trim())
.filter((s) -> (!s.isEmpty() && !s.startsWith("#") && !s.startsWith("//")))
.collect(Collectors.toList()));
}
else
{
createFolder();
return null;
}
}
catch(IOException e)
{
return null;
}
}
public class PlaylistLoadError {
private final int number;
private final String item;
private final String reason;
private PlaylistLoadError(int number, String item, String reason)
{
this.number = number;
this.item = item;
this.reason = reason;
}
public int getIndex()
{
return number;
}
public String getItem()
{
return item;
}
public String getReason()
{
return reason;
}
}
}
@@ -0,0 +1,123 @@
/*
* Copyright 2016 John Grosh (jagrosh).
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.queue;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
*
* @author John Grosh (jagrosh)
* @param <T>
*/
public class FairQueue<T extends Queueable> {
private final List<T> list = new ArrayList<>();
private final Set<String> set = new HashSet<>();
public int add(T item)
{
int lastIndex;
for(lastIndex=list.size()-1; lastIndex>-1; lastIndex--)
if(list.get(lastIndex).getIdentifier().equals(item.getIdentifier()))
break;
lastIndex++;
set.clear();
for(; lastIndex<list.size(); lastIndex++)
{
if(set.contains(list.get(lastIndex).getIdentifier()))
break;
set.add(list.get(lastIndex).getIdentifier());
}
list.add(lastIndex, item);
return lastIndex;
}
public int size()
{
return list.size();
}
public T pull()
{
return list.remove(0);
}
public boolean isEmpty()
{
return list.isEmpty();
}
public List<T> getList()
{
return list;
}
public T get(int index)
{
return list.get(index);
}
public T remove(int index)
{
return list.remove(index);
}
public int removeAll(String identifier)
{
int count = 0;
for(int i=list.size()-1; i>=0; i--)
{
if(list.get(i).getIdentifier().equals(identifier))
{
list.remove(i);
count++;
}
}
return count;
}
public void clear()
{
list.clear();
}
public int shuffle(String identifier)
{
List<Integer> iset = new ArrayList<>();
for(int i=0; i<list.size(); i++)
{
if(list.get(i).getIdentifier().equals(identifier))
iset.add(i);
}
for(int j=0; j<iset.size(); j++)
{
int first = iset.get(j);
int second = iset.get((int)(Math.random()*iset.size()));
T temp = list.get(first);
list.set(first, list.get(second));
list.set(second, temp);
}
return iset.size();
}
public void skip(int number)
{
for(int i=0; i<number; i++)
list.remove(0);
}
}
@@ -0,0 +1,25 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.queue;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public interface Queueable {
public String getIdentifier();
}
@@ -0,0 +1,140 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.utils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.dv8tion.jda.core.entities.Guild;
import net.dv8tion.jda.core.entities.Role;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.entities.VoiceChannel;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class FinderUtil {
public static List<TextChannel> findTextChannel(String query, Guild guild)
{
String id;
if(query.matches("<#\\d+>"))
{
id = query.replaceAll("<#(\\d+)>", "$1");
TextChannel tc = guild.getJDA().getTextChannelById(id);
if(tc!=null && tc.getGuild().equals(guild))
return Collections.singletonList(tc);
}
ArrayList<TextChannel> exact = new ArrayList<>();
ArrayList<TextChannel> wrongcase = new ArrayList<>();
ArrayList<TextChannel> startswith = new ArrayList<>();
ArrayList<TextChannel> contains = new ArrayList<>();
String lowerquery = query.toLowerCase();
guild.getTextChannels().stream().forEach((tc) -> {
if(tc.getName().equals(lowerquery))
exact.add(tc);
else if (tc.getName().equalsIgnoreCase(lowerquery) && exact.isEmpty())
wrongcase.add(tc);
else if (tc.getName().toLowerCase().startsWith(lowerquery) && wrongcase.isEmpty())
startswith.add(tc);
else if (tc.getName().toLowerCase().contains(lowerquery) && startswith.isEmpty())
contains.add(tc);
});
if(!exact.isEmpty())
return exact;
if(!wrongcase.isEmpty())
return wrongcase;
if(!startswith.isEmpty())
return startswith;
return contains;
}
public static List<VoiceChannel> findVoiceChannel(String query, Guild guild)
{
String id;
if(query.matches("<#\\d+>"))
{
id = query.replaceAll("<#(\\d+)>", "$1");
VoiceChannel tc = guild.getJDA().getVoiceChannelById(id);
if(tc!=null && tc.getGuild().equals(guild))
return Collections.singletonList(tc);
}
ArrayList<VoiceChannel> exact = new ArrayList<>();
ArrayList<VoiceChannel> wrongcase = new ArrayList<>();
ArrayList<VoiceChannel> startswith = new ArrayList<>();
ArrayList<VoiceChannel> contains = new ArrayList<>();
String lowerquery = query.toLowerCase();
guild.getVoiceChannels().stream().forEach((tc) -> {
if(tc.getName().equals(lowerquery))
exact.add(tc);
else if (tc.getName().equalsIgnoreCase(lowerquery) && exact.isEmpty())
wrongcase.add(tc);
else if (tc.getName().toLowerCase().startsWith(lowerquery) && wrongcase.isEmpty())
startswith.add(tc);
else if (tc.getName().toLowerCase().contains(lowerquery) && startswith.isEmpty())
contains.add(tc);
});
if(!exact.isEmpty())
return exact;
if(!wrongcase.isEmpty())
return wrongcase;
if(!startswith.isEmpty())
return startswith;
return contains;
}
public static List<Role> findRole(String query, Guild guild)
{
String id;
if(query.matches("<@&\\d+>"))
{
id = query.replaceAll("<@&(\\d+)>", "$1");
Role role = guild.getRoleById(id);
if(role!=null)
return Collections.singletonList(role);
}
if(query.matches("[Ii][Dd]\\s*:\\s*\\d+"))
{
id = query.replaceAll("[Ii][Dd]\\s*:\\s*(\\d+)", "$1");
for(Role role: guild.getRoles())
if(role.getId().equals(id))
return Collections.singletonList(role);
}
ArrayList<Role> exact = new ArrayList<>();
ArrayList<Role> wrongcase = new ArrayList<>();
ArrayList<Role> startswith = new ArrayList<>();
ArrayList<Role> contains = new ArrayList<>();
String lowerQuery = query.toLowerCase();
guild.getRoles().stream().forEach((role) -> {
if(role.getName().equals(query))
exact.add(role);
else if (role.getName().equalsIgnoreCase(query) && exact.isEmpty())
wrongcase.add(role);
else if (role.getName().toLowerCase().startsWith(lowerQuery) && wrongcase.isEmpty())
startswith.add(role);
else if (role.getName().toLowerCase().contains(lowerQuery) && startswith.isEmpty())
contains.add(role);
});
if(!exact.isEmpty())
return exact;
if(!wrongcase.isEmpty())
return wrongcase;
if(!startswith.isEmpty())
return startswith;
return contains;
}
}
@@ -0,0 +1,117 @@
/*
* Copyright 2016 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.utils;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import java.util.List;
import com.jagrosh.jmusicbot.audio.AudioHandler;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.entities.Role;
import net.dv8tion.jda.core.entities.TextChannel;
import net.dv8tion.jda.core.entities.User;
import net.dv8tion.jda.core.entities.VoiceChannel;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class FormatUtil {
public static String formatTime(long duration)
{
if(duration == Long.MAX_VALUE)
return "LIVE";
long seconds = Math.round(duration/1000.0);
long hours = seconds/(60*60);
seconds %= 60*60;
long minutes = seconds/60;
seconds %= 60;
return (hours>0 ? hours+":" : "") + (minutes<10 ? "0"+minutes : minutes) + ":" + (seconds<10 ? "0"+seconds : seconds);
}
public static String formattedAudio(AudioHandler handler, JDA jda, boolean inTopic)
{
if(handler==null)
return "No music playing\n\u23F9 "+progressBar(-1)+" "+volumeIcon(100);
else if (handler.getCurrentTrack()==null)
return "No music playing\n\u23F9 "+progressBar(-1)+" "+volumeIcon(handler.getPlayer().getVolume());
else
{
String userid = handler.getCurrentTrack().getIdentifier();
User user = jda.getUserById(userid);
AudioTrack track = handler.getCurrentTrack().getTrack();
String title = track.getInfo().title;
if(inTopic && title.length()>30)
title = title.substring(0,27)+"...";
double progress = (double)track.getPosition()/track.getDuration();
String str = "**"+title+"** ["+(user==null||inTopic ? (userid==null ? "autoplay" : "<@"+userid+">") : user.getName())+"]\n\u25B6 "+progressBar(progress)
+" "+(inTopic ? "" : "`")+"["+formatTime(track.getPosition()) + "/" + formatTime(track.getDuration())
+"]"+(inTopic ? "" : "`")+" " +volumeIcon(handler.getPlayer().getVolume())
+(inTopic ? "" : "\n**<"+track.getInfo().uri+">**");
return str;
}
}
private static String progressBar(double percent)
{
String str = "";
for(int i=0; i<8; i++)
if(i == (int)(percent*8))
str+="\uD83D\uDD18";
else
str+="";
return str;
}
public static String volumeIcon(int volume)
{
if(volume == 0)
return "\uD83D\uDD07";
if(volume < 30)
return "\uD83D\uDD08";
if(volume < 70)
return "\uD83D\uDD09";
return "\uD83D\uDD0A";
}
public static String listOfTChannels(List<TextChannel> list, String query)
{
String out = " Multiple text channels found matching \""+query+"\":";
for(int i=0; i<6 && i<list.size(); i++)
out+="\n - "+list.get(i).getName()+" (<#"+list.get(i).getId()+">)";
if(list.size()>6)
out+="\n**And "+(list.size()-6)+" more...**";
return out;
}
public static String listOfVChannels(List<VoiceChannel> list, String query)
{
String out = " Multiple voice channels found matching \""+query+"\":";
for(int i=0; i<6 && i<list.size(); i++)
out+="\n - "+list.get(i).getName()+" (ID:"+list.get(i).getId()+")";
if(list.size()>6)
out+="\n**And "+(list.size()-6)+" more...**";
return out;
}
public static String listOfRoles(List<Role> list, String query)
{
String out = " Multiple text channels found matching \""+query+"\":";
for(int i=0; i<6 && i<list.size(); i++)
out+="\n - "+list.get(i).getName()+" (ID:"+list.get(i).getId()+")";
if(list.size()>6)
out+="\n**And "+(list.size()-6)+" more...**";
return out;
}
}
@@ -0,0 +1,42 @@
/*
* Copyright 2017 John Grosh <john.a.grosh@gmail.com>.
*
* 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
*
* http://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.
*/
package com.jagrosh.jmusicbot.utils;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
/**
*
* @author John Grosh <john.a.grosh@gmail.com>
*/
public class OtherUtil {
public static InputStream imageFromUrl(String url)
{
if(url==null)
return null;
try {
URL u = new URL(url);
URLConnection urlConnection = u.openConnection();
urlConnection.setRequestProperty("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36");
return urlConnection.getInputStream();
} catch(IOException|IllegalArgumentException e) {
}
return null;
}
}