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