From 434ebc47030cd19647e39d5b4676540cb7dc98ad Mon Sep 17 00:00:00 2001 From: Aleksei Date: Thu, 13 Feb 2020 23:01:27 -0500 Subject: [PATCH] Open source release --- .gitignore | 32 +++ build.gradle | 33 +++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54413 bytes gradle/wrapper/gradle-wrapper.properties | 12 + gradlew | 172 ++++++++++++ gradlew.bat | 84 ++++++ profilePic.png | Bin 0 -> 49100 bytes profilePicDEV.png | Bin 0 -> 30527 bytes settings.gradle | 8 + src/main/java/dev/salmonllama/fsbot/Main.java | 50 ++++ .../developer/CreateGalleryCommand.java | 88 +++++++ .../fsbot/commands/developer/EvalCommand.java | 51 ++++ .../commands/developer/InviteCommand.java | 34 +++ .../fsbot/commands/developer/TestCommand.java | 29 +++ .../fsbot/commands/general/ColorCommand.java | 103 ++++++++ .../fsbot/commands/general/ColorsCommand.java | 40 +++ .../fsbot/commands/general/HelpCommand.java | 172 ++++++++++++ .../fsbot/commands/general/OutfitCommand.java | 89 +++++++ .../fsbot/commands/general/PingCommand.java | 32 +++ .../general/SpecificOutfitCommand.java | 57 ++++ .../fsbot/commands/staff/AddColorCommand.java | 62 +++++ .../fsbot/commands/staff/EchoCommand.java | 34 +++ .../commands/staff/GetOutfitCommand.java | 116 +++++++++ .../commands/staff/GetServersCommand.java | 54 ++++ .../commands/staff/OutfitInfoCommand.java | 55 ++++ .../commands/staff/RemoveOutfitCommand.java | 65 +++++ .../fsbot/commands/staff/RetagCommand.java | 64 +++++ .../commands/staff/SetStatusCommand.java | 48 ++++ .../commands/staff/WelcomeMessageCommand.java | 110 ++++++++ .../salmonllama/fsbot/config/BotConfig.java | 76 ++++++ .../dev/salmonllama/fsbot/guthix/Command.java | 24 ++ .../fsbot/guthix/CommandContext.java | 99 +++++++ .../fsbot/guthix/CommandPermission.java | 28 ++ .../fsbot/guthix/ContextBuilder.java | 73 ++++++ .../dev/salmonllama/fsbot/guthix/Guthix.java | 144 ++++++++++ .../fsbot/guthix/PermissionManager.java | 46 ++++ .../fsbot/guthix/PermissionType.java | 12 + .../salmonllama/fsbot/guthix/Registry.java | 93 +++++++ .../fsbot/listeners/AchievementListener.java | 27 ++ .../fsbot/listeners/ImageListener.java | 152 +++++++++++ .../fsbot/listeners/NewMemberListener.java | 37 +++ .../ReactionDeleteConfirmationListener.java | 93 +++++++ .../ReactionRetagConfirmationListener.java | 107 ++++++++ .../fsbot/listeners/ReportListener.java | 60 +++++ .../fsbot/listeners/ServerJoined.java | 35 +++ .../fsbot/listeners/ThumbsListener.java | 36 +++ .../salmonllama/fsbot/utilities/Checks.java | 17 ++ .../fsbot/utilities/ColorRole.java | 41 +++ .../fsbot/utilities/Constants.java | 18 ++ .../fsbot/utilities/MessageUtilities.java | 17 ++ .../salmonllama/fsbot/utilities/Outfit.java | 70 +++++ .../utilities/database/DatabaseUtilities.java | 246 ++++++++++++++++++ .../utilities/database/RoleColourUtility.java | 68 +++++ .../utilities/database/ServerConfUtility.java | 74 ++++++ .../utilities/exceptions/DiscordError.java | 25 ++ .../exceptions/DiscordException.java | 23 ++ .../utilities/exceptions/DiscordWarning.java | 9 + .../exceptions/OutfitNotFoundException.java | 14 + .../exceptions/TagNotFoundException.java | 14 + .../fsbot/utilities/warnings/Warning.java | 29 +++ 60 files changed, 3501 insertions(+) create mode 100644 .gitignore create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 profilePic.png create mode 100644 profilePicDEV.png create mode 100644 settings.gradle create mode 100644 src/main/java/dev/salmonllama/fsbot/Main.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/developer/CreateGalleryCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/developer/EvalCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/developer/InviteCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/developer/TestCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/general/ColorCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/general/ColorsCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/general/HelpCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/general/OutfitCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/general/PingCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/general/SpecificOutfitCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/staff/AddColorCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/staff/EchoCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/staff/GetOutfitCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/staff/GetServersCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/staff/OutfitInfoCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/staff/RemoveOutfitCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/staff/RetagCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/staff/SetStatusCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/commands/staff/WelcomeMessageCommand.java create mode 100644 src/main/java/dev/salmonllama/fsbot/config/BotConfig.java create mode 100644 src/main/java/dev/salmonllama/fsbot/guthix/Command.java create mode 100644 src/main/java/dev/salmonllama/fsbot/guthix/CommandContext.java create mode 100644 src/main/java/dev/salmonllama/fsbot/guthix/CommandPermission.java create mode 100644 src/main/java/dev/salmonllama/fsbot/guthix/ContextBuilder.java create mode 100644 src/main/java/dev/salmonllama/fsbot/guthix/Guthix.java create mode 100644 src/main/java/dev/salmonllama/fsbot/guthix/PermissionManager.java create mode 100644 src/main/java/dev/salmonllama/fsbot/guthix/PermissionType.java create mode 100644 src/main/java/dev/salmonllama/fsbot/guthix/Registry.java create mode 100644 src/main/java/dev/salmonllama/fsbot/listeners/AchievementListener.java create mode 100644 src/main/java/dev/salmonllama/fsbot/listeners/ImageListener.java create mode 100644 src/main/java/dev/salmonllama/fsbot/listeners/NewMemberListener.java create mode 100644 src/main/java/dev/salmonllama/fsbot/listeners/ReactionDeleteConfirmationListener.java create mode 100644 src/main/java/dev/salmonllama/fsbot/listeners/ReactionRetagConfirmationListener.java create mode 100644 src/main/java/dev/salmonllama/fsbot/listeners/ReportListener.java create mode 100644 src/main/java/dev/salmonllama/fsbot/listeners/ServerJoined.java create mode 100644 src/main/java/dev/salmonllama/fsbot/listeners/ThumbsListener.java create mode 100644 src/main/java/dev/salmonllama/fsbot/utilities/Checks.java create mode 100644 src/main/java/dev/salmonllama/fsbot/utilities/ColorRole.java create mode 100644 src/main/java/dev/salmonllama/fsbot/utilities/Constants.java create mode 100644 src/main/java/dev/salmonllama/fsbot/utilities/MessageUtilities.java create mode 100644 src/main/java/dev/salmonllama/fsbot/utilities/Outfit.java create mode 100644 src/main/java/dev/salmonllama/fsbot/utilities/database/DatabaseUtilities.java create mode 100644 src/main/java/dev/salmonllama/fsbot/utilities/database/RoleColourUtility.java create mode 100644 src/main/java/dev/salmonllama/fsbot/utilities/database/ServerConfUtility.java create mode 100644 src/main/java/dev/salmonllama/fsbot/utilities/exceptions/DiscordError.java create mode 100644 src/main/java/dev/salmonllama/fsbot/utilities/exceptions/DiscordException.java create mode 100644 src/main/java/dev/salmonllama/fsbot/utilities/exceptions/DiscordWarning.java create mode 100644 src/main/java/dev/salmonllama/fsbot/utilities/exceptions/OutfitNotFoundException.java create mode 100644 src/main/java/dev/salmonllama/fsbot/utilities/exceptions/TagNotFoundException.java create mode 100644 src/main/java/dev/salmonllama/fsbot/utilities/warnings/Warning.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eca6160 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +.gradle +build/ +bin/ + +# Ignore any sqlite databases created +*.db + +# Stupid IntelliJ stuff +.idea +*.iml +out/ + +# Stupid VSCode stuff +.vscode + +# Stupid Eclipse project files +.classpath +.project +.settings + +# Ignore Gradle GUI config +gradle-app.setting + +# Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) +!gradle-wrapper.jar + +# Ignore commandler files +cmdframework.db +config.properties + +# Ignore the bot config +bot.config \ No newline at end of file diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..8e42ecf --- /dev/null +++ b/build.gradle @@ -0,0 +1,33 @@ +/* + * Developed by Alek Gryczewski on 10/18/18 1:39 PM + * Last modified 9/25/18 7:33 PM + * Copyright (c) 2018. All rights reserved. + */ + +plugins { + id 'application' +} + +group 'studio.spiderling' +version '1.1.11' + +sourceCompatibility = 1.8 + +repositories { + mavenCentral() + maven { + url 'https://jitpack.io' + } +} + +dependencies { + + implementation 'com.github.Kaaz:ConfigurationBuilder:0.4' + implementation group: 'org.javacord', name: 'javacord', version:'3.0.5' + implementation group: 'com.rethinkdb', name: 'rethinkdb-driver', version: '2.3.3' + implementation 'com.vdurmont:emoji-java:4.0.0' + + testCompile group: 'junit', name: 'junit', version: '4.12' +} + +mainClassName = 'dev.salmonllama.fsbot.Main' diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..1948b9074f1016d15d505d185bc3f73deb82d8c8 GIT binary patch literal 54413 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 \(.*\)$'` + 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="" + +# 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..f955316 --- /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= + +@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/profilePic.png b/profilePic.png new file mode 100644 index 0000000000000000000000000000000000000000..3ee8de7eaf63a28975b2f246e58f4d94f4c4d3ac GIT binary patch literal 49100 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 literal 0 HcmV?d00001 diff --git a/profilePicDEV.png b/profilePicDEV.png new file mode 100644 index 0000000000000000000000000000000000000000..f8a0d66e413d39569028a263aeec5eda18750e88 GIT binary patch literal 30527 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
hvUZcRL0p#TfdI~aJVYYAIsFeN@c zPT8(ZCFIe#PJ;Da8A@g`B?3ZLN=wAlBY~%uNup0oE>rkk+FxmJ*aw+|vA38*7qwoZ z91nfq9Rr-UT{RqO;xL65XzF%1D3~xZ1ru|D(DE<}*2N^$%PzZYi=@LXvQyO#A?b|7 zG?{?SJsj_1l3Z(%m4+-j*`xdzGoc=dSqYj3uYP%nt{S5DBO#ga4I)q?it&9vllp{6 zDb*7;CPSo=-z%@MNSh;>i|-RsReOao3CXIbi?RqL@}YoIq?ov`!*g(}ma)(LMza&} zWeunsM#6CCiiCCR)@`vgxW#s?PWF?Y^rR^#e596Ou`(tCG2|v07SdD*-UDjk*~$b# zCAG#R%#uN?l*-8r_D`54F{o{lz|s&;?UB%?LaKDxrnafZd_pS;+?a^%s`;o>x2yGN zh^sZKt-?-21QF%+OaY%106nH~UYT=GW-%8FC(HHMU%$oE-WK1%oc!lL_qjvTB9knW z;BqBz2NjJp^XMLI6g@kpf>0OC1B0lEG{mhBA~5);V3i!mHF=!l_!;ekIZB5J*EmAysNk-V=^dH>U=&E06g!=ZtyxMlAbh9Vr~NKd>!y zy|XqzG0wo`YO{R-l=9G~VN=b~#q%tLS87xccqlX0K*tY-~-LsEv!1I`8Z1e%w+JSATy^S6Ya_D|Ioq<`e$c4`Z}jPu<4y`>4} zt(G~Q$X$2cb;_dTY3C3QesdOFFH90j;`C$^-Hu2-@B2u2B$iJomCT{5IPD6xiQX%< z6Md8!OvRTW)P5PrQ;TThRF`owhH#KFH4rL8;DSXnP{u>DnM1Q~X-C_udi9)38J;~E z7~DR>TeiS%4Yoy(DYts&aA1#k#3QEI5jB8?KRz#%tc;PwZ=R1h*<2-v*WGE_6x3rN zQ0XpIQey~N#O(eyvCd3lUCfr(MvxJxSGyy50P_Qx%m7drv{{(*+i$;ptEa84egMh!&wlo^r(|`ulH70|*2@+vqbRvk zo2Vs(RS>dQd8LLB@(5{^wn_L9s=-KuaWa{KM<1V@`vFFT z8Qe!fzpBSc;h+uT6=n(x+ay&dk0oWSxO9PMTPuWWX^(A+IUL~byYD{bLd$wG2H(RZ zXp)qG$qch_ugs#<)deaXk&u+w$$)ysge)?EF%tim2zAB!UNVZd4NS#Y@XomhoWE21 zq$45Y5@|EkaWf7XnC}oc1o0|UASlo?7b=pKKXTA)I*)MVLT`ecSLeb82r!GvTj2F^Y&E&iJImtW?gUV?Y^C{t}O z<_g#Bb=O_DP1DadJ-A9F`|Y>il#@*AJ${x$>Y`1sO|$`8gr+3chR8C;EGnUDwDY;x zp7&~A7@x_M8sS-2GSV0%kYTE8x%UoC$1$8RgwW8k8sxah*@U(lhc<5v{8_Dc?@`Q= zleohUJ4_*lbg}05NO&}gNBJiMnR3xv6k`yTX)4i;Eo#=0j9R^ME#|V`^rEFJ+h8N* zm;=0gRbapn^cQWqt$3dnWukj5b2w4lgiAyh$c2=xxl5MAkO$|>EQ#ZL_DK?FGFMCE z)JR#jIu^zVfO@bY_-UQ~uGfjS(VoPLwsq(2f#4XD3YM9SEINxT}{OpBlt|&Ppwe32;Zekm-k5j@H&JRA;=JnfiWQj zZoc{E?R2pv?IB-O<*jykU>6=5?+VH7sjaqQ7=moX7tk>L@P9rKqI=U@SAF5-0>&gG zwHTnb?@^i5iFRNoUW=i8PcIsE!w?wQ+d|N8rDkhFC527#_fkc{Ne5EKeGPYCua`ks70Rm z#3xQ6T#wAxt7OW?qA`+XbfNo-j9@O7%s}9CwQfLK$^LIib!}&k6 logging to Discord, not console + +public class Main { + + public static void main(String[] args) { + String configLocation = Constants.BOT_FOLDER.concat(Constants.CONFIG_NAME); + BotConfig.initConfig(configLocation); + + // Initialise the database with values from the bot's config file + RethinkDB r = RethinkDB.r; + Connection conn = r.connection().hostname(BotConfig.DB_HOST).port(BotConfig.DB_PORT).connect(); + + new DiscordApiBuilder().setToken(BotConfig.TOKEN).login().thenAccept(api -> { + DatabaseUtilities db = new DatabaseUtilities(r, conn, api); + db.tableSetup(); + + Guthix guthix = new Guthix(api, db); + + // Register listeners + api.addMessageCreateListener(new ImageListener(r, conn)); + api.addServerMemberJoinListener(new NewMemberListener(api)); + api.addServerJoinListener(new ServerJoined(api, db)); + api.addMessageCreateListener(new ThumbsListener()); + api.addMessageCreateListener(new AchievementListener()); + api.addMessageCreateListener(new ReportListener()); + + System.out.println(String.format("Bot invite: %s", api.createBotInvite())); + System.out.println(String.format("Logged in as %s", api.getYourself().getDiscriminatedName())); + }); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/developer/CreateGalleryCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/developer/CreateGalleryCommand.java new file mode 100644 index 0000000..76b7407 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/developer/CreateGalleryCommand.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.developer; + +import com.rethinkdb.RethinkDB; +import com.rethinkdb.net.Connection; +import dev.salmonllama.fsbot.config.BotConfig; +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; +import org.javacord.api.entity.channel.TextChannel; +import org.javacord.api.entity.message.embed.EmbedBuilder; +import org.javacord.api.entity.server.Server; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +public class CreateGalleryCommand extends Command { // TODO: This command needs help. + @Override public String name() { return "Create Gallery"; } + @Override public String description() { return "Creates a channel gallery, tracking any posted images"; } + @Override public String usage() { return "creategallery "; } + @Override public String category() { return "Developer"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.OWNER); } + @Override public Collection aliases() { return new ArrayList<>(Arrays.asList("creategallery", "addgallery", "addgall")); } + + static final RethinkDB R = RethinkDB.r; + static final Connection CONN = R.connection().hostname("localhost").port(28015).connect(); + + @Override + public void onCommand(CommandContext ctx) { + Server server = ctx.getServer(); + TextChannel channel = ctx.getChannel(); + String[] args = ctx.getArgs(); + String targetChannelId = channel.getIdAsString(); + String targetChannelName = channel.asServerChannel().get().getName(); // TODO: un-band-aid this. + String targetServerName = server.getName(); + String targetServerId = server.getIdAsString(); + + if (!server.getIdAsString().equals(BotConfig.HOME_SERVER)) { + channel.sendMessage("Fashion galleries can only be created in the Fashionscape server"); + return; + } + + if (args.length < 1) { + channel.sendMessage("No Arguments provided"); + return; + } + else if (args.length > 1) { + channel.sendMessage("Too many arguments provided."); + return; + } + + String tag = args[0]; + + if (R.db("fsbot").table("channels").getField("cId").contains(targetChannelId).run(CONN)) { + EmbedBuilder embed = new EmbedBuilder() + .setColor(Color.RED) + .addField("ERROR", "This channel is already gathering images"); + channel.sendMessage(embed); + } + else { + R.db("fsbot").table("channels").insert( + R.hashMap("sName", targetServerName) + .with("sId", targetServerId) + .with("cName", targetChannelName) + .with("cId", targetChannelId) + .with("tag", tag) + ).run(CONN); + + R.db("fsbot").tableCreate(tag).run(CONN); + + EmbedBuilder embed = new EmbedBuilder() + .setColor(Color.GREEN) + .addField("Success", "Channel added to storage with the following values:") + .addField("Channel Name:", targetChannelName) + .addField("Channel Id:", targetChannelId) + .addField("Tag:", tag) + .addField("End:", String.format("Table \"%s\" created for images in this channel", tag)); + channel.sendMessage(embed); + } + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/developer/EvalCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/developer/EvalCommand.java new file mode 100644 index 0000000..517e20f --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/developer/EvalCommand.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.developer; + +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; +import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +public class EvalCommand extends Command { + @Override public String name() { return "Eval"; } + @Override public String description() { return "Evaluates the given parameters"; } + @Override public String usage() { return "eval "; } + @Override public String category() { return "Developer"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.OWNER); } + @Override public Collection aliases() { return new ArrayList<>(Arrays.asList("eval", "ev")); } + + private final DatabaseUtilities db; + + public EvalCommand(DatabaseUtilities db) { + this.db = db; + } + + @Override + public void onCommand(CommandContext ctx) { + String statement = String.join(" ", ctx.getArgs()); + + ScriptEngineManager factory = new ScriptEngineManager(); + ScriptEngine engine = factory.getEngineByName("JavaScript"); + + engine.put("db", db); + + try { + Object result = engine.eval(statement); + ctx.reply(result.toString()); + } + catch (Exception e) { + ctx.reply(e.getMessage()); + } + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/developer/InviteCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/developer/InviteCommand.java new file mode 100644 index 0000000..5bc4afc --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/developer/InviteCommand.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.developer; + +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; +import org.javacord.api.DiscordApi; +import org.javacord.api.entity.channel.TextChannel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +public class InviteCommand extends Command { + @Override public String name() { return "Invite"; } + @Override public String description() { return "Spits out a bot invite"; } + @Override public String usage() { return "invite"; } + @Override public String category() { return "Developer"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.OWNER); } + @Override public Collection aliases() { return new ArrayList<>(Arrays.asList("invite", "inv")); } + + @Override + public void onCommand(CommandContext ctx) { + TextChannel channel = ctx.getChannel(); + DiscordApi api = ctx.getApi(); + + channel.sendMessage(String.format("<%s>", api.createBotInvite())); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/developer/TestCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/developer/TestCommand.java new file mode 100644 index 0000000..295fe87 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/developer/TestCommand.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.developer; + +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +public class TestCommand extends Command { + @Override public String name() { return "Test"; } + @Override public String description() { return "A test command"; } + @Override public String usage() { return "test"; } + @Override public String category() { return "Developer"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.OWNER); } + @Override public Collection aliases() { return new ArrayList<>(Arrays.asList("test", "t")); } + + @Override + public void onCommand(CommandContext ctx) { + ctx.getChannel().sendMessage("Test Command has been invoked"); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/general/ColorCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/general/ColorCommand.java new file mode 100644 index 0000000..40406d7 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/general/ColorCommand.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.general; + +import dev.salmonllama.fsbot.config.BotConfig; +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; +import dev.salmonllama.fsbot.utilities.ColorRole; +import dev.salmonllama.fsbot.utilities.database.RoleColourUtility; +import org.javacord.api.DiscordApi; +import org.javacord.api.entity.channel.TextChannel; +import org.javacord.api.entity.message.embed.EmbedBuilder; +import org.javacord.api.entity.permission.Role; +import org.javacord.api.entity.server.Server; +import dev.salmonllama.fsbot.utilities.warnings.Warning; +import org.javacord.api.entity.user.User; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +public class ColorCommand extends Command { + @Override public String name() { return "Color"; } + @Override public String description() { return "Assigns the provided cosmetic role"; } + @Override public String usage() { return "color "; } + @Override public String category() { return "General"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.NONE); } + @Override public Collection aliases() { return new ArrayList<>(Arrays.asList("color", "colour")); } + + @Override + public void onCommand(CommandContext ctx) { + String[] args = ctx.getArgs(); + DiscordApi api = ctx.getApi(); + Server server = ctx.getServer(); + User user = ctx.getUser(); + TextChannel channel = ctx.getChannel(); + + // Live server + if (!server.getIdAsString().equals(BotConfig.HOME_SERVER)) { return; } + + if (!user.getRoles(server).contains(server.getRoleById(BotConfig.HYDRIX_ROLE).orElseThrow(AssertionError::new))) { + channel.sendMessage(new Warning("You need the Hydrix role to use this").sendWarning()); + return; + } + + if (args.length != 1) { + channel.sendMessage(new Warning("Not enough arguments provided").sendWarning()); + return; + } + + // args[0] is the subcommand, either list, or a colour + if (args[0].equals("list")) { + System.out.println(RoleColourUtility.getAllRoleInfo()); + return; + } + + String roleId = RoleColourUtility.getColour(args[0]); + Role role; + + if (!api.getRoleById(roleId).isPresent()) { + channel.sendMessage(new Warning("That role doesn't exist, mate!").sendWarning()); + return; + } + else { + role = api.getRoleById(roleId).get(); + } + + // Actual command logic now + + // Remove all the colour roles from the user, except the target one + List allRoles = RoleColourUtility.getAllRoles(); + allRoles.forEach(r -> { + if (r.id.equals(roleId)) { + server.removeRoleFromUser(user, server.getRoleById(r.id).orElseThrow(AssertionError::new)); + } + }); + + // If user already has the role, remove it + if (user.getRoles(server).contains(role)) { + user.removeRole(role); + channel.sendMessage(new EmbedBuilder() + .setColor(Color.GREEN) + .setTitle("Role Removed") + .addInlineField("Role:", role.getName()) + ); + } + else { + user.addRole(role); + channel.sendMessage(new EmbedBuilder() + .setColor(Color.GREEN) + .setTitle("Role Added") + .addInlineField("Role:", role.getName()) + ); + } + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/general/ColorsCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/general/ColorsCommand.java new file mode 100644 index 0000000..dd9b8da --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/general/ColorsCommand.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.general; + +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; +import org.javacord.api.entity.channel.TextChannel; +import org.javacord.api.entity.server.Server; +import dev.salmonllama.fsbot.config.BotConfig; +import dev.salmonllama.fsbot.utilities.ColorRole; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +public class ColorsCommand extends Command { + @Override public String name() { return "Colors"; } + @Override public String description() { return "Lists available cosmetic roles"; } + @Override public String usage() { return "colors"; } + @Override public String category() { return "General"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.NONE); } + @Override public Collection aliases() { return new ArrayList<>(Arrays.asList("colors", "colours")); } + + @Override + public void onCommand(CommandContext ctx) { + Server server = ctx.getServer(); + TextChannel channel = ctx.getChannel(); + + if (!server.getIdAsString().equals(BotConfig.HOME_SERVER)) { + return; + } + + channel.sendMessage(ColorRole.rolesListEmbed()); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/general/HelpCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/general/HelpCommand.java new file mode 100644 index 0000000..ccc5f92 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/general/HelpCommand.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.general; + +import dev.salmonllama.fsbot.config.BotConfig; +import dev.salmonllama.fsbot.guthix.*; +import org.javacord.api.entity.channel.TextChannel; +import org.javacord.api.entity.message.embed.EmbedBuilder; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + + +public class HelpCommand extends Command { + @Override public String name() { return "Help"; } + @Override public String description() { return "Shows all commands, or a specific command's information"; } + @Override public String usage() { return "help [String command]"; } + @Override public String category() { return "General"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.NONE); } + @Override public Collection aliases() { return new ArrayList<>(Arrays.asList("help", "h")); } + + private final Guthix guthix; + + public HelpCommand(Guthix guthix) { + this.guthix = guthix; + } + + @Override + public void onCommand(CommandContext ctx) { + String[] args = ctx.getArgs(); + TextChannel channel = ctx.getChannel(); + + String prefix = BotConfig.DEFAULT_PREFIX; + + if (args.length == 0) { + channel.sendMessage(buildCategoryHelp(prefix)); + } + if (args.length == 1) { + if (isCategory(args[0])) { + channel.sendMessage(buildTargetCategory(prefix, args[0])); + } + else if (isCommand(args[0])) { + channel.sendMessage(buildCommandHelp(channel, prefix, args[0])); + } + else { + channel.sendMessage("No match found"); + } + } + } + + private EmbedBuilder buildCategoryHelp(String prefix) { + StringBuilder builder = new StringBuilder(); + builder.append("```yml"); + List categories = new ArrayList<>(); + + for (Command cmd : guthix.listCommands()) { + String category = cmd.category(); + if (!categories.contains(category)) { + categories.add(category); + + builder.append("\n -").append(category); + } + } + + builder.append("```"); + + return new EmbedBuilder() + .setTitle("Command Categories") + .setColor(Color.GREEN) + .addField("Prefix", String.format("```%s```", prefix)) + .addField("Categories", builder.toString()) + .setFooter(String.format("Use `%shelp to see commands in the category", prefix)); + } + + private EmbedBuilder buildTargetCategory(String prefix, String category) { + StringBuilder builder = new StringBuilder().append("```yml"); + + for (Command cmd : guthix.listCommands()) { + String cat = cmd.category().toLowerCase(); + if (cat.equals(category.toLowerCase())) { + builder.append("\n- ").append(cmd.name()); + } + } + + builder.append("```"); + + return new EmbedBuilder() + .setTitle(String.format("%s Commands", category)) + .setColor(Color.GREEN) + .addField("Prefix", String.format("```%s```", prefix)) + .addField("Commands:", builder.toString()) + .setFooter(String.format("Use `%shelp to see command info", prefix)); + } + + private EmbedBuilder buildCommandHelp(TextChannel channel, String prefix, String command) { + + String targetedCommand = null; + Collection targetedAliases = null; + String targetedDescription = null; + String targetedUsage = null; + CommandPermission targetedPermissions = null; // TODO: Make a toString() method for this eh? + + for (Command cmd : guthix.listCommands()) { + for (String alias : cmd.aliases()) { + if (alias.equals(command)) { + targetedCommand = cmd.name(); + targetedAliases = cmd.aliases(); + targetedDescription = cmd.description(); + targetedUsage = cmd.usage(); + targetedPermissions = cmd.permission(); + } + } + } + + if (targetedCommand == null) { + return new EmbedBuilder() + .setColor(Color.RED) + .addField("ERROR:", "That command does not exist"); + } + + StringBuilder builder = new StringBuilder(); + builder.append("```yml\n"); + + if (targetedAliases.size() != 0) { + builder.append(String.format("- Aliases: %s\n", String.join(", ", targetedAliases))); + } + if (!targetedDescription.equals("none")) { + builder.append(String.format("- Description: %s\n", targetedDescription)); + } + if (!targetedUsage.equals("")) { + builder.append(String.format("- Usage: %s\n", targetedUsage)); + } + builder.append(String.format("- Permissions: %s\n", targetedPermissions)); + + builder.append("```"); + + return new EmbedBuilder() + .setColor(Color.GREEN) + .setTitle(String.format("%s%s", prefix, targetedCommand)) + .setDescription(builder.toString()); + } + + public boolean isCommand(String input) { + for (Command cmd : guthix.listCommands()) { + Collection aliases = cmd.aliases(); + for (String alias : aliases) { + if (alias.toLowerCase().equals(input.toLowerCase())) { + return true; + } + } + } + + return false; + } + + public boolean isCategory(String input) { + for (Command cmd : guthix.listCommands()) { + String category = cmd.category(); + if (category.toLowerCase().equals(input.toLowerCase())) { + return true; + } + } + + return false; + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/general/OutfitCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/general/OutfitCommand.java new file mode 100644 index 0000000..b152310 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/general/OutfitCommand.java @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.general; + +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; +import org.javacord.api.entity.channel.TextChannel; +import org.javacord.api.entity.message.Message; +import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +public class OutfitCommand extends Command { + @Override public String name() { return "Outfit"; } + @Override public String description() { return "Generates a random image with the given tag. Use ~tags to see valid tags."; } + @Override public String usage() { return "outfit "; } + @Override public String category() { return "General"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.NONE); } + @Override public Collection aliases() { return new ArrayList<>(Arrays.asList("outfit", "o")); } + + private final DatabaseUtilities db; + + public OutfitCommand(DatabaseUtilities db) { + this.db = db; + } + + @Override + public void onCommand(CommandContext ctx) { + String[] args = ctx.getArgs(); + TextChannel channel = ctx.getChannel(); + Message message = ctx.getMessage(); + // Find the first arg for tag/submitter/naught, find the next arg for number to send + + int MAX_OUTFITS = 5; + + if (args.length == 0) { + channel.sendMessage(db.randomOutfit().generateEmbed()); + } + else if (message.getMentionedUsers().size() > 0) { + // Outfit needs to be from submitter + String userId = message.getMentionedUsers().get(0).getIdAsString(); + + if (args.length == 2) { + // Send a number of outfits + int iterator = ((Integer.parseInt(args[1]) > MAX_OUTFITS) ? MAX_OUTFITS : Integer.parseInt(args[1])); + + for (int i = 0; i < iterator; i++) { + channel.sendMessage(db.getOutfitBySubmitter(userId).generateEmbed()); + } + } + else { + channel.sendMessage(db.getOutfitBySubmitter(userId).generateEmbed()); + } + } + else if (db.getTags().contains(args[0].toLowerCase())) { + // Outfit needs to be tagged + String tag = args[0]; + + if (args.length == 2) { + // Send a number of outfits + int iterator = ((Integer.parseInt(args[1]) > MAX_OUTFITS) ? MAX_OUTFITS : Integer.parseInt(args[1])); + + for (int i = 0; i < iterator; i++) { + channel.sendMessage(db.randomTaggedOutfit(tag).generateEmbed()); + } + } + else { + channel.sendMessage(db.randomTaggedOutfit(tag).generateEmbed()); + } + } + else if (args.length == 1 && args[0].matches("\\d")) { + int iterator = ((Integer.parseInt(args[0]) > MAX_OUTFITS) ? MAX_OUTFITS : Integer.parseInt(args[0])); + + for (int i = 0; i < iterator; i++) { + channel.sendMessage(db.randomOutfit().generateEmbed()); + } + } + else { + channel.sendMessage("Something went wrong"); + } + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/general/PingCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/general/PingCommand.java new file mode 100644 index 0000000..5ef8811 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/general/PingCommand.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.general; + +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; + +import java.util.*; + +public class PingCommand extends Command { + @Override public String name() { return "Ping"; } + @Override public String description() { return "Pings the bot, checks for a heartbeat"; } + @Override public String usage() { return "ping"; } + @Override public String category() { return "General"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.NONE); } + @Override public Collection aliases() { return new ArrayList<>(Collections.singletonList("ping")); } + + @Override + public void onCommand(CommandContext ctx) { + final long start = System.currentTimeMillis(); + + ctx.getChannel().sendMessage("I'm alive").thenAccept(msg -> { + final long end = System.currentTimeMillis(); + msg.edit(String.format("I'm alive! (%sms)", (end - start))); + }); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/general/SpecificOutfitCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/general/SpecificOutfitCommand.java new file mode 100644 index 0000000..f80087a --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/general/SpecificOutfitCommand.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.general; + +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; +import org.javacord.api.entity.channel.ServerTextChannel; +import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities; +import org.javacord.api.entity.channel.TextChannel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.ArrayBlockingQueue; + +public class SpecificOutfitCommand extends Command { + @Override public String name() { return "Specific Outfit"; } + @Override public String description() { return "Generates an outfit of a specific tag"; } + @Override public String usage() { return " [int number]"; } + @Override public String category() { return "General"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.NONE); } + @Override public Collection aliases() { return new ArrayList<>(db.getTags()); } + + private final DatabaseUtilities db; + + public SpecificOutfitCommand(DatabaseUtilities db) { + this.db = db; + } + + @Override + public void onCommand(CommandContext ctx) { + String[] args = ctx.getArgs(); + String tag = ctx.getUsedAlias(); + TextChannel channel = ctx.getChannel(); + + if (args.length == 1) { + // Send the specific number of outfits with a max of 5 + int outfitNumber = Integer.parseInt(args[0]); + if (outfitNumber > 5) { outfitNumber = 5; } + + for (int i = 0; i < outfitNumber; i++) { + channel.sendMessage(db.randomTaggedOutfit(tag).generateEmbed()); + } + } + else if (args.length > 1) { + channel.sendMessage("You did that wrong, mate"); + } + else { + channel.sendMessage(db.randomTaggedOutfit(tag).generateEmbed()); + } + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/staff/AddColorCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/staff/AddColorCommand.java new file mode 100644 index 0000000..436b734 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/staff/AddColorCommand.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.staff; + +import dev.salmonllama.fsbot.config.BotConfig; +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; +import dev.salmonllama.fsbot.utilities.database.RoleColourUtility; +import dev.salmonllama.fsbot.utilities.warnings.Warning; +import org.javacord.api.DiscordApi; +import org.javacord.api.entity.channel.TextChannel; +import org.javacord.api.entity.message.embed.EmbedBuilder; +import org.javacord.api.entity.permission.Role; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +public class AddColorCommand extends Command { + @Override public String name() { return "Add Color"; } + @Override public String description() { return "adds the provided role to the toggleable cosmetic roles."; } + @Override public String usage() { return "addcolor "; } + @Override public String category() { return "Staff"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); } + @Override public Collection aliases() { return new ArrayList<>(Arrays.asList("addcolor", "addcolour", "addclr")); } + + @Override + public void onCommand(CommandContext ctx) { + String[] args = (String[]) ctx.getArgs(); + TextChannel channel = ctx.getChannel(); + DiscordApi api = ctx.getApi(); + + if (args.length != 2) { + channel.sendMessage(new Warning("Insufficient arguments").sendWarning()); + return; + } + + String colour = args[0]; + String id = args[1]; + + Role role = api.getRoleById(args[1]).orElse(null); + if (role == null) { + channel.sendMessage(new Warning("Supplied roleId isn't a roleId").sendWarning()); + return; + } + + RoleColourUtility.addColourRole(colour, id); + + channel.sendMessage(new EmbedBuilder() + .setTitle(role.getName()) + .setColor(role.getColor().orElse(Color.GREEN)) + .addField("Role id:", role.getIdAsString()) + .addField("Colour to be stored as:", args[0]) + ); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/staff/EchoCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/staff/EchoCommand.java new file mode 100644 index 0000000..0ee01d5 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/staff/EchoCommand.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.staff; + +import dev.salmonllama.fsbot.config.BotConfig; +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +public class EchoCommand extends Command { + @Override public String name() { return "Echo"; } + @Override public String description() { return "Echos your message. Typical bash"; } + @Override public String usage() { return "echo "; } + @Override public String category() { return "Staff"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); } + @Override public Collection aliases() { return new ArrayList<>(Collections.singletonList("echo")); } + + @Override + public void onCommand(CommandContext ctx) { + String echoMessage = String.join(" ", ctx.getArgs()); + + ctx.getMessage().delete(); + + ctx.reply(echoMessage); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/staff/GetOutfitCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/staff/GetOutfitCommand.java new file mode 100644 index 0000000..45c1c0e --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/staff/GetOutfitCommand.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.staff; + +import com.rethinkdb.RethinkDB; +import com.rethinkdb.net.Connection; +import com.rethinkdb.net.Cursor; +import dev.salmonllama.fsbot.config.BotConfig; +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; +import org.javacord.api.entity.channel.TextChannel; +import org.javacord.api.entity.message.embed.EmbedBuilder; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +public class GetOutfitCommand extends Command { // TODO: This command also needs HELP + @Override public String name() { return "Get Outift"; } + @Override public String description() { return "Shows the outfit, given an ID"; } + @Override public String usage() { return "getoutfit "; } + @Override public String category() { return "Staff"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); } + @Override public Collection aliases() { return new ArrayList<>(Arrays.asList("getoutfit", "get")); } + + static final RethinkDB r = RethinkDB.r; + static final Connection conn = r.connection().hostname("localhost").port(28015).connect(); + + @Override + public void onCommand(CommandContext ctx) { + String[] args = ctx.getArgs(); + TextChannel channel = ctx.getChannel(); + + if (args.length != 1) { + channel.sendMessage("you did that wrong mate"); + return; + } + + String id = args[0]; + + // Check if the given id exists in the table + try { + r.db("fsbot").table("outfits").filter(row -> row.getField("id").eq(id)).run(conn); + } + catch (Exception e) { + EmbedBuilder embed = new EmbedBuilder() + .setColor(Color.RED) + .addField("ERROR:", "That id does not exist in that table") + .addField("Output:", e.toString()) + .setFooter("Contact Salmonllama#5727 if you see this"); + channel.sendMessage(embed); + return; + } + + // Get the entry for the given table/tag and id, build the embed, and send it. + try { + + String embedLink = null; + String embedSubmitter = null; + + Cursor linkCursor = r.db("fsbot") + .table("outfits") + .filter(row -> row.getField("id").eq(id)) + .getField("link") + .run(conn); + + Cursor submitterCursor = r.db("fsbot") + .table("outfits") + .filter(row -> row.getField("id").eq(id)) + .getField("submitter") + .run(conn); + + List links = linkCursor.toList(); + for (Object link : links) { + embedLink = link.toString(); + } + + List submitters = submitterCursor.toList(); + for (Object submitter : submitters) { + embedSubmitter = submitter.toString(); + } + + // Send a diff message if any of the things are null + if (embedLink == null) { + EmbedBuilder embed = new EmbedBuilder() + .setColor(Color.RED) + .addField("ERROR: ", "The entry you tried to access doesn't exist."); + channel.sendMessage(embed); + return; + } + + // Send the final Embed with the outfit image as a link, the submitter, and the id of the image. + EmbedBuilder embed = new EmbedBuilder() + .setColor(Color.GREEN) + .setAuthor(embedSubmitter) + .setImage(embedLink) + .setFooter(id); + channel.sendMessage(embed); + } + catch (Exception e) { + EmbedBuilder embed = new EmbedBuilder() + .setColor(Color.RED) + .addField("ERROR:", "Something went wrong...") + .addField("Output:", e.toString()) + .setFooter("Contact Crablet#9999 and show him this error."); + channel.sendMessage(embed); + } + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/staff/GetServersCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/staff/GetServersCommand.java new file mode 100644 index 0000000..938f2a9 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/staff/GetServersCommand.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.staff; + +import dev.salmonllama.fsbot.config.BotConfig; +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; +import org.javacord.api.DiscordApi; +import org.javacord.api.entity.message.embed.EmbedBuilder; +import org.javacord.api.entity.server.Server; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +public class GetServersCommand extends Command { + @Override public String name() { return "Get Servers"; } + @Override public String description() { return "Lists all the servers the bot is in"; } + @Override public String usage() { return "getservers"; } + @Override public String category() { return "Staff"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); } + @Override public Collection aliases() { return new ArrayList<>(Arrays.asList("getservers", "servers")); } + + @Override + public void onCommand(CommandContext ctx) { + DiscordApi api = ctx.getApi(); + + StringBuilder builder = new StringBuilder(); + builder.append("```md"); + + for (Server server : api.getServers()) { + String serverId = server.getIdAsString(); + String serverName = server.getName(); + + builder.append(String.format("\n- %s: %s", serverName, serverId)); + } + + builder.append("\n```"); + + EmbedBuilder embed = new EmbedBuilder() + .setColor(Color.GREEN) + .setTitle("Servers") + .setDescription(builder.toString()) + .setFooter(String.format("%s is in %d servers", api.getYourself().getName(), api.getServers().size())); + + ctx.getChannel().sendMessage(embed).join(); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/staff/OutfitInfoCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/staff/OutfitInfoCommand.java new file mode 100644 index 0000000..5b9bd96 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/staff/OutfitInfoCommand.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.staff; + +import dev.salmonllama.fsbot.config.BotConfig; +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; +import org.javacord.api.DiscordApi; +import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities; +import dev.salmonllama.fsbot.utilities.exceptions.DiscordError; +import dev.salmonllama.fsbot.utilities.exceptions.OutfitNotFoundException; +import org.javacord.api.entity.channel.TextChannel; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +public class OutfitInfoCommand extends Command { + @Override public String name() { return "Outfit Info"; } + @Override public String description() { return "Shows all related info about the outfit"; } + @Override public String usage() { return "outfitinfo "; } + @Override public String category() { return "Staff"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); } + @Override public Collection aliases() { return new ArrayList<>(Arrays.asList("outfitinfo", "oinfo")); } + + private final DatabaseUtilities db; + + public OutfitInfoCommand(DatabaseUtilities db) { + this.db = db; + } + + @Override + public void onCommand(CommandContext ctx) { + String[] args = ctx.getArgs(); + DiscordApi api = ctx.getApi(); + TextChannel channel = ctx.getChannel(); + + if (args.length != 1) { + channel.sendMessage(String.format("you did that wrong. bother %s to make these more specific.", api.getOwner().join().getDiscriminatedName())); + return; + } + + try { + channel.sendMessage(db.getOutfitFromId(args[0]).generateInfo()); + } + catch (OutfitNotFoundException e) { + channel.sendMessage(new DiscordError(e.getMessage()).get()); + } + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/staff/RemoveOutfitCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/staff/RemoveOutfitCommand.java new file mode 100644 index 0000000..deb3424 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/staff/RemoveOutfitCommand.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.staff; + +import com.vdurmont.emoji.EmojiParser; +import dev.salmonllama.fsbot.config.BotConfig; +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; +import org.javacord.api.entity.channel.TextChannel; +import org.javacord.api.entity.user.User; +import dev.salmonllama.fsbot.listeners.ReactionDeleteConfirmationListener; +import dev.salmonllama.fsbot.utilities.Outfit; +import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities; +import dev.salmonllama.fsbot.utilities.exceptions.DiscordError; +import dev.salmonllama.fsbot.utilities.exceptions.OutfitNotFoundException; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +public class RemoveOutfitCommand extends Command { + @Override public String name() { return "Remove Outfit"; } + @Override public String description() { return "Removes an outfit from the database given an id"; } + @Override public String usage() { return "remove "; } + @Override public String category() { return "Staff"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); } + @Override public Collection aliases() { return new ArrayList<>(Arrays.asList("removeoutfit", "remove")); } + + private final DatabaseUtilities db; + + public RemoveOutfitCommand(DatabaseUtilities db) { + this.db = db; + } + + @Override + public void onCommand(CommandContext ctx) { + String[] args = ctx.getArgs(); + TextChannel channel = ctx.getChannel(); + User author = ctx.getUser(); + + if (args.length != 1) { + channel.sendMessage("You did that wrong, mate"); + return; + } + + // get the outfit, confirm deletion through reaction. + try { + Outfit outfit = db.getOutfitFromId(args[0]); + + channel.sendMessage(outfit.generateInfo().setTitle("Confirm Outfit Deletion")).thenAccept(message -> { + message.addReaction(EmojiParser.parseToUnicode(":white_check_mark:")); + message.addReaction(EmojiParser.parseToUnicode(":octagonal_sign:")); + message.addReactionAddListener(new ReactionDeleteConfirmationListener(author, message, outfit, db)); + }); + } + catch (OutfitNotFoundException e) { + channel.sendMessage(new DiscordError(e.getMessage()).get()); + } + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/staff/RetagCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/staff/RetagCommand.java new file mode 100644 index 0000000..ee9f059 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/staff/RetagCommand.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.staff; + +import com.vdurmont.emoji.EmojiParser; +import dev.salmonllama.fsbot.config.BotConfig; +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; +import org.javacord.api.entity.channel.TextChannel; +import org.javacord.api.entity.user.User; +import dev.salmonllama.fsbot.listeners.ReactionRetagConfirmationListener; +import dev.salmonllama.fsbot.utilities.Outfit; +import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities; +import dev.salmonllama.fsbot.utilities.exceptions.DiscordError; +import dev.salmonllama.fsbot.utilities.exceptions.OutfitNotFoundException; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +public class RetagCommand extends Command { + @Override public String name() { return "Retag"; } + @Override public String description() { return "Changes the tag of the given outfit"; } + @Override public String usage() { return "retag "; } + @Override public String category() { return "Staff"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); } + @Override public Collection aliases() { return new ArrayList<>(Collections.singletonList("retag")); } + + private final DatabaseUtilities db; + + public RetagCommand(DatabaseUtilities db) { + this.db = db; + } + + @Override + public void onCommand(CommandContext ctx) { + String[] args = ctx.getArgs(); + TextChannel channel = ctx.getChannel(); + User author = ctx.getUser(); + + if (args.length != 2) { + channel.sendMessage("You did that wrong mate. check the help command."); + return; + } + + try { + Outfit outfit = this.db.getOutfitFromId(args[0]); + + channel.sendMessage(outfit.generateInfo().setTitle(String.format("Update tag to %s?", args[1]))).thenAcceptAsync(message -> { + message.addReaction(EmojiParser.parseToUnicode(":white_check_mark:")); + message.addReaction(EmojiParser.parseToUnicode(":octagonal_sign:")); + message.addReactionAddListener(new ReactionRetagConfirmationListener(author, message, outfit, db, outfit.getTag(), args[1])); + }); + } + catch (OutfitNotFoundException e) { + channel.sendMessage(new DiscordError(e.getMessage()).get()); + } + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/staff/SetStatusCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/staff/SetStatusCommand.java new file mode 100644 index 0000000..8ab296c --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/staff/SetStatusCommand.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.staff; + +import com.vdurmont.emoji.EmojiParser; +import dev.salmonllama.fsbot.config.BotConfig; +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; +import org.javacord.api.entity.message.embed.EmbedBuilder; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +public class SetStatusCommand extends Command { + @Override public String name() { return "Set Status"; } + @Override public String description() { return "Updates the bot's current status"; } + @Override public String usage() { return "updatestatus "; } + @Override public String category() { return "Staff"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); } + @Override public Collection aliases() { return new ArrayList<>(Arrays.asList("setstatus", "status")); } + + @Override + public void onCommand(CommandContext ctx) { + String[] args = ctx.getArgs(); + + // argument checking + if (args.length == 0) { + EmbedBuilder embed = new EmbedBuilder() + .setColor(Color.YELLOW) + .addField("WARNING", "Not enough arguments supplied"); + ctx.getChannel().sendMessage(embed); + return; + } + + String status = String.join(" ", args); + + ctx.getApi().updateActivity(status); + + ctx.getMessage().addReaction(EmojiParser.parseToUnicode(":white_check_mark:")); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/commands/staff/WelcomeMessageCommand.java b/src/main/java/dev/salmonllama/fsbot/commands/staff/WelcomeMessageCommand.java new file mode 100644 index 0000000..c1b502a --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/commands/staff/WelcomeMessageCommand.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.commands.staff; + +import dev.salmonllama.fsbot.config.BotConfig; +import dev.salmonllama.fsbot.guthix.Command; +import dev.salmonllama.fsbot.guthix.CommandContext; +import dev.salmonllama.fsbot.guthix.CommandPermission; +import dev.salmonllama.fsbot.guthix.PermissionType; +import dev.salmonllama.fsbot.utilities.database.ServerConfUtility; +import dev.salmonllama.fsbot.utilities.warnings.Warning; +import org.javacord.api.entity.channel.TextChannel; +import org.javacord.api.entity.message.embed.EmbedBuilder; +import org.javacord.api.entity.server.Server; + +import java.awt.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +public class WelcomeMessageCommand extends Command { + @Override public String name() { return "Welcome Message"; } + @Override public String description() { return "View or update the server welcome message. Options: get|set|getchannel|setchannel."; } + @Override public String usage() { return "welcomemessage [String newMessage]"; } + @Override public String category() { return "Staff"; } + @Override public CommandPermission permission() { return new CommandPermission(PermissionType.ROLE, BotConfig.STAFF_ROLE); } + @Override public Collection aliases() { return new ArrayList<>(Arrays.asList("welcomemessage", "wmsg")); } + + @Override + public void onCommand(CommandContext ctx) { + String[] args = ctx.getArgs(); + TextChannel channel = ctx.getChannel(); + Server server = ctx.getServer(); + + if (!server.getIdAsString().equals(BotConfig.HOME_SERVER)) { + return; + } + + if (args.length == 0) channel.sendMessage(new Warning("Not enough arguments provided").sendWarning()); + + switch (args[0]) { + case ("get"): + channel.sendMessage(fetchWelcomeMsg(server)); + break; + case ("set"): + StringBuilder sb = new StringBuilder(); + for (int i = 1; i < args.length; i++) { + sb.append(String.format("%s ", args[i])); + } + + channel.sendMessage(updateWelcomeMsg(sb.toString(), server)); + break; + case ("getchannel"): + channel.sendMessage(fetchWelcomeChannel(server)); + break; + case ("setchannel"): + if (args.length < 2) channel.sendMessage(new Warning("Not enough arguments provided").sendWarning()); + + channel.sendMessage(updateWelcomeChannel(args[1], server)); + break; + } + } + + private EmbedBuilder fetchWelcomeMsg(Server server) { + + ServerConfUtility conf = new ServerConfUtility(server.getIdAsString()); + String msg = conf.getWelcomeMsg(); + + return new EmbedBuilder() + .setColor(Color.GREEN) + .setTitle("Current welcome message:") + .setDescription(msg); + } + + private EmbedBuilder updateWelcomeMsg(String msg, Server server) { + + ServerConfUtility conf = new ServerConfUtility(server.getIdAsString()); + conf.setWelcomeMsg(msg); + + return new EmbedBuilder() + .setColor(Color.GREEN) + .setTitle("Welcome message updated") + .addField("New welcome message:", msg); + } + + private EmbedBuilder fetchWelcomeChannel(Server server) { + + ServerConfUtility conf = new ServerConfUtility(server.getIdAsString()); + String welcomeChannel = conf.getWelcomeChannel(); + + return new EmbedBuilder() + .setColor(Color.GREEN) + .setTitle("Current welcome channel:") + .setDescription(welcomeChannel); + } + + private EmbedBuilder updateWelcomeChannel(String id, Server server) { + + ServerConfUtility conf = new ServerConfUtility(server.getIdAsString()); + conf.setWelcomeChannel(id); + + return new EmbedBuilder() + .setColor(Color.GREEN) + .setTitle("Welcome channel updated:") + .addField("New welcome channel:", id); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/config/BotConfig.java b/src/main/java/dev/salmonllama/fsbot/config/BotConfig.java new file mode 100644 index 0000000..180e55f --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/config/BotConfig.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.config; + +import com.kaaz.configuration.ConfigurationBuilder; +import com.kaaz.configuration.ConfigurationOption; + +import java.io.File; + +public class BotConfig { + @ConfigurationOption + public static String TOKEN = "token-goes-here"; + + @ConfigurationOption + public static String DB_HOST = "localhost"; + + @ConfigurationOption + public static int DB_PORT = 28015; + + @ConfigurationOption + public static String DEFAULT_PREFIX = "~"; + + @ConfigurationOption + public static String BOT_OWNER = "owner's id here"; + + @ConfigurationOption + public static String STAFF_ROLE = "staff role id here"; + + @ConfigurationOption + public static String REPORT_CHANNEL = "report channel"; + + @ConfigurationOption + public static String ACHIEVEMENT_CHANNEL = "achievement channel here"; + + @ConfigurationOption + public static String ANNOUNCEMENT_CHANNEL = "announcement channel here"; + + @ConfigurationOption + public static String NEWS_CHANNEL = "news channel here"; + + @ConfigurationOption + public static String VOTE_CHANNEL = "vote channel here"; + + @ConfigurationOption + public static String REPORT_LOG = "report_log_channel"; + + @ConfigurationOption + public static String OUTFIT_LOG = "outfit log channel"; + + @ConfigurationOption + public static String BOT_LOG = "bot_log_channel"; + + @ConfigurationOption + public static String HYDRIX_ROLE = "hydrix role id here"; + + @ConfigurationOption + public static String IMGUR_ID = "imgur_id_here"; + + @ConfigurationOption + public static String IMGUR_BEARER = "imgur bearer here"; + + @ConfigurationOption + public static String HOME_SERVER = "340511685024546816"; + + public static void initConfig(String filePath) { + try { + new ConfigurationBuilder(BotConfig.class, new File(filePath)).build(false); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/guthix/Command.java b/src/main/java/dev/salmonllama/fsbot/guthix/Command.java new file mode 100644 index 0000000..2479c8a --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/guthix/Command.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.guthix; + +import java.util.Collection; + +public abstract class Command { + public abstract String name(); + public abstract String description(); + public abstract String usage(); + public abstract String category(); + public abstract CommandPermission permission(); + public abstract Collection aliases(); + + public abstract void onCommand(CommandContext ctx); + + public void invoke(final CommandContext ctx) { + Thread thread = new Thread(() -> onCommand(ctx)); + thread.start(); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/guthix/CommandContext.java b/src/main/java/dev/salmonllama/fsbot/guthix/CommandContext.java new file mode 100644 index 0000000..0fa5aa3 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/guthix/CommandContext.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.guthix; + +import org.javacord.api.DiscordApi; +import org.javacord.api.entity.channel.TextChannel; +import org.javacord.api.entity.message.Message; +import org.javacord.api.entity.message.MessageAuthor; +import org.javacord.api.entity.message.embed.EmbedBuilder; +import org.javacord.api.entity.permission.Role; +import org.javacord.api.entity.server.Server; +import org.javacord.api.entity.user.User; +import dev.salmonllama.fsbot.config.BotConfig; + +import java.util.Collection; +import java.util.concurrent.CompletableFuture; + +public class CommandContext { + private DiscordApi api; + private Message message; + private MessageAuthor author; + private TextChannel channel; + private Server server; + private Command usedCommand; + private String usedAlias; + private String[] args; + + CommandContext(DiscordApi api, Message message, MessageAuthor author, TextChannel channel, Server server, String alias, Command cmd, String[] args) { + this.api = api; + this.message = message; + this.author = author; + this.channel = channel; + this.server = server; + this.usedCommand = cmd; + this.usedAlias = alias; + this.args = args; + } + + public DiscordApi getApi() { + return api; + } + + public Message getMessage() { + return message; + } + + public MessageAuthor getAuthor() { + return author; + } + + public TextChannel getChannel() { + return channel; + } + + public Server getServer() { + return server; + } + + public Command getUsedCommand() { + return usedCommand; + } + + public String getUsedAlias() { + return usedAlias; + } + + public String[] getArgs() { + return args; + } + + public User getUser() { + if (author.asUser().isPresent()) { + return author.asUser().get(); + } else { + // Log something to discord, I dunno + } + return null; + } + + public Collection getUserRoles() { + User user = getUser(); + return user.getRoles(getServer()); + } + + public boolean isUserOwner() { + return getUser().getIdAsString().equals(BotConfig.BOT_OWNER); + } + + public CompletableFuture reply(String msg) { + return channel.sendMessage(msg); + } + + public CompletableFuture reply(EmbedBuilder embed) { + return channel.sendMessage(embed); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/guthix/CommandPermission.java b/src/main/java/dev/salmonllama/fsbot/guthix/CommandPermission.java new file mode 100644 index 0000000..b6ef613 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/guthix/CommandPermission.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.guthix; + +public class CommandPermission { + private PermissionType type; + private String value; + + public CommandPermission(PermissionType type) { + this.type = type; + } + + public CommandPermission(PermissionType type, String value) { + this.type = type; + this.value = value; + } + + public PermissionType getType() { + return type; + } + + public String getValue() { + return value; + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/guthix/ContextBuilder.java b/src/main/java/dev/salmonllama/fsbot/guthix/ContextBuilder.java new file mode 100644 index 0000000..39f7b04 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/guthix/ContextBuilder.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.guthix; + +import org.javacord.api.DiscordApi; +import org.javacord.api.entity.channel.TextChannel; +import org.javacord.api.entity.message.Message; +import org.javacord.api.entity.message.MessageAuthor; +import org.javacord.api.entity.server.Server; + +import java.util.Collection; + +public class ContextBuilder { + private DiscordApi api; + private Message message; + private MessageAuthor author; + private TextChannel channel; + private Server server; + private Command usedCommand; + private String usedAlias; + private String[] args; + + public ContextBuilder() { + + } + + public ContextBuilder setUsedCommand(Command commandUsed) { + this.usedCommand = commandUsed; + return this; + } + + public ContextBuilder setUsedAlias(String alias) { + this.usedAlias = alias; + return this; + } + + public ContextBuilder setArgs(String[] args) { + this.args = args; + return this; + } + + ContextBuilder setMessage(Message message) { + this.message = message; + return this; + } + + ContextBuilder setAuthor(MessageAuthor author) { + this.author = author; + return this; + } + + ContextBuilder setChannel(TextChannel channel) { + this.channel = channel; + return this; + } + + ContextBuilder setServer(Server server) { + this.server = server; + return this; + } + + ContextBuilder setApi(DiscordApi api) { + this.api = api; + return this; + } + + CommandContext build() { + return new CommandContext(api, message, author, channel, server, usedAlias, usedCommand, args); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/guthix/Guthix.java b/src/main/java/dev/salmonllama/fsbot/guthix/Guthix.java new file mode 100644 index 0000000..469817e --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/guthix/Guthix.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.guthix; + +import dev.salmonllama.fsbot.commands.general.HelpCommand; +import dev.salmonllama.fsbot.commands.general.SpecificOutfitCommand; +import dev.salmonllama.fsbot.commands.staff.OutfitInfoCommand; +import dev.salmonllama.fsbot.commands.general.OutfitCommand; +import dev.salmonllama.fsbot.commands.staff.*; +import dev.salmonllama.fsbot.commands.developer.InviteCommand; +import dev.salmonllama.fsbot.commands.developer.CreateGalleryCommand; +import dev.salmonllama.fsbot.commands.general.ColorsCommand; +import dev.salmonllama.fsbot.commands.general.ColorCommand; +import dev.salmonllama.fsbot.commands.general.PingCommand; +import dev.salmonllama.fsbot.commands.developer.EvalCommand; +import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities; +import org.javacord.api.DiscordApi; +import org.javacord.api.entity.channel.TextChannel; +import org.javacord.api.entity.message.Message; +import org.javacord.api.entity.message.MessageAuthor; +import org.javacord.api.entity.server.Server; +import org.javacord.api.event.message.MessageCreateEvent; +import org.javacord.api.listener.message.MessageCreateListener; +import dev.salmonllama.fsbot.commands.developer.TestCommand; + +import java.util.Collection; +import java.util.HashMap; + +/* +* Guthix is Fashionscape Bot's command repository and dispatcher + */ +public class Guthix implements MessageCreateListener { + private DiscordApi api; + private DatabaseUtilities db; + + private Registry registry; + private PermissionManager manager; + + public Guthix(DiscordApi api, DatabaseUtilities db) { + this.api = api; + api.addMessageCreateListener(this); + + this.db = db; + + manager = new PermissionManager(api); + registry = new Registry(); + + initCommands(); + } + + public void initCommands() { + // Developer Commands + addCommand(new TestCommand()); + addCommand(new EvalCommand(db)); + addCommand(new CreateGalleryCommand()); + addCommand(new InviteCommand()); + + // Staff Commands + addCommand(new EchoCommand()); + addCommand(new GetServersCommand()); + addCommand(new AddColorCommand()); + addCommand(new GetOutfitCommand()); + addCommand(new RetagCommand(db)); + addCommand(new RemoveOutfitCommand(db)); + addCommand(new OutfitInfoCommand(db)); + addCommand(new SetStatusCommand()); + addCommand(new WelcomeMessageCommand()); + + // General Commands + addCommand(new PingCommand()); + addCommand(new ColorCommand()); + addCommand(new ColorsCommand()); + addCommand(new OutfitCommand(db)); + addCommand(new SpecificOutfitCommand(db)); + addCommand(new HelpCommand(this)); + } + + public void addCommand(Command cmd) { + registry.addCommand(cmd); +// return cmd; + } + + public Collection listCommands() { + return registry.listCommands(); + } + + public HashMap mapCommands() { + return registry.mapCommands(); + } + + @Override + public void onMessageCreate(MessageCreateEvent event) { + MessageAuthor author = event.getMessageAuthor(); + + if (manager.sourceIsValid(author)) { + + } else { + return; + } + + String content = event.getMessageContent().toLowerCase(); + + if (registry.startsWithPrefix(content)) { + + } else { + return; + } + + String cmdString = registry.commandString(content); + + if (registry.isCommandAlias(cmdString)) { + + } else { + return; + } + + Message msg = event.getMessage(); + TextChannel channel = event.getChannel(); + Server server = event.getServer().orElse(null); + String[] cmdArgs = registry.getCmdArgs(content); + + Command cmd = registry.findCommand(cmdString).orElse(null); // TODO: default command here + CommandContext ctx = new ContextBuilder().setApi(api) + .setServer(server) + .setAuthor(author) + .setChannel(channel) + .setMessage(msg) + .setArgs(cmdArgs) + .setUsedAlias(cmdString) + .setUsedCommand(cmd) + .build(); + + if (manager.hasPermission(cmd.permission(), ctx)) { + + } else { + return; + } + + cmd.invoke(ctx); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/guthix/PermissionManager.java b/src/main/java/dev/salmonllama/fsbot/guthix/PermissionManager.java new file mode 100644 index 0000000..9d25e5e --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/guthix/PermissionManager.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.guthix; + +import org.javacord.api.DiscordApi; +import org.javacord.api.entity.message.MessageAuthor; + +public class PermissionManager { + private DiscordApi api; + + public PermissionManager(DiscordApi api) { + this.api = api; + } + + public boolean hasPermission(CommandPermission reqPerm, CommandContext ctx) { + PermissionType permtype = reqPerm.getType(); + + switch (permtype) { + case NONE: + return true; + case ROLE: + // If the author has the role, yes. Doesn't work in DM + return roleHandler(reqPerm.getValue(), ctx); + case OWNER: + // If the author is the owner, yes. + return ownerHandler(ctx); + } + + return false; + } + + private boolean roleHandler(String roleId, CommandContext ctx) { + return ctx.getUserRoles().stream().anyMatch(role -> role.getIdAsString().equals(roleId)); + } + + private boolean ownerHandler(CommandContext ctx) { + return ctx.isUserOwner(); + } + + boolean sourceIsValid(MessageAuthor author) { + return !author.isBotUser() && !author.isWebhook(); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/guthix/PermissionType.java b/src/main/java/dev/salmonllama/fsbot/guthix/PermissionType.java new file mode 100644 index 0000000..b20e97b --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/guthix/PermissionType.java @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.guthix; + +public enum PermissionType { + ROLE, // User has a specific role inside the server + OWNER, // User is the bot owner + NONE // No requirement +} diff --git a/src/main/java/dev/salmonllama/fsbot/guthix/Registry.java b/src/main/java/dev/salmonllama/fsbot/guthix/Registry.java new file mode 100644 index 0000000..206d71f --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/guthix/Registry.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.guthix; + +import dev.salmonllama.fsbot.config.BotConfig; + +import java.util.*; +import java.util.function.Predicate; + +class Registry { + private Collection commands; + private HashMap commandsMap; + + Registry() { + commands = new ArrayList<>(); + commandsMap = new HashMap<>(); + } + + Predicate valueMatch(String input) { // TODO: Move to a helper class + return str -> str.equals(input); + } + + Command addCommand(Command cmd) { + commands.add(cmd); + commandsMap.put(cmd.name(), cmd); + return cmd; + } + + Collection listCommands() { + return commands; + } + + HashMap mapCommands() { + return commandsMap; + } + + boolean startsWithPrefix(String input) { + return input.startsWith(BotConfig.DEFAULT_PREFIX); + } + + Collection getCommandAliases() { + Collection aliases = new ArrayList<>(); + + commands.forEach(cmd -> { + aliases.addAll(cmd.aliases()); + }); + + return aliases; + } + + Optional findCommand(String alias) { + for (Command cmd: commands) { + if (cmd.aliases().contains(alias)) { + return Optional.of(cmd); + } + } + return Optional.empty(); + } + + boolean isCommandAlias(String input) { + return getCommandAliases().stream().anyMatch(valueMatch(input)); + } + + String removePrefix(String input) { + int startPoint = BotConfig.DEFAULT_PREFIX.length(); + return input.substring(startPoint); + } + + List splitArgs(String input) { + if (input.contains(" ")) { + input = removePrefix(input); + String[] splits = input.split(" "); + return new ArrayList<>(Arrays.asList(splits)); + } else { + input = removePrefix(input); + return new ArrayList<>(Collections.singletonList(input)); + } + } + + String[] getCmdArgs(String input) { + List splits = splitArgs(input); + splits.remove(0); + return splits.toArray(new String[0]); + } + + String commandString(String input) { + // Cleans the string of the prefix and any args, returning only the command + return splitArgs(input).get(0); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/listeners/AchievementListener.java b/src/main/java/dev/salmonllama/fsbot/listeners/AchievementListener.java new file mode 100644 index 0000000..1e4cd15 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/listeners/AchievementListener.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.listeners; + +import com.vdurmont.emoji.EmojiParser; +import dev.salmonllama.fsbot.config.BotConfig; +import org.javacord.api.entity.message.MessageAttachment; +import org.javacord.api.event.message.MessageCreateEvent; +import org.javacord.api.listener.message.MessageCreateListener; + +public class AchievementListener implements MessageCreateListener { + + public void onMessageCreate(MessageCreateEvent event) { + if (!event.getChannel().getIdAsString().equals(BotConfig.ACHIEVEMENT_CHANNEL)) { + return; + } + + if (event.getMessage().getAttachments().stream().noneMatch(MessageAttachment::isImage)) { + return; + } + + event.getMessage().addReaction(EmojiParser.parseToUnicode(":tada:")); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/listeners/ImageListener.java b/src/main/java/dev/salmonllama/fsbot/listeners/ImageListener.java new file mode 100644 index 0000000..b2964e3 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/listeners/ImageListener.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.listeners; + +import com.rethinkdb.RethinkDB; +import com.rethinkdb.net.Connection; +import com.rethinkdb.net.Cursor; +import com.vdurmont.emoji.EmojiParser; +import okhttp3.*; +import org.javacord.api.entity.message.Message; +import org.javacord.api.entity.message.MessageAttachment; +import org.javacord.api.entity.message.embed.EmbedBuilder; +import org.javacord.api.event.message.MessageCreateEvent; +import org.javacord.api.listener.message.MessageCreateListener; +import org.json.JSONObject; +import dev.salmonllama.fsbot.config.BotConfig; + +import java.awt.*; +import java.util.List; + +public class ImageListener implements MessageCreateListener { + + final RethinkDB r; + final Connection conn; + + public ImageListener(RethinkDB r, Connection conn) { + this.r = r; + this.conn = conn; + } + + @Override + public void onMessageCreate(MessageCreateEvent event) { + + event.getMessage().getUserAuthor().ifPresent(author -> { + if (author.isBot()) { + return; + } + }); + + Message message = event.getMessage(); + String channel = event.getChannel().getIdAsString(); + String submitter = event.getMessage().getAuthor().getIdAsString(); + + if (message.getAttachments().stream().noneMatch(MessageAttachment::isImage)) { + return; + } + + // If the message contains an image, check if the channel is being listened to for image storage + + String dbTable = null; + + if(r.db("fsbot").table("channels").g("cId").contains(channel).run(conn)) { + Cursor cursor = r.db("fsbot") + .table("channels") + .filter(row -> row.getField("cId").eq(channel)) + .getField("tag") + .run(conn); + + List tags = cursor.toList(); + + for (Object tag : tags) { + dbTable = tag.toString(); + } + } + else { + return; + } + + List attachments = message.getAttachments(); + + for (MessageAttachment attachment : attachments) { + String discordLink = attachment.getUrl().toString(); + String imgurLink = null; + + // Upload the image to imgur + try { + OkHttpClient client = new OkHttpClient(); + + MediaType mediaType = MediaType.parse("multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW"); + + RequestBody body = RequestBody.create( + mediaType, + String.format("------WebKitFormBoundary7MA4YWxkTrZu0gW\r\nContent-Disposition: form-data; name=\"image\"\r\n\r\n%s\r\n------WebKitFormBoundary7MA4YWxkTrZu0gW--", discordLink)); + + Request request = new Request.Builder() + .url("https://api.imgur.com/3/image") + .method("POST", body) + .header("Authorization", BotConfig.IMGUR_ID) + .header("Authorization", BotConfig.IMGUR_BEARER) + .build(); + + Response response = client.newCall(request).execute(); + + if (response.body() == null) { + event.getChannel().sendMessage("Something went wrong!"); + return; + } + + String jsonData = response.body().string(); + imgurLink = new JSONObject(jsonData).getJSONObject("data").getString("link"); + + if (imgurLink == null) { + event.getChannel().sendMessage("Something went wrong!"); + return; + } + + } + catch (Exception e) { + e.printStackTrace(); + } + + r.db("fsbot").table("outfits").insert( + r.hashMap("link", imgurLink) + .with("submitter", submitter) + .with("tag", dbTable) + ).run(conn); + + final String finalLink = imgurLink; + Cursor imgId = r.db("fsbot") + .table("outfits") + .filter(row -> row.getField("link").eq(finalLink)) + .getField("id") + .run(conn); + + String embedId = null; + List imgIds = imgId.toList(); + for (Object id : imgIds) { + embedId = id.toString(); + } + + EmbedBuilder embed = new EmbedBuilder() + .setTitle("Added an entry of tag " + dbTable) + .setColor(Color.GREEN) + .setThumbnail(imgurLink) + .setAuthor("Placeholder") + .setFooter(embedId); + + event.getApi().getUserById(submitter).thenAccept(user -> embed.setAuthor(user.getDiscriminatedName())); + + event.getApi().getChannelById(BotConfig.OUTFIT_LOG).ifPresent(chnl -> { + chnl.asTextChannel().ifPresent(txtchnl -> { + txtchnl.sendMessage(embed).join(); + }); + }); + + message.addReaction(EmojiParser.parseToUnicode(":heartpulse:")); + } + } +} \ No newline at end of file diff --git a/src/main/java/dev/salmonllama/fsbot/listeners/NewMemberListener.java b/src/main/java/dev/salmonllama/fsbot/listeners/NewMemberListener.java new file mode 100644 index 0000000..e7506a8 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/listeners/NewMemberListener.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.listeners; + +import dev.salmonllama.fsbot.utilities.database.ServerConfUtility; +import org.javacord.api.DiscordApi; +import org.javacord.api.event.server.member.ServerMemberJoinEvent; +import org.javacord.api.listener.server.member.ServerMemberJoinListener; + +public class NewMemberListener implements ServerMemberJoinListener { + + private DiscordApi api; + + public NewMemberListener(DiscordApi api) { + this.api = api; + } + + public void onServerMemberJoin(ServerMemberJoinEvent event) { + + if (!event.getServer().getIdAsString().equals("340511685024546816")) return; + + ServerConfUtility conf = new ServerConfUtility(event.getServer().getIdAsString()); + String welcomeMsg = conf.getWelcomeMsg(); + String welcomeChannel = conf.getWelcomeChannel(); + + String logMessage = String.format(welcomeMsg, event.getUser().getMentionTag()); + + api.getChannelById(welcomeChannel).ifPresent(chnl -> { + chnl.asServerTextChannel().ifPresent(channel -> { + channel.sendMessage(logMessage); + }); + }); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/listeners/ReactionDeleteConfirmationListener.java b/src/main/java/dev/salmonllama/fsbot/listeners/ReactionDeleteConfirmationListener.java new file mode 100644 index 0000000..a320213 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/listeners/ReactionDeleteConfirmationListener.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.listeners; + +import com.vdurmont.emoji.EmojiParser; +import dev.salmonllama.fsbot.config.BotConfig; +import org.javacord.api.entity.channel.Channel; +import org.javacord.api.entity.message.Message; +import org.javacord.api.entity.message.embed.EmbedBuilder; +import org.javacord.api.entity.user.User; +import org.javacord.api.event.message.reaction.ReactionAddEvent; +import org.javacord.api.listener.message.reaction.ReactionAddListener; +import dev.salmonllama.fsbot.utilities.Outfit; +import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities; +import dev.salmonllama.fsbot.utilities.exceptions.DiscordError; +import dev.salmonllama.fsbot.utilities.exceptions.OutfitNotFoundException; + +import java.awt.*; + +public class ReactionDeleteConfirmationListener implements ReactionAddListener { + + private final User author; + private final Message message; + private final Outfit outfit; + private final DatabaseUtilities db; + + public ReactionDeleteConfirmationListener(User author, Message message, Outfit outfit, DatabaseUtilities db) { + this.author = author; + this.message = message; + this.outfit = outfit; + this.db = db; + } + + public void onReactionAdd(ReactionAddEvent event) { + if (!event.getUser().getIdAsString().equals(author.getIdAsString())) { + return; + } + + if (event.getEmoji().equalsEmoji(EmojiParser.parseToUnicode(":white_check_mark:"))) { + // Delete the message and send confirmation + + String completed = ""; + + try { + completed = db.removeFromDatabase(outfit.getId()); + } + catch(OutfitNotFoundException e) { + event.getChannel().sendMessage(new DiscordError(e.getMessage()).get().addField("Info:", "This message was sent by a reaction listener. **YOU SHOULD NOT BE SEEING THIS!**")); + } + + if (Integer.parseInt(completed) > 0) { + // Successful deletion + EmbedBuilder embed = new EmbedBuilder() + .setTitle("Success!") + .setColor(Color.GREEN) + .setDescription("Outfit deleted successfully"); + + message.removeAllReactions(); + message.edit(embed); + + event.getApi().getChannelById(BotConfig.OUTFIT_LOG).map(Channel::asServerTextChannel).orElseThrow(AssertionError::new).map(channel -> + channel.sendMessage(outfit.generateInfo().setTitle(String.format("Outfit Deleted by %s", event.getUser().getDiscriminatedName())).setColor(Color.RED)) + ); + } + else { + // Deletion failure + EmbedBuilder embed = new EmbedBuilder() + .setTitle("Error!") + .setColor(Color.RED) + .setDescription("An error occurred and the outfit was not deleted. Did you use the correct ID?") + .setFooter(String.format("Bother %s about making these stupid things more useful.", event.getApi().getOwner().thenAccept(User::getDiscriminatedName))); + + message.removeAllReactions(); + message.edit(embed); + } + } + else if (event.getEmoji().equalsEmoji(EmojiParser.parseToUnicode(":octagonal_sign:"))) { + // Cancel deletion and do nothing + EmbedBuilder embed = new EmbedBuilder() + .setTitle("Oops!") + .setColor(Color.GREEN) + .setDescription("Deletion cancelled. No changes were made."); + + message.removeAllReactions(); + message.edit(embed); + } + + event.getApi().removeListener(this); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/listeners/ReactionRetagConfirmationListener.java b/src/main/java/dev/salmonllama/fsbot/listeners/ReactionRetagConfirmationListener.java new file mode 100644 index 0000000..f1feeba --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/listeners/ReactionRetagConfirmationListener.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.listeners; + +import com.vdurmont.emoji.EmojiParser; +import dev.salmonllama.fsbot.config.BotConfig; +import org.javacord.api.entity.message.Message; +import org.javacord.api.entity.message.embed.EmbedBuilder; +import org.javacord.api.entity.user.User; +import org.javacord.api.event.message.reaction.ReactionAddEvent; +import org.javacord.api.listener.message.reaction.ReactionAddListener; +import dev.salmonllama.fsbot.utilities.Outfit; +import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities; +import dev.salmonllama.fsbot.utilities.exceptions.DiscordError; +import dev.salmonllama.fsbot.utilities.exceptions.OutfitNotFoundException; + +import java.awt.*; + +public class ReactionRetagConfirmationListener implements ReactionAddListener { + + private User author; + private final Message message; + private final Outfit outfit; + private final DatabaseUtilities db; + private final String oldTag; + private final String newTag; + + public ReactionRetagConfirmationListener(User author, Message message, Outfit outfit, DatabaseUtilities db, String oldTag, String newTag) { + this.author = author; + this.message = message; + this.outfit = outfit; + this.db = db; + this.oldTag = oldTag; + this.newTag = newTag; + } + + public void onReactionAdd(ReactionAddEvent event) { + if (!event.getUser().getIdAsString().equals(this.author.getIdAsString())) { + return; + } + + if (event.getEmoji().equalsEmoji(EmojiParser.parseToUnicode(":white_check_mark:"))) { + // retag the image, send a confirmation, or an error if it failed for whatever reason. + String result = ""; + + try { + result = db.changeOutfitTag(outfit.getId(), this.newTag); + } + catch (OutfitNotFoundException e) { + message.delete(); + event.getChannel().sendMessage(new DiscordError(e.getMessage()).get().addField("Info", "This message was generated by a listener thread. YOU SHOULD NOT BE SEEING THIS ERROR!")); + } + + message.removeAllReactions(); + + if (Integer.parseInt(result) > 0) { + // success, send confirmation + EmbedBuilder embed = new EmbedBuilder() + .setTitle("Success") + .setColor(Color.GREEN) + .setDescription("Outfit retagged successfully") + .setFooter("Check the log for more information"); + + message.edit(embed); + + // log message with new tag and old tag + + event.getApi().getChannelById(BotConfig.OUTFIT_LOG).ifPresent(channel -> { + channel.asServerTextChannel().ifPresent(chnl -> { + chnl.sendMessage(outfit.tagChangeEmbed(this.author.getDiscriminatedName(), this.oldTag, this.newTag)); + }); + }); + } + else { + // failure, something went wrong + EmbedBuilder embed = new EmbedBuilder() + .setTitle("Error!") + .setColor(Color.RED) + .setFooter("Big oopsie"); + + event.getApi().getOwner().thenAcceptAsync(user -> { + embed.setDescription(String.format("Something has gone horribly wrong, contact %s", user.getDiscriminatedName())); + }); + + message.edit(embed); + } + } + else if (event.getEmoji().equalsEmoji(EmojiParser.parseToUnicode(":octagonal_sign:"))) { + // Cancel the image retagging. + + message.removeAllReactions(); + + EmbedBuilder embed = new EmbedBuilder() + .setTitle("Operation cancelled!") + .setDescription("No changes made.") + .setColor(Color.GREEN) + .setFooter("boop"); + + message.edit(embed); + } + + event.getApi().removeListener(this); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/listeners/ReportListener.java b/src/main/java/dev/salmonllama/fsbot/listeners/ReportListener.java new file mode 100644 index 0000000..a1438f9 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/listeners/ReportListener.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.listeners; + +import org.javacord.api.DiscordApi; +import org.javacord.api.entity.channel.TextChannel; +import org.javacord.api.entity.message.Message; +import org.javacord.api.entity.message.MessageAuthor; +import org.javacord.api.entity.message.embed.EmbedBuilder; +import org.javacord.api.event.message.MessageCreateEvent; +import org.javacord.api.listener.message.MessageCreateListener; +import dev.salmonllama.fsbot.config.BotConfig; +import dev.salmonllama.fsbot.utilities.MessageUtilities; + +import java.awt.*; +import java.util.Optional; +import java.util.concurrent.TimeUnit; + +public class ReportListener implements MessageCreateListener { + + public void onMessageCreate(MessageCreateEvent event) { + DiscordApi api = event.getApi(); + TextChannel channel = event.getChannel(); + + if (!channel.getIdAsString().equals(BotConfig.REPORT_CHANNEL)) { + return; + } + + Message message = event.getMessage(); + MessageAuthor author = message.getAuthor(); + + if (author.isBotUser()) { + return; + } + + String content = message.getContent(); + + message.delete().join(); + + EmbedBuilder embed = new EmbedBuilder() + .setTitle("User Report") + .setDescription(content) + .setColor(Color.GREEN) + .setFooter("From: " + author.getDiscriminatedName()); + + Optional logChannel = api.getTextChannelById(BotConfig.REPORT_LOG); + if (logChannel.isPresent()) { + logChannel.get().sendMessage(embed); + } + else { + channel.sendMessage("Could not find proper log channel, please contact a staff member.") + .thenAccept(MessageUtilities.deleteAfter(30, TimeUnit.SECONDS)); + + } + + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/listeners/ServerJoined.java b/src/main/java/dev/salmonllama/fsbot/listeners/ServerJoined.java new file mode 100644 index 0000000..7ad7035 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/listeners/ServerJoined.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.listeners; + +import dev.salmonllama.fsbot.utilities.database.DatabaseUtilities; +import org.javacord.api.DiscordApi; +import org.javacord.api.entity.message.embed.EmbedBuilder; +import org.javacord.api.event.server.ServerJoinEvent; +import org.javacord.api.listener.server.ServerJoinListener; + +import java.awt.*; + +public class ServerJoined implements ServerJoinListener { + + private DiscordApi api; + private DatabaseUtilities db; + + public ServerJoined(DiscordApi api, DatabaseUtilities db) { + this.api = api; + this.db = db; + } + + public void onServerJoin(ServerJoinEvent event) { + db.newServerProcess(event.getServer()); + + EmbedBuilder embed = new EmbedBuilder() + .setTitle("Server joined") + .setColor(Color.GREEN) + .addInlineField("Server name:", event.getServer().getName()) + .addInlineField("Server Id:", event.getServer().getIdAsString()); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/listeners/ThumbsListener.java b/src/main/java/dev/salmonllama/fsbot/listeners/ThumbsListener.java new file mode 100644 index 0000000..77ae970 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/listeners/ThumbsListener.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.listeners; + +import com.vdurmont.emoji.EmojiParser; +import dev.salmonllama.fsbot.config.BotConfig; +import org.javacord.api.event.message.MessageCreateEvent; +import org.javacord.api.listener.message.MessageCreateListener; + +public class ThumbsListener implements MessageCreateListener { + + public void onMessageCreate(MessageCreateEvent event) { + + if (event.getChannel().getIdAsString().equals(BotConfig.ANNOUNCEMENT_CHANNEL)) { + // Announcements + event.getMessage().addReaction(EmojiParser.parseToUnicode(":thumbsup:")); + event.getMessage().addReaction(EmojiParser.parseToUnicode(":thumbsdown:")); + } + else if (event.getChannel().getIdAsString().equals(BotConfig.NEWS_CHANNEL)) { + // Newsfeed + event.getMessage().addReaction(EmojiParser.parseToUnicode(":thumbsup:")); + event.getMessage().addReaction(EmojiParser.parseToUnicode(":thumbsdown:")); + } + else if (event.getChannel().getIdAsString().equals(BotConfig.VOTE_CHANNEL)) { + // Votes + event.getMessage().addReaction(EmojiParser.parseToUnicode(":thumbsup:")); + event.getMessage().addReaction(EmojiParser.parseToUnicode(":thumbsdown:")); + } + + + } + +} diff --git a/src/main/java/dev/salmonllama/fsbot/utilities/Checks.java b/src/main/java/dev/salmonllama/fsbot/utilities/Checks.java new file mode 100644 index 0000000..0be99cd --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/utilities/Checks.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.utilities; + +public class Checks { + + public static boolean checkTag() { + return true; + } + + public static boolean checkId() { + return true; + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/utilities/ColorRole.java b/src/main/java/dev/salmonllama/fsbot/utilities/ColorRole.java new file mode 100644 index 0000000..2a4bdf9 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/utilities/ColorRole.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.utilities; + +import org.javacord.api.entity.message.embed.EmbedBuilder; +import dev.salmonllama.fsbot.utilities.database.RoleColourUtility; + +import java.util.List; +import java.awt.Color; + +public class ColorRole { + public String id; + public String name; + + public ColorRole(String id, String name) { + this.id = id; + this.name = name; + } + + public static EmbedBuilder rolesListEmbed() { + List roles = RoleColourUtility.getAllRoles(); + + EmbedBuilder embed = new EmbedBuilder() + .setTitle("Available ColorRoles") + .setColor(Color.GREEN) + .setFooter("Showing " + roles.size() + " roles"); + + StringBuilder builder = new StringBuilder(); + + roles.forEach(role -> { + builder.append(role.name).append("\n"); + }); + + embed.addField("Roles:", builder.toString()); + + return embed; + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/utilities/Constants.java b/src/main/java/dev/salmonllama/fsbot/utilities/Constants.java new file mode 100644 index 0000000..0e46308 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/utilities/Constants.java @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.utilities; + +public class Constants { + public static final String BOT_FOLDER = System.getenv("HOME").concat("/.fsbot/"); + + public static final String CONFIG_NAME = "bot.config"; + + public static final String DB_NAME = "fsbot"; + + public static final String OUTFIT_TABLE = "outfits"; + + public static final String SCONF_TABLE = "server_conf"; +} diff --git a/src/main/java/dev/salmonllama/fsbot/utilities/MessageUtilities.java b/src/main/java/dev/salmonllama/fsbot/utilities/MessageUtilities.java new file mode 100644 index 0000000..7395c20 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/utilities/MessageUtilities.java @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.utilities; + +import org.javacord.api.entity.message.Message; + +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +public class MessageUtilities { + public static Consumer deleteAfter(long delay, TimeUnit timeUnit) { + return msg -> msg.getApi().getThreadPool().getScheduler().schedule((Runnable) msg::delete, delay, timeUnit); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/utilities/Outfit.java b/src/main/java/dev/salmonllama/fsbot/utilities/Outfit.java new file mode 100644 index 0000000..7642bc1 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/utilities/Outfit.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.utilities; + +import org.javacord.api.DiscordApi; +import org.javacord.api.entity.message.embed.EmbedBuilder; + +import java.awt.*; + +public class Outfit { + + private String id; + private String tag; + private String submitter; + private String link; + private DiscordApi api; + + public Outfit(String id, String tag, String submitter, String link) { + this.id = id; + this.tag = tag; + this.submitter = submitter; + this.link = link; + } + + public void setId(String id) { this.id = id; } + public String getId() { return this.id; } + + public String getTag() { return this.tag; } + + // public void setTag(String tag) { this.tag = tag; } + // public String getTag() { return this.tag; } + + // public void setSubmitter(String submitter) { this.submitter = submitter; } + // public String getSubmitter() { return this.submitter; } + + // public void setLink(String link) { this.link = link; } + // public String getLink() { return this.link; } + + public EmbedBuilder generateEmbed() { + return new EmbedBuilder() + .setColor(Color.GREEN) + .setTitle(submitter + " | " + tag) + .setImage(this.link) + .setFooter(this.id); + } + + public EmbedBuilder generateInfo() { + return new EmbedBuilder() + .setColor(Color.GREEN) + .setTitle("Outfit Information") + .setThumbnail(this.link) + .addField("Submitter:", this.submitter, true) + .addField("Tag:", this.tag, true) + .addField("Id:", this.id) + .addField("Link:", this.link); + } + + public EmbedBuilder tagChangeEmbed(String changer, String oldTag, String newTag) { + return new EmbedBuilder() + .setColor(Color.YELLOW) + .setTitle(String.format("Tag changed by %s", changer)) + .setThumbnail(this.link) + .addField("Old Tag:", oldTag) + .addField("New Tag:", newTag) + .setFooter(this.id); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/utilities/database/DatabaseUtilities.java b/src/main/java/dev/salmonllama/fsbot/utilities/database/DatabaseUtilities.java new file mode 100644 index 0000000..879eac2 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/utilities/database/DatabaseUtilities.java @@ -0,0 +1,246 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.utilities.database; + +import com.rethinkdb.RethinkDB; +import com.rethinkdb.gen.ast.Table; +import com.rethinkdb.net.Connection; +import com.rethinkdb.net.Cursor; +import org.javacord.api.DiscordApi; +import dev.salmonllama.fsbot.utilities.Outfit; +import org.javacord.api.entity.server.Server; +import dev.salmonllama.fsbot.utilities.exceptions.OutfitNotFoundException; + +import java.util.*; + +public class DatabaseUtilities { + + private final RethinkDB r; + private final Connection conn; + private final DiscordApi api; + + public DatabaseUtilities(RethinkDB r, Connection conn, DiscordApi api) { + this.r = r; + this.conn = conn; + this.api = api; + } + + private Table getTable(String table) { + if (this.r.db("fsbot").tableList().contains(table).run(this.conn)) { + return this.r.db("fsbot").table(table); + } + else { + return null; + } + } + + private boolean validateId(String id) { + return r.db("fsbot").table("outfits").getField("id").contains(id).run(this.conn); + } + + public boolean tagExists(String tag) { + return r.db("fsbot").table("outfits").getField("tag").distinct().contains(tag).run(this.conn); + } + + public int countTags() { + return this.r.db("fsbot").table("outfits").getField("tag").distinct().count().run(this.conn); + } + + public ArrayList getTags() { + return r.db("fsbot").table("outfits").getField("tag").distinct().run(this.conn); + } + + public Outfit getOutfitFromId(String id) throws OutfitNotFoundException { + if (!this.validateId(id)) throw new OutfitNotFoundException(); + + HashMap sample = this.r.db("fsbot").table("outfits") + .get(id) + .run(this.conn); + + String tag = sample.get("tag").toString(); + String link = sample.get("link").toString(); + String submitterId = sample.get("submitter").toString(); + + String submitterName = api.getUserById(submitterId).join().getDiscriminatedName(); + + return new Outfit(id, tag, submitterName, link); + } + + public String removeFromDatabase(String id) throws OutfitNotFoundException { + if (!this.validateId(id)) throw new OutfitNotFoundException(); + + // Remove outfit return deletion status. 0 = failed, >= 1 = success + HashMap deletion = r.db("fsbot").table("outfits").get(id).delete().run(conn); + + return deletion.get("deleted").toString(); + } + + public Outfit randomOutfit() { + HashMap sample = this.r.db("fsbot").table("outfits") + .sample(1).nth(0) + .run(this.conn); + + String id = sample.get("id").toString(); + String tag = sample.get("tag").toString(); + String link = sample.get("link").toString(); + String submitterId = sample.get("submitter").toString(); + + String submitterName = api.getUserById(submitterId).join().getDiscriminatedName(); + + return new Outfit(id, tag, submitterName, link); + } + + public Outfit randomTaggedOutfit(String targetTag) { + HashMap sample = r.db("fsbot").table("outfits") + .filter(row -> row.getField("tag").eq(targetTag)) + .sample(1).nth(0) + .run(conn); + + String id = String.valueOf(sample.get("id")); + String tag = String.valueOf(sample.get("tag")); + String link = String.valueOf(sample.get("link")); + String submitterId = String.valueOf(sample.get("submitter")); + + String submitterName = api.getUserById(submitterId).join().getDiscriminatedName(); // Try a thenAccept with a thenApply + + return new Outfit(id, tag, submitterName, link); + } + + public String changeOutfitTag(String outfitId, String newTag) throws OutfitNotFoundException { + if (!this.validateId(outfitId)) throw new OutfitNotFoundException(); + + HashMap replacement = this.r.db("fsbot").table("outfits").get(outfitId).update(r.hashMap("tag", newTag)).run(this.conn); + + return replacement.get("replaced").toString(); + } + + public Outfit getOutfitBySubmitter(String userId) { + HashMap sample = r.db("fsbot").table("outfits") + .filter(row -> row.getField("submitter").eq(userId)) + .sample(1).nth(0) + .run(conn); + + String id = String.valueOf(sample.get("id")); + String tag = String.valueOf(sample.get("tag")); + String link = String.valueOf(sample.get("link")); + String submitterId = String.valueOf(sample.get("submitter")); + + String submitterName = api.getUserById(submitterId).join().getDiscriminatedName(); + + return new Outfit(id, tag, submitterName, link); + } + + public long getSubmitterCount(String submitter) { + return r.db("fsbot").table("outfits") + .filter( + row -> row.getField("submitter").eq(submitter) + ) + .count() + .run(conn); + } + + public List getSubmitterIds(String submitter) { + List ids = new ArrayList<>(); + Cursor cursor = r.db("fsbot").table("outfits") + .filter( + row -> row.getField("submitter").eq(submitter) + ) + .getField("id") + .run(conn); + + cursor.forEach(ids::add); + + return ids; + } + + public void updateSubmitter(String submitter, String newSubmitter) { + // Add feature to return update-error? + r.db("fsbot").table("outfits") + .filter(r.hashMap("submitter", submitter)) + .update(r.hashMap("submitter", newSubmitter)) + .run(conn); + } + + public void newServerProcess(Server server) { + + if (this.r.db("fsbot").table("serverConf").contains(server.getIdAsString()).run(this.conn)) { + return; + } + + String serverName = server.getName(); + String serverId = server.getIdAsString(); + String logChannel = "null"; + String giveawayChannel = "null"; + String welcomeChannel = "null"; + String defaultWelcome = "welcome to the server"; + + this.r.db("fsbot").table("serverConf").insert( + this.r.hashMap("id", serverId) + .with("name", serverName) + .with("logChannel", logChannel) + .with("giveawayChannel", giveawayChannel) + .with("welcomeMsg", defaultWelcome) + .with("welcomeChannel", welcomeChannel) + .with("prefix", "~") + ).run(this.conn); + } + + public void tableSetup() { // TODO: Fix this -- invert conditionals, just create the tables. -> if *not* exist then create + // Check for database existence, if not, create + if (r.dbList().contains("fsbot").run(conn)) { +// System.out.println("database 'fsbot' already exists."); + } + else { + r.dbCreate("fsbot").run(conn); + System.out.println("database fsbot did not exist, and has been created"); + } + + // Check for channels table existence, if not, create + if (r.db("fsbot").tableList().contains("channels").run(conn)) { +// System.out.println("table channels already exists"); + } + else { + r.db("fsbot").tableCreate("channels").run(conn); + System.out.println("table channels did not exist, and has been created."); + } + + // Check for serverconf table existence, if not, create + if (r.db("fsbot").tableList().contains("serverConf").run(conn)) { +// System.out.println("table serverConf already exists"); + } + else { + r.db("fsbot").tableCreate("serverConf").run(conn); + System.out.println("table serverConf did not exist, and has been created"); + } + + // Check for permissions table existene, if not, create + if (r.db("fsbot").tableList().contains("permissions").run(conn)) { +// System.out.println("table permissions already exists"); + } + else { + r.db("fsbot").tableCreate("permissions").run(conn); + System.out.println("table permissions did not exist and has been created"); + } + + // Check for outfits table existence, if not, create + if (r.db("fsbot").tableList().contains("outfits").run(conn)) { +// System.out.println("table outfits already exists"); + } + else { + r.db("fsbot").tableCreate("outfits").run(conn); + System.out.println("table outfits did not exist and has been created"); + } + + // Check for colourRoles table existence, if not, create + if (r.db("fsbot").tableList().contains("colourRoles").run(conn)) { +// System.out.println("table colourRoles already exists"); + } + else { + r.db("fsbot").tableCreate("colourRoles").run(conn); + System.out.println("table colourRoles did not exist and has been created"); + } + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/utilities/database/RoleColourUtility.java b/src/main/java/dev/salmonllama/fsbot/utilities/database/RoleColourUtility.java new file mode 100644 index 0000000..be86216 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/utilities/database/RoleColourUtility.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.utilities.database; + +import com.rethinkdb.RethinkDB; +import com.rethinkdb.net.Connection; +import com.rethinkdb.net.Cursor; +import dev.salmonllama.fsbot.utilities.ColorRole; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class RoleColourUtility { + + private static RethinkDB r = RethinkDB.r; + private static Connection conn = r.connection().hostname("localhost").port(28015).connect(); + + public static void addColourRole(String colourName, String roleId) { + r.db("fsbot").table("colourRoles").insert( + r.hashMap("id", roleId) + .with("name", colourName)).run(conn); + } + + public static void deleteColourRole() { + + } + + public static String getColour(String colourName) { + String roleId = null; + + Cursor cursor = r.db("fsbot").table("colourRoles") + .filter(row -> row.getField("name").eq(colourName)) + .getField("id") + .run(conn); + List roleIds = cursor.toList(); + for (Object id : roleIds) { + roleId = id.toString(); + } + + return roleId; + } + + public static List getAllRoles() { + List allRoles = new ArrayList<>(); + + Cursor cursor = r.db("fsbot").table("colourRoles") + .run(conn); + + while (cursor.hasNext()) { + HashMap role = (HashMap) cursor.next(); + + String id = String.valueOf(role.get("id")); + String name = String.valueOf(role.get("name")); + + allRoles.add(new ColorRole(id, name)); + } + + return allRoles; + } + + public static String getAllRoleInfo() { + return r.db("fsbot").table("colourRoles").run(conn); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/utilities/database/ServerConfUtility.java b/src/main/java/dev/salmonllama/fsbot/utilities/database/ServerConfUtility.java new file mode 100644 index 0000000..06c2cbf --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/utilities/database/ServerConfUtility.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.utilities.database; + +import com.rethinkdb.RethinkDB; +import com.rethinkdb.net.Connection; +import com.rethinkdb.net.Cursor; + +import java.util.List; + +public class ServerConfUtility { + + private static final RethinkDB r = RethinkDB.r; + private static final Connection conn = r.connection().hostname("localhost").port(28015).connect(); + + private static String serverId; + + public ServerConfUtility(String sId) { + serverId = sId; + } + + // TODO: Turn server into method args, not class field. + + public String getWelcomeMsg() { + + String welcomeMsg = null; + + Cursor welcomes = r.db("fsbot").table("serverConf") + .filter(row -> row.getField("id").eq(serverId)) + .getField("welcomeMsg") + .run(conn); + List welcomeMsgs = welcomes.toList(); + for (Object msg : welcomeMsgs) { + welcomeMsg = msg.toString(); + } + + return welcomeMsg; + } + + public void setWelcomeMsg(String msg) { + + r.db("fsbot").table("serverConf") + .get(serverId) + .update(r.hashMap("welcomeMsg", msg)) + .run(conn); + } + + public String getWelcomeChannel() { + + String welcomeChannel = null; + + Cursor channels = r.db("fsbot").table("serverConf") + .filter(row -> row.getField("id").eq(serverId)) + .getField("welcomeChannel") + .run(conn); + List welcomeChannels = channels.toList(); + for (Object chnl : welcomeChannels) { + welcomeChannel = chnl.toString(); + } + + return welcomeChannel; + } + + public void setWelcomeChannel(String id) { + + r.db("fsbot").table("serverConf") + .get(serverId) + .update(r.hashMap("welcomeChannel", id)) + .run(conn); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/utilities/exceptions/DiscordError.java b/src/main/java/dev/salmonllama/fsbot/utilities/exceptions/DiscordError.java new file mode 100644 index 0000000..97faa75 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/utilities/exceptions/DiscordError.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.utilities.exceptions; + +import org.javacord.api.entity.message.embed.EmbedBuilder; + +import java.awt.*; + +public class DiscordError { + + String title = "Error!"; + String message; + String footer = "Contact Salmonllama#5727 if you think this is wrong"; + + public DiscordError(String message) { + this.message = message; + } + + public EmbedBuilder get() { + return new EmbedBuilder().setTitle(this.title).setDescription(this.message).setColor(Color.RED).setFooter(this.footer); + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/utilities/exceptions/DiscordException.java b/src/main/java/dev/salmonllama/fsbot/utilities/exceptions/DiscordException.java new file mode 100644 index 0000000..745bce3 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/utilities/exceptions/DiscordException.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.utilities.exceptions; + +import java.awt.*; + +public class DiscordException { + + private Color color; + private String message; + private String footer; + private String title; + + DiscordException(String title, String message, String footer, Color color) { + this.title = title; + this.message = message; + this.footer = footer; + this.color = color; + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/utilities/exceptions/DiscordWarning.java b/src/main/java/dev/salmonllama/fsbot/utilities/exceptions/DiscordWarning.java new file mode 100644 index 0000000..47d1cf2 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/utilities/exceptions/DiscordWarning.java @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.utilities.exceptions; + +public class DiscordWarning { +} diff --git a/src/main/java/dev/salmonllama/fsbot/utilities/exceptions/OutfitNotFoundException.java b/src/main/java/dev/salmonllama/fsbot/utilities/exceptions/OutfitNotFoundException.java new file mode 100644 index 0000000..7253e95 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/utilities/exceptions/OutfitNotFoundException.java @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.utilities.exceptions; + +public class OutfitNotFoundException extends Exception { + + @Override + public String getMessage() { + return "An outfit with that ID was not found"; + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/utilities/exceptions/TagNotFoundException.java b/src/main/java/dev/salmonllama/fsbot/utilities/exceptions/TagNotFoundException.java new file mode 100644 index 0000000..f455c40 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/utilities/exceptions/TagNotFoundException.java @@ -0,0 +1,14 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.utilities.exceptions; + +public class TagNotFoundException extends Exception { + + @Override + public String getMessage() { + return "That tag was not found, check your spelling and try again."; + } +} diff --git a/src/main/java/dev/salmonllama/fsbot/utilities/warnings/Warning.java b/src/main/java/dev/salmonllama/fsbot/utilities/warnings/Warning.java new file mode 100644 index 0000000..6ca4c39 --- /dev/null +++ b/src/main/java/dev/salmonllama/fsbot/utilities/warnings/Warning.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020. Aleksei Gryczewski + * All rights reserved. + */ + +package dev.salmonllama.fsbot.utilities.warnings; + +import org.javacord.api.entity.message.embed.EmbedBuilder; + +import java.awt.*; + +public class Warning { + private static Color embedColor = Color.YELLOW; + private static String embedFooter = "Bother Crablet#9999 to add usages to these things"; + private String embedWarning; + + public Warning(String warnMessage) { + this.embedWarning = warnMessage; + } + + public EmbedBuilder sendWarning() { + EmbedBuilder warning = new EmbedBuilder() + .setColor(Color.YELLOW) + .setFooter(embedFooter) + .addField("WARNING", embedWarning); + + return warning; + } +}