├── .gitignore ├── src ├── main │ ├── resources │ │ ├── natives │ │ │ ├── linux-arm │ │ │ │ └── libconnector.so │ │ │ ├── linux-x86 │ │ │ │ └── libconnector.so │ │ │ ├── linux-aarch32 │ │ │ │ └── libconnector.so │ │ │ ├── linux-aarch64 │ │ │ │ └── libconnector.so │ │ │ └── linux-armhf │ │ │ │ └── libconnector.so │ │ ├── logback.xml │ │ └── reference.conf │ └── java │ │ └── com │ │ └── jagrosh │ │ └── jmusicbot │ │ ├── queue │ │ ├── Queueable.java │ │ └── FairQueue.java │ │ ├── commands │ │ ├── OwnerCommand.java │ │ ├── music │ │ │ ├── SCSearchCmd.java │ │ │ ├── NowplayingCmd.java │ │ │ ├── ShuffleCmd.java │ │ │ ├── PlaylistsCmd.java │ │ │ ├── SkipCmd.java │ │ │ ├── RemoveCmd.java │ │ │ ├── LyricsCmd.java │ │ │ ├── QueueCmd.java │ │ │ └── SearchCmd.java │ │ ├── AdminCommand.java │ │ ├── owner │ │ │ ├── ShutdownCmd.java │ │ │ ├── EvalCmd.java │ │ │ ├── SetnameCmd.java │ │ │ ├── SetstatusCmd.java │ │ │ ├── SetavatarCmd.java │ │ │ ├── AutoplaylistCmd.java │ │ │ ├── DebugCmd.java │ │ │ ├── SetgameCmd.java │ │ │ └── PlaylistCmd.java │ │ ├── dj │ │ │ ├── StopCmd.java │ │ │ ├── ForceskipCmd.java │ │ │ ├── PauseCmd.java │ │ │ ├── SkiptoCmd.java │ │ │ ├── VolumeCmd.java │ │ │ ├── RepeatCmd.java │ │ │ ├── MoveTrackCmd.java │ │ │ ├── ForceRemoveCmd.java │ │ │ └── PlaynextCmd.java │ │ ├── DJCommand.java │ │ ├── admin │ │ │ ├── PrefixCmd.java │ │ │ ├── SkipratioCmd.java │ │ │ ├── SetdjCmd.java │ │ │ ├── SetvcCmd.java │ │ │ └── SettcCmd.java │ │ ├── general │ │ │ └── SettingsCmd.java │ │ └── MusicCommand.java │ │ ├── entities │ │ ├── Pair.java │ │ └── Prompt.java │ │ ├── settings │ │ ├── RepeatMode.java │ │ ├── SettingsManager.java │ │ └── Settings.java │ │ ├── gui │ │ ├── ConsolePanel.java │ │ ├── GUI.java │ │ └── TextAreaOutputStream.java │ │ ├── audio │ │ ├── RequestMetadata.java │ │ ├── QueuedTrack.java │ │ ├── PlayerManager.java │ │ ├── AloneInVoiceHandler.java │ │ ├── TransformativeAudioSourceManager.java │ │ └── NowplayingHandler.java │ │ ├── utils │ │ ├── FormatUtil.java │ │ └── OtherUtil.java │ │ ├── Listener.java │ │ ├── Bot.java │ │ └── JMusicBot.java └── test │ └── java │ └── com │ └── jagrosh │ └── jmusicbot │ └── FairQueueTest.java ├── README.md ├── scripts └── run_jmusicbot.sh └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /nbproject/ 2 | /target/ 3 | /Playlists/ 4 | /test/ 5 | *.json 6 | *.txt 7 | nb*.xml -------------------------------------------------------------------------------- /src/main/resources/natives/linux-arm/libconnector.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wolf-yuan-6115/flowermoon/HEAD/src/main/resources/natives/linux-arm/libconnector.so -------------------------------------------------------------------------------- /src/main/resources/natives/linux-x86/libconnector.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wolf-yuan-6115/flowermoon/HEAD/src/main/resources/natives/linux-x86/libconnector.so -------------------------------------------------------------------------------- /src/main/resources/natives/linux-aarch32/libconnector.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wolf-yuan-6115/flowermoon/HEAD/src/main/resources/natives/linux-aarch32/libconnector.so -------------------------------------------------------------------------------- /src/main/resources/natives/linux-aarch64/libconnector.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wolf-yuan-6115/flowermoon/HEAD/src/main/resources/natives/linux-aarch64/libconnector.so -------------------------------------------------------------------------------- /src/main/resources/natives/linux-armhf/libconnector.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wolf-yuan-6115/flowermoon/HEAD/src/main/resources/natives/linux-armhf/libconnector.so -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | %nopex[%d{HH:mm:ss}] [%level] [%logger{0}]: %msg%n%ex 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 花月 - JMusic 中文翻譯版本 2 | 3 | 一個跨平台,簡潔且容易使用的 Discord 音樂機器人! 4 | 5 | > 此音樂機器人為 JMusic 修改版,可以直接使用 Jmusic 現有的設置檔案 6 | > 目前 JMusic 版本: 0.3.9 7 | 8 | ## 功能 9 | 10 | - 容易託管 (只需要安裝 Java) 11 | - 快速載入音樂 12 | - 沒有需要提供其他 API 金鑰 (除了 Discord 機器人 Token) 13 | - 流暢的音樂播放器 14 | - 可自訂伺服器"DJ"身分組 15 | - 美麗、簡潔的選單 16 | - 支援許多音樂來源,例如 YouTube, SoundCloud 等等 17 | - 支援許多廣播、直播來源 18 | - 可播放本地檔案 19 | - 播放清單支援 (包含 YouTube 及本地播放清單) 20 | 21 | ## 支援的格式 22 | 23 | 花月支援所有 [lavaplayer](https://github.com/sedmelluq/lavaplayer#supported-formats) 支援的格式: 24 | 25 | ### 來源 26 | 27 | - YouTube 28 | - SoundCloud 29 | - Bandcamp 30 | - Vimeo 31 | - Twitch 直播 32 | - 本地檔案 33 | - HTTP 網站 34 | 35 | ### 格式 36 | 37 | - MP3 38 | - FLAC 39 | - WAV 40 | - Matroska/WebM (AAC, Opus 或 Vorbis 編碼) 41 | - MP4/M4A (AAC 編碼) 42 | - OGG 格式 (Opus, Vorbis 和 FLAC 編碼) 43 | - AAC 格式 44 | - 直播播放清單 (M3U and PLS) 45 | 46 | ## 配置 47 | 48 | 啟動程式時程式將會自動產生設定擋 49 | 50 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/queue/Queueable.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.queue; 17 | 18 | /** 19 | * 20 | * @author John Grosh 21 | */ 22 | public interface Queueable { 23 | 24 | public long getIdentifier(); 25 | } 26 | -------------------------------------------------------------------------------- /scripts/run_jmusicbot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This will have this script check for a new version of JMusicBot every 4 | # startup (and download it if the latest version isn't currently downloaded) 5 | DOWNLOAD=true 6 | 7 | # This will cause the script to run in a loop so that the bot auto-restarts 8 | # when you use the shutdown command 9 | LOOP=true 10 | 11 | download() { 12 | if [ $DOWNLOAD == true ]; then 13 | URL=$(curl -s https://api.github.com/repos/jagrosh/MusicBot/releases/latest \ 14 | | grep -i browser_download_url.*\.jar \ 15 | | sed 's/.*\(http.*\)"/\1/') 16 | FILENAME=$(echo $URL | sed 's/.*\/\([^\/]*\)/\1/') 17 | if [ -f $FILENAME ]; then 18 | echo "Latest version already downloaded (${FILENAME})" 19 | else 20 | curl -L $URL -o $FILENAME 21 | fi 22 | fi 23 | } 24 | 25 | run() { 26 | java -Dnogui=true -jar $(ls -t JMusicBot* | head -1) 27 | } 28 | 29 | while 30 | download 31 | run 32 | $LOOP 33 | do 34 | continue 35 | done 36 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/OwnerCommand.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 | 20 | /** 21 | * 22 | * @author John Grosh (john.a.grosh@gmail.com) 23 | */ 24 | public abstract class OwnerCommand extends Command 25 | { 26 | public OwnerCommand() 27 | { 28 | this.category = new Category("Owner"); 29 | this.ownerCommand = true; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/music/SCSearchCmd.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.jmusicbot.Bot; 19 | 20 | /** 21 | * 22 | * @author John Grosh 23 | */ 24 | public class SCSearchCmd extends SearchCmd 25 | { 26 | public SCSearchCmd(Bot bot) 27 | { 28 | super(bot); 29 | this.searchPrefix = "scsearch:"; 30 | this.name = "scsearch"; 31 | this.help = "searches Soundcloud for a provided query"; 32 | this.aliases = bot.getConfig().getAliases(this.name); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/entities/Pair.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.entities; 17 | 18 | /** 19 | * 20 | * @author John Grosh (john.a.grosh@gmail.com) 21 | * @param 22 | * @param 23 | */ 24 | public class Pair 25 | { 26 | private final K key; 27 | private final V value; 28 | 29 | public Pair(K key, V value) 30 | { 31 | this.key = key; 32 | this.value = value; 33 | } 34 | 35 | public K getKey() 36 | { 37 | return key; 38 | } 39 | 40 | public V getValue() 41 | { 42 | return value; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/settings/RepeatMode.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 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 | /** 19 | * 20 | * @author Michaili K 21 | */ 22 | public enum RepeatMode 23 | { 24 | OFF(null, "Off"), 25 | ALL("\uD83D\uDD01", "All"), // 🔁 26 | SINGLE("\uD83D\uDD02", "Single"); // 🔂 27 | 28 | private final String emoji; 29 | private final String userFriendlyName; 30 | 31 | private RepeatMode(String emoji, String userFriendlyName) 32 | { 33 | this.emoji = emoji; 34 | this.userFriendlyName = userFriendlyName; 35 | } 36 | 37 | public String getEmoji() 38 | { 39 | return emoji; 40 | } 41 | 42 | public String getUserFriendlyName() 43 | { 44 | return userFriendlyName; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/AdminCommand.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 net.dv8tion.jda.api.Permission; 20 | 21 | /** 22 | * 23 | * @author John Grosh (john.a.grosh@gmail.com) 24 | */ 25 | public abstract class AdminCommand extends Command 26 | { 27 | public AdminCommand() 28 | { 29 | this.category = new Category("Admin", event -> 30 | { 31 | if(event.getAuthor().getId().equals(event.getClient().getOwnerId())) 32 | return true; 33 | if(event.getGuild()==null) 34 | return true; 35 | return event.getMember().hasPermission(Permission.MANAGE_SERVER); 36 | }); 37 | this.guildOnly = true; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/owner/ShutdownCmd.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 | 22 | /** 23 | * 24 | * @author John Grosh 25 | */ 26 | public class ShutdownCmd extends OwnerCommand 27 | { 28 | private final Bot bot; 29 | 30 | public ShutdownCmd(Bot bot) 31 | { 32 | this.bot = bot; 33 | this.name = "shutdown"; 34 | this.help = "safely shuts down"; 35 | this.aliases = bot.getConfig().getAliases(this.name); 36 | this.guildOnly = false; 37 | } 38 | 39 | @Override 40 | protected void execute(CommandEvent event) 41 | { 42 | event.replyWarning("Shutting down..."); 43 | bot.shutdown(); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/gui/ConsolePanel.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.Dimension; 19 | import java.awt.GridLayout; 20 | import java.io.PrintStream; 21 | import javax.swing.JPanel; 22 | import javax.swing.JScrollPane; 23 | import javax.swing.JTextArea; 24 | 25 | /** 26 | * 27 | * @author John Grosh 28 | */ 29 | public class ConsolePanel extends JPanel { 30 | 31 | public ConsolePanel() 32 | { 33 | super(); 34 | JTextArea text = new JTextArea(); 35 | text.setLineWrap(true); 36 | text.setWrapStyleWord(true); 37 | text.setEditable(false); 38 | PrintStream con=new PrintStream(new TextAreaOutputStream(text)); 39 | System.setOut(con); 40 | System.setErr(con); 41 | 42 | JScrollPane pane = new JScrollPane(); 43 | pane.setViewportView(text); 44 | 45 | super.setLayout(new GridLayout(1,1)); 46 | super.add(pane); 47 | super.setPreferredSize(new Dimension(400,300)); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/dj/StopCmd.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.commands.DJCommand; 22 | 23 | /** 24 | * 25 | * @author John Grosh 26 | */ 27 | public class StopCmd extends DJCommand 28 | { 29 | public StopCmd(Bot bot) 30 | { 31 | super(bot); 32 | this.name = "stop"; 33 | this.help = "stops the current song and clears the queue"; 34 | this.aliases = bot.getConfig().getAliases(this.name); 35 | this.bePlaying = false; 36 | } 37 | 38 | @Override 39 | public void doCommand(CommandEvent event) 40 | { 41 | AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); 42 | handler.stopAndClear(); 43 | event.getGuild().getAudioManager().closeAudioConnection(); 44 | event.reply(event.getClient().getSuccess()+" The player has stopped and the queue has been cleared."); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/DJCommand.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.CommandEvent; 19 | import com.jagrosh.jmusicbot.Bot; 20 | import com.jagrosh.jmusicbot.settings.Settings; 21 | import net.dv8tion.jda.api.Permission; 22 | import net.dv8tion.jda.api.entities.Role; 23 | 24 | /** 25 | * 26 | * @author John Grosh (john.a.grosh@gmail.com) 27 | */ 28 | public abstract class DJCommand extends MusicCommand 29 | { 30 | public DJCommand(Bot bot) 31 | { 32 | super(bot); 33 | this.category = new Category("DJ", event -> checkDJPermission(event)); 34 | } 35 | 36 | public static boolean checkDJPermission(CommandEvent event) 37 | { 38 | if(event.getAuthor().getId().equals(event.getClient().getOwnerId())) 39 | return true; 40 | if(event.getGuild()==null) 41 | return true; 42 | if(event.getMember().hasPermission(Permission.MANAGE_SERVER)) 43 | return true; 44 | Settings settings = event.getClient().getSettingsFor(event.getGuild()); 45 | Role dj = settings.getRole(event.getGuild()); 46 | return dj!=null && (event.getMember().getRoles().contains(dj) || dj.getIdLong()==event.getGuild().getIdLong()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/commands/dj/ForceskipCmd.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.audio.RequestMetadata; 22 | import com.jagrosh.jmusicbot.commands.DJCommand; 23 | 24 | /** 25 | * 26 | * @author John Grosh 27 | */ 28 | public class ForceskipCmd extends DJCommand 29 | { 30 | public ForceskipCmd(Bot bot) 31 | { 32 | super(bot); 33 | this.name = "forceskip"; 34 | this.help = "skips the current song"; 35 | this.aliases = bot.getConfig().getAliases(this.name); 36 | this.bePlaying = true; 37 | } 38 | 39 | @Override 40 | public void doCommand(CommandEvent event) 41 | { 42 | AudioHandler handler = (AudioHandler)event.getGuild().getAudioManager().getSendingHandler(); 43 | RequestMetadata rm = handler.getRequestMetadata(); 44 | event.reply(event.getClient().getSuccess()+" Skipped **"+handler.getPlayer().getPlayingTrack().getInfo().title 45 | +"** "+(rm.getOwner() == 0L ? "(autoplay)" : "(requested by **" + rm.user.username + "**)")); 46 | handler.getPlayer().stopTrack(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/test/java/com/jagrosh/jmusicbot/FairQueueTest.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 com.jagrosh.jmusicbot.queue.FairQueue; 19 | import com.jagrosh.jmusicbot.queue.Queueable; 20 | import org.junit.Test; 21 | import static org.junit.Assert.*; 22 | 23 | /** 24 | * 25 | * @author John Grosh (john.a.grosh@gmail.com) 26 | */ 27 | public class FairQueueTest 28 | { 29 | @Test 30 | public void differentIdentifierSize() 31 | { 32 | FairQueue queue = new FairQueue<>(); 33 | int size = 100; 34 | for(int i=0; i queue = 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 && i6) 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 && i6) 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 = "<title>"; 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 | <?xml version="1.0" encoding="UTF-8"?> 2 | <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 3 | <modelVersion>4.0.0</modelVersion> 4 | <groupId>com.jagrosh</groupId> 5 | <artifactId>JMusicBot</artifactId> 6 | <version>Snapshot</version> 7 | <packaging>jar</packaging> 8 | 9 | <repositories> 10 | <repository> 11 | <id>dv8tion</id> 12 | <name>m2-dv8tion</name> 13 | <url>https://m2.dv8tion.net/releases</url> 14 | </repository> 15 | <repository> 16 | <id>central</id> 17 | <name>bintray</name> 18 | <url>https://jcenter.bintray.com</url> 19 | </repository> 20 | <repository> 21 | <id>jitpack.io</id> 22 | <url>https://jitpack.io</url> 23 | <snapshots> 24 | <enabled>true</enabled> 25 | <updatePolicy>always</updatePolicy> 26 | </snapshots> 27 | </repository> 28 | </repositories> 29 | 30 | <dependencies> 31 | <!-- Discord Dependencies --> 32 | <dependency> 33 | <groupId>net.dv8tion</groupId> 34 | <artifactId>JDA</artifactId> 35 | <version>4.4.1_353</version> 36 | </dependency> 37 | <dependency> 38 | <groupId>com.jagrosh</groupId> 39 | <artifactId>jda-utilities</artifactId> 40 | <version>3.0.5</version> 41 | <type>pom</type> 42 | </dependency> 43 | 44 | <!-- Music Dependencies --> 45 | <!-- using a fork of this to fix some issues faster --> 46 | <!-- dependency> 47 | <groupId>com.sedmelluq</groupId> 48 | <artifactId>lavaplayer</artifactId> 49 | <version>1.3.78</version> 50 | </dependency --> 51 | <dependency> 52 | <groupId>com.github.jagrosh</groupId> 53 | <artifactId>lavaplayer</artifactId> 54 | <version>jmusicbot-SNAPSHOT</version> 55 | </dependency> 56 | <!-- this is needed, but isn't actually hosted anywhere anymore... uh --> 57 | <!--dependency> 58 | <groupId>com.sedmelluq</groupId> 59 | <artifactId>lavaplayer-natives-extra</artifactId> 60 | <version>1.3.13</version> 61 | </dependency--> 62 | <dependency> 63 | <groupId>com.github.jagrosh</groupId> 64 | <artifactId>JLyrics</artifactId> 65 | <version>master-SNAPSHOT</version> 66 | </dependency> 67 | 68 | <!-- Misc Internal Dependencies --> 69 | <dependency> 70 | <groupId>ch.qos.logback</groupId> 71 | <artifactId>logback-classic</artifactId> 72 | <version>1.2.3</version> 73 | </dependency> 74 | <dependency> 75 | <groupId>com.typesafe</groupId> 76 | <artifactId>config</artifactId> 77 | <version>1.3.2</version> 78 | </dependency> 79 | <dependency> 80 | <groupId>org.jsoup</groupId> 81 | <artifactId>jsoup</artifactId> 82 | <version>1.15.3</version> 83 | </dependency> 84 | 85 | <!-- Testing Dependencies --> 86 | <dependency> 87 | <groupId>junit</groupId> 88 | <artifactId>junit</artifactId> 89 | <version>4.13.1</version> 90 | <scope>test</scope> 91 | </dependency> 92 | <dependency> 93 | <groupId>org.hamcrest</groupId> 94 | <artifactId>hamcrest-core</artifactId> 95 | <version>1.3</version> 96 | <scope>test</scope> 97 | </dependency> 98 | </dependencies> 99 | 100 | <build> 101 | <plugins> 102 | <plugin> 103 | <groupId>org.apache.maven.plugins</groupId> 104 | <artifactId>maven-shade-plugin</artifactId> 105 | <version>1.5</version> 106 | <executions> 107 | <execution> 108 | <phase>package</phase> 109 | <goals> 110 | <goal>shade</goal> 111 | </goals> 112 | <configuration> 113 | <shadedArtifactAttached>true</shadedArtifactAttached> 114 | <shadedClassifierName>All</shadedClassifierName> 115 | <artifactSet> 116 | <includes> 117 | <include>*:*</include> 118 | </includes> 119 | </artifactSet> 120 | <transformers> 121 | <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> 122 | <resource>reference.conf</resource> 123 | </transformer> 124 | <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> 125 | <manifestEntries> 126 | <Main-Class>com.jagrosh.jmusicbot.JMusicBot</Main-Class> 127 | <Specification-Title>${project.artifactId}</Specification-Title> 128 | <Specification-Version>${project.version}</Specification-Version> 129 | <Implementation-Title>${project.artifactId}</Implementation-Title> 130 | <Implementation-Version>${project.version}</Implementation-Version> 131 | <Implementation-Vendor-Id>${project.groupId}</Implementation-Vendor-Id> 132 | </manifestEntries> 133 | </transformer> 134 | </transformers> 135 | </configuration> 136 | </execution> 137 | </executions> 138 | </plugin> 139 | </plugins> 140 | </build> 141 | 142 | <properties> 143 | <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> 144 | <maven.compiler.source>1.8</maven.compiler.source> 145 | <maven.compiler.target>1.8</maven.compiler.target> 146 | </properties> 147 | </project> 148 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/audio/NowplayingHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh <john.a.grosh@gmail.com>. 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<Long,Pair<Long,Long>> 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<Long> 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<Long,Long> 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<Long,Long> 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 <john.a.grosh@gmail.com>. 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 <john.a.grosh@gmail.com> 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 = "<query>"; 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<playlist.getTracks().size(); i++) 126 | { 127 | AudioTrack track = playlist.getTracks().get(i); 128 | builder.addChoices("`["+FormatUtil.formatTime(track.getDuration())+"]` [**"+track.getInfo().title+"**]("+track.getInfo().uri+")"); 129 | } 130 | builder.build().display(m); 131 | } 132 | 133 | @Override 134 | public void noMatches() 135 | { 136 | m.editMessage(FormatUtil.filter(event.getClient().getWarning()+" No results found for `"+event.getArgs()+"`.")).queue(); 137 | } 138 | 139 | @Override 140 | public void loadFailed(FriendlyException throwable) 141 | { 142 | if(throwable.severity==Severity.COMMON) 143 | m.editMessage(event.getClient().getError()+" Error loading: "+throwable.getMessage()).queue(); 144 | else 145 | m.editMessage(event.getClient().getError()+" Error loading track.").queue(); 146 | } 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/main/resources/reference.conf: -------------------------------------------------------------------------------- 1 | /// START OF JMUSICBOT CONFIG /// 2 | ///////////////////////////////////////////////////////// 3 | // Config for the JMusicBot // 4 | ///////////////////////////////////////////////////////// 5 | // Any line starting with // is ignored // 6 | // You MUST set the token and owner // 7 | // All other items have defaults if you don't set them // 8 | // Open in Notepad++ for best results // 9 | ///////////////////////////////////////////////////////// 10 | 11 | 12 | // This sets the token for the bot to log in with 13 | // This MUST be a bot token (user tokens will not work) 14 | // If you don't know how to get a bot token, please see the guide here: 15 | // https://github.com/jagrosh/MusicBot/wiki/Getting-a-Bot-Token 16 | 17 | token = BOT_TOKEN_HERE 18 | 19 | 20 | // This sets the owner of the bot 21 | // This needs to be the owner's ID (a 17-18 digit number) 22 | // https://github.com/jagrosh/MusicBot/wiki/Finding-Your-User-ID 23 | 24 | owner = 0 // OWNER ID 25 | 26 | 27 | // This sets the prefix for the bot 28 | // The prefix is used to control the commands 29 | // If you use !!, the play command will be !!play 30 | // If you do not set this, the prefix will be a mention of the bot (@Botname play) 31 | 32 | prefix = "@mention" 33 | 34 | 35 | // If you set this, it modifies the default game of the bot 36 | // Set this to NONE to have no game 37 | // Set this to DEFAULT to use the default game 38 | // You can make the game "Playing X", "Listening to X", or "Watching X" 39 | // where X is the title. If you don't include an action, it will use the 40 | // default of "Playing" 41 | 42 | game = "DEFAULT" 43 | 44 | 45 | // If you set this, it will modify the default status of bot 46 | // Valid values: ONLINE IDLE DND INVISIBLE 47 | 48 | status = ONLINE 49 | 50 | 51 | // If you set this to true, the bot will list the title of the song it is currently playing in its 52 | // "Playing" status. Note that this will ONLY work if the bot is playing music on ONE guild; 53 | // if the bot is playing on multiple guilds, this will not work. 54 | 55 | songinstatus=false 56 | 57 | 58 | // If you set this, the bot will also use this prefix in addition to 59 | // the one provided above 60 | 61 | altprefix = "NONE" 62 | 63 | 64 | // If you set these, it will change the various emojis 65 | 66 | success = "🎶" 67 | warning = "💡" 68 | error = "🚫" 69 | loading = "⌚" 70 | searching = "🔎" 71 | 72 | 73 | // If you set this, you change the word used to view the help. 74 | // For example, if you set the prefix to !! and the help to cmds, you would type 75 | // !!cmds to see the help text 76 | 77 | help = help 78 | 79 | 80 | // If you set this, the "nowplaying" command will show youtube thumbnails 81 | // Note: If you set this to true, the nowplaying boxes will NOT refresh 82 | // This is because refreshing the boxes causes the image to be reloaded 83 | // every time it refreshes. 84 | 85 | npimages = false 86 | 87 | 88 | // If you set this, the bot will not leave a voice channel after it finishes a queue. 89 | // Keep in mind that being connected to a voice channel uses additional bandwith, 90 | // so this option is not recommended if bandwidth is a concern. 91 | 92 | stayinchannel = false 93 | 94 | 95 | // This sets the maximum amount of seconds any track loaded can be. If not set or set 96 | // to any number less than or equal to zero, there is no maximum time length. This time 97 | // restriction applies to songs loaded from any source. 98 | 99 | maxtime = 0 100 | 101 | 102 | // This sets the amount of seconds the bot will stay alone on a voice channel until it 103 | // automatically leaves the voice channel and clears the queue. If not set or set 104 | // to any number less than or equal to zero, the bot won't leave when alone. 105 | 106 | alonetimeuntilstop = 0 107 | 108 | 109 | // This sets an alternative folder to be used as the Playlists folder 110 | // This can be a relative or absolute path 111 | 112 | playlistsfolder = "Playlists" 113 | 114 | 115 | // By default, the bot will DM the owner if the bot is running and a new version of the bot 116 | // becomes available. Set this to false to disable this feature. 117 | 118 | updatealerts=true 119 | 120 | 121 | // Changing this changes the lyrics provider 122 | // Currently available providers: "A-Z Lyrics", "Genius", "MusicMatch", "LyricsFreak" 123 | // At the time of writing, I would recommend sticking with A-Z Lyrics or MusicMatch, 124 | // as Genius tends to have a lot of non-song results and you might get something 125 | // completely unrelated to what you want. 126 | // If you are interested in contributing a provider, please see 127 | // https://github.com/jagrosh/JLyrics 128 | 129 | lyrics.default = "A-Z Lyrics" 130 | 131 | 132 | // These settings allow you to configure custom aliases for all commands. 133 | // Multiple aliases may be given, separated by commas. 134 | // 135 | // Example 1: Giving command "play" the alias "p": 136 | // play = [ p ] 137 | // 138 | // Example 2: Giving command "search" the aliases "yts" and "find": 139 | // search = [ yts, find ] 140 | 141 | aliases { 142 | // General commands 143 | settings = [ status ] 144 | 145 | // Music commands 146 | lyrics = [] 147 | nowplaying = [ np, current ] 148 | play = [] 149 | playlists = [ pls ] 150 | queue = [ list ] 151 | remove = [ delete ] 152 | scsearch = [] 153 | search = [ ytsearch ] 154 | shuffle = [] 155 | skip = [ voteskip ] 156 | 157 | // Admin commands 158 | prefix = [ setprefix ] 159 | setdj = [] 160 | setskip = [ setskippercent, skippercent, setskipratio ] 161 | settc = [] 162 | setvc = [] 163 | 164 | // DJ Commands 165 | forceremove = [ forcedelete, modremove, moddelete, modelete ] 166 | forceskip = [ modskip ] 167 | movetrack = [ move ] 168 | pause = [] 169 | playnext = [] 170 | repeat = [] 171 | skipto = [ jumpto ] 172 | stop = [ leave ] 173 | volume = [ vol ] 174 | } 175 | 176 | 177 | // Transforms are used to modify specific play inputs and convert them to different kinds of inputs 178 | // These are quite complicated to use, and have limited use-cases, but in theory allow for rough 179 | // whitelists or blacklists, roundabout loading from some sources, and customization of how things are 180 | // requested. 181 | // 182 | // These are NOT EASY to set up, so if you want to use these, you'll need to look through the code 183 | // for how they work and what fields are needed. Also, it's possible this feature might get entirely 184 | // removed in the future if I find a better way to do this. 185 | 186 | transforms = {} 187 | 188 | 189 | // If you set this to true, it will enable the eval command for the bot owner. This command 190 | // allows the bot owner to run arbitrary code from the bot's account. 191 | // 192 | // WARNING: 193 | // This command can be extremely dangerous. If you don't know what you're doing, you could 194 | // cause horrific problems on your Discord server or on whatever computer this bot is running 195 | // on. Never run this command unless you are completely positive what you are running. 196 | // 197 | // DO NOT ENABLE THIS IF YOU DON'T KNOW WHAT THIS DOES OR HOW TO USE IT 198 | // IF SOMEONE ASKS YOU TO ENABLE THIS, THERE IS AN 11/10 CHANCE THEY ARE TRYING TO SCAM YOU 199 | 200 | eval=false 201 | 202 | 203 | /// END OF JMUSICBOT CONFIG /// 204 | -------------------------------------------------------------------------------- /src/main/java/com/jagrosh/jmusicbot/utils/OtherUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 John Grosh <john.a.grosh@gmail.com>. 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 <john.a.grosh@gmail.com> 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 <john.a.grosh@gmail.com>. 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 <john.a.grosh@gmail.com> 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 = "<append|delete|make|setdefault>"; 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 = "<name>"; 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 = "<name>"; 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 = "<name> <URL> | <URL> | ..."; 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 = "<playlistname|NONE>"; 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<String> 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 | --------------------------------------------------------------------------------