diff --git a/pom.xml b/pom.xml
index b8b350654..af250b4f5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -45,22 +45,21 @@
4.4.1_353
- com.jagrosh
- jda-utilities
- 3.0.5
- pom
+ com.github.JDA-Applications
+ JDA-Utilities
+ c16a4b264b
dev.arbjerg
lavaplayer
- 2.2.1
+ 2.2.2
dev.lavalink.youtube
common
- 1.5.2
+ 1.8.3
com.github.jagrosh
diff --git a/src/main/java/com/jagrosh/jmusicbot/Bot.java b/src/main/java/com/jagrosh/jmusicbot/Bot.java
index de6d6f756..6041e2669 100644
--- a/src/main/java/com/jagrosh/jmusicbot/Bot.java
+++ b/src/main/java/com/jagrosh/jmusicbot/Bot.java
@@ -26,6 +26,7 @@
import com.jagrosh.jmusicbot.playlist.PlaylistLoader;
import com.jagrosh.jmusicbot.settings.SettingsManager;
import java.util.Objects;
+import com.jagrosh.jmusicbot.utils.YoutubeOauth2TokenHandler;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Activity;
import net.dv8tion.jda.api.entities.Guild;
@@ -44,24 +45,28 @@ public class Bot
private final PlaylistLoader playlists;
private final NowplayingHandler nowplaying;
private final AloneInVoiceHandler aloneInVoiceHandler;
+ private final YoutubeOauth2TokenHandler youTubeOauth2TokenHandler;
+ private final GUI gui;
private boolean shuttingDown = false;
private JDA jda;
- private GUI gui;
- public Bot(EventWaiter waiter, BotConfig config, SettingsManager settings)
+ public Bot(EventWaiter waiter, BotConfig config, SettingsManager settings, GUI gui)
{
this.waiter = waiter;
this.config = config;
this.settings = settings;
this.playlists = new PlaylistLoader(config);
this.threadpool = Executors.newSingleThreadScheduledExecutor();
+ this.youTubeOauth2TokenHandler = new YoutubeOauth2TokenHandler();
+ this.youTubeOauth2TokenHandler.init();
this.players = new PlayerManager(this);
this.players.init();
this.nowplaying = new NowplayingHandler(this);
this.nowplaying.init();
this.aloneInVoiceHandler = new AloneInVoiceHandler(this);
this.aloneInVoiceHandler.init();
+ this.gui = gui;
}
public BotConfig getConfig()
@@ -103,6 +108,11 @@ public AloneInVoiceHandler getAloneInVoiceHandler()
{
return aloneInVoiceHandler;
}
+
+ public YoutubeOauth2TokenHandler getYouTubeOauth2Handler()
+ {
+ return youTubeOauth2TokenHandler;
+ }
public JDA getJDA()
{
@@ -152,9 +162,4 @@ public void setJDA(JDA jda)
{
this.jda = jda;
}
-
- public void setGUI(GUI gui)
- {
- this.gui = gui;
- }
}
diff --git a/src/main/java/com/jagrosh/jmusicbot/BotConfig.java b/src/main/java/com/jagrosh/jmusicbot/BotConfig.java
index f6a8757ac..06c64cf5e 100644
--- a/src/main/java/com/jagrosh/jmusicbot/BotConfig.java
+++ b/src/main/java/com/jagrosh/jmusicbot/BotConfig.java
@@ -42,7 +42,7 @@ public class BotConfig
private String token, prefix, altprefix, helpWord, playlistsFolder, logLevel,
successEmoji, warningEmoji, errorEmoji, loadingEmoji, searchingEmoji,
evalEngine;
- private boolean stayInChannel, songInGame, npImages, updatealerts, useEval, dbots;
+ private boolean youtubeOauth2, stayInChannel, songInGame, npImages, updatealerts, useEval, dbots;
private long owner, maxSeconds, aloneTimeUntilStop;
private int maxYTPlaylistPages;
private double skipratio;
@@ -84,6 +84,7 @@ public void load()
searchingEmoji = config.getString("searching");
game = OtherUtil.parseGame(config.getString("game"));
status = OtherUtil.parseStatus(config.getString("status"));
+ youtubeOauth2 = config.getBoolean("youtubeoauth2");
stayInChannel = config.getBoolean("stayinchannel");
songInGame = config.getBoolean("songinstatus");
npImages = config.getBoolean("npimages");
@@ -293,6 +294,11 @@ public String getHelp()
return helpWord;
}
+ public boolean useYoutubeOauth2()
+ {
+ return youtubeOauth2;
+ }
+
public boolean getStay()
{
return stayInChannel;
diff --git a/src/main/java/com/jagrosh/jmusicbot/JMusicBot.java b/src/main/java/com/jagrosh/jmusicbot/JMusicBot.java
index 30409a6bf..2e2ad7ddb 100644
--- a/src/main/java/com/jagrosh/jmusicbot/JMusicBot.java
+++ b/src/main/java/com/jagrosh/jmusicbot/JMusicBot.java
@@ -82,28 +82,15 @@ private static void startBot()
config.load();
if(!config.isValid())
return;
- LOG.info("Loaded config from " + config.getConfigLocation());
- // set log level from config
- ((ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(
- Level.toLevel(config.getLogLevel(), Level.INFO));
-
- // set up the listener
- EventWaiter waiter = new EventWaiter();
- SettingsManager settings = new SettingsManager();
- Bot bot = new Bot(waiter, config, settings);
- CommandClient client = createCommandClient(config, settings, bot);
-
+ GUI gui = null;
if(!prompt.isNoGUI())
{
- try
+ try
{
- GUI gui = new GUI(bot);
- bot.setGUI(gui);
+ gui = new GUI();
gui.init();
-
- LOG.info("Loaded config from " + config.getConfigLocation());
}
catch(Exception e)
{
@@ -113,6 +100,21 @@ private static void startBot()
}
}
+ LOG.info("Loaded config from " + config.getConfigLocation());
+
+ // set log level from config
+ ((ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME)).setLevel(
+ Level.toLevel(config.getLogLevel(), Level.INFO));
+
+ // set up the listener
+ EventWaiter waiter = new EventWaiter();
+ SettingsManager settings = new SettingsManager();
+ Bot bot = new Bot(waiter, config, settings, gui);
+ if (gui != null)
+ gui.setBot(bot);
+ CommandClient client = createCommandClient(config, settings, bot);
+
+
// attempt to log in and start
try
{
diff --git a/src/main/java/com/jagrosh/jmusicbot/Listener.java b/src/main/java/com/jagrosh/jmusicbot/Listener.java
index 2e50b793a..3f2ee1531 100644
--- a/src/main/java/com/jagrosh/jmusicbot/Listener.java
+++ b/src/main/java/com/jagrosh/jmusicbot/Listener.java
@@ -17,8 +17,10 @@
import com.jagrosh.jmusicbot.utils.OtherUtil;
import java.util.concurrent.TimeUnit;
+import com.jagrosh.jmusicbot.utils.YoutubeOauth2TokenHandler;
import net.dv8tion.jda.api.JDA;
import net.dv8tion.jda.api.entities.Guild;
+import net.dv8tion.jda.api.entities.PrivateChannel;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.entities.VoiceChannel;
import net.dv8tion.jda.api.events.ReadyEvent;
@@ -85,6 +87,26 @@ public void onReady(ReadyEvent event)
catch(Exception ignored) {} // ignored
}, 0, 24, TimeUnit.HOURS);
}
+ if (bot.getConfig().useYoutubeOauth2())
+ {
+ YoutubeOauth2TokenHandler.Data data = bot.getYouTubeOauth2Handler().getData();
+ if (data != null)
+ {
+ try
+ {
+ PrivateChannel channel = bot.getJDA().openPrivateChannelById(bot.getConfig().getOwnerId()).complete();
+ channel
+ .sendMessage(
+ "# DO NOT AUTHORISE THIS WITH YOUR MAIN GOOGLE ACCOUNT!!!\n"
+ + "## Create or use an alternative/burner Google account!\n"
+ + "To give JMusicBot access to your Google account, go to "
+ + data.getAuthorisationUrl()
+ + " and enter the code **" + data.getCode() + "**")
+ .queue();
+ }
+ catch (Exception ignored) {}
+ }
+ }
}
@Override
diff --git a/src/main/java/com/jagrosh/jmusicbot/audio/AudioHandler.java b/src/main/java/com/jagrosh/jmusicbot/audio/AudioHandler.java
index e5b93fc50..b26c95471 100644
--- a/src/main/java/com/jagrosh/jmusicbot/audio/AudioHandler.java
+++ b/src/main/java/com/jagrosh/jmusicbot/audio/AudioHandler.java
@@ -41,6 +41,7 @@
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.User;
+import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
@@ -53,6 +54,7 @@ public class AudioHandler extends AudioEventAdapter implements AudioSendHandler
public final static String PAUSE_EMOJI = "\u23F8"; // ⏸
public final static String STOP_EMOJI = "\u23F9"; // ⏹
+ private final static Logger LOGGER = LoggerFactory.getLogger(AudioHandler.class);
private final List defaultQueue = new LinkedList<>();
private final Set votes = new HashSet<>();
@@ -202,8 +204,22 @@ public void onTrackEnd(AudioPlayer player, AudioTrack track, AudioTrackEndReason
}
@Override
- public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception) {
- LoggerFactory.getLogger("AudioHandler").error("Track " + track.getIdentifier() + " has failed to play", exception);
+ public void onTrackException(AudioPlayer player, AudioTrack track, FriendlyException exception)
+ {
+ if (
+ exception.getMessage().equals("Sign in to confirm you're not a bot")
+ || exception.getMessage().equals("Please sign in")
+ || exception.getMessage().equals("This video requires login.")
+ )
+ LOGGER.error(
+ "Track {} has failed to play: {}. "
+ + "You will need to sign in to Google to play YouTube tracks. "
+ + "More info: https://jmusicbot.com/youtube-oauth2",
+ track.getIdentifier(),
+ exception.getMessage()
+ );
+ else
+ LOGGER.error("Track {} has failed to play", track.getIdentifier(), exception);
}
@Override
diff --git a/src/main/java/com/jagrosh/jmusicbot/audio/PlayerManager.java b/src/main/java/com/jagrosh/jmusicbot/audio/PlayerManager.java
index 56999a92b..ecaee80a7 100644
--- a/src/main/java/com/jagrosh/jmusicbot/audio/PlayerManager.java
+++ b/src/main/java/com/jagrosh/jmusicbot/audio/PlayerManager.java
@@ -17,6 +17,7 @@
import com.dunctebot.sourcemanagers.DuncteBotSources;
import com.jagrosh.jmusicbot.Bot;
+import com.jagrosh.jmusicbot.utils.OtherUtil;
import com.sedmelluq.discord.lavaplayer.container.MediaContainerRegistry;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager;
@@ -31,6 +32,11 @@
import com.sedmelluq.discord.lavaplayer.source.vimeo.VimeoAudioSourceManager;
import dev.lavalink.youtube.YoutubeAudioSourceManager;
import net.dv8tion.jda.api.entities.Guild;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
/**
*
@@ -38,6 +44,7 @@
*/
public class PlayerManager extends DefaultAudioPlayerManager
{
+ private final static Logger LOGGER = LoggerFactory.getLogger(PlayerManager.class);
private final Bot bot;
public PlayerManager(Bot bot)
@@ -49,8 +56,7 @@ public void init()
{
TransformativeAudioSourceManager.createTransforms(bot.getConfig().getTransforms()).forEach(t -> registerSourceManager(t));
- YoutubeAudioSourceManager yt = new YoutubeAudioSourceManager(true);
- yt.setPlaylistPageCount(bot.getConfig().getMaxYTPlaylistPages());
+ YoutubeAudioSourceManager yt = setupYoutubeAudioSourceManager();
registerSourceManager(yt);
registerSourceManager(SoundCloudAudioSourceManager.createDefault());
@@ -66,7 +72,42 @@ public void init()
DuncteBotSources.registerAll(this, "en-US");
}
-
+
+ private YoutubeAudioSourceManager setupYoutubeAudioSourceManager()
+ {
+ YoutubeAudioSourceManager yt = new YoutubeAudioSourceManager(true);
+ yt.setPlaylistPageCount(bot.getConfig().getMaxYTPlaylistPages());
+
+ // OAuth2 setup
+ if (bot.getConfig().useYoutubeOauth2())
+ {
+ String token = null;
+ try
+ {
+ token = Files.readString(OtherUtil.getPath("youtubetoken.txt"));
+ }
+ catch (NoSuchFileException e)
+ {
+ /* ignored */
+ }
+ catch (IOException e)
+ {
+ LOGGER.warn("Failed to read YouTube OAuth2 token file: {}", e.getMessage());
+ return yt;
+ }
+ LOGGER.debug("Read YouTube OAuth2 refresh token from youtubetoken.txt");
+ try
+ {
+ yt.useOauth2(token, false);
+ }
+ catch (Exception e)
+ {
+ LOGGER.warn("Failed to authorise with YouTube. If this issue persists, delete the youtubetoken.txt file to reauthorise.", e);
+ }
+ }
+ return yt;
+ }
+
public Bot getBot()
{
return bot;
diff --git a/src/main/java/com/jagrosh/jmusicbot/gui/GUI.java b/src/main/java/com/jagrosh/jmusicbot/gui/GUI.java
index e3f47ae7a..6e8ecab2e 100644
--- a/src/main/java/com/jagrosh/jmusicbot/gui/GUI.java
+++ b/src/main/java/com/jagrosh/jmusicbot/gui/GUI.java
@@ -30,15 +30,18 @@
public class GUI extends JFrame
{
private final ConsolePanel console;
- private final Bot bot;
+ private Bot bot;
- public GUI(Bot bot)
+ public GUI()
{
super();
- this.bot = bot;
console = new ConsolePanel();
}
+ public void setBot(Bot bot) {
+ this.bot = bot;
+ }
+
public void init()
{
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
@@ -56,7 +59,8 @@ public void init()
{
try
{
- bot.shutdown();
+ if (bot != null)
+ bot.shutdown();
}
catch(Exception ex)
{
diff --git a/src/main/java/com/jagrosh/jmusicbot/utils/YoutubeOauth2TokenHandler.java b/src/main/java/com/jagrosh/jmusicbot/utils/YoutubeOauth2TokenHandler.java
new file mode 100644
index 000000000..3e1071702
--- /dev/null
+++ b/src/main/java/com/jagrosh/jmusicbot/utils/YoutubeOauth2TokenHandler.java
@@ -0,0 +1,91 @@
+package com.jagrosh.jmusicbot.utils;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.turbo.TurboFilter;
+import ch.qos.logback.core.spi.FilterReply;
+import org.slf4j.LoggerFactory;
+import org.slf4j.Marker;
+
+import java.nio.file.Files;
+
+/**
+ * A logback turbo filter, used retrieve the YouTube OAuth2 refresh token that gets logged once authorized with YouTube.
+ *
+ * @author Michaili K.
+ */
+public class YoutubeOauth2TokenHandler extends TurboFilter {
+ public final static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(YoutubeOauth2TokenHandler.class);
+ private Data data;
+
+
+ public void init()
+ {
+ LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
+ context.addTurboFilter(this);
+ }
+
+ public Data getData()
+ {
+ return data;
+ }
+
+ @Override
+ public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t)
+ {
+ if (!logger.getName().equals("dev.lavalink.youtube.http.YoutubeOauth2Handler"))
+ return FilterReply.NEUTRAL;
+
+ if (format.equals("OAUTH INTEGRATION: To give youtube-source access to your account, go to {} and enter code {}"))
+ {
+ this.data = new Data((String) params[0], (String) params[1]);
+ return FilterReply.NEUTRAL;
+ }
+ if (format.equals("OAUTH INTEGRATION: Token retrieved successfully. Store your refresh token as this can be reused. ({})"))
+ {
+ LOGGER.info(
+ "Authorization successful & retrieved token! Storing the token in {}",
+ OtherUtil.getPath("youtubetoken.txt").toAbsolutePath()
+ );
+
+ try
+ {
+ Files.write(OtherUtil.getPath("youtubetoken.txt"), params[0].toString().getBytes());
+ }
+ catch (Exception e)
+ {
+ LOGGER.error(
+ "Failed to write the YouTube OAuth2 refresh token to storage! You will need to authorize again on the next reboot",
+ e
+ );
+ }
+ return FilterReply.DENY;
+ }
+
+ return FilterReply.NEUTRAL;
+ }
+
+ public static class Data
+ {
+ private final String authorisationUrl;
+ private final String code;
+
+ private Data(String authorisationUrl, String code)
+ {
+ this.authorisationUrl = authorisationUrl;
+ this.code = code;
+ }
+
+ public String getCode()
+ {
+ return code;
+ }
+
+ public String getAuthorisationUrl()
+ {
+ return authorisationUrl;
+ }
+ }
+
+}
diff --git a/src/main/resources/reference.conf b/src/main/resources/reference.conf
index a4fda1e02..b5564f54e 100644
--- a/src/main/resources/reference.conf
+++ b/src/main/resources/reference.conf
@@ -48,6 +48,14 @@ game = "DEFAULT"
status = ONLINE
+// If you set this to true, the bot will use a Google account to play YouTube tracks.
+// This is only needed if you are experiencing issues with YouTube playback.
+// Once enabled, instructions for login will be written to the console and DMs.
+// DO NOT use your main Google account! Use an alternative account.
+
+youtubeoauth2=false
+
+
// If you set this to true, the bot will list the title of the song it is currently playing in its
// "Playing" status. Note that this will ONLY work if the bot is playing music on ONE guild;
// if the bot is playing on multiple guilds, this will not work.