queue = new FairQueue<>(); 33 | int size = 100; 34 | for(int i=0; iqueue = new FairQueue<>(); 43 | int size = 100; 44 | for(int i=0; i . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.dj; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jmusicbot.Bot; 20 | import com.jagrosh.jmusicbot.audio.AudioHandler; 21 | import com.jagrosh.jmusicbot.commands.DJCommand; 22 | 23 | /** 24 | * 25 | * @author John Grosh 26 | */ 27 | public class PauseCmd extends DJCommand 28 | { 29 | public PauseCmd(Bot bot) 30 | { 31 | super(bot); 32 | this.name = "pause"; 33 | this.help = "pauses the current song"; 34 | this.aliases = bot.getConfig().getAliases(this.name); 35 | this.bePlaying = true; 36 | } 37 | 38 | @Override 39 | public void doCommand(CommandEvent event) 40 | { 41 | AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); 42 | if(handler.getPlayer().isPaused()) 43 | { 44 | event.replyWarning("The player is already paused! Use `"+event.getClient().getPrefix()+"play` to unpause!"); 45 | return; 46 | } 47 | handler.getPlayer().setPaused(true); 48 | event.replySuccess("Paused **"+handler.getPlayer().getPlayingTrack().getInfo().title+"**. Type `"+event.getClient().getPrefix()+"play` to unpause!"); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/audio/RequestMetadata.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.audio; 17 | 18 | import net.dv8tion.jda.api.entities.User; 19 | 20 | /** 21 | * 22 | * @author John Grosh (john.a.grosh@gmail.com) 23 | */ 24 | public class RequestMetadata 25 | { 26 | public static final RequestMetadata EMPTY = new RequestMetadata(null); 27 | 28 | public final UserInfo user; 29 | 30 | public RequestMetadata(User user) 31 | { 32 | this.user = user == null ? null : new UserInfo(user.getIdLong(), user.getName(), user.getDiscriminator(), user.getEffectiveAvatarUrl()); 33 | } 34 | 35 | public long getOwner() 36 | { 37 | return user == null ? 0L : user.id; 38 | } 39 | 40 | public class RequestInfo 41 | { 42 | public final String query, url; 43 | 44 | private RequestInfo(String query, String url) 45 | { 46 | this.query = query; 47 | this.url = url; 48 | } 49 | } 50 | 51 | public class UserInfo 52 | { 53 | public final long id; 54 | public final String username, discrim, avatar; 55 | 56 | private UserInfo(long id, String username, String discrim, String avatar) 57 | { 58 | this.id = id; 59 | this.username = username; 60 | this.discrim = discrim; 61 | this.avatar = avatar; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/admin/PrefixCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.admin; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jmusicbot.Bot; 20 | import com.jagrosh.jmusicbot.commands.AdminCommand; 21 | import com.jagrosh.jmusicbot.settings.Settings; 22 | 23 | /** 24 | * 25 | * @author John Grosh (john.a.grosh@gmail.com) 26 | */ 27 | public class PrefixCmd extends AdminCommand 28 | { 29 | public PrefixCmd(Bot bot) 30 | { 31 | this.name = "prefix"; 32 | this.help = "sets a server-specific prefix"; 33 | this.arguments = " "; 34 | this.aliases = bot.getConfig().getAliases(this.name); 35 | } 36 | 37 | @Override 38 | protected void execute(CommandEvent event) 39 | { 40 | if(event.getArgs().isEmpty()) 41 | { 42 | event.replyError("Please include a prefix or NONE"); 43 | return; 44 | } 45 | 46 | Settings s = event.getClient().getSettingsFor(event.getGuild()); 47 | if(event.getArgs().equalsIgnoreCase("none")) 48 | { 49 | s.setPrefix(null); 50 | event.replySuccess("Prefix cleared."); 51 | } 52 | else 53 | { 54 | s.setPrefix(event.getArgs()); 55 | event.replySuccess("Custom prefix set to `" + event.getArgs() + "` on *" + event.getGuild().getName() + "*"); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/music/NowplayingCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.music; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jmusicbot.Bot; 20 | import com.jagrosh.jmusicbot.audio.AudioHandler; 21 | import com.jagrosh.jmusicbot.commands.MusicCommand; 22 | import net.dv8tion.jda.api.Permission; 23 | import net.dv8tion.jda.api.entities.Message; 24 | 25 | /** 26 | * 27 | * @author John Grosh 28 | */ 29 | public class NowplayingCmd extends MusicCommand 30 | { 31 | public NowplayingCmd(Bot bot) 32 | { 33 | super(bot); 34 | this.name = "nowplaying"; 35 | this.help = "shows the song that is currently playing"; 36 | this.aliases = bot.getConfig().getAliases(this.name); 37 | this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS}; 38 | } 39 | 40 | @Override 41 | public void doCommand(CommandEvent event) 42 | { 43 | AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); 44 | Message m = handler.getNowPlaying(event.getJDA()); 45 | if(m==null) 46 | { 47 | event.reply(handler.getNoMusicPlaying(event.getJDA())); 48 | bot.getNowplayingHandler().clearLastNPMessage(event.getGuild()); 49 | } 50 | else 51 | { 52 | event.reply(m, msg -> bot.getNowplayingHandler().setLastNPMessage(msg)); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/owner/EvalCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 John Grosh (jagrosh). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.owner; 17 | 18 | import javax.script.ScriptEngine; 19 | import javax.script.ScriptEngineManager; 20 | import com.jagrosh.jdautilities.command.CommandEvent; 21 | import com.jagrosh.jmusicbot.Bot; 22 | import com.jagrosh.jmusicbot.commands.OwnerCommand; 23 | 24 | /** 25 | * 26 | * @author John Grosh (jagrosh) 27 | */ 28 | public class EvalCmd extends OwnerCommand 29 | { 30 | private final Bot bot; 31 | 32 | public EvalCmd(Bot bot) 33 | { 34 | this.bot = bot; 35 | this.name = "eval"; 36 | this.help = "evaluates nashorn code"; 37 | this.aliases = bot.getConfig().getAliases(this.name); 38 | this.guildOnly = false; 39 | } 40 | 41 | @Override 42 | protected void execute(CommandEvent event) 43 | { 44 | ScriptEngine se = new ScriptEngineManager().getEngineByName("Nashorn"); 45 | se.put("bot", bot); 46 | se.put("event", event); 47 | se.put("jda", event.getJDA()); 48 | se.put("guild", event.getGuild()); 49 | se.put("channel", event.getChannel()); 50 | try 51 | { 52 | event.reply(event.getClient().getSuccess()+" Evaluated Successfully:\n```\n"+se.eval(event.getArgs())+" ```"); 53 | } 54 | catch(Exception e) 55 | { 56 | event.reply(event.getClient().getError()+" An exception was thrown:\n```\n"+e+" ```"); 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/owner/SetnameCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.owner; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jmusicbot.Bot; 20 | import com.jagrosh.jmusicbot.commands.OwnerCommand; 21 | import net.dv8tion.jda.api.exceptions.RateLimitedException; 22 | 23 | /** 24 | * 25 | * @author John Grosh 26 | */ 27 | public class SetnameCmd extends OwnerCommand 28 | { 29 | public SetnameCmd(Bot bot) 30 | { 31 | this.name = "setname"; 32 | this.help = "sets the name of the bot"; 33 | this.arguments = " "; 34 | this.aliases = bot.getConfig().getAliases(this.name); 35 | this.guildOnly = false; 36 | } 37 | 38 | @Override 39 | protected void execute(CommandEvent event) 40 | { 41 | try 42 | { 43 | String oldname = event.getSelfUser().getName(); 44 | event.getSelfUser().getManager().setName(event.getArgs()).complete(false); 45 | event.reply(event.getClient().getSuccess()+" Name changed from `"+oldname+"` to `"+event.getArgs()+"`"); 46 | } 47 | catch(RateLimitedException e) 48 | { 49 | event.reply(event.getClient().getError()+" Name can only be changed twice per hour!"); 50 | } 51 | catch(Exception e) 52 | { 53 | event.reply(event.getClient().getError()+" That name is not valid!"); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/owner/SetstatusCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.owner; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jmusicbot.Bot; 20 | import com.jagrosh.jmusicbot.commands.OwnerCommand; 21 | import net.dv8tion.jda.api.OnlineStatus; 22 | 23 | /** 24 | * 25 | * @author John Grosh 26 | */ 27 | public class SetstatusCmd extends OwnerCommand 28 | { 29 | public SetstatusCmd(Bot bot) 30 | { 31 | this.name = "setstatus"; 32 | this.help = "sets the status the bot displays"; 33 | this.arguments = " "; 34 | this.aliases = bot.getConfig().getAliases(this.name); 35 | this.guildOnly = false; 36 | } 37 | 38 | @Override 39 | protected void execute(CommandEvent event) 40 | { 41 | try { 42 | OnlineStatus status = OnlineStatus.fromKey(event.getArgs()); 43 | if(status==OnlineStatus.UNKNOWN) 44 | { 45 | event.replyError("Please include one of the following statuses: `ONLINE`, `IDLE`, `DND`, `INVISIBLE`"); 46 | } 47 | else 48 | { 49 | event.getJDA().getPresence().setStatus(status); 50 | event.replySuccess("Set the status to `"+status.getKey().toUpperCase()+"`"); 51 | } 52 | } catch(Exception e) { 53 | event.reply(event.getClient().getError()+" The status could not be set!"); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/music/ShuffleCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.music; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jmusicbot.Bot; 20 | import com.jagrosh.jmusicbot.audio.AudioHandler; 21 | import com.jagrosh.jmusicbot.commands.MusicCommand; 22 | 23 | /** 24 | * 25 | * @author John Grosh 26 | */ 27 | public class ShuffleCmd extends MusicCommand 28 | { 29 | public ShuffleCmd(Bot bot) 30 | { 31 | super(bot); 32 | this.name = "shuffle"; 33 | this.help = "shuffles songs you have added"; 34 | this.aliases = bot.getConfig().getAliases(this.name); 35 | this.beListening = true; 36 | this.bePlaying = true; 37 | } 38 | 39 | @Override 40 | public void doCommand(CommandEvent event) 41 | { 42 | AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); 43 | int s = handler.getQueue().shuffle(event.getAuthor().getIdLong()); 44 | switch (s) 45 | { 46 | case 0: 47 | event.replyError("You don't have any music in the queue to shuffle!"); 48 | break; 49 | case 1: 50 | event.replyWarning("You only have one song in the queue!"); 51 | break; 52 | default: 53 | event.replySuccess("You successfully shuffled your "+s+" entries."); 54 | break; 55 | } 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/audio/QueuedTrack.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.audio; 17 | 18 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 19 | import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; 20 | import com.jagrosh.jmusicbot.queue.Queueable; 21 | import com.jagrosh.jmusicbot.utils.FormatUtil; 22 | import net.dv8tion.jda.api.entities.User; 23 | 24 | /** 25 | * 26 | * @author John Grosh 27 | */ 28 | public class QueuedTrack implements Queueable 29 | { 30 | private final AudioTrack track; 31 | 32 | public QueuedTrack(AudioTrack track, User owner) 33 | { 34 | this(track, new RequestMetadata(owner)); 35 | } 36 | 37 | public QueuedTrack(AudioTrack track, RequestMetadata rm) 38 | { 39 | this.track = track; 40 | this.track.setUserData(rm); 41 | } 42 | 43 | @Override 44 | public long getIdentifier() 45 | { 46 | return track.getUserData(RequestMetadata.class).getOwner(); 47 | } 48 | 49 | public AudioTrack getTrack() 50 | { 51 | return track; 52 | } 53 | 54 | @Override 55 | public String toString() 56 | { 57 | String entry = "`[" + FormatUtil.formatTime(track.getDuration()) + "]` "; 58 | AudioTrackInfo trackInfo = track.getInfo(); 59 | entry = entry + (trackInfo.uri.startsWith("http") ? "[**" + trackInfo.title + "**]("+trackInfo.uri+")" : "**" + trackInfo.title + "**"); 60 | return entry + " - <@" + track.getUserData(RequestMetadata.class).getOwner() + ">"; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/admin/SkipratioCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.admin; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jmusicbot.Bot; 20 | import com.jagrosh.jmusicbot.commands.AdminCommand; 21 | import com.jagrosh.jmusicbot.settings.Settings; 22 | 23 | /** 24 | * 25 | * @author John Grosh (john.a.grosh@gmail.com) 26 | */ 27 | public class SkipratioCmd extends AdminCommand 28 | { 29 | public SkipratioCmd(Bot bot) 30 | { 31 | this.name = "setskip"; 32 | this.help = "sets a server-specific skip percentage"; 33 | this.arguments = "<0 - 100>"; 34 | this.aliases = bot.getConfig().getAliases(this.name); 35 | } 36 | 37 | @Override 38 | protected void execute(CommandEvent event) 39 | { 40 | try 41 | { 42 | int val = Integer.parseInt(event.getArgs().endsWith("%") ? event.getArgs().substring(0,event.getArgs().length()-1) : event.getArgs()); 43 | if( val < 0 || val > 100) 44 | { 45 | event.replyError("The provided value must be between 0 and 100!"); 46 | return; 47 | } 48 | Settings s = event.getClient().getSettingsFor(event.getGuild()); 49 | s.setSkipRatio(val / 100.0); 50 | event.replySuccess("Skip percentage has been set to `" + val + "%` of listeners on *" + event.getGuild().getName() + "*"); 51 | } 52 | catch(NumberFormatException ex) 53 | { 54 | event.replyError("Please include an integer between 0 and 100 (default is 55). This number is the percentage of listening users that must vote to skip a song."); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/dj/SkiptoCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.dj; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jmusicbot.Bot; 20 | import com.jagrosh.jmusicbot.audio.AudioHandler; 21 | import com.jagrosh.jmusicbot.commands.DJCommand; 22 | 23 | /** 24 | * 25 | * @author John Grosh 26 | */ 27 | public class SkiptoCmd extends DJCommand 28 | { 29 | public SkiptoCmd(Bot bot) 30 | { 31 | super(bot); 32 | this.name = "skipto"; 33 | this.help = "skips to the specified song"; 34 | this.arguments = " "; 35 | this.aliases = bot.getConfig().getAliases(this.name); 36 | this.bePlaying = true; 37 | } 38 | 39 | @Override 40 | public void doCommand(CommandEvent event) 41 | { 42 | int index = 0; 43 | try 44 | { 45 | index = Integer.parseInt(event.getArgs()); 46 | } 47 | catch(NumberFormatException e) 48 | { 49 | event.reply(event.getClient().getError()+" `"+event.getArgs()+"` is not a valid integer!"); 50 | return; 51 | } 52 | AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); 53 | if(index<1 || index>handler.getQueue().size()) 54 | { 55 | event.reply(event.getClient().getError()+" Position must be a valid integer between 1 and "+handler.getQueue().size()+"!"); 56 | return; 57 | } 58 | handler.getQueue().skip(index-1); 59 | event.reply(event.getClient().getSuccess()+" Skipped to **"+handler.getQueue().get(0).getTrack().getInfo().title+"**"); 60 | handler.getPlayer().stopTrack(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/gui/GUI.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.gui; 17 | 18 | import java.awt.event.WindowEvent; 19 | import java.awt.event.WindowListener; 20 | import javax.swing.JFrame; 21 | import javax.swing.JTabbedPane; 22 | import javax.swing.WindowConstants; 23 | import com.jagrosh.jmusicbot.Bot; 24 | 25 | 26 | /** 27 | * 28 | * @author John Grosh 29 | */ 30 | public class GUI extends JFrame 31 | { 32 | private final ConsolePanel console; 33 | private final Bot bot; 34 | 35 | public GUI(Bot bot) 36 | { 37 | super(); 38 | this.bot = bot; 39 | console = new ConsolePanel(); 40 | } 41 | 42 | public void init() 43 | { 44 | setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); 45 | setTitle("JMusicBot"); 46 | JTabbedPane tabs = new JTabbedPane(); 47 | tabs.add("Console", console); 48 | getContentPane().add(tabs); 49 | pack(); 50 | setLocationRelativeTo(null); 51 | setVisible(true); 52 | addWindowListener(new WindowListener() 53 | { 54 | @Override public void windowOpened(WindowEvent e) { /* unused */ } 55 | @Override public void windowClosing(WindowEvent e) 56 | { 57 | try 58 | { 59 | bot.shutdown(); 60 | } 61 | catch(Exception ex) 62 | { 63 | System.exit(0); 64 | } 65 | } 66 | @Override public void windowClosed(WindowEvent e) { /* unused */ } 67 | @Override public void windowIconified(WindowEvent e) { /* unused */ } 68 | @Override public void windowDeiconified(WindowEvent e) { /* unused */ } 69 | @Override public void windowActivated(WindowEvent e) { /* unused */ } 70 | @Override public void windowDeactivated(WindowEvent e) { /* unused */ } 71 | }); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/music/PlaylistsCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.music; 17 | 18 | import java.util.List; 19 | import com.jagrosh.jdautilities.command.CommandEvent; 20 | import com.jagrosh.jmusicbot.Bot; 21 | import com.jagrosh.jmusicbot.commands.MusicCommand; 22 | 23 | /** 24 | * 25 | * @author John Grosh 26 | */ 27 | public class PlaylistsCmd extends MusicCommand 28 | { 29 | public PlaylistsCmd(Bot bot) 30 | { 31 | super(bot); 32 | this.name = "playlists"; 33 | this.help = "shows the available playlists"; 34 | this.aliases = bot.getConfig().getAliases(this.name); 35 | this.guildOnly = true; 36 | this.beListening = false; 37 | this.beListening = false; 38 | } 39 | 40 | @Override 41 | public void doCommand(CommandEvent event) 42 | { 43 | if(!bot.getPlaylistLoader().folderExists()) 44 | bot.getPlaylistLoader().createFolder(); 45 | if(!bot.getPlaylistLoader().folderExists()) 46 | { 47 | event.reply(event.getClient().getWarning()+" Playlists folder does not exist and could not be created!"); 48 | return; 49 | } 50 | List list = bot.getPlaylistLoader().getPlaylistNames(); 51 | if(list==null) 52 | event.reply(event.getClient().getError()+" Failed to load available playlists!"); 53 | else if(list.isEmpty()) 54 | event.reply(event.getClient().getWarning()+" There are no playlists in the Playlists folder!"); 55 | else 56 | { 57 | StringBuilder builder = new StringBuilder(event.getClient().getSuccess()+" Available playlists:\n"); 58 | list.forEach(str -> builder.append("`").append(str).append("` ")); 59 | builder.append("\nType `").append(event.getClient().getTextualPrefix()).append("play playlist ` to play a playlist"); 60 | event.reply(builder.toString()); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/dj/VolumeCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.dj; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jmusicbot.Bot; 20 | import com.jagrosh.jmusicbot.audio.AudioHandler; 21 | import com.jagrosh.jmusicbot.commands.DJCommand; 22 | import com.jagrosh.jmusicbot.settings.Settings; 23 | import com.jagrosh.jmusicbot.utils.FormatUtil; 24 | 25 | /** 26 | * 27 | * @author John Grosh 28 | */ 29 | public class VolumeCmd extends DJCommand 30 | { 31 | public VolumeCmd(Bot bot) 32 | { 33 | super(bot); 34 | this.name = "volume"; 35 | this.aliases = bot.getConfig().getAliases(this.name); 36 | this.help = "sets or shows volume"; 37 | this.arguments = "[0-150]"; 38 | } 39 | 40 | @Override 41 | public void doCommand(CommandEvent event) 42 | { 43 | AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); 44 | Settings settings = event.getClient().getSettingsFor(event.getGuild()); 45 | int volume = handler.getPlayer().getVolume(); 46 | if(event.getArgs().isEmpty()) 47 | { 48 | event.reply(FormatUtil.volumeIcon(volume)+" Current volume is `"+volume+"`"); 49 | } 50 | else 51 | { 52 | int nvolume; 53 | try{ 54 | nvolume = Integer.parseInt(event.getArgs()); 55 | }catch(NumberFormatException e){ 56 | nvolume = -1; 57 | } 58 | if(nvolume<0 || nvolume>150) 59 | event.reply(event.getClient().getError()+" Volume must be a valid integer between 0 and 150!"); 60 | else 61 | { 62 | handler.getPlayer().setVolume(nvolume); 63 | settings.setVolume(nvolume); 64 | event.reply(FormatUtil.volumeIcon(nvolume)+" Volume changed from `"+volume+"` to `"+nvolume+"`"); 65 | } 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/owner/SetavatarCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.owner; 17 | 18 | import java.io.IOException; 19 | import java.io.InputStream; 20 | import com.jagrosh.jdautilities.command.CommandEvent; 21 | import com.jagrosh.jmusicbot.Bot; 22 | import com.jagrosh.jmusicbot.commands.OwnerCommand; 23 | import com.jagrosh.jmusicbot.utils.OtherUtil; 24 | import net.dv8tion.jda.api.entities.Icon; 25 | 26 | /** 27 | * 28 | * @author John Grosh 29 | */ 30 | public class SetavatarCmd extends OwnerCommand 31 | { 32 | public SetavatarCmd(Bot bot) 33 | { 34 | this.name = "setavatar"; 35 | this.help = "sets the avatar of the bot"; 36 | this.arguments = " "; 37 | this.aliases = bot.getConfig().getAliases(this.name); 38 | this.guildOnly = false; 39 | } 40 | 41 | @Override 42 | protected void execute(CommandEvent event) 43 | { 44 | String url; 45 | if(event.getArgs().isEmpty()) 46 | if(!event.getMessage().getAttachments().isEmpty() && event.getMessage().getAttachments().get(0).isImage()) 47 | url = event.getMessage().getAttachments().get(0).getUrl(); 48 | else 49 | url = null; 50 | else 51 | url = event.getArgs(); 52 | InputStream s = OtherUtil.imageFromUrl(url); 53 | if(s==null) 54 | { 55 | event.reply(event.getClient().getError()+" Invalid or missing URL"); 56 | } 57 | else 58 | { 59 | try { 60 | event.getSelfUser().getManager().setAvatar(Icon.from(s)).queue( 61 | v -> event.reply(event.getClient().getSuccess()+" Successfully changed avatar."), 62 | t -> event.reply(event.getClient().getError()+" Failed to set avatar.")); 63 | } catch(IOException e) { 64 | event.reply(event.getClient().getError()+" Could not load from provided URL."); 65 | } 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/audio/PlayerManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.audio; 17 | 18 | import com.jagrosh.jmusicbot.Bot; 19 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; 20 | import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager; 21 | import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers; 22 | import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioSourceManager; 23 | import net.dv8tion.jda.api.entities.Guild; 24 | 25 | /** 26 | * 27 | * @author John Grosh (john.a.grosh@gmail.com) 28 | */ 29 | public class PlayerManager extends DefaultAudioPlayerManager 30 | { 31 | private final Bot bot; 32 | 33 | public PlayerManager(Bot bot) 34 | { 35 | this.bot = bot; 36 | } 37 | 38 | public void init() 39 | { 40 | TransformativeAudioSourceManager.createTransforms(bot.getConfig().getTransforms()).forEach(t -> registerSourceManager(t)); 41 | AudioSourceManagers.registerRemoteSources(this); 42 | AudioSourceManagers.registerLocalSource(this); 43 | source(YoutubeAudioSourceManager.class).setPlaylistPageCount(10); 44 | } 45 | 46 | public Bot getBot() 47 | { 48 | return bot; 49 | } 50 | 51 | public boolean hasHandler(Guild guild) 52 | { 53 | return guild.getAudioManager().getSendingHandler()!=null; 54 | } 55 | 56 | public AudioHandler setUpHandler(Guild guild) 57 | { 58 | AudioHandler handler; 59 | if(guild.getAudioManager().getSendingHandler()==null) 60 | { 61 | AudioPlayer player = createPlayer(); 62 | player.setVolume(bot.getSettingsManager().getSettings(guild).getVolume()); 63 | handler = new AudioHandler(this, guild, player); 64 | player.addListener(handler); 65 | guild.getAudioManager().setSendingHandler(handler); 66 | } 67 | else 68 | handler = (AudioHandler) guild.getAudioManager().getSendingHandler(); 69 | return handler; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/owner/AutoplaylistCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.owner; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jmusicbot.Bot; 20 | import com.jagrosh.jmusicbot.commands.OwnerCommand; 21 | import com.jagrosh.jmusicbot.settings.Settings; 22 | 23 | /** 24 | * 25 | * @author John Grosh 26 | */ 27 | public class AutoplaylistCmd extends OwnerCommand 28 | { 29 | private final Bot bot; 30 | 31 | public AutoplaylistCmd(Bot bot) 32 | { 33 | this.bot = bot; 34 | this.guildOnly = true; 35 | this.name = "autoplaylist"; 36 | this.arguments = " "; 37 | this.help = "sets the default playlist for the server"; 38 | this.aliases = bot.getConfig().getAliases(this.name); 39 | } 40 | 41 | @Override 42 | public void execute(CommandEvent event) 43 | { 44 | if(event.getArgs().isEmpty()) 45 | { 46 | event.reply(event.getClient().getError()+" Please include a playlist name or NONE"); 47 | return; 48 | } 49 | if(event.getArgs().equalsIgnoreCase("none")) 50 | { 51 | Settings settings = event.getClient().getSettingsFor(event.getGuild()); 52 | settings.setDefaultPlaylist(null); 53 | event.reply(event.getClient().getSuccess()+" Cleared the default playlist for **"+event.getGuild().getName()+"**"); 54 | return; 55 | } 56 | String pname = event.getArgs().replaceAll("\\s+", "_"); 57 | if(bot.getPlaylistLoader().getPlaylist(pname)==null) 58 | { 59 | event.reply(event.getClient().getError()+" Could not find `"+pname+".txt`!"); 60 | } 61 | else 62 | { 63 | Settings settings = event.getClient().getSettingsFor(event.getGuild()); 64 | settings.setDefaultPlaylist(pname); 65 | event.reply(event.getClient().getSuccess()+" The default playlist for **"+event.getGuild().getName()+"** is now `"+pname+"`"); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/admin/SetdjCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.admin; 17 | 18 | import java.util.List; 19 | import com.jagrosh.jdautilities.command.CommandEvent; 20 | import com.jagrosh.jdautilities.commons.utils.FinderUtil; 21 | import com.jagrosh.jmusicbot.Bot; 22 | import com.jagrosh.jmusicbot.commands.AdminCommand; 23 | import com.jagrosh.jmusicbot.settings.Settings; 24 | import com.jagrosh.jmusicbot.utils.FormatUtil; 25 | import net.dv8tion.jda.api.entities.Role; 26 | 27 | /** 28 | * 29 | * @author John Grosh 30 | */ 31 | public class SetdjCmd extends AdminCommand 32 | { 33 | public SetdjCmd(Bot bot) 34 | { 35 | this.name = "setdj"; 36 | this.help = "sets the DJ role for certain music commands"; 37 | this.arguments = " "; 38 | this.aliases = bot.getConfig().getAliases(this.name); 39 | } 40 | 41 | @Override 42 | protected void execute(CommandEvent event) 43 | { 44 | if(event.getArgs().isEmpty()) 45 | { 46 | event.reply(event.getClient().getError()+" Please include a role name or NONE"); 47 | return; 48 | } 49 | Settings s = event.getClient().getSettingsFor(event.getGuild()); 50 | if(event.getArgs().equalsIgnoreCase("none")) 51 | { 52 | s.setDJRole(null); 53 | event.reply(event.getClient().getSuccess()+" DJ role cleared; Only Admins can use the DJ commands."); 54 | } 55 | else 56 | { 57 | List list = FinderUtil.findRoles(event.getArgs(), event.getGuild()); 58 | if(list.isEmpty()) 59 | event.reply(event.getClient().getWarning()+" No Roles found matching \""+event.getArgs()+"\""); 60 | else if (list.size()>1) 61 | event.reply(event.getClient().getWarning()+FormatUtil.listOfRoles(list, event.getArgs())); 62 | else 63 | { 64 | s.setDJRole(list.get(0)); 65 | event.reply(event.getClient().getSuccess()+" DJ commands can now be used by users with the **"+list.get(0).getName()+"** role."); 66 | } 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/admin/SetvcCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.admin; 17 | 18 | import java.util.List; 19 | import com.jagrosh.jdautilities.command.CommandEvent; 20 | import com.jagrosh.jdautilities.commons.utils.FinderUtil; 21 | import com.jagrosh.jmusicbot.Bot; 22 | import com.jagrosh.jmusicbot.commands.AdminCommand; 23 | import com.jagrosh.jmusicbot.settings.Settings; 24 | import com.jagrosh.jmusicbot.utils.FormatUtil; 25 | import net.dv8tion.jda.api.entities.VoiceChannel; 26 | 27 | /** 28 | * 29 | * @author John Grosh 30 | */ 31 | public class SetvcCmd extends AdminCommand 32 | { 33 | public SetvcCmd(Bot bot) 34 | { 35 | this.name = "setvc"; 36 | this.help = "sets the voice channel for playing music"; 37 | this.arguments = " "; 38 | this.aliases = bot.getConfig().getAliases(this.name); 39 | } 40 | 41 | @Override 42 | protected void execute(CommandEvent event) 43 | { 44 | if(event.getArgs().isEmpty()) 45 | { 46 | event.reply(event.getClient().getError()+" Please include a voice channel or NONE"); 47 | return; 48 | } 49 | Settings s = event.getClient().getSettingsFor(event.getGuild()); 50 | if(event.getArgs().equalsIgnoreCase("none")) 51 | { 52 | s.setVoiceChannel(null); 53 | event.reply(event.getClient().getSuccess()+" Music can now be played in any channel"); 54 | } 55 | else 56 | { 57 | List list = FinderUtil.findVoiceChannels(event.getArgs(), event.getGuild()); 58 | if(list.isEmpty()) 59 | event.reply(event.getClient().getWarning()+" No Voice Channels found matching \""+event.getArgs()+"\""); 60 | else if (list.size()>1) 61 | event.reply(event.getClient().getWarning()+FormatUtil.listOfVChannels(list, event.getArgs())); 62 | else 63 | { 64 | s.setVoiceChannel(list.get(0)); 65 | event.reply(event.getClient().getSuccess()+" Music can now only be played in "+list.get(0).getAsMention()); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/admin/SettcCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.admin; 17 | 18 | import java.util.List; 19 | import com.jagrosh.jdautilities.command.CommandEvent; 20 | import com.jagrosh.jdautilities.commons.utils.FinderUtil; 21 | import com.jagrosh.jmusicbot.Bot; 22 | import com.jagrosh.jmusicbot.commands.AdminCommand; 23 | import com.jagrosh.jmusicbot.settings.Settings; 24 | import com.jagrosh.jmusicbot.utils.FormatUtil; 25 | import net.dv8tion.jda.api.entities.TextChannel; 26 | 27 | /** 28 | * 29 | * @author John Grosh 30 | */ 31 | public class SettcCmd extends AdminCommand 32 | { 33 | public SettcCmd(Bot bot) 34 | { 35 | this.name = "settc"; 36 | this.help = "sets the text channel for music commands"; 37 | this.arguments = " "; 38 | this.aliases = bot.getConfig().getAliases(this.name); 39 | } 40 | 41 | @Override 42 | protected void execute(CommandEvent event) 43 | { 44 | if(event.getArgs().isEmpty()) 45 | { 46 | event.reply(event.getClient().getError()+" Please include a text channel or NONE"); 47 | return; 48 | } 49 | Settings s = event.getClient().getSettingsFor(event.getGuild()); 50 | if(event.getArgs().equalsIgnoreCase("none")) 51 | { 52 | s.setTextChannel(null); 53 | event.reply(event.getClient().getSuccess()+" Music commands can now be used in any channel"); 54 | } 55 | else 56 | { 57 | List list = FinderUtil.findTextChannels(event.getArgs(), event.getGuild()); 58 | if(list.isEmpty()) 59 | event.reply(event.getClient().getWarning()+" No Text Channels found matching \""+event.getArgs()+"\""); 60 | else if (list.size()>1) 61 | event.reply(event.getClient().getWarning()+FormatUtil.listOfTChannels(list, event.getArgs())); 62 | else 63 | { 64 | s.setTextChannel(list.get(0)); 65 | event.reply(event.getClient().getSuccess()+" Music commands can now only be used in <#"+list.get(0).getId()+">"); 66 | } 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/dj/RepeatCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.dj; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jmusicbot.Bot; 20 | import com.jagrosh.jmusicbot.commands.DJCommand; 21 | import com.jagrosh.jmusicbot.settings.RepeatMode; 22 | import com.jagrosh.jmusicbot.settings.Settings; 23 | 24 | /** 25 | * 26 | * @author John Grosh 27 | */ 28 | public class RepeatCmd extends DJCommand 29 | { 30 | public RepeatCmd(Bot bot) 31 | { 32 | super(bot); 33 | this.name = "repeat"; 34 | this.help = "re-adds music to the queue when finished"; 35 | this.arguments = "[off|all|single]"; 36 | this.aliases = bot.getConfig().getAliases(this.name); 37 | this.guildOnly = true; 38 | } 39 | 40 | // override musiccommand's execute because we don't actually care where this is used 41 | @Override 42 | protected void execute(CommandEvent event) 43 | { 44 | String args = event.getArgs(); 45 | RepeatMode value; 46 | Settings settings = event.getClient().getSettingsFor(event.getGuild()); 47 | if(args.isEmpty()) 48 | { 49 | if(settings.getRepeatMode() == RepeatMode.OFF) 50 | value = RepeatMode.ALL; 51 | else 52 | value = RepeatMode.OFF; 53 | } 54 | else if(args.equalsIgnoreCase("false") || args.equalsIgnoreCase("off")) 55 | { 56 | value = RepeatMode.OFF; 57 | } 58 | else if(args.equalsIgnoreCase("true") || args.equalsIgnoreCase("on") || args.equalsIgnoreCase("all")) 59 | { 60 | value = RepeatMode.ALL; 61 | } 62 | else if(args.equalsIgnoreCase("one") || args.equalsIgnoreCase("single")) 63 | { 64 | value = RepeatMode.SINGLE; 65 | } 66 | else 67 | { 68 | event.replyError("Valid options are `off`, `all` or `single` (or leave empty to toggle between `off` and `all`)"); 69 | return; 70 | } 71 | settings.setRepeatMode(value); 72 | event.replySuccess("Repeat mode is now `"+value.getUserFriendlyName()+"`"); 73 | } 74 | 75 | @Override 76 | public void doCommand(CommandEvent event) { /* Intentionally Empty */ } 77 | } 78 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/dj/MoveTrackCmd.java: -------------------------------------------------------------------------------- 1 | package com.jagrosh.jmusicbot.commands.dj; 2 | 3 | 4 | import com.jagrosh.jdautilities.command.CommandEvent; 5 | import com.jagrosh.jmusicbot.Bot; 6 | import com.jagrosh.jmusicbot.audio.AudioHandler; 7 | import com.jagrosh.jmusicbot.audio.QueuedTrack; 8 | import com.jagrosh.jmusicbot.commands.DJCommand; 9 | import com.jagrosh.jmusicbot.queue.FairQueue; 10 | 11 | /** 12 | * Command that provides users the ability to move a track in the playlist. 13 | */ 14 | public class MoveTrackCmd extends DJCommand 15 | { 16 | 17 | public MoveTrackCmd(Bot bot) 18 | { 19 | super(bot); 20 | this.name = "movetrack"; 21 | this.help = "move a track in the current queue to a different position"; 22 | this.arguments = " "; 23 | this.aliases = bot.getConfig().getAliases(this.name); 24 | this.bePlaying = true; 25 | } 26 | 27 | @Override 28 | public void doCommand(CommandEvent event) 29 | { 30 | int from; 31 | int to; 32 | 33 | String[] parts = event.getArgs().split("\\s+", 2); 34 | if(parts.length < 2) 35 | { 36 | event.replyError("Please include two valid indexes."); 37 | return; 38 | } 39 | 40 | try 41 | { 42 | // Validate the args 43 | from = Integer.parseInt(parts[0]); 44 | to = Integer.parseInt(parts[1]); 45 | } 46 | catch (NumberFormatException e) 47 | { 48 | event.replyError("Please provide two valid indexes."); 49 | return; 50 | } 51 | 52 | if (from == to) 53 | { 54 | event.replyError("Can't move a track to the same position."); 55 | return; 56 | } 57 | 58 | // Validate that from and to are available 59 | AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler(); 60 | FairQueue queue = handler.getQueue(); 61 | if (isUnavailablePosition(queue, from)) 62 | { 63 | String reply = String.format("`%d` is not a valid position in the queue!", from); 64 | event.replyError(reply); 65 | return; 66 | } 67 | if (isUnavailablePosition(queue, to)) 68 | { 69 | String reply = String.format("`%d` is not a valid position in the queue!", to); 70 | event.replyError(reply); 71 | return; 72 | } 73 | 74 | // Move the track 75 | QueuedTrack track = queue.moveItem(from - 1, to - 1); 76 | String trackTitle = track.getTrack().getInfo().title; 77 | String reply = String.format("Moved **%s** from position `%d` to `%d`.", trackTitle, from, to); 78 | event.replySuccess(reply); 79 | } 80 | 81 | private static boolean isUnavailablePosition(FairQueue queue, int position) 82 | { 83 | return (position < 1 || position > queue.size()); 84 | } 85 | } -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/music/SkipCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.music; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jmusicbot.Bot; 20 | import com.jagrosh.jmusicbot.audio.AudioHandler; 21 | import com.jagrosh.jmusicbot.audio.RequestMetadata; 22 | import com.jagrosh.jmusicbot.commands.MusicCommand; 23 | 24 | /** 25 | * 26 | * @author John Grosh 27 | */ 28 | public class SkipCmd extends MusicCommand 29 | { 30 | public SkipCmd(Bot bot) 31 | { 32 | super(bot); 33 | this.name = "skip"; 34 | this.help = "votes to skip the current song"; 35 | this.aliases = bot.getConfig().getAliases(this.name); 36 | this.beListening = true; 37 | this.bePlaying = true; 38 | } 39 | 40 | @Override 41 | public void doCommand(CommandEvent event) 42 | { 43 | AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); 44 | RequestMetadata rm = handler.getRequestMetadata(); 45 | if(event.getAuthor().getIdLong() == rm.getOwner()) 46 | { 47 | event.reply(event.getClient().getSuccess()+" Skipped **"+handler.getPlayer().getPlayingTrack().getInfo().title+"**"); 48 | handler.getPlayer().stopTrack(); 49 | } 50 | else 51 | { 52 | int listeners = (int)event.getSelfMember().getVoiceState().getChannel().getMembers().stream() 53 | .filter(m -> !m.getUser().isBot() && !m.getVoiceState().isDeafened()).count(); 54 | String msg; 55 | if(handler.getVotes().contains(event.getAuthor().getId())) 56 | msg = event.getClient().getWarning()+" You already voted to skip this song `["; 57 | else 58 | { 59 | msg = event.getClient().getSuccess()+" You voted to skip the song `["; 60 | handler.getVotes().add(event.getAuthor().getId()); 61 | } 62 | int skippers = (int)event.getSelfMember().getVoiceState().getChannel().getMembers().stream() 63 | .filter(m -> handler.getVotes().contains(m.getUser().getId())).count(); 64 | int required = (int)Math.ceil(listeners * bot.getSettingsManager().getSettings(event.getGuild()).getSkipRatio()); 65 | msg += skippers + " votes, " + required + "/" + listeners + " needed]`"; 66 | if(skippers>=required) 67 | { 68 | msg += "\n" + event.getClient().getSuccess() + " Skipped **" + handler.getPlayer().getPlayingTrack().getInfo().title 69 | + "** " + (rm.getOwner() == 0L ? "(autoplay)" : "(requested by **" + rm.user.username + "**)"); 70 | handler.getPlayer().stopTrack(); 71 | } 72 | event.reply(msg); 73 | } 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/audio/AloneInVoiceHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.audio; 17 | 18 | import com.jagrosh.jmusicbot.Bot; 19 | import net.dv8tion.jda.api.entities.Guild; 20 | import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent; 21 | 22 | import java.time.Instant; 23 | import java.util.HashMap; 24 | import java.util.HashSet; 25 | import java.util.Map; 26 | import java.util.Set; 27 | import java.util.concurrent.TimeUnit; 28 | 29 | /** 30 | * 31 | * @author Michaili K (mysteriouscursor+git@protonmail.com) 32 | */ 33 | public class AloneInVoiceHandler 34 | { 35 | private final Bot bot; 36 | private final HashMap aloneSince = new HashMap<>(); 37 | private long aloneTimeUntilStop = 0; 38 | 39 | public AloneInVoiceHandler(Bot bot) 40 | { 41 | this.bot = bot; 42 | } 43 | 44 | public void init() 45 | { 46 | aloneTimeUntilStop = bot.getConfig().getAloneTimeUntilStop(); 47 | if(aloneTimeUntilStop > 0) 48 | bot.getThreadpool().scheduleWithFixedDelay(() -> check(), 0, 5, TimeUnit.SECONDS); 49 | } 50 | 51 | private void check() 52 | { 53 | Set toRemove = new HashSet<>(); 54 | for(Map.Entry entrySet: aloneSince.entrySet()) 55 | { 56 | if(entrySet.getValue().getEpochSecond() > Instant.now().getEpochSecond() - aloneTimeUntilStop) continue; 57 | 58 | Guild guild = bot.getJDA().getGuildById(entrySet.getKey()); 59 | 60 | if(guild == null) 61 | { 62 | toRemove.add(entrySet.getKey()); 63 | continue; 64 | } 65 | 66 | ((AudioHandler) guild.getAudioManager().getSendingHandler()).stopAndClear(); 67 | guild.getAudioManager().closeAudioConnection(); 68 | 69 | toRemove.add(entrySet.getKey()); 70 | } 71 | toRemove.forEach(id -> aloneSince.remove(id)); 72 | } 73 | 74 | public void onVoiceUpdate(GuildVoiceUpdateEvent event) 75 | { 76 | if(aloneTimeUntilStop <= 0) return; 77 | 78 | Guild guild = event.getEntity().getGuild(); 79 | if(!bot.getPlayerManager().hasHandler(guild)) return; 80 | 81 | boolean alone = isAlone(guild); 82 | boolean inList = aloneSince.containsKey(guild.getIdLong()); 83 | 84 | if(!alone && inList) 85 | aloneSince.remove(guild.getIdLong()); 86 | else if(alone && !inList) 87 | aloneSince.put(guild.getIdLong(), Instant.now()); 88 | } 89 | 90 | private boolean isAlone(Guild guild) 91 | { 92 | if(guild.getAudioManager().getConnectedChannel() == null) return false; 93 | return guild.getAudioManager().getConnectedChannel().getMembers().stream() 94 | .noneMatch(x -> 95 | !x.getVoiceState().isDeafened() 96 | && !x.getUser().isBot()); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/general/SettingsCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.general; 17 | 18 | import com.jagrosh.jdautilities.command.Command; 19 | import com.jagrosh.jdautilities.command.CommandEvent; 20 | import com.jagrosh.jmusicbot.Bot; 21 | import com.jagrosh.jmusicbot.settings.RepeatMode; 22 | import com.jagrosh.jmusicbot.settings.Settings; 23 | import com.jagrosh.jmusicbot.utils.FormatUtil; 24 | import net.dv8tion.jda.api.EmbedBuilder; 25 | import net.dv8tion.jda.api.MessageBuilder; 26 | import net.dv8tion.jda.api.entities.Role; 27 | import net.dv8tion.jda.api.entities.TextChannel; 28 | import net.dv8tion.jda.api.entities.VoiceChannel; 29 | 30 | /** 31 | * 32 | * @author John Grosh 33 | */ 34 | public class SettingsCmd extends Command 35 | { 36 | private final static String EMOJI = "\uD83C\uDFA7"; // 🎧 37 | 38 | public SettingsCmd(Bot bot) 39 | { 40 | this.name = "settings"; 41 | this.help = "shows the bots settings"; 42 | this.aliases = bot.getConfig().getAliases(this.name); 43 | this.guildOnly = true; 44 | } 45 | 46 | @Override 47 | protected void execute(CommandEvent event) 48 | { 49 | Settings s = event.getClient().getSettingsFor(event.getGuild()); 50 | MessageBuilder builder = new MessageBuilder() 51 | .append(EMOJI + " **") 52 | .append(FormatUtil.filter(event.getSelfUser().getName())) 53 | .append("** settings:"); 54 | TextChannel tchan = s.getTextChannel(event.getGuild()); 55 | VoiceChannel vchan = s.getVoiceChannel(event.getGuild()); 56 | Role role = s.getRole(event.getGuild()); 57 | EmbedBuilder ebuilder = new EmbedBuilder() 58 | .setColor(event.getSelfMember().getColor()) 59 | .setDescription("Text Channel: " + (tchan == null ? "Any" : "**#" + tchan.getName() + "**") 60 | + "\nVoice Channel: " + (vchan == null ? "Any" : vchan.getAsMention()) 61 | + "\nDJ Role: " + (role == null ? "None" : "**" + role.getName() + "**") 62 | + "\nCustom Prefix: " + (s.getPrefix() == null ? "None" : "`" + s.getPrefix() + "`") 63 | + "\nRepeat Mode: " + (s.getRepeatMode() == RepeatMode.OFF 64 | ? s.getRepeatMode().getUserFriendlyName() 65 | : "**"+s.getRepeatMode().getUserFriendlyName()+"**") 66 | + "\nDefault Playlist: " + (s.getDefaultPlaylist() == null ? "None" : "**" + s.getDefaultPlaylist() + "**") 67 | ) 68 | .setFooter(event.getJDA().getGuilds().size() + " servers | " 69 | + event.getJDA().getGuilds().stream().filter(g -> g.getSelfMember().getVoiceState().inVoiceChannel()).count() 70 | + " audio connections", null); 71 | event.getChannel().sendMessage(builder.setEmbeds(ebuilder.build()).build()).queue(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/utils/FormatUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.utils; 17 | 18 | import java.util.List; 19 | import net.dv8tion.jda.api.entities.Role; 20 | import net.dv8tion.jda.api.entities.TextChannel; 21 | import net.dv8tion.jda.api.entities.VoiceChannel; 22 | 23 | /** 24 | * 25 | * @author John Grosh 26 | */ 27 | public class FormatUtil { 28 | 29 | public static String formatTime(long duration) 30 | { 31 | if(duration == Long.MAX_VALUE) 32 | return "LIVE"; 33 | long seconds = Math.round(duration/1000.0); 34 | long hours = seconds/(60*60); 35 | seconds %= 60*60; 36 | long minutes = seconds/60; 37 | seconds %= 60; 38 | return (hours>0 ? hours+":" : "") + (minutes<10 ? "0"+minutes : minutes) + ":" + (seconds<10 ? "0"+seconds : seconds); 39 | } 40 | 41 | public static String progressBar(double percent) 42 | { 43 | String str = ""; 44 | for(int i=0; i<12; i++) 45 | if(i == (int)(percent*12)) 46 | str+="\uD83D\uDD18"; // 🔘 47 | else 48 | str+="▬"; 49 | return str; 50 | } 51 | 52 | public static String volumeIcon(int volume) 53 | { 54 | if(volume == 0) 55 | return "\uD83D\uDD07"; // 🔇 56 | if(volume < 30) 57 | return "\uD83D\uDD08"; // 🔈 58 | if(volume < 70) 59 | return "\uD83D\uDD09"; // 🔉 60 | return "\uD83D\uDD0A"; // 🔊 61 | } 62 | 63 | public static String listOfTChannels(List list, String query) 64 | { 65 | String out = " Multiple text channels found matching \""+query+"\":"; 66 | for(int i=0; i<6 && i )"; 68 | if(list.size()>6) 69 | out+="\n**And "+(list.size()-6)+" more...**"; 70 | return out; 71 | } 72 | 73 | public static String listOfVChannels(List list, String query) 74 | { 75 | String out = " Multiple voice channels found matching \""+query+"\":"; 76 | for(int i=0; i<6 && i 6) 79 | out+="\n**And "+(list.size()-6)+" more...**"; 80 | return out; 81 | } 82 | 83 | public static String listOfRoles(List list, String query) 84 | { 85 | String out = " Multiple text channels found matching \""+query+"\":"; 86 | for(int i=0; i<6 && i 6) 89 | out+="\n**And "+(list.size()-6)+" more...**"; 90 | return out; 91 | } 92 | 93 | public static String filter(String input) 94 | { 95 | return input.replace("\u202E","") 96 | .replace("@everyone", "@\u0435veryone") // cyrillic letter e 97 | .replace("@here", "@h\u0435re") // cyrillic letter e 98 | .trim(); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/music/RemoveCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.music; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jmusicbot.Bot; 20 | import com.jagrosh.jmusicbot.audio.AudioHandler; 21 | import com.jagrosh.jmusicbot.audio.QueuedTrack; 22 | import com.jagrosh.jmusicbot.commands.MusicCommand; 23 | import com.jagrosh.jmusicbot.settings.Settings; 24 | import net.dv8tion.jda.api.Permission; 25 | import net.dv8tion.jda.api.entities.User; 26 | 27 | /** 28 | * 29 | * @author John Grosh 30 | */ 31 | public class RemoveCmd extends MusicCommand 32 | { 33 | public RemoveCmd(Bot bot) 34 | { 35 | super(bot); 36 | this.name = "remove"; 37 | this.help = "removes a song from the queue"; 38 | this.arguments = " "; 39 | this.aliases = bot.getConfig().getAliases(this.name); 40 | this.beListening = true; 41 | this.bePlaying = true; 42 | } 43 | 44 | @Override 45 | public void doCommand(CommandEvent event) 46 | { 47 | AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); 48 | if(handler.getQueue().isEmpty()) 49 | { 50 | event.replyError("There is nothing in the queue!"); 51 | return; 52 | } 53 | if(event.getArgs().equalsIgnoreCase("all")) 54 | { 55 | int count = handler.getQueue().removeAll(event.getAuthor().getIdLong()); 56 | if(count==0) 57 | event.replyWarning("You don't have any songs in the queue!"); 58 | else 59 | event.replySuccess("Successfully removed your "+count+" entries."); 60 | return; 61 | } 62 | int pos; 63 | try { 64 | pos = Integer.parseInt(event.getArgs()); 65 | } catch(NumberFormatException e) { 66 | pos = 0; 67 | } 68 | if(pos<1 || pos>handler.getQueue().size()) 69 | { 70 | event.replyError("Position must be a valid integer between 1 and "+handler.getQueue().size()+"!"); 71 | return; 72 | } 73 | Settings settings = event.getClient().getSettingsFor(event.getGuild()); 74 | boolean isDJ = event.getMember().hasPermission(Permission.MANAGE_SERVER); 75 | if(!isDJ) 76 | isDJ = event.getMember().getRoles().contains(settings.getRole(event.getGuild())); 77 | QueuedTrack qt = handler.getQueue().get(pos-1); 78 | if(qt.getIdentifier()==event.getAuthor().getIdLong()) 79 | { 80 | handler.getQueue().remove(pos-1); 81 | event.replySuccess("Removed **"+qt.getTrack().getInfo().title+"** from the queue"); 82 | } 83 | else if(isDJ) 84 | { 85 | handler.getQueue().remove(pos-1); 86 | User u; 87 | try { 88 | u = event.getJDA().getUserById(qt.getIdentifier()); 89 | } catch(Exception e) { 90 | u = null; 91 | } 92 | event.replySuccess("Removed **"+qt.getTrack().getInfo().title 93 | +"** from the queue (requested by "+(u==null ? "someone" : "**"+u.getName()+"**")+")"); 94 | } 95 | else 96 | { 97 | event.replyError("You cannot remove **"+qt.getTrack().getInfo().title+"** because you didn't add it!"); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/audio/TransformativeAudioSourceManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2021 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.audio; 17 | 18 | import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager; 19 | import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioSourceManager; 20 | import com.sedmelluq.discord.lavaplayer.track.AudioItem; 21 | import com.sedmelluq.discord.lavaplayer.track.AudioReference; 22 | import com.typesafe.config.Config; 23 | import java.io.IOException; 24 | import java.util.Collections; 25 | import java.util.List; 26 | import java.util.regex.PatternSyntaxException; 27 | import java.util.stream.Collectors; 28 | import org.jsoup.Jsoup; 29 | import org.jsoup.nodes.Document; 30 | import org.slf4j.Logger; 31 | import org.slf4j.LoggerFactory; 32 | 33 | /** 34 | * 35 | * @author John Grosh (john.a.grosh@gmail.com) 36 | */ 37 | public class TransformativeAudioSourceManager extends YoutubeAudioSourceManager 38 | { 39 | private final static Logger log = LoggerFactory.getLogger(TransformativeAudioSourceManager.class); 40 | private final String name, regex, replacement, selector, format; 41 | 42 | public TransformativeAudioSourceManager(String name, Config object) 43 | { 44 | this(name, object.getString("regex"), object.getString("replacement"), object.getString("selector"), object.getString("format")); 45 | } 46 | 47 | public TransformativeAudioSourceManager(String name, String regex, String replacement, String selector, String format) 48 | { 49 | this.name = name; 50 | this.regex = regex; 51 | this.replacement = replacement; 52 | this.selector = selector; 53 | this.format = format; 54 | } 55 | 56 | @Override 57 | public String getSourceName() 58 | { 59 | return name; 60 | } 61 | 62 | @Override 63 | public AudioItem loadItem(AudioPlayerManager apm, AudioReference ar) 64 | { 65 | if(ar.identifier == null || !ar.identifier.matches(regex)) 66 | return null; 67 | try 68 | { 69 | String url = ar.identifier.replaceAll(regex, replacement); 70 | Document doc = Jsoup.connect(url).get(); 71 | String value = doc.selectFirst(selector).ownText(); 72 | String formattedValue = String.format(format, value); 73 | return super.loadItem(apm, new AudioReference(formattedValue, null)); 74 | } 75 | catch (PatternSyntaxException ex) 76 | { 77 | log.info(String.format("Invalid pattern syntax '%s' in source '%s'", regex, name)); 78 | } 79 | catch (IOException ex) 80 | { 81 | log.warn(String.format("Failed to resolve URL in source '%s': ", name), ex); 82 | } 83 | catch (Exception ex) 84 | { 85 | log.warn(String.format("Exception in source '%s'", name), ex); 86 | } 87 | return null; 88 | } 89 | 90 | public static List createTransforms(Config transforms) 91 | { 92 | try 93 | { 94 | return transforms.root().entrySet().stream() 95 | .map(e -> new TransformativeAudioSourceManager(e.getKey(), transforms.getConfig(e.getKey()))) 96 | .collect(Collectors.toList()); 97 | } 98 | catch (Exception ex) 99 | { 100 | log.warn("Invalid transform ", ex); 101 | return Collections.emptyList(); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/queue/FairQueue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 John Grosh (jagrosh). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.queue; 17 | 18 | import java.util.ArrayList; 19 | import java.util.HashSet; 20 | import java.util.List; 21 | import java.util.Set; 22 | 23 | /** 24 | * 25 | * @author John Grosh (jagrosh) 26 | * @param 27 | */ 28 | public class FairQueue { 29 | private final List list = new ArrayList<>(); 30 | private final Set set = new HashSet<>(); 31 | 32 | public int add(T item) 33 | { 34 | int lastIndex; 35 | for(lastIndex=list.size()-1; lastIndex>-1; lastIndex--) 36 | if(list.get(lastIndex).getIdentifier()==item.getIdentifier()) 37 | break; 38 | lastIndex++; 39 | set.clear(); 40 | for(; lastIndex = list.size()) 53 | list.add(item); 54 | else 55 | list.add(index, item); 56 | } 57 | 58 | public int size() 59 | { 60 | return list.size(); 61 | } 62 | 63 | public T pull() 64 | { 65 | return list.remove(0); 66 | } 67 | 68 | public boolean isEmpty() 69 | { 70 | return list.isEmpty(); 71 | } 72 | 73 | public List getList() 74 | { 75 | return list; 76 | } 77 | 78 | public T get(int index) 79 | { 80 | return list.get(index); 81 | } 82 | 83 | public T remove(int index) 84 | { 85 | return list.remove(index); 86 | } 87 | 88 | public int removeAll(long identifier) 89 | { 90 | int count = 0; 91 | for(int i=list.size()-1; i>=0; i--) 92 | { 93 | if(list.get(i).getIdentifier()==identifier) 94 | { 95 | list.remove(i); 96 | count++; 97 | } 98 | } 99 | return count; 100 | } 101 | 102 | public void clear() 103 | { 104 | list.clear(); 105 | } 106 | 107 | public int shuffle(long identifier) 108 | { 109 | List iset = new ArrayList<>(); 110 | for(int i=0; i . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.music; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jlyrics.LyricsClient; 20 | import com.jagrosh.jmusicbot.Bot; 21 | import com.jagrosh.jmusicbot.audio.AudioHandler; 22 | import com.jagrosh.jmusicbot.commands.MusicCommand; 23 | import net.dv8tion.jda.api.EmbedBuilder; 24 | import net.dv8tion.jda.api.Permission; 25 | 26 | /** 27 | * 28 | * @author John Grosh (john.a.grosh@gmail.com) 29 | */ 30 | public class LyricsCmd extends MusicCommand 31 | { 32 | private final LyricsClient client = new LyricsClient(); 33 | 34 | public LyricsCmd(Bot bot) 35 | { 36 | super(bot); 37 | this.name = "lyrics"; 38 | this.arguments = "[song name]"; 39 | this.help = "shows the lyrics of a song"; 40 | this.aliases = bot.getConfig().getAliases(this.name); 41 | this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS}; 42 | } 43 | 44 | @Override 45 | public void doCommand(CommandEvent event) 46 | { 47 | String title; 48 | if(event.getArgs().isEmpty()) 49 | { 50 | AudioHandler sendingHandler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler(); 51 | if (sendingHandler.isMusicPlaying(event.getJDA())) 52 | title = sendingHandler.getPlayer().getPlayingTrack().getInfo().title; 53 | else 54 | { 55 | event.replyError("There must be music playing to use that!"); 56 | return; 57 | } 58 | } 59 | else 60 | title = event.getArgs(); 61 | event.getChannel().sendTyping().queue(); 62 | client.getLyrics(title).thenAccept(lyrics -> 63 | { 64 | if(lyrics == null) 65 | { 66 | event.replyError("Lyrics for `" + title + "` could not be found!" + (event.getArgs().isEmpty() ? " Try entering the song name manually (`lyrics [song name]`)" : "")); 67 | return; 68 | } 69 | 70 | EmbedBuilder eb = new EmbedBuilder() 71 | .setAuthor(lyrics.getAuthor()) 72 | .setColor(event.getSelfMember().getColor()) 73 | .setTitle(lyrics.getTitle(), lyrics.getURL()); 74 | if(lyrics.getContent().length()>15000) 75 | { 76 | event.replyWarning("Lyrics for `" + title + "` found but likely not correct: " + lyrics.getURL()); 77 | } 78 | else if(lyrics.getContent().length()>2000) 79 | { 80 | String content = lyrics.getContent().trim(); 81 | while(content.length() > 2000) 82 | { 83 | int index = content.lastIndexOf("\n\n", 2000); 84 | if(index == -1) 85 | index = content.lastIndexOf("\n", 2000); 86 | if(index == -1) 87 | index = content.lastIndexOf(" ", 2000); 88 | if(index == -1) 89 | index = 2000; 90 | event.reply(eb.setDescription(content.substring(0, index).trim()).build()); 91 | content = content.substring(index).trim(); 92 | eb.setAuthor(null).setTitle(null, null); 93 | } 94 | event.reply(eb.setDescription(content).build()); 95 | } 96 | else 97 | event.reply(eb.setDescription(lyrics.getContent()).build()); 98 | }); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/owner/DebugCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.owner; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jdautilities.commons.JDAUtilitiesInfo; 20 | import com.jagrosh.jmusicbot.Bot; 21 | import com.jagrosh.jmusicbot.commands.OwnerCommand; 22 | import com.jagrosh.jmusicbot.utils.OtherUtil; 23 | import com.sedmelluq.discord.lavaplayer.tools.PlayerLibrary; 24 | import net.dv8tion.jda.api.JDAInfo; 25 | import net.dv8tion.jda.api.Permission; 26 | import net.dv8tion.jda.api.entities.ChannelType; 27 | 28 | /** 29 | * 30 | * @author John Grosh (john.a.grosh@gmail.com) 31 | */ 32 | public class DebugCmd extends OwnerCommand 33 | { 34 | private final static String[] PROPERTIES = {"java.version", "java.vm.name", "java.vm.specification.version", 35 | "java.runtime.name", "java.runtime.version", "java.specification.version", "os.arch", "os.name"}; 36 | 37 | private final Bot bot; 38 | 39 | public DebugCmd(Bot bot) 40 | { 41 | this.bot = bot; 42 | this.name = "debug"; 43 | this.help = "shows debug info"; 44 | this.aliases = bot.getConfig().getAliases(this.name); 45 | this.guildOnly = false; 46 | } 47 | 48 | @Override 49 | protected void execute(CommandEvent event) 50 | { 51 | StringBuilder sb = new StringBuilder(); 52 | sb.append("```\nSystem Properties:"); 53 | for(String key: PROPERTIES) 54 | sb.append("\n ").append(key).append(" = ").append(System.getProperty(key)); 55 | sb.append("\n\nJMusicBot Information:") 56 | .append("\n Version = ").append(OtherUtil.getCurrentVersion()) 57 | .append("\n Owner = ").append(bot.getConfig().getOwnerId()) 58 | .append("\n Prefix = ").append(bot.getConfig().getPrefix()) 59 | .append("\n AltPrefix = ").append(bot.getConfig().getAltPrefix()) 60 | .append("\n MaxSeconds = ").append(bot.getConfig().getMaxSeconds()) 61 | .append("\n NPImages = ").append(bot.getConfig().useNPImages()) 62 | .append("\n SongInStatus = ").append(bot.getConfig().getSongInStatus()) 63 | .append("\n StayInChannel = ").append(bot.getConfig().getStay()) 64 | .append("\n UseEval = ").append(bot.getConfig().useEval()) 65 | .append("\n UpdateAlerts = ").append(bot.getConfig().useUpdateAlerts()); 66 | sb.append("\n\nDependency Information:") 67 | .append("\n JDA Version = ").append(JDAInfo.VERSION) 68 | .append("\n JDA-Utilities Version = ").append(JDAUtilitiesInfo.VERSION) 69 | .append("\n Lavaplayer Version = ").append(PlayerLibrary.VERSION); 70 | long total = Runtime.getRuntime().totalMemory() / 1024 / 1024; 71 | long used = total - (Runtime.getRuntime().freeMemory() / 1024 / 1024); 72 | sb.append("\n\nRuntime Information:") 73 | .append("\n Total Memory = ").append(total) 74 | .append("\n Used Memory = ").append(used); 75 | sb.append("\n\nDiscord Information:") 76 | .append("\n ID = ").append(event.getJDA().getSelfUser().getId()) 77 | .append("\n Guilds = ").append(event.getJDA().getGuildCache().size()) 78 | .append("\n Users = ").append(event.getJDA().getUserCache().size()); 79 | sb.append("\n```"); 80 | 81 | if(event.isFromType(ChannelType.PRIVATE) 82 | || event.getSelfMember().hasPermission(event.getTextChannel(), Permission.MESSAGE_ATTACH_FILES)) 83 | event.getChannel().sendFile(sb.toString().getBytes(), "debug_information.txt").queue(); 84 | else 85 | event.reply("Debug Information: " + sb.toString()); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/dj/ForceRemoveCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.dj; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jdautilities.commons.utils.FinderUtil; 20 | import com.jagrosh.jdautilities.menu.OrderedMenu; 21 | import com.jagrosh.jmusicbot.Bot; 22 | import com.jagrosh.jmusicbot.audio.AudioHandler; 23 | import com.jagrosh.jmusicbot.commands.DJCommand; 24 | import net.dv8tion.jda.api.Permission; 25 | import net.dv8tion.jda.api.entities.Member; 26 | import net.dv8tion.jda.api.entities.User; 27 | 28 | import java.util.List; 29 | import java.util.concurrent.TimeUnit; 30 | 31 | /** 32 | * 33 | * @author Michaili K. 34 | */ 35 | public class ForceRemoveCmd extends DJCommand 36 | { 37 | public ForceRemoveCmd(Bot bot) 38 | { 39 | super(bot); 40 | this.name = "forceremove"; 41 | this.help = "removes all entries by a user from the queue"; 42 | this.arguments = " "; 43 | this.aliases = bot.getConfig().getAliases(this.name); 44 | this.beListening = false; 45 | this.bePlaying = true; 46 | this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS}; 47 | } 48 | 49 | @Override 50 | public void doCommand(CommandEvent event) 51 | { 52 | if (event.getArgs().isEmpty()) 53 | { 54 | event.replyError("You need to mention a user!"); 55 | return; 56 | } 57 | 58 | AudioHandler handler = (AudioHandler) event.getGuild().getAudioManager().getSendingHandler(); 59 | if (handler.getQueue().isEmpty()) 60 | { 61 | event.replyError("There is nothing in the queue!"); 62 | return; 63 | } 64 | 65 | 66 | User target; 67 | List found = FinderUtil.findMembers(event.getArgs(), event.getGuild()); 68 | 69 | if(found.isEmpty()) 70 | { 71 | event.replyError("Unable to find the user!"); 72 | return; 73 | } 74 | else if(found.size()>1) 75 | { 76 | OrderedMenu.Builder builder = new OrderedMenu.Builder(); 77 | for(int i=0; i removeAllEntries(found.get(i-1).getUser(), event)) 85 | .setText("Found multiple users:") 86 | .setColor(event.getSelfMember().getColor()) 87 | .useNumbers() 88 | .setUsers(event.getAuthor()) 89 | .useCancelButton(true) 90 | .setCancel((msg) -> {}) 91 | .setEventWaiter(bot.getWaiter()) 92 | .setTimeout(1, TimeUnit.MINUTES) 93 | 94 | .build().display(event.getChannel()); 95 | 96 | return; 97 | } 98 | else 99 | { 100 | target = found.get(0).getUser(); 101 | } 102 | 103 | removeAllEntries(target, event); 104 | 105 | } 106 | 107 | private void removeAllEntries(User target, CommandEvent event) 108 | { 109 | int count = ((AudioHandler) event.getGuild().getAudioManager().getSendingHandler()).getQueue().removeAll(target.getIdLong()); 110 | if (count == 0) 111 | { 112 | event.replyWarning("**"+target.getName()+"** doesn't have any songs in the queue!"); 113 | } 114 | else 115 | { 116 | event.replySuccess("Successfully removed `"+count+"` entries from **"+target.getName()+"**#"+target.getDiscriminator()+"."); 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/MusicCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands; 17 | 18 | import com.jagrosh.jdautilities.command.Command; 19 | import com.jagrosh.jdautilities.command.CommandEvent; 20 | import com.jagrosh.jmusicbot.Bot; 21 | import com.jagrosh.jmusicbot.settings.Settings; 22 | import com.jagrosh.jmusicbot.audio.AudioHandler; 23 | import net.dv8tion.jda.api.entities.GuildVoiceState; 24 | import net.dv8tion.jda.api.entities.TextChannel; 25 | import net.dv8tion.jda.api.entities.VoiceChannel; 26 | import net.dv8tion.jda.api.exceptions.PermissionException; 27 | 28 | /** 29 | * 30 | * @author John Grosh 31 | */ 32 | public abstract class MusicCommand extends Command 33 | { 34 | protected final Bot bot; 35 | protected boolean bePlaying; 36 | protected boolean beListening; 37 | 38 | public MusicCommand(Bot bot) 39 | { 40 | this.bot = bot; 41 | this.guildOnly = true; 42 | this.category = new Category("Music"); 43 | } 44 | 45 | @Override 46 | protected void execute(CommandEvent event) 47 | { 48 | Settings settings = event.getClient().getSettingsFor(event.getGuild()); 49 | TextChannel tchannel = settings.getTextChannel(event.getGuild()); 50 | if(tchannel!=null && !event.getTextChannel().equals(tchannel)) 51 | { 52 | try 53 | { 54 | event.getMessage().delete().queue(); 55 | } catch(PermissionException ignore){} 56 | event.replyInDm(event.getClient().getError()+" You can only use that command in "+tchannel.getAsMention()+"!"); 57 | return; 58 | } 59 | bot.getPlayerManager().setUpHandler(event.getGuild()); // no point constantly checking for this later 60 | if(bePlaying && !((AudioHandler)event.getGuild().getAudioManager().getSendingHandler()).isMusicPlaying(event.getJDA())) 61 | { 62 | event.reply(event.getClient().getError()+" There must be music playing to use that!"); 63 | return; 64 | } 65 | if(beListening) 66 | { 67 | VoiceChannel current = event.getGuild().getSelfMember().getVoiceState().getChannel(); 68 | if(current==null) 69 | current = settings.getVoiceChannel(event.getGuild()); 70 | GuildVoiceState userState = event.getMember().getVoiceState(); 71 | if(!userState.inVoiceChannel() || userState.isDeafened() || (current!=null && !userState.getChannel().equals(current))) 72 | { 73 | event.replyError("You must be listening in "+(current==null ? "a voice channel" : current.getAsMention())+" to use that!"); 74 | return; 75 | } 76 | 77 | VoiceChannel afkChannel = userState.getGuild().getAfkChannel(); 78 | if(afkChannel != null && afkChannel.equals(userState.getChannel())) 79 | { 80 | event.replyError("You cannot use that command in an AFK channel!"); 81 | return; 82 | } 83 | 84 | if(!event.getGuild().getSelfMember().getVoiceState().inVoiceChannel()) 85 | { 86 | try 87 | { 88 | event.getGuild().getAudioManager().openAudioConnection(userState.getChannel()); 89 | } 90 | catch(PermissionException ex) 91 | { 92 | event.reply(event.getClient().getError()+" I am unable to connect to "+userState.getChannel().getAsMention()+"!"); 93 | return; 94 | } 95 | } 96 | } 97 | 98 | doCommand(event); 99 | } 100 | 101 | public abstract void doCommand(CommandEvent event); 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/Listener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot; 17 | 18 | import com.jagrosh.jmusicbot.utils.OtherUtil; 19 | import java.util.concurrent.TimeUnit; 20 | import net.dv8tion.jda.api.JDA; 21 | import net.dv8tion.jda.api.entities.Guild; 22 | import net.dv8tion.jda.api.entities.User; 23 | import net.dv8tion.jda.api.entities.VoiceChannel; 24 | import net.dv8tion.jda.api.events.ReadyEvent; 25 | import net.dv8tion.jda.api.events.ShutdownEvent; 26 | import net.dv8tion.jda.api.events.guild.GuildJoinEvent; 27 | import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent; 28 | import net.dv8tion.jda.api.events.message.guild.GuildMessageDeleteEvent; 29 | import net.dv8tion.jda.api.hooks.ListenerAdapter; 30 | import org.jetbrains.annotations.NotNull; 31 | import org.slf4j.Logger; 32 | import org.slf4j.LoggerFactory; 33 | 34 | /** 35 | * 36 | * @author John Grosh (john.a.grosh@gmail.com) 37 | */ 38 | public class Listener extends ListenerAdapter 39 | { 40 | private final Bot bot; 41 | 42 | public Listener(Bot bot) 43 | { 44 | this.bot = bot; 45 | } 46 | 47 | @Override 48 | public void onReady(ReadyEvent event) 49 | { 50 | if(event.getJDA().getGuildCache().isEmpty()) 51 | { 52 | Logger log = LoggerFactory.getLogger("MusicBot"); 53 | log.warn("This bot is not on any guilds! Use the following link to add the bot to your guilds!"); 54 | log.warn(event.getJDA().getInviteUrl(JMusicBot.RECOMMENDED_PERMS)); 55 | } 56 | credit(event.getJDA()); 57 | event.getJDA().getGuilds().forEach((guild) -> 58 | { 59 | try 60 | { 61 | String defpl = bot.getSettingsManager().getSettings(guild).getDefaultPlaylist(); 62 | VoiceChannel vc = bot.getSettingsManager().getSettings(guild).getVoiceChannel(guild); 63 | if(defpl!=null && vc!=null && bot.getPlayerManager().setUpHandler(guild).playFromDefault()) 64 | { 65 | guild.getAudioManager().openAudioConnection(vc); 66 | } 67 | } 68 | catch(Exception ignore) {} 69 | }); 70 | if(bot.getConfig().useUpdateAlerts()) 71 | { 72 | bot.getThreadpool().scheduleWithFixedDelay(() -> 73 | { 74 | try 75 | { 76 | User owner = bot.getJDA().retrieveUserById(bot.getConfig().getOwnerId()).complete(); 77 | String currentVersion = OtherUtil.getCurrentVersion(); 78 | String latestVersion = OtherUtil.getLatestVersion(); 79 | if(latestVersion!=null && !currentVersion.equalsIgnoreCase(latestVersion)) 80 | { 81 | String msg = String.format(OtherUtil.NEW_VERSION_AVAILABLE, currentVersion, latestVersion); 82 | owner.openPrivateChannel().queue(pc -> pc.sendMessage(msg).queue()); 83 | } 84 | } 85 | catch(Exception ex) {} // ignored 86 | }, 0, 24, TimeUnit.HOURS); 87 | } 88 | } 89 | 90 | @Override 91 | public void onGuildMessageDelete(GuildMessageDeleteEvent event) 92 | { 93 | bot.getNowplayingHandler().onMessageDelete(event.getGuild(), event.getMessageIdLong()); 94 | } 95 | 96 | @Override 97 | public void onGuildVoiceUpdate(@NotNull GuildVoiceUpdateEvent event) 98 | { 99 | bot.getAloneInVoiceHandler().onVoiceUpdate(event); 100 | } 101 | 102 | @Override 103 | public void onShutdown(ShutdownEvent event) 104 | { 105 | bot.shutdown(); 106 | } 107 | 108 | @Override 109 | public void onGuildJoin(GuildJoinEvent event) 110 | { 111 | credit(event.getJDA()); 112 | } 113 | 114 | // make sure people aren't adding clones to dbots 115 | private void credit(JDA jda) 116 | { 117 | Guild dbots = jda.getGuildById(110373943822540800L); 118 | if(dbots==null) 119 | return; 120 | if(bot.getConfig().getDBots()) 121 | return; 122 | jda.getTextChannelById(119222314964353025L) 123 | .sendMessage("This account is running JMusicBot. Please do not list bot clones on this server, <@"+bot.getConfig().getOwnerId()+">.").complete(); 124 | dbots.leave().queue(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/entities/Prompt.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh (jagrosh) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.entities; 17 | 18 | import java.util.Scanner; 19 | import javax.swing.JOptionPane; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | /** 24 | * 25 | * @author John Grosh (john.a.grosh@gmail.com) 26 | */ 27 | public class Prompt 28 | { 29 | private final String title; 30 | private final String noguiMessage; 31 | 32 | private boolean nogui; 33 | private boolean noprompt; 34 | private Scanner scanner; 35 | 36 | public Prompt(String title) 37 | { 38 | this(title, null); 39 | } 40 | 41 | public Prompt(String title, String noguiMessage) 42 | { 43 | this(title, noguiMessage, "true".equalsIgnoreCase(System.getProperty("nogui")), "true".equalsIgnoreCase(System.getProperty("noprompt"))); 44 | } 45 | 46 | public Prompt(String title, String noguiMessage, boolean nogui, boolean noprompt) 47 | { 48 | this.title = title; 49 | this.noguiMessage = noguiMessage == null ? "Switching to nogui mode. You can manually start in nogui mode by including the -Dnogui=true flag." : noguiMessage; 50 | this.nogui = nogui; 51 | this.noprompt = noprompt; 52 | } 53 | 54 | public boolean isNoGUI() 55 | { 56 | return nogui; 57 | } 58 | 59 | public void alert(Level level, String context, String message) 60 | { 61 | if(nogui) 62 | { 63 | Logger log = LoggerFactory.getLogger(context); 64 | switch(level) 65 | { 66 | case INFO: 67 | log.info(message); 68 | break; 69 | case WARNING: 70 | log.warn(message); 71 | break; 72 | case ERROR: 73 | log.error(message); 74 | break; 75 | default: 76 | log.info(message); 77 | break; 78 | } 79 | } 80 | else 81 | { 82 | try 83 | { 84 | int option = 0; 85 | switch(level) 86 | { 87 | case INFO: 88 | option = JOptionPane.INFORMATION_MESSAGE; 89 | break; 90 | case WARNING: 91 | option = JOptionPane.WARNING_MESSAGE; 92 | break; 93 | case ERROR: 94 | option = JOptionPane.ERROR_MESSAGE; 95 | break; 96 | default: 97 | option = JOptionPane.PLAIN_MESSAGE; 98 | break; 99 | } 100 | JOptionPane.showMessageDialog(null, " "+message, title, option); 101 | } 102 | catch(Exception e) 103 | { 104 | nogui = true; 105 | alert(Level.WARNING, context, noguiMessage); 106 | alert(level, context, message); 107 | } 108 | } 109 | } 110 | 111 | public String prompt(String content) 112 | { 113 | if(noprompt) 114 | return null; 115 | if(nogui) 116 | { 117 | if(scanner==null) 118 | scanner = new Scanner(System.in); 119 | try 120 | { 121 | System.out.println(content); 122 | if(scanner.hasNextLine()) 123 | return scanner.nextLine(); 124 | return null; 125 | } 126 | catch(Exception e) 127 | { 128 | alert(Level.ERROR, title, "Unable to read input from command line."); 129 | e.printStackTrace(); 130 | return null; 131 | } 132 | } 133 | else 134 | { 135 | try 136 | { 137 | return JOptionPane.showInputDialog(null, content, title, JOptionPane.QUESTION_MESSAGE); 138 | } 139 | catch(Exception e) 140 | { 141 | nogui = true; 142 | alert(Level.WARNING, title, noguiMessage); 143 | return prompt(content); 144 | } 145 | } 146 | } 147 | 148 | public static enum Level 149 | { 150 | INFO, WARNING, ERROR; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/music/QueueCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh
. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.music; 17 | 18 | import java.util.List; 19 | import java.util.concurrent.TimeUnit; 20 | import com.jagrosh.jdautilities.command.CommandEvent; 21 | import com.jagrosh.jdautilities.menu.Paginator; 22 | import com.jagrosh.jmusicbot.Bot; 23 | import com.jagrosh.jmusicbot.JMusicBot; 24 | import com.jagrosh.jmusicbot.audio.AudioHandler; 25 | import com.jagrosh.jmusicbot.audio.QueuedTrack; 26 | import com.jagrosh.jmusicbot.commands.MusicCommand; 27 | import com.jagrosh.jmusicbot.settings.RepeatMode; 28 | import com.jagrosh.jmusicbot.settings.Settings; 29 | import com.jagrosh.jmusicbot.utils.FormatUtil; 30 | import net.dv8tion.jda.api.MessageBuilder; 31 | import net.dv8tion.jda.api.Permission; 32 | import net.dv8tion.jda.api.entities.Message; 33 | import net.dv8tion.jda.api.exceptions.PermissionException; 34 | 35 | /** 36 | * 37 | * @author John Grosh 38 | */ 39 | public class QueueCmd extends MusicCommand 40 | { 41 | private final Paginator.Builder builder; 42 | 43 | public QueueCmd(Bot bot) 44 | { 45 | super(bot); 46 | this.name = "queue"; 47 | this.help = "shows the current queue"; 48 | this.arguments = "[pagenum]"; 49 | this.aliases = bot.getConfig().getAliases(this.name); 50 | this.bePlaying = true; 51 | this.botPermissions = new Permission[]{Permission.MESSAGE_ADD_REACTION,Permission.MESSAGE_EMBED_LINKS}; 52 | builder = new Paginator.Builder() 53 | .setColumns(1) 54 | .setFinalAction(m -> {try{m.clearReactions().queue();}catch(PermissionException ignore){}}) 55 | .setItemsPerPage(10) 56 | .waitOnSinglePage(false) 57 | .useNumberedItems(true) 58 | .showPageNumbers(true) 59 | .wrapPageEnds(true) 60 | .setEventWaiter(bot.getWaiter()) 61 | .setTimeout(1, TimeUnit.MINUTES); 62 | } 63 | 64 | @Override 65 | public void doCommand(CommandEvent event) 66 | { 67 | int pagenum = 1; 68 | try 69 | { 70 | pagenum = Integer.parseInt(event.getArgs()); 71 | } 72 | catch(NumberFormatException ignore){} 73 | AudioHandler ah = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); 74 | List list = ah.getQueue().getList(); 75 | if(list.isEmpty()) 76 | { 77 | Message nowp = ah.getNowPlaying(event.getJDA()); 78 | Message nonowp = ah.getNoMusicPlaying(event.getJDA()); 79 | Message built = new MessageBuilder() 80 | .setContent(event.getClient().getWarning() + " There is no music in the queue!") 81 | .setEmbeds((nowp==null ? nonowp : nowp).getEmbeds().get(0)).build(); 82 | event.reply(built, m -> 83 | { 84 | if(nowp!=null) 85 | bot.getNowplayingHandler().setLastNPMessage(m); 86 | }); 87 | return; 88 | } 89 | String[] songs = new String[list.size()]; 90 | long total = 0; 91 | for(int i=0; i getQueueTitle(ah, event.getClient().getSuccess(), songs.length, fintotal, settings.getRepeatMode())) 99 | .setItems(songs) 100 | .setUsers(event.getAuthor()) 101 | .setColor(event.getSelfMember().getColor()) 102 | ; 103 | builder.build().paginate(event.getChannel(), pagenum); 104 | } 105 | 106 | private String getQueueTitle(AudioHandler ah, String success, int songslength, long total, RepeatMode repeatmode) 107 | { 108 | StringBuilder sb = new StringBuilder(); 109 | if(ah.getPlayer().getPlayingTrack()!=null) 110 | { 111 | sb.append(ah.getStatusEmoji()).append(" **") 112 | .append(ah.getPlayer().getPlayingTrack().getInfo().title).append("**\n"); 113 | } 114 | return FormatUtil.filter(sb.append(success).append(" Current Queue | ").append(songslength) 115 | .append(" entries | `").append(FormatUtil.formatTime(total)).append("` ") 116 | .append(repeatmode.getEmoji() != null ? "| "+repeatmode.getEmoji() : "").toString()); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/settings/SettingsManager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.settings; 17 | 18 | import com.jagrosh.jdautilities.command.GuildSettingsManager; 19 | import com.jagrosh.jmusicbot.utils.OtherUtil; 20 | import java.io.IOException; 21 | import java.nio.file.Files; 22 | import java.util.HashMap; 23 | import net.dv8tion.jda.api.entities.Guild; 24 | import org.json.JSONException; 25 | import org.json.JSONObject; 26 | import org.slf4j.LoggerFactory; 27 | 28 | /** 29 | * 30 | * @author John Grosh (john.a.grosh@gmail.com) 31 | */ 32 | public class SettingsManager implements GuildSettingsManager 33 | { 34 | private final static double SKIP_RATIO = .55; 35 | private final HashMap settings; 36 | 37 | public SettingsManager() 38 | { 39 | this.settings = new HashMap<>(); 40 | try { 41 | JSONObject loadedSettings = new JSONObject(new String(Files.readAllBytes(OtherUtil.getPath("serversettings.json")))); 42 | loadedSettings.keySet().forEach((id) -> { 43 | JSONObject o = loadedSettings.getJSONObject(id); 44 | 45 | // Legacy version support: On versions 0.3.3 and older, the repeat mode was represented as a boolean. 46 | if (!o.has("repeat_mode") && o.has("repeat") && o.getBoolean("repeat")) 47 | o.put("repeat_mode", RepeatMode.ALL); 48 | 49 | 50 | settings.put(Long.parseLong(id), new Settings(this, 51 | o.has("text_channel_id") ? o.getString("text_channel_id") : null, 52 | o.has("voice_channel_id")? o.getString("voice_channel_id") : null, 53 | o.has("dj_role_id") ? o.getString("dj_role_id") : null, 54 | o.has("volume") ? o.getInt("volume") : 100, 55 | o.has("default_playlist")? o.getString("default_playlist") : null, 56 | o.has("repeat_mode") ? o.getEnum(RepeatMode.class, "repeat_mode"): RepeatMode.OFF, 57 | o.has("prefix") ? o.getString("prefix") : null, 58 | o.has("skip_ratio") ? o.getDouble("skip_ratio") : SKIP_RATIO)); 59 | }); 60 | } catch(IOException | JSONException e) { 61 | LoggerFactory.getLogger("Settings").warn("Failed to load server settings (this is normal if no settings have been set yet): "+e); 62 | } 63 | } 64 | 65 | /** 66 | * Gets non-null settings for a Guild 67 | * 68 | * @param guild the guild to get settings for 69 | * @return the existing settings, or new settings for that guild 70 | */ 71 | @Override 72 | public Settings getSettings(Guild guild) 73 | { 74 | return getSettings(guild.getIdLong()); 75 | } 76 | 77 | public Settings getSettings(long guildId) 78 | { 79 | return settings.computeIfAbsent(guildId, id -> createDefaultSettings()); 80 | } 81 | 82 | private Settings createDefaultSettings() 83 | { 84 | return new Settings(this, 0, 0, 0, 100, null, RepeatMode.OFF, null, SKIP_RATIO); 85 | } 86 | 87 | protected void writeSettings() 88 | { 89 | JSONObject obj = new JSONObject(); 90 | settings.keySet().stream().forEach(key -> { 91 | JSONObject o = new JSONObject(); 92 | Settings s = settings.get(key); 93 | if(s.textId!=0) 94 | o.put("text_channel_id", Long.toString(s.textId)); 95 | if(s.voiceId!=0) 96 | o.put("voice_channel_id", Long.toString(s.voiceId)); 97 | if(s.roleId!=0) 98 | o.put("dj_role_id", Long.toString(s.roleId)); 99 | if(s.getVolume()!=100) 100 | o.put("volume",s.getVolume()); 101 | if(s.getDefaultPlaylist() != null) 102 | o.put("default_playlist", s.getDefaultPlaylist()); 103 | if(s.getRepeatMode()!=RepeatMode.OFF) 104 | o.put("repeat_mode", s.getRepeatMode()); 105 | if(s.getPrefix() != null) 106 | o.put("prefix", s.getPrefix()); 107 | if(s.getSkipRatio() != SKIP_RATIO) 108 | o.put("skip_ratio", s.getSkipRatio()); 109 | obj.put(Long.toString(key), o); 110 | }); 111 | try { 112 | Files.write(OtherUtil.getPath("serversettings.json"), obj.toString(4).getBytes()); 113 | } catch(IOException ex){ 114 | LoggerFactory.getLogger("Settings").warn("Failed to write to file: "+ex); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/Bot.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot; 17 | 18 | import java.util.concurrent.Executors; 19 | import java.util.concurrent.ScheduledExecutorService; 20 | import com.jagrosh.jdautilities.commons.waiter.EventWaiter; 21 | import com.jagrosh.jmusicbot.audio.AloneInVoiceHandler; 22 | import com.jagrosh.jmusicbot.audio.AudioHandler; 23 | import com.jagrosh.jmusicbot.audio.NowplayingHandler; 24 | import com.jagrosh.jmusicbot.audio.PlayerManager; 25 | import com.jagrosh.jmusicbot.gui.GUI; 26 | import com.jagrosh.jmusicbot.playlist.PlaylistLoader; 27 | import com.jagrosh.jmusicbot.settings.SettingsManager; 28 | import java.util.Objects; 29 | import net.dv8tion.jda.api.JDA; 30 | import net.dv8tion.jda.api.entities.Activity; 31 | import net.dv8tion.jda.api.entities.Guild; 32 | 33 | /** 34 | * 35 | * @author John Grosh 36 | */ 37 | public class Bot 38 | { 39 | private final EventWaiter waiter; 40 | private final ScheduledExecutorService threadpool; 41 | private final BotConfig config; 42 | private final SettingsManager settings; 43 | private final PlayerManager players; 44 | private final PlaylistLoader playlists; 45 | private final NowplayingHandler nowplaying; 46 | private final AloneInVoiceHandler aloneInVoiceHandler; 47 | 48 | private boolean shuttingDown = false; 49 | private JDA jda; 50 | private GUI gui; 51 | 52 | public Bot(EventWaiter waiter, BotConfig config, SettingsManager settings) 53 | { 54 | this.waiter = waiter; 55 | this.config = config; 56 | this.settings = settings; 57 | this.playlists = new PlaylistLoader(config); 58 | this.threadpool = Executors.newSingleThreadScheduledExecutor(); 59 | this.players = new PlayerManager(this); 60 | this.players.init(); 61 | this.nowplaying = new NowplayingHandler(this); 62 | this.nowplaying.init(); 63 | this.aloneInVoiceHandler = new AloneInVoiceHandler(this); 64 | this.aloneInVoiceHandler.init(); 65 | } 66 | 67 | public BotConfig getConfig() 68 | { 69 | return config; 70 | } 71 | 72 | public SettingsManager getSettingsManager() 73 | { 74 | return settings; 75 | } 76 | 77 | public EventWaiter getWaiter() 78 | { 79 | return waiter; 80 | } 81 | 82 | public ScheduledExecutorService getThreadpool() 83 | { 84 | return threadpool; 85 | } 86 | 87 | public PlayerManager getPlayerManager() 88 | { 89 | return players; 90 | } 91 | 92 | public PlaylistLoader getPlaylistLoader() 93 | { 94 | return playlists; 95 | } 96 | 97 | public NowplayingHandler getNowplayingHandler() 98 | { 99 | return nowplaying; 100 | } 101 | 102 | public AloneInVoiceHandler getAloneInVoiceHandler() 103 | { 104 | return aloneInVoiceHandler; 105 | } 106 | 107 | public JDA getJDA() 108 | { 109 | return jda; 110 | } 111 | 112 | public void closeAudioConnection(long guildId) 113 | { 114 | Guild guild = jda.getGuildById(guildId); 115 | if(guild!=null) 116 | threadpool.submit(() -> guild.getAudioManager().closeAudioConnection()); 117 | } 118 | 119 | public void resetGame() 120 | { 121 | Activity game = config.getGame()==null || config.getGame().getName().equalsIgnoreCase("none") ? null : config.getGame(); 122 | if(!Objects.equals(jda.getPresence().getActivity(), game)) 123 | jda.getPresence().setActivity(game); 124 | } 125 | 126 | public void shutdown() 127 | { 128 | if(shuttingDown) 129 | return; 130 | shuttingDown = true; 131 | threadpool.shutdownNow(); 132 | if(jda.getStatus()!=JDA.Status.SHUTTING_DOWN) 133 | { 134 | jda.getGuilds().stream().forEach(g -> 135 | { 136 | g.getAudioManager().closeAudioConnection(); 137 | AudioHandler ah = (AudioHandler)g.getAudioManager().getSendingHandler(); 138 | if(ah!=null) 139 | { 140 | ah.stopAndClear(); 141 | ah.getPlayer().destroy(); 142 | nowplaying.updateTopic(g.getIdLong(), ah, true); 143 | } 144 | }); 145 | jda.shutdown(); 146 | } 147 | if(gui!=null) 148 | gui.dispose(); 149 | System.exit(0); 150 | } 151 | 152 | public void setJDA(JDA jda) 153 | { 154 | this.jda = jda; 155 | } 156 | 157 | public void setGUI(GUI gui) 158 | { 159 | this.gui = gui; 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/settings/Settings.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.settings; 17 | 18 | import com.jagrosh.jdautilities.command.GuildSettingsProvider; 19 | import java.util.Collection; 20 | import java.util.Collections; 21 | import net.dv8tion.jda.api.entities.Guild; 22 | import net.dv8tion.jda.api.entities.Role; 23 | import net.dv8tion.jda.api.entities.TextChannel; 24 | import net.dv8tion.jda.api.entities.VoiceChannel; 25 | 26 | /** 27 | * 28 | * @author John Grosh 29 | */ 30 | public class Settings implements GuildSettingsProvider 31 | { 32 | private final SettingsManager manager; 33 | protected long textId; 34 | protected long voiceId; 35 | protected long roleId; 36 | private int volume; 37 | private String defaultPlaylist; 38 | private RepeatMode repeatMode; 39 | private String prefix; 40 | private double skipRatio; 41 | 42 | public Settings(SettingsManager manager, String textId, String voiceId, String roleId, int volume, String defaultPlaylist, RepeatMode repeatMode, String prefix, double skipRatio) 43 | { 44 | this.manager = manager; 45 | try 46 | { 47 | this.textId = Long.parseLong(textId); 48 | } 49 | catch(NumberFormatException e) 50 | { 51 | this.textId = 0; 52 | } 53 | try 54 | { 55 | this.voiceId = Long.parseLong(voiceId); 56 | } 57 | catch(NumberFormatException e) 58 | { 59 | this.voiceId = 0; 60 | } 61 | try 62 | { 63 | this.roleId = Long.parseLong(roleId); 64 | } 65 | catch(NumberFormatException e) 66 | { 67 | this.roleId = 0; 68 | } 69 | this.volume = volume; 70 | this.defaultPlaylist = defaultPlaylist; 71 | this.repeatMode = repeatMode; 72 | this.prefix = prefix; 73 | this.skipRatio = skipRatio; 74 | } 75 | 76 | public Settings(SettingsManager manager, long textId, long voiceId, long roleId, int volume, String defaultPlaylist, RepeatMode repeatMode, String prefix, double skipRatio) 77 | { 78 | this.manager = manager; 79 | this.textId = textId; 80 | this.voiceId = voiceId; 81 | this.roleId = roleId; 82 | this.volume = volume; 83 | this.defaultPlaylist = defaultPlaylist; 84 | this.repeatMode = repeatMode; 85 | this.prefix = prefix; 86 | this.skipRatio = skipRatio; 87 | } 88 | 89 | // Getters 90 | public TextChannel getTextChannel(Guild guild) 91 | { 92 | return guild == null ? null : guild.getTextChannelById(textId); 93 | } 94 | 95 | public VoiceChannel getVoiceChannel(Guild guild) 96 | { 97 | return guild == null ? null : guild.getVoiceChannelById(voiceId); 98 | } 99 | 100 | public Role getRole(Guild guild) 101 | { 102 | return guild == null ? null : guild.getRoleById(roleId); 103 | } 104 | 105 | public int getVolume() 106 | { 107 | return volume; 108 | } 109 | 110 | public String getDefaultPlaylist() 111 | { 112 | return defaultPlaylist; 113 | } 114 | 115 | public RepeatMode getRepeatMode() 116 | { 117 | return repeatMode; 118 | } 119 | 120 | public String getPrefix() 121 | { 122 | return prefix; 123 | } 124 | 125 | public double getSkipRatio() 126 | { 127 | return skipRatio; 128 | } 129 | 130 | @Override 131 | public Collection getPrefixes() 132 | { 133 | return prefix == null ? Collections.emptySet() : Collections.singleton(prefix); 134 | } 135 | 136 | // Setters 137 | public void setTextChannel(TextChannel tc) 138 | { 139 | this.textId = tc == null ? 0 : tc.getIdLong(); 140 | this.manager.writeSettings(); 141 | } 142 | 143 | public void setVoiceChannel(VoiceChannel vc) 144 | { 145 | this.voiceId = vc == null ? 0 : vc.getIdLong(); 146 | this.manager.writeSettings(); 147 | } 148 | 149 | public void setDJRole(Role role) 150 | { 151 | this.roleId = role == null ? 0 : role.getIdLong(); 152 | this.manager.writeSettings(); 153 | } 154 | 155 | public void setVolume(int volume) 156 | { 157 | this.volume = volume; 158 | this.manager.writeSettings(); 159 | } 160 | 161 | public void setDefaultPlaylist(String defaultPlaylist) 162 | { 163 | this.defaultPlaylist = defaultPlaylist; 164 | this.manager.writeSettings(); 165 | } 166 | 167 | public void setRepeatMode(RepeatMode mode) 168 | { 169 | this.repeatMode = mode; 170 | this.manager.writeSettings(); 171 | } 172 | 173 | public void setPrefix(String prefix) 174 | { 175 | this.prefix = prefix; 176 | this.manager.writeSettings(); 177 | } 178 | 179 | public void setSkipRatio(double skipRatio) 180 | { 181 | this.skipRatio = skipRatio; 182 | this.manager.writeSettings(); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/gui/TextAreaOutputStream.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.gui; 17 | 18 | import java.awt.*; 19 | import java.io.*; 20 | import java.util.*; 21 | import java.util.List; 22 | import javax.swing.*; 23 | /** 24 | * 25 | * @author Lawrence Dol 26 | */ 27 | public class TextAreaOutputStream extends OutputStream { 28 | 29 | // ************************************************************************************************* 30 | // INSTANCE MEMBERS 31 | // ************************************************************************************************* 32 | 33 | private byte[] oneByte; // array for write(int val); 34 | private Appender appender; // most recent action 35 | 36 | public TextAreaOutputStream(JTextArea txtara) { 37 | this(txtara,1000); 38 | } 39 | 40 | public TextAreaOutputStream(JTextArea txtara, int maxlin) { 41 | if(maxlin<1) { throw new IllegalArgumentException("TextAreaOutputStream maximum lines must be positive (value="+maxlin+")"); } 42 | oneByte=new byte[1]; 43 | appender=new Appender(txtara,maxlin); 44 | } 45 | 46 | /** Clear the current console text area. */ 47 | public synchronized void clear() { 48 | if(appender!=null) { appender.clear(); } 49 | } 50 | 51 | @Override 52 | public synchronized void close() { 53 | appender=null; 54 | } 55 | 56 | @Override 57 | public synchronized void flush() { 58 | /* empty */ 59 | } 60 | 61 | @Override 62 | public synchronized void write(int val) { 63 | oneByte[0]=(byte)val; 64 | write(oneByte,0,1); 65 | } 66 | 67 | @Override 68 | public synchronized void write(byte[] ba) { 69 | write(ba,0,ba.length); 70 | } 71 | 72 | @Override 73 | public synchronized void write(byte[] ba,int str,int len) { 74 | if(appender!=null) { appender.append(bytesToString(ba,str,len)); } 75 | } 76 | 77 | //@edu.umd.cs.findbugs.annotations.SuppressWarnings("DM_DEFAULT_ENCODING") 78 | static private String bytesToString(byte[] ba, int str, int len) { 79 | try { 80 | return new String(ba,str,len,"UTF-8"); 81 | } catch(UnsupportedEncodingException thr) { 82 | return new String(ba,str,len); 83 | } // all JVMs are required to support UTF-8 84 | } 85 | 86 | // ************************************************************************************************* 87 | // STATIC MEMBERS 88 | // ************************************************************************************************* 89 | 90 | static class Appender 91 | implements Runnable 92 | { 93 | static private final String EOL1="\n"; 94 | static private final String EOL2=System.getProperty("line.separator",EOL1); 95 | 96 | private final JTextArea textArea; 97 | private final int maxLines; // maximum lines allowed in text area 98 | private final LinkedList lengths; // length of lines within text area 99 | private final List values; // values waiting to be appended 100 | 101 | private int curLength; // length of current line 102 | private boolean clear; 103 | private boolean queue; 104 | 105 | Appender(JTextArea txtara, int maxlin) { 106 | textArea =txtara; 107 | maxLines =maxlin; 108 | lengths =new LinkedList<>(); 109 | values =new ArrayList<>(); 110 | 111 | curLength=0; 112 | clear =false; 113 | queue =true; 114 | } 115 | 116 | private synchronized void append(String val) { 117 | values.add(val); 118 | if(queue) { 119 | queue=false; 120 | EventQueue.invokeLater(this); 121 | } 122 | } 123 | 124 | private synchronized void clear() { 125 | clear=true; 126 | curLength=0; 127 | lengths.clear(); 128 | values.clear(); 129 | if(queue) { 130 | queue=false; 131 | EventQueue.invokeLater(this); 132 | } 133 | } 134 | 135 | // MUST BE THE ONLY METHOD THAT TOUCHES textArea! 136 | @Override 137 | public synchronized void run() { 138 | if(clear) { textArea.setText(""); } 139 | values.stream().map((val) -> { 140 | curLength+=val.length(); 141 | return val; 142 | }).map((val) -> { 143 | if(val.endsWith(EOL1) || val.endsWith(EOL2)) { 144 | if(lengths.size()>=maxLines) { textArea.replaceRange("",0,lengths.removeFirst()); } 145 | lengths.addLast(curLength); 146 | curLength=0; 147 | } 148 | return val; 149 | }).forEach((val) -> { 150 | textArea.append(val); 151 | }); 152 | values.clear(); 153 | clear =false; 154 | queue =true; 155 | } 156 | } 157 | } /* END PUBLIC CLASS */ -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/dj/PlaynextCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.dj; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jmusicbot.Bot; 20 | import com.jagrosh.jmusicbot.audio.AudioHandler; 21 | import com.jagrosh.jmusicbot.audio.QueuedTrack; 22 | import com.jagrosh.jmusicbot.commands.DJCommand; 23 | import com.jagrosh.jmusicbot.utils.FormatUtil; 24 | import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler; 25 | import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; 26 | import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist; 27 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 28 | import net.dv8tion.jda.api.entities.Message; 29 | 30 | /** 31 | * 32 | * @author John Grosh (john.a.grosh@gmail.com) 33 | */ 34 | public class PlaynextCmd extends DJCommand 35 | { 36 | private final String loadingEmoji; 37 | 38 | public PlaynextCmd(Bot bot) 39 | { 40 | super(bot); 41 | this.loadingEmoji = bot.getConfig().getLoading(); 42 | this.name = "playnext"; 43 | this.arguments = " "; 44 | this.help = "plays a single song next"; 45 | this.aliases = bot.getConfig().getAliases(this.name); 46 | this.beListening = true; 47 | this.bePlaying = false; 48 | } 49 | 50 | @Override 51 | public void doCommand(CommandEvent event) 52 | { 53 | if(event.getArgs().isEmpty() && event.getMessage().getAttachments().isEmpty()) 54 | { 55 | event.replyWarning("Please include a song title or URL!"); 56 | return; 57 | } 58 | String args = event.getArgs().startsWith("<") && event.getArgs().endsWith(">") 59 | ? event.getArgs().substring(1,event.getArgs().length()-1) 60 | : event.getArgs().isEmpty() ? event.getMessage().getAttachments().get(0).getUrl() : event.getArgs(); 61 | event.reply(loadingEmoji+" Loading... `["+args+"]`", m -> bot.getPlayerManager().loadItemOrdered(event.getGuild(), args, new ResultHandler(m,event,false))); 62 | } 63 | 64 | private class ResultHandler implements AudioLoadResultHandler 65 | { 66 | private final Message m; 67 | private final CommandEvent event; 68 | private final boolean ytsearch; 69 | 70 | private ResultHandler(Message m, CommandEvent event, boolean ytsearch) 71 | { 72 | this.m = m; 73 | this.event = event; 74 | this.ytsearch = ytsearch; 75 | } 76 | 77 | private void loadSingle(AudioTrack track) 78 | { 79 | if(bot.getConfig().isTooLong(track)) 80 | { 81 | m.editMessage(FormatUtil.filter(event.getClient().getWarning()+" This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `" 82 | +FormatUtil.formatTime(track.getDuration())+"` > `"+FormatUtil.formatTime(bot.getConfig().getMaxSeconds()*1000)+"`")).queue(); 83 | return; 84 | } 85 | AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); 86 | int pos = handler.addTrackToFront(new QueuedTrack(track, event.getAuthor()))+1; 87 | String addMsg = FormatUtil.filter(event.getClient().getSuccess()+" Added **"+track.getInfo().title 88 | +"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0?"to begin playing":" to the queue at position "+pos)); 89 | m.editMessage(addMsg).queue(); 90 | } 91 | 92 | @Override 93 | public void trackLoaded(AudioTrack track) 94 | { 95 | loadSingle(track); 96 | } 97 | 98 | @Override 99 | public void playlistLoaded(AudioPlaylist playlist) 100 | { 101 | AudioTrack single; 102 | if(playlist.getTracks().size()==1 || playlist.isSearchResult()) 103 | single = playlist.getSelectedTrack()==null ? playlist.getTracks().get(0) : playlist.getSelectedTrack(); 104 | else if (playlist.getSelectedTrack()!=null) 105 | single = playlist.getSelectedTrack(); 106 | else 107 | single = playlist.getTracks().get(0); 108 | loadSingle(single); 109 | } 110 | 111 | @Override 112 | public void noMatches() 113 | { 114 | if(ytsearch) 115 | m.editMessage(FormatUtil.filter(event.getClient().getWarning()+" No results found for `"+event.getArgs()+"`.")).queue(); 116 | else 117 | bot.getPlayerManager().loadItemOrdered(event.getGuild(), "ytsearch:"+event.getArgs(), new ResultHandler(m,event,true)); 118 | } 119 | 120 | @Override 121 | public void loadFailed(FriendlyException throwable) 122 | { 123 | if(throwable.severity==FriendlyException.Severity.COMMON) 124 | m.editMessage(event.getClient().getError()+" Error loading: "+throwable.getMessage()).queue(); 125 | else 126 | m.editMessage(event.getClient().getError()+" Error loading track.").queue(); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/owner/SetgameCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2017 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.owner; 17 | 18 | import com.jagrosh.jdautilities.command.CommandEvent; 19 | import com.jagrosh.jmusicbot.Bot; 20 | import com.jagrosh.jmusicbot.commands.OwnerCommand; 21 | import net.dv8tion.jda.api.entities.Activity; 22 | 23 | /** 24 | * 25 | * @author John Grosh 26 | */ 27 | public class SetgameCmd extends OwnerCommand 28 | { 29 | public SetgameCmd(Bot bot) 30 | { 31 | this.name = "setgame"; 32 | this.help = "sets the game the bot is playing"; 33 | this.arguments = "[action] [game]"; 34 | this.aliases = bot.getConfig().getAliases(this.name); 35 | this.guildOnly = false; 36 | this.children = new OwnerCommand[]{ 37 | new SetlistenCmd(), 38 | new SetstreamCmd(), 39 | new SetwatchCmd() 40 | }; 41 | } 42 | 43 | @Override 44 | protected void execute(CommandEvent event) 45 | { 46 | String title = event.getArgs().toLowerCase().startsWith("playing") ? event.getArgs().substring(7).trim() : event.getArgs(); 47 | try 48 | { 49 | event.getJDA().getPresence().setActivity(title.isEmpty() ? null : Activity.playing(title)); 50 | event.reply(event.getClient().getSuccess()+" **"+event.getSelfUser().getName() 51 | +"** is "+(title.isEmpty() ? "no longer playing anything." : "now playing `"+title+"`")); 52 | } 53 | catch(Exception e) 54 | { 55 | event.reply(event.getClient().getError()+" The game could not be set!"); 56 | } 57 | } 58 | 59 | private class SetstreamCmd extends OwnerCommand 60 | { 61 | private SetstreamCmd() 62 | { 63 | this.name = "stream"; 64 | this.aliases = new String[]{"twitch","streaming"}; 65 | this.help = "sets the game the bot is playing to a stream"; 66 | this.arguments = " "; 67 | this.guildOnly = false; 68 | } 69 | 70 | @Override 71 | protected void execute(CommandEvent event) 72 | { 73 | String[] parts = event.getArgs().split("\\s+", 2); 74 | if(parts.length<2) 75 | { 76 | event.replyError("Please include a twitch username and the name of the game to 'stream'"); 77 | return; 78 | } 79 | try 80 | { 81 | event.getJDA().getPresence().setActivity(Activity.streaming(parts[1], "https://twitch.tv/"+parts[0])); 82 | event.replySuccess("**"+event.getSelfUser().getName() 83 | +"** is now streaming `"+parts[1]+"`"); 84 | } 85 | catch(Exception e) 86 | { 87 | event.reply(event.getClient().getError()+" The game could not be set!"); 88 | } 89 | } 90 | } 91 | 92 | private class SetlistenCmd extends OwnerCommand 93 | { 94 | private SetlistenCmd() 95 | { 96 | this.name = "listen"; 97 | this.aliases = new String[]{"listening"}; 98 | this.help = "sets the game the bot is listening to"; 99 | this.arguments = " "; 100 | this.guildOnly = false; 101 | } 102 | 103 | @Override 104 | protected void execute(CommandEvent event) 105 | { 106 | if(event.getArgs().isEmpty()) 107 | { 108 | event.replyError("Please include a title to listen to!"); 109 | return; 110 | } 111 | String title = event.getArgs().toLowerCase().startsWith("to") ? event.getArgs().substring(2).trim() : event.getArgs(); 112 | try 113 | { 114 | event.getJDA().getPresence().setActivity(Activity.listening(title)); 115 | event.replySuccess("**"+event.getSelfUser().getName()+"** is now listening to `"+title+"`"); 116 | } catch(Exception e) { 117 | event.reply(event.getClient().getError()+" The game could not be set!"); 118 | } 119 | } 120 | } 121 | 122 | private class SetwatchCmd extends OwnerCommand 123 | { 124 | private SetwatchCmd() 125 | { 126 | this.name = "watch"; 127 | this.aliases = new String[]{"watching"}; 128 | this.help = "sets the game the bot is watching"; 129 | this.arguments = " "; 130 | this.guildOnly = false; 131 | } 132 | 133 | @Override 134 | protected void execute(CommandEvent event) 135 | { 136 | if(event.getArgs().isEmpty()) 137 | { 138 | event.replyError("Please include a title to watch!"); 139 | return; 140 | } 141 | String title = event.getArgs(); 142 | try 143 | { 144 | event.getJDA().getPresence().setActivity(Activity.watching(title)); 145 | event.replySuccess("**"+event.getSelfUser().getName()+"** is now watching `"+title+"`"); 146 | } catch(Exception e) { 147 | event.reply(event.getClient().getError()+" The game could not be set!"); 148 | } 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 148 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/audio/NowplayingHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh4.0.0 4 |com.jagrosh 5 |JMusicBot 6 |Snapshot 7 |jar 8 | 9 |10 | 29 | 30 |11 | 15 |dv8tion 12 |m2-dv8tion 13 |https://m2.dv8tion.net/releases 14 |16 | 20 |central 17 |bintray 18 |https://jcenter.bintray.com 19 |21 | 28 |jitpack.io 22 |https://jitpack.io 23 |24 | 27 |true 25 |always 26 |31 | 32 | 99 | 100 |33 | 37 |net.dv8tion 34 |JDA 35 |4.4.1_353 36 |38 | 43 | 44 | 45 | 46 | 51 |com.jagrosh 39 |jda-utilities 40 |3.0.5 41 |pom 42 |52 | 56 | 57 | 62 |com.github.jagrosh 53 |lavaplayer 54 |jmusicbot-SNAPSHOT 55 |63 | 67 | 68 | 69 |com.github.jagrosh 64 |JLyrics 65 |master-SNAPSHOT 66 |70 | 74 |ch.qos.logback 71 |logback-classic 72 |1.2.3 73 |75 | 79 |com.typesafe 76 |config 77 |1.3.2 78 |80 | 84 | 85 | 86 |org.jsoup 81 |jsoup 82 |1.15.3 83 |87 | 92 |junit 88 |junit 89 |4.13.1 90 |test 91 |93 | 98 |org.hamcrest 94 |hamcrest-core 95 |1.3 96 |test 97 |101 | 141 | 142 |102 | 140 |103 | 139 |org.apache.maven.plugins 104 |maven-shade-plugin 105 |1.5 106 |107 | 138 |108 | 137 |package 109 |110 | 112 |shade 111 |113 | 136 |true 114 |All 115 |116 | 120 |117 | 119 |*:* 118 |121 | 135 |122 | 124 |reference.conf 123 |125 | 134 |126 | 133 |com.jagrosh.jmusicbot.JMusicBot 127 |${project.artifactId} 128 |${project.version} 129 |${project.artifactId} 130 |${project.version} 131 |${project.groupId} 132 |143 | 147 |UTF-8 144 |1.8 145 |1.8 146 |. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.audio; 17 | 18 | import com.jagrosh.jmusicbot.Bot; 19 | import com.jagrosh.jmusicbot.entities.Pair; 20 | import com.jagrosh.jmusicbot.settings.Settings; 21 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 22 | import java.util.HashMap; 23 | import java.util.HashSet; 24 | import java.util.Set; 25 | import java.util.concurrent.TimeUnit; 26 | import net.dv8tion.jda.api.Permission; 27 | import net.dv8tion.jda.api.entities.Activity; 28 | import net.dv8tion.jda.api.entities.Guild; 29 | import net.dv8tion.jda.api.entities.Message; 30 | import net.dv8tion.jda.api.entities.TextChannel; 31 | import net.dv8tion.jda.api.exceptions.PermissionException; 32 | import net.dv8tion.jda.api.exceptions.RateLimitedException; 33 | 34 | /** 35 | * 36 | * @author John Grosh (john.a.grosh@gmail.com) 37 | */ 38 | public class NowplayingHandler 39 | { 40 | private final Bot bot; 41 | private final HashMap > lastNP; // guild -> channel,message 42 | 43 | public NowplayingHandler(Bot bot) 44 | { 45 | this.bot = bot; 46 | this.lastNP = new HashMap<>(); 47 | } 48 | 49 | public void init() 50 | { 51 | if(!bot.getConfig().useNPImages()) 52 | bot.getThreadpool().scheduleWithFixedDelay(() -> updateAll(), 0, 5, TimeUnit.SECONDS); 53 | } 54 | 55 | public void setLastNPMessage(Message m) 56 | { 57 | lastNP.put(m.getGuild().getIdLong(), new Pair<>(m.getTextChannel().getIdLong(), m.getIdLong())); 58 | } 59 | 60 | public void clearLastNPMessage(Guild guild) 61 | { 62 | lastNP.remove(guild.getIdLong()); 63 | } 64 | 65 | private void updateAll() 66 | { 67 | Set toRemove = new HashSet<>(); 68 | for(long guildId: lastNP.keySet()) 69 | { 70 | Guild guild = bot.getJDA().getGuildById(guildId); 71 | if(guild==null) 72 | { 73 | toRemove.add(guildId); 74 | continue; 75 | } 76 | Pair pair = lastNP.get(guildId); 77 | TextChannel tc = guild.getTextChannelById(pair.getKey()); 78 | if(tc==null) 79 | { 80 | toRemove.add(guildId); 81 | continue; 82 | } 83 | AudioHandler handler = (AudioHandler)guild.getAudioManager().getSendingHandler(); 84 | Message msg = handler.getNowPlaying(bot.getJDA()); 85 | if(msg==null) 86 | { 87 | msg = handler.getNoMusicPlaying(bot.getJDA()); 88 | toRemove.add(guildId); 89 | } 90 | try 91 | { 92 | tc.editMessageById(pair.getValue(), msg).queue(m->{}, t -> lastNP.remove(guildId)); 93 | } 94 | catch(Exception e) 95 | { 96 | toRemove.add(guildId); 97 | } 98 | } 99 | toRemove.forEach(id -> lastNP.remove(id)); 100 | } 101 | 102 | public void updateTopic(long guildId, AudioHandler handler, boolean wait) 103 | { 104 | Guild guild = bot.getJDA().getGuildById(guildId); 105 | if(guild==null) 106 | return; 107 | Settings settings = bot.getSettingsManager().getSettings(guildId); 108 | TextChannel tchan = settings.getTextChannel(guild); 109 | if(tchan!=null && guild.getSelfMember().hasPermission(tchan, Permission.MANAGE_CHANNEL)) 110 | { 111 | String otherText; 112 | String topic = tchan.getTopic(); 113 | if(topic==null || topic.isEmpty()) 114 | otherText = "\u200B"; 115 | else if(topic.contains("\u200B")) 116 | otherText = topic.substring(topic.lastIndexOf("\u200B")); 117 | else 118 | otherText = "\u200B\n "+topic; 119 | String text = handler.getTopicFormat(bot.getJDA()) + otherText; 120 | if(!text.equals(tchan.getTopic())) 121 | { 122 | try 123 | { 124 | // normally here if 'wait' was false, we'd want to queue, however, 125 | // new discord ratelimits specifically limiting changing channel topics 126 | // mean we don't want a backlog of changes piling up, so if we hit a 127 | // ratelimit, we just won't change the topic this time 128 | tchan.getManager().setTopic(text).complete(wait); 129 | } 130 | catch(PermissionException | RateLimitedException ignore) {} 131 | } 132 | } 133 | } 134 | 135 | // "event"-based methods 136 | public void onTrackUpdate(long guildId, AudioTrack track, AudioHandler handler) 137 | { 138 | // update bot status if applicable 139 | if(bot.getConfig().getSongInStatus()) 140 | { 141 | if(track!=null && bot.getJDA().getGuilds().stream().filter(g -> g.getSelfMember().getVoiceState().inVoiceChannel()).count()<=1) 142 | bot.getJDA().getPresence().setActivity(Activity.listening(track.getInfo().title)); 143 | else 144 | bot.resetGame(); 145 | } 146 | 147 | // update channel topic if applicable 148 | updateTopic(guildId, handler, false); 149 | } 150 | 151 | public void onMessageDelete(Guild guild, long messageId) 152 | { 153 | Pair pair = lastNP.get(guild.getIdLong()); 154 | if(pair==null) 155 | return; 156 | if(pair.getValue() == messageId) 157 | lastNP.remove(guild.getIdLong()); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/music/SearchCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.music; 17 | 18 | import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler; 19 | import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; 20 | import com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity; 21 | import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist; 22 | import com.sedmelluq.discord.lavaplayer.track.AudioTrack; 23 | import java.util.concurrent.TimeUnit; 24 | import com.jagrosh.jdautilities.command.CommandEvent; 25 | import com.jagrosh.jdautilities.menu.OrderedMenu; 26 | import com.jagrosh.jmusicbot.Bot; 27 | import com.jagrosh.jmusicbot.audio.AudioHandler; 28 | import com.jagrosh.jmusicbot.audio.QueuedTrack; 29 | import com.jagrosh.jmusicbot.commands.MusicCommand; 30 | import com.jagrosh.jmusicbot.utils.FormatUtil; 31 | import net.dv8tion.jda.api.Permission; 32 | import net.dv8tion.jda.api.entities.Message; 33 | 34 | /** 35 | * 36 | * @author John Grosh 37 | */ 38 | public class SearchCmd extends MusicCommand 39 | { 40 | protected String searchPrefix = "ytsearch:"; 41 | private final OrderedMenu.Builder builder; 42 | private final String searchingEmoji; 43 | 44 | public SearchCmd(Bot bot) 45 | { 46 | super(bot); 47 | this.searchingEmoji = bot.getConfig().getSearching(); 48 | this.name = "search"; 49 | this.aliases = bot.getConfig().getAliases(this.name); 50 | this.arguments = " "; 51 | this.help = "searches Youtube for a provided query"; 52 | this.beListening = true; 53 | this.bePlaying = false; 54 | this.botPermissions = new Permission[]{Permission.MESSAGE_EMBED_LINKS}; 55 | builder = new OrderedMenu.Builder() 56 | .allowTextInput(true) 57 | .useNumbers() 58 | .useCancelButton(true) 59 | .setEventWaiter(bot.getWaiter()) 60 | .setTimeout(1, TimeUnit.MINUTES); 61 | } 62 | @Override 63 | public void doCommand(CommandEvent event) 64 | { 65 | if(event.getArgs().isEmpty()) 66 | { 67 | event.replyError("Please include a query."); 68 | return; 69 | } 70 | event.reply(searchingEmoji+" Searching... `["+event.getArgs()+"]`", 71 | m -> bot.getPlayerManager().loadItemOrdered(event.getGuild(), searchPrefix + event.getArgs(), new ResultHandler(m,event))); 72 | } 73 | 74 | private class ResultHandler implements AudioLoadResultHandler 75 | { 76 | private final Message m; 77 | private final CommandEvent event; 78 | 79 | private ResultHandler(Message m, CommandEvent event) 80 | { 81 | this.m = m; 82 | this.event = event; 83 | } 84 | 85 | @Override 86 | public void trackLoaded(AudioTrack track) 87 | { 88 | if(bot.getConfig().isTooLong(track)) 89 | { 90 | m.editMessage(FormatUtil.filter(event.getClient().getWarning()+" This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `" 91 | +FormatUtil.formatTime(track.getDuration())+"` > `"+bot.getConfig().getMaxTime()+"`")).queue(); 92 | return; 93 | } 94 | AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); 95 | int pos = handler.addTrack(new QueuedTrack(track, event.getAuthor()))+1; 96 | m.editMessage(FormatUtil.filter(event.getClient().getSuccess()+" Added **"+track.getInfo().title 97 | +"** (`"+FormatUtil.formatTime(track.getDuration())+"`) "+(pos==0 ? "to begin playing" 98 | : " to the queue at position "+pos))).queue(); 99 | } 100 | 101 | @Override 102 | public void playlistLoaded(AudioPlaylist playlist) 103 | { 104 | builder.setColor(event.getSelfMember().getColor()) 105 | .setText(FormatUtil.filter(event.getClient().getSuccess()+" Search results for `"+event.getArgs()+"`:")) 106 | .setChoices(new String[0]) 107 | .setSelection((msg,i) -> 108 | { 109 | AudioTrack track = playlist.getTracks().get(i-1); 110 | if(bot.getConfig().isTooLong(track)) 111 | { 112 | event.replyWarning("This track (**"+track.getInfo().title+"**) is longer than the allowed maximum: `" 113 | +FormatUtil.formatTime(track.getDuration())+"` > `"+bot.getConfig().getMaxTime()+"`"); 114 | return; 115 | } 116 | AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); 117 | int pos = handler.addTrack(new QueuedTrack(track, event.getAuthor()))+1; 118 | event.replySuccess("Added **" + FormatUtil.filter(track.getInfo().title) 119 | + "** (`" + FormatUtil.formatTime(track.getDuration()) + "`) " + (pos==0 ? "to begin playing" 120 | : " to the queue at position "+pos)); 121 | }) 122 | .setCancel((msg) -> {}) 123 | .setUsers(event.getAuthor()) 124 | ; 125 | for(int i=0; i<4 && i . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.utils; 17 | 18 | import com.jagrosh.jmusicbot.JMusicBot; 19 | import com.jagrosh.jmusicbot.entities.Prompt; 20 | import java.io.*; 21 | import java.net.URISyntaxException; 22 | import java.net.URL; 23 | import java.net.URLConnection; 24 | import java.nio.file.Path; 25 | import java.nio.file.Paths; 26 | import net.dv8tion.jda.api.OnlineStatus; 27 | import net.dv8tion.jda.api.entities.Activity; 28 | import okhttp3.*; 29 | import org.json.JSONException; 30 | import org.json.JSONObject; 31 | import org.json.JSONTokener; 32 | 33 | /** 34 | * 35 | * @author John Grosh 36 | */ 37 | public class OtherUtil 38 | { 39 | public final static String NEW_VERSION_AVAILABLE = "There is a new version of JMusicBot available!\n" 40 | + "Current version: %s\n" 41 | + "New Version: %s\n\n" 42 | + "Please visit https://github.com/jagrosh/MusicBot/releases/latest to get the latest release."; 43 | private final static String WINDOWS_INVALID_PATH = "c:\\windows\\system32\\"; 44 | 45 | /** 46 | * gets a Path from a String 47 | * also fixes the windows tendency to try to start in system32 48 | * any time the bot tries to access this path, it will instead start in the location of the jar file 49 | * 50 | * @param path the string path 51 | * @return the Path object 52 | */ 53 | public static Path getPath(String path) 54 | { 55 | Path result = Paths.get(path); 56 | // special logic to prevent trying to access system32 57 | if(result.toAbsolutePath().toString().toLowerCase().startsWith(WINDOWS_INVALID_PATH)) 58 | { 59 | try 60 | { 61 | result = Paths.get(new File(JMusicBot.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getParentFile().getPath() + File.separator + path); 62 | } 63 | catch(URISyntaxException ex) {} 64 | } 65 | return result; 66 | } 67 | 68 | /** 69 | * Loads a resource from the jar as a string 70 | * 71 | * @param clazz class base object 72 | * @param name name of resource 73 | * @return string containing the contents of the resource 74 | */ 75 | public static String loadResource(Object clazz, String name) 76 | { 77 | try(BufferedReader reader = new BufferedReader(new InputStreamReader(clazz.getClass().getResourceAsStream(name)))) 78 | { 79 | StringBuilder sb = new StringBuilder(); 80 | reader.lines().forEach(line -> sb.append("\r\n").append(line)); 81 | return sb.toString().trim(); 82 | } 83 | catch(IOException ex) 84 | { 85 | return null; 86 | } 87 | } 88 | 89 | /** 90 | * Loads image data from a URL 91 | * 92 | * @param url url of image 93 | * @return inputstream of url 94 | */ 95 | public static InputStream imageFromUrl(String url) 96 | { 97 | if(url==null) 98 | return null; 99 | try 100 | { 101 | URL u = new URL(url); 102 | URLConnection urlConnection = u.openConnection(); 103 | 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"); 104 | return urlConnection.getInputStream(); 105 | } 106 | catch(IOException | IllegalArgumentException ignore) {} 107 | return null; 108 | } 109 | 110 | /** 111 | * Parses an activity from a string 112 | * 113 | * @param game the game, including the action such as 'playing' or 'watching' 114 | * @return the parsed activity 115 | */ 116 | public static Activity parseGame(String game) 117 | { 118 | if(game==null || game.trim().isEmpty() || game.trim().equalsIgnoreCase("default")) 119 | return null; 120 | String lower = game.toLowerCase(); 121 | if(lower.startsWith("playing")) 122 | return Activity.playing(makeNonEmpty(game.substring(7).trim())); 123 | if(lower.startsWith("listening to")) 124 | return Activity.listening(makeNonEmpty(game.substring(12).trim())); 125 | if(lower.startsWith("listening")) 126 | return Activity.listening(makeNonEmpty(game.substring(9).trim())); 127 | if(lower.startsWith("watching")) 128 | return Activity.watching(makeNonEmpty(game.substring(8).trim())); 129 | if(lower.startsWith("streaming")) 130 | { 131 | String[] parts = game.substring(9).trim().split("\\s+", 2); 132 | if(parts.length == 2) 133 | { 134 | return Activity.streaming(makeNonEmpty(parts[1]), "https://twitch.tv/"+parts[0]); 135 | } 136 | } 137 | return Activity.playing(game); 138 | } 139 | 140 | public static String makeNonEmpty(String str) 141 | { 142 | return str == null || str.isEmpty() ? "\u200B" : str; 143 | } 144 | 145 | public static OnlineStatus parseStatus(String status) 146 | { 147 | if(status==null || status.trim().isEmpty()) 148 | return OnlineStatus.ONLINE; 149 | OnlineStatus st = OnlineStatus.fromKey(status); 150 | return st == null ? OnlineStatus.ONLINE : st; 151 | } 152 | 153 | public static void checkJavaVersion(Prompt prompt) 154 | { 155 | if(!System.getProperty("java.vm.name").contains("64")) 156 | prompt.alert(Prompt.Level.WARNING, "Java Version", 157 | "It appears that you may not be using a supported Java version. Please use 64-bit java."); 158 | } 159 | 160 | public static void checkVersion(Prompt prompt) 161 | { 162 | // Get current version number 163 | String version = getCurrentVersion(); 164 | 165 | // Check for new version 166 | String latestVersion = getLatestVersion(); 167 | 168 | if(latestVersion!=null && !latestVersion.equals(version)) 169 | { 170 | prompt.alert(Prompt.Level.WARNING, "JMusicBot Version", String.format(NEW_VERSION_AVAILABLE, version, latestVersion)); 171 | } 172 | } 173 | 174 | public static String getCurrentVersion() 175 | { 176 | if(JMusicBot.class.getPackage()!=null && JMusicBot.class.getPackage().getImplementationVersion()!=null) 177 | return JMusicBot.class.getPackage().getImplementationVersion(); 178 | else 179 | return "UNKNOWN"; 180 | } 181 | 182 | public static String getLatestVersion() 183 | { 184 | try 185 | { 186 | Response response = new OkHttpClient.Builder().build() 187 | .newCall(new Request.Builder().get().url("https://api.github.com/repos/jagrosh/MusicBot/releases/latest").build()) 188 | .execute(); 189 | ResponseBody body = response.body(); 190 | if(body != null) 191 | { 192 | try(Reader reader = body.charStream()) 193 | { 194 | JSONObject obj = new JSONObject(new JSONTokener(reader)); 195 | return obj.getString("tag_name"); 196 | } 197 | finally 198 | { 199 | response.close(); 200 | } 201 | } 202 | else 203 | return null; 204 | } 205 | catch(IOException | JSONException | NullPointerException ex) 206 | { 207 | return null; 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/owner/PlaylistCmd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 John Grosh . 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot.commands.owner; 17 | 18 | import java.io.IOException; 19 | import java.util.List; 20 | import com.jagrosh.jdautilities.command.Command; 21 | import com.jagrosh.jdautilities.command.CommandEvent; 22 | import com.jagrosh.jmusicbot.Bot; 23 | import com.jagrosh.jmusicbot.commands.OwnerCommand; 24 | import com.jagrosh.jmusicbot.playlist.PlaylistLoader.Playlist; 25 | 26 | /** 27 | * 28 | * @author John Grosh 29 | */ 30 | public class PlaylistCmd extends OwnerCommand 31 | { 32 | private final Bot bot; 33 | public PlaylistCmd(Bot bot) 34 | { 35 | this.bot = bot; 36 | this.guildOnly = false; 37 | this.name = "playlist"; 38 | this.arguments = " "; 39 | this.help = "playlist management"; 40 | this.aliases = bot.getConfig().getAliases(this.name); 41 | this.children = new OwnerCommand[]{ 42 | new ListCmd(), 43 | new AppendlistCmd(), 44 | new DeletelistCmd(), 45 | new MakelistCmd(), 46 | new DefaultlistCmd(bot) 47 | }; 48 | } 49 | 50 | @Override 51 | public void execute(CommandEvent event) 52 | { 53 | StringBuilder builder = new StringBuilder(event.getClient().getWarning()+" Playlist Management Commands:\n"); 54 | for(Command cmd: this.children) 55 | builder.append("\n`").append(event.getClient().getPrefix()).append(name).append(" ").append(cmd.getName()) 56 | .append(" ").append(cmd.getArguments()==null ? "" : cmd.getArguments()).append("` - ").append(cmd.getHelp()); 57 | event.reply(builder.toString()); 58 | } 59 | 60 | public class MakelistCmd extends OwnerCommand 61 | { 62 | public MakelistCmd() 63 | { 64 | this.name = "make"; 65 | this.aliases = new String[]{"create"}; 66 | this.help = "makes a new playlist"; 67 | this.arguments = " "; 68 | this.guildOnly = false; 69 | } 70 | 71 | @Override 72 | protected void execute(CommandEvent event) 73 | { 74 | String pname = event.getArgs().replaceAll("\\s+", "_"); 75 | pname = pname.replaceAll("[*?|\\/\":<>]", ""); 76 | if(pname == null || pname.isEmpty()) 77 | { 78 | event.replyError("Please provide a name for the playlist!"); 79 | } 80 | else if(bot.getPlaylistLoader().getPlaylist(pname) == null) 81 | { 82 | try 83 | { 84 | bot.getPlaylistLoader().createPlaylist(pname); 85 | event.reply(event.getClient().getSuccess()+" Successfully created playlist `"+pname+"`!"); 86 | } 87 | catch(IOException e) 88 | { 89 | event.reply(event.getClient().getError()+" I was unable to create the playlist: "+e.getLocalizedMessage()); 90 | } 91 | } 92 | else 93 | event.reply(event.getClient().getError()+" Playlist `"+pname+"` already exists!"); 94 | } 95 | } 96 | 97 | public class DeletelistCmd extends OwnerCommand 98 | { 99 | public DeletelistCmd() 100 | { 101 | this.name = "delete"; 102 | this.aliases = new String[]{"remove"}; 103 | this.help = "deletes an existing playlist"; 104 | this.arguments = " "; 105 | this.guildOnly = false; 106 | } 107 | 108 | @Override 109 | protected void execute(CommandEvent event) 110 | { 111 | String pname = event.getArgs().replaceAll("\\s+", "_"); 112 | if(bot.getPlaylistLoader().getPlaylist(pname)==null) 113 | event.reply(event.getClient().getError()+" Playlist `"+pname+"` doesn't exist!"); 114 | else 115 | { 116 | try 117 | { 118 | bot.getPlaylistLoader().deletePlaylist(pname); 119 | event.reply(event.getClient().getSuccess()+" Successfully deleted playlist `"+pname+"`!"); 120 | } 121 | catch(IOException e) 122 | { 123 | event.reply(event.getClient().getError()+" I was unable to delete the playlist: "+e.getLocalizedMessage()); 124 | } 125 | } 126 | } 127 | } 128 | 129 | public class AppendlistCmd extends OwnerCommand 130 | { 131 | public AppendlistCmd() 132 | { 133 | this.name = "append"; 134 | this.aliases = new String[]{"add"}; 135 | this.help = "appends songs to an existing playlist"; 136 | this.arguments = " | | ..."; 137 | this.guildOnly = false; 138 | } 139 | 140 | @Override 141 | protected void execute(CommandEvent event) 142 | { 143 | String[] parts = event.getArgs().split("\\s+", 2); 144 | if(parts.length<2) 145 | { 146 | event.reply(event.getClient().getError()+" Please include a playlist name and URLs to add!"); 147 | return; 148 | } 149 | String pname = parts[0]; 150 | Playlist playlist = bot.getPlaylistLoader().getPlaylist(pname); 151 | if(playlist==null) 152 | event.reply(event.getClient().getError()+" Playlist `"+pname+"` doesn't exist!"); 153 | else 154 | { 155 | StringBuilder builder = new StringBuilder(); 156 | playlist.getItems().forEach(item -> builder.append("\r\n").append(item)); 157 | String[] urls = parts[1].split("\\|"); 158 | for(String url: urls) 159 | { 160 | String u = url.trim(); 161 | if(u.startsWith("<") && u.endsWith(">")) 162 | u = u.substring(1, u.length()-1); 163 | builder.append("\r\n").append(u); 164 | } 165 | try 166 | { 167 | bot.getPlaylistLoader().writePlaylist(pname, builder.toString()); 168 | event.reply(event.getClient().getSuccess()+" Successfully added "+urls.length+" items to playlist `"+pname+"`!"); 169 | } 170 | catch(IOException e) 171 | { 172 | event.reply(event.getClient().getError()+" I was unable to append to the playlist: "+e.getLocalizedMessage()); 173 | } 174 | } 175 | } 176 | } 177 | 178 | public class DefaultlistCmd extends AutoplaylistCmd 179 | { 180 | public DefaultlistCmd(Bot bot) 181 | { 182 | super(bot); 183 | this.name = "setdefault"; 184 | this.aliases = new String[]{"default"}; 185 | this.arguments = " "; 186 | this.guildOnly = true; 187 | } 188 | } 189 | 190 | public class ListCmd extends OwnerCommand 191 | { 192 | public ListCmd() 193 | { 194 | this.name = "all"; 195 | this.aliases = new String[]{"available","list"}; 196 | this.help = "lists all available playlists"; 197 | this.guildOnly = true; 198 | } 199 | 200 | @Override 201 | protected void execute(CommandEvent event) 202 | { 203 | if(!bot.getPlaylistLoader().folderExists()) 204 | bot.getPlaylistLoader().createFolder(); 205 | if(!bot.getPlaylistLoader().folderExists()) 206 | { 207 | event.reply(event.getClient().getWarning()+" Playlists folder does not exist and could not be created!"); 208 | return; 209 | } 210 | List list = bot.getPlaylistLoader().getPlaylistNames(); 211 | if(list==null) 212 | event.reply(event.getClient().getError()+" Failed to load available playlists!"); 213 | else if(list.isEmpty()) 214 | event.reply(event.getClient().getWarning()+" There are no playlists in the Playlists folder!"); 215 | else 216 | { 217 | StringBuilder builder = new StringBuilder(event.getClient().getSuccess()+" Available playlists:\n"); 218 | list.forEach(str -> builder.append("`").append(str).append("` ")); 219 | event.reply(builder.toString()); 220 | } 221 | } 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/JMusicBot.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 John Grosh (jagrosh). 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.jagrosh.jmusicbot; 17 | 18 | import com.jagrosh.jdautilities.command.CommandClientBuilder; 19 | import com.jagrosh.jdautilities.commons.waiter.EventWaiter; 20 | import com.jagrosh.jdautilities.examples.command.*; 21 | import com.jagrosh.jmusicbot.commands.admin.*; 22 | import com.jagrosh.jmusicbot.commands.dj.*; 23 | import com.jagrosh.jmusicbot.commands.general.*; 24 | import com.jagrosh.jmusicbot.commands.music.*; 25 | import com.jagrosh.jmusicbot.commands.owner.*; 26 | import com.jagrosh.jmusicbot.entities.Prompt; 27 | import com.jagrosh.jmusicbot.gui.GUI; 28 | import com.jagrosh.jmusicbot.settings.SettingsManager; 29 | import com.jagrosh.jmusicbot.utils.OtherUtil; 30 | import java.awt.Color; 31 | import java.util.Arrays; 32 | import javax.security.auth.login.LoginException; 33 | import net.dv8tion.jda.api.*; 34 | import net.dv8tion.jda.api.entities.Activity; 35 | import net.dv8tion.jda.api.requests.GatewayIntent; 36 | import net.dv8tion.jda.api.utils.cache.CacheFlag; 37 | import org.slf4j.Logger; 38 | import org.slf4j.LoggerFactory; 39 | 40 | /** 41 | * 42 | * @author John Grosh (jagrosh) 43 | */ 44 | public class JMusicBot 45 | { 46 | public final static Logger LOG = LoggerFactory.getLogger(JMusicBot.class); 47 | public final static Permission[] RECOMMENDED_PERMS = {Permission.MESSAGE_READ, Permission.MESSAGE_WRITE, Permission.MESSAGE_HISTORY, Permission.MESSAGE_ADD_REACTION, 48 | Permission.MESSAGE_EMBED_LINKS, Permission.MESSAGE_ATTACH_FILES, Permission.MESSAGE_MANAGE, Permission.MESSAGE_EXT_EMOJI, 49 | Permission.MANAGE_CHANNEL, Permission.VOICE_CONNECT, Permission.VOICE_SPEAK, Permission.NICKNAME_CHANGE}; 50 | public final static GatewayIntent[] INTENTS = {GatewayIntent.DIRECT_MESSAGES, GatewayIntent.GUILD_MESSAGES, GatewayIntent.GUILD_MESSAGE_REACTIONS, GatewayIntent.GUILD_VOICE_STATES}; 51 | 52 | /** 53 | * @param args the command line arguments 54 | */ 55 | public static void main(String[] args) 56 | { 57 | if(args.length > 0) 58 | switch(args[0].toLowerCase()) 59 | { 60 | case "generate-config": 61 | BotConfig.writeDefaultConfig(); 62 | return; 63 | default: 64 | } 65 | startBot(); 66 | } 67 | 68 | private static void startBot() 69 | { 70 | // create prompt to handle startup 71 | Prompt prompt = new Prompt("JMusicBot"); 72 | 73 | // startup checks 74 | OtherUtil.checkVersion(prompt); 75 | OtherUtil.checkJavaVersion(prompt); 76 | 77 | // load config 78 | BotConfig config = new BotConfig(prompt); 79 | config.load(); 80 | if(!config.isValid()) 81 | return; 82 | LOG.info("Loaded config from " + config.getConfigLocation()); 83 | 84 | // set up the listener 85 | EventWaiter waiter = new EventWaiter(); 86 | SettingsManager settings = new SettingsManager(); 87 | Bot bot = new Bot(waiter, config, settings); 88 | 89 | AboutCommand aboutCommand = new AboutCommand(Color.BLUE.brighter(), 90 | "a music bot that is [easy to host yourself!](https://github.com/jagrosh/MusicBot) (v" + OtherUtil.getCurrentVersion() + ")", 91 | new String[]{"High-quality music playback", "FairQueue™ Technology", "Easy to host yourself"}, 92 | RECOMMENDED_PERMS); 93 | aboutCommand.setIsAuthor(false); 94 | aboutCommand.setReplacementCharacter("\uD83C\uDFB6"); // 🎶 95 | 96 | // set up the command client 97 | CommandClientBuilder cb = new CommandClientBuilder() 98 | .setPrefix(config.getPrefix()) 99 | .setAlternativePrefix(config.getAltPrefix()) 100 | .setOwnerId(Long.toString(config.getOwnerId())) 101 | .setEmojis(config.getSuccess(), config.getWarning(), config.getError()) 102 | .setHelpWord(config.getHelp()) 103 | .setLinkedCacheSize(200) 104 | .setGuildSettingsManager(settings) 105 | .addCommands(aboutCommand, 106 | new PingCommand(), 107 | new SettingsCmd(bot), 108 | 109 | new LyricsCmd(bot), 110 | new NowplayingCmd(bot), 111 | new PlayCmd(bot), 112 | new PlaylistsCmd(bot), 113 | new QueueCmd(bot), 114 | new RemoveCmd(bot), 115 | new SearchCmd(bot), 116 | new SCSearchCmd(bot), 117 | new ShuffleCmd(bot), 118 | new SkipCmd(bot), 119 | 120 | new ForceRemoveCmd(bot), 121 | new ForceskipCmd(bot), 122 | new MoveTrackCmd(bot), 123 | new PauseCmd(bot), 124 | new PlaynextCmd(bot), 125 | new RepeatCmd(bot), 126 | new SkiptoCmd(bot), 127 | new StopCmd(bot), 128 | new VolumeCmd(bot), 129 | 130 | new PrefixCmd(bot), 131 | new SetdjCmd(bot), 132 | new SkipratioCmd(bot), 133 | new SettcCmd(bot), 134 | new SetvcCmd(bot), 135 | 136 | new AutoplaylistCmd(bot), 137 | new DebugCmd(bot), 138 | new PlaylistCmd(bot), 139 | new SetavatarCmd(bot), 140 | new SetgameCmd(bot), 141 | new SetnameCmd(bot), 142 | new SetstatusCmd(bot), 143 | new ShutdownCmd(bot) 144 | ); 145 | if(config.useEval()) 146 | cb.addCommand(new EvalCmd(bot)); 147 | boolean nogame = false; 148 | if(config.getStatus()!=OnlineStatus.UNKNOWN) 149 | cb.setStatus(config.getStatus()); 150 | if(config.getGame()==null) 151 | cb.useDefaultGame(); 152 | else if(config.getGame().getName().equalsIgnoreCase("none")) 153 | { 154 | cb.setActivity(null); 155 | nogame = true; 156 | } 157 | else 158 | cb.setActivity(config.getGame()); 159 | 160 | if(!prompt.isNoGUI()) 161 | { 162 | try 163 | { 164 | GUI gui = new GUI(bot); 165 | bot.setGUI(gui); 166 | gui.init(); 167 | } 168 | catch(Exception e) 169 | { 170 | LOG.error("Could not start GUI. If you are " 171 | + "running on a server or in a location where you cannot display a " 172 | + "window, please run in nogui mode using the -Dnogui=true flag."); 173 | } 174 | } 175 | 176 | // attempt to log in and start 177 | try 178 | { 179 | JDA jda = JDABuilder.create(config.getToken(), Arrays.asList(INTENTS)) 180 | .enableCache(CacheFlag.MEMBER_OVERRIDES, CacheFlag.VOICE_STATE) 181 | .disableCache(CacheFlag.ACTIVITY, CacheFlag.CLIENT_STATUS, CacheFlag.EMOTE, CacheFlag.ONLINE_STATUS) 182 | .setActivity(nogame ? null : Activity.playing("loading...")) 183 | .setStatus(config.getStatus()==OnlineStatus.INVISIBLE || config.getStatus()==OnlineStatus.OFFLINE 184 | ? OnlineStatus.INVISIBLE : OnlineStatus.DO_NOT_DISTURB) 185 | .addEventListeners(cb.build(), waiter, new Listener(bot)) 186 | .setBulkDeleteSplittingEnabled(true) 187 | .build(); 188 | bot.setJDA(jda); 189 | } 190 | catch (LoginException ex) 191 | { 192 | prompt.alert(Prompt.Level.ERROR, "JMusicBot", ex + "\nPlease make sure you are " 193 | + "editing the correct config.txt file, and that you have used the " 194 | + "correct token (not the 'secret'!)\nConfig Location: " + config.getConfigLocation()); 195 | System.exit(1); 196 | } 197 | catch(IllegalArgumentException ex) 198 | { 199 | prompt.alert(Prompt.Level.ERROR, "JMusicBot", "Some aspect of the configuration is " 200 | + "invalid: " + ex + "\nConfig Location: " + config.getConfigLocation()); 201 | System.exit(1); 202 | } 203 | } 204 | } 205 | --------------------------------------------------------------------------------