From b3bfa5d8b95f525aac13415a566a9dac3475324e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20Gla=C3=9F?= Date: Sat, 27 Mar 2021 19:08:44 +0100 Subject: [PATCH] Added Sauce Command --- .../yoshibot/audio/AudioController.java | 2 +- .../audio/AudioControllerManager.java | 5 +- .../yoshibot/command/YoshiCommandContext.java | 246 ++++++++++-------- .../command/YoshiCommandDistributor.java | 108 ++++---- .../command/commands/JokeCommand.java | 74 +++--- .../command/commands/PlayCommand.java | 8 +- .../command/commands/SauceCommand.java | 48 ++++ .../command/commands/StopCommand.java | 8 +- .../yoshibot/listeners/CommandLine.java | 2 +- .../yoshibot/listeners/CommandListener.java | 28 +- .../yoshibot/main/Resources.java | 100 ++++--- .../yoshibot/main/SauceProvider.java | 147 +++++++++++ .../yoshibot/main/YoshiBot.java | 5 +- .../yoshibot/utils/RestHelper.java | 38 +++ gradlew | 0 rsc/Ordnerstruktur.txt | 18 +- 16 files changed, 566 insertions(+), 271 deletions(-) create mode 100644 app/src/main/java/de/yannicpunktdee/yoshibot/command/commands/SauceCommand.java create mode 100644 app/src/main/java/de/yannicpunktdee/yoshibot/main/SauceProvider.java create mode 100644 app/src/main/java/de/yannicpunktdee/yoshibot/utils/RestHelper.java mode change 100644 => 100755 gradlew diff --git a/app/src/main/java/de/yannicpunktdee/yoshibot/audio/AudioController.java b/app/src/main/java/de/yannicpunktdee/yoshibot/audio/AudioController.java index f7bbdda..fd54724 100644 --- a/app/src/main/java/de/yannicpunktdee/yoshibot/audio/AudioController.java +++ b/app/src/main/java/de/yannicpunktdee/yoshibot/audio/AudioController.java @@ -13,7 +13,7 @@ public class AudioController { public AudioController(Guild guild) { this.guild = guild; - this.audioPlayer = YoshiBot.audioPlayerManager.createPlayer(); + this.audioPlayer = YoshiBot.getInstance().audioPlayerManager.createPlayer(); audioPlayer.addListener(new AudioPlayerListener(guild.getAudioManager())); this.guild.getAudioManager().setSendingHandler(new AudioSendHandlerImpl(audioPlayer)); diff --git a/app/src/main/java/de/yannicpunktdee/yoshibot/audio/AudioControllerManager.java b/app/src/main/java/de/yannicpunktdee/yoshibot/audio/AudioControllerManager.java index 53cd6cb..095ab58 100644 --- a/app/src/main/java/de/yannicpunktdee/yoshibot/audio/AudioControllerManager.java +++ b/app/src/main/java/de/yannicpunktdee/yoshibot/audio/AudioControllerManager.java @@ -1,6 +1,7 @@ package de.yannicpunktdee.yoshibot.audio; import java.util.Map; +import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import de.yannicpunktdee.yoshibot.main.YoshiBot; @@ -16,10 +17,10 @@ public class AudioControllerManager { public AudioController getController(long guildId) { AudioController ac = null; - if(audioController.containsKey(guildId)) + if (audioController.containsKey(guildId)) ac = audioController.get(guildId); else { - ac = new AudioController(YoshiBot.jda.getGuildById(guildId)); + ac = new AudioController(Objects.requireNonNull(YoshiBot.getInstance().jda.getGuildById(guildId))); audioController.put(guildId, ac); } return ac; diff --git a/app/src/main/java/de/yannicpunktdee/yoshibot/command/YoshiCommandContext.java b/app/src/main/java/de/yannicpunktdee/yoshibot/command/YoshiCommandContext.java index f486b47..89d7bf1 100644 --- a/app/src/main/java/de/yannicpunktdee/yoshibot/command/YoshiCommandContext.java +++ b/app/src/main/java/de/yannicpunktdee/yoshibot/command/YoshiCommandContext.java @@ -1,9 +1,10 @@ package de.yannicpunktdee.yoshibot.command; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import de.yannicpunktdee.yoshibot.command.YoshiCommandDistributor.Action; +import de.yannicpunktdee.yoshibot.command.commands.SauceCommand; +import de.yannicpunktdee.yoshibot.main.SauceProvider; import de.yannicpunktdee.yoshibot.main.YoshiBot; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.TextChannel; @@ -12,21 +13,22 @@ import net.dv8tion.jda.api.entities.VoiceChannel; import net.dv8tion.jda.api.events.message.MessageReceivedEvent;; /** - * Parst einen Eingabestring, entscheidet ob er ein Kommando ist und zerlegt ihn in seine Bestandteile. - * Kommandos besitzen einen Status, der Auskunft gibt, ob Fehler beim Parsen des Eingabestrings aufgetreten - * sind oder der String gar kein Kommando war. Außerdem wird bei erfolgreich geparsten Kommandos eine Aktion - * festgelegt, die das Kommando ausführen soll, sowie die Argumente mit welcher das Kommando ausgeführt wird. - * Außerdem enthalten ist eine Referenz auf Ursprungs-User und -TextChannel. + * Parst einen Eingabestring, entscheidet ob er ein Kommando ist und zerlegt ihn in seine Bestandteile. Kommandos + * besitzen einen Status, der Auskunft gibt, ob Fehler beim Parsen des Eingabestrings aufgetreten sind oder der String + * gar kein Kommando war. Außerdem wird bei erfolgreich geparsten Kommandos eine Aktion festgelegt, die das Kommando + * ausführen soll, sowie die Argumente mit welcher das Kommando ausgeführt wird. Außerdem enthalten ist eine Referenz + * auf Ursprungs-User und -TextChannel. + * * @author Yannic Link */ public class YoshiCommandContext { /** - * Repräsentiert einen Status, den eine YoshiCommand-Instanz hat, nachdem der Eingabestring eingelesen - * und geparst wurde. Sofern der Status nicht OK ist, ist diese Instanz entweder kein Kommando oder - * besitzt eine fehlerhafte Syntax. + * Repräsentiert einen Status, den eine YoshiCommand-Instanz hat, nachdem der Eingabestring eingelesen und geparst + * wurde. Sofern der Status nicht OK ist, ist diese Instanz entweder kein Kommando oder besitzt eine fehlerhafte + * Syntax. */ - public static enum State { + public enum State { /** * Das Kommando wurde erfolgreich geparst und besitzt keine Syntaxfehler. */ @@ -48,10 +50,11 @@ public class YoshiCommandContext { */ BAD_SYNTAX } + /** * Hilfskonstrukt. Beschreibt den Zustand vom Parser. */ - private static enum ReadingState{ + private enum ReadingState { /** * Der Parser ist dabei festzustellen, ob es sich um ein Kommando handelt. */ @@ -84,7 +87,9 @@ public class YoshiCommandContext { * Der Parser liest gerade einen zusammenhängenden Argumentenwert ein. */ READING_STRING - }; + } + + ; /** @@ -105,133 +110,138 @@ public class YoshiCommandContext { */ private Map arguments; + private List argumentList; + private MessageReceivedEvent event; /** - * Erzeugt aus einem unbearbeiteten Eingabestring ein Kommando. Nach dem parsen enthält die state-Variable - * den Endzustand des Parsers. + * Erzeugt aus einem unbearbeiteten Eingabestring ein Kommando. Nach dem parsen enthält die state-Variable den + * Endzustand des Parsers. + * * @param argumentsString Ein unbearbeiteter Eingabestring - * @param user Der Benutzer, der das Kommando geschickt hat. - * @param channel Der Textchannel in den das Kommando geschickt wurde. */ public YoshiCommandContext(String argumentsString, MessageReceivedEvent event) { this.event = event; argumentsString = argumentsString.trim(); - arguments = new HashMap(); + argumentList = new ArrayList<>(); + argumentList.addAll(Arrays.asList(argumentsString.split(" "))); + argumentList.remove(0); + + arguments = new HashMap<>(); ReadingState readingState = ReadingState.VERIFYING; String currentKey = null; int startPos = 0; int length = argumentsString.length(); - for(int position = 0; position < length; position++) { + for (int position = 0; position < length; position++) { char currentChar = argumentsString.charAt(position); - switch(readingState) { + switch (readingState) { + case VERIFYING: + if (!Character.isWhitespace(currentChar)) continue; + if (!argumentsString.substring(0, position).equals(PREFIX)) { + state = State.NO_COMMAND; + return; + } + readingState = ReadingState.AFTER_VERIFY; + continue; + case AFTER_VERIFY: + if (Character.isWhitespace(currentChar)) continue; + if (currentChar == '-') { + state = State.NO_ACTION; + return; + } + startPos = position; + readingState = ReadingState.READING_ACTION; + continue; + case READING_ACTION: + if (!Character.isWhitespace(currentChar)) continue; + try { + action = Action.valueOf(argumentsString.substring(startPos, position).toUpperCase()); + readingState = ReadingState.INTERMEDIATE; + } catch (IllegalArgumentException e) { + state = State.UNKNOWN_ACTION; + return; + } + continue; + case INTERMEDIATE: + if (Character.isWhitespace(currentChar)) continue; + if ((currentChar != '-' || currentChar == '"') && action != Action.SAUCE) { + state = State.BAD_SYNTAX; + return; + } + startPos = position + 1; + readingState = ReadingState.READING_KEY; + continue; + case READING_KEY: + if (!Character.isWhitespace(currentChar)) continue; + currentKey = argumentsString.substring(startPos, position); + readingState = ReadingState.AFTER_KEY; + continue; + case AFTER_KEY: + if (Character.isWhitespace(currentChar)) continue; + if (currentChar == '-') { + arguments.put(currentKey, null); + startPos = position + 1; + readingState = ReadingState.READING_KEY; + } else if (currentChar == '"') { + startPos = position + 1; + readingState = ReadingState.READING_STRING; + } else { + startPos = position; + readingState = ReadingState.READING_VALUE; + } + continue; + case READING_VALUE: + if (!Character.isWhitespace(currentChar)) continue; + arguments.put(currentKey, argumentsString.substring(startPos, position)); + readingState = ReadingState.INTERMEDIATE; + continue; + case READING_STRING: + if (currentChar != '"') continue; + if (argumentsString.charAt(position - 1) == '\\') continue; + arguments.put(currentKey, argumentsString.substring(startPos, position)); + readingState = ReadingState.INTERMEDIATE; + continue; + } + } + + switch (readingState) { + case INTERMEDIATE: + state = State.OK; + return; case VERIFYING: - if(!Character.isWhitespace(currentChar)) continue; - if(!argumentsString.substring(0, position).equals(PREFIX)) { + if (argumentsString.equals(PREFIX)) { + action = Action.HELP; + state = State.OK; + } else { state = State.NO_COMMAND; - return; } - readingState = ReadingState.AFTER_VERIFY; - continue; - case AFTER_VERIFY: - if(Character.isWhitespace(currentChar)) continue; - if(currentChar == '-'){ - state = State.NO_ACTION; - return; - } - startPos = position; - readingState = ReadingState.READING_ACTION; - continue; + return; case READING_ACTION: - if(!Character.isWhitespace(currentChar)) continue; - try{ - action = Action.valueOf(argumentsString.substring(startPos, position).toUpperCase()); + try { + action = Action.valueOf(argumentsString.substring(startPos).toUpperCase()); readingState = ReadingState.INTERMEDIATE; - } catch(IllegalArgumentException e) { + } catch (IllegalArgumentException e) { state = State.UNKNOWN_ACTION; return; } - continue; - case INTERMEDIATE: - if(Character.isWhitespace(currentChar)) continue; - if(currentChar != '-' || currentChar == '"') { - state = State.BAD_SYNTAX; - return; - } - startPos = position + 1; - readingState = ReadingState.READING_KEY; - continue; + state = State.OK; + return; case READING_KEY: - if(!Character.isWhitespace(currentChar)) continue; - currentKey = argumentsString.substring(startPos, position); - readingState = ReadingState.AFTER_KEY; - continue; - case AFTER_KEY: - if(Character.isWhitespace(currentChar)) continue; - if(currentChar == '-') { - arguments.put(currentKey, null); - startPos = position + 1; - readingState = ReadingState.READING_KEY; - } else if(currentChar == '"'){ - startPos = position + 1; - readingState = ReadingState.READING_STRING; - } else { - startPos = position; - readingState = ReadingState.READING_VALUE; - } - continue; + arguments.put(argumentsString.substring(startPos), null); + state = State.OK; + return; case READING_VALUE: - if(!Character.isWhitespace(currentChar)) continue; - arguments.put(currentKey, argumentsString.substring(startPos, position)); - readingState = ReadingState.INTERMEDIATE; - continue; - case READING_STRING: - if(currentChar != '"') continue; - if(argumentsString.charAt(position - 1) == '\\') continue; - arguments.put(currentKey, argumentsString.substring(startPos, position)); - readingState = ReadingState.INTERMEDIATE; - continue; - } - } - - switch(readingState) { - case INTERMEDIATE: - state = State.OK; - return; - case VERIFYING: - if(argumentsString.equals(PREFIX)) { - action = Action.HELP; + arguments.put(currentKey, argumentsString.substring(startPos)); state = State.OK; - }else { - state = State.NO_COMMAND; - } - return; - case READING_ACTION: - try{ - action = Action.valueOf(argumentsString.substring(startPos).toUpperCase()); - readingState = ReadingState.INTERMEDIATE; - } catch(IllegalArgumentException e) { - state = State.UNKNOWN_ACTION; return; - } - state = State.OK; - return; - case READING_KEY: - arguments.put(argumentsString.substring(startPos), null); - state = State.OK; - return; - case READING_VALUE: - arguments.put(currentKey, argumentsString.substring(startPos)); - state = State.OK; - return; - default: - break; + default: + break; } } @@ -248,41 +258,51 @@ public class YoshiCommandContext { public State getState() { return state; } + /** * Gibt die im Kommando spezifizierte Aktion zurück. null, wenn status fehlerhaft oder kein Kommando. */ public Action getAction() { return action; } + /** * Prüft, ob das Kommando mit Argumenten aufgerufen wurde. */ public boolean hasArguments() { return !arguments.isEmpty(); } + /** * Prüft, ob alle Key-Werte in der Argumentenliste vorhanden sind. + * * @param args Liste von den auf Existenz zu überprüfenden Argumenten. */ public boolean containsArguments(String[] args) { - for(String arg : args) { - if(!arguments.containsKey(arg)) { + for (String arg : args) { + if (!arguments.containsKey(arg)) { return false; } } return true; } + /** * Gibt den Wert eines Arguments zurück. + * * @param arg Name des Arguments. */ public String getArgument(String arg) { - if(!arguments.containsKey(arg)) return null; + if (!arguments.containsKey(arg)) return null; return arguments.get(arg); } public MessageReceivedEvent getEvent() { return event; } - + + public List getArguments() { + return this.argumentList; + } + } diff --git a/app/src/main/java/de/yannicpunktdee/yoshibot/command/YoshiCommandDistributor.java b/app/src/main/java/de/yannicpunktdee/yoshibot/command/YoshiCommandDistributor.java index ee3b448..f3576a4 100644 --- a/app/src/main/java/de/yannicpunktdee/yoshibot/command/YoshiCommandDistributor.java +++ b/app/src/main/java/de/yannicpunktdee/yoshibot/command/YoshiCommandDistributor.java @@ -3,64 +3,73 @@ package de.yannicpunktdee.yoshibot.command; import de.yannicpunktdee.yoshibot.command.commands.*; /** - * Unterscheidet nach der spezifizierten Action welche YoshiCommand-Kindklasse zum Ausführen des Kommandos - * verwendet wird. + * Unterscheidet nach der spezifizierten Action welche YoshiCommand-Kindklasse zum Ausführen des Kommandos verwendet + * wird. + * * @author Yannic Link */ public class YoshiCommandDistributor { /** * Führt das jeweils zuständige Kommando aus. + * * @param context */ public static void distribute(YoshiCommandContext context) { - switch(context.getState()) { - case NO_ACTION: - context.getEvent().getTextChannel().sendMessage("Im letzten Befehl wurde keine Aktion spezifiziert. Führe \"" - + YoshiCommandContext.PREFIX + " help\" für Hilfe aus.").queue(); - return; - case UNKNOWN_ACTION: - context.getEvent().getTextChannel().sendMessage("Im letzten Befehl wurde eine unbekannte Aktion angegeben. Führe \"" - + YoshiCommandContext.PREFIX + " help\" für Hilfe aus.").queue(); - return; - case BAD_SYNTAX: - context.getEvent().getTextChannel().sendMessage("Der letzte Befehl hatte ein falsches Format. Führe \"" - + YoshiCommandContext.PREFIX + " help\" für Hilfe aus.").queue(); - return; - default: - break; + switch (context.getState()) { + case NO_ACTION: + context.getEvent().getTextChannel() + .sendMessage("Im letzten Befehl wurde keine Aktion spezifiziert. Führe \"" + + YoshiCommandContext.PREFIX + " help\" für Hilfe aus.").queue(); + return; + case UNKNOWN_ACTION: + context.getEvent().getTextChannel() + .sendMessage("Im letzten Befehl wurde eine unbekannte Aktion angegeben. Führe \"" + + YoshiCommandContext.PREFIX + " help\" für Hilfe aus.").queue(); + return; + case BAD_SYNTAX: + context.getEvent().getTextChannel().sendMessage("Der letzte Befehl hatte ein falsches Format. Führe \"" + + YoshiCommandContext.PREFIX + " help\" für Hilfe aus.") + .queue(); + return; + default: + break; } YoshiCommand command = null; - switch(context.getAction()) { - case STOP: - command = new StopCommand(context); - break; - case HELP: - command = new HelpCommand(context); - break; - case JOKE: - command = new JokeCommand(context); - break; - case SAY: - command = new SayCommand(context); - break; - case LIST: - command = new ListCommand(context); - break; - case PLAY: - command = new PlayCommand(context); - break; - default: - context.getEvent().getTextChannel().sendMessage("Dieses Kommando existiert noch nicht.").queue(); - break; + switch (context.getAction()) { + case STOP: + command = new StopCommand(context); + break; + case HELP: + command = new HelpCommand(context); + break; + case JOKE: + command = new JokeCommand(context); + break; + case SAY: + command = new SayCommand(context); + break; + case LIST: + command = new ListCommand(context); + break; + case PLAY: + command = new PlayCommand(context); + break; + case SAUCE: + command = new SauceCommand(context); + break; + default: + context.getEvent().getTextChannel().sendMessage("Dieses Kommando existiert noch nicht.").queue(); + break; } - if(command != null) command.execute(); + if (command != null) command.execute(); } - + /** * Enth�lt alle m�glichen Aktionen, die der Yoshi-Bot ausf�hren kann. + * * @author Yannic Link */ public enum Action { @@ -77,9 +86,9 @@ public class YoshiCommandDistributor { */ JOKE, /** - * Gib die Nachricht -message aus. �ber die Option -out [text|voice] wird angegeben, ob die Nachricht - * per Textnachricht oder als Text-To-Speech ausgegeben wird. Mit -channel l�sst sich der Ausgabechannel - * bestimmen. Standardm��ig wird die Ausgabe in den Textchannel zur�ckgesendet, aus dem das Kommando kam. + * Gib die Nachricht -message aus. �ber die Option -out [text|voice] wird angegeben, ob die Nachricht per + * Textnachricht oder als Text-To-Speech ausgegeben wird. Mit -channel l�sst sich der Ausgabechannel bestimmen. + * Standardm��ig wird die Ausgabe in den Textchannel zur�ckgesendet, aus dem das Kommando kam. */ SAY, /** @@ -90,16 +99,17 @@ public class YoshiCommandDistributor { /** * Gibt eine vorhandene Ressource -name aus. (Vorhandene Ressourcen lassen sich mit der Aktion LIST anzeigen). * �ber den Parameter -type [link|audio|video] l�sst sich der Typ der Ressource spezifizieren. Ein Link wird - * �ber in den per -channel spezifizierten (default=Ursprungskanal) Textkanal geschickt. Eine Audiodatei - * wird �ber den per -channel spezifizierten (default=Aktueller Kanal) Voice-Channel ausgegeben. Ein Video - * wird �ber den per -channel spezifizierten (default=Aktueller Kanal) Voice-Channel abgespielt. + * �ber in den per -channel spezifizierten (default=Ursprungskanal) Textkanal geschickt. Eine Audiodatei wird + * �ber den per -channel spezifizierten (default=Aktueller Kanal) Voice-Channel ausgegeben. Ein Video wird �ber + * den per -channel spezifizierten (default=Aktueller Kanal) Voice-Channel abgespielt. */ PLAY, /** * L�scht die Ressource, die �ber -name spezifiziert wurde. Mit -type wird der Ressourcentyp festgelegt. */ - DELETE + DELETE, + SAUCE } - + } diff --git a/app/src/main/java/de/yannicpunktdee/yoshibot/command/commands/JokeCommand.java b/app/src/main/java/de/yannicpunktdee/yoshibot/command/commands/JokeCommand.java index 95c7e56..44c8362 100644 --- a/app/src/main/java/de/yannicpunktdee/yoshibot/command/commands/JokeCommand.java +++ b/app/src/main/java/de/yannicpunktdee/yoshibot/command/commands/JokeCommand.java @@ -8,6 +8,7 @@ import java.net.URL; import java.util.List; import java.util.Random; +import de.yannicpunktdee.yoshibot.utils.RestHelper; import org.apache.commons.lang3.StringEscapeUtils; import org.json.JSONException; import org.json.JSONObject; @@ -19,9 +20,9 @@ import net.dv8tion.jda.api.entities.TextChannel; /** * Schickt einen zufälligen Jokus aus einer zufällig ausgewählten Quelle in den Textchannel. + * * @author Yannic Link */ -@SuppressWarnings("deprecation") public class JokeCommand extends YoshiCommand { /** @@ -34,71 +35,56 @@ public class JokeCommand extends YoshiCommand { /** * {@inheritDoc} */ - @Override public synchronized boolean execute() { + @Override + public synchronized boolean execute() { String message = "Jokus"; Random random = new Random(); int number = random.nextInt(3); - switch(number) { - case 0: message = jokeApi(); break; - case 1: message = officialJokeApi(); break; - case 2: message = chuckNorris(); break; - default: message = "Jokus"; break; + switch (number) { + case 0: + message = jokeApi(); + break; + case 1: + message = officialJokeApi(); + break; + case 2: + message = chuckNorris(); + break; + default: + message = "Jokus"; + break; } - if(context.containsArguments(new String[] {"channel"})) { + if (context.containsArguments(new String[]{"channel"})) { String arg = context.getArgument("channel"); - if(arg == null) { + if (arg == null) { sendMessage("Es wurde kein channel angegeben."); return false; } - List channels = YoshiBot.jda.getTextChannelsByName(context.getArgument("channel"), true); - if(channels.isEmpty()) { + List channels = YoshiBot.getInstance().jda + .getTextChannelsByName(context.getArgument("channel"), true); + if (channels.isEmpty()) { sendMessage("Der Kanalname konnte nicht gefunden werden."); return false; } channels.get(0).sendMessage(message).queue(); - }else { + } else { sendMessage(message); } return true; } - private String getFromURL(String url) throws IOException { - StringBuilder response = new StringBuilder(""); - - HttpURLConnection con = null; - - try{ - con = (HttpURLConnection)(new URL(url)).openConnection(); - con.setRequestMethod("GET"); - BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream())); - - String line; - while((line = br.readLine()) != null) { - response.append(StringEscapeUtils.unescapeHtml4(line)); - } - - br.close(); - }catch(IOException e) { - return null; - }finally { - if(con != null) con.disconnect(); - } - - return response.toString(); - } - private String chuckNorris() { String url = "http://api.icndb.com/jokes/random"; JSONObject json = null; try { - String raw = getFromURL(url); + String raw = RestHelper.getFromURL(url); json = new JSONObject(raw); return json.getJSONObject("value").getString("joke"); - }catch(JSONException | IOException e) { + } catch (JSONException | IOException e) { return "Konnte keinen Jokus von \"" + url + "\" laden."; } } @@ -109,13 +95,13 @@ public class JokeCommand extends YoshiCommand { JSONObject json = null; try { - String raw = getFromURL(url); + String raw = RestHelper.getFromURL(url); json = new JSONObject(raw); String result = json.getString("setup"); result += " - "; result += json.getString("punchline"); return result; - }catch(JSONException | IOException e) { + } catch (JSONException | IOException e) { return "Konnte keinen Jokus von \"" + url + "\" laden."; } } @@ -126,15 +112,15 @@ public class JokeCommand extends YoshiCommand { JSONObject json = null; try { - String raw = getFromURL(url); + String raw = RestHelper.getFromURL(url); json = new JSONObject(raw); String result = json.getString("setup"); result += " - "; result += json.getString("delivery"); return result; - }catch(JSONException | IOException e) { + } catch (JSONException | IOException e) { return "Konnte keinen Jokus von \"" + url + "\" laden."; } } - + } diff --git a/app/src/main/java/de/yannicpunktdee/yoshibot/command/commands/PlayCommand.java b/app/src/main/java/de/yannicpunktdee/yoshibot/command/commands/PlayCommand.java index 6e61dd3..567a56f 100644 --- a/app/src/main/java/de/yannicpunktdee/yoshibot/command/commands/PlayCommand.java +++ b/app/src/main/java/de/yannicpunktdee/yoshibot/command/commands/PlayCommand.java @@ -23,7 +23,9 @@ public class PlayCommand extends YoshiCommand { public boolean execute() { if(!super.execute()) return false; - List channels = YoshiBot.jda.getVoiceChannelsByName(context.getArgument("channel"), true); + YoshiBot yoshiBot = YoshiBot.getInstance(); + + List channels = yoshiBot.jda.getVoiceChannelsByName(context.getArgument("channel"), true); if(!(channels.size() > 0)) { context.getEvent().getTextChannel().sendMessage("Der Kanalname konnte nicht gefunden werden.").queue(); return false; @@ -36,9 +38,9 @@ public class PlayCommand extends YoshiCommand { return false; } - AudioController ac = YoshiBot.audioControllerManager.getController(vc.getGuild().getIdLong()); + AudioController ac = yoshiBot.audioControllerManager.getController(vc.getGuild().getIdLong()); vc.getGuild().getAudioManager().openAudioConnection(vc); - YoshiBot.audioPlayerManager.loadItem(fileName, new AudioLoadResultHandlerImpl(ac)); + yoshiBot.audioPlayerManager.loadItem(fileName, new AudioLoadResultHandlerImpl(ac)); return true; } diff --git a/app/src/main/java/de/yannicpunktdee/yoshibot/command/commands/SauceCommand.java b/app/src/main/java/de/yannicpunktdee/yoshibot/command/commands/SauceCommand.java new file mode 100644 index 0000000..a4d25ab --- /dev/null +++ b/app/src/main/java/de/yannicpunktdee/yoshibot/command/commands/SauceCommand.java @@ -0,0 +1,48 @@ +package de.yannicpunktdee.yoshibot.command.commands; + +import de.yannicpunktdee.yoshibot.command.YoshiCommand; +import de.yannicpunktdee.yoshibot.command.YoshiCommandContext; +import de.yannicpunktdee.yoshibot.main.SauceProvider; +import net.dv8tion.jda.api.entities.MessageEmbed; + +import java.util.List; + +public class SauceCommand extends YoshiCommand { + /** + * Erzeugt ein neues Kommando, führt es aber noch nicht aus. Es wird ermittelt, ob die Argumentenkombination valide + * ist und das isOk-Flag gesetzt. Im Fehlerfall wird eine Fehleremeldung spezifiziert. + * + * @param context Der Kontext mit dem das Kommando aufgerufen wurde. + */ + public SauceCommand(YoshiCommandContext context) { + super(context); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean execute() { + if (!super.execute()) return false; + if (!context.getEvent().getTextChannel().isNSFW()) { + sendMessage("Dieser Kanal is nix gut, weil vong nsfw her. Geh woanders hin du kek"); + return true; + } + List arguments = context.getArguments(); + arguments.remove(0); + try { + arguments.stream().map(Integer::parseInt).map(this::byIndex).forEach(this::sendMessage); + } catch (Exception e) { + sendMessage(byTags(arguments)); + } + return true; + } + + private MessageEmbed byIndex(int index) { + return SauceProvider.getSauce(index); + } + + private MessageEmbed byTags(List tags){ + return SauceProvider.getRandomSauce(String.join(" ", tags)); + } +} diff --git a/app/src/main/java/de/yannicpunktdee/yoshibot/command/commands/StopCommand.java b/app/src/main/java/de/yannicpunktdee/yoshibot/command/commands/StopCommand.java index 842d20f..3c226fc 100644 --- a/app/src/main/java/de/yannicpunktdee/yoshibot/command/commands/StopCommand.java +++ b/app/src/main/java/de/yannicpunktdee/yoshibot/command/commands/StopCommand.java @@ -8,16 +8,16 @@ public class StopCommand extends YoshiCommand { protected final String[] requiredArguments = {"please", "uwu"}; - + public StopCommand(YoshiCommandContext context) { super(context); } @Override public boolean execute() { - if(!super.execute()) return false; - YoshiBot.stop(); + if (!super.execute()) return false; + YoshiBot.getInstance().stop(); return true; } - + } diff --git a/app/src/main/java/de/yannicpunktdee/yoshibot/listeners/CommandLine.java b/app/src/main/java/de/yannicpunktdee/yoshibot/listeners/CommandLine.java index 2b48d3b..9bee151 100644 --- a/app/src/main/java/de/yannicpunktdee/yoshibot/listeners/CommandLine.java +++ b/app/src/main/java/de/yannicpunktdee/yoshibot/listeners/CommandLine.java @@ -22,7 +22,7 @@ public class CommandLine extends Thread implements Runnable { while((line = reader.readLine()) != null) { line = line.trim(); if(line.equalsIgnoreCase("exit")) { - YoshiBot.stop(); + YoshiBot.getInstance().stop(); return; } System.out.print("> "); diff --git a/app/src/main/java/de/yannicpunktdee/yoshibot/listeners/CommandListener.java b/app/src/main/java/de/yannicpunktdee/yoshibot/listeners/CommandListener.java index a38ee9e..7ea1e01 100644 --- a/app/src/main/java/de/yannicpunktdee/yoshibot/listeners/CommandListener.java +++ b/app/src/main/java/de/yannicpunktdee/yoshibot/listeners/CommandListener.java @@ -7,9 +7,12 @@ import net.dv8tion.jda.api.entities.ChannelType; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; +import java.util.Arrays; + /** - * Lauscht auf eingehende Nachrichten und leitet diese an die YoshiBot.executeCommand-Methode weiter, - * falls es sich um ein Kommando handelt. + * Lauscht auf eingehende Nachrichten und leitet diese an die YoshiBot.executeCommand-Methode weiter, falls es sich um + * ein Kommando handelt. + * * @author Yannic Link */ public class CommandListener extends ListenerAdapter { @@ -17,23 +20,24 @@ public class CommandListener extends ListenerAdapter { /** * {@inheritDoc} */ - @Override public void onMessageReceived(MessageReceivedEvent event) { - if(!event.isFromType(ChannelType.TEXT)) return; + @Override + public void onMessageReceived(MessageReceivedEvent event) { + if (!event.isFromType(ChannelType.TEXT)) return; + + if (event.getAuthor().isBot()) return; - if(event.getAuthor().isBot()) return; + boolean inErlaubtemKanal = Arrays.stream(Resources.getRestrictCommandsToChannel()) + .anyMatch(channel -> channel.equalsIgnoreCase(event.getTextChannel().getName())); - if(Resources.getRestrictCommandsToChannel() != null - && !Resources.getRestrictCommandsToChannel().equalsIgnoreCase(event.getTextChannel().getName())) + if (Resources.getRestrictCommandsToChannel() != null && !inErlaubtemKanal) return; String raw = event.getMessage().getContentRaw().trim(); - if(!raw.startsWith(YoshiCommandContext.PREFIX)) return; + if (!raw.startsWith(YoshiCommandContext.PREFIX)) return; YoshiCommandContext context = new YoshiCommandContext(raw, event); - if(!context.getState().equals(YoshiCommandContext.State.NO_COMMAND)) YoshiBot.executeCommand(context); - - return; + if (!context.getState().equals(YoshiCommandContext.State.NO_COMMAND)) YoshiBot.executeCommand(context); } - + } diff --git a/app/src/main/java/de/yannicpunktdee/yoshibot/main/Resources.java b/app/src/main/java/de/yannicpunktdee/yoshibot/main/Resources.java index 8c5b18d..5bdb186 100644 --- a/app/src/main/java/de/yannicpunktdee/yoshibot/main/Resources.java +++ b/app/src/main/java/de/yannicpunktdee/yoshibot/main/Resources.java @@ -6,7 +6,11 @@ import de.yannicpunktdee.yoshibot.utils.Logger.Type; import java.io.File; import java.io.FileInputStream; import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Map; import java.util.Properties; +import java.util.stream.Collectors; public class Resources { @@ -19,91 +23,125 @@ public class Resources { private static final String default_audio_source_directory = "rsc/audio/"; private static String audio_source_directory = default_audio_source_directory; - private static final String default_restrict_commands_to_channel = null; - private static String restrict_commands_to_channel = default_restrict_commands_to_channel; + private static final String[] default_restrict_commands_to_channel = null; + private static String[] restrict_commands_to_channel = default_restrict_commands_to_channel; + + private static String[] filtered_tags; + private static Map> feedDetails; + - public synchronized static boolean init(String pathToConfig) { Logger.log("Lade Config.properties ...", Type.INFO); - - if(pathToConfig != null){ - if(!(new File(pathToConfig)).exists()){ + + if (pathToConfig != null) { + if (!(new File(pathToConfig)).exists()) { Logger.log("Der in den Argumenten angegebene Pfad zur Config.properties existiert nicht.", Type.ERROR); return false; } propertiesFilePath = pathToConfig; - }else if(!(new File(propertiesFilePath)).exists()){ - Logger.log("Es wurde keine Config-Datei über den Pfad \"" + propertiesFilePath + "\" gefunden.", Type.ERROR); + } else if (!(new File(propertiesFilePath)).exists()) { + Logger.log("Es wurde keine Config-Datei über den Pfad \"" + propertiesFilePath + "\" gefunden.", + Type.ERROR); return false; } - + propertiesFile = new Properties(); try { propertiesFile.load( new FileInputStream(propertiesFilePath) - ); + ); Logger.log("Config-Datei erfolgreich geladen.", Type.INFO); } catch (IOException e) { Logger.log("Es ist ein Fehler beim Öffnen der Config.propeties aufgetreten.", Type.ERROR); return false; } - + boolean isOk = initJdaBuilderString(); - if(isOk) isOk = initAudio(); - if(isOk) isOk = initChannelRestrict(); - - if(isOk) Logger.log("Die Konfigurationen wurden erfolgreich geladen.", Type.INFO); + if (isOk) isOk = initChannelRestrict(); + if (isOk) isOk = initAudio(); + initTagFilter(); + + if (isOk) Logger.log("Die Konfigurationen wurden erfolgreich geladen.", Type.INFO); else Logger.log("Die Konfiguration konnte nicht geladen werden", Type.ERROR); - + return isOk; } private static boolean initJdaBuilderString() { - if(!propertiesFile.containsKey("jda_builder_string")) { + if (!propertiesFile.containsKey("jda_builder_string")) { Logger.log("Die Config.properties benötigt das Attribut jda_builder_string.", Type.ERROR); return false; } jda_builder_string = propertiesFile.getProperty("jda_builder_string"); return true; } + public static String getJdaBuilderString() { return jda_builder_string; } + + private static void initTagFilter() { + if (!propertiesFile.containsKey("tags_general_filter")) { + Logger.log("Kein Attribut 'tags_general_filter' gefunden", Type.WARNING); + } else { + filtered_tags = propertiesFile.getProperty("tags_general_filter").split(" "); + } + if (!propertiesFile.containsKey("feed_positive_tags")) { + Logger.log("Kein Attribut 'feed_positive_tags' gefunden", Type.WARNING); + } else { + String[] automatedFeeds = propertiesFile.getProperty("feed_positive_tags").split("//"); + feedDetails = Arrays.stream(automatedFeeds).map(feedDetail -> feedDetail.split(" ")) + .collect(Collectors.toMap(list -> list[0], + list -> Arrays.asList(list).subList(1, list.length))); + } + } + private static boolean initAudio() { - if(propertiesFile.containsKey("audio_source_directory")) { + if (propertiesFile.containsKey("audio_source_directory")) { audio_source_directory = propertiesFile.getProperty("audio_source_directory"); - }else{ + } else { Logger.log("Die Config.properties spezifiziert kein audio_source_directory. Lade default.", Type.WARNING); } - + File file = new File(audio_source_directory); - if(!file.exists() || !file.isDirectory()){ + if (!file.exists() || !file.isDirectory()) { Logger.log("Das Audio-Verzeichnis wurde nicht gefunden.", Type.ERROR); return false; } - - if(file.listFiles().length < 1) + + if (file.listFiles().length < 1) Logger.log("Das Audio-Verzeichnis ist leer.", Type.WARNING); - + return true; } + public static String getAudioFilePath(String name) { - name = audio_source_directory + (audio_source_directory.endsWith("/")? "" : "/") + name + ".opus"; - if((new File(name)).exists()) return name; + name = audio_source_directory + (audio_source_directory.endsWith("/") ? "" : "/") + name + ".opus"; + if ((new File(name)).exists()) return name; else return null; } - public static String getAudioSourceDirectory(){ + + public static String getAudioSourceDirectory() { return audio_source_directory; } private static boolean initChannelRestrict() { - if(propertiesFile.containsKey("restrict_commands_to_channel")) - restrict_commands_to_channel = propertiesFile.getProperty("restrict_commands_to_channel"); + if (propertiesFile.containsKey("restrict_commands_to_channel")) + restrict_commands_to_channel = propertiesFile.getProperty("restrict_commands_to_channel").split(" "); return true; } - public static String getRestrictCommandsToChannel() { + + public static String[] getRestrictCommandsToChannel() { return restrict_commands_to_channel; } - + + public static Map> getFeedDetails() { + return feedDetails; + } + + public static String[] getGeneralFilterTags() { + return filtered_tags; + } + } diff --git a/app/src/main/java/de/yannicpunktdee/yoshibot/main/SauceProvider.java b/app/src/main/java/de/yannicpunktdee/yoshibot/main/SauceProvider.java new file mode 100644 index 0000000..c730ecd --- /dev/null +++ b/app/src/main/java/de/yannicpunktdee/yoshibot/main/SauceProvider.java @@ -0,0 +1,147 @@ +package de.yannicpunktdee.yoshibot.main; + +import de.yannicpunktdee.yoshibot.utils.Logger; +import de.yannicpunktdee.yoshibot.utils.RestHelper; +import net.dv8tion.jda.api.EmbedBuilder; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.TextChannel; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.io.IOException; +import java.util.*; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +public class SauceProvider { + + private static final String BASE_URL = "https://r34-json.herokuapp.com/"; + + private int lastKnownSauce = -1; + + private boolean isSauceInit = false; + + public SauceProvider(int timer) { + this(); + ScheduledExecutorService sauceScheduler = Executors.newScheduledThreadPool(1); + sauceScheduler.scheduleAtFixedRate(this::provideSauce, 0, timer, TimeUnit.SECONDS); + new Thread(this::initSauceProviding).start(); + } + + public SauceProvider() { + if (lastKnownSauce == -1) { + this.lastKnownSauce = this.getNewestIndex(); + } + } + + public static MessageEmbed getSauce(int index) { + String url = BASE_URL + "posts?id=" + index; + JSONObject post = getParsedSauceData(url).getJSONArray("posts").getJSONObject(0); + return makeStringFromJson(post); + } + + public static MessageEmbed getRandomSauce(String tags) { + tags = tagsForRest(tags); + tags += String.join("+-", Resources.getGeneralFilterTags()); + Random rand = new Random(); + String url = BASE_URL + "posts?tags=" + String.join("+", tags); + JSONObject baseObj = getParsedSauceData(url); + int amount = baseObj.getInt("count"); + int selectedIndex = rand.nextInt(amount); + int page = (selectedIndex / 100) % 100; + int pageIndex = selectedIndex % 100; + String pagedUrl = url + "&pid=" + page; + JSONObject post = Objects.requireNonNull(getParsedSauceData(pagedUrl)).getJSONArray("posts") + .getJSONObject(pageIndex); + return makeStringFromJson(post); + } + + private static MessageEmbed makeStringFromJson(JSONObject post) { + EmbedBuilder eb = new EmbedBuilder(); + + eb.setTitle("Soße").setDescription("URL: " + post.getString("file_url")); + eb.addField("ID", post.getString("id"), false); + eb.addField("Tags", "`" + post.getJSONArray("tags").join("` `") + "`", false); + eb.setImage(post.getString("file_url")); + + return eb.build(); + } + + private void provideSauce() { + if (!isSauceInit) return; + for (Map.Entry> feed : Resources.getFeedDetails().entrySet()) { + String url = BASE_URL + "posts?tags=" + String.join("+", feed.getValue()) + + "+" + String.join("+", Resources.getGeneralFilterTags()); + JSONArray posts = getParsedSauceData(url).getJSONArray("posts"); + assert posts != null; + List postsInternal = + StreamSupport.stream(posts.spliterator(), false) + .map(obj -> (JSONObject) obj) + .filter(obj -> obj.getInt("id") > lastKnownSauce) + .collect(Collectors.toList()); + Collections.reverse(postsInternal); + YoshiBot yoshiBot = YoshiBot.getInstance(); + for (JSONObject post : postsInternal) { + yoshiBot.jda.getTextChannelsByName(feed.getKey(), true).get(0) + .sendMessage(makeStringFromJson(post)).queue(); + } + Logger.log(String.format("Found %d posts for feed '%s'", postsInternal.size(), feed.getKey()), + Logger.Type.INFO); + } + lastKnownSauce = this.getNewestIndex(); + } + + private void initSauceProviding() { + try { + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + YoshiBot yoshiBot = YoshiBot.getInstance(); + for (Map.Entry> entry : Resources.getFeedDetails().entrySet()) { + List channels = yoshiBot.jda.getTextChannelsByName(entry.getKey(), true); + if (channels.size() > 0) { + this.isSauceInit = true; + this.provideSauce(); + } else { + Logger.log("Konnte keine Kanaäle finden für die Soße", Logger.Type.ERROR); + } + } + } + + private int getNewestIndex() { + JSONObject result = getParsedSauceData("https://r34-json.herokuapp.com/posts?limit=1&q=index"); + return result.getJSONArray("posts").getJSONObject(0).getInt("id"); + } + + private static JSONObject getParsedSauceData(String url) { + + String raw = null; + try { + raw = RestHelper.getFromURL(url); + } catch (IOException e) { + e.printStackTrace(); + } + assert raw != null; + return new JSONObject(raw); + } + + private static String tagsForRest(String tags) { + char[] chars = tags.toCharArray(); + for (int i = 1; i < chars.length - 1; i++) { + if (chars[i] == ' ') { + if (chars[i - 1] != '(' && chars[i + 1] != ')' && chars[i - 1] != '~' && chars[i + 1] != '~') { + chars[i] = '\t'; + } + } + } + String result = new String(chars); + result = result.replace(" ", "%20").replace("\t", "+"); + return result; + } + + +} diff --git a/app/src/main/java/de/yannicpunktdee/yoshibot/main/YoshiBot.java b/app/src/main/java/de/yannicpunktdee/yoshibot/main/YoshiBot.java index 6f9fa26..dc6fef2 100644 --- a/app/src/main/java/de/yannicpunktdee/yoshibot/main/YoshiBot.java +++ b/app/src/main/java/de/yannicpunktdee/yoshibot/main/YoshiBot.java @@ -76,13 +76,14 @@ public class YoshiBot { jda = jdaBuilder.build(); - jdaBuilder.setActivity(Activity.playing("Haare waschen.")); - jdaBuilder.setStatus(OnlineStatus.ONLINE); + jdaBuilder.setStatus(OnlineStatus.ONLINE).setActivity(Activity.playing("Haare waschen.")); System.out.println("YoshiBot online."); commandLineThread = new CommandLine(); commandLineThread.start(); + + SauceProvider provider = new SauceProvider(300); } public synchronized void stop() { diff --git a/app/src/main/java/de/yannicpunktdee/yoshibot/utils/RestHelper.java b/app/src/main/java/de/yannicpunktdee/yoshibot/utils/RestHelper.java new file mode 100644 index 0000000..366cd12 --- /dev/null +++ b/app/src/main/java/de/yannicpunktdee/yoshibot/utils/RestHelper.java @@ -0,0 +1,38 @@ +package de.yannicpunktdee.yoshibot.utils; + +import org.apache.commons.lang3.StringEscapeUtils; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; + +public final class RestHelper { + + public static String getFromURL(String url) throws IOException { + StringBuilder response = new StringBuilder(""); + + HttpURLConnection con = null; + + try { + con = (HttpURLConnection) (new URL(url)).openConnection(); + con.setRequestMethod("GET"); + BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream())); + + String line; + while ((line = br.readLine()) != null) { + response.append(StringEscapeUtils.unescapeHtml4(line)); + } + + br.close(); + } catch (IOException e) { + return null; + } finally { + if (con != null) con.disconnect(); + } + + return response.toString(); + } + +} diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/rsc/Ordnerstruktur.txt b/rsc/Ordnerstruktur.txt index 2cf89d3..359a5f5 100644 --- a/rsc/Ordnerstruktur.txt +++ b/rsc/Ordnerstruktur.txt @@ -1,10 +1,10 @@ -rsc - |-- .gitkeep - |-- Ordnerstruktur.txt - |-- Config.properties - |-- audio - |-- temp - |-- temp_1.opus - |-- temp_2.opus - |-- audio_1.opus +rsc + |-- .gitkeep + |-- Ordnerstruktur.txt + |-- Config.properties + |-- audio + |-- temp + |-- temp_1.opus + |-- temp_2.opus + |-- audio_1.opus |-- audio_2.opus \ No newline at end of file -- 2.17.1