diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..8ba28af
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,31 @@
+# Log file
+*.log
+
+# IDE settings
+.classpath
+.project
+.vscode
+out/*
+target/*
+build/*
+
+# theia settings
+.theia/
+**/.settings/org.eclipse.buildship.core.prefs
+
+# IntelliJ files
+**/*.iml
+**/.idea
+**/.gradle
+*.ipr
+*.iws
+**/out/
+
+# gradle
+**/build/
+!gradle/wrapper/*.jar
+
+# Ignore Mac DS_Store files
+**/.DS_Store
+/bin/
+config.json
diff --git a/.settings/net.sf.jautodoc.prefs b/.settings/net.sf.jautodoc.prefs
new file mode 100644
index 0000000..25bc25d
--- /dev/null
+++ b/.settings/net.sf.jautodoc.prefs
@@ -0,0 +1,6 @@
+eclipse.preferences.version=1
+header_text=/*\r\n * The MIT License (MIT)\r\n *\r\n * Copyright (c) 2020 ROMVoid\r\n *\r\n * Permission is hereby granted, free of charge, to any person obtaining a copy of\r\n * this software and associated documentation files (the "Software"), to deal in\r\n * the Software without restriction, including without limitation the rights to\r\n * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\r\n * the Software, and to permit persons to whom the Software is furnished to do so,\r\n * subject to the following conditions\:\r\n *\r\n * The above copyright notice and this permission notice shall be included in all\r\n * copies or substantial portions of the Software.\r\n *\r\n * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\r\n * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\r\n * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r\n * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r\n * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r\n */
+mode=mode_replace
+project_specific_settings=true
+replace_header=true
+replacements=\n\n\nGets the\nSets the\nAdds the\nEdits the\nRemoves the\nInits the\nParses the\nCreates the\nBuilds the\nChecks if is\nPrints the\nChecks for\n\n\n
diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000..81c6189
--- /dev/null
+++ b/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding//src/main/java/net/romvoid/crashbot/utilities/HasteUtil.java=UTF-8
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..f5c99a7
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1 @@
+language: java
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..e2086d5
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2020 ROMVoid
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..9abdd30
--- /dev/null
+++ b/README.md
@@ -0,0 +1,41 @@
+## Overview
+CrashBot waits for users to upload files to discord and takes the file, and uploads said file to a hastebin style website so Moderators, helpers or anyone else can easily read the logs instead of having to download the logs every time one is posted.
+
+## Building
+
+### Prerequisite:
+You will need the following to utilize all of features:
+* JDK (8)
+* Eclipse or Java supported IDE
+
+**We will not provide any support whatsoever in obtaining any of the above.**
+
+Please do note that you will not receive any help whatsoever while trying to build your own, your expected to know what your doing.
+
+1. Make sure you have the prerequisites installed and running (Make sure your Java version is up-to-date)
+2. Clone this repository (you can also fork this repo and clone your fork).
+3. Open a Terminal in the root folder.
+4. Run `gradlew build`
+5. Grab the jar from `build/libs`
+6. Run the bot
+7. On first run the terminal will ask for a bot-token (Again your excected to know how to obtain one)
+
+
+## Release Notes
+Can be found in [RELEASE_NOTES](RELEASE_NOTES.md).
+
+## Authors
+* ROMVoid95 - [ROM](https://github.com/ROMVoid95)
+
+## Acknowledgments
+* [JDA by DV8FromTheWorld and MinnDevelopment](https://github.com/DV8FromTheWorld/JDA)
+
+
+## Contributing
+Please, follow [Contributing](CONTRIBUTING.md) page.
+
+## Code of Conduct
+Please, follow [Code of Conduct](CODE_OF_CONDUCT.md) page.
+
+## License
+This project is licensed under **MIT** - see the [LICENSE](LICENSE) file for details
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
new file mode 100644
index 0000000..f031db8
--- /dev/null
+++ b/RELEASE_NOTES.md
@@ -0,0 +1,2 @@
+## 1.0.0
+* First Initial Release
\ No newline at end of file
diff --git a/_config.yml b/_config.yml
new file mode 100644
index 0000000..c419263
--- /dev/null
+++ b/_config.yml
@@ -0,0 +1 @@
+theme: jekyll-theme-cayman
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..f2fc6ab
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,91 @@
+plugins {
+ id 'application'
+ id 'java'
+ id "com.github.ben-manes.versions" version "0.28.0"
+ id "com.github.johnrengelman.shadow" version "5.2.0"
+ }
+
+mainClassName = 'net.romvoid.crashbot.Bot'
+group = 'net.romvoid.crashbot'
+version = '3.0.0'
+
+description = 'A Minecraft logs Discord Bot'
+
+configurations {
+ compile
+}
+
+repositories {
+ maven {
+ url "https://jitpack.io"
+ }
+ jcenter()
+}
+
+dependencies {
+ compile ('net.dv8tion:JDA:4.1.1_164'){
+ transitive = true
+ exclude module: 'opus-java'
+ }
+ //compile group: '', name: '', version: ''
+
+ compile group: 'com.jagrosh', name: 'jda-utilities', version: '3.0.4'
+
+ // https://mvnrepository.com/artifact/commons-io/commons-io
+ compile group: 'commons-io', name: 'commons-io', version: '2.7'
+
+ // https://mvnrepository.com/artifact/org.apache.commons/commons-lang3
+ compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.11'
+
+ // https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient
+ compile group: 'org.apache.httpcomponents', name: 'httpclient', version: '4.5.12'
+
+ // https://mvnrepository.com/artifact/com.google.code.gson/gson
+ compile group: 'com.google.code.gson', name: 'gson', version: '2.8.6'
+
+ // https://mvnrepository.com/artifact/org.json/json
+ compile group: 'org.json', name: 'json', version: '20200518'
+
+ // https://mvnrepository.com/artifact/org.slf4j/slf4j-api
+ compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.30'
+
+ // https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
+ compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
+
+ // https://bintray.com/minndevelopment/maven/discord-webhooks
+ compile group: 'club.minnced', name: 'discord-webhooks', version: '0.3.1'
+
+ // https://search.maven.org/artifact/com.github.enerccio/gson-utilities/1.1.0/jar
+ compile group: 'com.github.enerccio', name: 'gson-utilities', version: '1.1.0'
+
+ // https://mvnrepository.com/artifact/org.jsoup/jsoup
+ compile group: 'org.jsoup', name: 'jsoup', version: '1.13.1'
+
+ // https://search.maven.org/artifact/org.junit.jupiter/junit-jupiter-engine
+ testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.7.0-M1'
+
+ compile(group: 'org.kohsuke', name: 'github-api', version: '1.95'){
+ exclude module: 'okhttp'
+ }
+
+ compile 'com.github.ROMVoid95:ReadOnlyCfg:1.0.2'
+
+}
+
+shadowJar { task ->
+ baseName = task.project.name
+}
+
+jar {
+
+ configurations.compile.each { dep ->
+ from(project.zipTree(dep)) {
+ exclude 'META-INF', 'META-INF/**'
+ }
+ }
+ manifest {
+ attributes(
+ 'Main-Class': mainClassName
+ )
+ }
+}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..cc4fdc2
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..4e4abad
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.0-bin.zip
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStorePath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..af6708f
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## 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"'
+
+# 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, switch paths to Windows format before running java
+if $cygwin ; 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=$((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"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..6d57edc
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,84 @@
+@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 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"
+
+@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 init
+
+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 init
+
+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
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+: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 %CMD_LINE_ARGS%
+
+: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
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..b6256e0
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+rootProject.name = 'CrashBot'
\ No newline at end of file
diff --git a/solutions/solutions.json b/solutions/solutions.json
new file mode 100644
index 0000000..7a096b4
--- /dev/null
+++ b/solutions/solutions.json
@@ -0,0 +1,22 @@
+[
+ {
+ "keys": [
+ "Galacticraft-Planets-Mod-1.12",
+ "MicdoodleCore-1.12"
+ ],
+ "reply": "You seem it have downloaded Galacticraft from an invalid site, please download from http://micdoodle8.com/mods/galacticraft/downloads"
+ },
+ {
+ "keys": [
+ "Galacticraft-Mod-1.12",
+ "MicdoodleCore-1.12"
+ ],
+ "reply": "You seem it have downloaded Galacticraft from an invalid site, please download from http://micdoodle8.com/mods/galacticraft/downloads"
+ },
+ {
+ "keys": [
+ "You have a double version of the same space station, station for planet ID: -31"
+ ],
+ "reply": "Disable the Venus Space Station in ExtraPlanets.cfg file or Galaxyspace/dimensions.cfg file."
+ }
+]
\ No newline at end of file
diff --git a/src/main/java/net/romvoid/crashbot/Bot.java b/src/main/java/net/romvoid/crashbot/Bot.java
new file mode 100644
index 0000000..9d42ec5
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/Bot.java
@@ -0,0 +1,190 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 ROMVoid
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package net.romvoid.crashbot;
+
+import static net.dv8tion.jda.api.requests.GatewayIntent.*;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.security.auth.login.LoginException;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.jagrosh.jdautilities.command.CommandClientBuilder;
+import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
+import com.jagrosh.jdautilities.examples.command.GuildlistCommand;
+
+import net.dv8tion.jda.api.JDABuilder;
+import net.dv8tion.jda.api.OnlineStatus;
+import net.dv8tion.jda.api.entities.Activity;
+import net.dv8tion.jda.api.requests.GatewayIntent;
+import net.dv8tion.jda.api.utils.cache.CacheFlag;
+import net.dv8tion.jda.internal.JDAImpl;
+import net.romvoid.crashbot.commands.InviteCommand;
+import net.romvoid.crashbot.config.Configuration;
+import net.romvoid.crashbot.config.Setup;
+
+/**
+ * The Main Bot Class.
+ */
+public class Bot {
+
+ private static Bot instance;
+ private JDAImpl jda;
+ private static final SimpleDateFormat timeStampFormatter = new SimpleDateFormat("MM.dd.yyyy HH:mm:ss");
+ /** The Constant CONFIG_KEYS. */
+ private static final String[] CONFIG_KEYS = { "token", "prefix" };
+
+ /** The configuration. */
+ private final Configuration configuration;
+
+ private static EventWaiter waiter;
+
+ public static CommandClientBuilder client;
+
+ /** The prefix. */
+ private static String prefix;
+
+ private static Set intents = new HashSet<>();
+
+ public static final Logger LOG = LoggerFactory.getLogger(Bot.class);
+
+
+ /**
+ * Instantiates a new bot.
+ */
+ private Bot() {
+ instance = this;
+ configuration = new Configuration(new File("config.json"));
+ for (String configKey : CONFIG_KEYS) {
+ if (!configuration.has(configKey)) {
+ String input = Setup.prompt(configKey);
+ configuration.set(configKey, input);
+ }
+ }
+ prefix = instance.configuration.getString("prefix");
+ setIntents();
+ client = new CommandClientBuilder();
+ waiter = new EventWaiter();
+ client.setOwnerId("626123188849475584");
+ client.setEmojis("\uD83D\uDE03", "\uD83D\uDE2E", "\uD83D\uDE26");
+ client.setPrefix(prefix);
+
+ client.addCommands(new InviteCommand(), new GuildlistCommand(waiter));
+ initJDA();
+
+
+ }
+
+ /**
+ * The main method.
+ *
+ * @param args the arguments
+ */
+ public static void main(String[] args) {
+ if (instance != null)
+ throw new RuntimeException("CrashBot has already been initialized in this VM.");
+ new Bot();
+ }
+
+ /**
+ * Initiates the JDA Instance and builder.
+ */
+ public static void initJDA() {
+ if (instance == null)
+ throw new NullPointerException("CrashBot has not been initialized yet.");
+
+ try {
+ instance.jda = (JDAImpl) JDABuilder.create(instance.configuration.getString("token"), intents)
+ .setStatus(OnlineStatus.DO_NOT_DISTURB)
+ .setActivity(Activity.playing("Loading...").asRichPresence())
+ .addEventListeners(waiter, client.build(), new MessageListener())
+ .disableCache(CacheFlag.VOICE_STATE, CacheFlag.EMOTE).build();
+ } catch (LoginException e) {
+ e.printStackTrace();
+ }
+ getJDA().getPresence().setStatus(OnlineStatus.ONLINE);
+ getJDA().getPresence().setActivity(Activity.playing("MFND.ru"));
+ }
+
+ private void setIntents() {
+ intents.add(GUILD_MESSAGES);
+ intents.add(GUILD_MEMBERS);
+ intents.add(GUILD_PRESENCES);
+ }
+
+ /**
+ * Gets the single instance of Bot.
+ *
+ * @return single instance of Bot
+ */
+ public static Bot getInstance() {
+ if (instance == null)
+ throw new IllegalStateException("Bot has not been initialised. Please use Bot#init() to create the bot");
+ return instance;
+ }
+
+ /**
+ * Gets the prefix.
+ *
+ * @return the prefix
+ */
+ public String getPrefix() {
+ return prefix;
+ }
+
+ /**
+ * Gets the configuration.
+ *
+ * @return the configuration
+ */
+ public static Configuration getConfiguration() {
+ return instance == null ? null : instance.configuration;
+ }
+
+ /**
+ * Gets the JDA Instance.
+ *
+ * @return the JDA Instance
+ */
+ public static JDAImpl getJDA() {
+ return instance == null ? null : instance.jda;
+ }
+
+ /**
+ * @return a freshly generated timestamp in the 'dd.MM.yyyy HH:mm:ss' format.
+ */
+ public static String getNewTimestamp() {
+ return timeStampFormatter.format(new Date());
+ }
+
+ public static void logMsg(String messageId, String conent) {
+ String msg = "[" + messageId + "] " + conent;
+ Bot.LOG.info(msg);
+ }
+}
diff --git a/src/main/java/net/romvoid/crashbot/MessageListener.java b/src/main/java/net/romvoid/crashbot/MessageListener.java
new file mode 100644
index 0000000..bb5dc9c
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/MessageListener.java
@@ -0,0 +1,65 @@
+package net.romvoid.crashbot;
+
+import java.util.Arrays;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import net.dv8tion.jda.api.hooks.ListenerAdapter;
+import net.romvoid.crashbot.file.FileHandler;
+import net.romvoid.crashbot.file.Patterns;
+
+public class MessageListener extends ListenerAdapter {
+
+ private String[] pasteSites = { "pastebin.com", "paste.ubuntu.com", "pastebin.ubuntu.com", "ghostbin.co",
+ "paste.ee", "paste.dimdev.org" };
+ private String[] special = { "pastebin.ubuntu.com", "paste.ubuntu.com" };
+ private String url;
+ private boolean foundUrl;
+ private Matcher matcher;
+ private boolean isPasteURL;
+ private boolean isUbuntuURL;
+ private Matcher urlSplit;
+
+ @Override
+ public void onMessageReceived(MessageReceivedEvent event) {
+ if(event.getAuthor().isBot()) {return;}
+ if (!event.getMessage().getAttachments().isEmpty()) {
+ new FileHandler(event);
+ return;
+ }
+ if (event.getMessage().getAttachments().isEmpty()) {
+ matcher = Patterns.Url.PASTEURL.matcher(event.getMessage().getContentStripped());
+ foundUrl = matcher.find();
+ System.out.println("Found URL: " + foundUrl);
+ if (foundUrl) {
+ url = matcher.group(0);
+ isPasteURL = isPasteSite(url);
+ System.out.println("is PasteSite: " + isPasteURL);
+ if (isPasteURL) {
+ Pattern pattern = Pattern.compile(Patterns.Url.DIVIDEURL);
+ urlSplit = pattern.matcher(url);
+ if (urlSplit.find())
+ isUbuntuURL = isSpecialPasteSite(urlSplit.group(1));
+ System.out.println("is UbuntuPaste: " + isUbuntuURL);
+ if (isUbuntuURL) {
+ new FileHandler(event, url);
+ return;
+ } else {
+ new FileHandler(event, urlSplit.group(1), urlSplit.group(2));
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ private boolean isSpecialPasteSite(String message) {
+ return Arrays.stream(special).parallel().anyMatch(message::contains);
+ }
+
+ private boolean isPasteSite(String message) {
+ return Arrays.stream(pasteSites).parallel().anyMatch(message::contains);
+ }
+
+}
diff --git a/src/main/java/net/romvoid/crashbot/commands/Cmd.java b/src/main/java/net/romvoid/crashbot/commands/Cmd.java
new file mode 100644
index 0000000..b4a2de3
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/commands/Cmd.java
@@ -0,0 +1,16 @@
+package net.romvoid.crashbot.commands;
+
+import com.jagrosh.jdautilities.command.Command;
+import com.jagrosh.jdautilities.command.CommandEvent;
+
+import net.romvoid.crashbot.Bot;
+
+public abstract class Cmd extends Command {
+
+ protected abstract void execute(CommandEvent event);
+
+ void log(CommandEvent event) {
+ Bot.LOG.info(event.getAuthor().getName() + " invoked command " + this.name);
+ }
+
+}
diff --git a/src/main/java/net/romvoid/crashbot/commands/GuildsCommand.java b/src/main/java/net/romvoid/crashbot/commands/GuildsCommand.java
new file mode 100644
index 0000000..1895796
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/commands/GuildsCommand.java
@@ -0,0 +1,67 @@
+package net.romvoid.crashbot.commands;
+
+import java.awt.Color;
+import java.util.concurrent.TimeUnit;
+
+import com.jagrosh.jdautilities.command.Command;
+import com.jagrosh.jdautilities.command.CommandEvent;
+import com.jagrosh.jdautilities.commons.waiter.EventWaiter;
+import com.jagrosh.jdautilities.doc.standard.CommandInfo;
+import com.jagrosh.jdautilities.doc.standard.Error;
+import com.jagrosh.jdautilities.menu.Paginator;
+
+import net.dv8tion.jda.api.entities.ChannelType;
+import net.dv8tion.jda.api.exceptions.PermissionException;
+import net.romvoid.crashbot.Bot;
+
+@CommandInfo(name = "guilds", description = "Gets a paginated list of the guilds the bot is on.", requirements = {
+ "The bot has all necessary permissions.", "The user is the bot's owner." })
+@Error(value = "If arguments are provided, but they are not an integer.", response = "[PageNumber] is not a valid integer!")
+public class GuildsCommand extends Command {
+
+ private final Paginator.Builder pbuilder;
+
+ public GuildsCommand(EventWaiter waiter) {
+ this.name = "guilds";
+ this.help = "shows the list of guilds the bot is on";
+ this.arguments = "[pagenum]";
+ this.guildOnly = false;
+ this.ownerCommand = true;
+ pbuilder = new Paginator.Builder().setColumns(1).setItemsPerPage(10).showPageNumbers(true)
+ .waitOnSinglePage(false).useNumberedItems(false).setFinalAction(m -> {
+ try {
+ m.clearReactions().queue();
+ } catch (PermissionException ex) {
+ m.delete().queue();
+ }
+ }).setEventWaiter(waiter).setTimeout(1, TimeUnit.MINUTES);
+ }
+
+ @Override
+ protected void execute(CommandEvent event) {
+ Bot.LOG.info(event.getAuthor().getName() + " invoked command " + this.name);
+ int page = 1;
+ if (!event.getArgs().isEmpty()) {
+ try {
+ page = Integer.parseInt(event.getArgs());
+ } catch (NumberFormatException e) {
+ event.reply(event.getClient().getError() + " `" + event.getArgs() + "` is not a valid integer!");
+ return;
+ }
+ }
+ pbuilder.clearItems();
+ event.getJDA().getGuilds().stream()
+ .map(g -> "**" + g.getName() + "** (ID:" + g.getId() + ") ~ " + g.getMembers().size() + " Members")
+ .forEach(pbuilder::addItems);
+ Paginator p = pbuilder
+ .setColor(event.isFromType(ChannelType.TEXT) ? event.getSelfMember().getColor() : Color.black)
+ .setText(event.getClient().getSuccess() + " Guilds that **" + event.getSelfUser().getName()
+ + "** is connected to"
+ + (event.getJDA().getShardInfo() == null ? ":"
+ : "(Shard ID " + event.getJDA().getShardInfo().getShardId() + "):"))
+ .setUsers(event.getAuthor()).build();
+ p.paginate(event.getChannel(), page);
+ event.getMessage().delete().queue();
+ }
+
+}
diff --git a/src/main/java/net/romvoid/crashbot/commands/InviteCommand.java b/src/main/java/net/romvoid/crashbot/commands/InviteCommand.java
new file mode 100644
index 0000000..34763ee
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/commands/InviteCommand.java
@@ -0,0 +1,50 @@
+package net.romvoid.crashbot.commands;
+
+import java.util.concurrent.TimeUnit;
+
+import com.jagrosh.jdautilities.command.Command;
+import com.jagrosh.jdautilities.command.CommandEvent;
+
+import net.dv8tion.jda.api.EmbedBuilder;
+import net.dv8tion.jda.api.entities.Message;
+import net.dv8tion.jda.api.entities.MessageChannel;
+import net.romvoid.crashbot.Bot;
+import net.romvoid.crashbot.utilities.EmbedUtil;
+
+public class InviteCommand extends Command {
+
+ public InviteCommand() {
+ this.name = "invite";
+ this.help = "Generates an invite link for the bot";
+ this.guildOnly = false;
+ }
+
+ @Override
+ protected void execute(CommandEvent event) {
+ Bot.LOG.info(event.getAuthor().getName() + " invoked command " + this.name);
+ MessageChannel channel = event.getChannel();
+ String inviteURL = Bot.getJDA().getInviteUrl() + "&permissions=388177";
+ if (event.getArgs().isEmpty()) {
+ EmbedBuilder embed = EmbedUtil.embed("Invite Me To Your Server",
+ "Want to use this bot in your server also? No problem! heres your invite link");
+ embed.addField("Invite Link", "[Invite Me](" + inviteURL + ")", false);
+ Message msg = EmbedUtil.message(embed);
+ EmbedUtil.sendAndDeleteOnGuilds(channel, msg, 2, TimeUnit.MINUTES);
+ } else {
+ String[] args = event.getArgs().split("\\s+");
+ if (args.length >= 2) {
+ event.replyWarning("You can only define 1 serverId per command!");
+ } else {
+ String specialInvite = inviteURL + "&guild_id=" + args[0];
+ EmbedBuilder embed = EmbedUtil.embed("Invite Me To Your Server",
+ "Want to use this bot in your server also? No problem! heres your invite link");
+ embed.addField("Here is your direct link to invite the bot", "[Invite Me](" + specialInvite + ")",
+ false);
+ Message msg = EmbedUtil.message(embed);
+ EmbedUtil.sendAndDeleteOnGuilds(channel, msg, 2, TimeUnit.MINUTES);
+ }
+ }
+ event.getMessage().delete().queue();
+ }
+
+}
diff --git a/src/main/java/net/romvoid/crashbot/config/Configuration.java b/src/main/java/net/romvoid/crashbot/config/Configuration.java
new file mode 100644
index 0000000..6b28491
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/config/Configuration.java
@@ -0,0 +1,189 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 ROMVoid
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package net.romvoid.crashbot.config;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+
+public class Configuration {
+
+ private File file;
+ private JsonObject json;
+ public JsonParser jsonParser;
+ private Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ public Configuration(final File file) {
+
+ this.file = file;
+ String cont = null;
+
+ try {
+ if (file.exists()) {
+ BufferedReader reader = new BufferedReader(new FileReader(file));
+ cont = reader.lines().collect(Collectors.joining("\n"));
+ reader.close();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ if (cont == null || cont.equals("")) {
+ cont = "{}";
+ }
+ json = JsonParser.parseString(cont).getAsJsonObject();
+
+ }
+
+ /**
+ * @param key
+ * @param val
+ * @description Sets tha value of a key in config
+ */
+ public Configuration set(final String key, final String val) {
+ if (json.has(key)) {
+ json.remove(key);
+ }
+ if (val != null) {
+ json.addProperty(key, val);
+ }
+ return this.save();
+ }
+
+ /**
+ * @param key
+ * @param val
+ * @description Sets the value of a key in config
+ */
+ public Configuration set(final String key, final int val) {
+ if (json.has(key)) {
+ json.remove(key);
+ }
+ this.json.addProperty(key, val);
+ return this.save();
+ }
+
+ /**
+ * @param key
+ * @description Removes key from config
+ */
+ public Configuration unset(final String key) {
+ if (json.has(key))
+ json.remove(key);
+
+ return this.save();
+ }
+
+ /**
+ * @description Saves the config
+ */
+ private Configuration save() {
+ try {
+ if (json.entrySet().size() == 0) {
+ if (file.exists()) {
+ file.delete();
+ }
+ } else {
+ if (!file.exists()) {
+ file.createNewFile();
+ }
+
+ BufferedWriter br = new BufferedWriter(new FileWriter(file));
+ String jsonOut = gson.toJson(json);
+ br.write(jsonOut);
+ br.close();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return this;
+ }
+
+ /**
+ * @param key
+ * @return Value of key in config as string
+ */
+ public String getString(final String key) {
+ try {
+ return json.get(key).getAsString();
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+
+ /**
+ * @param key
+ * @return Value of key in config as integer
+ */
+ public int getInt(final String key) {
+ if (json.has(key)) {
+ return json.get(key).getAsInt();
+ }
+ return 0;
+ }
+
+ /**
+ * @param key
+ * @return If key exists
+ */
+ public boolean has(final String key) {
+ try {
+ return json.has(key);
+ } catch (NullPointerException ex) {
+ return false;
+ }
+ }
+
+ public List keySet() {
+ List keys = new ArrayList<>();
+ Set> entries = json.entrySet();
+ for (Map.Entry entry : entries) {
+ keys.add(entry.getKey());
+ }
+ return keys;
+ }
+
+ public List values() {
+ List values = new ArrayList<>();
+ Set> entries = json.entrySet();
+ for (Map.Entry entry : entries) {
+ values.add(entry.getValue().getAsString());
+ }
+ return values;
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/romvoid/crashbot/config/InteralConfig.java b/src/main/java/net/romvoid/crashbot/config/InteralConfig.java
new file mode 100644
index 0000000..12a1498
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/config/InteralConfig.java
@@ -0,0 +1,13 @@
+package net.romvoid.crashbot.config;
+
+public class InteralConfig {
+
+ public String webhookUrl;
+
+ public InteralConfig() {}
+
+ public String getWebhookUrl() {
+ return this.webhookUrl;
+ }
+
+}
diff --git a/src/main/java/net/romvoid/crashbot/config/Setup.java b/src/main/java/net/romvoid/crashbot/config/Setup.java
new file mode 100644
index 0000000..729e1e9
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/config/Setup.java
@@ -0,0 +1,59 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 ROMVoid
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package net.romvoid.crashbot.config;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+public class Setup {
+
+ private static final BufferedReader sys_in;
+
+ static {
+ InputStreamReader isr = new InputStreamReader(System.in);
+ sys_in = new BufferedReader(isr);
+ }
+
+ public static String prompt(String req) {
+ String token;
+
+ // prompt for token
+ System.out.println("Enter The Bots " + req + ":");
+
+
+ try {
+ // read and trim line
+ String line = sys_in.readLine();
+ token = line.trim();
+
+ return token;
+ } catch (IOException e) {
+ System.err.println(e.getMessage());
+ }
+ // RAGEQUIT
+ System.out.println("Exiting");
+ Runtime.getRuntime().exit(1);
+ return null;
+ }
+}
diff --git a/src/main/java/net/romvoid/crashbot/config/StringUtil.java b/src/main/java/net/romvoid/crashbot/config/StringUtil.java
new file mode 100644
index 0000000..78a213b
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/config/StringUtil.java
@@ -0,0 +1,61 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 ROMVoid
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package net.romvoid.crashbot.config;
+
+public class StringUtil {
+ /**
+ * Check if String is numeric.
+ *
+ * @param str the String which should be tested.
+ * @return true if String is Numeric else false.
+ */
+ @SuppressWarnings("unused")
+ public static boolean isNumeric(String str) {
+ try {
+ double d = Double.parseDouble(str);
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Create Emoji out of Code points
+ *
+ * @param str the Code point.
+ * @return the Emoji as a String.
+ */
+ public static String emojiOutCodePoint(String str) {
+ int[] codepoints = {Integer.valueOf(str.replace("U+", "0x"))};
+ String s = new String(codepoints, 0, codepoints.length);
+ return s;
+ }
+
+ public static boolean nicknameHasBrackets(String nickname) {
+ if (nickname.contains("[") || nickname.contains("]")) {
+ return true;
+ }
+ return false;
+
+ }
+}
diff --git a/src/main/java/net/romvoid/crashbot/file/Embeds.java b/src/main/java/net/romvoid/crashbot/file/Embeds.java
new file mode 100644
index 0000000..8a11e9f
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/file/Embeds.java
@@ -0,0 +1,153 @@
+package net.romvoid.crashbot.file;
+
+import java.awt.Color;
+import java.net.URI;
+import java.time.Instant;
+
+import net.dv8tion.jda.api.EmbedBuilder;
+import net.dv8tion.jda.api.entities.Message;
+import net.dv8tion.jda.api.entities.TextChannel;
+
+public class Embeds {
+
+ private static final String SOLUTION_FOOTER = "Solutions are provided on a best-attempt basis and are not guaranteed";
+
+ /**
+ * Make crashlogWithSolutionEmbed.
+ *
+ * @param channel the channel
+ * @param message the message
+ * @param filename the filename
+ * @param url the URL
+ * @param fix the fix
+ * @return the embed builder
+ */
+ public static EmbedBuilder crashlogWithSolutionEmbed(TextChannel channel, Message message, String filename, URI url, String fix) {
+ String user = message.getAuthor().getName() + "#" + message.getAuthor().getDiscriminator();
+ EmbedBuilder embedBuilder = new EmbedBuilder();
+ embedBuilder.setColor(Color.GREEN);
+ embedBuilder.setTitle("Crash Report Utility");
+ embedBuilder.setDescription("`Links do not expire`");
+ embedBuilder.addField(user + "'s Crash Log", "[" + filename + "](" + url + ")", false);
+ embedBuilder.addField("Possible Fixes", "`" + fix + "`", false);
+ embedBuilder.setFooter(SOLUTION_FOOTER + "\nCrash-Log Service for " + channel.getGuild().getName());
+ embedBuilder.setTimestamp(Instant.now());
+ return embedBuilder;
+ }
+
+ /**
+ * Make crashlogOnlyEmbed.
+ *
+ * @param channel the channel
+ * @param message the message
+ * @param filename the filename
+ * @param url the URL
+ * @param fix the fix
+ * @return the embed builder
+ */
+ public static EmbedBuilder crashlogOnlyEmbed(TextChannel channel, Message message, String filename, URI url) {
+ String user = message.getAuthor().getName() + "#" + message.getAuthor().getDiscriminator();
+ EmbedBuilder embedBuilder = new EmbedBuilder();
+ embedBuilder.setColor(Color.cyan);
+ embedBuilder.setTitle("Crash Report Utility");
+ embedBuilder.setDescription("`Links do not expire`");
+ embedBuilder.addField(user + "'s Crash Log", "[" + filename + "](" + url + ")", false);
+ embedBuilder.setFooter("Crash-Log Service for " + channel.getGuild().getName());
+ embedBuilder.setTimestamp(Instant.now());
+ return embedBuilder;
+ }
+
+ /**
+ * Make crashlogNotSupportedEmbed.
+ *
+ * @param channel the channel
+ * @param message the message
+ * @param filename the filename
+ * @param url the URL
+ * @param version the version
+ * @return the embed builder
+ */
+ public static EmbedBuilder crashlogNotSupportedEmbed(TextChannel channel, Message message, String filename, URI url, String version) {
+ String user = message.getAuthor().getName() + "#" + message.getAuthor().getDiscriminator();
+ EmbedBuilder embedBuilder = new EmbedBuilder();
+ embedBuilder.setColor(Color.RED);
+ embedBuilder.setTitle("Crash Report Utility");
+ embedBuilder.setDescription("`Links do not expire`");
+ embedBuilder.addField(user + "'s Crash Log", "[" + filename + "](" + url + ")", false);
+ embedBuilder.addField("Version Not Supported","`" + version + "` is no longer supported."
+ + "\nYour URL is still provided in the case any community members may know a fix", false);
+ embedBuilder.setFooter(channel.getGuild().getName());
+ embedBuilder.setTimestamp(Instant.now());
+ return embedBuilder;
+ }
+
+ /**
+ * Make crashlogWithSolutionEmbed.
+ *
+ * @param channel the channel
+ * @param message the message
+ * @param filename the filename
+ * @param url the URL
+ * @param fix the fix
+ * @return the embed builder
+ */
+ public static EmbedBuilder crashlogWithSolutionEmbed(TextChannel channel, Message message, String url, String fix) {
+ String user = message.getAuthor().getName() + "#" + message.getAuthor().getDiscriminator();
+ EmbedBuilder embedBuilder = new EmbedBuilder();
+ embedBuilder.setColor(Color.GREEN);
+ embedBuilder.setTitle("Crash Report Utility");
+ embedBuilder.setDescription("`Links do not expire`");
+ embedBuilder.addField(user + "'s Crash Log", "[" + url + "](" + url + ")", false);
+ embedBuilder.addField("Possible Fixes", "`" + fix + "`", false);
+ embedBuilder.setFooter(SOLUTION_FOOTER + "\nCrash-Log Service for " + channel.getGuild().getName());
+ embedBuilder.setTimestamp(Instant.now());
+ return embedBuilder;
+ }
+
+ /**
+ * Make crashlogOnlyEmbed.
+ *
+ * @param channel the channel
+ * @param message the message
+ * @param filename the filename
+ * @param url the URL
+ * @param fix the fix
+ * @return the embed builder
+ */
+ public static EmbedBuilder crashlogOnlyEmbed(TextChannel channel, Message message, String url) {
+ String user = message.getAuthor().getName() + "#" + message.getAuthor().getDiscriminator();
+ EmbedBuilder embedBuilder = new EmbedBuilder();
+ embedBuilder.setColor(Color.cyan);
+ embedBuilder.setTitle("Crash Report Utility");
+ embedBuilder.setDescription("`Links do not expire`");
+ embedBuilder.addField(user + "'s Crash Log", "[" + url + "](" + url + ")", false);
+ embedBuilder.setFooter("Crash-Log Service for " + channel.getGuild().getName());
+ embedBuilder.setTimestamp(Instant.now());
+ return embedBuilder;
+ }
+
+ /**
+ * Make crashlogNotSupportedEmbed.
+ *
+ * @param channel the channel
+ * @param message the message
+ * @param filename the filename
+ * @param url the URL
+ * @param version the version
+ * @return the embed builder
+ */
+ public static EmbedBuilder crashlogNotSupportedEmbed(TextChannel channel, Message message, String url, String version) {
+ String user = message.getAuthor().getName() + "#" + message.getAuthor().getDiscriminator();
+ EmbedBuilder embedBuilder = new EmbedBuilder();
+ embedBuilder.setColor(Color.RED);
+ embedBuilder.setTitle("Crash Report Utility");
+ embedBuilder.setDescription("`Links do not expire`");
+ embedBuilder.addField(user + "'s Crash Log", "[" + url + "](" + url + ")", false);
+ embedBuilder.addField("Version Not Supported","`" + version + "` is no longer supported."
+ + "\nYour URL is still provided in the case any community members may know a fix", false);
+ embedBuilder.setFooter(channel.getGuild().getName());
+ embedBuilder.setTimestamp(Instant.now());
+ return embedBuilder;
+ }
+
+}
diff --git a/src/main/java/net/romvoid/crashbot/file/FileHandler.java b/src/main/java/net/romvoid/crashbot/file/FileHandler.java
new file mode 100644
index 0000000..d9c4ac9
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/file/FileHandler.java
@@ -0,0 +1,227 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 ROMVoid
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package net.romvoid.crashbot.file;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import net.dv8tion.jda.api.EmbedBuilder;
+import net.dv8tion.jda.api.entities.Message;
+import net.dv8tion.jda.api.entities.Message.Attachment;
+import net.dv8tion.jda.api.entities.TextChannel;
+import net.dv8tion.jda.api.events.message.MessageReceivedEvent;
+import net.romvoid.crashbot.Bot;
+import net.romvoid.crashbot.file.solution.FileUtil;
+import net.romvoid.crashbot.file.solution.Parser;
+import net.romvoid.crashbot.file.solution.Solution;
+import net.romvoid.crashbot.utilities.HasteUtil;
+
+/**
+ * The listener interface for receiving file events. The class that is
+ * interested in processing a file event implements this interface, and the
+ * object created with that class is registered with a component using the
+ * component's addFileListener method. When the file event occurs,
+ * that object's appropriate method is invoked.
+ *
+ * @see FileEvent
+ */
+public class FileHandler {
+ private static StringBuilder builder = new StringBuilder();
+ static final Map cache = new HashMap<>();
+ private MessageReceivedEvent event;
+ List list;
+ private String messageId;
+ private TextChannel channel;
+ private Message message;
+ private String name;
+ private String author;
+
+ public FileHandler(MessageReceivedEvent event) {
+ this.event = event;
+
+ this.list = new ArrayList(event.getMessage().getAttachments());
+ this.message = event.getMessage();
+ this.channel = event.getTextChannel();
+ this.messageId = event.getMessageId();
+ this.author = event.getAuthor().getAsTag();
+
+ this.handleFile();
+ }
+
+ public FileHandler(MessageReceivedEvent event, String url) {
+ this.event = event;
+ this.message = event.getMessage();
+ this.channel = event.getTextChannel();
+ this.messageId = event.getMessageId();
+ this.author = event.getAuthor().getAsTag();
+
+ this.getContentFromUbuntu(url, this.messageId);
+ }
+
+ public FileHandler(MessageReceivedEvent event, String url, String id) {
+ this.event = event;
+ this.message = event.getMessage();
+ this.channel = event.getTextChannel();
+ this.messageId = event.getMessageId();
+ this.author = event.getAuthor().getAsTag();
+
+ this.getContent(url, id , this.messageId);
+ }
+
+ /**
+ * On message received.
+ *
+ * @param event the onMessageReceived event
+ */
+ private void handleFile() {
+
+ list.forEach(file -> {
+ this.name = file.getFileName();
+ switch (file.getFileExtension()) {
+ case "txt":
+ if (FileUtil.matchToExt(Patterns.Filenames.CRASHLOG, file.getFileName())) {
+ Bot.logMsg(messageId, author + " submitted " + name);
+ getFileContent(file, messageId);
+ }
+ break;
+ case "log":
+ if (FileUtil.matchToExt(Patterns.Filenames.LOGS_TXT, file.getFileName())) {
+ Bot.logMsg(messageId, author + " submitted " + name);
+ getFileContent(file, messageId);
+ }
+ break;
+ }
+ });
+
+ }
+
+ public static void sendEmbed(TextChannel channel, EmbedBuilder embed) {
+ channel.sendMessage(embed.build()).queue();
+ }
+//
+// public static void sendPing(TextChannel channel, String asMention) {
+// channel.sendMessage(asMention).queue();
+// }
+
+ /**
+ * Gets the content of the log for processing to upload to hastebin
+ *
+ * @param event the onMessageReceived event
+ * @return the file content
+ */
+ private void getFileContent(Attachment doc, String messageId) {
+ doc.retrieveInputStream().thenAccept(in -> {
+ builder = new StringBuilder();
+ byte[] buf = new byte[1024];
+ int count = 0;
+ try {
+ while ((count = in.read(buf)) > 0) {
+ builder.append(new String(buf, 0, count));
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ try {
+ in.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ String msg = builder.toString();
+ HasteUtil.paste(msg);
+ Solution foundSolution = Parser.find(msg);
+ boolean unsupported = Parser.checkVersion(msg);
+ System.out.println("Supported: " + unsupported);
+ EmbedBuilder embed;
+
+ if(!unsupported) {
+ embed = Embeds.crashlogNotSupportedEmbed(channel, message, name, HasteUtil.getLink(), Parser.version);
+ sendEmbed(channel, embed);
+ } else {
+ if(foundSolution != null) {
+ embed = Embeds.crashlogWithSolutionEmbed(channel, message, name, HasteUtil.getLink(), foundSolution.getReply());
+ sendEmbed(channel, embed);
+ } else {
+ embed = Embeds.crashlogOnlyEmbed(channel, message, name, HasteUtil.getLink());
+ sendEmbed(channel, embed);
+ }
+ }
+ this.event.getMessage().delete().queue();
+ }).exceptionally(t -> { // handle failure
+ t.printStackTrace();
+ return null;
+ });
+ }
+
+ private void getContent(String url, String id, String messageId) {
+ String URLString = url + "raw/" + id + "/";
+ String msg = HasteUtil.getOtherPaste(URLString);
+ HasteUtil.paste(msg);
+ Solution foundSolution = Parser.find(msg);
+ boolean unsupported = Parser.checkVersion(msg);
+ System.out.println("Supported: " + unsupported);
+ EmbedBuilder embed;
+ String name = HasteUtil.getLink().toString();
+
+ if(!unsupported) {
+ embed = Embeds.crashlogNotSupportedEmbed(channel, message, name, Parser.version);
+ sendEmbed(channel, embed);
+ } else {
+ if(foundSolution != null) {
+ embed = Embeds.crashlogWithSolutionEmbed(channel, message, name, foundSolution.getReply());
+ sendEmbed(channel, embed);
+ } else {
+ embed = Embeds.crashlogOnlyEmbed(channel, message, name);
+ sendEmbed(channel, embed);
+ }
+ }
+ this.event.getMessage().delete().queue();
+ }
+
+ private void getContentFromUbuntu(String url, String messageId) {
+ String msg = HasteUtil.getCrashFromUbuntuPaste(url);
+ HasteUtil.paste(msg);
+ Solution foundSolution = Parser.find(msg);
+ boolean unsupported = Parser.checkVersion(msg);
+ System.out.println("Supported: " + unsupported);
+ EmbedBuilder embed;
+
+ String name = HasteUtil.getLink().toString();
+
+ if(!unsupported) {
+ embed = Embeds.crashlogNotSupportedEmbed(channel, message, name, Parser.version);
+ sendEmbed(channel, embed);
+ } else {
+ if(foundSolution != null) {
+ embed = Embeds.crashlogWithSolutionEmbed(channel, message, name, foundSolution.getReply());
+ sendEmbed(channel, embed);
+ } else {
+ embed = Embeds.crashlogOnlyEmbed(channel, message, name);
+ sendEmbed(channel, embed);
+ }
+ }
+ this.event.getMessage().delete().queue();
+ }
+}
diff --git a/src/main/java/net/romvoid/crashbot/file/Patterns.java b/src/main/java/net/romvoid/crashbot/file/Patterns.java
new file mode 100644
index 0000000..7dc4ad5
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/file/Patterns.java
@@ -0,0 +1,38 @@
+package net.romvoid.crashbot.file;
+
+import java.util.regex.Pattern;
+
+public final class Patterns {
+
+ public static final class Filenames {
+ private final static String DATE = "(19|20)[0-9]{2}[- \\/.](0[1-9]|1[012])[- \\/.](0[1-9]|[12][0-9]|3[01])";
+ private final static String TIME = "(?:[01]\\d|2[0123]).(?:[012345]\\d).(?:[012345]\\d)";
+
+ public static final Pattern CRASHLOG = Pattern
+ .compile("(crash-" + DATE + "_" + TIME + "-(client|server).(txt)|(message).(txt))");
+ public static final Pattern LOGS_TXT = Pattern.compile("(latest|message).(txt|log)");
+ public static final Pattern LOG_GZ = Pattern
+ .compile("([12]\\d{3})-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])-([1-9]).(log.gz)");
+ public static final Pattern ARCHIVE = Pattern.compile("(tar|gz|zip|7z|rar)");
+
+ public final static Pattern DISCORD_MSG_URL = Pattern
+ .compile("(https:\\/\\/cdn.discordapp.com\\/attachments\\/)(\\d{17,20})\\/(\\d{17,20})\\/");
+ }
+
+ public static final class Url {
+ public static final Pattern PASTEURL = Pattern.compile("https?:\\/\\/(www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{2,256}\\.[a-z]{2,4}\\b([-a-zA-Z0-9@:%_\\+.~#?&//=]*)");
+ public static final String DIVIDEURL = "((?:https?:\\/\\/)?\\.?(?:[-;:&=\\+\\$,\\w]+@)?[A-Za-z0-9.-]+|(?:www.)[A-Za-z0-9.-]+)((?:\\/[\\+~%\\/.\\w-_]*)?\\??(?:[-\\+=&;%@.\\w_]*)#?(?:[.\\!\\/\\\\w]*))?";
+ }
+
+ public static final class Discord {
+ public final static Pattern DISCORD_ID = Pattern.compile("\\d{17,20}"); // ID
+ public final static Pattern FULL_USER_REF = Pattern.compile("(\\S.{0,30}\\S)\\s*#(\\d{4})"); // $1 -> username,
+ // $2
+ // ->
+ // discriminator
+ public final static Pattern USER_MENTION = Pattern.compile("<@!?(\\d{17,20})>"); // $1 -> ID
+ public final static Pattern CHANNEL_MENTION = Pattern.compile("<#(\\d{17,20})>"); // $1 -> ID
+ public final static Pattern ROLE_MENTION = Pattern.compile("<@&(\\d{17,20})>"); // $1 -> ID
+ public final static Pattern EMOTE_MENTION = Pattern.compile("<:(.{2,32}):(\\d{17,20})>");
+ }
+}
diff --git a/src/main/java/net/romvoid/crashbot/file/solution/FileUtil.java b/src/main/java/net/romvoid/crashbot/file/solution/FileUtil.java
new file mode 100644
index 0000000..14ab34f
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/file/solution/FileUtil.java
@@ -0,0 +1,46 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 ROMVoid
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package net.romvoid.crashbot.file.solution;
+
+import java.io.File;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.io.FilenameUtils;
+
+public class FileUtil {
+
+ public static boolean matchToExt(Pattern pattern, String filename) {
+ Matcher matcher = pattern.matcher(filename);
+ return matcher.matches();
+ }
+
+ public static String getFileExtension(String filename) {
+ return FilenameUtils.getExtension(filename);
+ }
+
+ public static boolean isArchive(File file) {
+ return Pattern.matches("log.gz", getFileExtension(file.getName()));
+ }
+
+}
diff --git a/src/main/java/net/romvoid/crashbot/file/solution/Parser.java b/src/main/java/net/romvoid/crashbot/file/solution/Parser.java
new file mode 100644
index 0000000..d56a1e3
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/file/solution/Parser.java
@@ -0,0 +1,67 @@
+package net.romvoid.crashbot.file.solution;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+
+import org.apache.commons.io.IOUtils;
+
+public class Parser {
+
+ private static String[] notSupported = new String[] { "Minecraft Version: 1.8.9" };
+ public static String version;
+
+ public static boolean checkVersion(String message) {
+ boolean result = true;
+ try {
+ BufferedReader reader = new BufferedReader(
+ new InputStreamReader(IOUtils.toInputStream(message, Charset.defaultCharset())));
+ String line = null;
+ int currentLine = 0;
+ while ((line = reader.readLine()) != null) {
+ currentLine++;
+ for (String ver : notSupported) {
+ if (line.contains(ver)) {
+ version = ver.replace("Minecraft Version: ", "");
+ System.out.println(version);
+ return false;
+ }
+ }
+ }
+ reader.close();
+ } catch (IOException e) {
+
+ }
+ return result;
+ }
+
+ public static Solution find(String message) {
+// ArrayList result = new ArrayList<>();
+// InputStreamReader isr = new InputStreamReader(IOUtils.toInputStream(message, Charset.defaultCharset()));
+// try(BufferedReader br = new BufferedReader(isr)) {
+// while (br.ready()) {
+// result.add(br.readLine());
+// }
+// } catch (IOException e) {
+// e.printStackTrace();
+// }
+ for (Solution solution : SolutionFile.getSolutions()) {
+ if(Arrays.stream(solution.getKeys().toArray(new String[0])).parallel().allMatch(message::contains)) {
+ return solution;
+ }
+// int arraySize = result.size();
+// for (int i = 0; i < arraySize; i ++) {
+// for(String key : solution.getKeys()) {
+// if(result.get(i).contains(key)) {
+// System.out.println("Line #" + i + " matches Key: " + key);
+// return solution;
+// }
+// }
+// }
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/net/romvoid/crashbot/file/solution/Solution.java b/src/main/java/net/romvoid/crashbot/file/solution/Solution.java
new file mode 100644
index 0000000..c329dd4
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/file/solution/Solution.java
@@ -0,0 +1,64 @@
+package net.romvoid.crashbot.file.solution;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.lang3.builder.ToStringBuilder;
+
+import com.google.gson.annotations.Expose;
+import com.google.gson.annotations.SerializedName;
+
+public class Solution {
+
+ @SerializedName("keys")
+ @Expose
+ public List keys;
+ @SerializedName("reply")
+ @Expose
+ public String reply;
+
+ public Solution() {}
+
+ /**
+ *
+ * @param keys
+ * @param reply
+ */
+ public Solution(String reply, String... keys) {
+ super();
+ this.keys = Arrays.asList(keys);
+ this.reply = reply;
+ }
+
+ public List getKeys() {
+ return keys;
+ }
+
+ public void setKeys(String... keys) {
+ this.keys = Arrays.asList(keys);;
+ }
+
+ public Solution withKeys(String... keys) {
+ this.keys = Arrays.asList(keys);;
+ return this;
+ }
+
+ public String getReply() {
+ return reply;
+ }
+
+ public void setReply(String reply) {
+ this.reply = reply;
+ }
+
+ public Solution withReply(String reply) {
+ this.reply = reply;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return new ToStringBuilder(this).append("keys", keys).append("reply", reply).toString();
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/net/romvoid/crashbot/file/solution/SolutionFile.java b/src/main/java/net/romvoid/crashbot/file/solution/SolutionFile.java
new file mode 100644
index 0000000..0204595
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/file/solution/SolutionFile.java
@@ -0,0 +1,55 @@
+package net.romvoid.crashbot.file.solution;
+
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public class SolutionFile {
+
+ private static List solutions = new ArrayList();
+ private static Gson gson = new GsonBuilder().setPrettyPrinting().create();
+ private static Writer writer;
+ private static Reader reader;
+
+ public static List read() {
+ try {
+ reader = Files.newBufferedReader(Paths.get("solutions/solutions.json"));
+ //setSolutions(gson.fromJson(reader, new TypeToken>() {}.getType()));
+ List list = Arrays.asList(gson.fromJson(reader, Solution[].class));
+ reader.close();
+ return list;
+ } catch (Exception e) {
+ e.getStackTrace();
+ }
+ return null;
+ }
+
+ public static void write(Solution solution) {
+ solutions.addAll(getSolutions());
+ solutions.add(solution);
+ try {
+
+ writer = Files.newBufferedWriter(Paths.get("solutions/solutions.json"));
+ gson.toJson(solutions, writer);
+ for(Solution s : solutions) {
+ System.out.println(s.toString());
+ }
+ writer.close();
+ } catch (Exception e) {
+ e.getStackTrace();
+ }
+ }
+
+
+ public static List getSolutions() {
+ solutions = read();
+ return solutions;
+ }
+}
diff --git a/src/main/java/net/romvoid/crashbot/logging/InverseThresholdFilter.java b/src/main/java/net/romvoid/crashbot/logging/InverseThresholdFilter.java
new file mode 100644
index 0000000..00d1255
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/logging/InverseThresholdFilter.java
@@ -0,0 +1,34 @@
+package net.romvoid.crashbot.logging;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.filter.Filter;
+import ch.qos.logback.core.spi.FilterReply;
+
+public class InverseThresholdFilter extends Filter {
+ protected Level level;
+
+ @Override
+ public FilterReply decide(ILoggingEvent event) {
+ if (!isStarted()) {
+ return FilterReply.NEUTRAL;
+ }
+
+ if (event.getLevel().isGreaterOrEqual(level)) {
+ return FilterReply.DENY;
+ } else {
+ return FilterReply.NEUTRAL;
+ }
+ }
+
+ public void setLevel(String level) {
+ this.level = Level.toLevel(level);
+ }
+
+ @Override
+ public void start() {
+ if (this.level != null) {
+ super.start();
+ }
+ }
+}
diff --git a/src/main/java/net/romvoid/crashbot/utilities/Colors.java b/src/main/java/net/romvoid/crashbot/utilities/Colors.java
new file mode 100644
index 0000000..ac137b4
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/utilities/Colors.java
@@ -0,0 +1,54 @@
+package net.romvoid.crashbot.utilities;
+
+import java.awt.Color;
+
+public class Colors {
+
+ /**
+ * Universal Colors to use for Embeds
+ *
+ * TRY TO USE THESE ON MOST EMBEDS
+ */
+ public static class Universal {
+
+ /**
+ * Primary Embed Color (Light Green)
+ */
+ public static Color PRIMARY = new Color(46, 204, 113);
+
+ /**
+ * Secondary Embed Color (Light Blue)
+ */
+ public static Color SECONDARY = new Color(41, 128, 185);
+
+ /**
+ * Error Embed Color (Bright Red)
+ */
+ public static Color ERROR = new Color(231, 76, 60);
+
+ /**
+ * No Permissions for Command Embed Color (Dark Purple)
+ */
+ public static Color NO_PERMISSION = new Color(75, 31, 94);
+
+ /**
+ * Command Not Implemented Embed Color (Gold)
+ */
+ public static Color NOT_IMPLEMENTED = new Color(243, 156, 18);
+ }
+
+
+ public static class Custom {
+
+ public static Color Orange = new Color(255, 102, 0);
+ public static Color Red = new Color(255, 0, 0);
+ public static Color OrangeYellow = new Color(255, 204, 0);
+ public static Color LimeGreen = new Color(0, 255, 0);
+ public static Color Black = new Color(0, 0, 0);
+ public static Color MidnightBlue = new Color(0, 51, 102);
+ public static Color RoyalPurple = new Color(102, 51, 153);
+ public static Color DodgerBlue = new Color(51, 153, 255);
+ public static Color EmeraldGreen = new Color(85, 212, 63);
+ public static Color DarkGreen = new Color(58, 95, 11);
+ }
+}
diff --git a/src/main/java/net/romvoid/crashbot/utilities/EmbedUtil.java b/src/main/java/net/romvoid/crashbot/utilities/EmbedUtil.java
new file mode 100644
index 0000000..8570268
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/utilities/EmbedUtil.java
@@ -0,0 +1,165 @@
+package net.romvoid.crashbot.utilities;
+
+import java.util.concurrent.TimeUnit;
+
+import net.dv8tion.jda.api.EmbedBuilder;
+import net.dv8tion.jda.api.MessageBuilder;
+import net.dv8tion.jda.api.entities.ChannelType;
+import net.dv8tion.jda.api.entities.Message;
+import net.dv8tion.jda.api.entities.MessageChannel;
+import net.romvoid.crashbot.Bot;
+
+/**
+ * Generates common embed messages.
+ *
+ * @author ROMVoid
+ */
+public class EmbedUtil {
+ /**
+ * The default deletion interval.
+ * TODO: Create a config-option to set this per-guild
+ */
+ private static final long defaultDeleteInterval = 60;
+ /**
+ * The default deletion interval TimeUnit.
+ * TODO: Create a config-option to set this per-guild
+ */
+ private static final TimeUnit defaultDeleteIntervalTimeUnit = TimeUnit.SECONDS;
+
+ /**
+ * Creates an embedded message indicating a successful feature execution.
+ *
+ * @param title the title of the embedded.
+ * @param description a more detailed description.
+ * @return the generated EmbedBuilder.
+ */
+ public static EmbedBuilder success(String title, String description) {
+ return embed(":white_check_mark: " + title, description).setColor(Colors.Universal.PRIMARY);
+ }
+
+ /**
+ * Creates an embedded message with additional information.
+ *
+ * @param title the title of the embedded.
+ * @param description a more detailed description.
+ * @return the generated EmbedBuilder.
+ */
+ public static EmbedBuilder info(String title, String description) {
+ return embed(":information_source: " + title, description).setColor(Colors.Universal.SECONDARY);
+ }
+
+ /**
+ * Creates an embedded unknown error message.
+ *
+ * @return the generated EmbedBuilder.
+ */
+ public static EmbedBuilder error() {
+ return error("Unknown error", "An unknown error occurred.");
+ }
+
+ /**
+ * Creates an embedded error message.
+ *
+ * @param title the title of the embedded.
+ * @param description the error description.
+ * @return the generated EmbedBuilder.
+ */
+ public static EmbedBuilder error(String title, String description) {
+ return embed(":warning: " + title, description).setColor(Colors.Universal.ERROR);
+ }
+
+ /**
+ * Creates an embedded no permissions message.
+ *
+ * @return the generated EmbedBuilder.
+ */
+ public static EmbedBuilder no_permissions() {
+ return embed(":warning: " + "No permissions", "You don't have the permission to execute this command!").setColor(Colors.Universal.NO_PERMISSION);
+ }
+
+
+ /**
+ * Creates an embedded message.
+ *
+ * @param title the title of the embedded.
+ * @param description the description (main message).
+ * @return the generated EmbedBuilder.
+ */
+ public static EmbedBuilder embed(String title, String description) {
+ return new EmbedBuilder().setTitle(title).setDescription(description);
+ }
+
+ /**
+ * Attaches a timestamp to an embedded message.
+ *
+ * @param embedBuilder the EmbedBuilder to attach a timestamp to.
+ * @return the given EmbedBuilder with a timestamp.
+ */
+ public static EmbedBuilder withTimestamp(EmbedBuilder embedBuilder) {
+ return embedBuilder.setFooter(Bot.getNewTimestamp(), null);
+ }
+
+ /**
+ * Converts an EmbedBuilder into a message.
+ *
+ * @param embedBuilder the EmbedBuilder that contains the embed data.
+ * @return the compiled Message.
+ */
+ public static Message message(EmbedBuilder embedBuilder) {
+ return new MessageBuilder().setEmbed(embedBuilder.build()).build();
+ }
+
+ /**
+ * Always sends a message but deletes it only if the messageChannel is on a server.
+ *
+ * @param messageChannel the channel to send the message in.
+ * @param message the message to send.
+ */
+ public static void sendAndDeleteOnGuilds(MessageChannel messageChannel, Message message) {
+ sendAndDeleteOnGuilds(messageChannel, message, defaultDeleteInterval, defaultDeleteIntervalTimeUnit);
+ }
+
+ /**
+ * Always sends a message but deletes it only if the messageChannel is on a server.
+ *
+ * @param messageChannel the channel to send the message in.
+ * @param message the message to send.
+ * @param deleteInterval the interval after that the message should be deleted if the channel is on a server.
+ * @param deleteIntervalTimeUnit the TimeUnit of deleteInterval.
+ */
+ public static void sendAndDeleteOnGuilds(MessageChannel messageChannel, Message message, long deleteInterval, TimeUnit deleteIntervalTimeUnit) {
+ sendAndDelete(messageChannel, message,
+ // only delete message on servers.
+ messageChannel.getType() == ChannelType.TEXT ? deleteInterval : -1,
+ deleteIntervalTimeUnit);
+ }
+
+ /**
+ * Sends a message and deletes it after an interval (if interval is >= 0).
+ *
+ * @param messageChannel the channel to send the message in.
+ * @param message the message to send.
+ * @param deleteInterval the interval after that the message should be deleted. Message won't be deleted if
+ * interval is < 0.
+ * @param deleteIntervalTimeUnit the TimeUnit of deleteInterval.
+ */
+ public static void sendAndDelete(MessageChannel messageChannel, Message message, long deleteInterval, TimeUnit deleteIntervalTimeUnit) {
+ messageChannel.sendMessage(message).queue(deleteInterval < 0
+ // do nothing if interval < 0
+ ? null
+ // else: delete after interval
+ : sentMessage -> sentMessage.delete().queueAfter(deleteInterval, deleteIntervalTimeUnit, null, ignored -> {
+ //TODO evaluate exception, i.e. bot does not have permission
+ }));
+ }
+
+ public static void sendAndDelete(MessageChannel messageChannel, EmbedBuilder embed, long deleteInterval, TimeUnit deleteIntervalTimeUnit) {
+ messageChannel.sendMessage(embed.build()).queue(deleteInterval < 0
+ // do nothing if interval < 0
+ ? null
+ // else: delete after interval
+ : sentMessage -> sentMessage.delete().queueAfter(deleteInterval, deleteIntervalTimeUnit, null, ignored -> {
+ //TODO evaluate exception, i.e. bot does not have permission
+ }));
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/net/romvoid/crashbot/utilities/HasteUtil.java b/src/main/java/net/romvoid/crashbot/utilities/HasteUtil.java
new file mode 100644
index 0000000..a6acceb
--- /dev/null
+++ b/src/main/java/net/romvoid/crashbot/utilities/HasteUtil.java
@@ -0,0 +1,172 @@
+/*
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2020 ROMVoid
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+ * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+ * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+ * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+package net.romvoid.crashbot.utilities;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import org.json.JSONObject;
+import org.jsoup.Connection;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.jsoup.nodes.Element;
+
+public class HasteUtil {
+
+ /** The hastebin server URL.
+ * TODO: Create a per-guild config-option to choose a custom URL
+ *
+ * */
+ private static String HASTEBIN_SERVER = "http://www.mfnd.ru:7775/"; // requires trailing slash
+ private static URI uri;
+
+ /**
+ * A simple implementation of the Hastebin Client API, allowing data to be pasted online for
+ * players to access.
+ *
+ * @param urlParameters The string to be sent in the body of the POST request
+ * @return A formatted URL which links to the pasted file
+ */
+ public synchronized static void paste(String urlParameters) {
+ HttpURLConnection connection = null;
+
+ try {
+ //Create connection
+ URL url = new URL(HASTEBIN_SERVER + "documents");
+ connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestProperty("Content-Type", "application/json");
+ connection.setRequestMethod("POST");
+ connection.setDoInput(true);
+ connection.setDoOutput(true);
+ connection.setConnectTimeout(0);
+
+ //Send request
+ DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
+ wr.writeBytes(urlParameters);
+ wr.flush();
+ wr.close();
+
+ //Get Response
+ BufferedReader rd = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ JSONObject object = new JSONObject(rd.readLine());
+ String[] out = object.toString().replace("{", "").replace("}", "").replace("\"", "").split(":");
+ String output = HASTEBIN_SERVER + out[1];
+ uri = new URI(output + ".crashlog");
+
+ } catch (IOException | URISyntaxException e) {
+
+ } finally {
+
+ connection.disconnect();
+ }
+ }
+
+ public static URI getLink() {
+ return uri;
+ }
+
+ /**
+ * Returns the URL of the server being used.
+ *
+ * @return API to use for posting data
+ */
+ public static String getPasteURL() {
+ return HASTEBIN_SERVER;
+ }
+
+ /**
+ * Sets the URL used by the paste method, allowing for the server logs are pasted to to be
+ * dynamically changed.
+ *
+ * @param URL API URL of HasteBin instance
+ */
+ public static void setPasteURL(String URL) {
+ HASTEBIN_SERVER = URL;
+ }
+
+ /**
+ * Grabs a HasteBin file from the Internet and attempts to return the file with formatting
+ * intact.
+ *
+ * @return String HasteBin Raw Text
+ */
+ public static synchronized String getPaste(String ID) {
+ String URLString = HASTEBIN_SERVER + "raw/" + ID + "/";
+ try {
+ URL URL = new URL(URLString);
+ HttpURLConnection connection = (HttpURLConnection) URL.openConnection();
+ connection.setDoOutput(true);
+ connection.setConnectTimeout(0);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ String paste = "";
+ while (reader.ready()) {
+ String line = reader.readLine();
+ if (line.contains("package")) continue;
+ if (paste.equals("")) paste = line;
+ else paste = paste + "\n" + line;
+ }
+ return paste;
+ } catch (IOException e) {
+ return "";
+ }
+ }
+
+ public static synchronized String getOtherPaste(String url) {
+ try {
+ URL URL = new URL(url);
+ HttpURLConnection connection = (HttpURLConnection) URL.openConnection();
+ connection.setDoOutput(true);
+ connection.setConnectTimeout(0);
+ BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+ String paste = "";
+ while (reader.ready()) {
+ String line = reader.readLine();
+ if (line.contains("package")) continue;
+ if (paste.equals("")) paste = line;
+ else paste = paste + "\n" + line;
+ uri = new URI(url);
+ }
+ return paste;
+ } catch (IOException | URISyntaxException e) {
+ return "";
+ }
+ }
+
+ public static synchronized String getCrashFromUbuntuPaste(String url) {
+ try {
+ Connection con = Jsoup.connect(url).ignoreHttpErrors(true).timeout(0);
+ Document doc = con.get();
+ Element paste = doc.select("pre").get(1);
+ return paste.text();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return "";
+ }
+}
diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml
new file mode 100644
index 0000000..4bc0224
--- /dev/null
+++ b/src/main/resources/logback.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+ WARN
+
+
+ [%d{HH:mm:ss}] [%level] [%logger{0}]: %msg%n
+
+
+
+
+ System.err
+
+ WARN
+
+
+ [%d{HH:mm:ss}] [%level] [%logger{0}]: %msg%n
+
+
+
+
+ logs/current.log
+
+
+ logs/%d{yyyy-MM-dd}.log
+
+
+ 30
+ 3GB
+
+
+
+
+ [%d{HH:mm:ss}] [%level] [%logger{0}]: %msg%n
+
+
+
+
+
+
+
+
+
\ No newline at end of file