| @ -0,0 +1,6 @@ | |||||
| # | |||||
| # https://help.github.com/articles/dealing-with-line-endings/ | |||||
| # | |||||
| # These are explicitly windows files and should use crlf | |||||
| *.bat text eol=crlf | |||||
| @ -0,0 +1,196 @@ | |||||
| # ---> Java | |||||
| # Compiled class file | |||||
| *.class | |||||
| # Log file | |||||
| *.log | |||||
| # BlueJ files | |||||
| *.ctxt | |||||
| .idea | |||||
| # Mobile Tools for Java (J2ME) | |||||
| .mtj.tmp/ | |||||
| # Package Files # | |||||
| *.jar | |||||
| *.war | |||||
| *.nar | |||||
| *.ear | |||||
| *.zip | |||||
| *.tar.gz | |||||
| *.rar | |||||
| # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml | |||||
| hs_err_pid* | |||||
| # ---> Eclipse | |||||
| .metadata | |||||
| bin/ | |||||
| tmp/ | |||||
| *.tmp | |||||
| *.bak | |||||
| *.swp | |||||
| *~.nib | |||||
| local.properties | |||||
| .settings/ | |||||
| .loadpath | |||||
| .recommenders | |||||
| # External tool builders | |||||
| .externalToolBuilders/ | |||||
| # Locally stored "Eclipse launch configurations" | |||||
| *.launch | |||||
| # PyDev specific (Python IDE for Eclipse) | |||||
| *.pydevproject | |||||
| # CDT-specific (C/C++ Development Tooling) | |||||
| .cproject | |||||
| # CDT- autotools | |||||
| .autotools | |||||
| # Java annotation processor (APT) | |||||
| .factorypath | |||||
| # PDT-specific (PHP Development Tools) | |||||
| .buildpath | |||||
| # sbteclipse plugin | |||||
| .target | |||||
| # Tern plugin | |||||
| .tern-project | |||||
| # TeXlipse plugin | |||||
| .texlipse | |||||
| # STS (Spring Tool Suite) | |||||
| .springBeans | |||||
| # Code Recommenders | |||||
| .recommenders/ | |||||
| # Annotation Processing | |||||
| .apt_generated/ | |||||
| .apt_generated_test/ | |||||
| # Scala IDE specific (Scala & Java development for Eclipse) | |||||
| .cache-main | |||||
| .scala_dependencies | |||||
| .worksheet | |||||
| # Uncomment this line if you wish to ignore the project description file. | |||||
| # Typically, this file would be tracked if it contains build/dependency configurations: | |||||
| .project | |||||
| # ---> Maven | |||||
| target/ | |||||
| pom.xml.tag | |||||
| pom.xml.releaseBackup | |||||
| pom.xml.versionsBackup | |||||
| pom.xml.next | |||||
| release.properties | |||||
| dependency-reduced-pom.xml | |||||
| buildNumber.properties | |||||
| .mvn/timing.properties | |||||
| # https://github.com/takari/maven-wrapper#usage-without-binary-jar | |||||
| .mvn/wrapper/maven-wrapper.jar | |||||
| # ---> JetBrains | |||||
| # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider | |||||
| # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 | |||||
| # User-specific stuff | |||||
| .idea/**/workspace.xml | |||||
| .idea/**/tasks.xml | |||||
| .idea/**/usage.statistics.xml | |||||
| .idea/**/dictionaries | |||||
| .idea/**/shelf | |||||
| # Generated files | |||||
| .idea/**/contentModel.xml | |||||
| # Sensitive or high-churn files | |||||
| .idea/**/dataSources/ | |||||
| .idea/**/dataSources.ids | |||||
| .idea/**/dataSources.local.xml | |||||
| .idea/**/sqlDataSources.xml | |||||
| .idea/**/dynamic.xml | |||||
| .idea/**/uiDesigner.xml | |||||
| .idea/**/dbnavigator.xml | |||||
| # Gradle | |||||
| .idea/**/gradle.xml | |||||
| .idea/**/libraries | |||||
| # Gradle and Maven with auto-import | |||||
| # When using Gradle or Maven with auto-import, you should exclude module files, | |||||
| # since they will be recreated, and may cause churn. Uncomment if using | |||||
| # auto-import. | |||||
| .idea/artifacts | |||||
| .idea/compiler.xml | |||||
| .idea/jarRepositories.xml | |||||
| .idea/modules.xml | |||||
| .idea/*.iml | |||||
| .idea/modules | |||||
| *.iml | |||||
| *.ipr | |||||
| # CMake | |||||
| cmake-build-*/ | |||||
| # Mongo Explorer plugin | |||||
| .idea/**/mongoSettings.xml | |||||
| # File-based project format | |||||
| *.iws | |||||
| # IntelliJ | |||||
| out/ | |||||
| # mpeltonen/sbt-idea plugin | |||||
| .idea_modules/ | |||||
| # JIRA plugin | |||||
| atlassian-ide-plugin.xml | |||||
| # Cursive Clojure plugin | |||||
| .idea/replstate.xml | |||||
| # Crashlytics plugin (for Android Studio and IntelliJ) | |||||
| com_crashlytics_export_strings.xml | |||||
| crashlytics.properties | |||||
| crashlytics-build.properties | |||||
| fabric.properties | |||||
| # Editor-based Rest Client | |||||
| .idea/httpRequests | |||||
| # Android studio 3.1+ serialized cache file | |||||
| .idea/caches/build_file_checksums.ser | |||||
| # ---> Gradle | |||||
| .gradle | |||||
| **/build/ | |||||
| !src/**/build/ | |||||
| # Ignore Gradle GUI config | |||||
| gradle-app.setting | |||||
| # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) | |||||
| !gradle-wrapper.jar | |||||
| # Cache of project | |||||
| .gradletasknamecache | |||||
| # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 | |||||
| # gradle/wrapper/gradle-wrapper.properties | |||||
| .classpath | |||||
| app/src/main/resources/* | |||||
| !app/src/main/resources/.gitkeep | |||||
| @ -0,0 +1,11 @@ | |||||
| # YoshiBot | |||||
| Ein in Java geschriebener Discordbot, der lustige Sachen kann. | |||||
| ##Einrichtung | |||||
| Ordner app/src/main/resources anlegen und darein die Config.properties anlegen. Diese sollte folgende Werte enthalten: | |||||
| - jda_builder_string -> Client Secret | |||||
| - audio_source_directory -> Verzeichnis, in dem die Audio-Dateien liegen, auch unter Windows mit / anstatt \ (dieses Verzeichnis sollte natürlich existieren) | |||||
| - restrict_commands_to_channel -> Textkanalname, auf dem die Botkommandos empfangen werden | |||||
| @ -0,0 +1,38 @@ | |||||
| /* | |||||
| * This file was generated by the Gradle 'init' task. | |||||
| * | |||||
| * This generated file contains a sample Java application project to get you started. | |||||
| * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle | |||||
| * User Manual available at https://docs.gradle.org/6.8.3/userguide/building_java_projects.html | |||||
| */ | |||||
| plugins { | |||||
| // Apply the application plugin to add support for building a CLI application in Java. | |||||
| id 'application' | |||||
| } | |||||
| repositories { | |||||
| // Use JCenter for resolving dependencies. | |||||
| jcenter() | |||||
| } | |||||
| dependencies { | |||||
| // Use JUnit test framework. | |||||
| testImplementation 'junit:junit:4.13' | |||||
| // This dependency is used by the application. | |||||
| implementation 'com.google.guava:guava:29.0-jre' | |||||
| implementation group: 'org.json', name: 'json', version: '20210307' | |||||
| implementation 'net.dv8tion:JDA:4.2.0_247' | |||||
| implementation 'com.sedmelluq:lavaplayer:1.3.73' | |||||
| implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.0' | |||||
| } | |||||
| application { | |||||
| // Define the main class for the application. | |||||
| mainClass = 'de.yannicpunktdee.yoshibot.main.Main' | |||||
| } | |||||
| @ -0,0 +1,30 @@ | |||||
| package de.yannicpunktdee.yoshibot.audio; | |||||
| import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; | |||||
| import de.yannicpunktdee.yoshibot.main.YoshiBot; | |||||
| import net.dv8tion.jda.api.entities.Guild; | |||||
| public class AudioController { | |||||
| private Guild guild; | |||||
| private AudioPlayer audioPlayer; | |||||
| public AudioController(Guild guild) { | |||||
| this.guild = guild; | |||||
| this.audioPlayer = YoshiBot.audioPlayerManager.createPlayer(); | |||||
| audioPlayer.addListener(new AudioPlayerListener(guild.getAudioManager())); | |||||
| this.guild.getAudioManager().setSendingHandler(new AudioSendHandlerImpl(audioPlayer)); | |||||
| } | |||||
| public Guild getGuild() { | |||||
| return guild; | |||||
| } | |||||
| public AudioPlayer getAudioPlayer() { | |||||
| return audioPlayer; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,28 @@ | |||||
| package de.yannicpunktdee.yoshibot.audio; | |||||
| import java.util.Map; | |||||
| import java.util.concurrent.ConcurrentHashMap; | |||||
| import de.yannicpunktdee.yoshibot.main.YoshiBot; | |||||
| public class AudioControllerManager { | |||||
| public Map<Long, AudioController> audioController; | |||||
| public AudioControllerManager() { | |||||
| audioController = new ConcurrentHashMap<Long, AudioController>(); | |||||
| } | |||||
| public AudioController getController(long guildId) { | |||||
| AudioController ac = null; | |||||
| if(audioController.containsKey(guildId)) | |||||
| ac = audioController.get(guildId); | |||||
| else { | |||||
| ac = new AudioController(YoshiBot.jda.getGuildById(guildId)); | |||||
| audioController.put(guildId, ac); | |||||
| } | |||||
| return ac; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,39 @@ | |||||
| package de.yannicpunktdee.yoshibot.audio; | |||||
| import com.sedmelluq.discord.lavaplayer.player.AudioLoadResultHandler; | |||||
| import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; | |||||
| import com.sedmelluq.discord.lavaplayer.track.AudioPlaylist; | |||||
| import com.sedmelluq.discord.lavaplayer.track.AudioTrack; | |||||
| import de.yannicpunktdee.yoshibot.main.YoshiBot; | |||||
| public class AudioLoadResultHandlerImpl implements AudioLoadResultHandler { | |||||
| private AudioController audioController; | |||||
| public AudioLoadResultHandlerImpl(AudioController audioController) { | |||||
| this.audioController = audioController; | |||||
| } | |||||
| @Override | |||||
| public void trackLoaded(AudioTrack track) { | |||||
| audioController.getAudioPlayer().playTrack(track); | |||||
| } | |||||
| @Override | |||||
| public void playlistLoaded(AudioPlaylist playlist) { | |||||
| System.out.println("Kann aktuell noch keine Playlists abspielen"); | |||||
| } | |||||
| @Override | |||||
| public void noMatches() { | |||||
| System.out.println("Nothing found"); | |||||
| } | |||||
| @Override | |||||
| public void loadFailed(FriendlyException exception) { | |||||
| System.out.println("Loading failed"); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,25 @@ | |||||
| package de.yannicpunktdee.yoshibot.audio; | |||||
| import com.sedmelluq.discord.lavaplayer.player.event.AudioEvent; | |||||
| import com.sedmelluq.discord.lavaplayer.player.event.AudioEventListener; | |||||
| import net.dv8tion.jda.api.managers.AudioManager; | |||||
| public class AudioPlayerListener implements AudioEventListener { | |||||
| private AudioManager audioManager; | |||||
| public AudioPlayerListener(AudioManager audioManager) { | |||||
| this.audioManager = audioManager; | |||||
| } | |||||
| @Override | |||||
| public void onEvent(AudioEvent event) { | |||||
| if(event.player.getPlayingTrack() == null) { | |||||
| event.player.stopTrack(); | |||||
| audioManager.closeAudioConnection(); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,37 @@ | |||||
| package de.yannicpunktdee.yoshibot.audio; | |||||
| import java.nio.ByteBuffer; | |||||
| import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; | |||||
| import com.sedmelluq.discord.lavaplayer.track.playback.AudioFrame; | |||||
| import de.yannicpunktdee.yoshibot.main.YoshiBot; | |||||
| import net.dv8tion.jda.api.audio.AudioSendHandler; | |||||
| public class AudioSendHandlerImpl implements AudioSendHandler { | |||||
| private AudioPlayer audioPlayer; | |||||
| private AudioFrame lastFrame; | |||||
| public AudioSendHandlerImpl(AudioPlayer audioPlayer) { | |||||
| this.audioPlayer = audioPlayer; | |||||
| } | |||||
| @Override | |||||
| public boolean canProvide() { | |||||
| lastFrame = audioPlayer.provide(); | |||||
| return lastFrame != null; | |||||
| } | |||||
| @Override | |||||
| public ByteBuffer provide20MsAudio() { | |||||
| return ByteBuffer.wrap(lastFrame.getData()); | |||||
| } | |||||
| @Override | |||||
| public boolean isOpus() { | |||||
| return true; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,43 @@ | |||||
| package de.yannicpunktdee.yoshibot.command; | |||||
| /** | |||||
| * Abstrakte Superklasse für alle Kommandos. | |||||
| * @author Yannic Link | |||||
| */ | |||||
| public abstract class YoshiCommand { | |||||
| protected final String[] requiredArguments = {}; | |||||
| /** | |||||
| * Der Kontext mit dem das Kommando aufgerufen wurde. | |||||
| */ | |||||
| protected YoshiCommandContext context; | |||||
| /** | |||||
| * 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 YoshiCommand(YoshiCommandContext context) { | |||||
| this.context = context; | |||||
| } | |||||
| /** | |||||
| * Führt das Kommando aus. | |||||
| * @return True, wenn Ausführung erfolgreich. False, wenn Ausführung fehlgeschlagen. Fehlermeldung wird in | |||||
| * errorMessage spezifiziert. | |||||
| */ | |||||
| public boolean execute() { | |||||
| if(!context.containsArguments(requiredArguments)){ | |||||
| sendMessage("Fehlende Argumente"); | |||||
| return false; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| protected void sendMessage(String message) { | |||||
| context.getEvent().getTextChannel().sendMessage(message).queue(); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,288 @@ | |||||
| package de.yannicpunktdee.yoshibot.command; | |||||
| import java.util.HashMap; | |||||
| import java.util.Map; | |||||
| import de.yannicpunktdee.yoshibot.command.YoshiCommandDistributor.Action; | |||||
| import de.yannicpunktdee.yoshibot.main.YoshiBot; | |||||
| import net.dv8tion.jda.api.entities.Guild; | |||||
| import net.dv8tion.jda.api.entities.TextChannel; | |||||
| import net.dv8tion.jda.api.entities.User; | |||||
| 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. | |||||
| * @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. | |||||
| */ | |||||
| public static enum State { | |||||
| /** | |||||
| * Das Kommando wurde erfolgreich geparst und besitzt keine Syntaxfehler. | |||||
| */ | |||||
| OK, | |||||
| /** | |||||
| * Der Eingabestring war kein Kommando für den Yoshi-Bot. | |||||
| */ | |||||
| NO_COMMAND, | |||||
| /** | |||||
| * Im Kommando wurde keine Aktion spezifiziert. | |||||
| */ | |||||
| NO_ACTION, | |||||
| /** | |||||
| * Die angegebene Aktion existiert nicht. | |||||
| */ | |||||
| UNKNOWN_ACTION, | |||||
| /** | |||||
| * Das Kommando hatt einen Syntaxfehler. | |||||
| */ | |||||
| BAD_SYNTAX | |||||
| } | |||||
| /** | |||||
| * Hilfskonstrukt. Beschreibt den Zustand vom Parser. | |||||
| */ | |||||
| private static enum ReadingState{ | |||||
| /** | |||||
| * Der Parser ist dabei festzustellen, ob es sich um ein Kommando handelt. | |||||
| */ | |||||
| VERIFYING, | |||||
| /** | |||||
| * Zwischenzustand zwischen VERIFYING und READING_ACTION. | |||||
| */ | |||||
| AFTER_VERIFY, | |||||
| /** | |||||
| * Der Parser liest die Action ein. | |||||
| */ | |||||
| READING_ACTION, | |||||
| /** | |||||
| * Zustand nach READING_ACTION, READING_VALUE oder READING_STRING). | |||||
| */ | |||||
| INTERMEDIATE, | |||||
| /** | |||||
| * Der Parser ist dabei einen Argumentenkey einzulesen. | |||||
| */ | |||||
| READING_KEY, | |||||
| /** | |||||
| * Der Parser hat gerade einen Argumentenkey eingelesen. | |||||
| */ | |||||
| AFTER_KEY, | |||||
| /** | |||||
| * Der Parser liest gerade einen Argumentenwert ein. | |||||
| */ | |||||
| READING_VALUE, | |||||
| /** | |||||
| * Der Parser liest gerade einen zusammenhängenden Argumentenwert ein. | |||||
| */ | |||||
| READING_STRING | |||||
| }; | |||||
| /** | |||||
| * Das Präfix, mit dem Yoshi-Bot-Kommandos beginnen. | |||||
| */ | |||||
| public static final String PREFIX = "::yoshi"; | |||||
| /** | |||||
| * Der (Fehler-)Status, den der Parser nach Einlesen des Eingabestrings angenommen hat. | |||||
| */ | |||||
| private State state; | |||||
| /** | |||||
| * Die im Eingabesting spezifizierte Aktion. | |||||
| */ | |||||
| private Action action; | |||||
| /** | |||||
| * Eine Map, die die Key-Werte der Argumente (ohne Bindestrich) auf dessen Werte abbildet. | |||||
| */ | |||||
| private Map<String, String> arguments; | |||||
| private MessageReceivedEvent event; | |||||
| /** | |||||
| * 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<String, String>(); | |||||
| ReadingState readingState = ReadingState.VERIFYING; | |||||
| String currentKey = null; | |||||
| int startPos = 0; | |||||
| int length = argumentsString.length(); | |||||
| for(int position = 0; position < length; position++) { | |||||
| char currentChar = argumentsString.charAt(position); | |||||
| 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 == '"') { | |||||
| 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(argumentsString.equals(PREFIX)) { | |||||
| action = Action.HELP; | |||||
| 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; | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Prüft, ob der Eingabestring ein valides Kommando war. | |||||
| */ | |||||
| public boolean isValid() { | |||||
| return state.equals(State.OK); | |||||
| } | |||||
| /** | |||||
| * Gibt den (Fehler-)Status des Parsers zurück. | |||||
| */ | |||||
| 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)) { | |||||
| 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; | |||||
| return arguments.get(arg); | |||||
| } | |||||
| public MessageReceivedEvent getEvent() { | |||||
| return event; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,106 @@ | |||||
| package de.yannicpunktdee.yoshibot.command; | |||||
| import de.yannicpunktdee.yoshibot.command.commands.HelpCommand; | |||||
| import de.yannicpunktdee.yoshibot.command.commands.JokeCommand; | |||||
| import de.yannicpunktdee.yoshibot.command.commands.PlayCommand; | |||||
| import de.yannicpunktdee.yoshibot.command.commands.SayCommand; | |||||
| import de.yannicpunktdee.yoshibot.command.commands.StopCommand; | |||||
| /** | |||||
| * 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; | |||||
| } | |||||
| 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 PLAY: | |||||
| command = new PlayCommand(context); | |||||
| break; | |||||
| default: | |||||
| context.getEvent().getTextChannel().sendMessage("Dieses Kommando existiert noch nicht.").queue(); | |||||
| break; | |||||
| } | |||||
| if(command != null) command.execute(); | |||||
| } | |||||
| /** | |||||
| * Enth�lt alle m�glichen Aktionen, die der Yoshi-Bot ausf�hren kann. | |||||
| * @author Yannic Link | |||||
| */ | |||||
| public enum Action { | |||||
| /** | |||||
| * Sende eine Hilfe-Nachricht, in der die Benutzung des Yoshi-Bots dokumentiert ist. | |||||
| */ | |||||
| HELP, | |||||
| /** | |||||
| * Fahre den Yoshi-Bot herunter (nur mit speziellen Berechtigungen m�glich). | |||||
| */ | |||||
| STOP, | |||||
| /** | |||||
| * Erzählt einen Jokus. | |||||
| */ | |||||
| 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. | |||||
| */ | |||||
| SAY, | |||||
| /** | |||||
| * Listet alle zugewiesenen Ressourcen auf. Mit der Option -type [all|link|audio|video] l�sst sich das Format | |||||
| * der Ressource spezifizieren. | |||||
| */ | |||||
| LIST, | |||||
| /** | |||||
| * 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. | |||||
| */ | |||||
| PLAY, | |||||
| /** | |||||
| * L�scht die Ressource, die �ber -name spezifiziert wurde. Mit -type wird der Ressourcentyp festgelegt. | |||||
| */ | |||||
| DELETE | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,15 @@ | |||||
| package de.yannicpunktdee.yoshibot.command.commands; | |||||
| import de.yannicpunktdee.yoshibot.command.YoshiCommand; | |||||
| import de.yannicpunktdee.yoshibot.command.YoshiCommandContext; | |||||
| public class HelpCommand extends YoshiCommand { | |||||
| public HelpCommand(YoshiCommandContext context) { | |||||
| super(context); | |||||
| } | |||||
| @Override | |||||
| public boolean execute() {return true;} | |||||
| } | |||||
| @ -0,0 +1,140 @@ | |||||
| package de.yannicpunktdee.yoshibot.command.commands; | |||||
| import java.io.BufferedReader; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStreamReader; | |||||
| import java.net.HttpURLConnection; | |||||
| import java.net.URL; | |||||
| import java.util.List; | |||||
| import java.util.Random; | |||||
| import org.apache.commons.lang3.StringEscapeUtils; | |||||
| import org.json.JSONException; | |||||
| import org.json.JSONObject; | |||||
| import de.yannicpunktdee.yoshibot.command.YoshiCommand; | |||||
| import de.yannicpunktdee.yoshibot.command.YoshiCommandContext; | |||||
| import de.yannicpunktdee.yoshibot.main.YoshiBot; | |||||
| 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 { | |||||
| /** | |||||
| * Erstellt einen neuen JokeCommand. | |||||
| */ | |||||
| public JokeCommand(YoshiCommandContext context) { | |||||
| super(context); | |||||
| } | |||||
| /** | |||||
| * {@inheritDoc} | |||||
| */ | |||||
| @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; | |||||
| } | |||||
| if(context.containsArguments(new String[] {"channel"})) { | |||||
| String arg = context.getArgument("channel"); | |||||
| if(arg == null) { | |||||
| sendMessage("Es wurde kein channel angegeben."); | |||||
| return false; | |||||
| } | |||||
| List<TextChannel> channels = YoshiBot.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 { | |||||
| 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); | |||||
| json = new JSONObject(raw); | |||||
| return json.getJSONObject("value").getString("joke"); | |||||
| }catch(JSONException | IOException e) { | |||||
| return "Konnte keinen Jokus von \"" + url + "\" laden."; | |||||
| } | |||||
| } | |||||
| private String officialJokeApi() { | |||||
| String url = "https://official-joke-api.appspot.com/jokes/random"; | |||||
| JSONObject json = null; | |||||
| try { | |||||
| String raw = getFromURL(url); | |||||
| json = new JSONObject(raw); | |||||
| String result = json.getString("setup"); | |||||
| result += " - "; | |||||
| result += json.getString("punchline"); | |||||
| return result; | |||||
| }catch(JSONException | IOException e) { | |||||
| return "Konnte keinen Jokus von \"" + url + "\" laden."; | |||||
| } | |||||
| } | |||||
| private String jokeApi() { | |||||
| String url = "https://v2.jokeapi.dev/joke/any"; | |||||
| JSONObject json = null; | |||||
| try { | |||||
| String raw = getFromURL(url); | |||||
| json = new JSONObject(raw); | |||||
| String result = json.getString("setup"); | |||||
| result += " - "; | |||||
| result += json.getString("delivery"); | |||||
| return result; | |||||
| }catch(JSONException | IOException e) { | |||||
| return "Konnte keinen Jokus von \"" + url + "\" laden."; | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,46 @@ | |||||
| package de.yannicpunktdee.yoshibot.command.commands; | |||||
| import java.util.List; | |||||
| import de.yannicpunktdee.yoshibot.audio.AudioController; | |||||
| import de.yannicpunktdee.yoshibot.audio.AudioLoadResultHandlerImpl; | |||||
| import de.yannicpunktdee.yoshibot.command.YoshiCommand; | |||||
| import de.yannicpunktdee.yoshibot.command.YoshiCommandContext; | |||||
| import de.yannicpunktdee.yoshibot.main.Resources; | |||||
| import de.yannicpunktdee.yoshibot.main.YoshiBot; | |||||
| import net.dv8tion.jda.api.entities.VoiceChannel; | |||||
| public class PlayCommand extends YoshiCommand { | |||||
| protected final String[] requiredArguments = new String[] {"channel", "name"}; | |||||
| public PlayCommand(YoshiCommandContext context) { | |||||
| super(context); | |||||
| } | |||||
| @Override | |||||
| public boolean execute() { | |||||
| if(!super.execute()) return false; | |||||
| List<VoiceChannel> 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; | |||||
| } | |||||
| VoiceChannel vc = channels.get(0); | |||||
| String fileName = Resources.getAudioFilePath(context.getArgument("name")); | |||||
| if(fileName == null) { | |||||
| context.getEvent().getTextChannel().sendMessage("Audio konnte nicht gefunden werden.").queue(); | |||||
| return false; | |||||
| } | |||||
| AudioController ac = YoshiBot.audioControllerManager.getController(vc.getGuild().getIdLong()); | |||||
| vc.getGuild().getAudioManager().openAudioConnection(vc); | |||||
| YoshiBot.audioPlayerManager.loadItem(fileName, new AudioLoadResultHandlerImpl(ac)); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,15 @@ | |||||
| package de.yannicpunktdee.yoshibot.command.commands; | |||||
| import de.yannicpunktdee.yoshibot.command.YoshiCommand; | |||||
| import de.yannicpunktdee.yoshibot.command.YoshiCommandContext; | |||||
| public class SayCommand extends YoshiCommand { | |||||
| public SayCommand(YoshiCommandContext context) { | |||||
| super(context); | |||||
| } | |||||
| @Override | |||||
| public boolean execute() {return true;} | |||||
| } | |||||
| @ -0,0 +1,23 @@ | |||||
| package de.yannicpunktdee.yoshibot.command.commands; | |||||
| import de.yannicpunktdee.yoshibot.command.YoshiCommand; | |||||
| import de.yannicpunktdee.yoshibot.command.YoshiCommandContext; | |||||
| import de.yannicpunktdee.yoshibot.main.YoshiBot; | |||||
| 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(); | |||||
| return true; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,44 @@ | |||||
| package de.yannicpunktdee.yoshibot.listeners; | |||||
| import java.io.BufferedReader; | |||||
| import java.io.IOException; | |||||
| import java.io.InputStreamReader; | |||||
| import de.yannicpunktdee.yoshibot.main.YoshiBot; | |||||
| public class CommandLine extends Thread implements Runnable { | |||||
| private BufferedReader reader; | |||||
| /** | |||||
| * Nicht manuell aufrufen. Wird einmalig in startYoshiBot in einem neuen Thread aufgerufen und reagiert | |||||
| * auf administrative Konsoleneingaben außerhalb von Discord. | |||||
| */ | |||||
| @Override public void run() { | |||||
| String line = ""; | |||||
| reader = new BufferedReader(new InputStreamReader(System.in)); | |||||
| try { | |||||
| System.out.print("> "); | |||||
| while((line = reader.readLine()) != null) { | |||||
| line = line.trim(); | |||||
| if(line.equalsIgnoreCase("exit")) { | |||||
| YoshiBot.stop(); | |||||
| return; | |||||
| } | |||||
| System.out.print("> "); | |||||
| } | |||||
| } catch(IOException e) { | |||||
| System.err.println("Es ist eine IOException aufgetreten."); | |||||
| } | |||||
| } | |||||
| public void stopCommandLine() { | |||||
| try { | |||||
| reader.close(); | |||||
| interrupt(); | |||||
| } catch (IOException e) { | |||||
| System.err.println("Fehler beim Schließen des Readers."); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,39 @@ | |||||
| package de.yannicpunktdee.yoshibot.listeners; | |||||
| import de.yannicpunktdee.yoshibot.command.YoshiCommandContext; | |||||
| import de.yannicpunktdee.yoshibot.main.Resources; | |||||
| import de.yannicpunktdee.yoshibot.main.YoshiBot; | |||||
| import net.dv8tion.jda.api.entities.ChannelType; | |||||
| import net.dv8tion.jda.api.events.message.MessageReceivedEvent; | |||||
| import net.dv8tion.jda.api.hooks.ListenerAdapter; | |||||
| /** | |||||
| * 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 { | |||||
| /** | |||||
| * {@inheritDoc} | |||||
| */ | |||||
| @Override public void onMessageReceived(MessageReceivedEvent event) { | |||||
| if(!event.isFromType(ChannelType.TEXT)) return; | |||||
| if(event.getAuthor().isBot()) return; | |||||
| if(Resources.getRestrictCommandsToChannel() != null | |||||
| && !Resources.getRestrictCommandsToChannel().equalsIgnoreCase(event.getTextChannel().getName())) | |||||
| return; | |||||
| String raw = event.getMessage().getContentRaw().trim(); | |||||
| if(!raw.startsWith(YoshiCommandContext.PREFIX)) return; | |||||
| YoshiCommandContext context = new YoshiCommandContext(raw, event); | |||||
| if(!context.getState().equals(YoshiCommandContext.State.NO_COMMAND)) YoshiBot.executeCommand(context); | |||||
| return; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,29 @@ | |||||
| package de.yannicpunktdee.yoshibot.main; | |||||
| import java.net.URISyntaxException; | |||||
| import javax.security.auth.login.LoginException; | |||||
| /** | |||||
| * Main-Klasse und Startpunkt für die Bot-Applikation. | |||||
| * @author Yannic Link | |||||
| */ | |||||
| public class Main { | |||||
| /** | |||||
| * Eintrittspunkt für die Applikation. Erzeugen eines neuen Yoshi-Bots und Starten. | |||||
| * @param args Aufrufargumente. Werden später zum Konfigurieren genutzt. | |||||
| * @throws URISyntaxException | |||||
| */ | |||||
| public static void main(String[] args) throws URISyntaxException { | |||||
| System.out.println("Starte Applikation.\n"); | |||||
| YoshiBot.init((args.length > 0)? args[0] : null); | |||||
| try { | |||||
| YoshiBot.start(); | |||||
| }catch(LoginException e) { | |||||
| System.err.println("Es ist ein Fehler beim Login aufgetreten."); | |||||
| } | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,77 @@ | |||||
| package de.yannicpunktdee.yoshibot.main; | |||||
| import java.io.File; | |||||
| import java.io.IOException; | |||||
| import java.util.Properties; | |||||
| public class Resources { | |||||
| private static final String default_propertiesFilePath = "Config.properties"; | |||||
| private static String propertiesFilePath = default_propertiesFilePath; | |||||
| private static Properties propertiesFile; | |||||
| private static String jda_builder_string; | |||||
| 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; | |||||
| public synchronized static void init(String pathToConfig) { | |||||
| if(pathToConfig != null) propertiesFilePath = pathToConfig; | |||||
| if(!(new File(propertiesFilePath)).exists()) propertiesFilePath = default_propertiesFilePath; | |||||
| propertiesFile = new Properties(); | |||||
| try { | |||||
| propertiesFile.load(Resources.class.getClassLoader().getResourceAsStream(propertiesFilePath)); | |||||
| System.out.println("Properties-Datei erfolgreich geladen."); | |||||
| } catch (IOException e) { | |||||
| System.err.println("Es wurde keine Config-Datei gefunden. Benutze Standards."); | |||||
| return; | |||||
| } | |||||
| initJdaBuilderString(); | |||||
| initAudio(); | |||||
| initChannelRestrict(); | |||||
| } | |||||
| private static void initJdaBuilderString() { | |||||
| if(!propertiesFile.containsKey("jda_builder_string")) { | |||||
| System.err.println("Es wurde kein jda_builder_string gefunden."); | |||||
| YoshiBot.stop(); | |||||
| } else jda_builder_string = propertiesFile.getProperty("jda_builder_string"); | |||||
| } | |||||
| public static String getJdaBuilderString() { | |||||
| return jda_builder_string; | |||||
| } | |||||
| private static void initAudio() { | |||||
| if(!propertiesFile.containsKey("audio_source_directory")) return; | |||||
| String dir = propertiesFile.getProperty("audio_source_directory"); | |||||
| File file = new File(dir); | |||||
| if(!file.exists() || !file.isDirectory()){ | |||||
| System.err.println("Das Audio-Verzeichnis wurde nicht gefunden"); | |||||
| return; | |||||
| } | |||||
| audio_source_directory = dir; | |||||
| } | |||||
| public static String getAudioFilePath(String name) { | |||||
| name = audio_source_directory + (audio_source_directory.endsWith("/")? "" : "/") + name + ".opus"; | |||||
| System.out.println("Dateiname: " + name); | |||||
| if((new File(name)).exists()) return name; | |||||
| else return null; | |||||
| } | |||||
| private static void initChannelRestrict() { | |||||
| if(propertiesFile.containsKey("restrict_commands_to_channel")) | |||||
| restrict_commands_to_channel = propertiesFile.getProperty("restrict_commands_to_channel"); | |||||
| } | |||||
| public static String getRestrictCommandsToChannel() { | |||||
| return restrict_commands_to_channel; | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,99 @@ | |||||
| package de.yannicpunktdee.yoshibot.main; | |||||
| import javax.security.auth.login.LoginException; | |||||
| import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager; | |||||
| import com.sedmelluq.discord.lavaplayer.player.DefaultAudioPlayerManager; | |||||
| import com.sedmelluq.discord.lavaplayer.source.AudioSourceManagers; | |||||
| import com.sedmelluq.discord.lavaplayer.source.local.LocalAudioSourceManager; | |||||
| import de.yannicpunktdee.yoshibot.audio.AudioControllerManager; | |||||
| import de.yannicpunktdee.yoshibot.command.YoshiCommandContext; | |||||
| import de.yannicpunktdee.yoshibot.command.YoshiCommandDistributor; | |||||
| import de.yannicpunktdee.yoshibot.listeners.CommandLine; | |||||
| import de.yannicpunktdee.yoshibot.listeners.CommandListener; | |||||
| import net.dv8tion.jda.api.JDA; | |||||
| import net.dv8tion.jda.api.JDABuilder; | |||||
| import net.dv8tion.jda.api.OnlineStatus; | |||||
| import net.dv8tion.jda.api.entities.Activity; | |||||
| /** | |||||
| * Repräsentiert einen Yoshi-Bot. Der Bot initialisiert alle Ressourcen und schaltet sich in der | |||||
| * startYoshiBot-Methode online und beginnt dann zu lauschen. Parallel lauscht ein Thread auf | |||||
| * Konsoleneingaben für administrative Zwecke, die nicht über den Chat erledigt werden sollten. | |||||
| * @author Yannic Link | |||||
| */ | |||||
| public class YoshiBot { | |||||
| private static CommandLine commandLineThread; | |||||
| /** | |||||
| * Erlaubt es einige Einstellungen vor und nach der Erzeugung eines Bots vorzunehmen. | |||||
| */ | |||||
| public static JDABuilder jdaBuilder; | |||||
| /** | |||||
| * Instanz vom aktuell laufenden Bot. | |||||
| */ | |||||
| public static JDA jda; | |||||
| /** | |||||
| * LavaPlayer AudioPlayerManager. | |||||
| */ | |||||
| public static AudioPlayerManager audioPlayerManager; | |||||
| public static AudioControllerManager audioControllerManager; | |||||
| /** | |||||
| * Initialisiert alle dynamisch hinzugefügten und statischen Ressourcen. Startet aber nicht | |||||
| * den Bot selbst. | |||||
| */ | |||||
| public static void init(String configPath) { | |||||
| Resources.init(configPath); | |||||
| } | |||||
| /** | |||||
| * Startet den Bot und schaltet ihn online. Beginnt auf Konsoleneingaben für administrative | |||||
| * Zwecke zu lauschen. | |||||
| * @throws LoginException Falls das Token ungültig ist. | |||||
| */ | |||||
| public static void start() throws LoginException { | |||||
| System.out.println("Starte YoshiBot."); | |||||
| jdaBuilder = JDABuilder.createDefault(Resources.getJdaBuilderString()); | |||||
| jdaBuilder.addEventListeners(new CommandListener()); | |||||
| audioPlayerManager = new DefaultAudioPlayerManager(); | |||||
| audioPlayerManager.registerSourceManager(new LocalAudioSourceManager()); | |||||
| AudioSourceManagers.registerRemoteSources(audioPlayerManager); | |||||
| audioControllerManager = new AudioControllerManager(); | |||||
| jda = jdaBuilder.build(); | |||||
| jdaBuilder.setActivity(Activity.playing("Haare waschen.")); | |||||
| jdaBuilder.setStatus(OnlineStatus.ONLINE); | |||||
| System.out.println("YoshiBot online."); | |||||
| commandLineThread = new CommandLine(); | |||||
| commandLineThread.start(); | |||||
| } | |||||
| public static synchronized void stop() { | |||||
| commandLineThread.stopCommandLine(); | |||||
| System.out.println("Beende YoshiBot ..."); | |||||
| jdaBuilder.setStatus(OnlineStatus.OFFLINE); | |||||
| jda.shutdown(); | |||||
| System.out.println("YoshiBot offline."); | |||||
| } | |||||
| /** | |||||
| * Leitet den Context an den CommandDistributor weiter. | |||||
| * @param command Der Kontext für das Kommando. | |||||
| */ | |||||
| public static void executeCommand(YoshiCommandContext command) { | |||||
| YoshiCommandDistributor.distribute(command); | |||||
| } | |||||
| } | |||||
| @ -0,0 +1,5 @@ | |||||
| distributionBase=GRADLE_USER_HOME | |||||
| distributionPath=wrapper/dists | |||||
| distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip | |||||
| zipStoreBase=GRADLE_USER_HOME | |||||
| zipStorePath=wrapper/dists | |||||
| @ -0,0 +1,185 @@ | |||||
| #!/usr/bin/env sh | |||||
| # | |||||
| # Copyright 2015 the original author or authors. | |||||
| # | |||||
| # Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| # you may not use this file except in compliance with the License. | |||||
| # You may obtain a copy of the License at | |||||
| # | |||||
| # https://www.apache.org/licenses/LICENSE-2.0 | |||||
| # | |||||
| # Unless required by applicable law or agreed to in writing, software | |||||
| # distributed under the License is distributed on an "AS IS" BASIS, | |||||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| # See the License for the specific language governing permissions and | |||||
| # limitations under the License. | |||||
| # | |||||
| ############################################################################## | |||||
| ## | |||||
| ## Gradle start up script for UN*X | |||||
| ## | |||||
| ############################################################################## | |||||
| # Attempt to set APP_HOME | |||||
| # Resolve links: $0 may be a link | |||||
| PRG="$0" | |||||
| # Need this for relative symlinks. | |||||
| while [ -h "$PRG" ] ; do | |||||
| ls=`ls -ld "$PRG"` | |||||
| link=`expr "$ls" : '.*-> \(.*\)$'` | |||||
| if expr "$link" : '/.*' > /dev/null; then | |||||
| PRG="$link" | |||||
| else | |||||
| PRG=`dirname "$PRG"`"/$link" | |||||
| fi | |||||
| done | |||||
| SAVED="`pwd`" | |||||
| cd "`dirname \"$PRG\"`/" >/dev/null | |||||
| APP_HOME="`pwd -P`" | |||||
| cd "$SAVED" >/dev/null | |||||
| APP_NAME="Gradle" | |||||
| APP_BASE_NAME=`basename "$0"` | |||||
| # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | |||||
| DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' | |||||
| # Use the maximum available, or set MAX_FD != -1 to use that value. | |||||
| MAX_FD="maximum" | |||||
| warn () { | |||||
| echo "$*" | |||||
| } | |||||
| die () { | |||||
| echo | |||||
| echo "$*" | |||||
| echo | |||||
| exit 1 | |||||
| } | |||||
| # OS specific support (must be 'true' or 'false'). | |||||
| cygwin=false | |||||
| msys=false | |||||
| darwin=false | |||||
| nonstop=false | |||||
| case "`uname`" in | |||||
| CYGWIN* ) | |||||
| cygwin=true | |||||
| ;; | |||||
| Darwin* ) | |||||
| darwin=true | |||||
| ;; | |||||
| MINGW* ) | |||||
| msys=true | |||||
| ;; | |||||
| NONSTOP* ) | |||||
| nonstop=true | |||||
| ;; | |||||
| esac | |||||
| CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar | |||||
| # Determine the Java command to use to start the JVM. | |||||
| if [ -n "$JAVA_HOME" ] ; then | |||||
| if [ -x "$JAVA_HOME/jre/sh/java" ] ; then | |||||
| # IBM's JDK on AIX uses strange locations for the executables | |||||
| JAVACMD="$JAVA_HOME/jre/sh/java" | |||||
| else | |||||
| JAVACMD="$JAVA_HOME/bin/java" | |||||
| fi | |||||
| if [ ! -x "$JAVACMD" ] ; then | |||||
| die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME | |||||
| Please set the JAVA_HOME variable in your environment to match the | |||||
| location of your Java installation." | |||||
| fi | |||||
| else | |||||
| JAVACMD="java" | |||||
| which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. | |||||
| Please set the JAVA_HOME variable in your environment to match the | |||||
| location of your Java installation." | |||||
| fi | |||||
| # Increase the maximum file descriptors if we can. | |||||
| if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then | |||||
| MAX_FD_LIMIT=`ulimit -H -n` | |||||
| if [ $? -eq 0 ] ; then | |||||
| if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then | |||||
| MAX_FD="$MAX_FD_LIMIT" | |||||
| fi | |||||
| ulimit -n $MAX_FD | |||||
| if [ $? -ne 0 ] ; then | |||||
| warn "Could not set maximum file descriptor limit: $MAX_FD" | |||||
| fi | |||||
| else | |||||
| warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" | |||||
| fi | |||||
| fi | |||||
| # For Darwin, add options to specify how the application appears in the dock | |||||
| if $darwin; then | |||||
| GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" | |||||
| fi | |||||
| # For Cygwin or MSYS, switch paths to Windows format before running java | |||||
| if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then | |||||
| APP_HOME=`cygpath --path --mixed "$APP_HOME"` | |||||
| CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` | |||||
| JAVACMD=`cygpath --unix "$JAVACMD"` | |||||
| # We build the pattern for arguments to be converted via cygpath | |||||
| ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` | |||||
| SEP="" | |||||
| for dir in $ROOTDIRSRAW ; do | |||||
| ROOTDIRS="$ROOTDIRS$SEP$dir" | |||||
| SEP="|" | |||||
| done | |||||
| OURCYGPATTERN="(^($ROOTDIRS))" | |||||
| # Add a user-defined pattern to the cygpath arguments | |||||
| if [ "$GRADLE_CYGPATTERN" != "" ] ; then | |||||
| OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" | |||||
| fi | |||||
| # Now convert the arguments - kludge to limit ourselves to /bin/sh | |||||
| i=0 | |||||
| for arg in "$@" ; do | |||||
| CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` | |||||
| CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option | |||||
| if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition | |||||
| eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` | |||||
| else | |||||
| eval `echo args$i`="\"$arg\"" | |||||
| fi | |||||
| i=`expr $i + 1` | |||||
| done | |||||
| case $i in | |||||
| 0) set -- ;; | |||||
| 1) set -- "$args0" ;; | |||||
| 2) set -- "$args0" "$args1" ;; | |||||
| 3) set -- "$args0" "$args1" "$args2" ;; | |||||
| 4) set -- "$args0" "$args1" "$args2" "$args3" ;; | |||||
| 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; | |||||
| 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; | |||||
| 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; | |||||
| 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; | |||||
| 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; | |||||
| esac | |||||
| fi | |||||
| # Escape application args | |||||
| save () { | |||||
| for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done | |||||
| echo " " | |||||
| } | |||||
| APP_ARGS=`save "$@"` | |||||
| # Collect all arguments for the java command, following the shell quoting and substitution rules | |||||
| eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" | |||||
| exec "$JAVACMD" "$@" | |||||
| @ -0,0 +1,89 @@ | |||||
| @rem | |||||
| @rem Copyright 2015 the original author or authors. | |||||
| @rem | |||||
| @rem Licensed under the Apache License, Version 2.0 (the "License"); | |||||
| @rem you may not use this file except in compliance with the License. | |||||
| @rem You may obtain a copy of the License at | |||||
| @rem | |||||
| @rem https://www.apache.org/licenses/LICENSE-2.0 | |||||
| @rem | |||||
| @rem Unless required by applicable law or agreed to in writing, software | |||||
| @rem distributed under the License is distributed on an "AS IS" BASIS, | |||||
| @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
| @rem See the License for the specific language governing permissions and | |||||
| @rem limitations under the License. | |||||
| @rem | |||||
| @if "%DEBUG%" == "" @echo off | |||||
| @rem ########################################################################## | |||||
| @rem | |||||
| @rem Gradle startup script for Windows | |||||
| @rem | |||||
| @rem ########################################################################## | |||||
| @rem Set local scope for the variables with windows NT shell | |||||
| if "%OS%"=="Windows_NT" setlocal | |||||
| set DIRNAME=%~dp0 | |||||
| if "%DIRNAME%" == "" set DIRNAME=. | |||||
| set APP_BASE_NAME=%~n0 | |||||
| set APP_HOME=%DIRNAME% | |||||
| @rem Resolve any "." and ".." in APP_HOME to make it shorter. | |||||
| for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi | |||||
| @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. | |||||
| set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" | |||||
| @rem Find java.exe | |||||
| if defined JAVA_HOME goto findJavaFromJavaHome | |||||
| set JAVA_EXE=java.exe | |||||
| %JAVA_EXE% -version >NUL 2>&1 | |||||
| if "%ERRORLEVEL%" == "0" goto execute | |||||
| echo. | |||||
| echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. | |||||
| echo. | |||||
| echo Please set the JAVA_HOME variable in your environment to match the | |||||
| echo location of your Java installation. | |||||
| goto fail | |||||
| :findJavaFromJavaHome | |||||
| set JAVA_HOME=%JAVA_HOME:"=% | |||||
| set JAVA_EXE=%JAVA_HOME%/bin/java.exe | |||||
| if exist "%JAVA_EXE%" goto execute | |||||
| echo. | |||||
| echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% | |||||
| echo. | |||||
| echo Please set the JAVA_HOME variable in your environment to match the | |||||
| echo location of your Java installation. | |||||
| goto fail | |||||
| :execute | |||||
| @rem Setup the command line | |||||
| set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar | |||||
| @rem Execute Gradle | |||||
| "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* | |||||
| :end | |||||
| @rem End local scope for the variables with windows NT shell | |||||
| if "%ERRORLEVEL%"=="0" goto mainEnd | |||||
| :fail | |||||
| rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of | |||||
| rem the _cmd.exe /c_ return code! | |||||
| if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 | |||||
| exit /b 1 | |||||
| :mainEnd | |||||
| if "%OS%"=="Windows_NT" endlocal | |||||
| :omega | |||||
| @ -0,0 +1,11 @@ | |||||
| /* | |||||
| * This file was generated by the Gradle 'init' task. | |||||
| * | |||||
| * The settings file is used to specify which projects to include in your build. | |||||
| * | |||||
| * Detailed information about configuring a multi-project build in Gradle can be found | |||||
| * in the user manual at https://docs.gradle.org/6.8.3/userguide/multi_project_builds.html | |||||
| */ | |||||
| rootProject.name = 'YoshiBot' | |||||
| include('app') | |||||