@ -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') |