Merge branch 'devel'

This commit is contained in:
Sebastien 2018-03-07 13:55:22 +01:00
commit 25513feb91
34 changed files with 1041 additions and 58 deletions

View File

@ -64,6 +64,10 @@ dependencies {
compile 'mysql:mysql-connector-java'
compile 'org.reflections:reflections:0.9.11'
compile 'org.apache.commons:commons-lang3:3.7'
compile 'com.google.api-client:google-api-client:1.23.0'
compile 'com.google.apis:google-api-services-youtube:v3-rev192-1.23.0'
compile 'com.google.oauth-client:google-oauth-client-java6:1.23.0'
compile 'com.google.oauth-client:google-oauth-client-jetty:1.23.0'
testCompile('org.springframework.boot:spring-boot-starter-test')
testCompile('com.jayway.jsonpath:json-path')

View File

@ -23,7 +23,7 @@ public class Music implements Commande {
public AudioM audio;
Logger logger = LogManager.getLogger();
public Music() {
audio = new AudioM(MainBot.jda.getGuilds().get(0));
audio = AudioM.getInstance(MainBot.jda.getGuilds().get(0));
}
@Override

View File

@ -32,7 +32,7 @@ public class Madame implements Commande{
String url = redirect.get("http://dites.bonjourmadame.fr/random");
logger.debug("URL: "+url);
if(scanPageForTipeee(url)){
if(scanPageForTipeee(url, logger)){
logger.debug("Advertisement detected! Retry! ("+url+")");
}
else{
@ -82,7 +82,7 @@ public class Madame implements Commande{
* @throws StringIndexOutOfBoundsException
* @throws IOException
*/
private boolean scanPageForTipeee(String url) throws StringIndexOutOfBoundsException, IOException{
public static boolean scanPageForTipeee(String url, Logger logger) throws StringIndexOutOfBoundsException, IOException{
String content = FindContentOnWebPage.getSourceUrl(url);
String imgClickLink = content.substring(content.indexOf("photo post"));
imgClickLink = imgClickLink.substring(imgClickLink.indexOf("<a"));

View File

@ -0,0 +1,42 @@
package net.Broken.Commands;
import net.Broken.Commande;
import net.Broken.audio.AudioM;
import net.Broken.audio.NotConnectedException;
import net.Broken.audio.NullMusicManager;
import net.Broken.audio.Youtube.YoutubeTools;
import net.dv8tion.jda.core.events.message.MessageReceivedEvent;
import java.io.IOException;
public class ytTest implements Commande {
@Override
public void action(String[] args, MessageReceivedEvent event) {
YoutubeTools yt = YoutubeTools.getInstance(null);
// try {
//// event.getTextChannel().sendMessage(yt.getRelatedVideo(args[0])).queue();
//// AudioM.getInstance(null).getGuildMusicManager().scheduler.autoPlay();
//
// } catch (NotConnectedException e) {
// e.printStackTrace();
// } catch (NullMusicManager nullMusicManager) {
// nullMusicManager.printStackTrace();
// }
}
@Override
public boolean isPrivateUsable() {
return false;
}
@Override
public boolean isAdminCmd() {
return false;
}
@Override
public boolean isNSFW() {
return false;
}
}

View File

@ -1,9 +1,11 @@
package net.Broken;
import net.Broken.RestApi.ApiCommandLoader;
import net.Broken.Tools.Command.CommandLoader;
import net.Broken.Tools.DayListener.DayListener;
import net.Broken.Tools.DayListener.Listeners.DailyMadame;
import net.Broken.Tools.DayListener.Listeners.ResetSpam;
import net.Broken.audio.Youtube.YoutubeTools;
import net.dv8tion.jda.core.AccountType;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.JDABuilder;
@ -16,6 +18,7 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.security.auth.login.LoginException;
import java.io.IOException;
import java.util.List;
@ -28,15 +31,13 @@ public class Init {
* @param dev dev Mode or not
* @return JDA object
*/
static JDA initBot(String token, boolean dev){
boolean okInit;
static JDA initJda(String token, boolean dev){
JDA jda = null;
logger.debug("-------------------INITIALISATION-------------------");
//Bot démarrer sans token
if (token == null) {
logger.fatal("Veuilliez indiquer le token du bot en argument...");
okInit=false;
}
else
{
@ -56,7 +57,6 @@ public class Init {
*************************************/
jda.getPresence().setGame(Game.of("Statut: Loading..."));
jda.getTextChannels().forEach(textChannel -> textChannel.sendTyping().queue());
CommandLoader.load();
//On recupere le l'id serveur
@ -94,9 +94,9 @@ public class Init {
dayListener.addListener(new DailyMadame());
dayListener.start();
logger.debug("-----------------FIN INITIALISATION-----------------");
jda.getPresence().setGame(Game.of("Statut: Ok!"));
}
catch (LoginException | InterruptedException | RateLimitedException e)
@ -107,4 +107,12 @@ public class Init {
return jda;
}
static void polish(JDA jda){
CommandLoader.load();
ApiCommandLoader.load();
jda.getPresence().setGame(Game.of("Statut: Ok!"));
}
}

View File

@ -6,6 +6,7 @@ import net.Broken.Tools.EmbedMessageUtils;
import net.Broken.Tools.MessageTimeOut;
import net.Broken.Tools.PrivateMessage;
import net.Broken.Tools.UserSpamUtils;
import net.Broken.audio.Youtube.YoutubeTools;
import net.dv8tion.jda.core.JDA;
import net.dv8tion.jda.core.Permission;
import net.dv8tion.jda.core.entities.ChannelType;
@ -20,6 +21,7 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.stereotype.Controller;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@ -36,6 +38,7 @@ public class MainBot {
public static boolean roleFlag = false;
public static HashMap<User, UserSpamUtils> spamUtils = new HashMap<>();
public static JDA jda;
public static boolean ready = false;
@ -69,7 +72,7 @@ public class MainBot {
i++;
}
jda = Init.initBot(token, dev);
jda = Init.initJda(token, dev);
ConfigurableApplicationContext ctx = SpringApplication.run(MainBot.class, args);
if(jda == null) {
System.exit(SpringApplication.exit(ctx, (ExitCodeGenerator) () -> {
@ -78,8 +81,14 @@ public class MainBot {
}));
}
try {
YoutubeTools.getInstance(jda.getGuilds().get(0)).getYouTubeService();
} catch (IOException e) {
e.printStackTrace();
}
ApiCommandLoader.load();
Init.polish(jda);
ready = true;
@ -95,8 +104,14 @@ public class MainBot {
*/
public static void handleCommand(CommandParser.CommandContainer cmd)
{
//On verifie que la commande existe
if(!ready)
{
return;
}
//On verifie que la commande existe
if (commandes.containsKey(cmd.commande))
{
Commande cmdObj = commandes.get(cmd.commande);
@ -145,6 +160,16 @@ public class MainBot {
else
{
MessageReceivedEvent event = cmd.event;
if(commandes.size() == 0){
if(event.isFromType(ChannelType.PRIVATE))
event.getPrivateChannel().sendMessage("Loading please wait...").queue();
else {
Message message = event.getTextChannel().sendMessage("Loading please wait...").complete();
new MessageTimeOut(messageTimeOut, message, event.getMessage());
}
}
else{
if(event.isFromType(ChannelType.PRIVATE))
event.getPrivateChannel().sendMessage(EmbedMessageUtils.getUnknowCommand()).queue();
else {
@ -154,6 +179,8 @@ public class MainBot {
logger.warn("Commande inconnue");
}
}
}

View File

@ -0,0 +1,30 @@
package net.Broken.RestApi.Commands;
import net.Broken.Commands.Music;
import net.Broken.RestApi.CommandInterface;
import net.Broken.RestApi.Data.CommandPostData;
import net.Broken.RestApi.Data.CommandResponseData;
import net.Broken.audio.AudioM;
import net.Broken.audio.NotConnectedException;
import net.Broken.audio.NullMusicManager;
import net.Broken.audio.TrackScheduler;
import net.dv8tion.jda.core.entities.User;
import org.apache.logging.log4j.LogManager;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
public class AutoFlowOff implements CommandInterface{
@Override
public ResponseEntity<CommandResponseData> action(Music musicCommande, CommandPostData data, User user) {
AudioM audioM = AudioM.getInstance(null);
try {
TrackScheduler scheduler = audioM.getGuildMusicManager().scheduler;
scheduler.setAutoFlow(false);
return new ResponseEntity<>(new CommandResponseData(data.command,"ok"), HttpStatus.OK);
} catch (NullMusicManager | NotConnectedException nullMusicManager) {
LogManager.getLogger().catching(nullMusicManager);
return new ResponseEntity<>(new CommandResponseData(data.command,"Not connected", "connect"), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}

View File

@ -0,0 +1,31 @@
package net.Broken.RestApi.Commands;
import net.Broken.Commands.Music;
import net.Broken.RestApi.CommandInterface;
import net.Broken.RestApi.Data.CommandPostData;
import net.Broken.RestApi.Data.CommandResponseData;
import net.Broken.audio.AudioM;
import net.Broken.audio.NotConnectedException;
import net.Broken.audio.NullMusicManager;
import net.Broken.audio.TrackScheduler;
import net.dv8tion.jda.core.entities.User;
import org.apache.logging.log4j.LogManager;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
public class AutoFlowOn implements CommandInterface{
@Override
public ResponseEntity<CommandResponseData> action(Music musicCommande, CommandPostData data, User user) {
AudioM audioM = AudioM.getInstance(null);
try {
TrackScheduler scheduler = audioM.getGuildMusicManager().scheduler;
scheduler.setAutoFlow(true);
return new ResponseEntity<>(new CommandResponseData(data.command,"ok"), HttpStatus.OK);
} catch (NullMusicManager | NotConnectedException nullMusicManager) {
LogManager.getLogger().catching(nullMusicManager);
return new ResponseEntity<>(new CommandResponseData(data.command,"Not connected", "connect"), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}

View File

@ -16,7 +16,7 @@ import org.springframework.http.ResponseEntity;
public class Connect implements CommandInterface{
@Override
public ResponseEntity<CommandResponseData> action(Music musicCommande, CommandPostData data, User user) {
AudioM audioM = musicCommande.getAudioManager();
AudioM audioM = AudioM.getInstance(null);
if(data.chanelId == null)
return new ResponseEntity<>(new CommandResponseData(data.command,"Missing chanelId"),HttpStatus.BAD_REQUEST);
VoiceChannel voiceChannel = null;

View File

@ -14,13 +14,15 @@ public class CurrentMusicData {
private final long currentPos;
private final String state;
private final boolean pause;
private final boolean autoflow;
public CurrentMusicData(UserAudioTrackData info, long currentPos, String state, boolean pause) {
public CurrentMusicData(UserAudioTrackData info, long currentPos, String state, boolean pause, boolean autoflow) {
this.info = info;
this.currentPos = currentPos;
this.state = state;
this.pause = pause;
this.autoflow = autoflow;
}
public UserAudioTrackData getInfo() {
@ -37,4 +39,8 @@ public class CurrentMusicData {
else
return state;
}
public boolean isAutoflow() {
return autoflow;
}
}

View File

@ -0,0 +1,23 @@
package net.Broken.RestApi;
import net.Broken.MainBot;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/")
public class GeneralApiController {
@RequestMapping(value = "/isReady", method = RequestMethod.GET)
public ResponseEntity<String> isReady(){
if(MainBot.ready){
return new ResponseEntity<>(HttpStatus.OK);
}
else
{
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
}
}

View File

@ -51,16 +51,16 @@ public class MusicWebAPIController {
AudioTrack currentTrack = player.getPlayingTrack();
if(currentTrack == null)
{
return new CurrentMusicData(null,0, "STOP",false);
return new CurrentMusicData(null,0, "STOP",false, musicCommande.audio.getGuildMusicManager().scheduler.isAutoFlow());
}
UserAudioTrackData uat = new UserAudioTrackData(musicCommande.audio.getGuildMusicManager().scheduler.getCurrentPlayingTrack());
return new CurrentMusicData(uat, currentTrack.getPosition(), currentTrack.getState().toString(), player.isPaused());
return new CurrentMusicData(uat, currentTrack.getPosition(), currentTrack.getState().toString(), player.isPaused(), musicCommande.audio.getGuildMusicManager().scheduler.isAutoFlow());
} catch (NullMusicManager | NotConnectedException nullMusicManager) {
return new CurrentMusicData(null,0, "STOP",false);
return new CurrentMusicData(null,0, "STOP",false, false);
}
}else
{
return new CurrentMusicData(null,0, "DISCONNECTED",false);
return new CurrentMusicData(null,0, "DISCONNECTED",false, false);
}
}

View File

@ -1,5 +1,6 @@
package net.Broken.Tools.DayListener.Listeners;
import net.Broken.Commands.Over18.Madame;
import net.Broken.MainBot;
import net.Broken.Tools.DayListener.NewDayListener;
import net.Broken.Tools.Redirection;
@ -24,8 +25,16 @@ public class DailyMadame implements NewDayListener{
while(!success && !error)
{
try {
chanel.sendMessage("Le Daily Madame mes petits cochons :kissing_heart:\n" + redirect.get("http://dites.bonjourmadame.fr/random")).queue();
String url = redirect.get("http://dites.bonjourmadame.fr/random");
logger.debug("URL: "+url);
if(Madame.scanPageForTipeee(url, logger)){
logger.debug("Advertisement detected! Retry! ("+url+")");
}
else{
chanel.sendMessage("Le Daily Madame mes petits cochons :kissing_heart:\n" + url).queue();
success=true;
}
} catch (IOException e) {
errorCp++;
logger.warn("Erreur de redirection. (Essais n°"+errorCp+")");

View File

@ -52,10 +52,19 @@ public class AudioM {
private Guild guild;
private Logger logger = LogManager.getLogger();
private static AudioM INSTANCE;
public static AudioM getInstance(Guild guild){
if(INSTANCE == null){
INSTANCE = new AudioM(guild);
}
return INSTANCE;
}
public AudioM(Guild guild) {
private AudioM(Guild guild) {
this.playerManager = new DefaultAudioPlayerManager();
AudioSourceManagers.registerRemoteSources(playerManager);
AudioSourceManagers.registerLocalSource(playerManager);
@ -80,11 +89,7 @@ public class AudioM {
logger.info("Single Track detected!");
UserAudioTrack uat = new UserAudioTrack(event.getAuthor(), track);
Message message = event.getTextChannel().sendMessage(EmbedMessageUtils.getMusicOk("Ajout de "+track.getInfo().title+" à la file d'attente!")).complete();
List<Message> messages = new ArrayList<Message>(){{
add(message);
add(event.getMessage());
}};
new MessageTimeOut(messages, MainBot.messageTimeOut).start();
new MessageTimeOut(MainBot.messageTimeOut, message, event.getMessage()).start();
play(guild, voiceChannel, musicManager, uat, onHead);
}
@ -95,11 +100,7 @@ public class AudioM {
AudioTrack firstTrack = playlist.getSelectedTrack();
Message message = event.getTextChannel().sendMessage(EmbedMessageUtils.getMusicOk("Ajout de "+firstTrack.getInfo().title+" et les 30 premiers titres à la file d'attente!")).complete();
List<Message> messages = new ArrayList<Message>(){{
add(message);
add(event.getMessage());
}};
new MessageTimeOut(messages, MainBot.messageTimeOut).start();
new MessageTimeOut(MainBot.messageTimeOut, message, event.getMessage()).start();
playListLoader(playlist, playlistLimit ,event.getAuthor() , onHead);
@ -132,6 +133,37 @@ public class AudioM {
});
}
public void loadAndPlayAuto(String trackUrl){
playerManager.loadItemOrdered(musicManager, trackUrl, new AudioLoadResultHandler(){
@Override
public void trackLoaded(AudioTrack track) {
logger.info("Auto add " + track.getInfo().title +" to playlist.");
UserAudioTrack userAudioTrack = new UserAudioTrack(MainBot.jda.getSelfUser(),track);
play(guild, playedChanel, musicManager, userAudioTrack, true);
}
@Override
public void playlistLoaded(AudioPlaylist playlist) {
AudioTrack track = playlist.getTracks().get(0);
logger.info("Auto add " + track.getInfo().title +" to playlist.");
UserAudioTrack userAudioTrack = new UserAudioTrack(MainBot.jda.getSelfUser(),track);
play(guild, playedChanel, musicManager, userAudioTrack, true);
}
@Override
public void noMatches() {
logger.warn("Track not found: "+trackUrl);
}
@Override
public void loadFailed(FriendlyException exception) {
logger.error("Cant load media!");
logger.error(exception.getMessage());
}
});
}
/**
* Load playlist to playlist
* @param playlist Loaded playlist
@ -320,6 +352,7 @@ public class AudioM {
public AudioPlayerManager getPlayerManager() {
return playerManager;
}
public VoiceChannel getPlayedChanel() {
return playedChanel;
}

View File

@ -1,11 +1,14 @@
package net.Broken.audio;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayer;
import com.sedmelluq.discord.lavaplayer.player.event.AudioEventAdapter;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.sedmelluq.discord.lavaplayer.track.AudioTrackEndReason;
import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo;
import net.Broken.MainBot;
import net.Broken.RestApi.Data.UserAudioTrackData;
import net.Broken.audio.Youtube.YoutubeTools;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -21,6 +24,8 @@ public class TrackScheduler extends AudioEventAdapter {
private final AudioPlayer player;
private final BlockingDeque<UserAudioTrack> queue;
private UserAudioTrack currentPlayingTrack;
private boolean autoFlow = false;
private ArrayList<String> history = new ArrayList<>();
Logger logger = LogManager.getLogger();
/**
@ -42,12 +47,23 @@ public class TrackScheduler extends AudioEventAdapter {
// Calling startTrack with the noInterrupt set to true will start the track only if nothing is currently playing. If
// something is playing, it returns false and does nothing. In that case the player was already playing so this
// track goes to the queue instead.
if(track.getSubmittedUser() != MainBot.jda.getSelfUser()){
logger.debug("Flush history");
history = new ArrayList<>();
}
history.add(track.getAudioTrack().getIdentifier());
if (!player.startTrack(track.getAudioTrack(), true)) {
queue.offer(track);
}
else{
currentPlayingTrack = track;
}
if(track.getSubmittedUser() != MainBot.jda.getSelfUser()) {
needAutoPlay();
}
}
/**
@ -58,14 +74,22 @@ public class TrackScheduler extends AudioEventAdapter {
// Calling startTrack with the noInterrupt set to true will start the track only if nothing is currently playing. If
// something is playing, it returns false and does nothing. In that case the player was already playing so this
// track goes to the queue instead.
if(track.getSubmittedUser() != MainBot.jda.getSelfUser()){
logger.debug("Flush history");
history = new ArrayList<>();
}
history.add(track.getAudioTrack().getIdentifier());
if (!player.startTrack(track.getAudioTrack(), true)) {
queue.addFirst(track);
}
else{
currentPlayingTrack = track;
}
if(track.getSubmittedUser() != MainBot.jda.getSelfUser()) {
needAutoPlay();
}
}
public void pause() {
player.setPaused(true);
@ -76,7 +100,6 @@ public class TrackScheduler extends AudioEventAdapter {
}
public void stop(){
player.stopTrack();
this.currentPlayingTrack = null;
@ -115,11 +138,13 @@ public class TrackScheduler extends AudioEventAdapter {
return false;
} else {
logger.info("Delete succeful");
needAutoPlay();
return true;
}
}
}
logger.info("Delete failure! Not found.");
return false;
}
@ -130,9 +155,11 @@ public class TrackScheduler extends AudioEventAdapter {
// Start the next track, regardless of if something is already playing or not. In case queue was empty, we are
// giving null to startTrack, which is a valid argument and will simply stop the player.
UserAudioTrack track = queue.poll();
if(track != null)
this.currentPlayingTrack = track;
if(track != null)
player.startTrack(track.getAudioTrack(), false);
needAutoPlay();
}
@Override
@ -140,8 +167,35 @@ public class TrackScheduler extends AudioEventAdapter {
// Only start the next track if the end reason is suitable for it (FINISHED or LOAD_FAILED)
if (endReason.mayStartNext) {
nextTrack();
needAutoPlay();
}
}
private void needAutoPlay(){
if((queue.size() < 1) && autoFlow && currentPlayingTrack != null){
logger.info("Auto add needed!");
AudioM audioM = AudioM.getInstance(null);
YoutubeTools youtubeTools = YoutubeTools.getInstance(null);
try {
String id = youtubeTools.getRelatedVideo(currentPlayingTrack.getAudioTrack().getInfo().identifier, history);
logger.info("Related id: "+id);
audioM.loadAndPlayAuto(id);
} catch (GoogleJsonResponseException e) {
logger.error("There was a service error: " + e.getDetails().getCode() + " : " + e.getDetails().getMessage());
} catch (Throwable t) {
logger.catching(t);
}
}
}
public void setAutoFlow(boolean autoFlow) {
this.autoFlow = autoFlow;
needAutoPlay();
}
public boolean isAutoFlow() {
return autoFlow;
}
}

View File

@ -31,7 +31,7 @@ public class WebLoadUtils {
AudioPlayerManager playerM = musicCommand.getAudioManager().getPlayerManager();
try {
AudioM audioM = musicCommand.getAudioManager();
AudioM audioM = AudioM.getInstance(null);
playerM.loadItemOrdered(musicCommand.getAudioManager().getGuildMusicManager(), data.url, new AudioLoadResultHandler() {
@Override
public void trackLoaded(AudioTrack track) {

View File

@ -0,0 +1,41 @@
package net.Broken.audio.Youtube;
import com.google.api.client.auth.oauth2.AuthorizationCodeFlow;
import com.google.api.client.auth.oauth2.AuthorizationCodeRequestUrl;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.java6.auth.oauth2.VerificationCodeReceiver;
import com.google.api.client.util.Preconditions;
import net.Broken.Tools.PrivateMessage;
import net.dv8tion.jda.core.entities.Guild;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
public class Authorization extends AuthorizationCodeInstalledApp {
private Guild guild;
private Logger logger = LogManager.getLogger();
/**
* @param flow authorization code flow
* @param receiver verification code receiver
*/
public Authorization(AuthorizationCodeFlow flow, VerificationCodeReceiver receiver, Guild guild) {
super(flow, receiver);
this.guild = guild;
}
@Override
protected void onAuthorization(AuthorizationCodeRequestUrl authorizationUrl) throws IOException {
notify(authorizationUrl.build());
}
protected void notify(String url){
Preconditions.checkNotNull(url);
logger.fatal("Please open this URL: "+url);
PrivateMessage.send(guild.getOwner().getUser(),"Please open this url to confirm google api account acces : " + url,null);
}
}

View File

@ -0,0 +1,54 @@
package net.Broken.audio.Youtube;
import com.google.api.client.extensions.java6.auth.oauth2.AbstractPromptReceiver;
import net.dv8tion.jda.core.entities.Guild;
import org.apache.logging.log4j.LogManager;
import java.io.IOException;
public class Receiver extends AbstractPromptReceiver {
private static Receiver INSTANCE;
private Guild guild;
private String code;
private Receiver(Guild guild){
this.guild = guild;
}
public static Receiver getInstance(Guild guild){
if(INSTANCE == null)
INSTANCE = new Receiver(guild);
return INSTANCE;
}
@Override
public String getRedirectUri() throws IOException {
return System.getenv("SITE_URL") + "/youtube/callback/";
}
@Override
public String waitForCode() {
if(System.getenv("SITE_URL").isEmpty()){
LogManager.getLogger().fatal("Please set \"SITE_URL\" environment variable and restart the bot!");
}
while(code == null){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return code;
}
@Override
public void stop() {
}
public void setCode(String code) {
this.code = code;
}
}

View File

@ -0,0 +1,143 @@
package net.Broken.audio.Youtube;
import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.youtube.YouTube;
import com.google.api.services.youtube.YouTubeScopes;
import com.google.api.services.youtube.model.SearchListResponse;
import com.google.api.services.youtube.model.SearchResult;
import net.dv8tion.jda.core.entities.Guild;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.*;
import java.util.*;
public class YoutubeTools {
/** Application name. */
private final String APPLICATION_NAME = "Discord Bot";
/** Directory to store user credentials for this application. */
private final File DATA_STORE_DIR = new File(".credentials/java-youtube-api");
private final File CLIENT_SECRET_DIR = new File(".credentials/client_secret.json");
/** Global instance of the {@link FileDataStoreFactory}. */
private FileDataStoreFactory DATA_STORE_FACTORY;
/** Global instance of the JSON factory. */
private final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
/** Global instance of the HTTP transport. */
private HttpTransport HTTP_TRANSPORT;
private Logger logger = LogManager.getLogger();
private Guild guild;
/** Global instance of the scopes required by this quickstart.
*
* If modifying these scopes, delete your previously saved credentials
* at ~/.credentials/drive-java-quickstart
*/
private final Collection<String> SCOPES = Arrays.asList(YouTubeScopes.YOUTUBEPARTNER, YouTubeScopes.YOUTUBE_FORCE_SSL);
private static YoutubeTools INSTANCE ;
private YoutubeTools(Guild guild){
try {
HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport();
DATA_STORE_FACTORY = new FileDataStoreFactory(DATA_STORE_DIR);
} catch (Throwable t) {
logger.catching(t);
}
this.guild = guild;
}
public static YoutubeTools getInstance(Guild guild){
if(INSTANCE == null)
INSTANCE = new YoutubeTools(guild);
return INSTANCE;
}
/**
* Creates an authorized Credential object.
* @return an authorized Credential object.
* @throws IOException
*/
private Credential authorize() throws IOException {
// Load client secrets.
InputStream in = new FileInputStream(CLIENT_SECRET_DIR);
GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader( in ));
// Build flow and trigger user authorization request.
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
.setDataStoreFactory(DATA_STORE_FACTORY)
.setAccessType("online")
.build();
Credential credential = new Authorization(flow, Receiver.getInstance(null), guild).authorize("user");
logger.debug("Credentials saved to " + DATA_STORE_DIR.getAbsolutePath());
return credential;
}
/**
* Build and return an authorized API client service, such as a YouTube
* Data API client service.
* @return an authorized API client service
* @throws IOException
*/
public YouTube getYouTubeService() throws IOException {
Credential credential = authorize();
return new YouTube.Builder(
HTTP_TRANSPORT, JSON_FACTORY, credential)
.setApplicationName(APPLICATION_NAME)
.build();
}
public String getRelatedVideo(String videoId, ArrayList<String> history) throws IOException, GoogleJsonResponseException, Throwable {
YouTube youtube = getYouTubeService();
HashMap<String, String> parameters = new HashMap<>();
parameters.put("part", "snippet");
parameters.put("relatedToVideoId", videoId);
parameters.put("type", "video");
YouTube.Search.List searchListRelatedVideosRequest = youtube.search().list(parameters.get("part"));
if (parameters.containsKey("relatedToVideoId") && parameters.get("relatedToVideoId") != "") {
searchListRelatedVideosRequest.setRelatedToVideoId(parameters.get("relatedToVideoId"));
}
if (parameters.containsKey("type") && !parameters.get("type").equals("")) {
searchListRelatedVideosRequest.setType(parameters.get("type"));
}
SearchListResponse response = searchListRelatedVideosRequest.execute();
for(SearchResult item : response.getItems()){
if(!history.contains(item.getId().getVideoId())){
return item.getId().getVideoId();
}
else
logger.debug("ID already on history");
}
logger.debug("All on history ?");
return response.getItems().get(0).getId().getVideoId();
}
}

View File

@ -0,0 +1,12 @@
package net.Broken.webView;
import net.Broken.MainBot;
public class CheckPage {
public static String getPageIfReady(String page){
if(MainBot.ready)
return page;
else
return "loading";
}
}

View File

@ -11,6 +11,11 @@ import org.springframework.web.bind.annotation.RequestMapping;
public class GeneralWebView {
@RequestMapping("/")
public String music(Model model){
return "index";
return CheckPage.getPageIfReady("index");
}
@RequestMapping("/loading")
public String loading(Model model){
return "loading";
}
}

View File

@ -11,6 +11,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
public class MusicWebView {
@RequestMapping("/music")
public String music(Model model){
return "music";
return CheckPage.getPageIfReady("music");
}
}

View File

@ -14,6 +14,6 @@ public class RegisterWebView {
@RequestMapping("/register")
public String music(@RequestParam(value="id", required = true, defaultValue = "") String id, Model model){
model.addAttribute("id", id);
return "register";
return CheckPage.getPageIfReady("register");
}
}

View File

@ -0,0 +1,24 @@
package net.Broken.webView;
import net.Broken.audio.Youtube.Receiver;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class YoutubeCallBack {
@RequestMapping("/youtube/callback")
public String callback(@RequestParam(value="error", required = false, defaultValue = "") String error,
@RequestParam(value = "code", required = false, defaultValue = "") String code,
Model model){
model.addAttribute("error", error);
model.addAttribute("code", code);
if(!code.equals("")){
Receiver.getInstance(null).setCode(code);
}
return "youtubeCallBack";
}
}

View File

@ -25,8 +25,12 @@
</File>
</Appenders>
<Loggers>
<Logger name="net.Broken" level="trace" additivity="false">
<AppenderRef ref="Console"/>
<AppenderRef ref="current"/>
<AppenderRef ref="RollingFile"/>
</Logger>
<Root level="debug">
<AppenderRef ref="RollingFile" level="info" />
<AppenderRef ref="Console" level="info"/>
<AppenderRef ref="RollingFile" level="info"/>
<AppenderRef ref="current" level="info"/>

View File

@ -0,0 +1,153 @@
<!DOCTYPE html>
<html >
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0"/>
<title>Music Control - Discord Bot</title>
<link rel="icon"
type="image/x-icon"
href="favicon.png"/>
<!-- CSS -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
<link href="css/materialize.css" type="text/css" rel="stylesheet" media="screen,projection"/>
<link href="css/style.css" type="text/css" rel="stylesheet" media="screen,projection"/>
</head>
<body class="blue-grey lighten-5" >
<!--__________________________________________________________-->
<!-- NAV BAR -->
<!-- AND -->
<!-- LOGIN -->
<!--__________________________________________________________-->
<nav class="blue-grey darken-4 z-depth-3" role="navigation">
<div class="nav-wrapper container">
<a id="logo-container" href="/" class="brand-logo">Discord Bot</a>
<ul class="right hide-on-med-and-down">
<li >
<a class="waves-effect waves-light sidenav-trigger" href="/" data-target="slide-out">Home</a>
</li>
<li>
<a class="waves-effect waves-light" href="/music" >Music Control</a>
</li>
<li id="nav-bar-account">
<a class="dropdown-account" data-activates="dropdown1"><i class="material-icons">account_box</i></a>
</li>
</ul>
<ul id="nav-mobile" class="side-nav">
<li class="active">
<a class="waves-effect waves-light sidenav-trigger" href="/" data-target="slide-out">Home</a>
</li>
<li>
<a class="waves-effect waves-light sidenav-trigger" href="/music" data-target="slide-out" >Music Control</a>
</li>
<!--TODO Connection mobile-->
</ul>
<a href="#" data-activates="nav-mobile" class="button-navbar-mobile button-collapse"><i class="material-icons">menu</i></a>
</div>
</nav>
<!-- Dropdown connected -->
<ul id="dropdown_connected" class="dropdown-content ">
<li>
<a class="center blue-grey-text text-darken-4" id="nav-name" style="text-decoration: underline; font-weight: bold"></a>
</li>
<li class="divider"></li>
<li>
<a class="center tooltipped" data-position="left" data-delay="50" data-tooltip="Under Development!">My Account</a>
</li>
<li>
<a class="center tooltipped" data-position="left" data-delay="50" data-tooltip="Under Development!">My Playlists</a>
</li>
<li class="divider"></li>
<li><a class="center red-text" id="nav-disconnect" style="font-weight: bold">Disconnect</a></li>
</ul>
<!--________________________________________-->
<!-- Connection modal -->
<!--________________________________________-->
<div id="modal_connection" class="modal">
<div class="modal-content">
<div class="row center">
<div class="col s12">
<h3 class="" style="font-weight: bold">Sign in</h3>
</div>
</div>
<div class="row center" style="margin-bottom: 0px">
<form name="login_form" id="login_form" action="javascript:void(0);" onsubmit="tryConnection()">
<div class="row" style="margin-bottom: 0px">
<div class="input-field col s6 offset-s3">
<i class="material-icons prefix">account_box</i>
<input name="username" id="user_input" type="text" class="validate"/>
<label for="user_input" data-error="User not registered!">User Name</label>
</div>
</div>
<div class="row">
<div class="input-field col s6 offset-s3">
<i class="material-icons prefix">security</i>
<input name="password" id="password_input" type="password" class="validate"/>
<label for="password_input" data-error="Wrong password!">Password</label>
</div>
</div>
<div class="row" style="margin-bottom: 10px">
<button id="btn-submit-connect" class="btn waves-effect waves-light light-green darken-1 scale-transition scale-out" type="submit" name="action" >
Submit<i class="material-icons right">send</i>
</button>
</div>
<div class="row">
<a class="btn waves-effect waves-light brown" href="/register">
Create account<i class="material-icons right">person_add</i>
</a>
</div>
</form>
</div>
</div>
</div>
<!--__________________________________________________________-->
<!-- -->
<!-- END -->
<!-- -->
<!--__________________________________________________________-->
<div class="section no-pad-bot main" id="index-banner">
<div class="center row">
<div class="row center valign-wrapper">
<div class="col s6">
<h1 class="red-text text-darken-2 "><b>Oops!</b></h1>
<h2 class=" blue-grey-text "><b>Page not found!</b></h2>
<a class="btn btn-large light-green darken-3 waves-effect" href="/">
<i class="large material-icons">home</i>
</a>
</div>
<div class="col s6">
<img class="" src="/img/404.gif" style="border:none;outline: none">
</div>
</div>
<div class="row">
</div>
</div>
</div>
<!-- Scripts-->
<script src="https://code.jquery.com/jquery-3.3.1.js"
integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60="
crossorigin="anonymous"></script>
<script src="/js/materialize.js"></script>
<script src="/js/navabar.js"></script>
<script src="/js/js.cookie.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 MiB

View File

@ -0,0 +1,18 @@
$(document).ready(function () {
setInterval("loop()",1000);
});
function loop() {
$.ajax({
type: "GET",
url: "/api/isReady",
success: function (data) {
console.log("Ready");
debugger;
location.reload();
}
}).fail(function (data) {
console.log("Not ready");
});
}

View File

@ -10,7 +10,8 @@ var btn_info;
var btn_disconnect;
var btn_flush;
var btn_add;
var switchAutoFlow;
var loadingFlag = false;
$(document).ready(function() {
@ -21,7 +22,7 @@ $(document).ready(function() {
btn_disconnect = $('#btn_disconnect');
btn_flush = $('#flush_btn');
btn_add = $('#add_btn');
switchAutoFlow = $("#autoflow");
setInterval("getCurentMusic()",1000);
$('#modalAdd').modal();
@ -160,7 +161,10 @@ function getCurentMusic() {
break;
}
if(switchAutoFlow.is(':checked') != data.autoflow)
switchAutoFlow.prop('checked', data.autoflow);
getPlayList();
})
.fail(function (data) {
if(!error){
@ -177,7 +181,7 @@ function getPlayList() {
data = data.list;
if(data != null && data.length != 0){
var noUpdate = comparePlaylist(data, savedPlaylist);
// console.log("List up to date : "+noUpdate);
if(!noUpdate){
savedPlaylist = data;
$('#playlist_list').empty();
@ -208,13 +212,25 @@ function getPlayList() {
});
}
}
else
else{
$('#playlist_list').empty();
savedPlaylist = {};
}
if(loadingFlag){
modal_loading.modal('close');
loadingFlag = false;
}
}).fail(function (data) {
if(!error){
alert("Comunication error, please refresh.");
error = true;
}
if(loadingFlag){
modal_loading.modal('close');
loadingFlag = false;
}
});
@ -249,7 +265,7 @@ function getChannels(){
function updateModal(data){
$('#modal_title').text("Title: "+ data.info.audioTrackInfo.title);
$('#modal_author').text("Author: "+ data.info.author);
$('#modal_author').text("Author: "+ data.info.audioTrackInfo.author);
$('#modal_lenght').text("Duration: "+ msToTime(data.info.audioTrackInfo.length));
$('#modal_url').text("URL: "+ data.info.audioTrackInfo.uri);
$('#modal_submit').text("Submitted by: "+ data.info.user);
@ -307,8 +323,9 @@ function sendCommand(command){
data: JSON.stringify(command),
success: function (data) {
console.log(data);
loadingFlag = true;
getCurentMusic();
modal_loading.modal('close');
}
}).fail(function (data) {
@ -437,6 +454,15 @@ function listeners() {
$('#btn_disconnect').click(function () {
sendCommand({command : "DISCONNECT"})
});
switchAutoFlow.click(function () {
console.log(switchAutoFlow.is(':checked'))
if(switchAutoFlow.is(':checked')){
sendCommand({command: 'AUTOFLOWON'})
}
else
sendCommand({command: 'AUTOFLOWOFF'})
});
}
function disableBtn(btn) {

View File

@ -124,8 +124,9 @@
</div>
<!-- Scripts-->
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.js"
integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60="
crossorigin="anonymous"></script>
<script th:src="@{/js/materialize.js}"></script>
<script th:src="@{/js/navabar.js}"></script>
<script th:src="@{/js/js.cookie.js}"></script>

View File

@ -0,0 +1,79 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0"/>
<title>Music Control - Discord Bot</title>
<link rel="icon"
type="image/x-icon"
href="/favicon.png"/>
<!-- CSS -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
<link href="css/materialize.css" type="text/css" rel="stylesheet" media="screen,projection"/>
<link href="css/style.css" type="text/css" rel="stylesheet" media="screen,projection"/>
</head>
<body class="blue-grey lighten-5" >
<!--__________________________________________________________-->
<!-- NAV BAR -->
<!-- AND -->
<!-- LOGIN -->
<!--__________________________________________________________-->
<nav class="blue-grey darken-4 z-depth-3" role="navigation">
<div class="nav-wrapper container">
<a id="logo-container" href="/" class="brand-logo">Discord Bot</a>
<ul class="right hide-on-med-and-down">
</ul>
<ul id="nav-mobile" class="side-nav">
</ul>
<a href="#" data-activates="nav-mobile" class="button-navbar-mobile button-collapse"><i class="material-icons">menu</i></a>
</div>
</nav>
<!--__________________________________________________________-->
<!-- -->
<!-- END -->
<!-- -->
<!--__________________________________________________________-->
<div class="section no-pad-bot main" id="index-banner">
<div class="row center center-align">
<h3 class="">Bot is starting</h3>
</div>
<div class="row center" >
<div class="preloader-wrapper big active">
<div class="spinner-layer spinner-blue-only">
<div class="circle-clipper left">
<div class="circle"></div>
</div>
<div class="gap-patch">
<div class="circle"></div>
</div>
<div class="circle-clipper right">
<div class="circle"></div>
</div>
</div>
</div>
</div>
<div class="row center center-align">
<h3>Please wait</h3>
</div>
</div>
<!-- Scripts-->
<script src="https://code.jquery.com/jquery-3.3.1.js"
integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60="
crossorigin="anonymous"></script>
<script th:src="@{/js/materialize.js}"></script>
<script th:src="@{/js/loading.js}"></script>
<script th:src="@{/js/js.cookie.js}"></script>
</body>
</html>

View File

@ -174,11 +174,11 @@
<tr>
<th style="padding: 0px;">
<div class="row center valign-wrapper" style="margin: 0px">
<div class="col s4 center"><h5>Playlist</h5></div>
<div class="col s4 center">
<div class="col s3 center blue-grey-text text-darken-3"><h5><b>Playlist</b></h5></div>
<div class="col s3 center">
<a class="waves-effect waves-light btn modal-trigger red darken-4" id="flush_btn"><i class="material-icons">delete_sweep</i></a>
</div>
<div class="col s4 center">
<div class="col s3 center" style="padding-right: 0px">
<!-- Modal Trigger -->
<a class="waves-effect waves-light btn modal-trigger green darken-4" id="add_btn" href="#modalAdd"><i class="material-icons">add_circle_outline</i></a>
@ -215,6 +215,17 @@
</div>
</div>
</div>
<div class="col s3 center " style="padding-left: 0px">
<div class="row switch blue-grey-text text-darken-3" style="margin-bottom: 0px">
AutoFlow
</div>
<div class="row switch tooltipped" data-position="bottom" data-delay="50" data-tooltip="Experimental!">
<label>
<input type="checkbox" id="autoflow"/>
<span class="lever"></span>
</label>
</div>
</div>
</div>
</th>
@ -360,7 +371,9 @@
</p>
<!-- Scripts-->
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.js"
integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60="
crossorigin="anonymous"></script>
<script>
var needLogin = true;

View File

@ -191,7 +191,9 @@
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.js"
integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60="
crossorigin="anonymous"></script>
<script th:src="@{/js/materialize.js}"></script>
<script th:src="@{/js/register.js}"></script>
<script th:src="@{/js/navabar.js}"></script>

View File

@ -0,0 +1,141 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0"/>
<title>Music Control - Discord Bot</title>
<link rel="icon"
type="image/x-icon"
href="/favicon.png"/>
<!-- CSS -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"/>
<link href="/css/materialize.css" type="text/css" rel="stylesheet" media="screen,projection"/>
<link href="/css/style.css" type="text/css" rel="stylesheet" media="screen,projection"/>
</head>
<body class="blue-grey lighten-5" >
<!--__________________________________________________________-->
<!-- NAV BAR -->
<!-- AND -->
<!-- LOGIN -->
<!--__________________________________________________________-->
<nav class="blue-grey darken-4 z-depth-3" role="navigation">
<div class="nav-wrapper container">
<a id="logo-container" href="/" class="brand-logo">Discord Bot</a>
<ul class="right hide-on-med-and-down">
<li >
<a class="waves-effect waves-light sidenav-trigger" href="/" data-target="slide-out">Home</a>
</li>
<li>
<a class="waves-effect waves-light" href="/music" >Music Control</a>
</li>
<li id="nav-bar-account">
<a class="dropdown-account" data-activates="dropdown1"><i class="material-icons">account_box</i></a>
</li>
</ul>
<ul id="nav-mobile" class="side-nav">
<li class="active">
<a class="waves-effect waves-light sidenav-trigger" href="/" data-target="slide-out">Home</a>
</li>
<li>
<a class="waves-effect waves-light sidenav-trigger" href="/music" data-target="slide-out" >Music Control</a>
</li>
<!--TODO Connection mobile-->
</ul>
<a href="#" data-activates="nav-mobile" class="button-navbar-mobile button-collapse"><i class="material-icons">menu</i></a>
</div>
</nav>
<!-- Dropdown connected -->
<ul id="dropdown_connected" class="dropdown-content ">
<li>
<a class="center blue-grey-text text-darken-4" id="nav-name" style="text-decoration: underline; font-weight: bold"></a>
</li>
<li class="divider"></li>
<li>
<a class="center tooltipped" data-position="left" data-delay="50" data-tooltip="Under Development!">My Account</a>
</li>
<li>
<a class="center tooltipped" data-position="left" data-delay="50" data-tooltip="Under Development!">My Playlists</a>
</li>
<li class="divider"></li>
<li><a class="center red-text" id="nav-disconnect" style="font-weight: bold">Disconnect</a></li>
</ul>
<!--________________________________________-->
<!-- Connection modal -->
<!--________________________________________-->
<div id="modal_connection" class="modal">
<div class="modal-content">
<div class="row center">
<div class="col s12">
<h3 class="" style="font-weight: bold">Sign in</h3>
</div>
</div>
<div class="row center" style="margin-bottom: 0px">
<form name="login_form" id="login_form" action="javascript:void(0);" onsubmit="tryConnection()">
<div class="row" style="margin-bottom: 0px">
<div class="input-field col s6 offset-s3">
<i class="material-icons prefix">account_box</i>
<input name="username" id="user_input" type="text" class="validate"/>
<label for="user_input" data-error="User not registered!">User Name</label>
</div>
</div>
<div class="row">
<div class="input-field col s6 offset-s3">
<i class="material-icons prefix">security</i>
<input name="password" id="password_input" type="password" class="validate"/>
<label for="password_input" data-error="Wrong password!">Password</label>
</div>
</div>
<div class="row" style="margin-bottom: 10px">
<button id="btn-submit-connect" class="btn waves-effect waves-light light-green darken-1 scale-transition scale-out" type="submit" name="action" >
Submit<i class="material-icons right">send</i>
</button>
</div>
<div class="row">
<a class="btn waves-effect waves-light brown" href="/register">
Create account<i class="material-icons right">person_add</i>
</a>
</div>
</form>
</div>
</div>
</div>
<!--__________________________________________________________-->
<!-- -->
<!-- END -->
<!-- -->
<!--__________________________________________________________-->
<div class="section no-pad-bot main" id="index-banner">
<div class="center row">
<div class="col s12">
<h2 th:text="Succes" th:if="!${#strings.isEmpty(code)}"></h2>
<h2 th:text="${error}" th:if="${#strings.isEmpty(code)}"></h2>
</div>
</div>
</div>
<!-- Scripts-->
<script src="https://code.jquery.com/jquery-3.3.1.js"
integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60="
crossorigin="anonymous"></script>
<script th:src="@{/js/materialize.js}"></script>
<script th:src="@{/js/navabar.js}"></script>
<script th:src="@{/js/js.cookie.js}"></script>
</body>
</html>