From 86a439c98b013bafa06bc514cd654d5472ead0aa Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 3 Aug 2022 13:44:06 +0000 Subject: [PATCH 01/48] :arrow_up: Update dependency org.hibernate:hibernate-validator to v7.0.5.Final --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9e9089c..e6e4c2d 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ dependencies { implementation('net.dv8tion:JDA:4.4.0_352') implementation(platform("org.apache.logging.log4j:log4j-bom:2.18.0")) - implementation group: 'org.hibernate', name: 'hibernate-validator', version: '7.0.4.Final' + implementation group: 'org.hibernate', name: 'hibernate-validator', version: '7.0.5.Final' // Use MySQL Connector-J implementation('mysql:mysql-connector-java:8.0.30') From 7f8c31ece21fb81969cf2f45aa6c7d75086a074f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 2 Apr 2023 10:58:05 +0000 Subject: [PATCH 02/48] :arrow_up: Update dependency org.springdoc:springdoc-openapi-ui to v1.7.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d7670c1..0f71c91 100644 --- a/build.gradle +++ b/build.gradle @@ -35,7 +35,7 @@ dependencies { implementation('org.springframework.boot:spring-boot-starter-actuator') implementation('io.micrometer:micrometer-registry-prometheus:1.9.2') - implementation('org.springdoc:springdoc-openapi-ui:1.6.9') + implementation('org.springdoc:springdoc-openapi-ui:1.7.0') implementation('org.springdoc:springdoc-openapi-security:1.6.9') implementation('org.liquibase:liquibase-core') From f483b19b2d682a10022f73112dd63cc6c0e18b19 Mon Sep 17 00:00:00 2001 From: SebClem Date: Fri, 14 Apr 2023 19:37:14 +0200 Subject: [PATCH 03/48] Quick fix startup --- src/main/java/net/Broken/Init.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/net/Broken/Init.java b/src/main/java/net/Broken/Init.java index fc61d9c..c191383 100644 --- a/src/main/java/net/Broken/Init.java +++ b/src/main/java/net/Broken/Init.java @@ -49,8 +49,7 @@ public class Init { logger.info("Connected on " + jda.getGuilds().size() + " Guilds:"); for (Guild server : jda.getGuilds()) { - server.loadMembers().get(); - logger.info("... " + server.getName() + " " + server.getMembers().size() + " Members"); + server.loadMembers(); } return jda; } catch (LoginException | InterruptedException e) { From cf467ceec357f83b233c9af26f63253caa1444b5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 20 Apr 2023 01:04:31 +0000 Subject: [PATCH 04/48] :arrow_up: Update actions/download-artifact action to v3.0.2 --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2f50121..aa444a6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -44,7 +44,7 @@ jobs: - uses: actions/checkout@v3 - name: Download artifact - uses: actions/download-artifact@v3.0.0 + uses: actions/download-artifact@v3.0.2 with: # Artifact name name: claptrap_jar diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c599ed1..f239e9d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -42,7 +42,7 @@ jobs: - uses: actions/checkout@v3 - name: Download artifact - uses: actions/download-artifact@v3.0.0 + uses: actions/download-artifact@v3.0.2 with: # Artifact name name: claptrap_jar From ac20fa38a1e33cfcde2d53087a94099e16885ff7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 4 Oct 2023 22:23:51 +0000 Subject: [PATCH 05/48] :arrow_up: Update dependency gradle to v7.6.3 --- gradle/wrapper/gradle-wrapper.jar | Bin 59203 -> 61624 bytes gradle/wrapper/gradle-wrapper.properties | 3 +- gradlew | 285 ++++++++++++++--------- gradlew.bat | 181 +++++++------- 4 files changed, 266 insertions(+), 203 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e708b1c023ec8b20f512888fe07c5bd3ff77bb8f..afba109285af78dbd2a1d187e33ac4f87c76e392 100644 GIT binary patch delta 39359 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 delta 36880 zcmZ5`19K(}&}6u=ZQHhO+qU^cPi)(^?PO!ywy`lc$=-WkU0u~()%=6$?&;~7&O`9w zIq*h0(3WpUHE4t~a8emQ2oR7BC=d{ll%PG#ltfRM6hAL2;I0~;2Kv8Udrr9NU#im7 zsqKO791ybAnysaE4nfw|)M?7db-WoToc298N0#8ibj5yvCykouL@S5 zSnn^zY`i7MAwu$raZQfvHi;-c6t|xk=eAlyfGPpu^ZKi5&OIQPhU|m<71M%DJfwXmPhpUU6;D%{CY5wBQmFu9?082n=M6nqf`kmlPs2^D zG}*m+h#`%7Uo5{SYv0wWF`PrJFg)f){KJA)13vCqptE2XT+n#%O_nJ%5`f}pE49PG zrHEIE?n_4SI`(7g^H=Q%vr;uM82m)Vn2J*bZq=oF1#V_BnW&QQVveB#C%zy zh+*d`&^Vr!Z_2nHDg&h?RJoUP49gaWid@U952KeJ)-YWy*vA#2rcL|BznOoF?Xvfm zlkeru`@~DJHnjab1*U`grs--C4PZoJw9JzW!f+tZBfCi<`kJ=oKx2~1PA+}%h5H+I zVJJYqbs@SBb8ErTgou|OUqYzqit9c)3ysoNe zrT=(Gm|=C>A6EPtgk;f@hex=h+iK?+(&d&>XU)9ykR(8)A<4Z%hkQuKL|!%aZ_zv@ z;8{CwJppmWyXRn}nX9!_ZhPGd<+-1e434>NTuwL(V}xkV`T8mRsjF*vDnWMNB{B-UE7S$o)*0PP<%SpS)G zXR351W?e8S@6i^nRhZHXfKyQo&n6=p%uGL1LP?Qh!nrGE1e=sqt3@0l{y;Ez-A7&X zO5J@6eFp!$CsRD28fBEog>eYkCC(RBZNd{r(h;*=ubF#UG>pJZXiLoAYrs7PLUBxO zW@cU87>fAMxS#!c4h?{liqzpQ`W*qD>?UdyCevybaaT+ddZ{j2LAxipT=)Z1%y#<; zi+i`PasV#AP+j%_aV7mDFYz6Q`$QLi$kqH$hAKen7tk02Sfw71)+I?=P%iG#oaz2( zvzmD$>7$c|uEv*@TAo}T!s$GxAGj(ESVT~nwhL}f-h0rNx$a*C49ApQZt>M@v_zjF zaq1GOEdJ~jt&ZDSyYN>mh>qy{2qsXi3NaZ7PN;>S{Z^I(hkykEfrbY8Pg+10yA^Nf z!9hRbnG&T?UcRviBiI=- z&`nCNF@ciA?;KxM0W|}X{xQ=Jg+ubLh5=Or z6NT3>t1izzhQ~16M-34urNRjBj;y9}>x$zT)lOVrGIx=B4$U=8Mo7DZlkDlLx17Y{ zc&@vh%*fmbHWLIDjwc*LpfM!Epj8kW}n3!xu-C;kGmt+ZE{wz;lJ2iis zgeZYmpc#!-eJ=+(lFRv3*R!|-{FAgE^>`|2EbrZsEu1aA_sF=IhN5Mrb!oA=5H#da zCPawUlrC(-ZQ*`rYDE5KbDWA=Z*d)~))-(Ht9Y9s{Urr}a?k^ZzdZ-R8R^VtLzUG& z^>-)i0?RE3{lzZp9VrDyD3>{i2BU<6!c1p^_yiw!sv!?NU2#`sAs2s$RfUSSkd8Dl z6xJUC!^E)#w?F{(LcV-;^PxHD3bm+pwq+u3EfE`_6+8IXPa`JwkjpA{o%L<%yhuNi z%Edz=cOP~O@1(oAr^kyrzT`zDrux*w6l_)*J9d_#h3&(b-4RORpEjx;DqmJRAxd54 zKCe7H-pdUP&L{hNAgMcISiFdN@m1@cfD|)0z^D)QLOWQWg^-stPs@X5ec;xXkIVbY$)DG5gO!MYve$|Yph12?7vE3a(eLDYdfc{pPZ^XO)UekrEpu@8_ zxDm+_-_AQIQ9v;f6!bCH(YwRLGg=07ZfCe+w5u7aK`G17=b}P(92l)jW_NQkO0vn=L`@*rML*SAV5c2NcKohw@)f2m+lo$ zOr_CD)*46-g)}`J6-CeEj=!CVx~2BCr*g?Hcg*>(xRY|BQzf=#6DI0*IWJM(q_>}? z6IvCN*87qh|G{Gt3yZccH++U}W^p;~`98%1Ewy}(rL>I5iWz`kwGo=jcYq6Q{~G+h z4)LeI#UJ2dKMP`xpJXiW?FXM`X+I-kK-qnj%X#!qHzbWEBkv2Q%O1i{uHLSaGvvx2PQOw+^vEWxO@d214; zA${N~LZh`&;v5#yzSyC@c7Gzfs9vSdkk28iS~p~vIU6J1qRlQRu11wzR;)vbTQD?@ zVOaDii*$RgYagmr)w?A|1&EspNOMvJ)D`Z*S3L3}ApjqJ(zv`Yu6tEZeZsh4R70lX zXt`*fal�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%0AV^qz&fiv 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8049c68..b1624c4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-bin.zip +networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 4f906e0..65dcd68 100644 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ -#!/usr/bin/env sh +#!/bin/sh # -# Copyright 2015 the original author or authors. +# Copyright © 2015-2021 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,67 +17,101 @@ # ############################################################################## -## -## Gradle start up script for UN*X -## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# ############################################################################## # Attempt to set APP_HOME -# Resolve links: $0 may be a link -PRG="$0" -# Need this for relative symlinks. -while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # 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 - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +121,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 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" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + 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 @@ -106,80 +140,105 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=`expr $i + 1` - done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" 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, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. -# 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" +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 107acd3..6689b85 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,89 +1,92 @@ -@rem -@rem Copyright 2015 the original author or authors. -@rem -@rem Licensed under the Apache License, Version 2.0 (the "License"); -@rem you may not use this file except in compliance with the License. -@rem You may obtain a copy of the License at -@rem -@rem https://www.apache.org/licenses/LICENSE-2.0 -@rem -@rem Unless required by applicable law or agreed to in writing, software -@rem distributed under the License is distributed on an "AS IS" BASIS, -@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -@rem See the License for the specific language governing permissions and -@rem limitations under the License. -@rem - -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Resolve any "." and ".." in APP_HOME to make it shorter. -for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto execute - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 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! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega From 08887099cb3d636a7aa453421d9b96d6110ba02d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 15 Nov 2023 02:02:24 +0000 Subject: [PATCH 06/48] :arrow_up: Update dependency org.liquibase:liquibase-core to v4.25.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d7670c1..a2455d0 100644 --- a/build.gradle +++ b/build.gradle @@ -56,7 +56,7 @@ dependencies { implementation('org.apache.commons:commons-lang3:3.12.0') - liquibaseRuntime('org.liquibase:liquibase-core:4.14.0') + liquibaseRuntime('org.liquibase:liquibase-core:4.25.0') liquibaseRuntime('org.liquibase:liquibase-groovy-dsl:3.0.2') liquibaseRuntime('mysql:mysql-connector-java:8.0.30') liquibaseRuntime group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1' From c3fda3cede43d1097a70a31860a6f526f400b9d7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 16 Nov 2023 20:11:06 +0000 Subject: [PATCH 07/48] :arrow_up: Update dependency org.liquibase.ext:liquibase-hibernate5 to v4.25.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d7670c1..5732c6d 100644 --- a/build.gradle +++ b/build.gradle @@ -60,7 +60,7 @@ dependencies { liquibaseRuntime('org.liquibase:liquibase-groovy-dsl:3.0.2') liquibaseRuntime('mysql:mysql-connector-java:8.0.30') liquibaseRuntime group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1' - liquibaseRuntime group: 'org.liquibase.ext', name: 'liquibase-hibernate5', version: '4.14.0' + liquibaseRuntime group: 'org.liquibase.ext', name: 'liquibase-hibernate5', version: '4.25.0' liquibaseRuntime 'org.springframework.boot:spring-boot-starter-data-jpa' liquibaseRuntime 'org.springframework.data:spring-data-jpa' liquibaseRuntime 'org.springframework:spring-beans' From 9741f851a515651d49b24acfbdc935fdefd92d52 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 23 Nov 2023 09:39:09 +0000 Subject: [PATCH 08/48] :arrow_up: Update plugin org.springframework.boot to v2.7.18 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d7670c1..e8ed1cb 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.springframework.boot' version '2.7.2' + id 'org.springframework.boot' version '2.7.18' id 'io.spring.dependency-management' version '1.0.12.RELEASE' id 'java' id 'groovy' From 9f555bea8d1e21f83a861254c4b9090698fa6fac Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 17:01:46 +0000 Subject: [PATCH 09/48] :arrow_up: Update dependency io.micrometer:micrometer-registry-prometheus to v1.12.1 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index d7670c1..17b48fc 100644 --- a/build.gradle +++ b/build.gradle @@ -33,7 +33,7 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-log4j2") implementation("org.springframework.boot:spring-boot-starter-oauth2-client") implementation('org.springframework.boot:spring-boot-starter-actuator') - implementation('io.micrometer:micrometer-registry-prometheus:1.9.2') + implementation('io.micrometer:micrometer-registry-prometheus:1.12.1') implementation('org.springdoc:springdoc-openapi-ui:1.6.9') implementation('org.springdoc:springdoc-openapi-security:1.6.9') From 79e35e1512491ce0f0801b4eb65f3669e48b9722 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Cl=C3=A9ment?= Date: Thu, 14 Dec 2023 15:05:21 +0100 Subject: [PATCH 10/48] Feature/jda 5 (#223) * Fist pass * All easyfix done * Fix slash command permission * Fix Auth manager --------- Co-authored-by: sclement --- .vscode/settings.json | 3 +- build.gradle | 2 +- .../CustomMethodSecurityExpressionRoot.java | 8 +- .../Broken/Api/Security/SecurityConfig.java | 50 ++--- .../Services/DiscordOauthService.java | 31 ++-- .../net/Broken/Api/Services/AudioService.java | 6 +- .../net/Broken/Api/Services/GuildService.java | 7 +- .../Broken/Audio/GuildAudioBotService.java | 118 ++++++------ src/main/java/net/Broken/BotListener.java | 175 +++++++++--------- .../net/Broken/Commands/ChannelsReview.java | 132 ------------- .../java/net/Broken/Commands/Ordre66.java | 39 ---- .../java/net/Broken/Commands/ReportUsers.java | 69 ------- src/main/java/net/Broken/Init.java | 21 +-- src/main/java/net/Broken/SlashCommand.java | 9 +- .../java/net/Broken/SlashCommands/Cat.java | 33 ++-- .../java/net/Broken/SlashCommands/Clear.java | 27 +-- .../java/net/Broken/SlashCommands/Music.java | 68 +++---- .../net/Broken/SlashCommands/Over18/Ass.java | 7 +- .../Broken/SlashCommands/Over18/Boobs.java | 8 +- .../Broken/SlashCommands/Over18/Madame.java | 10 +- .../java/net/Broken/SlashCommands/Rank.java | 16 +- .../net/Broken/Tools/AutoVoiceChannel.java | 27 +-- .../Broken/Tools/Command/CommandParser.java | 2 +- .../Tools/Command/NumberedSlashCommand.java | 17 +- .../Tools/Command/SlashCommandLoader.java | 31 ++-- .../DayListener/Listeners/DailyMadame.java | 14 +- .../java/net/Broken/Tools/SettingsUtils.java | 8 +- .../UserManager/Stats/UserStatsUtils.java | 29 ++- .../Broken/Tools/UserManager/UserUtils.java | 26 --- 29 files changed, 371 insertions(+), 622 deletions(-) delete mode 100644 src/main/java/net/Broken/Commands/ChannelsReview.java delete mode 100644 src/main/java/net/Broken/Commands/Ordre66.java delete mode 100644 src/main/java/net/Broken/Commands/ReportUsers.java delete mode 100644 src/main/java/net/Broken/Tools/UserManager/UserUtils.java diff --git a/.vscode/settings.json b/.vscode/settings.json index 1133129..f7a6660 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { - "java.configuration.updateBuildConfiguration": "automatic" + "java.configuration.updateBuildConfiguration": "automatic", + "java.compile.nullAnalysis.mode": "disabled" } \ No newline at end of file diff --git a/build.gradle b/build.gradle index d7670c1..5f8520b 100644 --- a/build.gradle +++ b/build.gradle @@ -45,7 +45,7 @@ dependencies { implementation('io.jsonwebtoken:jjwt-jackson:0.11.5') implementation('com.sedmelluq:lavaplayer:1.3.78') - implementation('net.dv8tion:JDA:4.4.1_353') + implementation('net.dv8tion:JDA:5.0.0-beta.18') implementation(platform("org.apache.logging.log4j:log4j-bom:2.18.0")) implementation group: 'org.hibernate', name: 'hibernate-validator', version: '7.0.4.Final' diff --git a/src/main/java/net/Broken/Api/Security/Expression/CustomMethodSecurityExpressionRoot.java b/src/main/java/net/Broken/Api/Security/Expression/CustomMethodSecurityExpressionRoot.java index 2bacdf8..4cd0f39 100644 --- a/src/main/java/net/Broken/Api/Security/Expression/CustomMethodSecurityExpressionRoot.java +++ b/src/main/java/net/Broken/Api/Security/Expression/CustomMethodSecurityExpressionRoot.java @@ -2,13 +2,14 @@ package net.Broken.Api.Security.Expression; import net.Broken.Api.Data.Music.Connect; import net.Broken.Api.Security.Data.JwtPrincipal; -import net.Broken.Audio.GuildAudioBotService; import net.Broken.MainBot; import net.Broken.Tools.CacheTools; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.VoiceChannel; +import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; +import net.dv8tion.jda.api.entities.channel.unions.AudioChannelUnion; + import org.springframework.security.access.expression.SecurityExpressionRoot; import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations; import org.springframework.security.core.Authentication; @@ -61,8 +62,7 @@ public class CustomMethodSecurityExpressionRoot public boolean canInteractWithVoiceChannel(String guildId) { JwtPrincipal jwtPrincipal = (JwtPrincipal) authentication.getPrincipal(); Guild guild = MainBot.jda.getGuildById(guildId); - GuildAudioBotService guildAudioBotService = GuildAudioBotService.getInstance(guild); - VoiceChannel channel = guild.getAudioManager().getConnectedChannel(); + AudioChannelUnion channel = guild.getAudioManager().getConnectedChannel(); if (channel == null) { return false; diff --git a/src/main/java/net/Broken/Api/Security/SecurityConfig.java b/src/main/java/net/Broken/Api/Security/SecurityConfig.java index b2c0448..6bc7d2f 100644 --- a/src/main/java/net/Broken/Api/Security/SecurityConfig.java +++ b/src/main/java/net/Broken/Api/Security/SecurityConfig.java @@ -1,51 +1,55 @@ package net.Broken.Api.Security; -import net.Broken.Api.Security.Components.UnauthorizedHandler; -import net.Broken.Api.Security.Filters.JwtFilter; +import static org.springframework.security.config.Customizer.withDefaults; + import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import net.Broken.Api.Security.Components.UnauthorizedHandler; +import net.Broken.Api.Security.Filters.JwtFilter; + @EnableWebSecurity @Configuration -public class SecurityConfig extends WebSecurityConfigurerAdapter { +public class SecurityConfig { private final UnauthorizedHandler unauthorizedHandler; + public SecurityConfig(UnauthorizedHandler unauthorizedHandler) { this.unauthorizedHandler = unauthorizedHandler; } - @Override - protected void configure(HttpSecurity http) throws Exception { - http.cors().and().csrf().disable() - .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() - .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() - .authorizeRequests() - .antMatchers("/api/v2/auth/**").permitAll() - .antMatchers("/api/v2/guild/inviteLink").permitAll() - .antMatchers("/swagger-ui/**").permitAll() - .antMatchers("/swagger-ui.html").permitAll() - .antMatchers("/v3/api-docs/**").permitAll() - .antMatchers("/actuator/**").permitAll() - .anyRequest().authenticated(); + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http.cors(withDefaults()).csrf(csrf -> csrf.disable()) + .exceptionHandling(handling -> handling.authenticationEntryPoint(unauthorizedHandler)) + .sessionManagement(management -> management.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(requests -> requests + .antMatchers("/api/v2/auth/**").permitAll() + .antMatchers("/api/v2/guild/inviteLink").permitAll() + .antMatchers("/swagger-ui/**").permitAll() + .antMatchers("/swagger-ui.html").permitAll() + .antMatchers("/v3/api-docs/**").permitAll() + .antMatchers("/actuator/**").permitAll() + .anyRequest().authenticated()); http.addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class); - + return http.build(); } @Bean - public JwtFilter jwtFilter(){ + public JwtFilter jwtFilter() { return new JwtFilter(); } - @Bean - @Override - public AuthenticationManager authenticationManagerBean() throws Exception { - return super.authenticationManagerBean(); + public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception { + return http.getSharedObject(AuthenticationManagerBuilder.class) + .build(); } } diff --git a/src/main/java/net/Broken/Api/Security/Services/DiscordOauthService.java b/src/main/java/net/Broken/Api/Security/Services/DiscordOauthService.java index 5676833..139df42 100644 --- a/src/main/java/net/Broken/Api/Security/Services/DiscordOauthService.java +++ b/src/main/java/net/Broken/Api/Security/Services/DiscordOauthService.java @@ -58,11 +58,13 @@ public class DiscordOauthService { try { HttpResponse response = makeFormPost(this.tokenEndpoint, data); if (response.statusCode() != 200) { - logger.warn("[OAUTH] Invalid response while getting AccessToken: Status Code: " + response.statusCode() + " Body:" + response.body()); + logger.warn("[OAUTH] Invalid response while getting AccessToken: Status Code: " + response.statusCode() + + " Body:" + response.body()); throw new OAuthLoginFail(); } ObjectMapper objectMapper = new ObjectMapper(); - AccessTokenResponse accessTokenResponse = objectMapper.readValue(response.body(), AccessTokenResponse.class); + AccessTokenResponse accessTokenResponse = objectMapper.readValue(response.body(), + AccessTokenResponse.class); return accessTokenResponse.access_token(); } catch (IOException | InterruptedException e) { logger.catching(e); @@ -81,7 +83,8 @@ public class DiscordOauthService { HttpClient client = HttpClient.newHttpClient(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() != 200) { - logger.warn("[OAUTH] Invalid response while getting UserInfo: Status Code: " + response.statusCode() + " Body:" + response.body()); + logger.warn("[OAUTH] Invalid response while getting UserInfo: Status Code: " + response.statusCode() + + " Body:" + response.body()); throw new OAuthLoginFail(); } ObjectMapper mapper = new ObjectMapper(); @@ -99,49 +102,49 @@ public class DiscordOauthService { try { HttpResponse response = makeFormPost(this.tokenRevokeEndpoint, data); if (response.statusCode() != 200) { - logger.warn("[OAUTH] Invalid response while token revocation: Status Code: " + response.statusCode() + " Body:" + response.body()); + logger.warn("[OAUTH] Invalid response while token revocation: Status Code: " + response.statusCode() + + " Body:" + response.body()); } } catch (IOException | InterruptedException e) { logger.catching(e); } } - public record LoginOrRegisterResponse(T response, boolean created) { } public LoginOrRegisterResponse loginOrRegisterDiscordUser(DiscordOauthUserInfo discordOauthUserInfo) { Optional optionalUserEntity = userRepository.findByDiscordId(discordOauthUserInfo.id()); return optionalUserEntity.map( - userEntity -> new LoginOrRegisterResponse<>(userEntity, false)) + userEntity -> new LoginOrRegisterResponse<>(userEntity, false)) .orElseGet(() -> { UserEntity created = userRepository.save(new UserEntity(discordOauthUserInfo)); return new LoginOrRegisterResponse<>(created, true); }); } - public UserEntity updateUserInfo(DiscordOauthUserInfo discordOauthUserInfo, UserEntity userEntity){ + public UserEntity updateUserInfo(DiscordOauthUserInfo discordOauthUserInfo, UserEntity userEntity) { boolean updated = false; - if(userEntity.getUsername() == null || !userEntity.getUsername().equals(discordOauthUserInfo.username())){ + if (userEntity.getUsername() == null || !userEntity.getUsername().equals(discordOauthUserInfo.username())) { userEntity.setUsername(discordOauthUserInfo.username()); updated = true; } - if(userEntity.getDiscriminator() == null || !userEntity.getDiscriminator().equals(discordOauthUserInfo.discriminator())){ + if (userEntity.getDiscriminator() == null + || !userEntity.getDiscriminator().equals(discordOauthUserInfo.discriminator())) { userEntity.setDiscriminator(discordOauthUserInfo.discriminator()); updated = true; } - if(userEntity.getAvatar() == null || !userEntity.getAvatar().equals(discordOauthUserInfo.avatar())){ + if (userEntity.getAvatar() == null || !userEntity.getAvatar().equals(discordOauthUserInfo.avatar())) { userEntity.setAvatar(discordOauthUserInfo.avatar()); updated = true; } - if(updated){ + if (updated) { return userRepository.save(userEntity); } return userEntity; } - private String getFormString(HashMap params) { StringBuilder result = new StringBuilder(); boolean first = true; @@ -157,7 +160,8 @@ public class DiscordOauthService { return result.toString(); } - private HttpResponse makeFormPost(String endpoint, HashMap data) throws IOException, InterruptedException { + private HttpResponse makeFormPost(String endpoint, HashMap data) + throws IOException, InterruptedException { HttpRequest.BodyPublisher body = HttpRequest.BodyPublishers.ofString(getFormString(data)); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(tokenEndpoint)) @@ -168,5 +172,4 @@ public class DiscordOauthService { return client.send(request, HttpResponse.BodyHandlers.ofString()); } - } diff --git a/src/main/java/net/Broken/Api/Services/AudioService.java b/src/main/java/net/Broken/Api/Services/AudioService.java index e3a5708..a037284 100644 --- a/src/main/java/net/Broken/Api/Services/AudioService.java +++ b/src/main/java/net/Broken/Api/Services/AudioService.java @@ -9,7 +9,9 @@ import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.audio.hooks.ConnectionStatus; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.VoiceChannel; +import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; +import net.dv8tion.jda.api.entities.channel.unions.AudioChannelUnion; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.http.HttpStatus; @@ -28,7 +30,7 @@ public class AudioService { Member member = guild.getMemberById(userId); - VoiceChannel channel = guild.getAudioManager().getConnectedChannel(); + AudioChannelUnion channel = guild.getAudioManager().getConnectedChannel(); ConnectionStatus status = guild.getAudioManager().getConnectionStatus(); if (channel != null) { // The user can view the audio status if: diff --git a/src/main/java/net/Broken/Api/Services/GuildService.java b/src/main/java/net/Broken/Api/Services/GuildService.java index 867c086..4fb0709 100644 --- a/src/main/java/net/Broken/Api/Services/GuildService.java +++ b/src/main/java/net/Broken/Api/Services/GuildService.java @@ -8,6 +8,9 @@ import net.Broken.MainBot; import net.dv8tion.jda.api.Permission; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.User; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; +import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; + import org.springframework.stereotype.Service; import java.util.ArrayList; @@ -35,7 +38,7 @@ public class GuildService { net.dv8tion.jda.api.entities.Guild guild = MainBot.jda.getGuildById(guildId); Member member = guild.getMemberById(userId); List voiceChannels = new ArrayList<>(); - for (net.dv8tion.jda.api.entities.VoiceChannel voiceChannel : guild.getVoiceChannels()) { + for (VoiceChannel voiceChannel : guild.getVoiceChannels()) { if (member.hasPermission(voiceChannel, Permission.VIEW_CHANNEL)) { voiceChannels.add(new Channel(voiceChannel.getId(), voiceChannel.getName())); } @@ -47,7 +50,7 @@ public class GuildService { net.dv8tion.jda.api.entities.Guild guild = MainBot.jda.getGuildById(guildId); Member member = guild.getMemberById(userId); List voiceChannels = new ArrayList<>(); - for (net.dv8tion.jda.api.entities.TextChannel textChannel : guild.getTextChannels()) { + for (TextChannel textChannel : guild.getTextChannels()) { if (member.hasPermission(textChannel, Permission.VIEW_CHANNEL)) { voiceChannels.add(new Channel(textChannel.getId(), textChannel.getName())); } diff --git a/src/main/java/net/Broken/Audio/GuildAudioBotService.java b/src/main/java/net/Broken/Audio/GuildAudioBotService.java index b9cbbd4..fc0f9d2 100644 --- a/src/main/java/net/Broken/Audio/GuildAudioBotService.java +++ b/src/main/java/net/Broken/Audio/GuildAudioBotService.java @@ -24,17 +24,19 @@ import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; import net.Broken.Tools.EmbedMessageUtils; import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.MessageBuilder; -import net.dv8tion.jda.api.entities.Emoji; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.User; -import net.dv8tion.jda.api.entities.VoiceChannel; -import net.dv8tion.jda.api.events.interaction.GenericInteractionCreateEvent; -import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; +import net.dv8tion.jda.api.entities.channel.middleman.AudioChannel; +import net.dv8tion.jda.api.entities.channel.unions.AudioChannelUnion; +import net.dv8tion.jda.api.entities.emoji.Emoji; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.InteractionHook; import net.dv8tion.jda.api.interactions.components.ActionRow; -import net.dv8tion.jda.api.interactions.components.Button; +import net.dv8tion.jda.api.interactions.components.buttons.Button; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; public class GuildAudioBotService { @@ -47,7 +49,7 @@ public class GuildAudioBotService { /** * Extrem limit for playlist */ - private final int listExtremLimit = 300; + private static final int LIST_EXTREM_LIMIT = 300; private final Guild guild; private final Logger logger = LogManager.getLogger(); @@ -66,9 +68,7 @@ public class GuildAudioBotService { } public static GuildAudioBotService getInstance(Guild guild) { - if (!INSTANCES.containsKey(guild)) { - INSTANCES.put(guild, new GuildAudioBotService(guild)); - } + INSTANCES.computeIfAbsent(guild, k -> new GuildAudioBotService(guild)); return INSTANCES.get(guild); } @@ -80,13 +80,13 @@ public class GuildAudioBotService { * @param playlistLimit Limit of playlist * @param onHead True for adding audio track on top of playlist */ - public void loadAndPlay(SlashCommandEvent event, VoiceChannel voiceChannel, final String trackUrl, int playlistLimit, boolean onHead) { + public void loadAndPlay(SlashCommandInteractionEvent event, AudioChannel voiceChannel, final String trackUrl, int playlistLimit, boolean onHead) { audioPlayerManager.loadItemOrdered(guildAudioManager, trackUrl, new AudioLoadResultHandler() { @Override public void trackLoaded(AudioTrack track) { - logger.info("[" + guild + "] Single Track detected!"); + logger.info("[{}] Single Track detected!", guild.getName()); UserAudioTrack uat = new UserAudioTrack(event.getUser(), track); - Message message = new MessageBuilder() + MessageCreateData message = new MessageCreateBuilder() .setEmbeds(EmbedMessageUtils.getMusicAdded(track.getInfo(), event.getMember(), -1)) .build(); clearLastButton(); @@ -96,10 +96,10 @@ public class GuildAudioBotService { @Override public void playlistLoaded(AudioPlaylist playlist) { - logger.info("[" + guild + "] Playlist detected! Limit: " + playlistLimit); + logger.info("[{}] Playlist detected! Limit: {}", guild, playlistLimit); AudioTrack firstTrack = playlist.getSelectedTrack(); int size = Math.min(playlist.getTracks().size(), playlistLimit); - Message message = new MessageBuilder() + MessageCreateData message = new MessageCreateBuilder() .setEmbeds(EmbedMessageUtils.getMusicAdded(firstTrack.getInfo(), event.getMember(), size)) .build(); clearLastButton(); @@ -109,16 +109,16 @@ public class GuildAudioBotService { @Override public void noMatches() { - logger.warn("[" + guild + "] Cant find media!"); - Message message = new MessageBuilder().setEmbeds(EmbedMessageUtils.getMusicError("Video not found !")).build(); + logger.warn("[{}] Cant find media!", guild); + MessageCreateData message = new MessageCreateBuilder().setEmbeds(EmbedMessageUtils.getMusicError("Video not found !")).build(); event.getHook().setEphemeral(true).sendMessage(message).queue(); } @Override public void loadFailed(FriendlyException exception) { - logger.error("[" + guild + "] Can't load media!"); + logger.error("[{}] Can't load media!", guild); logger.error(exception.getMessage()); - Message message = new MessageBuilder().setEmbeds(EmbedMessageUtils.getMusicError("Playback error !")).build(); + MessageCreateData message = new MessageCreateBuilder().setEmbeds(EmbedMessageUtils.getMusicError("Playback error !")).build(); event.getHook().setEphemeral(true).sendMessage(message).queue(); } }); @@ -126,12 +126,12 @@ public class GuildAudioBotService { public boolean loadAndPlaySync(String trackUrl, String userId) throws ExecutionException, InterruptedException { Member member = guild.getMemberById(userId); - VoiceChannel playedChanel = guild.getAudioManager().getConnectedChannel(); + AudioChannelUnion playedChanel = guild.getAudioManager().getConnectedChannel(); final String uuid = UUID.randomUUID().toString(); Future future = audioPlayerManager.loadItemOrdered(guildAudioManager, trackUrl, new AudioLoadResultHandler() { @Override public void trackLoaded(AudioTrack track) { - logger.info("[" + guild + "] Auto add " + track.getInfo().title + " to playlist."); + logger.info("[{}] Auto add {} to playlist.", guild, track.getInfo().title); UserAudioTrack userAudioTrack = new UserAudioTrack(member.getUser(), track); play(guild, playedChanel, guildAudioManager, userAudioTrack, true); addStatus.put(uuid, true); @@ -140,7 +140,7 @@ public class GuildAudioBotService { @Override public void playlistLoaded(AudioPlaylist playlist) { AudioTrack track = playlist.getTracks().get(0); - logger.info("[" + guild + "] Auto add " + track.getInfo().title + " to playlist."); + logger.info("[{}] Auto add {} to playlist.", guild, track.getInfo().title); UserAudioTrack userAudioTrack = new UserAudioTrack(member.getUser(), track); play(guild, playedChanel, guildAudioManager, userAudioTrack, true); addStatus.put(uuid, true); @@ -148,13 +148,13 @@ public class GuildAudioBotService { @Override public void noMatches() { - logger.warn("[" + guild + "] Track not found: " + trackUrl); + logger.warn("[{}] Track not found: {}", guild, trackUrl); addStatus.put(uuid, false); } @Override public void loadFailed(FriendlyException exception) { - logger.error("[" + guild + "] Cant load media!"); + logger.error("[{}] Cant load media!", guild); logger.error(exception.getMessage()); addStatus.put(uuid, false); } @@ -174,7 +174,7 @@ public class GuildAudioBotService { */ public void playListLoader(AudioPlaylist playlist, int playlistLimit, User user, boolean onHead) { - VoiceChannel playedChanel = guild.getAudioManager().getConnectedChannel(); + AudioChannelUnion playedChanel = guild.getAudioManager().getConnectedChannel(); List tracks = playlist.getTracks(); if (onHead) Collections.reverse(tracks); @@ -183,7 +183,7 @@ public class GuildAudioBotService { for (AudioTrack track : playlist.getTracks()) { UserAudioTrack uat = new UserAudioTrack(user, track); play(guild, playedChanel, guildAudioManager, uat, onHead); - if ((playlistLimit != -1 && i >= playlistLimit) || i > listExtremLimit) + if ((playlistLimit != -1 && i >= playlistLimit) || i > LIST_EXTREM_LIMIT) break; i++; } @@ -199,7 +199,7 @@ public class GuildAudioBotService { * @param track Track to add to playlist * @param onHead True for adding audio track on top of playlist */ - public void play(Guild guild, VoiceChannel channel, GuildAudioManager musicManager, UserAudioTrack track, boolean onHead) { + public void play(Guild guild, AudioChannel channel, GuildAudioManager musicManager, UserAudioTrack track, boolean onHead) { if (!guild.getAudioManager().isConnected()) guild.getAudioManager().openAudioConnection(channel); if (!onHead) @@ -208,39 +208,39 @@ public class GuildAudioBotService { musicManager.scheduler.addNext(track); } - public void add(SlashCommandEvent event, String url, int playListLimit, boolean onHead) { + public void add(SlashCommandInteractionEvent event, String url, int playListLimit, boolean onHead) { if (guild.getAudioManager().isConnected()) { loadAndPlay(event, guild.getAudioManager().getConnectedChannel(), url, playListLimit, onHead); } else { - Message message = new MessageBuilder().setEmbeds(EmbedMessageUtils.getMusicError("Not connected to vocal chanel !")).build(); + MessageCreateData message = new MessageCreateBuilder().setEmbeds(EmbedMessageUtils.getMusicError("Not connected to vocal chanel !")).build(); event.getHook().setEphemeral(true).sendMessage(message).queue(); } } - public void connect(VoiceChannel voiceChannel) { + public void connect(AudioChannel voiceChannel) { guild.getAudioManager().openAudioConnection(voiceChannel); } - public void pause(GenericInteractionCreateEvent event) { + public void pause(InteractionHook hook) { pause(); - Message message = new MessageBuilder().setEmbeds( + MessageCreateData message = new MessageCreateBuilder().setEmbeds( EmbedMessageUtils.buildStandar( new EmbedBuilder() .setTitle(":pause_button: Playback paused") .setColor(Color.green) )).build(); clearLastButton(); - lastMessageWithButton = event.getHook().sendMessage(message).addActionRow(getActionButton()).complete(); + lastMessageWithButton = hook.sendMessage(message).addActionRow(getActionButton()).complete(); } public void pause() { guildAudioManager.scheduler.pause(); } - public void resume(GenericInteractionCreateEvent event) { - Message message; + public void resume(InteractionHook hook) { + MessageCreateData message; if (guildAudioManager.player.getPlayingTrack() == null) { - message = new MessageBuilder().setEmbeds( + message = new MessageCreateBuilder().setEmbeds( EmbedMessageUtils.buildStandar( new EmbedBuilder() .setTitle(":warning: Nothing to play, playlist is empty !") @@ -248,7 +248,7 @@ public class GuildAudioBotService { )).build(); } else { resume(); - message = new MessageBuilder().setEmbeds( + message = new MessageCreateBuilder().setEmbeds( EmbedMessageUtils.buildStandar( new EmbedBuilder() .setTitle(":arrow_forward: Playback resumed") @@ -256,39 +256,39 @@ public class GuildAudioBotService { )).build(); } clearLastButton(); - lastMessageWithButton = event.getHook().sendMessage(message).addActionRow(getActionButton()).complete(); + lastMessageWithButton = hook.sendMessage(message).addActionRow(getActionButton()).complete(); } public void resume() { guildAudioManager.scheduler.resume(); } - public void skipTrack(GenericInteractionCreateEvent event) { + public void skipTrack(InteractionHook hook) { skipTrack(); - Message message = new MessageBuilder().setEmbeds( + MessageCreateData message = new MessageCreateBuilder().setEmbeds( EmbedMessageUtils.buildStandar( new EmbedBuilder() .setTitle(":track_next: Next Track") .setColor(Color.green) )).build(); clearLastButton(); - lastMessageWithButton = event.getHook().sendMessage(message).addActionRow(getActionButton()).complete(); + lastMessageWithButton = hook.sendMessage(message).addActionRow(getActionButton()).complete(); } public void skipTrack() { guildAudioManager.scheduler.nextTrack(); } - public void stop(GenericInteractionCreateEvent event) { + public void stop(InteractionHook hook) { stop(); - Message message = new MessageBuilder().setEmbeds( + MessageCreateData message = new MessageCreateBuilder().setEmbeds( EmbedMessageUtils.buildStandar( new EmbedBuilder() .setTitle(":stop_button: Playback stopped") .setColor(Color.green) )).build(); clearLastButton(); - lastMessageWithButton = event.getHook().sendMessage(message).addActionRow(getActionButton()).complete(); + lastMessageWithButton = hook.sendMessage(message).addActionRow(getActionButton()).complete(); } @@ -298,16 +298,16 @@ public class GuildAudioBotService { clearLastButton(); } - public void disconnect(GenericInteractionCreateEvent event) { + public void disconnect(InteractionHook hook) { disconnect(); - Message message = new MessageBuilder().setEmbeds( + MessageCreateData message = new MessageCreateBuilder().setEmbeds( EmbedMessageUtils.buildStandar( new EmbedBuilder() .setTitle(":eject: Disconnected") .setColor(Color.green) )).build(); clearLastButton(); - event.getHook().sendMessage(message).queue(); + hook.sendMessage(message).queue(); } public void disconnect() { @@ -316,24 +316,24 @@ public class GuildAudioBotService { guild.getAudioManager().closeAudioConnection(); clearLastButton(); } - public void info(GenericInteractionCreateEvent event) { + public void info(InteractionHook hook) { AudioTrackInfo info = guildAudioManager.scheduler.getInfo(); UserAudioTrack userAudioTrack = guildAudioManager.scheduler.getCurrentPlayingTrack(); - Message message = new MessageBuilder().setEmbeds(EmbedMessageUtils.getMusicInfo(info, userAudioTrack)).build(); + MessageCreateData message = new MessageCreateBuilder().setEmbeds(EmbedMessageUtils.getMusicInfo(info, userAudioTrack)).build(); clearLastButton(); - lastMessageWithButton = event.getHook().sendMessage(message).addActionRow(getActionButton()).complete(); + lastMessageWithButton = hook.sendMessage(message).addActionRow(getActionButton()).complete(); } - public void flush(GenericInteractionCreateEvent event) { + public void flush(InteractionHook hook) { guildAudioManager.scheduler.flush(); - Message message = new MessageBuilder().setEmbeds( + MessageCreateData message = new MessageCreateBuilder().setEmbeds( EmbedMessageUtils.buildStandar( new EmbedBuilder() .setTitle(":wastebasket: Playlist flushed") .setColor(Color.green) )).build(); clearLastButton(); - lastMessageWithButton = event.getHook().sendMessage(message).addActionRow(getActionButton()).complete(); + lastMessageWithButton = hook.sendMessage(message).addActionRow(getActionButton()).complete(); } /** @@ -341,18 +341,18 @@ public class GuildAudioBotService { * * @param event */ - public void list(GenericInteractionCreateEvent event) { + public void list(InteractionHook hook) { List list = guildAudioManager.scheduler.getList(); - if (list.size() == 0) { - Message message = new MessageBuilder().setEmbeds( + if (list.isEmpty()) { + MessageCreateData message = new MessageCreateBuilder().setEmbeds( EmbedMessageUtils.buildStandar( new EmbedBuilder() .setTitle(":scroll: Playlist") .setColor(Color.green) .setDescription("Oh no ! The playlist is empty !") )).build(); - event.getHook().sendMessage(message).queue(); + hook.sendMessage(message).queue(); } else { StringBuilder resp = new StringBuilder(); int i = 0; @@ -370,14 +370,14 @@ public class GuildAudioBotService { } i++; } - Message message = new MessageBuilder().setEmbeds( + MessageCreateData message = new MessageCreateBuilder().setEmbeds( EmbedMessageUtils.buildStandar( new EmbedBuilder() .setTitle(":scroll: Playlist") .setColor(Color.green) .setDescription(resp.toString()) )).build(); - event.getHook().sendMessage(message).queue(); + hook.sendMessage(message).queue(); } } diff --git a/src/main/java/net/Broken/BotListener.java b/src/main/java/net/Broken/BotListener.java index 5fe53c9..9b156f8 100644 --- a/src/main/java/net/Broken/BotListener.java +++ b/src/main/java/net/Broken/BotListener.java @@ -1,5 +1,13 @@ package net.Broken; +import java.awt.Color; +import java.util.HashMap; +import java.util.Optional; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.context.ApplicationContext; + import net.Broken.Audio.GuildAudioBotService; import net.Broken.DB.Entity.GuildPreferenceEntity; import net.Broken.DB.Repository.GuildPreferenceRepository; @@ -7,28 +15,21 @@ import net.Broken.Tools.AutoVoiceChannel; import net.Broken.Tools.EmbedMessageUtils; import net.Broken.Tools.UserManager.Stats.UserStatsUtils; import net.dv8tion.jda.api.EmbedBuilder; -import net.dv8tion.jda.api.MessageBuilder; -import net.dv8tion.jda.api.entities.*; -import net.dv8tion.jda.api.events.ReadyEvent; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.MessageEmbed; +import net.dv8tion.jda.api.entities.Role; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import net.dv8tion.jda.api.events.guild.GuildJoinEvent; import net.dv8tion.jda.api.events.guild.member.GuildMemberJoinEvent; import net.dv8tion.jda.api.events.guild.member.GuildMemberRoleRemoveEvent; -import net.dv8tion.jda.api.events.guild.voice.GuildVoiceJoinEvent; -import net.dv8tion.jda.api.events.guild.voice.GuildVoiceLeaveEvent; -import net.dv8tion.jda.api.events.guild.voice.GuildVoiceMoveEvent; -import net.dv8tion.jda.api.events.interaction.ButtonClickEvent; -import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; +import net.dv8tion.jda.api.events.guild.voice.GuildVoiceUpdateEvent; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.message.MessageReceivedEvent; +import net.dv8tion.jda.api.events.session.ReadyEvent; import net.dv8tion.jda.api.hooks.ListenerAdapter; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; -import org.springframework.context.ApplicationContext; - -import java.awt.*; -import java.util.HashMap; -import java.util.Optional; - +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; /** * Bot Listener @@ -50,27 +51,27 @@ public class BotListener extends ListenerAdapter { logger.info("Connection success"); } - @Override public void onGuildMemberJoin(GuildMemberJoinEvent event) { GuildPreferenceEntity guildPref = getPreference(event.getGuild()); if (guildPref.isDefaultRole()) { - logger.info("[" + event.getGuild().getName() + "] : " + event.getUser().getName() + " join the guild, adding default role !"); - Role default_role = event.getGuild().getRoleById(guildPref.getDefaultRoleId()); - if (default_role != null) { - event.getGuild().addRoleToMember(event.getMember(), default_role).queue(); + logger.info("[{}] : {} join the guild, adding default role !", event.getGuild().getName(), + event.getUser().getName()); + Role defaultRole = event.getGuild().getRoleById(guildPref.getDefaultRoleId()); + if (defaultRole != null) { + event.getGuild().addRoleToMember(event.getMember(), defaultRole).queue(); } else { - logger.fatal("[" + event.getGuild().getName() + "] : Default role is null !"); + logger.fatal("[{}] : Default role is null !", event.getGuild().getName()); } } if (guildPref.isWelcome()) { TextChannel chanel = event.getGuild().getTextChannelById(guildPref.getWelcomeChanelID()); if (chanel != null) { - String message = guildPref.getWelcomeMessage().replaceAll("@name", event.getMember().getAsMention()); + String message = guildPref.getWelcomeMessage().replace("@name", event.getMember().getAsMention()); logger.debug(message); chanel.sendMessage(message).queue(); } else { - logger.fatal("[" + event.getGuild().getName() + "] : Welcome chanel is null !"); + logger.fatal("[{}] : Welcome chanel is null !", event.getGuild().getName()); } } @@ -79,61 +80,55 @@ public class BotListener extends ListenerAdapter { @Override public void onGuildMemberRoleRemove(GuildMemberRoleRemoveEvent event) { GuildPreferenceEntity guildPref = getPreference(event.getGuild()); - if (guildPref.isDefaultRole()) { - if (event.getMember().getRoles().size() == 0) { - logger.info("[" + event.getGuild().getName() + "] : " + event.getUser().getName() + " have no roles, reset to default !"); - Role default_role = event.getGuild().getRoleById(guildPref.getDefaultRoleId()); - if (default_role == null) { - logger.fatal("[" + event.getGuild().getName() + "] : Default role is null !"); - return; - } - event.getGuild().addRoleToMember(event.getMember(), default_role).queue(); + if (guildPref.isDefaultRole() && event.getMember().getRoles().isEmpty()) { + logger.info("[{}] : {} have no roles, reset to default !", event.getGuild().getName(), + event.getUser().getName()); + Role defaultRole = event.getGuild().getRoleById(guildPref.getDefaultRoleId()); + if (defaultRole == null) { + logger.fatal("[{}] : Default role is null !", event.getGuild().getName()); + return; } + event.getGuild().addRoleToMember(event.getMember(), defaultRole).queue(); } - } - @Override - public void onGuildVoiceJoin(GuildVoiceJoinEvent event) { - super.onGuildVoiceJoin(event); - if (!event.getMember().getUser().isBot()) { + public void onGuildVoiceUpdate(GuildVoiceUpdateEvent event) { + super.onGuildVoiceUpdate(event); + // Disconnect + if (event.getChannelJoined() == null) { + if (event.getGuild().getAudioManager().isConnected()) { + logger.trace("User disconnected from voice channel."); + + if (event.getGuild().getAudioManager().getConnectedChannel().getMembers().size() == 1) { + logger.debug("I'm alone, close audio connection."); + GuildAudioBotService.getInstance(event.getGuild()).stop(); + } + } else if (event.getMember().getUser() == MainBot.jda.getSelfUser()) { + GuildAudioBotService.getInstance(event.getGuild()).clearLastButton(); + } + AutoVoiceChannel autoVoiceChannel = AutoVoiceChannel.getInstance(event.getGuild()); + autoVoiceChannel.leave(event.getChannelLeft()); + } + // Connect + else if (event.getChannelLeft() == null && !event.getMember().getUser().isBot()) { UserStatsUtils userStatsUtils = UserStatsUtils.getINSTANCE(); if (!userStatsUtils.runningCounters.containsKey(event.getMember().getId())) { - UserStatsUtils.VoicePresenceCounter temp = new UserStatsUtils.VoicePresenceCounter(event.getMember()); + UserStatsUtils.VoicePresenceCounter temp = new UserStatsUtils.VoicePresenceCounter( + event.getMember()); temp.start(); userStatsUtils.runningCounters.put(event.getMember().getId(), temp); - } AutoVoiceChannel autoVoiceChannel = AutoVoiceChannel.getInstance(event.getGuild()); autoVoiceChannel.join(event.getChannelJoined()); } - } - - @Override - public void onGuildVoiceLeave(GuildVoiceLeaveEvent event) { - super.onGuildVoiceLeave(event); - if (event.getGuild().getAudioManager().isConnected()) { - logger.trace("User disconnected from voice channel."); - - if (event.getGuild().getAudioManager().getConnectedChannel().getMembers().size() == 1) { - logger.debug("I'm alone, close audio connection."); - GuildAudioBotService.getInstance(event.getGuild()).stop(); - } - } else if (event.getMember().getUser() == MainBot.jda.getSelfUser()) { - GuildAudioBotService.getInstance(event.getGuild()).clearLastButton(); + // Move + else { + AutoVoiceChannel autoVoiceChannel = AutoVoiceChannel.getInstance(event.getGuild()); + autoVoiceChannel.leave(event.getChannelLeft()); + autoVoiceChannel.join(event.getChannelJoined()); } - AutoVoiceChannel autoVoiceChannel = AutoVoiceChannel.getInstance(event.getGuild()); - autoVoiceChannel.leave(event.getChannelLeft()); - } - - @Override - public void onGuildVoiceMove(@NotNull GuildVoiceMoveEvent event) { - super.onGuildVoiceMove(event); - AutoVoiceChannel autoVoiceChannel = AutoVoiceChannel.getInstance(event.getGuild()); - autoVoiceChannel.leave(event.getChannelLeft()); - autoVoiceChannel.join(event.getChannelJoined()); } @Override @@ -144,23 +139,23 @@ public class BotListener extends ListenerAdapter { } @Override - public void onButtonClick(@NotNull ButtonClickEvent event) { - super.onButtonClick(event); + public void onButtonInteraction(ButtonInteractionEvent event) { + super.onButtonInteraction(event); event.deferReply().queue(); GuildAudioBotService guildAudioBotService = GuildAudioBotService.getInstance(event.getGuild()); switch (event.getComponentId()) { - case "pause" -> guildAudioBotService.pause(event); - case "play" -> guildAudioBotService.resume(event); - case "next" -> guildAudioBotService.skipTrack(event); - case "stop" -> guildAudioBotService.stop(event); - case "disconnect" -> guildAudioBotService.disconnect(event); + case "pause" -> guildAudioBotService.pause(event.getHook()); + case "play" -> guildAudioBotService.resume(event.getHook()); + case "next" -> guildAudioBotService.skipTrack(event.getHook()); + case "stop" -> guildAudioBotService.stop(event.getHook()); + case "disconnect" -> guildAudioBotService.disconnect(event.getHook()); } } @Override - public void onSlashCommand(@NotNull SlashCommandEvent event) { + public void onSlashCommandInteraction(SlashCommandInteractionEvent event) { + super.onSlashCommandInteraction(event); HashMap commands = MainBot.slashCommands; - super.onSlashCommand(event); if (commands.containsKey(event.getName())) { SlashCommand commandRunner = commands.get(event.getName()); // It's form private message @@ -170,18 +165,16 @@ public class BotListener extends ListenerAdapter { } else { MessageEmbed message = EmbedMessageUtils.buildStandar(new EmbedBuilder() .setTitle(":no_entry_sign: This command is not usable in private message") - .setColor(Color.red) - ); + .setColor(Color.red)); event.replyEmbeds(message).setEphemeral(true).queue(); } } else { - if (!commandRunner.isNSFW() || event.getTextChannel().isNSFW()) { + if (!commandRunner.isNSFW() || event.getChannel().asTextChannel().isNSFW()) { commandRunner.action(event); } else { MessageEmbed message = EmbedMessageUtils.buildStandar(new EmbedBuilder() .setTitle(":underage: This command is only usable in NSFW channels") - .setColor(Color.red) - ); + .setColor(Color.red)); event.replyEmbeds(message).setEphemeral(true).queue(); } } @@ -190,19 +183,23 @@ public class BotListener extends ListenerAdapter { @Override public void onGuildJoin(GuildJoinEvent event) { - logger.info("Join new guild! (" + event.getGuild().getName() + " " + event.getGuild().getMembers().size() + " Members)"); + logger.info("Join new guild! ({} {} Members)", event.getGuild().getName(), + event.getGuild().getMembers().size()); super.onGuildJoin(event); getPreference(event.getGuild()); - event.getGuild().loadMembers().onSuccess((members -> logger.debug("[" + event.getGuild().getName() + "] Members loaded"))); + event.getGuild().loadMembers() + .onSuccess((members -> logger.debug("[{}] Members loaded", event.getGuild().getName()))); EmbedBuilder eb = new EmbedBuilder().setColor(Color.GREEN) .setTitle("Hello there !") - .setDescription("Allow me to introduce myself -- I am a CL4P-TP the discord bot, but my friends call me Claptrap ! Or they would, if any of them were real...\n" + - "\nYou can access to my web UI with: " + botConfig.url()) + .setDescription( + "Allow me to introduce myself -- I am a CL4P-TP the discord bot, but my friends call me Claptrap ! Or they would, if any of them were real...\n" + + + "\nYou can access to my web UI with: " + botConfig.url()) .setImage("https://i.imgur.com/Anf1Srg.gif"); - Message message = new MessageBuilder().setEmbeds(EmbedMessageUtils.buildStandar(eb)).build(); + MessageCreateData message = new MessageCreateBuilder().setEmbeds(EmbedMessageUtils.buildStandar(eb)).build(); - TextChannel defaultChan = event.getGuild().getDefaultChannel(); - if (defaultChan != null && defaultChan.canTalk()) { + TextChannel defaultChan = event.getGuild().getDefaultChannel().asTextChannel(); + if (defaultChan.canTalk()) { defaultChan.sendMessage(message).queue(); } else { for (TextChannel chan : event.getGuild().getTextChannels()) { @@ -217,12 +214,10 @@ public class BotListener extends ListenerAdapter { private GuildPreferenceEntity getPreference(Guild guild) { Optional guildPref = guildPreferenceRepository.findByGuildId(guild.getId()); if (guildPref.isEmpty()) { - logger.info("[" + guild.getName() + "] : Generate default pref"); + logger.info("[{}] : Generate default pref", guild.getName()); return guildPreferenceRepository.save(GuildPreferenceEntity.getDefault(guild.getId())); - } else{ + } else { return guildPref.get(); } } - - } diff --git a/src/main/java/net/Broken/Commands/ChannelsReview.java b/src/main/java/net/Broken/Commands/ChannelsReview.java deleted file mode 100644 index 46b1db6..0000000 --- a/src/main/java/net/Broken/Commands/ChannelsReview.java +++ /dev/null @@ -1,132 +0,0 @@ -package net.Broken.Commands; - -import net.Broken.Commande; -import net.Broken.Tools.EmbedMessageUtils; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.TextChannel; -import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; -import java.time.format.DateTimeFormatter; -import java.util.Date; -import java.util.HashMap; -import java.util.Locale; - -public class ChannelsReview implements Commande { - final Logger logger = LogManager.getLogger(); - - @Override - public void action(String[] args, MessageReceivedEvent event) { - DateFormat format = new SimpleDateFormat("dd.MM.yyyy"); - event.getTextChannel().sendMessage("Number of channels found in total : " + event.getGuild().getTextChannels().size()).queue(); - if (args.length >= 1) { - try { - SendBackBefore(format.parse(args[0]), event, format); - } catch (ParseException e) { - logger.warn("Can't parse date : " + e.getMessage()); - } - } else { - SendBack(event); - } - } - - - private void SendBackBefore(Date beforeDate, MessageReceivedEvent event, DateFormat format) { - HashMap result = new HashMap<>(); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy").withLocale(Locale.ENGLISH); - int charCtl = 0; - for (TextChannel textChannel : event.getGuild().getTextChannels()) { - if (textChannel.hasLatestMessage()) { - String lastMessageId = textChannel.getLatestMessageId(); - try { - Message lastMessage = textChannel.retrieveMessageById(lastMessageId).complete(); - if (beforeDate.compareTo(format.parse(lastMessage.getTimeCreated().format(formatter))) > 0) { - logger.debug("Last message in channel " + textChannel + " is " + lastMessageId); - String date = lastMessage.getTimeCreated().format(formatter); - charCtl += textChannel.getName().length() + date.length(); - result.put(textChannel.getName(), date); - } - } catch (RuntimeException | ParseException e) { - logger.warn("Can't find message with id: " + lastMessageId); - result.put(textChannel.getName(), "ERROR"); - charCtl += textChannel.getName().length() + 5; - } - - } else { - result.put(textChannel.getName(), "No message or access denied."); - charCtl += textChannel.getName().length() + 30; - } - if (charCtl > 3000) { - event.getTextChannel().sendMessage(EmbedMessageUtils.getLastMessageFromTextChannel(result)).complete(); - result = new HashMap<>(); - charCtl = 0; - } - } - if (charCtl != 0) - event.getTextChannel().sendMessage(EmbedMessageUtils.getLastMessageFromTextChannel(result)).queue(); - } - - private void SendBack(MessageReceivedEvent event) { - HashMap result = new HashMap<>(); - DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy").withLocale(Locale.ENGLISH); - int charCtl = 0; - for (TextChannel textChannel : event.getGuild().getTextChannels()) { - if (textChannel.hasLatestMessage()) { - String lastMessageId = textChannel.getLatestMessageId(); - logger.debug("Last message in channel " + textChannel + " is " + lastMessageId); - - try { - Message lastMessage = textChannel.retrieveMessageById(lastMessageId).complete(); - String date = lastMessage.getTimeCreated().format(formatter); - charCtl += textChannel.getName().length() + date.length(); - result.put(textChannel.getName(), date); - } catch (RuntimeException e) { - logger.warn("Can't find message with id: " + lastMessageId); - result.put(textChannel.getName(), "ERROR"); - charCtl += textChannel.getName().length() + 5; - } - - } else { - result.put(textChannel.getName(), "No message or access denied."); - charCtl += textChannel.getName().length() + 30; - } - if (charCtl > 3000) { - event.getTextChannel().sendMessage(EmbedMessageUtils.getLastMessageFromTextChannel(result)).queue(); - event.getTextChannel().sendTyping().queue(); - result = new HashMap<>(); - charCtl = 0; - } - } - if (charCtl != 0) - event.getTextChannel().sendMessage(EmbedMessageUtils.getLastMessageFromTextChannel(result)).queue(); - } - - @Override - public boolean isPrivateUsable() { - return false; - } - - @Override - public boolean isAdminCmd() { - return true; - } - - /** - * Determines if the command is usable only by bot level admin user - * - * @return boolean - */ - @Override - public boolean isBotAdminCmd() { - return false; - } - - @Override - public boolean isNSFW() { - return false; - } -} diff --git a/src/main/java/net/Broken/Commands/Ordre66.java b/src/main/java/net/Broken/Commands/Ordre66.java deleted file mode 100644 index 25cdd73..0000000 --- a/src/main/java/net/Broken/Commands/Ordre66.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.Broken.Commands; - -import net.Broken.Commande; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.events.message.MessageReceivedEvent; - -public class Ordre66 implements Commande { - - @Override - public void action(String[] args, MessageReceivedEvent event) { - Message rest = event.getTextChannel().sendMessage("Très bien maître " + event.getAuthor().getAsMention() + ". J'arrive ! ").complete(); - Message reste = event.getTextChannel().sendMessage("https://media2.giphy.com/media/UfzTayIyH7g5hk2BA2/giphy.gif\n").complete(); - } - - @Override - public boolean isPrivateUsable() { - return true; - } - - @Override - public boolean isAdminCmd() { - return true; - } - - /** - * Determines if the command is usable only by bot level admin user - * - * @return boolean - */ - @Override - public boolean isBotAdminCmd() { - return false; - } - - @Override - public boolean isNSFW() { - return false; - } -} diff --git a/src/main/java/net/Broken/Commands/ReportUsers.java b/src/main/java/net/Broken/Commands/ReportUsers.java deleted file mode 100644 index ccb9f09..0000000 --- a/src/main/java/net/Broken/Commands/ReportUsers.java +++ /dev/null @@ -1,69 +0,0 @@ -package net.Broken.Commands; - -import net.Broken.Commande; -import net.Broken.Tools.EmbedMessageUtils; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.events.message.MessageReceivedEvent; -import net.dv8tion.jda.api.requests.RestAction; - -import java.util.Objects; - - -public class ReportUsers implements Commande { - - @Override - public void action(String[] args, MessageReceivedEvent event) { - - event.getGuild().loadMembers().onSuccess(members -> { - if (event.getMessage().getMentionedRoles().size() == 1 && args.length == 1) { - RestAction restAction = null; - for (Member member : members) { - if (member.getRoles().size() == 1) { //check if the member has a role - if (member.getRoles().contains(event.getMessage().getMentionedRoles().get(0))) { //check if the mentioned role is the same as the member's role - restAction = Objects.requireNonNullElseGet(restAction, () -> event.getTextChannel().sendMessage("List des membres : ")) - .and(event.getTextChannel().sendMessage(member.getEffectiveName())); - } - } - } - if (restAction != null) - restAction.queue(); - } else if (args.length == 0) { - - for (Member member : members) { - if (member.getRoles().size() == 0) { - event.getTextChannel().sendMessage(member.getEffectiveName()).complete(); - } - } - } else { - event.getTextChannel().sendMessage(EmbedMessageUtils.getReportUsersError()).complete(); - } - }); - } - - @Override - public boolean isPrivateUsable() { - return false; - } - - @Override - public boolean isAdminCmd() { - return true; - } - - /** - * Determines if the command is usable only by bot level admin user - * - * @return boolean - */ - @Override - public boolean isBotAdminCmd() { - return false; - } - - @Override - public boolean isNSFW() { - return false; - } - - -} diff --git a/src/main/java/net/Broken/Init.java b/src/main/java/net/Broken/Init.java index c191383..fa6572c 100644 --- a/src/main/java/net/Broken/Init.java +++ b/src/main/java/net/Broken/Init.java @@ -1,13 +1,10 @@ package net.Broken; import net.Broken.DB.Entity.GuildPreferenceEntity; -import net.Broken.DB.Entity.UserEntity; import net.Broken.DB.Repository.GuildPreferenceRepository; -import net.Broken.DB.Repository.UserRepository; import net.Broken.Tools.Command.SlashCommandLoader; import net.Broken.Tools.DayListener.DayListener; import net.Broken.Tools.DayListener.Listeners.DailyMadame; -import net.Broken.Tools.UserManager.Stats.UserStatsUtils; import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDABuilder; import net.dv8tion.jda.api.OnlineStatus; @@ -19,14 +16,13 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.context.ApplicationContext; -import javax.security.auth.login.LoginException; -import java.util.List; - public class Init { static private final Logger logger = LogManager.getLogger(); + private Init() {} + static JDA initJda(BotConfigLoader config) { logger.info("-----------------------INIT-----------------------"); if (config == null) { @@ -47,12 +43,12 @@ public class Init { .setAutoReconnect(true); jda.getPresence().setPresence(OnlineStatus.DO_NOT_DISTURB, Activity.playing("Loading...")); - logger.info("Connected on " + jda.getGuilds().size() + " Guilds:"); + logger.info("Connected on {} Guilds:", jda.getGuilds().size()); for (Guild server : jda.getGuilds()) { server.loadMembers(); } return jda; - } catch (LoginException | InterruptedException e) { + } catch (InterruptedException e) { logger.catching(e); return null; } @@ -78,17 +74,8 @@ public class Init { private static void checkDatabase() { ApplicationContext context = SpringContext.getAppContext(); - UserRepository userRepository = (UserRepository) context.getBean("userRepository"); - List users = (List) userRepository.findAll(); - UserStatsUtils userStatsUtils = UserStatsUtils.getINSTANCE(); logger.debug("Stats..."); -// for (UserEntity userEntity : users) { -// logger.debug("..." + userEntity.getName()); -// userStatsUtils.getUserStats(userEntity); -// -// } - logger.debug("Guild Prefs..."); GuildPreferenceRepository guildPreference = context.getBean(GuildPreferenceRepository.class); for(GuildPreferenceEntity pref :guildPreference.findAll()){ diff --git a/src/main/java/net/Broken/SlashCommand.java b/src/main/java/net/Broken/SlashCommand.java index ed24759..69bdcda 100644 --- a/src/main/java/net/Broken/SlashCommand.java +++ b/src/main/java/net/Broken/SlashCommand.java @@ -1,6 +1,7 @@ package net.Broken; -import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.build.OptionData; import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; @@ -10,7 +11,7 @@ import java.util.List; * Interface that define command structure. */ public interface SlashCommand { - void action(SlashCommandEvent event); + void action(SlashCommandInteractionEvent event); String getDescription(); @@ -34,7 +35,5 @@ public interface SlashCommand { boolean isPrivateUsable(); - - boolean isDisableByDefault(); - + DefaultMemberPermissions getDefaultPermissions(); } diff --git a/src/main/java/net/Broken/SlashCommands/Cat.java b/src/main/java/net/Broken/SlashCommands/Cat.java index eae1581..ae2a77f 100644 --- a/src/main/java/net/Broken/SlashCommands/Cat.java +++ b/src/main/java/net/Broken/SlashCommands/Cat.java @@ -1,11 +1,11 @@ package net.Broken.SlashCommands; - import java.io.IOException; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -17,10 +17,12 @@ import com.fasterxml.jackson.databind.ObjectMapper; import net.Broken.SlashCommand; import net.Broken.Tools.EmbedMessageUtils; -import net.dv8tion.jda.api.MessageBuilder; -import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.build.OptionData; import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; /** * Command that return a random picture of cat. @@ -29,32 +31,33 @@ public class Cat implements SlashCommand { private final Logger logger = LogManager.getLogger(); @Override - public void action(SlashCommandEvent event) { + public void action(SlashCommandInteractionEvent event) { try { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://aws.random.cat/meow")) .GET() .build(); - HttpClient client = HttpClient.newHttpClient(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() != 200) { - logger.warn("[CAT] Fail to fetch cat: Status Code: " + response.statusCode() + " Body:" + response.body()); + logger.warn("[CAT] Fail to fetch cat: Status Code: {} Body: {}", response.statusCode(), + response.body()); throw new IOException(); } - TypeReference> typeRef = new TypeReference<>() {}; + TypeReference> typeRef = new TypeReference<>() { + }; ObjectMapper mapper = new ObjectMapper(); HashMap json = mapper.readValue(response.body(), typeRef); event.reply(json.get("file")).queue(); } catch (InterruptedException | IOException e) { logger.catching(e); - event.reply(new MessageBuilder().setEmbeds(EmbedMessageUtils.getInternalError()).build()).setEphemeral(true).queue(); + event.reply(new MessageCreateBuilder().setEmbeds(EmbedMessageUtils.getInternalError()).build()).setEphemeral(true) + .queue(); } - } @Override @@ -64,15 +67,14 @@ public class Cat implements SlashCommand { @Override public List getOptions() { - return null; + return Collections.emptyList(); } @Override public List getSubcommands() { - return null; + return Collections.emptyList(); } - /** * Determines if the command is usable only by bot level admin user * @@ -94,7 +96,8 @@ public class Cat implements SlashCommand { } @Override - public boolean isDisableByDefault() { - return false; - } + public DefaultMemberPermissions getDefaultPermissions(){ + return DefaultMemberPermissions.enabledFor(Permission.MESSAGE_SEND); + } + } diff --git a/src/main/java/net/Broken/SlashCommands/Clear.java b/src/main/java/net/Broken/SlashCommands/Clear.java index bc2640d..29d4a0d 100644 --- a/src/main/java/net/Broken/SlashCommands/Clear.java +++ b/src/main/java/net/Broken/SlashCommands/Clear.java @@ -1,29 +1,32 @@ package net.Broken.SlashCommands; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + import net.Broken.SlashCommand; import net.Broken.Tools.EmbedMessageUtils; -import net.dv8tion.jda.api.MessageBuilder; import net.dv8tion.jda.api.Permission; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.MessageChannel; -import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; +import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.OptionData; import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; -import java.util.ArrayList; -import java.util.List; public class Clear implements SlashCommand { @Override - public void action(SlashCommandEvent event) { + public void action(SlashCommandInteractionEvent event) { if (event.getMember().hasPermission(Permission.MESSAGE_MANAGE)) { event.reply(":white_check_mark: Done").setEphemeral(true).queue(); long n = event.getOption("n").getAsLong(); MessageChannel chanel = event.getChannel(); chanel.getIterableHistory().takeAsync((int) n).thenAccept(chanel::purgeMessages); } else { - Message message = new MessageBuilder().setEmbeds(EmbedMessageUtils.getFlushError("You are not a supreme being, you cannot do that !")).build(); + MessageCreateData message = new MessageCreateBuilder().setEmbeds(EmbedMessageUtils.getFlushError("You are not a supreme being, you cannot do that !")).build(); event.reply(message).setEphemeral(true).queue(); } } @@ -42,7 +45,7 @@ public class Clear implements SlashCommand { @Override public List getSubcommands() { - return null; + return Collections.emptyList(); } @Override @@ -61,7 +64,9 @@ public class Clear implements SlashCommand { } @Override - public boolean isDisableByDefault() { - return true; + public DefaultMemberPermissions getDefaultPermissions() { + return DefaultMemberPermissions.enabledFor(Permission.MESSAGE_MANAGE); } + + } diff --git a/src/main/java/net/Broken/SlashCommands/Music.java b/src/main/java/net/Broken/SlashCommands/Music.java index 30aa6a6..b1288e2 100644 --- a/src/main/java/net/Broken/SlashCommands/Music.java +++ b/src/main/java/net/Broken/SlashCommands/Music.java @@ -1,48 +1,52 @@ package net.Broken.SlashCommands; -import net.Broken.Audio.GuildAudioBotService; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import net.Broken.SlashCommand; +import net.Broken.Audio.GuildAudioBotService; import net.Broken.Tools.EmbedMessageUtils; -import net.dv8tion.jda.api.MessageBuilder; -import net.dv8tion.jda.api.entities.Message; -import net.dv8tion.jda.api.entities.VoiceChannel; -import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; +import net.dv8tion.jda.api.entities.channel.unions.AudioChannelUnion; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.OptionMapping; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.OptionData; import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.util.ArrayList; -import java.util.List; +import net.dv8tion.jda.api.utils.messages.MessageCreateBuilder; +import net.dv8tion.jda.api.utils.messages.MessageCreateData; /** * Command that return a random picture of cat. */ public class Music implements SlashCommand { + private static final String PLAYLIST_LIMIT = "playlist-limit"; private final Logger logger = LogManager.getLogger(); @Override - public void action(SlashCommandEvent event) { + public void action(SlashCommandInteractionEvent event) { GuildAudioBotService audio = GuildAudioBotService.getInstance(event.getGuild()); String action = event.getSubcommandName(); event.deferReply().queue(); switch (action) { case "play": - if (event.getMember().getVoiceState().inVoiceChannel()) { - VoiceChannel voiceChanel = event.getMember().getVoiceState().getChannel(); - logger.info("Connecting to " + voiceChanel.getName() + "..."); + if (event.getMember().getVoiceState().inAudioChannel()) { + AudioChannelUnion voiceChanel = event.getMember().getVoiceState().getChannel(); + logger.info("Connecting to {}...", voiceChanel.getName()); OptionMapping url = event.getOption("url"); - if (event.getOption("playlist-limit") == null) { + if (event.getOption(PLAYLIST_LIMIT) == null) { audio.loadAndPlay(event, voiceChanel, url.getAsString(), 30, false); } else { - long limit = event.getOption("playlist-limit").getAsLong(); + long limit = event.getOption(PLAYLIST_LIMIT).getAsLong(); audio.loadAndPlay(event, voiceChanel, url.getAsString(), (int) limit, false); } } else { - Message message = new MessageBuilder().setEmbeds(EmbedMessageUtils.getMusicError("You are not in a voice channel !")).build(); + MessageCreateData message = new MessageCreateBuilder().setEmbeds(EmbedMessageUtils.getMusicError("You are not in a voice channel !")).build(); event.getHook().setEphemeral(true).sendMessage(message).queue(); } break; @@ -52,34 +56,34 @@ public class Music implements SlashCommand { if (event.getOption("next") != null) { next = event.getOption("next").getAsBoolean(); } - if (event.getOption("playlist-limit") == null) { + if (event.getOption(PLAYLIST_LIMIT) == null) { audio.add(event, url.getAsString(), 30, next); } else { - long limit = event.getOption("playlist-limit").getAsLong(); + long limit = event.getOption(PLAYLIST_LIMIT).getAsLong(); audio.add(event, url.getAsString(), (int) limit, next); } break; case "pause": - audio.pause(event); + audio.pause(event.getHook()); break; case "resume": - audio.resume(event); + audio.resume(event.getHook()); break; case "next": - audio.skipTrack(event); + audio.skipTrack(event.getHook()); break; case "stop": case "disconnect": - audio.stop(event); + audio.stop(event.getHook()); break; case "info": - audio.info(event); + audio.info(event.getHook()); break; case "flush": - audio.flush(event); + audio.flush(event.getHook()); break; case "list": - audio.list(event); + audio.list(event.getHook()); break; } } @@ -91,7 +95,7 @@ public class Music implements SlashCommand { @Override public List getOptions() { - return null; + return Collections.emptyList(); } @Override @@ -99,11 +103,11 @@ public class Music implements SlashCommand { ArrayList subCommandList = new ArrayList<>(); subCommandList.add(new SubcommandData("play", "Play music") .addOption(OptionType.STRING, "url", "The URL of the video to play", true) - .addOption(OptionType.INTEGER, "playlist-limit", "If a playlist is loaded, enter the max number of loaded tracks (default to 30)")); + .addOption(OptionType.INTEGER, PLAYLIST_LIMIT, "If a playlist is loaded, enter the max number of loaded tracks (default to 30)")); subCommandList.add(new SubcommandData("add", "Add track to queue") .addOption(OptionType.STRING, "url", " The URL of the video to play", true) .addOption(OptionType.BOOLEAN, "next", "If true, track will be added on top of the playlist and will be the next to play") - .addOption(OptionType.INTEGER, "playlist-limit", "If a playlist is loaded, enter the max number of loaded tracks (default to 30)")); + .addOption(OptionType.INTEGER, PLAYLIST_LIMIT, "If a playlist is loaded, enter the max number of loaded tracks (default to 30)")); subCommandList.add(new SubcommandData("pause", "Pause playback")); subCommandList.add(new SubcommandData("resume", "Resume playback")); subCommandList.add(new SubcommandData("stop", "Stop playback")); @@ -136,7 +140,9 @@ public class Music implements SlashCommand { } @Override - public boolean isDisableByDefault() { - return false; + public DefaultMemberPermissions getDefaultPermissions() { + return DefaultMemberPermissions.ENABLED; } + + } diff --git a/src/main/java/net/Broken/SlashCommands/Over18/Ass.java b/src/main/java/net/Broken/SlashCommands/Over18/Ass.java index af6bd77..5bbcecb 100644 --- a/src/main/java/net/Broken/SlashCommands/Over18/Ass.java +++ b/src/main/java/net/Broken/SlashCommands/Over18/Ass.java @@ -3,6 +3,9 @@ package net.Broken.SlashCommands.Over18; import net.Broken.Tools.Command.Ignore; import net.Broken.Tools.Command.NoDev; import net.Broken.Tools.Command.NumberedSlashCommand; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; + import org.apache.logging.log4j.LogManager; @NoDev @@ -35,7 +38,7 @@ public class Ass extends NumberedSlashCommand { } @Override - public boolean isDisableByDefault() { - return true; + public DefaultMemberPermissions getDefaultPermissions() { + return DefaultMemberPermissions.enabledFor(Permission.MESSAGE_SEND); } } diff --git a/src/main/java/net/Broken/SlashCommands/Over18/Boobs.java b/src/main/java/net/Broken/SlashCommands/Over18/Boobs.java index bc44508..ad161d1 100644 --- a/src/main/java/net/Broken/SlashCommands/Over18/Boobs.java +++ b/src/main/java/net/Broken/SlashCommands/Over18/Boobs.java @@ -3,6 +3,10 @@ package net.Broken.SlashCommands.Over18; import net.Broken.Tools.Command.Ignore; import net.Broken.Tools.Command.NoDev; import net.Broken.Tools.Command.NumberedSlashCommand; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; + + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -37,7 +41,7 @@ public class Boobs extends NumberedSlashCommand { } @Override - public boolean isDisableByDefault() { - return true; + public DefaultMemberPermissions getDefaultPermissions() { + return DefaultMemberPermissions.enabledFor(Permission.MESSAGE_SEND); } } diff --git a/src/main/java/net/Broken/SlashCommands/Over18/Madame.java b/src/main/java/net/Broken/SlashCommands/Over18/Madame.java index 377eb68..d847242 100644 --- a/src/main/java/net/Broken/SlashCommands/Over18/Madame.java +++ b/src/main/java/net/Broken/SlashCommands/Over18/Madame.java @@ -2,6 +2,8 @@ package net.Broken.SlashCommands.Over18; import net.Broken.Tools.Command.NoDev; import net.Broken.Tools.Command.NumberedSlashCommand; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.Broken.Tools.FindContentOnWebPage; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -57,11 +59,11 @@ public class Madame extends NumberedSlashCommand { int randomResult = randomQueue.poll(); String url = baseURL + randomResult + urlSuffix; - logger.debug("URL: " + url); + logger.debug("URL: {}",url); if (scanPageForTipeee(url, logger)) { - logger.debug("Advertisement detected! Retry! (" + url + ")"); + logger.debug("Advertisement detected! Retry! ({})", url); } else { imgUrl = FindContentOnWebPage.doYourJob(url, "post-content", "img"); @@ -94,7 +96,7 @@ public class Madame extends NumberedSlashCommand { } @Override - public boolean isDisableByDefault() { - return true; + public DefaultMemberPermissions getDefaultPermissions() { + return DefaultMemberPermissions.enabledFor(Permission.MESSAGE_SEND); } } diff --git a/src/main/java/net/Broken/SlashCommands/Rank.java b/src/main/java/net/Broken/SlashCommands/Rank.java index 603d900..a895b7c 100644 --- a/src/main/java/net/Broken/SlashCommands/Rank.java +++ b/src/main/java/net/Broken/SlashCommands/Rank.java @@ -5,16 +5,18 @@ import net.Broken.SlashCommand; import net.Broken.SpringContext; import net.Broken.Tools.UserManager.Stats.UserStatsUtils; import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.commands.DefaultMemberPermissions; import net.dv8tion.jda.api.interactions.commands.build.OptionData; import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; -import net.dv8tion.jda.api.interactions.components.Button; +import net.dv8tion.jda.api.interactions.components.buttons.Button; +import java.util.Collections; import java.util.List; public class Rank implements SlashCommand { @Override - public void action(SlashCommandEvent event) { + public void action(SlashCommandInteractionEvent event) { String url = SpringContext.getAppContext().getBean(BotConfigLoader.class).url(); event.deferReply().queue(); UserStatsUtils userStats = UserStatsUtils.getINSTANCE(); @@ -31,12 +33,12 @@ public class Rank implements SlashCommand { @Override public List getOptions() { - return null; + return Collections.emptyList(); } @Override public List getSubcommands() { - return null; + return Collections.emptyList(); } @Override @@ -55,7 +57,7 @@ public class Rank implements SlashCommand { } @Override - public boolean isDisableByDefault() { - return false; + public DefaultMemberPermissions getDefaultPermissions() { + return DefaultMemberPermissions.ENABLED; } } diff --git a/src/main/java/net/Broken/Tools/AutoVoiceChannel.java b/src/main/java/net/Broken/Tools/AutoVoiceChannel.java index cfb62e4..1b1f28a 100644 --- a/src/main/java/net/Broken/Tools/AutoVoiceChannel.java +++ b/src/main/java/net/Broken/Tools/AutoVoiceChannel.java @@ -1,20 +1,21 @@ package net.Broken.Tools; -import net.Broken.DB.Entity.GuildPreferenceEntity; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.VoiceChannel; -import net.dv8tion.jda.api.requests.RestAction; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import static net.Broken.MainBot.jda; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; -import static net.Broken.MainBot.jda; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import net.Broken.DB.Entity.GuildPreferenceEntity; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.channel.middleman.AudioChannel; +import net.dv8tion.jda.api.requests.RestAction; public class AutoVoiceChannel { private static final HashMap INSTANCE_MAP = new HashMap<>(); @@ -34,14 +35,14 @@ public class AutoVoiceChannel { return INSTANCE_MAP.get(guild.getId()); } - public void join(VoiceChannel voiceChannel) { + public void join(AudioChannel voiceChannel) { Guild guild = jda.getGuildById(guildID); if (guild == null) return; GuildPreferenceEntity pref = SettingsUtils.getInstance().getPreference(guild); if (pref.isAutoVoice() && voiceChannel.getId().equals(pref.getAutoVoiceChannelID())) { - logger.info("Creating new voice channel for Guild : " + guild.getName()); - VoiceChannel newChannel = voiceChannel.createCopy().complete(); + logger.info("Creating new voice channel for Guild : {}", guild.getName()); + AudioChannel newChannel = (AudioChannel) voiceChannel.createCopy().complete(); int next = getNextNumber(); String title = pref.getAutoVoiceChannelTitle(); if (title.isEmpty()) { @@ -56,7 +57,7 @@ public class AutoVoiceChannel { } - public void leave(VoiceChannel voiceChannel) { + public void leave(AudioChannel voiceChannel) { if (voiceChannel.getMembers().isEmpty()) { String id = voiceChannel.getId(); for (Map.Entry entry : createdChannels.entrySet()) { @@ -79,7 +80,7 @@ public class AutoVoiceChannel { return 999; } - private void moveMembers(List members, VoiceChannel destination) { + private void moveMembers(List members, AudioChannel destination) { logger.debug("Moving Members to new voice channel..."); RestAction restAction = null; for (Member member : members) { diff --git a/src/main/java/net/Broken/Tools/Command/CommandParser.java b/src/main/java/net/Broken/Tools/Command/CommandParser.java index 58c4e77..fb40297 100644 --- a/src/main/java/net/Broken/Tools/Command/CommandParser.java +++ b/src/main/java/net/Broken/Tools/Command/CommandParser.java @@ -39,7 +39,7 @@ public class CommandParser { args[i] = args[i].replace('$', ' '); - logger.info("Author: " + e.getAuthor().getName() + ", Command: " + commande + ", args: " + Arrays.toString(args)); + logger.info("Author: {}, Command: {}, args: {}", e.getAuthor().getName(), commande, Arrays.toString(args)); return new CommandContainer(brt, sansTete, splitSansTete, commande, args, e); diff --git a/src/main/java/net/Broken/Tools/Command/NumberedSlashCommand.java b/src/main/java/net/Broken/Tools/Command/NumberedSlashCommand.java index 453df13..b01683e 100644 --- a/src/main/java/net/Broken/Tools/Command/NumberedSlashCommand.java +++ b/src/main/java/net/Broken/Tools/Command/NumberedSlashCommand.java @@ -1,6 +1,7 @@ package net.Broken.Tools.Command; import java.io.IOException; +import java.util.Collections; import java.util.List; import java.util.concurrent.LinkedBlockingQueue; @@ -13,7 +14,7 @@ import net.Broken.Tools.FindContentOnWebPage; import net.Broken.Tools.LimitChecker; import net.Broken.Tools.Random.TrueRandom; import net.dv8tion.jda.api.entities.MessageEmbed; -import net.dv8tion.jda.api.events.interaction.SlashCommandEvent; +import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; import net.dv8tion.jda.api.interactions.commands.build.OptionData; import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; @@ -39,7 +40,7 @@ public abstract class NumberedSlashCommand implements SlashCommand { * @param divClass DivClass to search to extract image * @param htmlType HTML tag to extract image (img) */ - public NumberedSlashCommand(Logger logger, String baseURL, String urlSuffix, String divClass, String htmlType) { + protected NumberedSlashCommand(Logger logger, String baseURL, String urlSuffix, String divClass, String htmlType) { this.logger = logger; this.baseURL = baseURL; this.divClass = divClass; @@ -48,19 +49,19 @@ public abstract class NumberedSlashCommand implements SlashCommand { try { logger.debug("Checking max..."); maxNumber = LimitChecker.doYourJob(baseURL, 2, urlSuffix); - logger.info("Limit is " + maxNumber); + logger.info("Limit is {}",maxNumber); } catch (IOException e) { logger.catching(e); } } - public NumberedSlashCommand(Logger logger, String baseURL, String urlSuffix) { + protected NumberedSlashCommand(Logger logger, String baseURL, String urlSuffix) { this(logger, baseURL, urlSuffix, null, null); } @Override - public void action(SlashCommandEvent event) { + public void action(SlashCommandInteractionEvent event) { event.deferReply().queue(); try { String result = poll(); @@ -82,7 +83,7 @@ public abstract class NumberedSlashCommand implements SlashCommand { } protected void checkRandom() throws IOException { - logger.trace("Queue size: " + randomQueue.size()); + logger.trace("Queue size: {}", randomQueue.size()); if (randomQueue.isEmpty()) { logger.debug("Queue empty, update it."); fillRandomQueue(); @@ -97,12 +98,12 @@ public abstract class NumberedSlashCommand implements SlashCommand { @Override public List getOptions() { - return null; + return Collections.emptyList(); } @Override public List getSubcommands() { - return null; + return Collections.emptyList(); } } diff --git a/src/main/java/net/Broken/Tools/Command/SlashCommandLoader.java b/src/main/java/net/Broken/Tools/Command/SlashCommandLoader.java index 8c8c3f8..70a891c 100644 --- a/src/main/java/net/Broken/Tools/Command/SlashCommandLoader.java +++ b/src/main/java/net/Broken/Tools/Command/SlashCommandLoader.java @@ -1,18 +1,20 @@ package net.Broken.Tools.Command; -import net.Broken.BotConfigLoader; -import net.Broken.MainBot; -import net.Broken.SlashCommand; -import net.dv8tion.jda.api.interactions.commands.build.CommandData; -import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction; +import java.lang.reflect.InvocationTargetException; +import java.util.Set; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.reflections.Reflections; import org.reflections.util.ClasspathHelper; import org.reflections.util.ConfigurationBuilder; -import java.lang.reflect.InvocationTargetException; -import java.util.Set; +import net.Broken.BotConfigLoader; +import net.Broken.MainBot; +import net.Broken.SlashCommand; +import net.dv8tion.jda.api.interactions.commands.build.Commands; +import net.dv8tion.jda.api.interactions.commands.build.SlashCommandData; +import net.dv8tion.jda.api.requests.restaction.CommandListUpdateAction; /** @@ -21,6 +23,8 @@ import java.util.Set; public class SlashCommandLoader { private static final Logger logger = LogManager.getLogger(); + private SlashCommandLoader() {} + /** * Search all implemented Command interface class and add it to MainBot.commands HashMap */ @@ -33,14 +37,14 @@ public class SlashCommandLoader { ); Set> modules = reflections.getSubTypesOf(SlashCommand.class); - logger.info("Find " + modules.size() + " Command:"); + logger.info("Find {} Command:", modules.size()); for (Class command : modules) { String reference = command.getName(); String[] splited = reference.split("\\."); String name = splited[splited.length - 1].toLowerCase(); if (!command.isAnnotationPresent(Ignore.class)) { - logger.info("..." + name); + logger.info("...{}", name); if (command.isAnnotationPresent(NoDev.class) && config.mode().equals("DEV")) { logger.warn("Command disabled in dev mode"); @@ -49,24 +53,25 @@ public class SlashCommandLoader { MainBot.slashCommands.put(name, command.getDeclaredConstructor().newInstance()); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - logger.error("Failed to load " + name + "!"); + logger.error("Failed to load {} !", name); } } } else { - logger.trace("Ignored command: " + name); + logger.trace("Ignored command: {}", name); } } } public static void registerSlashCommands(CommandListUpdateAction commandListUpdateAction) { MainBot.slashCommands.forEach((k, v) -> { - CommandData command = new CommandData(k, v.getDescription()); + SlashCommandData command = Commands.slash(k, v.getDescription()); if (v.getOptions() != null) command.addOptions(v.getOptions()); if (v.getSubcommands() != null) { command.addSubcommands(v.getSubcommands()); } - command.setDefaultEnabled(!v.isDisableByDefault()); + command.setDefaultPermissions(v.getDefaultPermissions()); + commandListUpdateAction.addCommands(command); }); commandListUpdateAction.queue(); diff --git a/src/main/java/net/Broken/Tools/DayListener/Listeners/DailyMadame.java b/src/main/java/net/Broken/Tools/DayListener/Listeners/DailyMadame.java index 800cdde..06295f6 100644 --- a/src/main/java/net/Broken/Tools/DayListener/Listeners/DailyMadame.java +++ b/src/main/java/net/Broken/Tools/DayListener/Listeners/DailyMadame.java @@ -8,7 +8,8 @@ import net.Broken.SpringContext; import net.Broken.Tools.DayListener.NewDayListener; import net.Broken.Tools.FindContentOnWebPage; import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.TextChannel; +import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.context.ApplicationContext; @@ -44,12 +45,8 @@ public class DailyMadame implements NewDayListener { if (day != Calendar.MONDAY && day != Calendar.SUNDAY) { LocalDate now = LocalDate.now().minusDays(1); String date = DateTimeFormatter.ofPattern("yyyy/MM/dd").format(now); - String url = "https://www.bonjourmadame.fr/" + date + "/"; - imgUrl = FindContentOnWebPage.doYourJob(url, "post-content", "img"); - - } else { Madame command = (Madame) MainBot.slashCommands.get("madame"); imgUrl = command.poll(); @@ -64,17 +61,14 @@ public class DailyMadame implements NewDayListener { for (TextChannel iterator : guild.getTextChannels()) { if (iterator.isNSFW() && iterator.canTalk()) { chanel = iterator; - logger.debug("break: " + chanel.getName()); + logger.debug("break: {}", chanel.getName()); break; } } if (chanel != null) { - chanel.sendMessage("Madame of the day :kissing_heart: \n" + imgUrl).queue(); - - } else { - logger.info("No NSFW chanel found for " + guild.getName() + ", ignoring it!"); + logger.info("No NSFW chanel found for {}, ignoring it!", guild.getName()); } } } diff --git a/src/main/java/net/Broken/Tools/SettingsUtils.java b/src/main/java/net/Broken/Tools/SettingsUtils.java index 374db05..a29b3f7 100644 --- a/src/main/java/net/Broken/Tools/SettingsUtils.java +++ b/src/main/java/net/Broken/Tools/SettingsUtils.java @@ -2,7 +2,6 @@ package net.Broken.Tools; import net.Broken.DB.Entity.GuildPreferenceEntity; import net.Broken.DB.Repository.GuildPreferenceRepository; -import net.Broken.DB.Repository.UserRepository; import net.Broken.SpringContext; import net.dv8tion.jda.api.entities.Guild; import org.apache.logging.log4j.LogManager; @@ -14,13 +13,10 @@ public class SettingsUtils { private static SettingsUtils INSTANCE; private final Logger logger = LogManager.getLogger(); private final GuildPreferenceRepository guildPreferenceRepository; - private final UserRepository userRepository; + private SettingsUtils() { ApplicationContext context = SpringContext.getAppContext(); guildPreferenceRepository = (GuildPreferenceRepository) context.getBean("guildPreferenceRepository"); - userRepository = (UserRepository) context.getBean("userRepository"); - - } public static SettingsUtils getInstance() { @@ -29,7 +25,7 @@ public class SettingsUtils { public GuildPreferenceEntity getPreference(Guild guild) { return guildPreferenceRepository.findByGuildId(guild.getId()).orElseGet(()->{ - logger.info("Generate default pref for " + guild.getName()); + logger.info("Generate default pref for {}", guild.getName()); return guildPreferenceRepository.save(GuildPreferenceEntity.getDefault(guild.getId())); }); } diff --git a/src/main/java/net/Broken/Tools/UserManager/Stats/UserStatsUtils.java b/src/main/java/net/Broken/Tools/UserManager/Stats/UserStatsUtils.java index 43ad202..41122fa 100644 --- a/src/main/java/net/Broken/Tools/UserManager/Stats/UserStatsUtils.java +++ b/src/main/java/net/Broken/Tools/UserManager/Stats/UserStatsUtils.java @@ -60,16 +60,16 @@ public class UserStatsUtils { logger.debug(userEntity); logger.debug(userEntity.getUserStats()); - if (userEntity.getUserStats() == null || userEntity.getUserStats().size() == 0 || userEntity.getUserStats().size() < jdaUser.getMutualGuilds().size()) { - logger.debug("Stats not found for " + userEntity.getUsername()); + if (userEntity.getUserStats() == null || userEntity.getUserStats().isEmpty() || userEntity.getUserStats().size() < jdaUser.getMutualGuilds().size()) { + logger.debug("Stats not found for {}", userEntity.getUsername()); List stats; - if (userEntity.getUserStats() == null || userEntity.getUserStats().size() == 0) { - logger.debug("No stats found for user " + jdaUser.getName() + ", use blank."); + if (userEntity.getUserStats() == null || userEntity.getUserStats().isEmpty()) { + logger.debug("No stats found for user {}, use blank.", jdaUser.getName()); logger.debug("Creating stats for guilds: "); stats = new ArrayList<>(); for (Guild guid : jdaUser.getMutualGuilds()) { - logger.debug("..." + guid.getName()); + logger.debug("...{}", guid.getName()); stats.add(new UserStats(guid.getId(), userEntity)); } } else { @@ -81,7 +81,7 @@ public class UserStatsUtils { for (Guild guid : jdaUser.getMutualGuilds()) { logger.debug(guid.getName()); if (!guildStat.contains(guid.getId())) { - logger.debug("Guild " + guid.getName() + " stats don't exist"); + logger.debug("Guild {} stats don't exist", guid.getName()); stats.add(new UserStats(guid.getId(), userEntity)); } } @@ -108,8 +108,8 @@ public class UserStatsUtils { .orElseGet(() -> genUserEntity(member.getUser())); List userStatsList = userStatsRepository.findByUserAndGuildId(userEntity, member.getGuild().getId()); - if (userStatsList.size() == 0) { - logger.debug("User stats not found for user " + userEntity.getUsername() + " guild: " + member.getGuild().getId()); + if (userStatsList.isEmpty()) { + logger.debug("User stats not found for user {} guild: {}", userEntity.getUsername(), member.getGuild().getId()); getUserStats(userEntity); userStatsList = userStatsRepository.findByUserAndGuildId(userEntity, member.getGuild().getId()); } @@ -119,8 +119,8 @@ public class UserStatsUtils { public UserStats getGuildUserStats(UserEntity userEntity, String guildId) { List userStatsList = userStatsRepository.findByUserAndGuildId(userEntity, guildId); - if (userStatsRepository.findByUserAndGuildId(userEntity, guildId).size() == 0) { - logger.debug("User stats not found for user " + userEntity.getUsername() + " guild: " + guildId); + if (userStatsRepository.findByUserAndGuildId(userEntity, guildId).isEmpty()) { + logger.debug("User stats not found for user {} guild: {}", userEntity.getUsername(), guildId); getUserStats(userEntity); userStatsList = userStatsRepository.findByUserAndGuildId(userEntity, guildId); } @@ -163,7 +163,6 @@ public class UserStatsUtils { public GuildStatsPack getStatPack(UserEntity userEntity, String guildId) { - UserStats userStats = getGuildUserStats(userEntity, guildId); GuildStats selfGuildStats = null; List allStats = userStatsRepository.findByGuildId(guildId); @@ -183,13 +182,13 @@ public class UserStatsUtils { } ranked.add(temp); } - if (needCache.size() != 0) { + if (!needCache.isEmpty()) { logger.info("Cache mismatch, loading all guild"); MainBot.jda.getGuildById(guildId).loadMembers().get(); for (UserStats stats : needCache) { Member member = guild.getMemberById(stats.getUser().getDiscordId()); if (member == null) { - logger.warn("Can't find member '" + stats.getUser().getUsername() + "'after load, User leave the guild ?"); + logger.warn("Can't find member '{}'after load, User leave the guild ?", stats.getUser().getUsername()); continue; } String avatar = member.getUser().getAvatarUrl(); @@ -253,10 +252,10 @@ public class UserStatsUtils { @Override public void run() { - while (member.getVoiceState().inVoiceChannel()) { + while (member.getVoiceState().inAudioChannel()) { try { Thread.sleep(10000); - if (member.getVoiceState().inVoiceChannel()) + if (member.getVoiceState().inAudioChannel()) if (member.getGuild().getAfkChannel() != member.getVoiceState().getChannel()) UserStatsUtils.getINSTANCE().addVocalCount(member); diff --git a/src/main/java/net/Broken/Tools/UserManager/UserUtils.java b/src/main/java/net/Broken/Tools/UserManager/UserUtils.java deleted file mode 100644 index 5be5c3f..0000000 --- a/src/main/java/net/Broken/Tools/UserManager/UserUtils.java +++ /dev/null @@ -1,26 +0,0 @@ -package net.Broken.Tools.UserManager; - -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -public class UserUtils { - - private static final UserUtils INSTANCE = new UserUtils(); - private final Logger logger = LogManager.getLogger(); - - /** - * Private default constructor - */ - private UserUtils() { - } - - /** - * Singleton - * - * @return Unique UserUtils instance - */ - public static UserUtils getInstance() { - return INSTANCE; - } - -} From 16ff25f0035605592cbea09cc4b1e5284026db2c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:06:43 +0000 Subject: [PATCH 11/48] :arrow_up: Update dependency mysql:mysql-connector-java to v8.0.33 --- build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 00debc5..ea84359 100644 --- a/build.gradle +++ b/build.gradle @@ -51,14 +51,14 @@ dependencies { implementation group: 'org.hibernate', name: 'hibernate-validator', version: '7.0.5.Final' // Use MySQL Connector-J - implementation('mysql:mysql-connector-java:8.0.30') + implementation('mysql:mysql-connector-java:8.0.33') implementation('org.reflections:reflections:0.10.2') implementation('org.apache.commons:commons-lang3:3.12.0') liquibaseRuntime('org.liquibase:liquibase-core:4.14.0') liquibaseRuntime('org.liquibase:liquibase-groovy-dsl:3.0.2') - liquibaseRuntime('mysql:mysql-connector-java:8.0.30') + liquibaseRuntime('mysql:mysql-connector-java:8.0.33') liquibaseRuntime group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1' liquibaseRuntime group: 'org.liquibase.ext', name: 'liquibase-hibernate5', version: '4.14.0' liquibaseRuntime 'org.springframework.boot:spring-boot-starter-data-jpa' From b644acce724e199193378f2766fa8b0f344bed29 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:06:57 +0000 Subject: [PATCH 12/48] :arrow_up: Update plugin io.spring.dependency-management to v1.1.4 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 00debc5..b525a39 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'org.springframework.boot' version '2.7.18' - id 'io.spring.dependency-management' version '1.0.12.RELEASE' + id 'io.spring.dependency-management' version '1.1.4' id 'java' id 'groovy' id 'org.liquibase.gradle' version '2.1.1' From 7c41e0bf43e8b21fe1b9043686923790c98b253e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:07:45 +0000 Subject: [PATCH 13/48] :arrow_up: Update dependency org.springdoc:springdoc-openapi-security to v1.7.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index be814d8..583e9d2 100644 --- a/build.gradle +++ b/build.gradle @@ -36,7 +36,7 @@ dependencies { implementation('io.micrometer:micrometer-registry-prometheus:1.9.2') implementation('org.springdoc:springdoc-openapi-ui:1.7.0') - implementation('org.springdoc:springdoc-openapi-security:1.6.9') + implementation('org.springdoc:springdoc-openapi-security:1.7.0') implementation('org.liquibase:liquibase-core') From 4518cfc0317c109334e2bdca04f0a70fbc2071f1 Mon Sep 17 00:00:00 2001 From: sclement Date: Thu, 14 Dec 2023 15:09:42 +0100 Subject: [PATCH 14/48] Fix dep --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index be814d8..e7be7df 100644 --- a/build.gradle +++ b/build.gradle @@ -64,7 +64,7 @@ dependencies { liquibaseRuntime 'org.springframework.boot:spring-boot-starter-data-jpa' liquibaseRuntime 'org.springframework.data:spring-data-jpa' liquibaseRuntime 'org.springframework:spring-beans' - liquibaseRuntime 'net.dv8tion:JDA:4.4.0_352' + liquibaseRuntime 'net.dv8tion:JDA:5.0.0-beta.18' liquibaseRuntime 'com.sedmelluq:lavaplayer:1.3.78' liquibaseRuntime sourceSets.main.output From 0f487439ac12e16db9d125de8f1ce1426eaebb47 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:13:07 +0000 Subject: [PATCH 15/48] :arrow_up: Update dependency org.liquibase:liquibase-groovy-dsl to v3.0.3 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 97e0c35..2bb4d8f 100644 --- a/build.gradle +++ b/build.gradle @@ -57,7 +57,7 @@ dependencies { liquibaseRuntime('org.liquibase:liquibase-core:4.25.0') - liquibaseRuntime('org.liquibase:liquibase-groovy-dsl:3.0.2') + liquibaseRuntime('org.liquibase:liquibase-groovy-dsl:3.0.3') liquibaseRuntime('mysql:mysql-connector-java:8.0.33') liquibaseRuntime group: 'javax.xml.bind', name: 'jaxb-api', version: '2.3.1' liquibaseRuntime group: 'org.liquibase.ext', name: 'liquibase-hibernate5', version: '4.25.0' From 0b60cd7b98097f45e40d4cbab8b09d71d5169d3b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:13:12 +0000 Subject: [PATCH 16/48] :arrow_up: Update dependency io.jsonwebtoken:jjwt-api to v0.12.3 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 97e0c35..f2743da 100644 --- a/build.gradle +++ b/build.gradle @@ -40,7 +40,7 @@ dependencies { implementation('org.liquibase:liquibase-core') - implementation('io.jsonwebtoken:jjwt-api:0.11.5') + implementation('io.jsonwebtoken:jjwt-api:0.12.3') implementation('io.jsonwebtoken:jjwt-impl:0.11.5') implementation('io.jsonwebtoken:jjwt-jackson:0.11.5') From 50e392db7d0d7bb8fef1885155e0aec22371cfb9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:13:22 +0000 Subject: [PATCH 17/48] :arrow_up: Update dependency org.apache.commons:commons-lang3 to v3.14.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 97e0c35..d11f34f 100644 --- a/build.gradle +++ b/build.gradle @@ -53,7 +53,7 @@ dependencies { // Use MySQL Connector-J implementation('mysql:mysql-connector-java:8.0.33') implementation('org.reflections:reflections:0.10.2') - implementation('org.apache.commons:commons-lang3:3.12.0') + implementation('org.apache.commons:commons-lang3:3.14.0') liquibaseRuntime('org.liquibase:liquibase-core:4.25.0') From 3297ed5570671f0b167212f352d2074539e293a0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:13:34 +0000 Subject: [PATCH 18/48] :arrow_up: Update plugin org.liquibase.gradle to v2.2.1 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 97e0c35..18cdc8b 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ plugins { id 'io.spring.dependency-management' version '1.1.4' id 'java' id 'groovy' - id 'org.liquibase.gradle' version '2.1.1' + id 'org.liquibase.gradle' version '2.2.1' id "nebula.lint" version "17.7.0" id "com.gorylenko.gradle-git-properties" version "2.4.1" } From e9082fd4d0731f0dd76e6989baf7a3c1a610c982 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:13:37 +0000 Subject: [PATCH 19/48] :arrow_up: Update actions/checkout action to v4 --- .github/workflows/build.yml | 4 ++-- .github/workflows/release.yml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2f50121..6d32010 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK 17 uses: actions/setup-java@v3 @@ -41,7 +41,7 @@ jobs: needs: - build-gradle steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Download artifact uses: actions/download-artifact@v3.0.0 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c599ed1..d6d43df 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up JDK 17 uses: actions/setup-java@v3 @@ -39,7 +39,7 @@ jobs: needs: - build-gradle steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Download artifact uses: actions/download-artifact@v3.0.0 From e187c932be6ededa8c13dc0645e9a39e3eaf05b1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:13:41 +0000 Subject: [PATCH 20/48] :arrow_up: Update actions/setup-java action to v4 --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2f50121..ee554b7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: - uses: actions/checkout@v3 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: 17 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c599ed1..ea291e3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: - uses: actions/checkout@v3 - name: Set up JDK 17 - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'temurin' java-version: 17 From 24cda85f9621c96a3d6535530ec897654a3e942a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:14:19 +0000 Subject: [PATCH 21/48] :arrow_up: Update dependency gradle to v8 --- gradle/wrapper/gradle-wrapper.jar | Bin 61624 -> 43462 bytes gradle/wrapper/gradle-wrapper.properties | 3 ++- gradlew | 29 +++++++++++++---------- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index afba109285af78dbd2a1d187e33ac4f87c76e392..d64cd4917707c1f8861d8cb53dd15194d4248596 100644 GIT binary patch literal 43462 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 literal 61624 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�Zy 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b1624c4..1af9e09 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 65dcd68..1aa94a4 100644 --- a/gradlew +++ b/gradlew @@ -83,10 +83,8 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,10 +131,13 @@ 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. + if ! command -v java >/dev/null 2>&1 + then + 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 fi # Increase the maximum file descriptors if we can. @@ -144,7 +145,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +153,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,11 +198,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ From 401ab585e9d0a99c256bd6c69d81bf4b239f665e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:14:23 +0000 Subject: [PATCH 22/48] :arrow_up: Update dependency org.hibernate:hibernate-validator to v8 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 97e0c35..1566bab 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ dependencies { implementation('net.dv8tion:JDA:5.0.0-beta.18') implementation(platform("org.apache.logging.log4j:log4j-bom:2.18.0")) - implementation group: 'org.hibernate', name: 'hibernate-validator', version: '7.0.5.Final' + implementation group: 'org.hibernate', name: 'hibernate-validator', version: '8.0.1.Final' // Use MySQL Connector-J implementation('mysql:mysql-connector-java:8.0.33') From a942c1adb5dbdabe99ca549b58c8ec3ac35027ca Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:14:27 +0000 Subject: [PATCH 23/48] :arrow_up: Update docker/build-push-action action to v5 --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2f50121..a247262 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -70,7 +70,7 @@ jobs: echo "tag=${{ steps.branch-name.outputs.current_branch }}" >> $GITHUB_ENV - name: Build and push Docker - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 with: push: true context: . diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c599ed1..fdd0527 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,7 +68,7 @@ jobs: echo "tag=${{ steps.branch-name.outputs.tag }}" >> $GITHUB_ENV - name: Build and push Docker - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v5 with: push: true context: . From c7ed36053c58884722904f32d335aaa781f746b4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:14:31 +0000 Subject: [PATCH 24/48] :arrow_up: Update docker/login-action action to v3 --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2f50121..e0846b8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -55,7 +55,7 @@ jobs: uses: docker/setup-buildx-action@v2 - name: Login to ghcr.io - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.repository_owner }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c599ed1..8b1bd52 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,7 +53,7 @@ jobs: uses: docker/setup-buildx-action@v2 - name: Login to ghcr.io - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.repository_owner }} From deaece521229c8c9567af95c7548d2d1566e9ee9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:14:34 +0000 Subject: [PATCH 25/48] :arrow_up: Update docker/setup-buildx-action action to v3 --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2f50121..827bafc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -52,7 +52,7 @@ jobs: path: build/libs/ - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to ghcr.io uses: docker/login-action@v2 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c599ed1..8f2bd9f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,7 +50,7 @@ jobs: path: build/libs/ - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 + uses: docker/setup-buildx-action@v3 - name: Login to ghcr.io uses: docker/login-action@v2 From 157c512c0c7051c14666ca9d77752a2ba5a764d7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:14:38 +0000 Subject: [PATCH 26/48] :arrow_up: Update openjdk Docker tag to v21 --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 9ae49ad..a7b61ca 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM openjdk:18.0.1 +FROM openjdk:21 WORKDIR /bot_src ARG BUILD_NBR ARG BRANCH_NAME From b6eda824b784b86537ac69406d0e8fe86770386f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:14:45 +0000 Subject: [PATCH 27/48] :arrow_up: Update plugin org.springframework.boot to v3 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 97e0c35..47b7660 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.springframework.boot' version '2.7.18' + id 'org.springframework.boot' version '3.2.0' id 'io.spring.dependency-management' version '1.1.4' id 'java' id 'groovy' From 3579ee45547ca8c5a06cb483fba33b14096b5af1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:15:10 +0000 Subject: [PATCH 28/48] :arrow_up: Update dependency io.jsonwebtoken:jjwt-impl to v0.12.3 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1c99758..c040970 100644 --- a/build.gradle +++ b/build.gradle @@ -41,7 +41,7 @@ dependencies { implementation('org.liquibase:liquibase-core') implementation('io.jsonwebtoken:jjwt-api:0.12.3') - implementation('io.jsonwebtoken:jjwt-impl:0.11.5') + implementation('io.jsonwebtoken:jjwt-impl:0.12.3') implementation('io.jsonwebtoken:jjwt-jackson:0.11.5') implementation('com.sedmelluq:lavaplayer:1.3.78') From 6305f7675dcdbc03da43d2220fe684833833b11c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:15:20 +0000 Subject: [PATCH 29/48] :arrow_up: Update plugin nebula.lint to v19 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 1c99758..17921c3 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { id 'java' id 'groovy' id 'org.liquibase.gradle' version '2.2.1' - id "nebula.lint" version "17.7.0" + id "nebula.lint" version "19.0.1" id "com.gorylenko.gradle-git-properties" version "2.4.1" } From d3ffaa01d56c927beb84fcbdd64d527350a10aa2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:16:32 +0000 Subject: [PATCH 30/48] :arrow_up: Update dependency org.apache.logging.log4j:log4j-bom to v2.22.0 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 9d046c9..72712a9 100644 --- a/build.gradle +++ b/build.gradle @@ -47,7 +47,7 @@ dependencies { implementation('com.sedmelluq:lavaplayer:1.3.78') implementation('net.dv8tion:JDA:5.0.0-beta.18') - implementation(platform("org.apache.logging.log4j:log4j-bom:2.18.0")) + implementation(platform("org.apache.logging.log4j:log4j-bom:2.22.0")) implementation group: 'org.hibernate', name: 'hibernate-validator', version: '8.0.1.Final' // Use MySQL Connector-J From e4d9e7aae25da26c93e1e8355f7cca37bf7cefe9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:16:55 +0000 Subject: [PATCH 31/48] :arrow_up: Update dependency io.jsonwebtoken:jjwt-jackson to v0.12.3 --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 884fc96..da50507 100644 --- a/build.gradle +++ b/build.gradle @@ -42,7 +42,7 @@ dependencies { implementation('io.jsonwebtoken:jjwt-api:0.12.3') implementation('io.jsonwebtoken:jjwt-impl:0.12.3') - implementation('io.jsonwebtoken:jjwt-jackson:0.11.5') + implementation('io.jsonwebtoken:jjwt-jackson:0.12.3') implementation('com.sedmelluq:lavaplayer:1.3.78') implementation('net.dv8tion:JDA:5.0.0-beta.18') From 57b4583fd7b6aff47af033fbd870283da54f9b55 Mon Sep 17 00:00:00 2001 From: sclement Date: Thu, 14 Dec 2023 17:21:23 +0100 Subject: [PATCH 32/48] Spring boot 3 --- .vscode/settings.json | 7 +- .../Api/Controllers/AudioController.java | 24 +-- .../Api/Controllers/AuthController.java | 7 +- .../Controllers/CrossOptionController.java | 10 +- .../Api/Controllers/GuildController.java | 6 +- .../Api/Controllers/SettingController.java | 10 +- src/main/java/net/Broken/Api/Data/Login.java | 2 +- .../Broken/Api/Data/Music/PlayBackInfo.java | 9 +- .../net/Broken/Api/Data/Music/Status.java | 13 +- .../net/Broken/Api/Data/Music/TrackInfo.java | 3 +- .../Api/Data/Settings/SettingDescriber.java | 3 +- .../Api/Data/Settings/SettingGroup.java | 7 +- .../net/Broken/Api/OpenApi/OpenApiConfig.java | 4 +- .../DiscordAuthenticationProvider.java | 8 +- .../Components/UnauthorizedHandler.java | 12 +- .../Security/Data/DiscordOauthUserInfo.java | 9 +- ...CustomMethodSecurityExpressionHandler.java | 25 --- .../CustomMethodSecurityExpressionRoot.java | 102 ---------- .../Api/Security/Expression/WebSecurity.java | 69 +++++++ .../Api/Security/Filters/JwtFilter.java | 21 ++- .../Broken/Api/Security/SecurityConfig.java | 14 +- .../Api/Security/Services/JwtService.java | 49 +++-- .../net/Broken/Api/Services/AudioService.java | 28 +-- .../net/Broken/Api/Services/GuildService.java | 9 +- .../Broken/Api/Services/SettingService.java | 4 +- .../Broken/Audio/AudioPlayerSendHandler.java | 15 +- .../Broken/Audio/GuildAudioBotService.java | 121 ++++++------ .../java/net/Broken/Audio/TrackScheduler.java | 31 +-- src/main/java/net/Broken/BotConfigLoader.java | 14 +- src/main/java/net/Broken/Commande.java | 2 - .../DB/Entity/GuildPreferenceEntity.java | 31 +-- .../java/net/Broken/DB/Entity/UserEntity.java | 14 +- .../java/net/Broken/DB/Entity/UserStats.java | 8 +- .../Repository/GuildPreferenceRepository.java | 5 +- src/main/java/net/Broken/Init.java | 24 +-- src/main/java/net/Broken/MainBot.java | 5 +- .../java/net/Broken/SlashCommands/Cat.java | 7 +- .../java/net/Broken/SlashCommands/Clear.java | 6 +- .../java/net/Broken/SlashCommands/Music.java | 15 +- .../net/Broken/SlashCommands/Over18/Ass.java | 5 +- .../Broken/SlashCommands/Over18/Boobs.java | 2 - .../Broken/SlashCommands/Over18/Madame.java | 8 +- .../java/net/Broken/SlashCommands/Rank.java | 9 +- .../net/Broken/Tools/AutoVoiceChannel.java | 1 - .../Broken/Tools/Command/CommandParser.java | 15 +- .../Tools/Command/NumberedSlashCommand.java | 6 +- .../Tools/Command/SlashCommandLoader.java | 18 +- .../Broken/Tools/DayListener/DayListener.java | 7 +- .../DayListener/Listeners/DailyMadame.java | 29 ++- .../net/Broken/Tools/EmbedMessageUtils.java | 39 ++-- .../Broken/Tools/FindContentOnWebPage.java | 82 ++++---- .../java/net/Broken/Tools/LimitChecker.java | 2 - .../Broken/Tools/Random/Data/RandomData.java | 15 +- .../net/Broken/Tools/Random/TrueRandom.java | 25 +-- .../java/net/Broken/Tools/Redirection.java | 7 +- .../Settings/SettingDescriptionBuilder.java | 177 +++++++++--------- .../Broken/Tools/Settings/SettingSaver.java | 17 +- .../Tools/Settings/SettingValueBuilder.java | 132 ++++++------- .../java/net/Broken/Tools/SettingsUtils.java | 2 +- .../java/net/Broken/Tools/TimeConvertor.java | 4 +- .../UserManager/Stats/UserStatsUtils.java | 56 +++--- src/main/java/net/Broken/VersionLoader.java | 6 +- src/main/resources/application.yml | 2 +- .../db/changelog/db.changelog-master.yml | 31 --- 64 files changed, 670 insertions(+), 780 deletions(-) delete mode 100644 src/main/java/net/Broken/Api/Security/Expression/CustomMethodSecurityExpressionHandler.java delete mode 100644 src/main/java/net/Broken/Api/Security/Expression/CustomMethodSecurityExpressionRoot.java create mode 100644 src/main/java/net/Broken/Api/Security/Expression/WebSecurity.java diff --git a/.vscode/settings.json b/.vscode/settings.json index f7a6660..deaad8b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,7 @@ { "java.configuration.updateBuildConfiguration": "automatic", - "java.compile.nullAnalysis.mode": "disabled" -} \ No newline at end of file + "java.compile.nullAnalysis.mode": "disabled", + "editor.codeActionsOnSave": { + "source.organizeImports": true + } +} diff --git a/src/main/java/net/Broken/Api/Controllers/AudioController.java b/src/main/java/net/Broken/Api/Controllers/AudioController.java index ee7bce0..c0cf857 100644 --- a/src/main/java/net/Broken/Api/Controllers/AudioController.java +++ b/src/main/java/net/Broken/Api/Controllers/AudioController.java @@ -23,60 +23,60 @@ public class AudioController { this.audioService = audioService; } - @GetMapping("/{guildId}/status") - @PreAuthorize("isInGuild(#guildId)") + @PreAuthorize("@webSecurity.isInGuild(#guildId)") public Status getMusicStatus(@PathVariable String guildId, Authentication authentication) { JwtPrincipal principal = (JwtPrincipal) authentication.getPrincipal(); return audioService.getGuildAudioStatus(guildId, principal.user().getDiscordId()); } - @PostMapping("/{guildId}/connect") - @PreAuthorize("isInGuild(#guildId) && canInteractWithVoiceChannel(#guildId, #body)") - public ResponseEntity connect(@PathVariable String guildId, @RequestBody Connect body, Authentication authentication) { + @PreAuthorize("@webSecurity.isInGuild(#guildId) && @webSecurity.canInteractWithVoiceChannel(#guildId, #body)") + public ResponseEntity connect(@PathVariable String guildId, @RequestBody Connect body, + Authentication authentication) { JwtPrincipal principal = (JwtPrincipal) authentication.getPrincipal(); return audioService.connect(guildId, body, principal.user().getDiscordId()); } @PostMapping("/{guildId}/disconnect") - @PreAuthorize("isInGuild(#guildId) && canInteractWithVoiceChannel(#guildId)") + @PreAuthorize("@webSecurity.isInGuild(#guildId) && @webSecurity.canInteractWithVoiceChannel(#guildId)") public ResponseEntity disconnect(@PathVariable String guildId, Authentication authentication) { JwtPrincipal principal = (JwtPrincipal) authentication.getPrincipal(); return audioService.disconnect(guildId, principal.user().getDiscordId()); } @PostMapping("/{guildId}/resume") - @PreAuthorize("isInGuild(#guildId) && canInteractWithVoiceChannel(#guildId)") + @PreAuthorize("@webSecurity.isInGuild(#guildId) && @webSecurity.canInteractWithVoiceChannel(#guildId)") public ResponseEntity resume(@PathVariable String guildId, Authentication authentication) { JwtPrincipal principal = (JwtPrincipal) authentication.getPrincipal(); return audioService.resume(guildId, principal.user().getDiscordId()); } @PostMapping("/{guildId}/pause") - @PreAuthorize("isInGuild(#guildId) && canInteractWithVoiceChannel(#guildId)") + @PreAuthorize("@webSecurity.isInGuild(#guildId) && @webSecurity.canInteractWithVoiceChannel(#guildId)") public ResponseEntity pause(@PathVariable String guildId, Authentication authentication) { JwtPrincipal principal = (JwtPrincipal) authentication.getPrincipal(); return audioService.pause(guildId, principal.user().getDiscordId()); } @PostMapping("/{guildId}/skip") - @PreAuthorize("isInGuild(#guildId) && canInteractWithVoiceChannel(#guildId)") + @PreAuthorize("@webSecurity.isInGuild(#guildId) && @webSecurity.canInteractWithVoiceChannel(#guildId)") public ResponseEntity skip(@PathVariable String guildId, Authentication authentication) { JwtPrincipal principal = (JwtPrincipal) authentication.getPrincipal(); return audioService.skip(guildId, principal.user().getDiscordId()); } @PostMapping("/{guildId}/stop") - @PreAuthorize("isInGuild(#guildId) && canInteractWithVoiceChannel(#guildId)") + @PreAuthorize("@webSecurity.isInGuild(#guildId) && @webSecurity.canInteractWithVoiceChannel(#guildId)") public ResponseEntity stop(@PathVariable String guildId, Authentication authentication) { JwtPrincipal principal = (JwtPrincipal) authentication.getPrincipal(); return audioService.stop(guildId, principal.user().getDiscordId()); } @PostMapping("/{guildId}/add") - @PreAuthorize("isInGuild(#guildId) && canInteractWithVoiceChannel(#guildId)") - public ResponseEntity add(@PathVariable String guildId, @RequestBody Add body, Authentication authentication) throws ExecutionException, InterruptedException { + @PreAuthorize("@webSecurity.isInGuild(#guildId) && @webSecurity.canInteractWithVoiceChannel(#guildId)") + public ResponseEntity add(@PathVariable String guildId, @RequestBody Add body, + Authentication authentication) throws ExecutionException, InterruptedException { JwtPrincipal principal = (JwtPrincipal) authentication.getPrincipal(); return audioService.add(guildId, principal.user().getDiscordId(), body); } diff --git a/src/main/java/net/Broken/Api/Controllers/AuthController.java b/src/main/java/net/Broken/Api/Controllers/AuthController.java index 75d6c43..89795c5 100644 --- a/src/main/java/net/Broken/Api/Controllers/AuthController.java +++ b/src/main/java/net/Broken/Api/Controllers/AuthController.java @@ -19,7 +19,8 @@ public class AuthController { private final JwtService jwtService; - public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, JwtService jwtService) { + public AuthController(AuthenticationManager authenticationManager, UserRepository userRepository, + JwtService jwtService) { this.authenticationManager = authenticationManager; this.jwtService = jwtService; } @@ -27,14 +28,12 @@ public class AuthController { @PostMapping("/discord") public JwtResponse loginDiscord(@Validated @RequestBody Login login) { Authentication authentication = authenticationManager.authenticate( - new UsernamePasswordAuthenticationToken(login.redirectUri(), login.code()) - ); + new UsernamePasswordAuthenticationToken(login.redirectUri(), login.code())); UserEntity user = (UserEntity) authentication.getPrincipal(); String jwt = jwtService.buildJwt(user); - return new JwtResponse(jwt); } } diff --git a/src/main/java/net/Broken/Api/Controllers/CrossOptionController.java b/src/main/java/net/Broken/Api/Controllers/CrossOptionController.java index 69b7b24..fd45139 100644 --- a/src/main/java/net/Broken/Api/Controllers/CrossOptionController.java +++ b/src/main/java/net/Broken/Api/Controllers/CrossOptionController.java @@ -8,7 +8,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; - @RestController @RequestMapping("/api/v2") @CrossOrigin(origins = "*", maxAge = 3600) @@ -16,13 +15,10 @@ import org.springframework.web.bind.annotation.RestController; public class CrossOptionController { /** - * For cross preflight request send by axios + * For cross preflight request send by axios */ - @RequestMapping( - value = "/**", - method = RequestMethod.OPTIONS - ) + @RequestMapping(value = "/**", method = RequestMethod.OPTIONS) public ResponseEntity handle() { - return new ResponseEntity<>("",HttpStatus.OK); + return new ResponseEntity<>("", HttpStatus.OK); } } diff --git a/src/main/java/net/Broken/Api/Controllers/GuildController.java b/src/main/java/net/Broken/Api/Controllers/GuildController.java index cf84cc1..9787d12 100644 --- a/src/main/java/net/Broken/Api/Controllers/GuildController.java +++ b/src/main/java/net/Broken/Api/Controllers/GuildController.java @@ -41,21 +41,21 @@ public class GuildController { } @GetMapping("/{guildId}/voiceChannels") - @PreAuthorize("isInGuild(#guildId)") + @PreAuthorize("@webSecurity.isInGuild(#guildId)") public List getVoiceChannels(@PathVariable String guildId, Authentication authentication) { JwtPrincipal principal = (JwtPrincipal) authentication.getPrincipal(); return guildService.getVoiceChannel(guildId, principal.user().getDiscordId()); } @GetMapping("/{guildId}/textChannels") - @PreAuthorize("isInGuild(#guildId)") + @PreAuthorize("@webSecurity.isInGuild(#guildId)") public List getTextChannels(@PathVariable String guildId, Authentication authentication) { JwtPrincipal principal = (JwtPrincipal) authentication.getPrincipal(); return guildService.getTextChannel(guildId, principal.user().getDiscordId()); } @GetMapping("/{guildId}/roles") - @PreAuthorize("isInGuild(#guildId)") + @PreAuthorize("@webSecurity.isInGuild(#guildId)") public List getRoles(@PathVariable String guildId) { return guildService.getRole(guildId); } diff --git a/src/main/java/net/Broken/Api/Controllers/SettingController.java b/src/main/java/net/Broken/Api/Controllers/SettingController.java index 2cbe1f6..2d10718 100644 --- a/src/main/java/net/Broken/Api/Controllers/SettingController.java +++ b/src/main/java/net/Broken/Api/Controllers/SettingController.java @@ -21,19 +21,19 @@ public class SettingController { } @GetMapping("description") - public List getSettingDescription(){ + public List getSettingDescription() { return settingService.getSettingDescription(); } @GetMapping("/{guildId}/values") - @PreAuthorize("isInGuild(#guildId) && canManageGuild(#guildId)") - public List getSettingValues(@PathVariable String guildId){ + @PreAuthorize("@webSecurity.isInGuild(#guildId) && @webSecurity.canManageGuild(#guildId)") + public List getSettingValues(@PathVariable String guildId) { return settingService.getValues(guildId); } @PostMapping("/{guildId}/values") - @PreAuthorize("isInGuild(#guildId) && canManageGuild(#guildId)") - public List getSettingValues(@PathVariable String guildId, @RequestBody List values){ + @PreAuthorize("@webSecurity.isInGuild(#guildId) && @webSecurity.anManageGuild(#guildId)") + public List getSettingValues(@PathVariable String guildId, @RequestBody List values) { GuildPreferenceEntity pref = settingService.saveValue(guildId, values); return new SettingValueBuilder(pref).build(); } diff --git a/src/main/java/net/Broken/Api/Data/Login.java b/src/main/java/net/Broken/Api/Data/Login.java index ffa13f1..7b9c6a1 100644 --- a/src/main/java/net/Broken/Api/Data/Login.java +++ b/src/main/java/net/Broken/Api/Data/Login.java @@ -1,6 +1,6 @@ package net.Broken.Api.Data; -import javax.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotBlank; public record Login( @NotBlank String code, @NotBlank String redirectUri) { diff --git a/src/main/java/net/Broken/Api/Data/Music/PlayBackInfo.java b/src/main/java/net/Broken/Api/Data/Music/PlayBackInfo.java index 67079a0..5c140f4 100644 --- a/src/main/java/net/Broken/Api/Data/Music/PlayBackInfo.java +++ b/src/main/java/net/Broken/Api/Data/Music/PlayBackInfo.java @@ -4,9 +4,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; @JsonInclude(JsonInclude.Include.NON_NULL) public record PlayBackInfo( - Boolean paused, - Boolean stopped, - Long progress, - TrackInfo trackInfo -) { + Boolean paused, + Boolean stopped, + Long progress, + TrackInfo trackInfo) { } diff --git a/src/main/java/net/Broken/Api/Data/Music/Status.java b/src/main/java/net/Broken/Api/Data/Music/Status.java index 99fafa9..da1625a 100644 --- a/src/main/java/net/Broken/Api/Data/Music/Status.java +++ b/src/main/java/net/Broken/Api/Data/Music/Status.java @@ -6,11 +6,10 @@ import net.dv8tion.jda.api.audio.hooks.ConnectionStatus; @JsonInclude(JsonInclude.Include.NON_NULL) public record Status( - Boolean connected, - ConnectionStatus connectionStatus, - Channel channel, - Boolean canView, - Boolean canInteract, - PlayBackInfo playBackInfo -) { + Boolean connected, + ConnectionStatus connectionStatus, + Channel channel, + Boolean canView, + Boolean canInteract, + PlayBackInfo playBackInfo) { } diff --git a/src/main/java/net/Broken/Api/Data/Music/TrackInfo.java b/src/main/java/net/Broken/Api/Data/Music/TrackInfo.java index 1d99679..749827a 100644 --- a/src/main/java/net/Broken/Api/Data/Music/TrackInfo.java +++ b/src/main/java/net/Broken/Api/Data/Music/TrackInfo.java @@ -7,7 +7,8 @@ import net.Broken.Audio.UserAudioTrack; public record TrackInfo(UserInfo submitter, AudioTrackInfo detail) { public TrackInfo(UserAudioTrack userAudioTrack) { - this(new UserInfo(userAudioTrack.getSubmittedUser().getId(), userAudioTrack.getSubmittedUser().getName(), userAudioTrack.getSubmittedUser().getAvatarUrl()), + this(new UserInfo(userAudioTrack.getSubmittedUser().getId(), userAudioTrack.getSubmittedUser().getName(), + userAudioTrack.getSubmittedUser().getAvatarUrl()), userAudioTrack.getAudioTrack().getInfo()); } } diff --git a/src/main/java/net/Broken/Api/Data/Settings/SettingDescriber.java b/src/main/java/net/Broken/Api/Data/Settings/SettingDescriber.java index 16dc5f8..65cfe70 100644 --- a/src/main/java/net/Broken/Api/Data/Settings/SettingDescriber.java +++ b/src/main/java/net/Broken/Api/Data/Settings/SettingDescriber.java @@ -7,8 +7,7 @@ public record SettingDescriber( String id, String name, String description, - TYPE type -) { + TYPE type) { public enum TYPE { BOOL, LIST, STRING, ROLE, TEXT_CHANNEL, VOICE_CHANNEL diff --git a/src/main/java/net/Broken/Api/Data/Settings/SettingGroup.java b/src/main/java/net/Broken/Api/Data/Settings/SettingGroup.java index a10180a..dd2000f 100644 --- a/src/main/java/net/Broken/Api/Data/Settings/SettingGroup.java +++ b/src/main/java/net/Broken/Api/Data/Settings/SettingGroup.java @@ -6,8 +6,7 @@ import java.util.List; @JsonInclude(JsonInclude.Include.NON_NULL) public record SettingGroup( - String name, - SettingDescriber mainField, - List fields -) { + String name, + SettingDescriber mainField, + List fields) { } diff --git a/src/main/java/net/Broken/Api/OpenApi/OpenApiConfig.java b/src/main/java/net/Broken/Api/OpenApi/OpenApiConfig.java index 681e437..66277b2 100644 --- a/src/main/java/net/Broken/Api/OpenApi/OpenApiConfig.java +++ b/src/main/java/net/Broken/Api/OpenApi/OpenApiConfig.java @@ -28,8 +28,8 @@ public class OpenApiConfig { new SecurityScheme().name(securitySchemeName) .type(SecurityScheme.Type.HTTP) .scheme("bearer") - .bearerFormat("JWT")) - ).addServersItem(new Server().url("/").description("Default")) + .bearerFormat("JWT"))) + .addServersItem(new Server().url("/").description("Default")) .info(new Info().title("ClaptrapBot API").version(versionLoader.getVersion())); } } \ No newline at end of file diff --git a/src/main/java/net/Broken/Api/Security/Components/DiscordAuthenticationProvider.java b/src/main/java/net/Broken/Api/Security/Components/DiscordAuthenticationProvider.java index 40b7bad..19f9cbc 100644 --- a/src/main/java/net/Broken/Api/Security/Components/DiscordAuthenticationProvider.java +++ b/src/main/java/net/Broken/Api/Security/Components/DiscordAuthenticationProvider.java @@ -29,10 +29,12 @@ public class DiscordAuthenticationProvider implements AuthenticationProvider { String token = discordOauthService.getAccessToken(code, redirectUri); DiscordOauthUserInfo discordOauthUserInfo = discordOauthService.getUserInfo(token); discordOauthService.revokeToken(token); - DiscordOauthService.LoginOrRegisterResponse loginOrRegisterResponse = discordOauthService.loginOrRegisterDiscordUser(discordOauthUserInfo); + DiscordOauthService.LoginOrRegisterResponse loginOrRegisterResponse = discordOauthService + .loginOrRegisterDiscordUser(discordOauthUserInfo); UserEntity userEntity = loginOrRegisterResponse.response(); - if(!loginOrRegisterResponse.created()){ - userEntity = discordOauthService.updateUserInfo(discordOauthUserInfo, loginOrRegisterResponse.response()); + if (!loginOrRegisterResponse.created()) { + userEntity = discordOauthService.updateUserInfo(discordOauthUserInfo, + loginOrRegisterResponse.response()); } return new UsernamePasswordAuthenticationToken(userEntity, null, new ArrayList<>()); } catch (OAuthLoginFail e) { diff --git a/src/main/java/net/Broken/Api/Security/Components/UnauthorizedHandler.java b/src/main/java/net/Broken/Api/Security/Components/UnauthorizedHandler.java index 0cb9d77..c5ea431 100644 --- a/src/main/java/net/Broken/Api/Security/Components/UnauthorizedHandler.java +++ b/src/main/java/net/Broken/Api/Security/Components/UnauthorizedHandler.java @@ -1,15 +1,17 @@ package net.Broken.Api.Security.Components; import com.fasterxml.jackson.databind.ObjectMapper; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.http.MediaType; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.stereotype.Component; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.Map; @@ -19,7 +21,9 @@ public class UnauthorizedHandler implements AuthenticationEntryPoint { private final Logger logger = LogManager.getLogger(); @Override - public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { + public void commence(HttpServletRequest request, + HttpServletResponse response, AuthenticationException authException) + throws IOException, ServletException { logger.error("[API] Unauthorized error: {}", authException.getMessage()); response.setContentType(MediaType.APPLICATION_JSON_VALUE); diff --git a/src/main/java/net/Broken/Api/Security/Data/DiscordOauthUserInfo.java b/src/main/java/net/Broken/Api/Security/Data/DiscordOauthUserInfo.java index b1ffc0c..867ef4c 100644 --- a/src/main/java/net/Broken/Api/Security/Data/DiscordOauthUserInfo.java +++ b/src/main/java/net/Broken/Api/Security/Data/DiscordOauthUserInfo.java @@ -4,9 +4,8 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @JsonIgnoreProperties(ignoreUnknown = true) public record DiscordOauthUserInfo( - String id, - String username, - String discriminator, - String avatar) { + String id, + String username, + String discriminator, + String avatar) { } - diff --git a/src/main/java/net/Broken/Api/Security/Expression/CustomMethodSecurityExpressionHandler.java b/src/main/java/net/Broken/Api/Security/Expression/CustomMethodSecurityExpressionHandler.java deleted file mode 100644 index 87edfa8..0000000 --- a/src/main/java/net/Broken/Api/Security/Expression/CustomMethodSecurityExpressionHandler.java +++ /dev/null @@ -1,25 +0,0 @@ -package net.Broken.Api.Security.Expression; - -import org.aopalliance.intercept.MethodInvocation; -import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; -import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations; -import org.springframework.security.authentication.AuthenticationTrustResolver; -import org.springframework.security.authentication.AuthenticationTrustResolverImpl; -import org.springframework.security.core.Authentication; - -public class CustomMethodSecurityExpressionHandler - extends DefaultMethodSecurityExpressionHandler { - private final AuthenticationTrustResolver trustResolver = - new AuthenticationTrustResolverImpl(); - - @Override - protected MethodSecurityExpressionOperations createSecurityExpressionRoot( - Authentication authentication, MethodInvocation invocation) { - CustomMethodSecurityExpressionRoot root = - new CustomMethodSecurityExpressionRoot(authentication); - root.setPermissionEvaluator(getPermissionEvaluator()); - root.setTrustResolver(this.trustResolver); - root.setRoleHierarchy(getRoleHierarchy()); - return root; - } -} \ No newline at end of file diff --git a/src/main/java/net/Broken/Api/Security/Expression/CustomMethodSecurityExpressionRoot.java b/src/main/java/net/Broken/Api/Security/Expression/CustomMethodSecurityExpressionRoot.java deleted file mode 100644 index 4cd0f39..0000000 --- a/src/main/java/net/Broken/Api/Security/Expression/CustomMethodSecurityExpressionRoot.java +++ /dev/null @@ -1,102 +0,0 @@ -package net.Broken.Api.Security.Expression; - -import net.Broken.Api.Data.Music.Connect; -import net.Broken.Api.Security.Data.JwtPrincipal; -import net.Broken.MainBot; -import net.Broken.Tools.CacheTools; -import net.dv8tion.jda.api.Permission; -import net.dv8tion.jda.api.entities.Guild; -import net.dv8tion.jda.api.entities.Member; -import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; -import net.dv8tion.jda.api.entities.channel.unions.AudioChannelUnion; - -import org.springframework.security.access.expression.SecurityExpressionRoot; -import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations; -import org.springframework.security.core.Authentication; - -public class CustomMethodSecurityExpressionRoot - extends SecurityExpressionRoot - implements MethodSecurityExpressionOperations { - private Object filterObject; - private Object returnObject; - /** - * Creates a new instance - * - * @param authentication the {@link Authentication} to use. Cannot be null. - */ - public CustomMethodSecurityExpressionRoot(Authentication authentication) { - super(authentication); - } - - public boolean isInGuild(String guildId){ - JwtPrincipal jwtPrincipal = (JwtPrincipal) authentication.getPrincipal(); - Guild guild = MainBot.jda.getGuildById(guildId); - return CacheTools.getJdaUser(jwtPrincipal.user()).getMutualGuilds().contains(guild); - } - - public boolean canManageGuild(String guildId){ - JwtPrincipal jwtPrincipal = (JwtPrincipal) authentication.getPrincipal(); - Member member = MainBot.jda.getGuildById(guildId).getMemberById(jwtPrincipal.user().getDiscordId()); - return member.hasPermission( - Permission.MANAGE_SERVER, - Permission.MANAGE_PERMISSIONS, - Permission.MANAGE_CHANNEL - ); - } - - public boolean canInteractWithVoiceChannel(String guildId, Connect connectPayload){ - JwtPrincipal jwtPrincipal = (JwtPrincipal) authentication.getPrincipal(); - Guild guild = MainBot.jda.getGuildById(guildId); - Member member = guild.getMemberById(jwtPrincipal.user().getDiscordId()); - VoiceChannel channel = guild.getVoiceChannelById(connectPayload.channelId()); - if( channel == null){ - return false; - } - - return (member.hasPermission(channel, Permission.VOICE_CONNECT) - || member.getVoiceState() != null - && member.getVoiceState().getChannel() == channel) - && member.hasPermission(channel, Permission.VOICE_SPEAK); - } - - public boolean canInteractWithVoiceChannel(String guildId) { - JwtPrincipal jwtPrincipal = (JwtPrincipal) authentication.getPrincipal(); - Guild guild = MainBot.jda.getGuildById(guildId); - AudioChannelUnion channel = guild.getAudioManager().getConnectedChannel(); - - if (channel == null) { - return false; - } - - Member member = guild.getMemberById(jwtPrincipal.user().getDiscordId()); - return (member.hasPermission(channel, Permission.VOICE_CONNECT) - || member.getVoiceState() != null - && member.getVoiceState().getChannel() == channel) - && member.hasPermission(channel, Permission.VOICE_SPEAK); - } - - @Override - public void setFilterObject(Object filterObject) { - this.filterObject = filterObject; - } - - @Override - public Object getFilterObject() { - return this.filterObject; - } - - @Override - public void setReturnObject(Object returnObject) { - this.returnObject = returnObject; - } - - @Override - public Object getReturnObject() { - return this.returnObject; - } - - @Override - public Object getThis() { - return this; - } -} diff --git a/src/main/java/net/Broken/Api/Security/Expression/WebSecurity.java b/src/main/java/net/Broken/Api/Security/Expression/WebSecurity.java new file mode 100644 index 0000000..bea72af --- /dev/null +++ b/src/main/java/net/Broken/Api/Security/Expression/WebSecurity.java @@ -0,0 +1,69 @@ +package net.Broken.Api.Security.Expression; + +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; + +import net.Broken.MainBot; +import net.Broken.Api.Data.Music.Connect; +import net.Broken.Api.Security.Data.JwtPrincipal; +import net.Broken.Tools.CacheTools; +import net.dv8tion.jda.api.Permission; +import net.dv8tion.jda.api.entities.Guild; +import net.dv8tion.jda.api.entities.Member; +import net.dv8tion.jda.api.entities.channel.concrete.VoiceChannel; +import net.dv8tion.jda.api.entities.channel.unions.AudioChannelUnion; + +@Service +public class WebSecurity { + public boolean isInGuild(String guildId) { + + JwtPrincipal jwtPrincipal = (JwtPrincipal) SecurityContextHolder.getContext().getAuthentication() + .getPrincipal(); + Guild guild = MainBot.jda.getGuildById(guildId); + return CacheTools.getJdaUser(jwtPrincipal.user()).getMutualGuilds().contains(guild); + } + + public boolean canManageGuild(String guildId) { + JwtPrincipal jwtPrincipal = (JwtPrincipal) SecurityContextHolder.getContext().getAuthentication() + .getPrincipal(); + Member member = MainBot.jda.getGuildById(guildId).getMemberById(jwtPrincipal.user().getDiscordId()); + return member.hasPermission( + Permission.MANAGE_SERVER, + Permission.MANAGE_PERMISSIONS, + Permission.MANAGE_CHANNEL); + } + + public boolean canInteractWithVoiceChannel(String guildId, Connect connectPayload) { + JwtPrincipal jwtPrincipal = (JwtPrincipal) SecurityContextHolder.getContext().getAuthentication() + .getPrincipal(); + Guild guild = MainBot.jda.getGuildById(guildId); + Member member = guild.getMemberById(jwtPrincipal.user().getDiscordId()); + VoiceChannel channel = guild.getVoiceChannelById(connectPayload.channelId()); + if (channel == null) { + return false; + } + + return (member.hasPermission(channel, Permission.VOICE_CONNECT) + || member.getVoiceState() != null + && member.getVoiceState().getChannel() == channel) + && member.hasPermission(channel, Permission.VOICE_SPEAK); + } + + public boolean canInteractWithVoiceChannel(String guildId) { + JwtPrincipal jwtPrincipal = (JwtPrincipal) SecurityContextHolder.getContext().getAuthentication() + .getPrincipal(); + Guild guild = MainBot.jda.getGuildById(guildId); + AudioChannelUnion channel = guild.getAudioManager().getConnectedChannel(); + + if (channel == null) { + return false; + } + + Member member = guild.getMemberById(jwtPrincipal.user().getDiscordId()); + return (member.hasPermission(channel, Permission.VOICE_CONNECT) + || member.getVoiceState() != null + && member.getVoiceState().getChannel() == channel) + && member.hasPermission(channel, Permission.VOICE_SPEAK); + } + +} diff --git a/src/main/java/net/Broken/Api/Security/Filters/JwtFilter.java b/src/main/java/net/Broken/Api/Security/Filters/JwtFilter.java index 771cadd..43e931b 100644 --- a/src/main/java/net/Broken/Api/Security/Filters/JwtFilter.java +++ b/src/main/java/net/Broken/Api/Security/Filters/JwtFilter.java @@ -2,6 +2,10 @@ package net.Broken.Api.Security.Filters; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import net.Broken.Api.Security.Data.JwtPrincipal; import net.Broken.Api.Security.Services.JwtService; import net.Broken.BotConfigLoader; @@ -15,10 +19,6 @@ import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.web.filter.OncePerRequestFilter; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList; @@ -32,23 +32,24 @@ public class JwtFilter extends OncePerRequestFilter { private final Logger logger = LogManager.getLogger(); @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { String authHeader = request.getHeader("Authorization"); if (authHeader != null && authHeader.startsWith("Bearer ")) { String token = authHeader.replace("Bearer ", ""); try { UserEntity user; JwtPrincipal principal; - if(config.mode().equals("DEV")){ + if (config.mode().equals("DEV")) { user = userRepository.findByDiscordId(token).orElseThrow(); principal = new JwtPrincipal("DEV", user); - } - else { + } else { Jws jwt = jwtService.verifyAndParseJwt(token); user = jwtService.getUserWithJwt(jwt); - principal = new JwtPrincipal(jwt.getBody().getId(), user); + principal = new JwtPrincipal(jwt.getPayload().getId(), user); } - UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(principal, null, new ArrayList<>()); + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( + principal, null, new ArrayList<>()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } catch (Exception e) { diff --git a/src/main/java/net/Broken/Api/Security/SecurityConfig.java b/src/main/java/net/Broken/Api/Security/SecurityConfig.java index 6bc7d2f..055259b 100644 --- a/src/main/java/net/Broken/Api/Security/SecurityConfig.java +++ b/src/main/java/net/Broken/Api/Security/SecurityConfig.java @@ -6,6 +6,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; @@ -16,6 +17,7 @@ import net.Broken.Api.Security.Components.UnauthorizedHandler; import net.Broken.Api.Security.Filters.JwtFilter; @EnableWebSecurity +@EnableMethodSecurity @Configuration public class SecurityConfig { private final UnauthorizedHandler unauthorizedHandler; @@ -30,12 +32,12 @@ public class SecurityConfig { .exceptionHandling(handling -> handling.authenticationEntryPoint(unauthorizedHandler)) .sessionManagement(management -> management.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(requests -> requests - .antMatchers("/api/v2/auth/**").permitAll() - .antMatchers("/api/v2/guild/inviteLink").permitAll() - .antMatchers("/swagger-ui/**").permitAll() - .antMatchers("/swagger-ui.html").permitAll() - .antMatchers("/v3/api-docs/**").permitAll() - .antMatchers("/actuator/**").permitAll() + .requestMatchers("/api/v2/auth/**").permitAll() + .requestMatchers("/api/v2/guild/inviteLink").permitAll() + .requestMatchers("/swagger-ui/**").permitAll() + .requestMatchers("/swagger-ui.html").permitAll() + .requestMatchers("/v3/api-docs/**").permitAll() + .requestMatchers("/actuator/**").permitAll() .anyRequest().authenticated()); http.addFilterBefore(jwtFilter(), UsernamePasswordAuthenticationFilter.class); diff --git a/src/main/java/net/Broken/Api/Security/Services/JwtService.java b/src/main/java/net/Broken/Api/Security/Services/JwtService.java index 1e4df47..8ae26c8 100644 --- a/src/main/java/net/Broken/Api/Security/Services/JwtService.java +++ b/src/main/java/net/Broken/Api/Security/Services/JwtService.java @@ -1,34 +1,33 @@ package net.Broken.Api.Security.Services; - -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jws; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import io.jsonwebtoken.security.Keys; -import net.Broken.DB.Entity.UserEntity; -import net.Broken.DB.Repository.UserRepository; -import org.springframework.stereotype.Service; - -import java.security.Key; import java.util.Calendar; import java.util.Date; import java.util.NoSuchElementException; import java.util.UUID; +import javax.crypto.SecretKey; + +import org.springframework.stereotype.Service; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jws; +import io.jsonwebtoken.Jwts; +import net.Broken.DB.Entity.UserEntity; +import net.Broken.DB.Repository.UserRepository; + @Service public class JwtService { - private final Key jwtKey; + private final SecretKey jwtKey; private final UserRepository userRepository; public JwtService(UserRepository userRepository) { this.userRepository = userRepository; - this.jwtKey = Keys.secretKeyFor(SignatureAlgorithm.HS256); + this.jwtKey = Jwts.SIG.HS256.key().build(); } - public String buildJwt(UserEntity user){ + public String buildJwt(UserEntity user) { Date iat = new Date(); Date nbf = new Date(); Calendar expCal = Calendar.getInstance(); @@ -36,33 +35,29 @@ public class JwtService { Date exp = expCal.getTime(); UUID uuid = UUID.randomUUID(); - return Jwts.builder() - .setSubject(user.getUsername()) + .subject(user.getUsername()) .claim("discord_id", user.getDiscordId()) .claim("avatar", user.getAvatar()) .claim("discriminator", user.getDiscriminator()) - .setId(uuid.toString()) - .setIssuedAt(iat) - .setNotBefore(nbf) - .setExpiration(exp) + .id(uuid.toString()) + .issuedAt(iat) + .notBefore(nbf) + .expiration(exp) .signWith(this.jwtKey) .compact(); - } - public Jws verifyAndParseJwt(String token) { - return Jwts.parserBuilder() - .setSigningKey(this.jwtKey) + return Jwts.parser() + .verifyWith(this.jwtKey) .build() - .parseClaimsJws(token); + .parseSignedClaims(token); } - public UserEntity getUserWithJwt(Jws jwt) throws NoSuchElementException { - String discordId = jwt.getBody().get("discord_id", String.class); + String discordId = jwt.getPayload().get("discord_id", String.class); return userRepository.findByDiscordId(discordId) .orElseThrow(); } diff --git a/src/main/java/net/Broken/Api/Services/AudioService.java b/src/main/java/net/Broken/Api/Services/AudioService.java index a037284..f182d68 100644 --- a/src/main/java/net/Broken/Api/Services/AudioService.java +++ b/src/main/java/net/Broken/Api/Services/AudioService.java @@ -29,35 +29,35 @@ public class AudioService { Guild guild = MainBot.jda.getGuildById(guildId); Member member = guild.getMemberById(userId); - AudioChannelUnion channel = guild.getAudioManager().getConnectedChannel(); ConnectionStatus status = guild.getAudioManager().getConnectionStatus(); if (channel != null) { -// The user can view the audio status if: -// -> He can view the voice channel -// -> OR He can *not* view the voice channel, but he is connected to this voice channel + // The user can view the audio status if: + // -> He can view the voice channel + // -> OR He can *not* view the voice channel, but he is connected to this voice + // channel boolean canView = member.hasPermission(channel, Permission.VIEW_CHANNEL) || (member.getVoiceState() != null - && member.getVoiceState().getChannel() == channel); + && member.getVoiceState().getChannel() == channel); GuildAudioBotService guildAudioBotService = GuildAudioBotService.getInstance(guild); if (canView) { -// The user can interact with the audio if: -// -> He can connect to this voice channel -// -> OR he is connected to this voice channel -// -> AND He can speak in this voice channel + // The user can interact with the audio if: + // -> He can connect to this voice channel + // -> OR he is connected to this voice channel + // -> AND He can speak in this voice channel boolean canInteract = (member.hasPermission(channel, Permission.VOICE_CONNECT) || member.getVoiceState() != null - && member.getVoiceState().getChannel() == channel) + && member.getVoiceState().getChannel() == channel) && member.hasPermission(channel, Permission.VOICE_SPEAK); - boolean stopped = guildAudioBotService.getGuidAudioManager().player.getPlayingTrack() == null; PlayBackInfo playBackInfo; if (!stopped) { boolean paused = guildAudioBotService.getGuidAudioManager().player.isPaused(); long position = guildAudioBotService.getGuidAudioManager().player.getPlayingTrack().getPosition(); - UserAudioTrack userAudioTrack = guildAudioBotService.getGuidAudioManager().scheduler.getCurrentPlayingTrack(); + UserAudioTrack userAudioTrack = guildAudioBotService.getGuidAudioManager().scheduler + .getCurrentPlayingTrack(); playBackInfo = new PlayBackInfo(paused, false, position, new TrackInfo(userAudioTrack)); @@ -90,7 +90,6 @@ public class AudioService { return new ResponseEntity<>(status, HttpStatus.OK); } - public ResponseEntity pause(String guildId, String userId) { Guild guild = MainBot.jda.getGuildById(guildId); GuildAudioBotService.getInstance(guild).pause(); @@ -119,7 +118,8 @@ public class AudioService { return new ResponseEntity<>(status, HttpStatus.OK); } - public ResponseEntity add(String guildId, String userId, Add body) throws ExecutionException, InterruptedException { + public ResponseEntity add(String guildId, String userId, Add body) + throws ExecutionException, InterruptedException { Guild guild = MainBot.jda.getGuildById(guildId); boolean success = GuildAudioBotService.getInstance(guild).loadAndPlaySync(body.url(), userId); if (success) { diff --git a/src/main/java/net/Broken/Api/Services/GuildService.java b/src/main/java/net/Broken/Api/Services/GuildService.java index 4fb0709..3335f45 100644 --- a/src/main/java/net/Broken/Api/Services/GuildService.java +++ b/src/main/java/net/Broken/Api/Services/GuildService.java @@ -27,8 +27,7 @@ public class GuildService { boolean canManage = guild.getMember(discordUser).hasPermission( Permission.MANAGE_SERVER, Permission.MANAGE_PERMISSIONS, - Permission.MANAGE_CHANNEL - ); + Permission.MANAGE_CHANNEL); guildList.add(new Guild(guild.getId(), guild.getName(), guild.getIconUrl(), canManage)); } return guildList; @@ -69,9 +68,9 @@ public class GuildService { return roles; } - - public String getInviteLink(){ - return MainBot.jda.setRequiredScopes("applications.commands").getInviteUrl(Permission.getPermissions(1644971949399L)); + public String getInviteLink() { + return MainBot.jda.setRequiredScopes("applications.commands") + .getInviteUrl(Permission.getPermissions(1644971949399L)); } } diff --git a/src/main/java/net/Broken/Api/Services/SettingService.java b/src/main/java/net/Broken/Api/Services/SettingService.java index 827597e..cafbafa 100644 --- a/src/main/java/net/Broken/Api/Services/SettingService.java +++ b/src/main/java/net/Broken/Api/Services/SettingService.java @@ -19,7 +19,6 @@ public class SettingService { public final GuildPreferenceRepository preferenceRepository; private final Logger logger = LogManager.getLogger(); - public SettingService(GuildPreferenceRepository preferenceRepository) { this.preferenceRepository = preferenceRepository; } @@ -28,7 +27,6 @@ public class SettingService { return new SettingDescriptionBuilder().build(); } - public List getValues(String guildId) { GuildPreferenceEntity pref = preferenceRepository.findByGuildId(guildId).orElseGet(() -> { logger.info("[API] : Generate default guild pref"); @@ -37,7 +35,7 @@ public class SettingService { return new SettingValueBuilder(pref).build(); } - public GuildPreferenceEntity saveValue(String guildId, List values){ + public GuildPreferenceEntity saveValue(String guildId, List values) { GuildPreferenceEntity pref = preferenceRepository.findByGuildId(guildId).orElseGet(() -> { logger.info("[API] : Generate default guild pref"); return preferenceRepository.save(GuildPreferenceEntity.getDefault(guildId)); diff --git a/src/main/java/net/Broken/Audio/AudioPlayerSendHandler.java b/src/main/java/net/Broken/Audio/AudioPlayerSendHandler.java index 665d3aa..ef30ea5 100644 --- a/src/main/java/net/Broken/Audio/AudioPlayerSendHandler.java +++ b/src/main/java/net/Broken/Audio/AudioPlayerSendHandler.java @@ -1,14 +1,17 @@ package net.Broken.Audio; -import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; -import com.sedmelluq.discord.lavaplayer.track.playback.MutableAudioFrame; -import net.dv8tion.jda.api.audio.AudioSendHandler; - import java.nio.ByteBuffer; +import com.sedmelluq.discord.lavaplayer.player.AudioPlayer; +import com.sedmelluq.discord.lavaplayer.track.playback.MutableAudioFrame; + +import net.dv8tion.jda.api.audio.AudioSendHandler; + /** - * This is a wrapper around AudioPlayer which makes it behave as an AudioSendHandler for JDA. As JDA calls canProvide - * before every call to provide20MsAudio(), we pull the frame in canProvide() and use the frame we already pulled in + * This is a wrapper around AudioPlayer which makes it behave as an + * AudioSendHandler for JDA. As JDA calls canProvide + * before every call to provide20MsAudio(), we pull the frame in canProvide() + * and use the frame we already pulled in * provide20MsAudio(). */ public class AudioPlayerSendHandler implements AudioSendHandler { diff --git a/src/main/java/net/Broken/Audio/GuildAudioBotService.java b/src/main/java/net/Broken/Audio/GuildAudioBotService.java index fc0f9d2..0a88ad4 100644 --- a/src/main/java/net/Broken/Audio/GuildAudioBotService.java +++ b/src/main/java/net/Broken/Audio/GuildAudioBotService.java @@ -80,7 +80,8 @@ public class GuildAudioBotService { * @param playlistLimit Limit of playlist * @param onHead True for adding audio track on top of playlist */ - public void loadAndPlay(SlashCommandInteractionEvent event, AudioChannel voiceChannel, final String trackUrl, int playlistLimit, boolean onHead) { + public void loadAndPlay(SlashCommandInteractionEvent event, AudioChannel voiceChannel, final String trackUrl, + int playlistLimit, boolean onHead) { audioPlayerManager.loadItemOrdered(guildAudioManager, trackUrl, new AudioLoadResultHandler() { @Override public void trackLoaded(AudioTrack track) { @@ -110,7 +111,8 @@ public class GuildAudioBotService { @Override public void noMatches() { logger.warn("[{}] Cant find media!", guild); - MessageCreateData message = new MessageCreateBuilder().setEmbeds(EmbedMessageUtils.getMusicError("Video not found !")).build(); + MessageCreateData message = new MessageCreateBuilder() + .setEmbeds(EmbedMessageUtils.getMusicError("Video not found !")).build(); event.getHook().setEphemeral(true).sendMessage(message).queue(); } @@ -118,7 +120,8 @@ public class GuildAudioBotService { public void loadFailed(FriendlyException exception) { logger.error("[{}] Can't load media!", guild); logger.error(exception.getMessage()); - MessageCreateData message = new MessageCreateBuilder().setEmbeds(EmbedMessageUtils.getMusicError("Playback error !")).build(); + MessageCreateData message = new MessageCreateBuilder() + .setEmbeds(EmbedMessageUtils.getMusicError("Playback error !")).build(); event.getHook().setEphemeral(true).sendMessage(message).queue(); } }); @@ -128,42 +131,42 @@ public class GuildAudioBotService { Member member = guild.getMemberById(userId); AudioChannelUnion playedChanel = guild.getAudioManager().getConnectedChannel(); final String uuid = UUID.randomUUID().toString(); - Future future = audioPlayerManager.loadItemOrdered(guildAudioManager, trackUrl, new AudioLoadResultHandler() { - @Override - public void trackLoaded(AudioTrack track) { - logger.info("[{}] Auto add {} to playlist.", guild, track.getInfo().title); - UserAudioTrack userAudioTrack = new UserAudioTrack(member.getUser(), track); - play(guild, playedChanel, guildAudioManager, userAudioTrack, true); - addStatus.put(uuid, true); - } + Future future = audioPlayerManager.loadItemOrdered(guildAudioManager, trackUrl, + new AudioLoadResultHandler() { + @Override + public void trackLoaded(AudioTrack track) { + logger.info("[{}] Auto add {} to playlist.", guild, track.getInfo().title); + UserAudioTrack userAudioTrack = new UserAudioTrack(member.getUser(), track); + play(guild, playedChanel, guildAudioManager, userAudioTrack, true); + addStatus.put(uuid, true); + } - @Override - public void playlistLoaded(AudioPlaylist playlist) { - AudioTrack track = playlist.getTracks().get(0); - logger.info("[{}] Auto add {} to playlist.", guild, track.getInfo().title); - UserAudioTrack userAudioTrack = new UserAudioTrack(member.getUser(), track); - play(guild, playedChanel, guildAudioManager, userAudioTrack, true); - addStatus.put(uuid, true); - } + @Override + public void playlistLoaded(AudioPlaylist playlist) { + AudioTrack track = playlist.getTracks().get(0); + logger.info("[{}] Auto add {} to playlist.", guild, track.getInfo().title); + UserAudioTrack userAudioTrack = new UserAudioTrack(member.getUser(), track); + play(guild, playedChanel, guildAudioManager, userAudioTrack, true); + addStatus.put(uuid, true); + } - @Override - public void noMatches() { - logger.warn("[{}] Track not found: {}", guild, trackUrl); - addStatus.put(uuid, false); - } + @Override + public void noMatches() { + logger.warn("[{}] Track not found: {}", guild, trackUrl); + addStatus.put(uuid, false); + } - @Override - public void loadFailed(FriendlyException exception) { - logger.error("[{}] Cant load media!", guild); - logger.error(exception.getMessage()); - addStatus.put(uuid, false); - } - }); + @Override + public void loadFailed(FriendlyException exception) { + logger.error("[{}] Cant load media!", guild); + logger.error(exception.getMessage()); + addStatus.put(uuid, false); + } + }); future.get(); return addStatus.remove(uuid); } - /** * Load playlist to playlist * @@ -189,7 +192,6 @@ public class GuildAudioBotService { } } - /** * Add single track to playlist, auto-connect if not connected to vocal chanel * @@ -199,7 +201,8 @@ public class GuildAudioBotService { * @param track Track to add to playlist * @param onHead True for adding audio track on top of playlist */ - public void play(Guild guild, AudioChannel channel, GuildAudioManager musicManager, UserAudioTrack track, boolean onHead) { + public void play(Guild guild, AudioChannel channel, GuildAudioManager musicManager, UserAudioTrack track, + boolean onHead) { if (!guild.getAudioManager().isConnected()) guild.getAudioManager().openAudioConnection(channel); if (!onHead) @@ -212,7 +215,8 @@ public class GuildAudioBotService { if (guild.getAudioManager().isConnected()) { loadAndPlay(event, guild.getAudioManager().getConnectedChannel(), url, playListLimit, onHead); } else { - MessageCreateData message = new MessageCreateBuilder().setEmbeds(EmbedMessageUtils.getMusicError("Not connected to vocal chanel !")).build(); + MessageCreateData message = new MessageCreateBuilder() + .setEmbeds(EmbedMessageUtils.getMusicError("Not connected to vocal chanel !")).build(); event.getHook().setEphemeral(true).sendMessage(message).queue(); } } @@ -227,8 +231,8 @@ public class GuildAudioBotService { EmbedMessageUtils.buildStandar( new EmbedBuilder() .setTitle(":pause_button: Playback paused") - .setColor(Color.green) - )).build(); + .setColor(Color.green))) + .build(); clearLastButton(); lastMessageWithButton = hook.sendMessage(message).addActionRow(getActionButton()).complete(); } @@ -244,16 +248,16 @@ public class GuildAudioBotService { EmbedMessageUtils.buildStandar( new EmbedBuilder() .setTitle(":warning: Nothing to play, playlist is empty !") - .setColor(Color.green) - )).build(); + .setColor(Color.green))) + .build(); } else { resume(); message = new MessageCreateBuilder().setEmbeds( EmbedMessageUtils.buildStandar( new EmbedBuilder() .setTitle(":arrow_forward: Playback resumed") - .setColor(Color.green) - )).build(); + .setColor(Color.green))) + .build(); } clearLastButton(); lastMessageWithButton = hook.sendMessage(message).addActionRow(getActionButton()).complete(); @@ -269,8 +273,8 @@ public class GuildAudioBotService { EmbedMessageUtils.buildStandar( new EmbedBuilder() .setTitle(":track_next: Next Track") - .setColor(Color.green) - )).build(); + .setColor(Color.green))) + .build(); clearLastButton(); lastMessageWithButton = hook.sendMessage(message).addActionRow(getActionButton()).complete(); } @@ -285,8 +289,8 @@ public class GuildAudioBotService { EmbedMessageUtils.buildStandar( new EmbedBuilder() .setTitle(":stop_button: Playback stopped") - .setColor(Color.green) - )).build(); + .setColor(Color.green))) + .build(); clearLastButton(); lastMessageWithButton = hook.sendMessage(message).addActionRow(getActionButton()).complete(); @@ -304,8 +308,8 @@ public class GuildAudioBotService { EmbedMessageUtils.buildStandar( new EmbedBuilder() .setTitle(":eject: Disconnected") - .setColor(Color.green) - )).build(); + .setColor(Color.green))) + .build(); clearLastButton(); hook.sendMessage(message).queue(); } @@ -316,10 +320,12 @@ public class GuildAudioBotService { guild.getAudioManager().closeAudioConnection(); clearLastButton(); } + public void info(InteractionHook hook) { AudioTrackInfo info = guildAudioManager.scheduler.getInfo(); UserAudioTrack userAudioTrack = guildAudioManager.scheduler.getCurrentPlayingTrack(); - MessageCreateData message = new MessageCreateBuilder().setEmbeds(EmbedMessageUtils.getMusicInfo(info, userAudioTrack)).build(); + MessageCreateData message = new MessageCreateBuilder() + .setEmbeds(EmbedMessageUtils.getMusicInfo(info, userAudioTrack)).build(); clearLastButton(); lastMessageWithButton = hook.sendMessage(message).addActionRow(getActionButton()).complete(); } @@ -330,8 +336,8 @@ public class GuildAudioBotService { EmbedMessageUtils.buildStandar( new EmbedBuilder() .setTitle(":wastebasket: Playlist flushed") - .setColor(Color.green) - )).build(); + .setColor(Color.green))) + .build(); clearLastButton(); lastMessageWithButton = hook.sendMessage(message).addActionRow(getActionButton()).complete(); } @@ -350,9 +356,9 @@ public class GuildAudioBotService { new EmbedBuilder() .setTitle(":scroll: Playlist") .setColor(Color.green) - .setDescription("Oh no ! The playlist is empty !") - )).build(); - hook.sendMessage(message).queue(); + .setDescription("Oh no ! The playlist is empty !"))) + .build(); + hook.sendMessage(message).queue(); } else { StringBuilder resp = new StringBuilder(); int i = 0; @@ -375,8 +381,8 @@ public class GuildAudioBotService { new EmbedBuilder() .setTitle(":scroll: Playlist") .setColor(Color.green) - .setDescription(resp.toString()) - )).build(); + .setDescription(resp.toString()))) + .build(); hook.sendMessage(message).queue(); } @@ -390,7 +396,6 @@ public class GuildAudioBotService { return guildAudioManager; } - public void clearLastButton() { if (lastMessageWithButton != null) { this.lastMessageWithButton.editMessageComponents(new ArrayList<>()).queue(); @@ -401,10 +406,10 @@ public class GuildAudioBotService { public void updateLastButton() { if (lastMessageWithButton != null) - lastMessageWithButton = lastMessageWithButton.editMessageComponents(ActionRow.of(getActionButton())).complete(); + lastMessageWithButton = lastMessageWithButton.editMessageComponents(ActionRow.of(getActionButton())) + .complete(); } - private List