From c4df8f41496100c8c090812734d2d58f07ed58f9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 11:18:03 +0000 Subject: [PATCH 01/23] Update plugin com.diffplug.spotless to v6.25.0 --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index db3b34e7b..e7627972c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -23,7 +23,7 @@ dependencyResolutionManagement { bundle("log4j", listOf("slf4j-api", "log4j-core", "log4j-slf4j2", "log4j-jsontemplate")) // plugins - plugin("spotless", "com.diffplug.spotless").version("6.24.0") + plugin("spotless", "com.diffplug.spotless").version("6.25.0") plugin("shadow", "com.github.johnrengelman.shadow").version("8.1.1") } From 9007cc45f7847791b03ea7111f7b169f9603cf80 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 24 Jan 2024 14:20:59 +0000 Subject: [PATCH 02/23] Update sadu to v1.4.1 --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index e7627972c..bd121773d 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,7 +8,7 @@ dependencyResolutionManagement { versionCatalogs { create("libs") { // misc - version("sadu", "1.4.0") + version("sadu", "1.4.1") library("sadu-queries", "de.chojo.sadu", "sadu-queries").versionRef("sadu") library("sadu-updater", "de.chojo.sadu", "sadu-updater").versionRef("sadu") library("sadu-postgresql", "de.chojo.sadu", "sadu-postgresql").versionRef("sadu") From 927e25f6318b4863884bbc526905d009e63f1f04 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 12:21:05 +0100 Subject: [PATCH 03/23] Update gradle/wrapper-validation-action action to v2 (#590) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/docker.yml | 2 +- .github/workflows/verify.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 6c694794a..4291782ca 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -21,7 +21,7 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} allowed-conclusions: success - uses: actions/checkout@v4 - - uses: gradle/wrapper-validation-action@v1 + - uses: gradle/wrapper-validation-action@v2 - name: Build Image run: docker build . -f docker/Dockerfile -t ghcr.io/rainbowdashlabs/reputation-bot:${{ github.sha }} - name: Login to Registry diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 4f5e72b84..6be715a3f 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -9,7 +9,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: gradle/wrapper-validation-action@v1 + - uses: gradle/wrapper-validation-action@v2 - name: Set up JDK 19 uses: actions/setup-java@v4 with: From ce8d154a353872e9cbce9088d23fc2fd694af234 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 2 Feb 2024 18:04:46 +0000 Subject: [PATCH 04/23] Update dependency gradle to v8.6 --- gradle/wrapper/gradle-wrapper.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af9e0930..a80b22ce5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME From 3bf15bd2b64fdfa09af2cc6bd9b2edaeaf51ecaf Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 4 Feb 2024 18:46:02 +0000 Subject: [PATCH 05/23] Update dependency org.junit:junit-bom to v5.10.2 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3bfce7cba..3319d018e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,7 +42,7 @@ dependencies { implementation("org.knowm.xchart", "xchart", "3.8.7") // unit testing - testImplementation(platform("org.junit:junit-bom:5.10.1")) + testImplementation(platform("org.junit:junit-bom:5.10.2")) testImplementation("org.junit.jupiter", "junit-jupiter") testImplementation("org.knowm.xchart", "xchart", "3.8.7") } From 8a927ac8c8b7ffd86f753a6c1b16bdbb67f83dcb Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 09:55:40 +0100 Subject: [PATCH 06/23] Update dependency org.slf4j:slf4j-api to v2.0.12 (#594) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index bd121773d..f7ab503a4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -16,7 +16,7 @@ dependencyResolutionManagement { bundle("sadu", listOf("sadu-queries", "sadu-updater", "sadu-postgresql", "sadu-datasource")) version("log4j", "2.22.1") - library("slf4j-api", "org.slf4j:slf4j-api:2.0.11") + library("slf4j-api", "org.slf4j:slf4j-api:2.0.12") library("log4j-core", "org.apache.logging.log4j", "log4j-core").versionRef("log4j") library("log4j-slf4j2", "org.apache.logging.log4j", "log4j-slf4j2-impl").versionRef("log4j") library("log4j-jsontemplate","org.apache.logging.log4j", "log4j-layout-template-json").versionRef("log4j") From 636154bc6cd58e8410a3a5552367a162524c428c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 13 Feb 2024 01:47:08 +0000 Subject: [PATCH 07/23] Update postgres Docker tag to v16.2 --- docker/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 67716dac3..3fb5ff72a 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -12,7 +12,7 @@ services: database: networks: - repbot - image: postgres:16.1 + image: postgres:16.2 expose: - 5432 volumes: From e068cdf9c39c24ea920c862417fdf184547a9296 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 21 Feb 2024 14:03:42 +0000 Subject: [PATCH 08/23] Update dependency org.postgresql:postgresql to v42.7.2 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3319d018e..e5ebeaf3d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,7 +30,7 @@ dependencies { } // database - implementation("org.postgresql", "postgresql", "42.7.1") + implementation("org.postgresql", "postgresql", "42.7.2") implementation(libs.bundles.sadu) // Logging From ca3bf7088437879edd2a8446d1b6caee90004331 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 03:39:24 +0000 Subject: [PATCH 09/23] Update log4j to v2.23.0 --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index f7ab503a4..865e0f226 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,7 +15,7 @@ dependencyResolutionManagement { library("sadu-datasource", "de.chojo.sadu", "sadu-datasource").versionRef("sadu") bundle("sadu", listOf("sadu-queries", "sadu-updater", "sadu-postgresql", "sadu-datasource")) - version("log4j", "2.22.1") + version("log4j", "2.23.0") library("slf4j-api", "org.slf4j:slf4j-api:2.0.12") library("log4j-core", "org.apache.logging.log4j", "log4j-core").versionRef("log4j") library("log4j-slf4j2", "org.apache.logging.log4j", "log4j-slf4j2-impl").versionRef("log4j") From cba0b9fc70704cf4420f2492931428849c6f38f4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 10 Mar 2024 22:34:56 +0000 Subject: [PATCH 10/23] Update dependency org.apache.logging.log4j:log4j-layout-template-json to v2.23.1 --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 865e0f226..85a7cddcd 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -15,7 +15,7 @@ dependencyResolutionManagement { library("sadu-datasource", "de.chojo.sadu", "sadu-datasource").versionRef("sadu") bundle("sadu", listOf("sadu-queries", "sadu-updater", "sadu-postgresql", "sadu-datasource")) - version("log4j", "2.23.0") + version("log4j", "2.23.1") library("slf4j-api", "org.slf4j:slf4j-api:2.0.12") library("log4j-core", "org.apache.logging.log4j", "log4j-core").versionRef("log4j") library("log4j-slf4j2", "org.apache.logging.log4j", "log4j-slf4j2-impl").versionRef("log4j") From e59b23f40ca60255f916d3f5be77424494bfd99d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 14 Mar 2024 23:19:21 +0000 Subject: [PATCH 11/23] Update dependency org.postgresql:postgresql to v42.7.3 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index e5ebeaf3d..bd91cf1e2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,7 +30,7 @@ dependencies { } // database - implementation("org.postgresql", "postgresql", "42.7.2") + implementation("org.postgresql", "postgresql", "42.7.3") implementation(libs.bundles.sadu) // Logging From 85a04b3d012ebe65301d69127ea5136fd320dfc9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 22 Mar 2024 16:48:53 +0000 Subject: [PATCH 12/23] Update dependency gradle to v8.7 --- gradle/wrapper/gradle-wrapper.jar | Bin 43462 -> 43453 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew.bat | 20 ++++++++++---------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd4917707c1f8861d8cb53dd15194d4248596..e6441136f3d4ba8a0da8d277868979cfbc8ad796 100644 GIT binary patch delta 34118 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 delta 34176 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 SGW6ptSs!X7{rG*o_g?|vpSEZK diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a80b22ce5..b82aa23a4 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew.bat b/gradlew.bat index 6689b85be..7101f8e46 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -43,11 +43,11 @@ 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. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail @@ -57,11 +57,11 @@ 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. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail From f449c589bac1ff55020b8065d851c1d08f781527 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 21:57:51 +0000 Subject: [PATCH 13/23] Update gradle/wrapper-validation-action action to v3 --- .github/workflows/docker.yml | 2 +- .github/workflows/verify.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 4291782ca..1dc1d5b82 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -21,7 +21,7 @@ jobs: repo-token: ${{ secrets.GITHUB_TOKEN }} allowed-conclusions: success - uses: actions/checkout@v4 - - uses: gradle/wrapper-validation-action@v2 + - uses: gradle/wrapper-validation-action@v3 - name: Build Image run: docker build . -f docker/Dockerfile -t ghcr.io/rainbowdashlabs/reputation-bot:${{ github.sha }} - name: Login to Registry diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 6be715a3f..2778406b4 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -9,7 +9,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: gradle/wrapper-validation-action@v2 + - uses: gradle/wrapper-validation-action@v3 - name: Set up JDK 19 uses: actions/setup-java@v4 with: From 6b697067ddc6182cbcb5eb6407c127ca5eb78bae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 12 Apr 2024 22:08:09 +0000 Subject: [PATCH 14/23] Update dependency org.slf4j:slf4j-api to v2.0.13 (#605) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Yannick Lamprecht <1420893+yannicklamprecht@users.noreply.github.com> --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 85a7cddcd..0c30519b5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -16,7 +16,7 @@ dependencyResolutionManagement { bundle("sadu", listOf("sadu-queries", "sadu-updater", "sadu-postgresql", "sadu-datasource")) version("log4j", "2.23.1") - library("slf4j-api", "org.slf4j:slf4j-api:2.0.12") + library("slf4j-api", "org.slf4j:slf4j-api:2.0.13") library("log4j-core", "org.apache.logging.log4j", "log4j-core").versionRef("log4j") library("log4j-slf4j2", "org.apache.logging.log4j", "log4j-slf4j2-impl").versionRef("log4j") library("log4j-jsontemplate","org.apache.logging.log4j", "log4j-layout-template-json").versionRef("log4j") From a9c46f6865cf496747c5e952a19e629883138d32 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 10 May 2024 00:59:03 +0000 Subject: [PATCH 15/23] Update postgres Docker tag to v16.3 --- docker/docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 3fb5ff72a..4bb757681 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -12,7 +12,7 @@ services: database: networks: - repbot - image: postgres:16.2 + image: postgres:16.3 expose: - 5432 volumes: From 981d1799b347daba861eb0a951b3996fe4fd9e66 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 11 May 2024 11:34:38 +0200 Subject: [PATCH 16/23] Update dependency org.knowm.xchart:xchart to v3.8.8 (#608) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index bd91cf1e2..236ec9876 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -39,12 +39,12 @@ dependencies { exclude("org.apache.logging.log4j") } - implementation("org.knowm.xchart", "xchart", "3.8.7") + implementation("org.knowm.xchart", "xchart", "3.8.8") // unit testing testImplementation(platform("org.junit:junit-bom:5.10.2")) testImplementation("org.junit.jupiter", "junit-jupiter") - testImplementation("org.knowm.xchart", "xchart", "3.8.7") + testImplementation("org.knowm.xchart", "xchart", "3.8.8") } java { From 4ba27237ad30209992be559aed5794243a05303a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 1 Jun 2024 00:47:24 +0000 Subject: [PATCH 17/23] Update dependency gradle to v8.8 --- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b82aa23a4..a4413138c 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a426..b740cf133 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # 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 +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. From 0773fd8ff04d4c48ebc26aca6a67724bb08309ae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 27 Jun 2024 17:24:27 +0000 Subject: [PATCH 18/23] Update dependency org.junit:junit-bom to v5.10.3 --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index 236ec9876..a3807c0dd 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -42,7 +42,7 @@ dependencies { implementation("org.knowm.xchart", "xchart", "3.8.8") // unit testing - testImplementation(platform("org.junit:junit-bom:5.10.2")) + testImplementation(platform("org.junit:junit-bom:5.10.3")) testImplementation("org.junit.jupiter", "junit-jupiter") testImplementation("org.knowm.xchart", "xchart", "3.8.8") } From fa45441290c0910a09816e7bf58a902bcfc1e597 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Jul 2024 21:50:43 +0200 Subject: [PATCH 19/23] Update dependency de.chojo:cjda-util to v2.9.8+jda-5.0.0 (#611) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle.kts b/build.gradle.kts index a3807c0dd..dab60b00a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,7 +25,7 @@ spotless { dependencies { //discord - implementation("de.chojo", "cjda-util", "2.9.6+beta.19") { + implementation("de.chojo", "cjda-util", "2.9.8+jda-5.0.0") { exclude(group = "club.minnced", module = "opus-java") } From 16e12bd4396cfe30ebc95de83095cdc36a4b5f38 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 11 Jul 2024 17:05:20 +0000 Subject: [PATCH 20/23] Update dependency gradle to v8.9 --- gradle/wrapper/gradle-wrapper.jar | Bin 43453 -> 43504 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 5 ++++- gradlew.bat | 2 ++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index e6441136f3d4ba8a0da8d277868979cfbc8ad796..2c3521197d7c4586c843d1d3e9090525f1898cde 100644 GIT binary patch delta 8703 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 Ojj7^59?Sdh$^Qpp*~F>< delta 8662 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 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a4413138c..09523c0e5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index b740cf133..f5feea6d6 100755 --- a/gradlew +++ b/gradlew @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -84,7 +86,8 @@ done # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} # 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 +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum diff --git a/gradlew.bat b/gradlew.bat index 7101f8e46..9b42019c7 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,6 +13,8 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem @if "%DEBUG%"=="" @echo off @rem ########################################################################## From 22d23097a1864395f4d21fc5f8a7459ae9a9d118 Mon Sep 17 00:00:00 2001 From: Lilly Tempest <46890129+rainbowdashlabs@users.noreply.github.com> Date: Mon, 15 Jul 2024 22:05:41 +0200 Subject: [PATCH 21/23] Deps/major sadu (#615) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .envrc | 1 + .gitignore | 1 + build.gradle.kts | 3 +- docker/Dockerfile | 2 +- settings.gradle.kts | 2 +- shell.nix | 6 + src/main/java/de/chojo/repbot/core/Data.java | 64 ++--- .../de/chojo/repbot/dao/access/Analyzer.java | 43 ++-- .../de/chojo/repbot/dao/access/Cleanup.java | 19 +- .../java/de/chojo/repbot/dao/access/Gdpr.java | 70 +++--- .../repbot/dao/access/gdpr/GdprUser.java | 99 ++++---- .../repbot/dao/access/gdpr/RemovalTask.java | 108 ++++---- .../repbot/dao/access/guild/Cleanup.java | 32 ++- .../chojo/repbot/dao/access/guild/Gdpr.java | 32 +-- .../repbot/dao/access/guild/RepGuild.java | 62 ++--- .../repbot/dao/access/guild/RepGuildId.java | 4 +- .../access/guild/reputation/Reputation.java | 15 +- .../access/guild/reputation/sub/Analyzer.java | 88 ++++--- .../dao/access/guild/reputation/sub/Log.java | 43 ++-- .../access/guild/reputation/sub/Ranking.java | 63 +++-- .../access/guild/reputation/sub/RepUser.java | 174 ++++++------- .../guild/reputation/sub/user/Gdpr.java | 25 +- .../dao/access/guild/settings/Settings.java | 177 +++++++------- .../guild/settings/sub/AbuseProtection.java | 45 ++-- .../guild/settings/sub}/Announcements.java | 34 ++- .../access/guild/settings/sub/General.java | 50 ++-- .../access/guild/settings/sub/Messages.java | 41 ++-- .../dao/access/guild/settings/sub/Ranks.java | 109 ++++----- .../access/guild/settings/sub/Reputation.java | 63 +++-- .../access/guild/settings/sub/Thanking.java | 63 +++-- .../guild/settings/sub/thanking/Channels.java | 48 ++-- .../settings/sub/thanking/Reactions.java | 25 +- .../settings/sub/thanking/RolesHolder.java | 18 +- .../settings/sub/thanking/Thankwords.java | 17 +- .../repbot/dao/access/metrics/Commands.java | 86 ++++--- .../repbot/dao/access/metrics/Messages.java | 67 +++-- .../repbot/dao/access/metrics/Reputation.java | 230 +++++++++--------- .../repbot/dao/access/metrics/Service.java | 69 +++--- .../repbot/dao/access/metrics/Statistic.java | 61 ++--- .../repbot/dao/access/metrics/Users.java | 97 ++++---- .../de/chojo/repbot/dao/provider/Guilds.java | 42 ++-- .../de/chojo/repbot/dao/provider/Metrics.java | 16 +- .../de/chojo/repbot/dao/provider/Voice.java | 92 +++---- .../repbot/dao/snapshots/RepProfile.java | 2 +- .../dao/snapshots/ReputationLogEntry.java | 23 +- .../repbot/dao/snapshots/ReputationRank.java | 16 +- .../statistics/CommandStatistic.java | 3 +- .../snapshots/statistics/CountStatistics.java | 2 +- .../snapshots/statistics/DowStatistics.java | 2 +- .../snapshots/statistics/UserStatistic.java | 2 +- .../de/chojo/repbot/service/GdprService.java | 4 +- .../de/chojo/repbot/statistic/Statistic.java | 2 +- .../web/routes/v1/metrics/Commands.java | 8 +- .../web/routes/v1/metrics/Messages.java | 14 +- .../web/routes/v1/metrics/Reputation.java | 26 +- .../repbot/web/routes/v1/metrics/Service.java | 8 +- .../repbot/web/routes/v1/metrics/Users.java | 4 +- 57 files changed, 1185 insertions(+), 1337 deletions(-) create mode 100644 .envrc create mode 100644 shell.nix rename src/main/java/de/chojo/repbot/dao/{pagination => access/guild/settings/sub}/Announcements.java (73%) diff --git a/.envrc b/.envrc new file mode 100644 index 000000000..1d953f4bd --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use nix diff --git a/.gitignore b/.gitignore index 864d91184..5f266afe0 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,4 @@ buildNumber.properties /out/ /site/ +/.direnv diff --git a/build.gradle.kts b/build.gradle.kts index dab60b00a..870b26bdf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -4,7 +4,6 @@ plugins { alias(libs.plugins.shadow) alias(libs.plugins.spotless) java - `maven-publish` } group = "de.chojo" @@ -49,7 +48,7 @@ dependencies { java { toolchain { - languageVersion.set(JavaLanguageVersion.of(19)) + languageVersion.set(JavaLanguageVersion.of(21)) } withSourcesJar() withJavadocJar() diff --git a/docker/Dockerfile b/docker/Dockerfile index 8884541a0..5f3ca93fd 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,4 +1,4 @@ -FROM gradle:jdk19-alpine as build +FROM gradle:jdk21-alpine as build COPY . . RUN gradle clean build --no-daemon diff --git a/settings.gradle.kts b/settings.gradle.kts index 0c30519b5..9bda9b772 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,7 +8,7 @@ dependencyResolutionManagement { versionCatalogs { create("libs") { // misc - version("sadu", "1.4.1") + version("sadu", "2.2.0") library("sadu-queries", "de.chojo.sadu", "sadu-queries").versionRef("sadu") library("sadu-updater", "de.chojo.sadu", "sadu-updater").versionRef("sadu") library("sadu-postgresql", "de.chojo.sadu", "sadu-postgresql").versionRef("sadu") diff --git a/shell.nix b/shell.nix new file mode 100644 index 000000000..0916b8b04 --- /dev/null +++ b/shell.nix @@ -0,0 +1,6 @@ +{ pkgs ? import {}, ... }: + +pkgs.mkShell { + packages = with pkgs; [jdk21]; +} + diff --git a/src/main/java/de/chojo/repbot/core/Data.java b/src/main/java/de/chojo/repbot/core/Data.java index 2cf8702d2..45d1bd3b6 100644 --- a/src/main/java/de/chojo/repbot/core/Data.java +++ b/src/main/java/de/chojo/repbot/core/Data.java @@ -10,15 +10,19 @@ import de.chojo.repbot.dao.access.Analyzer; import de.chojo.repbot.dao.access.Cleanup; import de.chojo.repbot.dao.access.Gdpr; +import de.chojo.repbot.dao.access.gdpr.RemovalTask; import de.chojo.repbot.dao.provider.Guilds; import de.chojo.repbot.dao.provider.Metrics; import de.chojo.repbot.dao.provider.Voice; import de.chojo.repbot.util.LogNotify; -import de.chojo.sadu.databases.PostgreSql; import de.chojo.sadu.datasource.DataSourceCreator; +import de.chojo.sadu.mapper.RowMapperRegistry; +import de.chojo.sadu.mapper.rowmapper.RowMapper; +import de.chojo.sadu.postgresql.databases.PostgreSql; +import de.chojo.sadu.postgresql.mapper.PostgresqlMapper; +import de.chojo.sadu.queries.configuration.QueryConfiguration; import de.chojo.sadu.updater.QueryReplacement; import de.chojo.sadu.updater.SqlUpdater; -import de.chojo.sadu.wrapper.QueryBuilderConfig; import org.slf4j.Logger; import java.io.IOException; @@ -50,8 +54,8 @@ public static Data create(Threading threading, Configuration configuration) thro } public void init() throws SQLException, IOException { - configure(); initConnection(); + configure(); updateDatabase(); initDao(); } @@ -72,46 +76,50 @@ public void initConnection() { private void updateDatabase() throws IOException, SQLException { var schema = configuration.database().schema(); SqlUpdater.builder(dataSource, PostgreSql.get()) - .setReplacements(new QueryReplacement("repbot_schema", schema)) - .setVersionTable(schema + ".repbot_version") - .setSchemas(schema) - .execute(); + .setReplacements(new QueryReplacement("repbot_schema", schema)) + .setVersionTable(schema + ".repbot_version") + .setSchemas(schema) + .execute(); } private void configure() { - log.info("Configuring QueryBuilder"); + log.info("Configuring Query Configuration"); var logger = getLogger("DbLogger"); - QueryBuilderConfig.setDefault(QueryBuilderConfig.builder() - .withExceptionHandler(err -> logger.error(LogNotify.NOTIFY_ADMIN, "An error occured during a database request", err)) - .withExecutor(threading.repBotWorker()) - .build()); + var registry = new RowMapperRegistry(); + registry.register(RowMapper.forClass(RemovalTask.class).mapper(RemovalTask::build).build()); + registry.register(PostgresqlMapper.getDefaultMapper()); + QueryConfiguration.setDefault( + QueryConfiguration.builder(dataSource) + .setExceptionHandler(err -> logger.error(LogNotify.NOTIFY_ADMIN, "An error occured during a database request", err)) + .setRowMapperRegistry(registry) + .build()); } private void initDao() { log.info("Creating DAOs"); - guilds = new Guilds(dataSource, configuration); - gdpr = new Gdpr(dataSource, configuration); - cleanup = new Cleanup(dataSource); + guilds = new Guilds(configuration); + gdpr = new Gdpr(configuration); + cleanup = new Cleanup(); metrics = new Metrics(dataSource); - analyzer = new Analyzer(dataSource, configuration); - voice = new Voice(dataSource, configuration); + analyzer = new Analyzer(configuration); + voice = new Voice(configuration); } private HikariDataSource getConnectionPool() { log.info("Creating connection pool."); var data = configuration.database(); return DataSourceCreator.create(PostgreSql.get()) - .configure(config -> config - .host(data.host()) - .port(data.port()) - .user(data.user()) - .password(data.password()) - .database(data.database())) - .create() - .withMaximumPoolSize(data.poolSize()) - .withThreadFactory(Threading.createThreadFactory(threading.hikariGroup())) - .forSchema(data.schema()) - .build(); + .configure(config -> config + .host(data.host()) + .port(data.port()) + .user(data.user()) + .password(data.password()) + .database(data.database())) + .create() + .withMaximumPoolSize(data.poolSize()) + .withThreadFactory(Threading.createThreadFactory(threading.hikariGroup())) + .forSchema(data.schema()) + .build(); } public Guilds guilds() { diff --git a/src/main/java/de/chojo/repbot/dao/access/Analyzer.java b/src/main/java/de/chojo/repbot/dao/access/Analyzer.java index c16298677..a16d09c05 100644 --- a/src/main/java/de/chojo/repbot/dao/access/Analyzer.java +++ b/src/main/java/de/chojo/repbot/dao/access/Analyzer.java @@ -5,39 +5,40 @@ */ package de.chojo.repbot.dao.access; -import de.chojo.jdautil.util.Futures; import de.chojo.repbot.config.Configuration; import de.chojo.repbot.util.LogNotify; -import de.chojo.sadu.base.QueryFactory; +import de.chojo.sadu.queries.api.query.Query; import org.slf4j.Logger; -import javax.sql.DataSource; - +import static de.chojo.sadu.queries.api.call.Call.call; import static org.slf4j.LoggerFactory.getLogger; -public class Analyzer extends QueryFactory { +public class Analyzer { private static final Logger log = getLogger(Analyzer.class); private final Configuration configuration; - public Analyzer(DataSource dataSource, Configuration configuration) { - super(dataSource); + public Analyzer(Configuration configuration) { this.configuration = configuration; } public void cleanup() { - builder().query(""" - DELETE FROM analyzer_results WHERE analyzed < NOW() - ?::interval; - """) - .parameter(stmt -> stmt.setString("%d HOURS".formatted(configuration.cleanup().analyzerLogHours()))) - .delete() - .send() - .whenComplete(Futures.whenComplete(res -> log.debug("Deleted {} entries from analyzer log", res.rows()), - err -> log.error(LogNotify.NOTIFY_ADMIN, "Could not cleanup analyzer log.", err))); - builder().query("DELETE FROM reputation_results WHERE submitted < now() - ?::interval") - .parameter(stmt -> stmt.setString("%d HOURS".formatted(configuration.cleanup().analyzerLogHours()))) - .delete() - .send() - .whenComplete(Futures.whenComplete(res -> log.debug("Deleted {} entries from reputation results", res.rows()), - err -> log.error(LogNotify.NOTIFY_ADMIN, "Could not cleanup reputation results.", err))); + var delete = Query.query(""" + DELETE FROM analyzer_results WHERE analyzed < now() - ?::INTERVAL; + """) + .single(call().bind("%d HOURS".formatted(configuration.cleanup().analyzerLogHours()))) + .delete(); + if (delete.changed()) { + log.debug("Deleted {} entries from analyzer log", delete.rows()); + } else { + delete.exceptions().forEach(e -> log.error(LogNotify.NOTIFY_ADMIN, "Could not cleanup analyzer log.", e)); + } + delete = Query.query("DELETE FROM reputation_results WHERE submitted < now() - ?::INTERVAL") + .single(call().bind("%d HOURS".formatted(configuration.cleanup().analyzerLogHours()))) + .delete(); + if (delete.changed()) { + log.debug("Deleted {} entries from reputation results", delete.rows()); + } else { + delete.exceptions().forEach(e -> log.error(LogNotify.NOTIFY_ADMIN, "Could not cleanup analyzer log.", e)); + } } } diff --git a/src/main/java/de/chojo/repbot/dao/access/Cleanup.java b/src/main/java/de/chojo/repbot/dao/access/Cleanup.java index 934b99a12..9b6d7d917 100644 --- a/src/main/java/de/chojo/repbot/dao/access/Cleanup.java +++ b/src/main/java/de/chojo/repbot/dao/access/Cleanup.java @@ -5,22 +5,15 @@ */ package de.chojo.repbot.dao.access; -import de.chojo.sadu.base.QueryFactory; +import de.chojo.sadu.queries.api.query.Query; -import javax.sql.DataSource; import java.util.List; -public class Cleanup extends QueryFactory { - public Cleanup(DataSource dataSource) { - super(dataSource); - } - +public class Cleanup { public List getCleanupList() { - return builder(Long.class) - .queryWithoutParams(""" - SELECT guild_id FROM self_cleanup; - """) - .readRow(stmt -> stmt.getLong("guild_id")) - .allSync(); + return Query.query("SELECT guild_id FROM self_cleanup;") + .single() + .mapAs(Long.class) + .all(); } } diff --git a/src/main/java/de/chojo/repbot/dao/access/Gdpr.java b/src/main/java/de/chojo/repbot/dao/access/Gdpr.java index 2ee81ba51..af7c88d0f 100644 --- a/src/main/java/de/chojo/repbot/dao/access/Gdpr.java +++ b/src/main/java/de/chojo/repbot/dao/access/Gdpr.java @@ -8,55 +8,52 @@ import de.chojo.repbot.config.Configuration; import de.chojo.repbot.dao.access.gdpr.GdprUser; import de.chojo.repbot.dao.access.gdpr.RemovalTask; -import de.chojo.sadu.base.QueryFactory; +import de.chojo.sadu.queries.api.query.Query; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.sharding.ShardManager; import org.slf4j.Logger; -import javax.sql.DataSource; import java.util.List; import java.util.Objects; +import static de.chojo.sadu.queries.api.call.Call.call; import static org.slf4j.LoggerFactory.getLogger; -public class Gdpr extends QueryFactory { +public class Gdpr { private static final Logger log = getLogger(Gdpr.class); private final Configuration configuration; - public Gdpr(DataSource dataSource, Configuration configuration) { - super(dataSource); + public Gdpr(Configuration configuration) { this.configuration = configuration; } public List getRemovalTasks() { - return builder(RemovalTask.class) - .queryWithoutParams(""" - SELECT - task_id, - user_id, - guild_id - FROM - cleanup_schedule - WHERE delete_after < NOW(); - """) - .readRow(rs -> RemovalTask.build(this, rs)) - .allSync(); + return Query.query(""" + SELECT + task_id, + user_id, + guild_id + FROM + cleanup_schedule + WHERE delete_after < now(); + """) + .single() + .map(RemovalTask::build) + .all(); } public void cleanupRequests() { - builder() - .query(""" - DELETE FROM gdpr_log - WHERE received IS NOT NULL - AND received < NOW() - ?::INTERVAL; - """, configuration.cleanup().gdprDays()) - .parameter(stmt -> stmt.setString("%d DAYS".formatted(configuration.cleanup().gdprDays()))) - .update() - .sendSync(); + Query.query(""" + DELETE FROM gdpr_log + WHERE received IS NOT NULL + AND received < now() - ?::INTERVAL; + """, configuration.cleanup().gdprDays()) + .single(call().bind("%d DAYS".formatted(configuration.cleanup().gdprDays()))) + .update(); } public GdprUser request(User user) { - return new GdprUser(this, user); + return new GdprUser(user); } /** @@ -68,15 +65,16 @@ public GdprUser request(User user) { * @return list of users */ public List getReportRequests(ShardManager shardManager) { - return builder(GdprUser.class) - .queryWithoutParams(""" - SELECT user_id - FROM gdpr_log - WHERE received IS NULL - AND last_attempt < NOW() - (LEAST(48, attempts) || ' HOURS')::interval - """) - .readRow(rs -> GdprUser.build(this, rs, shardManager)) - .allSync() + return Query + .query(""" + SELECT user_id + FROM gdpr_log + WHERE received IS NULL + AND last_attempt < now() - (least(48, attempts) || ' HOURS')::INTERVAL + """) + .single() + .map(rs -> GdprUser.build(rs, shardManager)) + .all() .stream() .filter(Objects::nonNull) .toList(); diff --git a/src/main/java/de/chojo/repbot/dao/access/gdpr/GdprUser.java b/src/main/java/de/chojo/repbot/dao/access/gdpr/GdprUser.java index 03b4b231b..429c7a6d9 100644 --- a/src/main/java/de/chojo/repbot/dao/access/gdpr/GdprUser.java +++ b/src/main/java/de/chojo/repbot/dao/access/gdpr/GdprUser.java @@ -5,9 +5,9 @@ */ package de.chojo.repbot.dao.access.gdpr; -import de.chojo.repbot.dao.access.Gdpr; -import de.chojo.sadu.base.QueryFactory; -import de.chojo.sadu.wrapper.util.Row; +import de.chojo.sadu.mapper.wrapper.Row; +import de.chojo.sadu.queries.api.query.Query; +import de.chojo.sadu.queries.configuration.QueryConfiguration; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.entities.channel.concrete.PrivateChannel; import net.dv8tion.jda.api.sharding.ShardManager; @@ -21,20 +21,20 @@ import java.sql.SQLException; import java.util.Optional; +import static de.chojo.sadu.queries.api.call.Call.call; import static org.slf4j.LoggerFactory.getLogger; -public class GdprUser extends QueryFactory { +public class GdprUser { private static final Logger log = getLogger(GdprUser.class); private final User user; - public GdprUser(Gdpr gdpr, User user) { - super(gdpr); + public GdprUser(User user) { this.user = user; } @Nullable - public static GdprUser build(Gdpr gdpr, Row rs, ShardManager shardManager) throws SQLException { + public static GdprUser build(Row rs, ShardManager shardManager) throws SQLException { try { var user = shardManager.retrieveUserById(rs.getLong("user_id")).complete(); if (user == null) { @@ -42,7 +42,7 @@ public static GdprUser build(Gdpr gdpr, Row rs, ShardManager shardManager) throw return null; } - return new GdprUser(gdpr, user); + return new GdprUser(user); } catch (RuntimeException e) { log.info("Could not process gdpr request for user {}. User could not be retrieved.", rs.getLong("user_id")); return null; @@ -51,63 +51,56 @@ public static GdprUser build(Gdpr gdpr, Row rs, ShardManager shardManager) throw public boolean queueDeletion() { log.info("User {} requested deletion of their data", userId()); - return builder() - .query(""" - INSERT INTO - cleanup_schedule(user_id, delete_after) - VALUES (?, NOW()) - ON CONFLICT(guild_id, user_id) - DO NOTHING; - """) - .parameter(stmt -> stmt.setLong(userId())) - .update() - .sendSync() - .changed(); + return Query.query(""" + INSERT INTO + cleanup_schedule(user_id, delete_after) + VALUES (?, now()) + ON CONFLICT(guild_id, user_id) + DO NOTHING; + """) + .single(call().bind(userId())) + .update() + .changed(); } public boolean request() { - return builder() - .query(""" - DELETE FROM gdpr_log - WHERE user_id = ? - AND received IS NOT NULL - AND received < NOW() - INTERVAL '30 days'; - """) - .parameter(stmt -> stmt.setLong(userId())) - .append() - .query(""" - INSERT INTO gdpr_log(user_id) VALUES(?) - ON CONFLICT(user_id) - DO NOTHING; - """) - .parameter(stmt -> stmt.setLong(userId())) - .update() - .sendSync() - .changed(); + try (var conn = QueryConfiguration.getDefault().withSingleTransaction()) { + var res = conn.query(""" + DELETE FROM gdpr_log + WHERE user_id = ? + AND received IS NOT NULL + AND received < now() - INTERVAL '30 days'; + """) + .single(call().bind(userId())) + .delete(); + return conn.query(""" + INSERT INTO gdpr_log(user_id) VALUES(?) + ON CONFLICT(user_id) + DO NOTHING; + """) + .single(call().bind(userId())) + .update() + .changed() && res.changed(); + } } public void requestSend() { - builder() - .query("UPDATE gdpr_log SET received = NOW(), last_attempt = NOW() WHERE user_id = ?") - .parameter(stmt -> stmt.setLong(userId())) - .update() - .sendSync(); + Query.query("UPDATE gdpr_log SET received = now(), last_attempt = now() WHERE user_id = ?") + .single(call().bind(userId())) + .update(); } public void requestSendFailed() { - builder() - .query("UPDATE gdpr_log SET attempts = attempts + 1, last_attempt = NOW() WHERE user_id = ?") - .parameter(stmt -> stmt.setLong(userId())) - .update() - .sendSync(); + Query.query("UPDATE gdpr_log SET attempts = attempts + 1, last_attempt = now() WHERE user_id = ?") + .single(call().bind(userId())) + .update(); } public Optional userData() { - return builder(String.class) - .query("SELECT aggregate_user_data(?)") - .parameter(stmt -> stmt.setLong(userId())) - .readRow(rs -> rs.getString(1)) - .firstSync(); + return Query.query("SELECT aggregate_user_data(?)") + .single(call().bind(userId())) + .mapAs(String.class) + .first(); } /** diff --git a/src/main/java/de/chojo/repbot/dao/access/gdpr/RemovalTask.java b/src/main/java/de/chojo/repbot/dao/access/gdpr/RemovalTask.java index 85de81bd6..666260a6c 100644 --- a/src/main/java/de/chojo/repbot/dao/access/gdpr/RemovalTask.java +++ b/src/main/java/de/chojo/repbot/dao/access/gdpr/RemovalTask.java @@ -5,95 +5,69 @@ */ package de.chojo.repbot.dao.access.gdpr; -import de.chojo.repbot.dao.access.Gdpr; -import de.chojo.sadu.base.QueryFactory; -import de.chojo.sadu.wrapper.stage.ResultStage; -import de.chojo.sadu.wrapper.util.Row; +import de.chojo.sadu.mapper.wrapper.Row; +import de.chojo.sadu.queries.configuration.QueryConfiguration; import org.slf4j.Logger; import java.sql.SQLException; +import java.util.List; +import static de.chojo.sadu.queries.api.call.Call.call; import static org.slf4j.LoggerFactory.getLogger; -public final class RemovalTask extends QueryFactory { +public final class RemovalTask { private static final Logger log = getLogger(RemovalTask.class); private final long taskId; private final long guildId; private final long userId; - public RemovalTask(QueryFactory holder, long taskId, long guildId, long userId) { - super(holder); + public RemovalTask(long taskId, long guildId, long userId) { this.taskId = taskId; this.guildId = guildId; this.userId = userId; } - public static RemovalTask build(Gdpr gdpr, Row rs) throws SQLException { - return new RemovalTask(gdpr, rs.getLong("task_id"), rs.getLong("guild_id"), rs.getLong("user_id")); + public static RemovalTask build(Row rs) throws SQLException { + return new RemovalTask(rs.getLong("task_id"), rs.getLong("guild_id"), rs.getLong("user_id")); } - public static void anonymExecute(QueryFactory holder, long guildId, long userId) { - new RemovalTask(holder, -1L, guildId, userId).executeRemovalTask(); + public static void anonymExecute(long guildId, long userId) { + new RemovalTask(-1L, guildId, userId).executeRemovalTask(); } public void executeRemovalTask() { - ResultStage builder; - if (userId() == 0) { - // Remove guild - builder = builder().query("DELETE FROM reputation_log WHERE guild_id = ?;") - .parameter(stmt -> stmt.setLong(guildId())) - .append().query("DELETE FROM guild_settings WHERE guild_id = ?;") - .parameter(stmt -> stmt.setLong(guildId())) - .append().query("DELETE FROM active_channel WHERE guild_id = ?;") - .parameter(stmt -> stmt.setLong(guildId())) - .append().query("DELETE FROM abuse_protection WHERE guild_id = ?;") - .parameter(stmt -> stmt.setLong(guildId())) - .append().query("DELETE FROM active_categories WHERE guild_id = ?;") - .parameter(stmt -> stmt.setLong(guildId())) - .append().query("DELETE FROM reputation_settings WHERE guild_id = ?;") - .parameter(stmt -> stmt.setLong(guildId())) - .append().query("DELETE FROM guild_ranks WHERE guild_id = ?;") - .parameter(stmt -> stmt.setLong(guildId())) - .append().query("DELETE FROM thankwords WHERE guild_id = ?;") - .parameter(stmt -> stmt.setLong(guildId())) - .append().query("DELETE FROM thank_settings WHERE guild_id = ?;") - .parameter(stmt -> stmt.setLong(guildId())) - .append().query("DELETE FROM reputation_offset WHERE guild_id = ?;") - .parameter(stmt -> stmt.setLong(guildId())) - .append().query("DELETE FROM receiver_roles WHERE guild_id = ?;") - .parameter(stmt -> stmt.setLong(guildId())) - .append().query("DELETE FROM donor_roles WHERE guild_id = ?;") - .parameter(stmt -> stmt.setLong(guildId())) - .append().query("DELETE FROM guild_reactions WHERE guild_id = ?;") - .parameter(stmt -> stmt.setLong(guildId())) - .append().query("DELETE FROM announcements WHERE guild_id = ?;") - .parameter(stmt -> stmt.setLong(guildId())); - log.trace("Removed guild settings for {}", guildId()); - } else if (guildId() == 0) { - // Remove complete user - builder = builder().query("DELETE FROM reputation_log WHERE receiver_id = ?;") - .parameter(stmt -> stmt.setLong(userId())) - .append().query("UPDATE reputation_log SET donor_id = NULL WHERE donor_id = ?;") - .parameter(stmt -> stmt.setLong(userId())) - .append().query("DELETE FROM reputation_offset WHERE user_id = ?;") - .parameter(stmt -> stmt.setLong(userId())); - log.trace("Removed Data of user {}", userId()); - } else { - // Remove user from guild - builder = builder().query("DELETE FROM reputation_log WHERE guild_id = ? AND receiver_id = ?;") - .parameter(stmt -> stmt.setLong(guildId()).setLong(userId())) - .append() - .query("UPDATE reputation_log SET donor_id = NULL WHERE guild_id = ? AND donor_id = ?;") - .parameter(stmt -> stmt.setLong(guildId()).setLong(userId())) - .append().query("DELETE FROM reputation_offset WHERE guild_id = ? AND user_id = ?;") - .parameter(stmt -> stmt.setLong(guildId()).setLong(userId())); - log.trace("Removed user reputation from guild {} of user {}", guildId(), userId()); - } + try (var conn = QueryConfiguration.getDefault().withSingleTransaction()) { + if (userId() == 0) { + var tables = List.of("reputation_log", "guild_settings", "active_channel", "abuse_protection", + "active_categories", "reputation_settings", "guild_ranks", "thankwords", "thank_settings", + "reputation_offset", "receiver_roles", "donor_roles", "guild_reactions", "announcements"); + for (var table : tables) { + conn.query("DELETE FROM %s WHERE guild_id = ?;", table).single(call().bind(guildId)).delete(); + } + log.trace("Removed guild settings for {}", guildId()); + } else if (guildId() == 0) { + conn.query("DELETE FROM reputation_log WHERE receiver_id = ?;").single(call().bind(userId())).delete(); + conn.query("UPDATE reputation_log SET donor_id = NULL WHERE donor_id = ?;").single(call().bind(userId())).update(); + conn.query("DELETE FROM reputation_offset WHERE user_id = ?;").single(call().bind(userId())).delete(); + log.trace("Removed Data of user {}", userId()); + } else { + // Remove user from guild + conn.query("DELETE FROM reputation_log WHERE guild_id = ? AND receiver_id = ?;") + .single(call().bind(guildId()).bind(userId())) + .delete(); + conn.query("UPDATE reputation_log SET donor_id = NULL WHERE guild_id = ? AND donor_id = ?;") + .single(call().bind(guildId()).bind(userId())) + .delete(); + conn.query("DELETE FROM reputation_offset WHERE guild_id = ? AND user_id = ?;") + .single(call().bind(guildId()).bind(userId())) + .delete(); + log.trace("Removed user reputation from guild {} of user {}", guildId(), userId()); + } - builder.append().query("DELETE FROM cleanup_schedule WHERE task_id = ?;") - .parameter(stmt -> stmt.setLong(taskId())) - .update() - .sendSync(); + conn.query("DELETE FROM cleanup_schedule WHERE task_id = ?;") + .single(call().bind(taskId())) + .delete(); + } } public long taskId() { diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/Cleanup.java b/src/main/java/de/chojo/repbot/dao/access/guild/Cleanup.java index 7c28c5fe2..fe47822d7 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/Cleanup.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/Cleanup.java @@ -6,48 +6,44 @@ package de.chojo.repbot.dao.access.guild; import de.chojo.repbot.dao.components.GuildHolder; -import de.chojo.sadu.base.QueryFactory; import net.dv8tion.jda.api.entities.Guild; import java.time.LocalDateTime; import java.util.Optional; -public class Cleanup extends QueryFactory implements GuildHolder { +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; + +public class Cleanup implements GuildHolder { private final RepGuild repGuild; public Cleanup(RepGuild repGuild) { - super(repGuild); this.repGuild = repGuild; } public void selfCleanupPrompt() { - builder() - .query(""" + query(""" INSERT INTO self_cleanup(guild_id) VALUES(?) """) - .parameter(stmt -> stmt.setLong(guildId())) - .update() - .sendSync(); + .single(call().bind(guildId())) + .update(); } public Optional getCleanupPromptTime() { - return builder(LocalDateTime.class) - .query(""" + return query(""" SELECT prompted FROM self_cleanup WHERE guild_id = ? """) - .parameter(stmt -> stmt.setLong(guildId())) - .readRow(rs -> rs.getTimestamp("prompted").toLocalDateTime()) - .firstSync(); + .single(call().bind(guildId())) + .map(rs -> rs.getTimestamp("prompted").toLocalDateTime()) + .first(); } public void cleanupDone() { - builder(Boolean.class) - .query(""" + query(""" DELETE FROM self_cleanup WHERE guild_id = ? """) - .parameter(stmt -> stmt.setLong(guildId())) - .update() - .sendSync(); + .single(call().bind(guildId())) + .update(); } @Override diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/Gdpr.java b/src/main/java/de/chojo/repbot/dao/access/guild/Gdpr.java index be33ddd12..d5f4d32e5 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/Gdpr.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/Gdpr.java @@ -5,20 +5,19 @@ */ package de.chojo.repbot.dao.access.guild; -import de.chojo.repbot.config.Configuration; import de.chojo.repbot.dao.components.GuildHolder; -import de.chojo.sadu.base.QueryFactory; import net.dv8tion.jda.api.entities.Guild; import org.slf4j.Logger; +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; import static org.slf4j.LoggerFactory.getLogger; -public class Gdpr extends QueryFactory implements GuildHolder { +public class Gdpr implements GuildHolder { private final RepGuild repGuild; private static final Logger log = getLogger(Gdpr.class); public Gdpr(RepGuild repGuild) { - super(repGuild); this.repGuild = repGuild; } @@ -33,29 +32,24 @@ public long guildId() { } public void queueDeletion() { - if (builder() - .query(""" - INSERT INTO - cleanup_schedule(guild_id, user_id, delete_after) - VALUES (?, 0, now() + ?::INTERVAL) - ON CONFLICT(guild_id, user_id) - DO NOTHING; - """) - .parameter(stmt -> stmt.setLong(guildId()) - .setString("%d DAYS".formatted(repGuild.configuration().cleanup().cleanupScheduleDays()))) + if (query(""" + INSERT INTO + cleanup_schedule(guild_id, user_id, delete_after) + VALUES (?, 0, now() + ?::INTERVAL) + ON CONFLICT(guild_id, user_id) + DO NOTHING; + """) + .single(call().bind(guildId()).bind("%d DAYS".formatted(repGuild.configuration().cleanup().cleanupScheduleDays()))) .update() - .sendSync() .changed()) { log.debug("Queuing guild {} for deletion.", guildId()); } } public void dequeueDeletion() { - if (builder() - .query("DELETE FROM cleanup_schedule WHERE guild_id = ? AND user_id = 0;") - .parameter(stmt -> stmt.setLong(guildId())) + if (query("DELETE FROM cleanup_schedule WHERE guild_id = ? AND user_id = 0;") + .single(call().bind(guildId())) .update() - .sendSync() .changed()) { log.debug("Deletion of guild {} canceled.", guildId()); } diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/RepGuild.java b/src/main/java/de/chojo/repbot/dao/access/guild/RepGuild.java index be93f6bba..916087574 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/RepGuild.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/RepGuild.java @@ -11,16 +11,17 @@ import de.chojo.repbot.dao.access.guild.reputation.Reputation; import de.chojo.repbot.dao.access.guild.settings.Settings; import de.chojo.repbot.dao.components.GuildHolder; -import de.chojo.sadu.base.QueryFactory; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.sharding.ShardManager; -import javax.sql.DataSource; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; -public class RepGuild extends QueryFactory implements GuildHolder { +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; + +public class RepGuild implements GuildHolder { private static final Cache CLEANUPS = CacheBuilder.newBuilder() .expireAfterAccess(2, TimeUnit.MINUTES).build(); private static final Cache GDPR = CacheBuilder.newBuilder().expireAfterAccess(2, TimeUnit.MINUTES) @@ -30,8 +31,8 @@ public class RepGuild extends QueryFactory implements GuildHolder { private Guild guild; private final Configuration configuration; - public RepGuild(DataSource dataSource, Guild guild, Configuration configuration) { - super(dataSource); + public RepGuild(Guild guild, Configuration configuration) { + super(); this.configuration = configuration; reputation = new Reputation(this); settings = new Settings(this); @@ -68,30 +69,29 @@ public RepGuild refresh(Guild guild) { * @return list of user ids */ public List userIds() { - return builder(Long.class) - .query(""" - SELECT - user_id AS user_id - FROM - ( - SELECT - donor_id AS user_id - FROM - reputation_log - WHERE guild_id = ? - UNION - DISTINCT - SELECT - receiver_id AS user_id - FROM - reputation_log - WHERE guild_id = ? - ) users - WHERE user_id != 0 - """) - .parameter(stmt -> stmt.setLong(guildId()).setLong(guildId())) - .readRow(rs -> rs.getLong("user_id")) - .allSync(); + return query(""" + SELECT + user_id AS user_id + FROM + ( + SELECT + donor_id AS user_id + FROM + reputation_log + WHERE guild_id = ? + UNION + DISTINCT + SELECT + receiver_id AS user_id + FROM + reputation_log + WHERE guild_id = ? + ) users + WHERE user_id != 0 + """) + .single(call().bind(guildId()).bind(guildId())) + .mapAs(Long.class) + .all(); } public Reputation reputation() { @@ -117,8 +117,8 @@ public boolean isById() { @Override public String toString() { return "RepGuild{" + - "guild=" + guild + - '}'; + "guild=" + guild + + '}'; } public RepGuild load(ShardManager shardManager) { diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/RepGuildId.java b/src/main/java/de/chojo/repbot/dao/access/guild/RepGuildId.java index 7df1f753d..81511fc62 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/RepGuildId.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/RepGuildId.java @@ -12,8 +12,8 @@ public class RepGuildId extends RepGuild { private final long guildId; - public RepGuildId(DataSource dataSource, long guildId, Configuration configuration) { - super(dataSource, null, configuration); + public RepGuildId(long guildId, Configuration configuration) { + super(null, configuration); this.guildId = guildId; } diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/reputation/Reputation.java b/src/main/java/de/chojo/repbot/dao/access/guild/reputation/Reputation.java index fa4112da9..12c586563 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/reputation/Reputation.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/reputation/Reputation.java @@ -15,7 +15,6 @@ import de.chojo.repbot.dao.access.guild.reputation.sub.RepUser; import de.chojo.repbot.dao.components.GuildHolder; import de.chojo.repbot.dao.snapshots.GuildReputationStats; -import de.chojo.sadu.base.QueryFactory; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.User; @@ -25,9 +24,11 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; import static org.slf4j.LoggerFactory.getLogger; -public class Reputation extends QueryFactory implements GuildHolder { +public class Reputation implements GuildHolder { private static final Logger log = getLogger(Reputation.class); private final RepGuild repGuild; @@ -37,7 +38,6 @@ public class Reputation extends QueryFactory implements GuildHolder { private final Analyzer analyzer; public Reputation(RepGuild repGuild) { - super(repGuild); this.repGuild = repGuild; ranking = new Ranking(this); logAccess = new Log(this); @@ -63,15 +63,14 @@ public long guildId() { } public GuildReputationStats stats() { - return builder(GuildReputationStats.class) - .query("SELECT total_reputation, week_reputation, today_reputation, top_channel FROM get_guild_stats(?)") - .parameter(stmt -> stmt.setLong(guildId())) - .readRow(rs -> new GuildReputationStats( + return query("SELECT total_reputation, week_reputation, today_reputation, top_channel FROM get_guild_stats(?)") + .single(call().bind(guildId())) + .map(rs -> new GuildReputationStats( rs.getInt("total_reputation"), rs.getInt("week_reputation"), rs.getInt("today_reputation"), rs.getLong("top_channel") - )).firstSync() + )).first() .orElseGet(() -> new GuildReputationStats(0, 0, 0, 0)); } diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/Analyzer.java b/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/Analyzer.java index 7657c1e04..ffd94b47f 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/Analyzer.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/Analyzer.java @@ -20,7 +20,6 @@ import de.chojo.repbot.dao.snapshots.SubmitResultEntry; import de.chojo.repbot.dao.snapshots.analyzer.ResultSnapshot; import de.chojo.repbot.service.reputation.SubmitResult; -import de.chojo.sadu.base.QueryFactory; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Message; import org.slf4j.Logger; @@ -29,9 +28,11 @@ import java.util.List; import java.util.Optional; +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; import static org.slf4j.LoggerFactory.getLogger; -public class Analyzer extends QueryFactory implements GuildHolder { +public class Analyzer implements GuildHolder { private static final Logger log = getLogger(Analyzer.class); public static final ObjectMapper MAPPER = JsonMapper.builder() .configure(MapperFeature.ALLOW_FINAL_FIELDS_AS_MUTATORS, true) @@ -43,7 +44,6 @@ public class Analyzer extends QueryFactory implements GuildHolder { private final Reputation reputation; public Analyzer(Reputation reputation) { - super(reputation); this.reputation = reputation; } @@ -55,16 +55,15 @@ public AnalyzerResult log(Message message, AnalyzerResult analyzerResult) { log.error("Could not serialize result", e); return analyzerResult; } - builder().query(""" - INSERT INTO analyzer_results(guild_id, channel_id, message_id, result) VALUES(?, ?, ?, ?::JSONB) - ON CONFLICT (guild_id, message_id) - DO NOTHING; - """).parameter(stmt -> stmt.setLong(message.getGuild().getIdLong()) - .setLong(message.getChannel().getIdLong()) - .setLong(message.getIdLong()) - .setString(resultString)) - .insert() - .send(); + query(""" + INSERT INTO analyzer_results(guild_id, channel_id, message_id, result) VALUES(?, ?, ?, ?::JSONB) + ON CONFLICT (guild_id, message_id) + DO NOTHING; + """).single(call().bind(message.getGuild().getIdLong()) + .bind(message.getChannel().getIdLong()) + .bind(message.getIdLong()) + .bind(resultString)) + .insert(); return analyzerResult; } @@ -76,14 +75,13 @@ public void log(Message message, SubmitResult result) { log.error("Could not serialize result", e); return; } - builder().query(""" + query(""" INSERT INTO reputation_results(guild_id, channel_id, message_id, result) VALUES(?, ?, ?, ?::JSONB); - """).parameter(stmt -> stmt.setLong(message.getGuild().getIdLong()) - .setLong(message.getChannel().getIdLong()) - .setLong(message.getIdLong()) - .setString(resultString)) - .insert() - .send(); + """).single(call().bind(message.getGuild().getIdLong()) + .bind(message.getChannel().getIdLong()) + .bind(message.getIdLong()) + .bind(resultString)) + .insert(); } public Optional get(long messageId) { @@ -98,44 +96,42 @@ public Optional get(long messageId) { private Optional getResults(long messageId) { - return builder(ResultEntry.class) - .query(""" + return query(""" SELECT guild_id, channel_id, message_id, result, analyzed FROM analyzer_results WHERE guild_id = ? AND message_id = ?;""") - .parameter(stmt -> stmt.setLong(guildId()).setLong(messageId)) - .readRow(row -> { - ResultSnapshot result; - try { - result = MAPPER.readValue(row.getString("result"), ResultSnapshot.class); - } catch (JsonProcessingException e) { - log.error("Could not deserialize result", e); - throw new SQLException(e); - } - return new ResultEntry(result, row.getLong("channel_id"), messageId); - }).firstSync(); + .single(call().bind(guildId()).bind(messageId)) + .map(row -> { + ResultSnapshot result; + try { + result = MAPPER.readValue(row.getString("result"), ResultSnapshot.class); + } catch (JsonProcessingException e) { + log.error("Could not deserialize result", e); + throw new SQLException(e); + } + return new ResultEntry(result, row.getLong("channel_id"), messageId); + }).first(); } private List getSubmitResults(long messageId) { - return builder(SubmitResultEntry.class) - .query(""" + return query(""" SELECT guild_id, channel_id, message_id, result, submitted FROM reputation_results WHERE guild_id = ? AND message_id = ? ORDER BY submitted;""") - .parameter(stmt -> stmt.setLong(guildId()).setLong(messageId)) - .readRow(row -> { - SubmitResult result; - try { - result = MAPPER.readValue(row.getString("result"), SubmitResult.class); - } catch (JsonProcessingException e) { - log.error("Could not deserialize result", e); - throw new SQLException(e); - } - return new SubmitResultEntry(result, row.getLong("channel_id"), messageId, row.getTimestamp("submitted").toInstant()); - }).allSync(); + .single(call().bind(guildId()).bind(messageId)) + .map(row -> { + SubmitResult result; + try { + result = MAPPER.readValue(row.getString("result"), SubmitResult.class); + } catch (JsonProcessingException e) { + log.error("Could not deserialize result", e); + throw new SQLException(e); + } + return new SubmitResultEntry(result, row.getLong("channel_id"), messageId, row.getTimestamp("submitted").toInstant()); + }).all(); } @Override diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/Log.java b/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/Log.java index 1ab4bf3a2..1e7d11c03 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/Log.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/Log.java @@ -9,7 +9,6 @@ import de.chojo.repbot.dao.components.GuildHolder; import de.chojo.repbot.dao.pagination.ReputationLogAccess; import de.chojo.repbot.dao.snapshots.ReputationLogEntry; -import de.chojo.sadu.base.QueryFactory; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.User; @@ -17,11 +16,13 @@ import java.util.List; import java.util.Optional; -public class Log extends QueryFactory implements GuildHolder { +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; + +public class Log implements GuildHolder { private final Reputation reputation; public Log(Reputation reputation) { - super(reputation); this.reputation = reputation; } @@ -70,8 +71,7 @@ public List messageLog(long messageId, int count) { } private List getLog(String column, long id, int pageSize, int page) { - return builder(ReputationLogEntry.class) - .query(""" + return query(""" SELECT guild_id, donor_id, @@ -90,14 +90,13 @@ private List getLog(String column, long id, int pageSize, in OFFSET ? LIMIT ?; """, column) - .parameter(stmt -> stmt.setLong(id).setLong(guildId()).setInt(page * pageSize).setInt(pageSize)) - .readRow(r -> ReputationLogEntry.build(this, r)) - .allSync(); + .single(call().bind(id).bind(guildId()).bind(page * pageSize).bind(pageSize)) + .map(r -> ReputationLogEntry.build(this, r)) + .all(); } public Optional getLatestReputation() { - return builder(ReputationLogEntry.class) - .query(""" + return query(""" SELECT guild_id, donor_id, @@ -111,9 +110,9 @@ public Optional getLatestReputation() { WHERE guild_id = ? ORDER BY received DESC LIMIT 1; - """).parameter(stmt -> stmt.setLong(guildId())) - .readRow(r -> ReputationLogEntry.build(this, r)) - .firstSync(); + """).single(call().bind(guildId())) + .map(r -> ReputationLogEntry.build(this, r)) + .first(); } /** @@ -123,8 +122,7 @@ public Optional getLatestReputation() { * @return a log entry if found */ public Optional getLogEntry(long message) { - return builder(ReputationLogEntry.class) - .query(""" + return query(""" SELECT guild_id, donor_id, @@ -140,9 +138,9 @@ public Optional getLogEntry(long message) { message_id = ? AND guild_id = ?; """) - .parameter(stmt -> stmt.setLong(message).setLong(guildId())) - .readRow(r -> ReputationLogEntry.build(this, r)) - .firstSync(); + .single(call().bind(message).bind(guildId())) + .map(r -> ReputationLogEntry.build(this, r)) + .first(); } /** @@ -164,8 +162,7 @@ private int getUserReceivedLogPages(User user, int pageSize) { } private int getLogPages(String column, long id, int pageSize) { - return builder(Integer.class) - .query(""" + return query(""" SELECT CEIL(COUNT(1)::numeric / ?) AS count FROM @@ -173,9 +170,9 @@ private int getLogPages(String column, long id, int pageSize) { WHERE guild_id = ? AND %s = ?; """, column) - .parameter(stmt -> stmt.setInt(pageSize).setLong(guildId()).setLong(id)) - .readRow(row -> row.getInt("count")) - .firstSync() + .single(call().bind(pageSize).bind(guildId()).bind(id)) + .map(row -> row.getInt("count")) + .first() .orElse(1); } diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/Ranking.java b/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/Ranking.java index f2054030a..6b582350c 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/Ranking.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/Ranking.java @@ -10,16 +10,17 @@ import de.chojo.repbot.dao.components.GuildHolder; import de.chojo.repbot.dao.pagination.GuildRanking; import de.chojo.repbot.dao.snapshots.RepProfile; -import de.chojo.sadu.base.QueryFactory; import net.dv8tion.jda.api.entities.Guild; import java.util.List; -public class Ranking extends QueryFactory implements GuildHolder { +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; + +public class Ranking implements GuildHolder { private final Reputation reputation; public Ranking(Reputation reputation) { - super(reputation); this.reputation = reputation; } @@ -44,18 +45,17 @@ private Integer getMonthRankingPageCount(int pageSize) { } private Integer pages(int pageSize, String table) { - return builder(Integer.class) - .query(""" - SELECT - CEIL(COUNT(1)::numeric / ?) AS count - FROM - %s - WHERE guild_id = ? - AND reputation != 0; - """, table) - .parameter(stmt -> stmt.setInt(pageSize).setLong(guildId())) - .readRow(row -> row.getInt("count")) - .firstSync() + return query(""" + SELECT + ceil(count(1)::NUMERIC / ?) AS count + FROM + %s + WHERE guild_id = ? + AND reputation != 0; + """, table) + .single(call().bind(pageSize).bind(guildId())) + .map(row -> row.getInt("count")) + .first() .orElse(1); } @@ -152,23 +152,22 @@ private List getMonthRankingPage(int pageSize, int page) { } private List getRankingPage(int pageSize, int page, String table) { - return builder(RepProfile.class) - .query(""" - SELECT - rank, - user_id, - reputation - FROM - %s - WHERE guild_id = ? - AND reputation != 0 - ORDER BY reputation DESC - OFFSET ? - LIMIT ?; - """, table) - .parameter(stmt -> stmt.setLong(guildId()).setInt(page * pageSize).setInt(pageSize)) - .readRow(RepProfile::buildReceivedRanking) - .allSync(); + return query(""" + SELECT + rank, + user_id, + reputation + FROM + %s + WHERE guild_id = ? + AND reputation != 0 + ORDER BY reputation DESC + OFFSET ? + LIMIT ?; + """, table) + .single(call().bind(guildId()).bind(page * pageSize).bind(pageSize)) + .map(RepProfile::buildReceivedRanking) + .all(); } @Override diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/RepUser.java b/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/RepUser.java index d56919674..418176670 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/RepUser.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/RepUser.java @@ -12,12 +12,11 @@ import de.chojo.repbot.dao.access.guild.settings.sub.AbuseProtection; import de.chojo.repbot.dao.components.MemberHolder; import de.chojo.repbot.dao.snapshots.RepProfile; -import de.chojo.sadu.base.QueryFactory; -import de.chojo.sadu.wrapper.stage.StatementStage; 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 org.intellij.lang.annotations.Language; import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; @@ -27,9 +26,11 @@ import java.time.Instant; import java.util.Optional; +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; import static org.slf4j.LoggerFactory.getLogger; -public class RepUser extends QueryFactory implements MemberHolder { +public class RepUser implements MemberHolder { private static final Logger log = getLogger(RepUser.class); private final Reputation reputation; private final Gdpr gdpr; @@ -37,7 +38,6 @@ public class RepUser extends QueryFactory implements MemberHolder { private Member member; public RepUser(Reputation reputation, Member member) { - super(reputation); gdpr = new Gdpr(this); this.reputation = reputation; this.member = member; @@ -45,7 +45,6 @@ public RepUser(Reputation reputation, Member member) { } public RepUser(Reputation reputation, User user) { - super(reputation); gdpr = new Gdpr(this); this.reputation = reputation; this.user = user; @@ -62,13 +61,11 @@ public Gdpr gdpr() { * @return true if added */ public boolean addReputation(long amount) { - return builder() - .query(""" - INSERT INTO reputation_offset(guild_id, user_id, amount) VALUES (?,?,?) - """) - .parameter(stmt -> stmt.setLong(guildId()).setLong(userId()).setLong(amount)) + return query(""" + INSERT INTO reputation_offset(guild_id, user_id, amount) VALUES (?,?,?) + """) + .single(call().bind(guildId()).bind(userId()).bind(amount)) .insert() - .sendSync() .changed(); } @@ -90,13 +87,11 @@ public boolean removeReputation(long amount) { */ public boolean setReputation(long amount) { var offset = amount - profile().reputation(); - return builder() - .query(""" - INSERT INTO reputation_offset(guild_id, user_id, amount) VALUES (?,?,?) - """) - .parameter(stmt -> stmt.setLong(guildId()).setLong(userId()).setLong(offset)) + return query(""" + INSERT INTO reputation_offset(guild_id, user_id, amount) VALUES (?,?,?) + """) + .single(call().bind(guildId()).bind(userId()).bind(offset)) .insert() - .sendSync() .changed(); } @@ -110,20 +105,18 @@ INSERT INTO reputation_offset(guild_id, user_id, amount) VALUES (?,?,?) * @return true if the repuation was logged. */ public boolean addReputation(@Nullable Member donor, @NotNull Message message, @Nullable Message refMessage, ThankType type) { - var success = builder() - .query(""" - INSERT INTO - reputation_log(guild_id, donor_id, receiver_id, message_id, ref_message_id, channel_id, cause) VALUES(?,?,?,?,?,?,?) - ON CONFLICT(guild_id, donor_id, receiver_id, message_id) - DO NOTHING; - """) - .parameter(stmt -> stmt.setLong(guildId()).setLong(donor == null ? 0 : donor.getIdLong()) - .setLong(userId()) - .setLong(message.getIdLong()) - .setLong(refMessage == null ? null : refMessage.getIdLong()) - .setLong(message.getChannel().getIdLong()).setString(type.name())) + var success = query(""" + INSERT INTO + reputation_log(guild_id, donor_id, receiver_id, message_id, ref_message_id, channel_id, cause) VALUES(?,?,?,?,?,?,?) + ON CONFLICT(guild_id, donor_id, receiver_id, message_id) + DO NOTHING; + """) + .single(call().bind(guildId()).bind(donor == null ? 0 : donor.getIdLong()) + .bind(userId()) + .bind(message.getIdLong()) + .bind(refMessage == null ? null : refMessage.getIdLong()) + .bind(message.getChannel().getIdLong()).bind(type.name())) .insert() - .sendSync() .changed(); if (success) { log.debug("{} received one reputation from {} on guild {} for message {}", userId(), donor != null ? donor.getIdLong() : "unkown", guildId(), message.getIdLong()); @@ -143,23 +136,21 @@ ON CONFLICT(guild_id, donor_id, receiver_id, message_id) * @return true if the repuation was logged. */ public boolean addOldReputation(@Nullable Member donor, @NotNull Message message, @Nullable Message refMessage, ThankType type) { - var success = builder() - .query(""" - INSERT INTO - reputation_log(guild_id, donor_id, receiver_id, message_id, ref_message_id, channel_id, cause, received) VALUES(?,?,?,?,?,?,?,?) - ON CONFLICT(guild_id, donor_id, receiver_id, message_id) - DO NOTHING; - """) - .parameter(stmt -> stmt.setLong(guildId()) - .setLong(donor == null ? 0 : donor.getIdLong()) - .setLong(userId()) - .setLong(message.getIdLong()) - .setLong(refMessage == null ? null : refMessage.getIdLong()) - .setLong(message.getChannel().getIdLong()) - .setString(type.name()) - .setTimestamp(Timestamp.from(message.getTimeCreated().toInstant()))) + var success = query(""" + INSERT INTO + reputation_log(guild_id, donor_id, receiver_id, message_id, ref_message_id, channel_id, cause, received) VALUES(?,?,?,?,?,?,?,?) + ON CONFLICT(guild_id, donor_id, receiver_id, message_id) + DO NOTHING; + """) + .single(call().bind(guildId()) + .bind(donor == null ? 0 : donor.getIdLong()) + .bind(userId()) + .bind(message.getIdLong()) + .bind(refMessage == null ? null : refMessage.getIdLong()) + .bind(message.getChannel().getIdLong()) + .bind(type.name()) + .bind(Timestamp.from(message.getTimeCreated().toInstant()))) .insert() - .sendSync() .changed(); if (success) { log.debug("{} received one reputation from {} for message {}", user().getName(), donor != null ? donor.getEffectiveName() : "unkown", message.getIdLong()); @@ -175,26 +166,26 @@ ON CONFLICT(guild_id, donor_id, receiver_id, message_id) * @return last timestamp as instant */ public Optional getLastReputation(Member other) { - return builder(Instant.class). + return query(""" - SELECT - received - FROM - reputation_log - WHERE - guild_id = ? - AND ((donor_id = ? AND receiver_id = ?) - OR (donor_id = ? AND receiver_id = ?)) - ORDER BY received DESC - LIMIT 1; - """) - .parameter(stmt -> stmt.setLong(reputation.guildId()) - .setLong(userId()) - .setLong(other.getIdLong()) - .setLong(other.getIdLong()) - .setLong(userId())) - .readRow(row -> row.getTimestamp("received").toInstant()) - .firstSync(); + SELECT + received + FROM + reputation_log + WHERE + guild_id = ? + AND ((donor_id = ? AND receiver_id = ?) + OR (donor_id = ? AND receiver_id = ?)) + ORDER BY received DESC + LIMIT 1; + """) + .single(call().bind(reputation.guildId()) + .bind(userId()) + .bind(other.getIdLong()) + .bind(other.getIdLong()) + .bind(userId())) + .map(row -> row.getTimestamp("received").toInstant()) + .first(); } /** @@ -216,28 +207,27 @@ public Duration getLastRatedDuration(Member other) { public RepProfile profile() { var mode = reputation.repGuild().settings().general().reputationMode(); // We probably don't want to cache the profile. There are just too many factors which can change the user reputation. - var builder = builder(RepProfile.class); - StatementStage query; + @Language("postgresql") + String query; if (mode.isSupportsOffset()) { - query = builder - .query(""" - SELECT rank, rank_donated, user_id, reputation, rep_offset, raw_reputation, donated - FROM %s - WHERE guild_id = ? AND user_id = ?; - """, mode.tableName()); + query = """ + SELECT rank, rank_donated, user_id, reputation, rep_offset, raw_reputation, donated + FROM %s + WHERE guild_id = ? AND user_id = ?; + """; } else { - query = builder - .query(""" - SELECT rank, rank_donated, user_id, reputation, 0 AS rep_offset, reputation AS raw_reputation, donated - FROM %s - WHERE guild_id = ? AND user_id = ?; - """, mode.tableName()); + query = """ + SELECT rank, rank_donated, user_id, reputation, 0 AS rep_offset, reputation AS raw_reputation, donated + FROM %s + WHERE guild_id = ? AND user_id = ?; + """; } - return query.parameter(stmt -> stmt.setLong(guildId()).setLong(userId())) - .readRow(row -> RepProfile.buildProfile(this, row)) - .firstSync() - .orElseGet(() -> RepProfile.empty(this, user())); + return query(query, mode.tableName()) + .single(call().bind(guildId()).bind(userId())) + .map(row -> RepProfile.buildProfile(this, row)) + .first() + .orElseGet(() -> RepProfile.empty(this, user())); } @@ -248,11 +238,10 @@ public RepProfile profile() { */ public int countReceived() { var hours = reputation().repGuild().settings().abuseProtection().maxReceivedHours(); - return builder(Integer.class) - .query("SELECT COUNT(1) FROM reputation_log WHERE received > NOW() - ?::interval AND receiver_id = ?") - .parameter(stmt -> stmt.setString("%s hours".formatted(hours)).setLong(memberId())) - .readRow(rs -> rs.getInt(1)) - .firstSync() + return query("SELECT COUNT(1) FROM reputation_log WHERE received > NOW() - ?::interval AND receiver_id = ?") + .single(call().bind("%s hours".formatted(hours)).bind(memberId())) + .map(rs -> rs.getInt(1)) + .first() .orElse(0); } @@ -263,11 +252,10 @@ public int countReceived() { */ public int countGiven() { var hours = reputation().repGuild().settings().abuseProtection().maxGivenHours(); - return builder(Integer.class) - .query("SELECT COUNT(1) FROM reputation_log WHERE received > NOW() - ?::interval AND donor_id = ?") - .parameter(stmt -> stmt.setString("%s hours".formatted(hours)).setLong(memberId())) - .readRow(rs -> rs.getInt(1)) - .firstSync() + return query("SELECT COUNT(1) FROM reputation_log WHERE received > NOW() - ?::interval AND donor_id = ?") + .single(call().bind("%s hours".formatted(hours)).bind(memberId())) + .map(rs -> rs.getInt(1)) + .first() .orElse(0); } diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/user/Gdpr.java b/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/user/Gdpr.java index f9b9f5741..77f62660b 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/user/Gdpr.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/reputation/sub/user/Gdpr.java @@ -5,23 +5,22 @@ */ package de.chojo.repbot.dao.access.guild.reputation.sub.user; -import de.chojo.repbot.config.Configuration; import de.chojo.repbot.dao.access.guild.reputation.sub.RepUser; import de.chojo.repbot.dao.components.MemberHolder; -import de.chojo.sadu.base.QueryFactory; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.User; import org.slf4j.Logger; +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; import static org.slf4j.LoggerFactory.getLogger; -public class Gdpr extends QueryFactory implements MemberHolder { +public class Gdpr implements MemberHolder { private final RepUser repUser; private static final Logger log = getLogger(Gdpr.class); public Gdpr(RepUser repUser) { - super(repUser); this.repUser = repUser; } @@ -32,32 +31,28 @@ public Member member() { public void queueDeletion() { log.info("User {} is scheduled for deletion on guild {}", userId(), guildId()); - builder() - .query(""" + query(""" INSERT INTO cleanup_schedule(guild_id, user_id, delete_after) VALUES (?,?,now() + ?::INTERVAL) ON CONFLICT(guild_id, user_id) DO NOTHING; """, repUser.configuration().cleanup().cleanupScheduleDays()) - .parameter(stmt -> stmt.setLong(guildId()) - .setLong(userId()) - .setString("%d DAYS".formatted(repUser.configuration().cleanup().cleanupScheduleDays()))) - .update() - .sendSync(); + .single(call().bind(guildId()) + .bind(userId()) + .bind("%d DAYS".formatted(repUser.configuration().cleanup().cleanupScheduleDays()))) + .update(); } public void dequeueDeletion() { - if (builder() - .query(""" + if (query(""" DELETE FROM cleanup_schedule WHERE guild_id = ? AND user_id = ?; """) - .parameter(stmt -> stmt.setLong(guildId()).setLong(userId())) + .single(call().bind(guildId()).bind(userId())) .update() - .sendSync() .changed()) { log.info("User {} deletion on guild {} canceled", userId(), guildId()); } diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/settings/Settings.java b/src/main/java/de/chojo/repbot/dao/access/guild/settings/Settings.java index 43cdcbda0..8a6b58afb 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/settings/Settings.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/settings/Settings.java @@ -13,11 +13,13 @@ import de.chojo.repbot.dao.access.guild.settings.sub.Reputation; import de.chojo.repbot.dao.access.guild.settings.sub.Thanking; import de.chojo.repbot.dao.components.GuildHolder; -import de.chojo.repbot.dao.pagination.Announcements; -import de.chojo.sadu.base.QueryFactory; +import de.chojo.repbot.dao.access.guild.settings.sub.Announcements; import net.dv8tion.jda.api.entities.Guild; -public class Settings extends QueryFactory implements GuildHolder { +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; + +public class Settings implements GuildHolder { private final RepGuild repGuild; private AbuseProtection abuseProtection; private General general; @@ -28,7 +30,6 @@ public class Settings extends QueryFactory implements GuildHolder { private Messages messages; public Settings(RepGuild repGuild) { - super(repGuild); this.repGuild = repGuild; } @@ -36,26 +37,25 @@ public AbuseProtection abuseProtection() { if (abuseProtection != null) { return abuseProtection; } - abuseProtection = builder(AbuseProtection.class) - .query(""" - SELECT - min_messages, - max_message_age, - receiver_context, - donor_context, - cooldown, - max_given, - max_given_hours, - max_received, - max_received_hours, - max_message_reputation - FROM - abuse_protection - WHERE guild_id = ?; - """) - .parameter(stmt -> stmt.setLong(guildId())) - .readRow(rs -> AbuseProtection.build(this, rs)) - .firstSync() + abuseProtection = query(""" + SELECT + min_messages, + max_message_age, + receiver_context, + donor_context, + cooldown, + max_given, + max_given_hours, + max_received, + max_received_hours, + max_message_reputation + FROM + abuse_protection + WHERE guild_id = ?; + """) + .single(call().bind(guildId())) + .map(rs -> AbuseProtection.build(this, rs)) + .first() .orElseGet(() -> new AbuseProtection(this)); return abuseProtection; } @@ -64,19 +64,18 @@ public Announcements announcements() { if (announcements != null) { return announcements; } - announcements = builder(Announcements.class) - .query(""" - SELECT - active, - same_channel, - channel_id - FROM - announcements - WHERE guild_id = ?; - """) - .parameter(stmt -> stmt.setLong(guildId())) - .readRow(rs -> Announcements.build(this, rs)) - .firstSync() + announcements = query(""" + SELECT + active, + same_channel, + channel_id + FROM + announcements + WHERE guild_id = ?; + """) + .single(call().bind(guildId())) + .map(rs -> Announcements.build(this, rs)) + .first() .orElseGet(() -> new Announcements(this)); return announcements; } @@ -85,22 +84,21 @@ public Reputation reputation() { if (reputation != null) { return reputation; } - reputation = builder(Reputation.class) - .query(""" - SELECT - reactions_active, - answer_active, - mention_active, - fuzzy_active, - embed_active, - skip_single_embed - FROM - reputation_settings - WHERE guild_id = ?; - """) - .parameter(stmt -> stmt.setLong(guildId())) - .readRow(rs -> Reputation.build(this, rs)) - .firstSync() + reputation = query(""" + SELECT + reactions_active, + answer_active, + mention_active, + fuzzy_active, + embed_active, + skip_single_embed + FROM + reputation_settings + WHERE guild_id = ?; + """) + .single(call().bind(guildId())) + .map(rs -> Reputation.build(this, rs)) + .first() .orElseGet(() -> new Reputation(this)); return reputation; } @@ -109,21 +107,20 @@ public General general() { if (general != null) { return general; } - general = builder(General.class) - .query(""" - SELECT - language, - emoji_debug, - stack_roles, - reputation_mode, - reset_date - FROM - guild_settings - WHERE guild_id = ?; - """) - .parameter(stmt -> stmt.setLong(guildId())) - .readRow(rs -> General.build(this, rs)) - .firstSync() + general = query(""" + SELECT + language, + emoji_debug, + stack_roles, + reputation_mode, + reset_date + FROM + guild_settings + WHERE guild_id = ?; + """) + .single(call().bind(guildId())) + .map(rs -> General.build(this, rs)) + .first() .orElseGet(() -> new General(this)); return general; } @@ -132,18 +129,17 @@ public Thanking thanking() { if (thanking != null) { return thanking; } - thanking = builder(Thanking.class) - .query(""" - SELECT - reaction, - channel_whitelist - FROM - thank_settings - WHERE guild_id = ?; - """) - .parameter(stmt -> stmt.setLong(guildId())) - .readRow(rs -> Thanking.build(this, rs)) - .firstSync() + thanking = query(""" + SELECT + reaction, + channel_whitelist + FROM + thank_settings + WHERE guild_id = ?; + """) + .single(call().bind(guildId())) + .map(rs -> Thanking.build(this, rs)) + .first() .orElseGet(() -> new Thanking(this)); return thanking; } @@ -152,17 +148,16 @@ public Messages messages() { if (messages != null) { return messages; } - messages = builder(Messages.class) - .query(""" - SELECT - reaction_confirmation - FROM - message_states - WHERE guild_id = ?; - """) - .parameter(stmt -> stmt.setLong(guildId())) - .readRow(rs -> Messages.build(this, rs)) - .firstSync() + messages = query(""" + SELECT + reaction_confirmation + FROM + message_states + WHERE guild_id = ?; + """) + .single(call().bind(guildId())) + .map(rs -> Messages.build(this, rs)) + .first() .orElseGet(() -> new Messages(this)); return messages; } diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/AbuseProtection.java b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/AbuseProtection.java index ecb66cd1b..26cb0731c 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/AbuseProtection.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/AbuseProtection.java @@ -5,12 +5,10 @@ */ package de.chojo.repbot.dao.access.guild.settings.sub; -import de.chojo.jdautil.consumer.ThrowingConsumer; import de.chojo.repbot.dao.access.guild.settings.Settings; import de.chojo.repbot.dao.components.GuildHolder; -import de.chojo.sadu.base.QueryFactory; -import de.chojo.sadu.wrapper.util.ParamBuilder; -import de.chojo.sadu.wrapper.util.Row; +import de.chojo.sadu.mapper.wrapper.Row; +import de.chojo.sadu.queries.api.call.Call; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; @@ -18,8 +16,12 @@ import java.sql.SQLException; import java.time.Instant; import java.time.temporal.ChronoUnit; +import java.util.function.Function; -public class AbuseProtection extends QueryFactory implements GuildHolder { +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; + +public class AbuseProtection implements GuildHolder { private final Settings settings; private int cooldown; private int maxMessageAge; @@ -34,7 +36,6 @@ public class AbuseProtection extends QueryFactory implements GuildHolder { public AbuseProtection(Settings settings, int cooldown, int maxMessageAge, int minMessages, boolean donorContext, boolean receiverContext, int maxGiven, int maxGivenHours, int maxReceived, int maxReceivedHours, int maxMessageReputation) { - super(settings); this.settings = settings; this.cooldown = cooldown; this.maxMessageAge = maxMessageAge; @@ -107,42 +108,42 @@ public int maxReceivedHours() { } public int cooldown(int cooldown) { - if (set("cooldown", stmt -> stmt.setInt(cooldown))) { + if (set("cooldown", stmt -> stmt.bind(cooldown))) { this.cooldown = cooldown; } return this.cooldown; } public int maxMessageAge(int maxMessageAge) { - if (set("max_message_age", stmt -> stmt.setInt(maxMessageAge))) { + if (set("max_message_age", stmt -> stmt.bind(maxMessageAge))) { this.maxMessageAge = maxMessageAge; } return this.maxMessageAge; } public int minMessages(int minMessages) { - if (set("min_messages", stmt -> stmt.setInt(minMessages))) { + if (set("min_messages", stmt -> stmt.bind(minMessages))) { this.minMessages = minMessages; } return this.minMessages; } public boolean donorContext(boolean donorContext) { - if (set("donor_context", stmt -> stmt.setBoolean(donorContext))) { + if (set("donor_context", stmt -> stmt.bind(donorContext))) { this.donorContext = donorContext; } return this.donorContext; } public boolean receiverContext(boolean receiverContext) { - if (set("receiver_context", stmt -> stmt.setBoolean(receiverContext))) { + if (set("receiver_context", stmt -> stmt.bind(receiverContext))) { this.receiverContext = receiverContext; } return this.receiverContext; } public int maxGiven(int maxGiven) { - var result = set("max_given", stmt -> stmt.setInt(Math.max(maxGiven, 0))); + var result = set("max_given", stmt -> stmt.bind(Math.max(maxGiven, 0))); if (result) { this.maxGiven = Math.max(maxGiven, 0); } @@ -150,7 +151,7 @@ public int maxGiven(int maxGiven) { } public int maxGivenHours(int maxGivenHours) { - var result = set("max_given_hours", stmt -> stmt.setInt(Math.max(maxGivenHours, 1))); + var result = set("max_given_hours", stmt -> stmt.bind(Math.max(maxGivenHours, 1))); if (result) { this.maxGivenHours = Math.max(maxGivenHours, 1); } @@ -158,7 +159,7 @@ public int maxGivenHours(int maxGivenHours) { } public int maxReceived(int maxReceived) { - var result = set("max_received", stmt -> stmt.setInt(Math.max(maxReceived, 0))); + var result = set("max_received", stmt -> stmt.bind(Math.max(maxReceived, 0))); if (result) { this.maxReceived = Math.max(maxReceived, 0); } @@ -166,7 +167,7 @@ public int maxReceived(int maxReceived) { } public int maxReceivedHours(int maxReceivedHours) { - var result = set("max_received_hours", stmt -> stmt.setInt(Math.max(maxReceivedHours, 1))); + var result = set("max_received_hours", stmt -> stmt.bind(Math.max(maxReceivedHours, 1))); if (result) { this.maxReceivedHours = Math.max(maxReceivedHours, 1); } @@ -174,7 +175,7 @@ public int maxReceivedHours(int maxReceivedHours) { } public int maxMessageReputation(int maxMessageReputation) { - if (set("max_message_reputation", stmt -> stmt.setInt(maxMessageReputation))) { + if (set("max_message_reputation", stmt -> stmt.bind(maxMessageReputation))) { this.maxMessageReputation = maxMessageReputation; } return this.maxMessageReputation; @@ -216,18 +217,14 @@ public boolean isReceiverLimit() { return maxReceived != 0; } - private boolean set(String parameter, ThrowingConsumer builder) { - return builder() - .query(""" + private boolean set(String parameter, Function builder) { + return query(""" INSERT INTO abuse_protection(guild_id, %s) VALUES (?, ?) ON CONFLICT(guild_id) DO UPDATE SET %s = excluded.%s; """, parameter, parameter, parameter) - .parameter(stmts -> { - stmts.setLong(guildId()); - builder.accept(stmts); - }).insert() - .sendSync() + .single(builder.apply(call().bind(guildId()))) + .insert() .changed(); } diff --git a/src/main/java/de/chojo/repbot/dao/pagination/Announcements.java b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Announcements.java similarity index 73% rename from src/main/java/de/chojo/repbot/dao/pagination/Announcements.java rename to src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Announcements.java index 66a9fa79a..58d90e98e 100644 --- a/src/main/java/de/chojo/repbot/dao/pagination/Announcements.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Announcements.java @@ -3,27 +3,28 @@ * * Copyright (C) RainbowDashLabs and Contributor */ -package de.chojo.repbot.dao.pagination; +package de.chojo.repbot.dao.access.guild.settings.sub; -import de.chojo.jdautil.consumer.ThrowingConsumer; import de.chojo.repbot.dao.access.guild.settings.Settings; import de.chojo.repbot.dao.components.GuildHolder; -import de.chojo.sadu.base.QueryFactory; -import de.chojo.sadu.wrapper.util.ParamBuilder; -import de.chojo.sadu.wrapper.util.Row; +import de.chojo.sadu.mapper.wrapper.Row; +import de.chojo.sadu.queries.api.call.Call; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.channel.concrete.TextChannel; import java.sql.SQLException; +import java.util.function.Function; -public class Announcements extends QueryFactory implements GuildHolder { +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; + +public class Announcements implements GuildHolder { private final Settings settings; private boolean active = false; private boolean sameChannel = true; private long channelId = 0; private Announcements(Settings settings, boolean active, boolean sameChannel, long channelId) { - super(settings); this.settings = settings; this.active = active; this.sameChannel = sameChannel; @@ -31,7 +32,6 @@ private Announcements(Settings settings, boolean active, boolean sameChannel, lo } public Announcements(Settings settings) { - super(settings); this.settings = settings; } @@ -55,38 +55,34 @@ public long channelId() { } public boolean active(boolean active) { - if (set("active", stmt -> stmt.setBoolean(active))) { + if (set("active", stmt -> stmt.bind(active))) { this.active = active; } return this.active; } public boolean sameChannel(boolean sameChannel) { - if (set("same_channel", stmt -> stmt.setBoolean(sameChannel))) { + if (set("same_channel", stmt -> stmt.bind(sameChannel))) { this.sameChannel = sameChannel; } return this.sameChannel; } public long channel(TextChannel textChannel) { - if (set("channel_id", stmt -> stmt.setLong(textChannel.getIdLong()))) { + if (set("channel_id", stmt -> stmt.bind(textChannel.getIdLong()))) { channelId = textChannel.getIdLong(); } return channelId; } - private boolean set(String parameter, ThrowingConsumer builder) { - return builder() - .query(""" + private boolean set(String parameter, Function builder) { + return query(""" INSERT INTO announcements(guild_id, %s) VALUES (?, ?) ON CONFLICT(guild_id) DO UPDATE SET %s = excluded.%s; """, parameter, parameter, parameter) - .parameter(stmts -> { - stmts.setLong(guildId()); - builder.accept(stmts); - }).insert() - .sendSync() + .single(builder.apply(call().bind(guildId()))) + .insert() .changed(); } diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/General.java b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/General.java index 85b69983a..b09f2bed9 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/General.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/General.java @@ -5,12 +5,10 @@ */ package de.chojo.repbot.dao.access.guild.settings.sub; -import de.chojo.jdautil.consumer.ThrowingConsumer; import de.chojo.repbot.dao.access.guild.settings.Settings; import de.chojo.repbot.dao.components.GuildHolder; -import de.chojo.sadu.base.QueryFactory; -import de.chojo.sadu.wrapper.util.ParamBuilder; -import de.chojo.sadu.wrapper.util.Row; +import de.chojo.sadu.mapper.wrapper.Row; +import de.chojo.sadu.queries.api.call.Call; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.interactions.DiscordLocale; import org.jetbrains.annotations.Nullable; @@ -20,8 +18,12 @@ import java.time.LocalDate; import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Function; -public class General extends QueryFactory implements GuildHolder { +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; + +public class General implements GuildHolder { private final AtomicBoolean stackRoles; private final Settings settings; private DiscordLocale language; @@ -34,7 +36,6 @@ public General(Settings settings) { } public General(Settings settings, DiscordLocale language, boolean emojiDebug, boolean stackRoles, ReputationMode reputationMode, LocalDate resetDate) { - super(settings); this.settings = settings; this.language = language; this.emojiDebug = emojiDebug; @@ -54,7 +55,7 @@ public static General build(Settings settings, Row rs) throws SQLException { } public boolean language(@Nullable DiscordLocale language) { - var result = set("language", stmt -> stmt.setString(language == null ? null : language.getLocale())); + var result = set("language", stmt -> stmt.bind(language == null ? null : language.getLocale())); if (result) { this.language = language; } @@ -62,7 +63,7 @@ public boolean language(@Nullable DiscordLocale language) { } public boolean emojiDebug(boolean emojiDebug) { - var result = set("emoji_debug", stmt -> stmt.setBoolean(emojiDebug)); + var result = set("emoji_debug", stmt -> stmt.bind(emojiDebug)); if (result) { this.emojiDebug = emojiDebug; } @@ -70,7 +71,7 @@ public boolean emojiDebug(boolean emojiDebug) { } public ReputationMode reputationMode(ReputationMode reputationMode) { - var result = set("reputation_mode", stmt -> stmt.setString(reputationMode.name())); + var result = set("reputation_mode", stmt -> stmt.bind(reputationMode.name())); if (result) { this.reputationMode = reputationMode; } @@ -78,7 +79,7 @@ public ReputationMode reputationMode(ReputationMode reputationMode) { } public boolean stackRoles(boolean stackRoles) { - var result = set("stack_roles", stmt -> stmt.setBoolean(stackRoles)); + var result = set("stack_roles", stmt -> stmt.bind(stackRoles)); if (result) { this.stackRoles.set(stackRoles); } @@ -86,12 +87,7 @@ public boolean stackRoles(boolean stackRoles) { } public boolean resetDate(LocalDate resetDate) { - boolean result; - if (resetDate == null) { - result = set("reset_date", stmt -> stmt.setDate(null)); - } else { - result = set("reset_date", stmt -> stmt.setDate(Date.valueOf(resetDate))); - } + var result = set("reset_date", stmt -> stmt.bind(resetDate == null ? null : Date.valueOf(resetDate))); if (result) { this.resetDate = resetDate; } @@ -128,18 +124,14 @@ public long guildId() { return settings.guildId(); } - private boolean set(String parameter, ThrowingConsumer builder) { - return builder() - .query(""" - INSERT INTO guild_settings(guild_id, %s) VALUES (?, ?) - ON CONFLICT(guild_id) - DO UPDATE SET %s = excluded.%s; - """, parameter, parameter, parameter) - .parameter(stmts -> { - stmts.setLong(guildId()); - builder.accept(stmts); - }).insert() - .sendSync() + private boolean set(String parameter, Function builder) { + return query(""" + INSERT INTO guild_settings(guild_id, %s) VALUES (?, ?) + ON CONFLICT(guild_id) + DO UPDATE SET %s = excluded.%s; + """, parameter, parameter, parameter) + .single(builder.apply(call().bind(guildId()))) + .insert() .changed(); } @@ -154,6 +146,6 @@ public String prettyString() { Language: %s Reputation Mode: %s """.stripIndent() - .formatted(stackRoles.get(), emojiDebug, language != null ? language.getLanguageName() : guild().getLocale().getLanguageName(), reputationMode.name()); + .formatted(stackRoles.get(), emojiDebug, language != null ? language.getLanguageName() : guild().getLocale().getLanguageName(), reputationMode.name()); } } diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Messages.java b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Messages.java index 5985c217f..c7e68702d 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Messages.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Messages.java @@ -5,19 +5,21 @@ */ package de.chojo.repbot.dao.access.guild.settings.sub; -import de.chojo.jdautil.consumer.ThrowingConsumer; import de.chojo.repbot.dao.access.guild.settings.Settings; import de.chojo.repbot.dao.components.GuildHolder; -import de.chojo.sadu.base.QueryFactory; -import de.chojo.sadu.wrapper.util.ParamBuilder; -import de.chojo.sadu.wrapper.util.Row; +import de.chojo.sadu.mapper.wrapper.Row; +import de.chojo.sadu.queries.api.call.Call; import net.dv8tion.jda.api.entities.Guild; import org.jetbrains.annotations.PropertyKey; import java.sql.SQLException; import java.util.List; +import java.util.function.Function; -public class Messages extends QueryFactory implements GuildHolder { +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; + +public class Messages implements GuildHolder { private final Settings settings; private boolean reactionConfirmation; @@ -26,7 +28,6 @@ public Messages(Settings settings) { } public Messages(Settings settings, boolean reactionConfirmation) { - super(settings); this.settings = settings; this.reactionConfirmation = reactionConfirmation; } @@ -37,7 +38,7 @@ public static Messages build(Settings settings, Row rs) throws SQLException { } public boolean reactionConfirmation(boolean reactionConfirmation) { - var result = set("reaction_confirmation", stmt -> stmt.setBoolean(reactionConfirmation)); + var result = set("reaction_confirmation", stmt -> stmt.bind(reactionConfirmation)); if (result) { this.reactionConfirmation = reactionConfirmation; } @@ -58,18 +59,14 @@ public long guildId() { return settings.guildId(); } - private boolean set(String parameter, ThrowingConsumer builder) { - return builder() - .query(""" - INSERT INTO message_states(guild_id, %s) VALUES (?, ?) - ON CONFLICT(guild_id) - DO UPDATE SET %s = excluded.%s; - """, parameter, parameter, parameter) - .parameter(stmts -> { - stmts.setLong(guildId()); - builder.accept(stmts); - }).insert() - .sendSync() + private boolean set(String parameter, Function builder) { + return query(""" + INSERT INTO message_states(guild_id, %s) VALUES (?, ?) + ON CONFLICT(guild_id) + DO UPDATE SET %s = excluded.%s; + """, parameter, parameter, parameter) + .single(builder.apply(call().bind(guildId()))) + .insert() .changed(); } @@ -87,8 +84,8 @@ private String getSetting(@PropertyKey(resourceBundle = "locale") String locale, public String prettyString() { return """ - Reaction confirmation: %s - """.stripIndent() - .formatted(reactionConfirmation); + Reaction confirmation: %s + """.stripIndent() + .formatted(reactionConfirmation); } } diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Ranks.java b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Ranks.java index 28580c8d9..0ad78d90d 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Ranks.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Ranks.java @@ -9,7 +9,6 @@ import de.chojo.repbot.dao.access.guild.settings.Settings; import de.chojo.repbot.dao.components.GuildHolder; import de.chojo.repbot.dao.snapshots.ReputationRank; -import de.chojo.sadu.base.QueryFactory; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Role; @@ -20,13 +19,15 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; -public class Ranks extends QueryFactory implements GuildHolder { +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; + +public class Ranks implements GuildHolder { private final LinkedHashSet ranks = new LinkedHashSet<>(); private final Settings settings; private final AtomicBoolean stackRoles; public Ranks(Settings settings, AtomicBoolean stackRoles) { - super(settings); this.settings = settings; this.stackRoles = stackRoles; } @@ -41,54 +42,51 @@ public Ranks(Settings settings, AtomicBoolean stackRoles) { * @return true if the role was added or updated */ public boolean add(Role role, long reputation) { - var result = builder() - .query(""" - DELETE FROM - guild_ranks - WHERE - guild_id = ? - AND (role_id = ? - OR reputation = ?); - """) - .parameter(stmt -> stmt.setLong(guildId()).setLong(role.getIdLong()) - .setLong(reputation)) - .append() - .query(""" - INSERT INTO guild_ranks(guild_id, role_id, reputation) VALUES(?,?,?) - ON CONFLICT(guild_id, role_id) - DO UPDATE - SET reputation = excluded.reputation, - role_id = excluded.role_id; - """) - .parameter(stmt -> stmt.setLong(guildId()).setLong(role.getIdLong()) - .setLong(reputation)) + var deleteRank = query(""" + DELETE FROM + guild_ranks + WHERE + guild_id = ? + AND (role_id = ? + OR reputation = ?); + """) + .single(call().bind(guildId()).bind(role.getIdLong()).bind(reputation)) + .delete() + .changed(); + + var insertRank = query(""" + INSERT INTO guild_ranks(guild_id, role_id, reputation) VALUES(?,?,?) + ON CONFLICT(guild_id, role_id) + DO UPDATE + SET reputation = excluded.reputation, + role_id = excluded.role_id; + """) + .single(call().bind(guildId()).bind(role.getIdLong()).bind(reputation)) .update() - .sendSync() .changed(); - if (result) { + if (deleteRank && insertRank) { ranks.removeIf(r -> r.roleId() == role.getIdLong() || reputation == r.reputation()); ranks.add(new ReputationRank(this, role.getIdLong(), reputation)); } - return result; + return deleteRank; } public List ranks() { if (!ranks.isEmpty()) { return ranks.stream().sorted().toList(); } - var ranks = builder(ReputationRank.class) - .query(""" - SELECT - role_id, - reputation - FROM - guild_ranks - WHERE guild_id = ? - ORDER BY reputation; - """) - .parameter(stmt -> stmt.setLong(guildId())) - .readRow(r -> ReputationRank.build(this, r)) - .allSync(); + var ranks = query(""" + SELECT + role_id, + reputation + FROM + guild_ranks + WHERE guild_id = ? + ORDER BY reputation; + """) + .single(call().bind(guildId())) + .map(r -> ReputationRank.build(this, r)) + .all(); this.ranks.addAll(ranks); return this.ranks.stream().sorted().toList(); } @@ -106,25 +104,25 @@ public List ranks() { public List currentRanks(RepUser user) { var profile = user.profile(); return ranks().stream() - .filter(rank -> rank.reputation() <= profile.reputation()) - .sorted() - .limit(stackRoles.get() ? Integer.MAX_VALUE : 1) - .toList(); + .filter(rank -> rank.reputation() <= profile.reputation()) + .sorted() + .limit(stackRoles.get() ? Integer.MAX_VALUE : 1) + .toList(); } public Optional currentRank(RepUser user) { var profile = user.profile(); return ranks().stream() - .filter(rank -> rank.reputation() <= profile.reputation()) - .sorted() - .limit(1) - .findFirst(); + .filter(rank -> rank.reputation() <= profile.reputation()) + .sorted() + .limit(1) + .findFirst(); } public Optional nextRank(RepUser user) { var profile = user.profile(); return ranks().stream().filter(rank -> rank.reputation() > profile.reputation()) - .sorted(Comparator.reverseOrder()).limit(1).findFirst(); + .sorted(Comparator.reverseOrder()).limit(1).findFirst(); } @Override @@ -138,11 +136,10 @@ public long guildId() { } public Optional rank(Role role) { - return builder(ReputationRank.class) - .query("SELECT reputation FROM guild_ranks WHERE guild_id = ? AND role_id = ?") - .parameter(p -> p.setLong(guildId()).setLong(role.getIdLong())) - .readRow(row -> new ReputationRank(this, role.getIdLong(), row.getInt("reputation"))) - .firstSync(); + return query("SELECT reputation FROM guild_ranks WHERE guild_id = ? AND role_id = ?") + .single(call().bind(guildId()).bind(role.getIdLong())) + .map(row -> new ReputationRank(this, role.getIdLong(), row.getInt("reputation"))) + .first(); } public void refresh() { @@ -151,7 +148,7 @@ public void refresh() { public String prettyString() { return ranks().stream().filter(r -> r.role().isPresent()) - .map(rank -> "%s(%d) %d".formatted(rank.role().get().getName(), rank.role().get().getPosition(), rank.reputation())) - .collect(Collectors.joining("\n")); + .map(rank -> "%s(%d) %d".formatted(rank.role().get().getName(), rank.role().get().getPosition(), rank.reputation())) + .collect(Collectors.joining("\n")); } } diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Reputation.java b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Reputation.java index e183cfa2f..360ec50b5 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Reputation.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Reputation.java @@ -5,19 +5,21 @@ */ package de.chojo.repbot.dao.access.guild.settings.sub; -import de.chojo.jdautil.consumer.ThrowingConsumer; import de.chojo.repbot.dao.access.guild.settings.Settings; import de.chojo.repbot.dao.components.GuildHolder; -import de.chojo.sadu.base.QueryFactory; -import de.chojo.sadu.wrapper.util.ParamBuilder; -import de.chojo.sadu.wrapper.util.Row; +import de.chojo.sadu.mapper.wrapper.Row; +import de.chojo.sadu.queries.api.call.Call; import net.dv8tion.jda.api.entities.Guild; import org.jetbrains.annotations.PropertyKey; import java.sql.SQLException; import java.util.List; +import java.util.function.Function; -public class Reputation extends QueryFactory implements GuildHolder { +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; + +public class Reputation implements GuildHolder { private final Settings settings; private boolean reactionActive; private boolean answerActive; @@ -31,7 +33,6 @@ public Reputation(Settings settings) { } public Reputation(Settings settings, boolean reactionActive, boolean answerActive, boolean mentionActive, boolean fuzzyActive, boolean embedActive, boolean directActive) { - super(settings); this.settings = settings; this.reactionActive = reactionActive; this.answerActive = answerActive; @@ -71,12 +72,12 @@ public boolean isEmbedActive() { return embedActive; } - public boolean isDirectActive(){ + public boolean isDirectActive() { return directActive; } public boolean embedActive(boolean embedActive) { - var result = set("embed_active", stmt -> stmt.setBoolean(embedActive)); + var result = set("embed_active", stmt -> stmt.bind(embedActive)); if (result) { this.embedActive = embedActive; } @@ -84,7 +85,7 @@ public boolean embedActive(boolean embedActive) { } public boolean reactionActive(boolean reactionActive) { - var result = set("reactions_active", stmt -> stmt.setBoolean(reactionActive)); + var result = set("reactions_active", stmt -> stmt.bind(reactionActive)); if (result) { this.reactionActive = reactionActive; } @@ -92,7 +93,7 @@ public boolean reactionActive(boolean reactionActive) { } public boolean answerActive(boolean answerActive) { - var result = set("answer_active", stmt -> stmt.setBoolean(answerActive)); + var result = set("answer_active", stmt -> stmt.bind(answerActive)); if (result) { this.answerActive = answerActive; } @@ -100,7 +101,7 @@ public boolean answerActive(boolean answerActive) { } public boolean mentionActive(boolean mentionActive) { - var result = set("mention_active", stmt -> stmt.setBoolean(mentionActive)); + var result = set("mention_active", stmt -> stmt.bind(mentionActive)); if (result) { this.mentionActive = mentionActive; } @@ -108,7 +109,7 @@ public boolean mentionActive(boolean mentionActive) { } public boolean fuzzyActive(boolean fuzzyActive) { - var result = set("fuzzy_active", stmt -> stmt.setBoolean(fuzzyActive)); + var result = set("fuzzy_active", stmt -> stmt.bind(fuzzyActive)); if (result) { this.fuzzyActive = fuzzyActive; } @@ -116,7 +117,7 @@ public boolean fuzzyActive(boolean fuzzyActive) { } public boolean directActive(boolean directActive) { - var result = set("skip_single_embed", stmt -> stmt.setBoolean(directActive)); + var result = set("skip_single_embed", stmt -> stmt.bind(directActive)); if (result) { this.directActive = directActive; } @@ -160,30 +161,26 @@ public long guildId() { return settings.guildId(); } - private boolean set(String parameter, ThrowingConsumer builder) { - return builder() - .query(""" - INSERT INTO reputation_settings(guild_id, %s) VALUES (?, ?) - ON CONFLICT(guild_id) - DO UPDATE SET %s = excluded.%s; - """, parameter, parameter, parameter) - .parameter(stmts -> { - stmts.setLong(guildId()); - builder.accept(stmts); - }).insert() - .sendSync() + private boolean set(String parameter, Function builder) { + return query(""" + INSERT INTO reputation_settings(guild_id, %s) VALUES (?, ?) + ON CONFLICT(guild_id) + DO UPDATE SET %s = excluded.%s; + """, parameter, parameter, parameter) + .single(builder.apply(call().bind(guildId()))) + .insert() .changed(); } public String prettyString() { return """ - Reaction active: %s - Answer active: %s - Mention active: %s - Fuzzy active: %s - Embed active: %s - Skip single embed: %s - """.formatted(reactionActive, answerActive, mentionActive, fuzzyActive, embedActive, directActive) - .stripIndent(); + Reaction active: %s + Answer active: %s + Mention active: %s + Fuzzy active: %s + Embed active: %s + Skip single embed: %s + """.formatted(reactionActive, answerActive, mentionActive, fuzzyActive, embedActive, directActive) + .stripIndent(); } } diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Thanking.java b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Thanking.java index 8f6a8f816..03d9dd7e2 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Thanking.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/Thanking.java @@ -12,14 +12,16 @@ import de.chojo.repbot.dao.access.guild.settings.sub.thanking.ReceiverRoles; import de.chojo.repbot.dao.access.guild.settings.sub.thanking.Thankwords; import de.chojo.repbot.dao.components.GuildHolder; -import de.chojo.sadu.base.QueryFactory; -import de.chojo.sadu.wrapper.util.Row; +import de.chojo.sadu.mapper.wrapper.Row; import net.dv8tion.jda.api.entities.Guild; import java.sql.SQLException; import java.util.HashSet; -public class Thanking extends QueryFactory implements GuildHolder { +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; + +public class Thanking implements GuildHolder { private static final String DEFAULT_REACTION = "🏅"; private final String mainReaction; private final Settings settings; @@ -36,7 +38,6 @@ public Thanking(Settings settings) { } public Thanking(Settings settings, String mainReaction, boolean channelWhitelist) { - super(settings); this.settings = settings; this.mainReaction = mainReaction; this.channelWhitelist = channelWhitelist; @@ -53,24 +54,22 @@ public Channels channels() { if (channels != null) { return channels; } - var channels = builder(Long.class) - .query(""" + var channels = query(""" SELECT channel_id FROM active_channel WHERE guild_id = ? """) - .parameter(stmt -> stmt.setLong(guildId())) - .readRow(r -> r.getLong("channel_id")) - .allSync(); - var categories = builder(Long.class) - .query(""" + .single(call().bind(guildId())) + .map(r -> r.getLong("channel_id")) + .all(); + var categories = query(""" SELECT category_id FROM active_categories WHERE guild_id = ? """) - .parameter(stmt -> stmt.setLong(guildId())) - .readRow(r -> r.getLong("category_id")) - .allSync(); + .single(call().bind(guildId())) + .mapAs(Long.class) + .all(); this.channels = new Channels(this, channelWhitelist, new HashSet<>(channels), new HashSet<>(categories)); return this.channels; } @@ -79,15 +78,14 @@ public DonorRoles donorRoles() { if (donorRoles != null) { return donorRoles; } - var roles = builder(Long.class) - .query(""" + var roles = query(""" SELECT role_id FROM donor_roles WHERE guild_id = ? """) - .parameter(stmt -> stmt.setLong(guildId())) - .readRow(r -> r.getLong("role_id")) - .allSync(); + .single(call().bind(guildId())) + .mapAs(Long.class) + .all(); donorRoles = new DonorRoles(this, new HashSet<>(roles)); return donorRoles; @@ -97,15 +95,14 @@ public ReceiverRoles receiverRoles() { if (receiverRoles != null) { return receiverRoles; } - var roles = builder(Long.class) - .query(""" + var roles = query(""" SELECT role_id FROM receiver_roles WHERE guild_id = ? """) - .parameter(stmt -> stmt.setLong(guildId())) - .readRow(r -> r.getLong("role_id")) - .allSync(); + .single(call().bind(guildId())) + .mapAs(Long.class) + .all(); receiverRoles = new ReceiverRoles(this, new HashSet<>(roles)); return receiverRoles; @@ -115,15 +112,14 @@ public Reactions reactions() { if (reactions != null) { return reactions; } - var reactions = builder(String.class) - .query(""" + var reactions = query(""" SELECT reaction FROM guild_reactions WHERE guild_id = ? """) - .parameter(stmt -> stmt.setLong(guildId())) - .readRow(r -> r.getString("reaction")) - .allSync(); + .single(call().bind(guildId())) + .mapAs(String.class) + .all(); this.reactions = new Reactions(this, mainReaction, new HashSet<>(reactions)); return this.reactions; } @@ -132,15 +128,14 @@ public Thankwords thankwords() { if (thankwords != null) { return thankwords; } - var thankwords = builder(String.class) - .query(""" + var thankwords = query(""" SELECT thankword FROM thankwords WHERE guild_id = ? """) - .parameter(stmt -> stmt.setLong(guildId())) - .readRow(r -> r.getString("thankword")) - .allSync(); + .single(call().bind(guildId())) + .mapAs(String.class) + .all(); this.thankwords = new Thankwords(this, new HashSet<>(thankwords)); return this.thankwords; diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/thanking/Channels.java b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/thanking/Channels.java index ada42b0e0..dc9c778dc 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/thanking/Channels.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/thanking/Channels.java @@ -7,7 +7,6 @@ import de.chojo.repbot.dao.access.guild.settings.sub.Thanking; import de.chojo.repbot.dao.components.GuildHolder; -import de.chojo.sadu.base.QueryFactory; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.channel.Channel; import net.dv8tion.jda.api.entities.channel.concrete.Category; @@ -24,9 +23,11 @@ import java.util.Objects; import java.util.Set; +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; import static org.slf4j.LoggerFactory.getLogger; -public class Channels extends QueryFactory implements GuildHolder { +public class Channels implements GuildHolder { private static final Logger log = getLogger(Channels.class); @@ -36,7 +37,6 @@ public class Channels extends QueryFactory implements GuildHolder { private boolean whitelist; public Channels(Thanking thanking, boolean whitelist, Set channels, Set categories) { - super(thanking); this.thanking = thanking; this.whitelist = whitelist; this.channels = channels; @@ -107,11 +107,9 @@ public boolean isWhitelist() { * @return true if a channel was added */ public boolean add(StandardGuildChannel channel) { - var result = builder() - .query("INSERT INTO active_channel(guild_id, channel_id) VALUES(?,?) ON CONFLICT(guild_id, channel_id) DO NOTHING;") - .parameter(stmt -> stmt.setLong(guildId()).setLong(channel.getIdLong())) + var result = query("INSERT INTO active_channel(guild_id, channel_id) VALUES(?,?) ON CONFLICT(guild_id, channel_id) DO NOTHING;") + .single(call().bind(guildId()).bind(channel.getIdLong())) .update() - .sendSync() .changed(); if (result) { channels.add(channel.getIdLong()); @@ -126,11 +124,9 @@ public boolean add(StandardGuildChannel channel) { * @return true if a category was added */ public boolean add(Category category) { - var result = builder() - .query("INSERT INTO active_categories(guild_id, category_id) VALUES(?,?) ON CONFLICT(guild_id, category_id) DO NOTHING;") - .parameter(stmt -> stmt.setLong(guildId()).setLong(category.getIdLong())) + var result = query("INSERT INTO active_categories(guild_id, category_id) VALUES(?,?) ON CONFLICT(guild_id, category_id) DO NOTHING;") + .single(call().bind(guildId()).bind(category.getIdLong())) .update() - .sendSync() .changed(); if (result) { categories.add(category.getIdLong()); @@ -145,11 +141,9 @@ public boolean add(Category category) { * @return true if the channel was removed */ public boolean remove(Channel channel) { - var result = builder() - .query("DELETE FROM active_channel WHERE guild_id = ? AND channel_id = ?;") - .parameter(stmt -> stmt.setLong(guildId()).setLong(channel.getIdLong())) + var result = query("DELETE FROM active_channel WHERE guild_id = ? AND channel_id = ?;") + .single(call().bind(guildId()).bind(channel.getIdLong())) .update() - .sendSync() .changed(); if (result) { channels.remove(channel.getIdLong()); @@ -164,11 +158,9 @@ public boolean remove(Channel channel) { * @return true if the channel was removed */ public boolean remove(Category category) { - var result = builder() - .query("DELETE FROM active_categories WHERE guild_id = ? AND category_id = ?;") - .parameter(stmt -> stmt.setLong(guildId()).setLong(category.getIdLong())) + var result =query("DELETE FROM active_categories WHERE guild_id = ? AND category_id = ?;") + .single(call().bind(guildId()).bind(category.getIdLong())) .update() - .sendSync() .changed(); if (result) { categories.remove(category.getIdLong()); @@ -182,11 +174,9 @@ public boolean remove(Category category) { * @return the amount of removed channel */ public int clearChannel() { - var result = builder() - .query("DELETE FROM active_channel WHERE guild_id = ?;") - .parameter(stmt -> stmt.setLong(guildId())) + var result = query("DELETE FROM active_channel WHERE guild_id = ?;") + .single(call().bind(guildId())) .update() - .sendSync() .rows(); if (result > 0) { channels.clear(); @@ -200,11 +190,9 @@ public int clearChannel() { * @return the amount of removed categories */ public int clearCategories() { - var result = builder() - .query("DELETE FROM active_categories WHERE guild_id = ?;") - .parameter(stmt -> stmt.setLong(guildId())) + var result = query("DELETE FROM active_categories WHERE guild_id = ?;") + .single(call().bind(guildId())) .update() - .sendSync() .rows(); if (result > 0) { categories.clear(); @@ -213,16 +201,14 @@ public int clearCategories() { } public boolean listType(boolean whitelist) { - var result = builder() - .query(""" + var result = query(""" INSERT INTO thank_settings(guild_id, channel_whitelist) VALUES (?,?) ON CONFLICT(guild_id) DO UPDATE SET channel_whitelist = excluded.channel_whitelist """) - .parameter(stmt -> stmt.setLong(guildId()).setBoolean(whitelist)) + .single(call().bind(guildId()).bind(whitelist)) .update() - .sendSync() .changed(); if (result) { this.whitelist = whitelist; diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/thanking/Reactions.java b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/thanking/Reactions.java index 468508806..7b86f5689 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/thanking/Reactions.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/thanking/Reactions.java @@ -8,7 +8,6 @@ import de.chojo.jdautil.parsing.Verifier; import de.chojo.repbot.dao.access.guild.settings.sub.Thanking; import de.chojo.repbot.dao.components.GuildHolder; -import de.chojo.sadu.base.QueryFactory; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.MessageReaction; import net.dv8tion.jda.api.entities.emoji.CustomEmoji; @@ -20,13 +19,15 @@ import java.util.Set; import java.util.stream.Collectors; -public class Reactions extends QueryFactory implements GuildHolder { +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; + +public class Reactions implements GuildHolder { private final Thanking thanking; private final Set reactions; private String mainReaction; public Reactions(Thanking thanking, String mainReaction, Set reactions) { - super(thanking); this.thanking = thanking; this.mainReaction = mainReaction; this.reactions = reactions; @@ -90,15 +91,13 @@ public List getAdditionalReactionMentions() { } public boolean add(String reaction) { - var result = builder() - .query(""" + var result = query(""" INSERT INTO guild_reactions(guild_id, reaction) VALUES (?,?) ON CONFLICT(guild_id, reaction) DO NOTHING; """) - .parameter(stmt -> stmt.setLong(guildId()).setString(reaction)) + .single(call().bind(guildId()).bind(reaction)) .update() - .sendSync() .changed(); if (result) { reactions.add(reaction); @@ -107,13 +106,11 @@ ON CONFLICT(guild_id, reaction) } public boolean remove(String reaction) { - var result = builder() - .query(""" + var result = query(""" DELETE FROM guild_reactions WHERE guild_id = ? AND reaction = ?; """) - .parameter(stmt -> stmt.setLong(guildId()).setString(reaction)) + .single(call().bind(guildId()).bind(reaction)) .update() - .sendSync() .changed(); if (result) { reactions.remove(reaction); @@ -123,16 +120,14 @@ public boolean remove(String reaction) { } public boolean mainReaction(String reaction) { - var result = builder() - .query(""" + var result = query(""" INSERT INTO thank_settings(guild_id, reaction) VALUES (?,?) ON CONFLICT(guild_id) DO UPDATE SET reaction = excluded.reaction """) - .parameter(stmt -> stmt.setLong(guildId()).setString(reaction)) + .single(call().bind(guildId()).bind(reaction)) .update() - .sendSync() .changed(); if (result) { mainReaction = reaction; diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/thanking/RolesHolder.java b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/thanking/RolesHolder.java index dff30a12e..418b721b9 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/thanking/RolesHolder.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/thanking/RolesHolder.java @@ -7,7 +7,6 @@ import de.chojo.repbot.dao.access.guild.settings.sub.Thanking; import de.chojo.repbot.dao.components.GuildHolder; -import de.chojo.sadu.base.QueryFactory; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Role; @@ -19,15 +18,16 @@ import java.util.Set; import java.util.stream.Collectors; +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; import static org.slf4j.LoggerFactory.getLogger; -public abstract class RolesHolder extends QueryFactory implements GuildHolder { +public abstract class RolesHolder implements GuildHolder { private static final Logger log = getLogger(RolesHolder.class); protected final Set roleIds; protected final Thanking thanking; public RolesHolder(Thanking thanking, Set roleIds) { - super(thanking); this.thanking = thanking; this.roleIds = roleIds; } @@ -56,11 +56,9 @@ public Guild guild() { protected abstract String targetTable(); public boolean add(Role role) { - var result = builder() - .query("INSERT INTO %s(guild_id, role_id) VALUES (?,?) ON CONFLICT(guild_id, role_id) DO NOTHING", targetTable()) - .parameter(stmt -> stmt.setLong(guildId()).setLong(role.getIdLong())) + var result = query("INSERT INTO %s(guild_id, role_id) VALUES (?,?) ON CONFLICT(guild_id, role_id) DO NOTHING", targetTable()) + .single(call().bind(guildId()).bind(role.getIdLong())) .update() - .sendSync() .changed(); if (result) { roleIds.add(role.getIdLong()); @@ -69,11 +67,9 @@ public boolean add(Role role) { } public boolean remove(Role role) { - var result = builder() - .query("DELETE FROM %s WHERE guild_id = ? AND role_id = ?", targetTable()) - .parameter(stmt -> stmt.setLong(guildId()).setLong(role.getIdLong())) + var result = query("DELETE FROM %s WHERE guild_id = ? AND role_id = ?", targetTable()) + .single(call().bind(guildId()).bind(role.getIdLong())) .update() - .sendSync() .changed(); if (result) { roleIds.remove(role.getIdLong()); diff --git a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/thanking/Thankwords.java b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/thanking/Thankwords.java index 981ecb189..1b6c9f23d 100644 --- a/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/thanking/Thankwords.java +++ b/src/main/java/de/chojo/repbot/dao/access/guild/settings/sub/thanking/Thankwords.java @@ -7,7 +7,6 @@ import de.chojo.repbot.dao.access.guild.settings.sub.Thanking; import de.chojo.repbot.dao.components.GuildHolder; -import de.chojo.sadu.base.QueryFactory; import net.dv8tion.jda.api.entities.Guild; import org.intellij.lang.annotations.Language; @@ -16,7 +15,10 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; -public class Thankwords extends QueryFactory implements GuildHolder { +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; + +public class Thankwords implements GuildHolder { @Language("RegExp") private static final String THANKWORD = "(?:^|\\b)%s(?:$|\\b)"; @Language("RegExp") @@ -29,7 +31,6 @@ public class Thankwords extends QueryFactory implements GuildHolder { private volatile Pattern cachedPattern; public Thankwords(Thanking thanking, Set thankwords) { - super(thanking); this.thanking = thanking; this.thankwords = thankwords; this.lock = new StampedLock(); @@ -78,15 +79,14 @@ private Pattern compilePattern() { public boolean add(String pattern) { long stamp = lock.writeLock(); try { - var result = builder().query(""" + var result = query(""" INSERT INTO thankwords(guild_id, thankword) VALUES(?,?) ON CONFLICT(guild_id, thankword) DO NOTHING; """) - .parameter(stmt -> stmt.setLong(guildId()).setString(pattern)) + .single(call().bind(guildId()).bind(pattern)) .update() - .sendSync() .changed(); if (result) { thankwords.add(pattern); @@ -101,15 +101,14 @@ ON CONFLICT(guild_id, thankword) public boolean remove(String pattern) { long stamp = lock.writeLock(); try { - var result = builder().query(""" + var result = query(""" DELETE FROM thankwords WHERE guild_id = ? AND thankword = ? - """).parameter(stmt -> stmt.setLong(guildId()).setString(pattern)) + """).single(call().bind(guildId()).bind(pattern)) .update() - .sendSync() .changed(); if (result) { thankwords.remove(pattern); diff --git a/src/main/java/de/chojo/repbot/dao/access/metrics/Commands.java b/src/main/java/de/chojo/repbot/dao/access/metrics/Commands.java index 4003f63b1..a57e8d0ad 100644 --- a/src/main/java/de/chojo/repbot/dao/access/metrics/Commands.java +++ b/src/main/java/de/chojo/repbot/dao/access/metrics/Commands.java @@ -9,7 +9,7 @@ import de.chojo.repbot.dao.snapshots.statistics.CommandsStatistic; import de.chojo.repbot.dao.snapshots.statistics.CountStatistics; import de.chojo.repbot.dao.snapshots.statistics.CountsStatistic; -import de.chojo.sadu.base.QueryFactory; +import de.chojo.sadu.queries.api.query.Query; import java.time.LocalDate; import java.util.Collections; @@ -17,69 +17,67 @@ import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; -public class Commands extends QueryFactory { - public Commands(QueryFactory factoryHolder) { - super(factoryHolder); +import static de.chojo.sadu.queries.api.call.Call.call; + +public class Commands { + public Commands() { + } public void logCommand(String command) { - builder() - .query(""" - INSERT INTO metrics_commands(day, command) VALUES (NOW()::date, ?) - ON CONFLICT(day,command) - DO UPDATE SET count = metrics_commands.count + 1 - """) - .parameter(stmt -> stmt.setString(command)) - .insert() - .send(); + Query.query(""" + INSERT INTO metrics_commands(day, command) VALUES (now()::DATE, ?) + ON CONFLICT(day,command) + DO UPDATE SET count = metrics_commands.count + 1 + """) + .single(call().bind(command)) + .insert(); } - public CompletableFuture week(int week) { + public CommandsStatistic week(int week) { return get("metrics_commands_week", "week", week); } - public CompletableFuture month(int month) { + public CommandsStatistic month(int month) { return get("metrics_commands_month", "month", month); } - public CompletableFuture week(int week, int count) { + public CountsStatistic week(int week, int count) { return get("metrics_commands_executed_week", "week", week, count); } - public CompletableFuture month(int month, int count) { + public CountsStatistic month(int month, int count) { return get("metrics_commands_executed_month", "month", month, count); } - private CompletableFuture get(String table, String timeframe, int offset, int count) { - return builder(CountStatistics.class) - .query(""" - SELECT %s, - count - FROM %s - WHERE %s <= DATE_TRUNC(?, NOW())::date - ?::interval - ORDER BY %s DESC - LIMIT ? - """, timeframe, table, timeframe, timeframe) - .parameter(stmt -> stmt.setString(timeframe) - .setString(offset + " " + timeframe).setInt(count)) - .readRow(rs -> CountStatistics.build(rs, timeframe)) - .all() - .thenApply(CountsStatistic::new); + private CountsStatistic get(String table, String timeframe, int offset, int count) { + return Query.query(""" + SELECT %s, + count + FROM %s + WHERE %s <= DATE_TRUNC(?, NOW())::date - ?::interval + ORDER BY %s DESC + LIMIT ? + """, timeframe, table, timeframe, timeframe) + .single(call().bind(timeframe).bind(offset + " " + timeframe).bind(count)) + .map(rs -> CountStatistics.build(rs, timeframe)) + .allResults() + .map(CountsStatistic::new); } - private CompletableFuture get(String table, String timeframe, int offset) { - return builder(CommandStatistic.class) + private CommandsStatistic get(String table, String timeframe, int offset) { + return Query .query(""" - SELECT %s, - command, - count - FROM %s - WHERE %s = DATE_TRUNC('%s', NOW())::date - ?::interval - """, timeframe, table, timeframe, timeframe) - .parameter(stmt -> stmt.setString(offset + " " + timeframe)) - .readRow(rs -> CommandStatistic.build(rs, timeframe)) - .all() - .thenApply(this::mapStatistics); + SELECT %s, + command, + count + FROM %s + WHERE %s = DATE_TRUNC('%s', NOW())::date - ?::interval + """, timeframe, table, timeframe, timeframe) + .single(call().bind(offset + " " + timeframe)) + .map(rs -> CommandStatistic.build(rs, timeframe)) + .allResults() + .map(this::mapStatistics); } private CommandsStatistic mapStatistics(List commandStatistics) { diff --git a/src/main/java/de/chojo/repbot/dao/access/metrics/Messages.java b/src/main/java/de/chojo/repbot/dao/access/metrics/Messages.java index 06868cd22..dd729901a 100644 --- a/src/main/java/de/chojo/repbot/dao/access/metrics/Messages.java +++ b/src/main/java/de/chojo/repbot/dao/access/metrics/Messages.java @@ -7,67 +7,66 @@ import de.chojo.repbot.dao.snapshots.statistics.CountStatistics; import de.chojo.repbot.dao.snapshots.statistics.CountsStatistic; -import de.chojo.sadu.base.QueryFactory; +import de.chojo.sadu.queries.api.call.Call; +import de.chojo.sadu.queries.api.query.Query; -import java.util.concurrent.CompletableFuture; +import static de.chojo.sadu.queries.api.call.Call.call; + +public class Messages { + public Messages() { -public class Messages extends QueryFactory { - public Messages(QueryFactory factoryHolder) { - super(factoryHolder); } public void countMessage() { - builder() - .queryWithoutParams(""" - INSERT INTO metrics_message_analyzed(hour, count) VALUES (DATE_TRUNC('hour', NOW()), 1) - ON CONFLICT(hour) - DO UPDATE SET count = metrics_message_analyzed.count + 1 - """) - .insert() - .send(); + Query.query(""" + INSERT INTO metrics_message_analyzed(hour, count) VALUES (date_trunc('hour', now()), 1) + ON CONFLICT(hour) + DO UPDATE SET count = metrics_message_analyzed.count + 1 + """) + .single() + .insert(); } - public CompletableFuture hour(int hour, int count) { + public CountsStatistic hour(int hour, int count) { return get("metrics_message_analyzed", "hour", hour, count); } - public CompletableFuture day(int day, int count) { + public CountsStatistic day(int day, int count) { return get("metrics_message_analyzed_day", "day", day, count); } - public CompletableFuture week(int week, int count) { + public CountsStatistic week(int week, int count) { return get("metrics_message_analyzed_week", "week", week, count); } - public CompletableFuture month(int month, int count) { + public CountsStatistic month(int month, int count) { return get("metrics_message_analyzed_month", "month", month, count); } - public CompletableFuture totalDay(int day, int count) { + public CountsStatistic totalDay(int day, int count) { return get("metrics_messages_analyzed_total_day", "day", day, count); } - public CompletableFuture totalWeek(int week, int count) { + public CountsStatistic totalWeek(int week, int count) { return get("metrics_messages_analyzed_total_week", "week", week, count); } - public CompletableFuture totalMonth(int month, int count) { + public CountsStatistic totalMonth(int month, int count) { return get("metrics_messages_analyzed_total_month", "month", month, count); } - private CompletableFuture get(String table, String timeframe, int offset, int count) { - return builder(CountStatistics.class).query(""" - SELECT %s, - count - FROM %s - WHERE %s <= DATE_TRUNC(?, NOW()) - ?::interval - ORDER BY %s DESC - LIMIT ? - """, timeframe, table, timeframe, timeframe) - .parameter(stmt -> stmt.setString(timeframe) - .setString(offset + " " + timeframe).setInt(count)) - .readRow(rs -> CountStatistics.build(rs, timeframe)) - .all() - .thenApply(CountsStatistic::new); + private CountsStatistic get(String table, String timeframe, int offset, int count) { + return Query.query(""" + SELECT %s, + count + FROM %s + WHERE %s <= date_trunc(?, now()) - ?::INTERVAL + ORDER BY %s DESC + LIMIT ? + """, timeframe, table, timeframe, timeframe) + .single(call().bind(timeframe).bind(offset + " " + timeframe).bind(count)) + .map(rs -> CountStatistics.build(rs, timeframe)) + .allResults() + .map(CountsStatistic::new); } } diff --git a/src/main/java/de/chojo/repbot/dao/access/metrics/Reputation.java b/src/main/java/de/chojo/repbot/dao/access/metrics/Reputation.java index 9789dccf3..2a3def4ad 100644 --- a/src/main/java/de/chojo/repbot/dao/access/metrics/Reputation.java +++ b/src/main/java/de/chojo/repbot/dao/access/metrics/Reputation.java @@ -11,176 +11,168 @@ import de.chojo.repbot.dao.snapshots.statistics.DowsStatistic; import de.chojo.repbot.dao.snapshots.statistics.LabeledCountStatistic; import de.chojo.repbot.dao.snapshots.statistics.builder.LabeledCountStatisticBuilder; -import de.chojo.sadu.base.QueryFactory; +import de.chojo.sadu.queries.api.query.Query; import java.util.concurrent.CompletableFuture; -public class Reputation extends QueryFactory { - public Reputation(QueryFactory factoryHolder) { - super(factoryHolder); +import static de.chojo.sadu.queries.api.call.Call.call; + +public class Reputation { + public Reputation() { + super(); } - public CompletableFuture totalWeek(int week, int count) { + public CountsStatistic totalWeek(int week, int count) { return get("metrics_reputation_total_week", "week", week, count); } - public CompletableFuture totalMonth(int month, int count) { + public CountsStatistic totalMonth(int month, int count) { return get("metrics_reputation_total_month", "month", month, count); } - public CompletableFuture week(int week, int count) { + public CountsStatistic week(int week, int count) { return get("metrics_reputation_week", "week", week, count); } - public CompletableFuture month(int month, int count) { + public CountsStatistic month(int month, int count) { return get("metrics_reputation_month", "month", month, count); } - public CompletableFuture weekChanges(int week, int count) { + public LabeledCountStatistic weekChanges(int week, int count) { return getChanges("metrics_reputation_changed_week", "week", week, count); } - public CompletableFuture monthChanges(int month, int count) { + public LabeledCountStatistic monthChanges(int month, int count) { return getChanges("metrics_reputation_changed_month", "month", month, count); } - public CompletableFuture typeWeek(int week, int count) { + public LabeledCountStatistic typeWeek(int week, int count) { return getType("metrics_reputation_type_week", "week", week, count); } - public CompletableFuture typeMonth(int month, int count) { + public LabeledCountStatistic typeMonth(int month, int count) { return getType("metrics_reputation_type_month", "month", month, count); } - public CompletableFuture typeTotalWeek(int week, int count) { + public LabeledCountStatistic typeTotalWeek(int week, int count) { return getType("metrics_reputation_type_total_week", "week", week, count); } - public CompletableFuture typeTotalMonth(int month, int count) { + public LabeledCountStatistic typeTotalMonth(int month, int count) { return getType("metrics_reputation_type_total_month", "month", month, count); } - public CompletableFuture dowWeek(int week) { + public DowsStatistic dowWeek(int week) { return get("metrics_reputation_dow_week", "week", week); } - public CompletableFuture dowMonth(int month) { + public DowsStatistic dowMonth(int month) { return get("metrics_reputation_dow_month", "month", month); } - public CompletableFuture dowYear(int year) { + public DowsStatistic dowYear(int year) { return get("metrics_reputation_dow_year", "year", year); } - private CompletableFuture get(String table, String timeframe, int offset, int count) { - return builder(CountStatistics.class) - .query(""" - SELECT %s, - count - FROM %s - WHERE %s <= DATE_TRUNC(?, NOW())::date - ?::interval - ORDER BY %s DESC - LIMIT ? - """, timeframe, table, timeframe, timeframe) - .parameter(stmt -> stmt.setString(timeframe) - .setString(offset + " " + timeframe).setInt(count)) - .readRow(rs -> CountStatistics.build(rs, timeframe)) - .all() - .thenApply(CountsStatistic::new); - } - - private CompletableFuture getType(String table, String timeframe, int offset, int count) { + private CountsStatistic get(String table, String timeframe, int offset, int count) { + return Query.query(""" + SELECT %s, + count + FROM %s + WHERE %s <= date_trunc(?, now())::DATE - ?::INTERVAL + ORDER BY %s DESC + LIMIT ? + """, timeframe, table, timeframe, timeframe) + .single(call().bind(timeframe) + .bind(offset + " " + timeframe) + .bind(count)) + .map(rs -> CountStatistics.build(rs, timeframe)) + .allResults() + .map(CountsStatistic::new); + } + + private LabeledCountStatistic getType(String table, String timeframe, int offset, int count) { var builder = new LabeledCountStatisticBuilder(); - return builder(LabeledCountStatisticBuilder.class) - .query(""" - SELECT %s, - cause, - count - FROM %s - WHERE %s <= DATE_TRUNC(?, NOW())::date - ?::interval - ORDER BY %s DESC - LIMIT ? - """, timeframe, table, timeframe, timeframe) - .parameter(stmt -> stmt.setString(timeframe) - .setString(offset + " " + timeframe) - .setInt(count)) - .readRow(rs -> builder.add(rs.getString("cause"), CountStatistics.build(rs, timeframe))) - .all() - .thenApply(r -> builder.build()); - } - - private CompletableFuture getChanges(String table, String timeframe, int offset, int count) { + Query.query(""" + SELECT %s, + cause, + count + FROM %s + WHERE %s <= date_trunc(?, now())::DATE - ?::INTERVAL + ORDER BY %s DESC + LIMIT ? + """, timeframe, table, timeframe, timeframe) + .single(call().bind(timeframe).bind(offset + " " + timeframe).bind(count)) + .map(rs -> builder.add(rs.getString("cause"), CountStatistics.build(rs, timeframe))) + .all(); + return builder.build(); + } + + private LabeledCountStatistic getChanges(String table, String timeframe, int offset, int count) { var builder = new LabeledCountStatisticBuilder(); - return builder(LabeledCountStatisticBuilder.class) - .query(""" - SELECT %s, - added - removed as delta, - added, - removed - FROM %s - WHERE %s <= DATE_TRUNC(?, NOW())::date - ?::interval - ORDER BY %s DESC - LIMIT ? - """, timeframe, table, timeframe, timeframe) - .parameter(stmt -> stmt.setString(timeframe) - .setString(offset + " " + timeframe) - .setInt(count)) - .readRow(rs -> builder.add("delta", CountStatistics.build(rs, "delta",timeframe)) - .add("added", CountStatistics.build(rs, "added",timeframe)) - .add("removed", CountStatistics.build(rs, "removed",timeframe))) - .all() - .thenApply(r -> builder.build()); - } - - private CompletableFuture get(String table, String timeframe, int offset) { - return builder(DowStatistics.class) - .query(""" - SELECT %s, - dow, - count - FROM %s - WHERE %s = DATE_TRUNC(?, NOW())::date - ?::interval - ORDER BY %s DESC - """, timeframe, table, timeframe, timeframe) - .parameter(stmt -> stmt.setString(timeframe) - .setString(offset + " " + timeframe)) - .readRow(rs -> DowStatistics.build(rs, timeframe)) - .all() - .thenApply(DowsStatistic::new); + Query.query(""" + SELECT %s, + added - removed AS delta, + added, + removed + FROM %s + WHERE %s <= date_trunc(?, now())::DATE - ?::INTERVAL + ORDER BY %s DESC + LIMIT ? + """, timeframe, table, timeframe, timeframe) + .single(call().bind(timeframe).bind(offset + " " + timeframe).bind(count)) + .map(rs -> builder.add("delta", CountStatistics.build(rs, "delta", timeframe)) + .add("added", CountStatistics.build(rs, "added", timeframe)) + .add("removed", CountStatistics.build(rs, "removed", timeframe))) + .all(); + return builder.build(); + } + + private DowsStatistic get(String table, String timeframe, int offset) { + return Query.query(""" + SELECT %s, + dow, + count + FROM %s + WHERE %s = date_trunc(?, now())::DATE - ?::INTERVAL + ORDER BY %s DESC + """, timeframe, table, timeframe, timeframe) + .single(call().bind(timeframe).bind(offset + " " + timeframe)) + .map(rs -> DowStatistics.build(rs, timeframe)) + .allResults() + .map(DowsStatistic::new); } /** * Save reputation counts of the previous day into metric tables. */ public void saveRepCounts() { - builder() - .query(""" - INSERT INTO metrics_reputation(day, cause, count) - SELECT received::date AS day, - cause, - COUNT(1) AS count - FROM reputation_log - WHERE received::date = NOW()::date - INTERVAL '1 DAY' - GROUP BY day, cause - ORDER BY day DESC - ON CONFLICT(day, cause) - DO UPDATE - SET count = excluded.count; - """) - .emptyParams() - .append() - .query(""" - INSERT INTO metrics_reputation_count(day, count) - SELECT NOW() - INTERVAL '1 DAY', - COUNT(1) - FROM reputation_log - WHERE received < NOW()::date - ON CONFLICT(day) - DO UPDATE - SET count = excluded.count; - """) - .emptyParams() - .insert() - .send(); + Query.query(""" + INSERT INTO metrics_reputation(day, cause, count) + SELECT received::DATE AS day, + cause, + count(1) AS count + FROM reputation_log + WHERE received::DATE = now()::DATE - INTERVAL '1 DAY' + GROUP BY day, cause + ORDER BY day DESC + ON CONFLICT(day, cause) + DO UPDATE + SET count = excluded.count; + """) + .single() + .insert(); + Query.query(""" + INSERT INTO metrics_reputation_count(day, count) + SELECT now() - INTERVAL '1 DAY', + count(1) + FROM reputation_log + WHERE received < now()::DATE + ON CONFLICT(day) + DO UPDATE + SET count = excluded.count; + """) + .single() + .insert(); } } diff --git a/src/main/java/de/chojo/repbot/dao/access/metrics/Service.java b/src/main/java/de/chojo/repbot/dao/access/metrics/Service.java index 59f7165d1..0beb1ce64 100644 --- a/src/main/java/de/chojo/repbot/dao/access/metrics/Service.java +++ b/src/main/java/de/chojo/repbot/dao/access/metrics/Service.java @@ -5,17 +5,18 @@ */ package de.chojo.repbot.dao.access.metrics; -import de.chojo.repbot.dao.provider.Metrics; import de.chojo.repbot.dao.snapshots.statistics.CountStatistics; import de.chojo.repbot.dao.snapshots.statistics.LabeledCountStatistic; import de.chojo.repbot.dao.snapshots.statistics.builder.LabeledCountStatisticBuilder; -import de.chojo.sadu.base.QueryFactory; +import de.chojo.sadu.queries.api.query.Query; import java.util.concurrent.CompletableFuture; -public class Service extends QueryFactory { - public Service(Metrics metrics) { - super(metrics); +import static de.chojo.sadu.queries.api.call.Call.call; + +public class Service { + public Service() { + super(); } public void successfulInteraction() { @@ -31,53 +32,51 @@ public void countInteraction() { } private void logInteraction(String type) { - builder() - .queryWithoutParams(""" - INSERT INTO metrics_handled_interactions(hour, %s) VALUES (DATE_TRUNC('hour', NOW()), 1) - ON CONFLICT(hour) - DO UPDATE SET %s = metrics_handled_interactions.%s + 1 - """, type, type, type) - .insert() - .send(); + Query.query(""" + INSERT INTO metrics_handled_interactions(hour, %s) VALUES (date_trunc('hour', now()), 1) + ON CONFLICT(hour) + DO UPDATE SET %s = metrics_handled_interactions.%s + 1 + """, type, type, type) + .single() + .insert(); } - public CompletableFuture hour(int hour, int count) { + public LabeledCountStatistic hour(int hour, int count) { return get("metrics_handled_interactions", "hour", hour, count); } - public CompletableFuture day(int day, int count) { + public LabeledCountStatistic day(int day, int count) { return get("metrics_handled_interactions_day", "day", day, count); } - public CompletableFuture week(int week, int count) { + public LabeledCountStatistic week(int week, int count) { return get("metrics_handled_interactions_week", "week", week, count); } - public CompletableFuture month(int month, int count) { + public LabeledCountStatistic month(int month, int count) { return get("metrics_handled_interactions_month", "month", month, count); } - private CompletableFuture get(String table, String timeframe, int offset, int count) { + private LabeledCountStatistic get(String table, String timeframe, int offset, int count) { var builder = new LabeledCountStatisticBuilder(); - return builder(LabeledCountStatisticBuilder.class) - .query(""" - SELECT %s, - count, - failed, - success - FROM %s - WHERE %s <= DATE_TRUNC(?, NOW()) - ?::interval - ORDER BY %s DESC - LIMIT ? - """, timeframe, table, timeframe, timeframe) - .parameter(stmt -> stmt.setString(timeframe) - .setString(offset + " " + timeframe) - .setInt(count)) - .readRow(rs -> builder.add("count", CountStatistics.build(rs, "count", timeframe)) + return Query.query(""" + SELECT %s, + count, + failed, + success + FROM %s + WHERE %s <= DATE_TRUNC(?, NOW()) - ?::interval + ORDER BY %s DESC + LIMIT ? + """, timeframe, table, timeframe, timeframe) + .single(call().bind(timeframe) + .bind(offset + " " + timeframe) + .bind(count)) + .map(rs -> builder.add("count", CountStatistics.build(rs, "count", timeframe)) .add("success", CountStatistics.build(rs, "success", timeframe)) .add("failed", CountStatistics.build(rs, "failed", timeframe)) ) - .all() - .thenApply(r -> builder.build()); + .allResults() + .map(r -> builder.build()); } } diff --git a/src/main/java/de/chojo/repbot/dao/access/metrics/Statistic.java b/src/main/java/de/chojo/repbot/dao/access/metrics/Statistic.java index d3de1a65d..93d338c0a 100644 --- a/src/main/java/de/chojo/repbot/dao/access/metrics/Statistic.java +++ b/src/main/java/de/chojo/repbot/dao/access/metrics/Statistic.java @@ -6,44 +6,45 @@ package de.chojo.repbot.dao.access.metrics; import de.chojo.repbot.statistic.element.DataStatistic; -import de.chojo.sadu.base.QueryFactory; +import de.chojo.sadu.queries.api.query.Query; import java.util.Optional; -public class Statistic extends QueryFactory { - public Statistic(QueryFactory factoryHolder) { - super(factoryHolder); +public class Statistic { + public Statistic() { + super(); } public Optional getStatistic() { - return builder(DataStatistic.class) - .queryWithoutParams(""" - SELECT - guilds, - active_guilds, - active_channel, - channel, - total_reputation, - today_reputation, - weekly_reputation, - weekly_avg_reputation - FROM - data_statistics; - """).readRow(rs -> new DataStatistic( - rs.getInt("guilds"), - rs.getInt("active_guilds"), - rs.getInt("active_channel"), - rs.getInt("channel"), - rs.getInt("total_reputation"), - rs.getInt("today_reputation"), - rs.getInt("weekly_reputation"), - rs.getInt("weekly_avg_reputation"))) - .firstSync(); + return Query.query(""" + SELECT + guilds, + active_guilds, + active_channel, + channel, + total_reputation, + today_reputation, + weekly_reputation, + weekly_avg_reputation + FROM + data_statistics; + """) + .single() + .map(rs -> new DataStatistic( + rs.getInt("guilds"), + rs.getInt("active_guilds"), + rs.getInt("active_channel"), + rs.getInt("channel"), + rs.getInt("total_reputation"), + rs.getInt("today_reputation"), + rs.getInt("weekly_reputation"), + rs.getInt("weekly_avg_reputation"))) + .first(); } public void refreshStatistics() { - builder().queryWithoutParams("REFRESH MATERIALIZED VIEW data_statistics") - .update() - .sendSync(); + Query.query("REFRESH MATERIALIZED VIEW data_statistics") + .single() + .update(); } } diff --git a/src/main/java/de/chojo/repbot/dao/access/metrics/Users.java b/src/main/java/de/chojo/repbot/dao/access/metrics/Users.java index 127cf0e5d..fb24a94d2 100644 --- a/src/main/java/de/chojo/repbot/dao/access/metrics/Users.java +++ b/src/main/java/de/chojo/repbot/dao/access/metrics/Users.java @@ -7,78 +7,75 @@ import de.chojo.repbot.dao.snapshots.statistics.UserStatistic; import de.chojo.repbot.dao.snapshots.statistics.UsersStatistic; -import de.chojo.sadu.base.QueryFactory; +import de.chojo.sadu.queries.api.query.Query; import java.util.concurrent.CompletableFuture; -public class Users extends QueryFactory { - public Users(QueryFactory factoryHolder) { - super(factoryHolder); +import static de.chojo.sadu.queries.api.call.Call.call; + +public class Users { + public Users() { + super(); } - public CompletableFuture week(int offset, int count) { + public UsersStatistic week(int offset, int count) { return get("metrics_users_week", "week", offset, count); } - public CompletableFuture month(int offset, int count) { + public UsersStatistic month(int offset, int count) { return get("metrics_users_month", "month", offset, count); } - private CompletableFuture get(String table, String timeframe, int offset, int count) { - return builder(UserStatistic.class) - .query(""" - SELECT %s, - donor_count, - receiver_count, - total_count - FROM %s - WHERE %s <= DATE_TRUNC(?, NOW())::date - ?::interval - ORDER BY %s DESC - LIMIT ? - """, timeframe, table, timeframe, timeframe) - .parameter(stmt -> stmt.setString(timeframe) - .setString(offset + " " + timeframe).setInt(count)) - .readRow(rs -> UserStatistic.build(rs, timeframe)) - .all() - .thenApply(UsersStatistic::new); + private UsersStatistic get(String table, String timeframe, int offset, int count) { + return Query.query(""" + SELECT %s, + donor_count, + receiver_count, + total_count + FROM %s + WHERE %s <= date_trunc(?, now())::DATE - ?::INTERVAL + ORDER BY %s DESC + LIMIT ? + """, timeframe, table, timeframe, timeframe) + .single(call().bind(timeframe).bind(offset + " " + timeframe).bind(count)) + .map(rs -> UserStatistic.build(rs, timeframe)) + .allResults() + .map(UsersStatistic::new); } /** * Save the user count of the last week. */ public void saveUserCountWeek() { - builder() - .query(""" - INSERT INTO metrics_users_week - SELECT week, receiver_count, donor_count, total_count - FROM metrics_unique_users_week - WHERE week = DATE_TRUNC('week', NOW() - INTERVAL '1 WEEK') - ON CONFLICT(week) - DO UPDATE SET receiver_count = excluded.receiver_count, - donor_count = excluded.donor_count, - total_count = excluded.donor_count - """).emptyParams() - .insert() - .send(); + Query.query(""" + INSERT INTO metrics_users_week + SELECT week, receiver_count, donor_count, total_count + FROM metrics_unique_users_week + WHERE week = date_trunc('week', now() - INTERVAL '1 WEEK') + ON CONFLICT(week) + DO UPDATE SET receiver_count = excluded.receiver_count, + donor_count = excluded.donor_count, + total_count = excluded.donor_count + """) + .single() + .insert(); } /** * Save the user count of the last month. */ public void saveUserCountMonth() { - builder() - .query(""" - INSERT INTO metrics_users_month - SELECT month, receiver_count, donor_count, total_count - FROM metrics_unique_users_month - WHERE month = DATE_TRUNC('month', NOW() - INTERVAL '1 MONTH') - ON CONFLICT(month) - DO UPDATE SET receiver_count = excluded.receiver_count, - donor_count = excluded.donor_count, - total_count = excluded.donor_count - """) - .emptyParams() - .insert() - .send(); + Query.query(""" + INSERT INTO metrics_users_month + SELECT month, receiver_count, donor_count, total_count + FROM metrics_unique_users_month + WHERE month = date_trunc('month', now() - INTERVAL '1 MONTH') + ON CONFLICT(month) + DO UPDATE SET receiver_count = excluded.receiver_count, + donor_count = excluded.donor_count, + total_count = excluded.donor_count + """) + .single() + .insert(); } } diff --git a/src/main/java/de/chojo/repbot/dao/provider/Guilds.java b/src/main/java/de/chojo/repbot/dao/provider/Guilds.java index 69750d30a..00e6a9826 100644 --- a/src/main/java/de/chojo/repbot/dao/provider/Guilds.java +++ b/src/main/java/de/chojo/repbot/dao/provider/Guilds.java @@ -12,31 +12,30 @@ import de.chojo.repbot.dao.access.guild.RepGuildId; import de.chojo.repbot.dao.access.guild.settings.sub.ReputationMode; import de.chojo.repbot.dao.pagination.GuildList; -import de.chojo.sadu.base.QueryFactory; import net.dv8tion.jda.api.entities.Guild; import org.slf4j.Logger; -import javax.sql.DataSource; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; import static org.slf4j.LoggerFactory.getLogger; -public class Guilds extends QueryFactory { +public class Guilds { private static final Logger log = getLogger(Guilds.class); private final Cache guilds = CacheBuilder.newBuilder().expireAfterAccess(30, TimeUnit.MINUTES) .build(); private final Configuration configuration; - public Guilds(DataSource dataSource, Configuration configuration) { - super(dataSource); + public Guilds(Configuration configuration) { this.configuration = configuration; } public RepGuild guild(Guild guild) { try { - return guilds.get(guild.getIdLong(), () -> new RepGuild(source(), guild, configuration)).refresh(guild); + return guilds.get(guild.getIdLong(), () -> new RepGuild(guild, configuration)).refresh(guild); } catch (ExecutionException e) { log.error("Could not create guild adapter", e); throw new RuntimeException("", e); @@ -54,15 +53,14 @@ public RepGuild guild(Guild guild) { */ public RepGuild byId(long id) { var cached = guilds.getIfPresent(id); - return cached != null ? cached : new RepGuildId(source(), id, configuration); + return cached != null ? cached : new RepGuildId(id, configuration); } public List byReputationMode(ReputationMode mode) { - return builder(RepGuild.class) - .query("SELECT guild_id FROM guild_settings WHERE reputation_mode = ?") - .parameter(stmt -> stmt.setString(mode.name())) - .readRow(row -> byId(row.getLong("guild_id"))) - .allSync(); + return query("SELECT guild_id FROM guild_settings WHERE reputation_mode = ?") + .single(call().bind(mode.name())) + .map(row -> byId(row.getLong("guild_id"))) + .all(); } public GuildList guilds(int pageSize) { @@ -70,29 +68,27 @@ public GuildList guilds(int pageSize) { } private Integer pages(int pageSize) { - return builder(Integer.class) - .query(""" + return query(""" SELECT - CEIL(COUNT(1)::numeric / ?) AS count + CEIL(COUNT(1)::numeric / ?)::INTEGER AS count FROM guilds """) - .parameter(stmt -> stmt.setInt(pageSize)) - .readRow(row -> row.getInt("count")) - .firstSync() + .single(call().bind(pageSize)) + .mapAs(Integer.class) + .first() .orElse(1); } private List page(int pageSize, int page) { - return builder(RepGuild.class) - .query(""" + return query(""" SELECT guild_id FROM guilds OFFSET ? LIMIT ?; """) - .parameter(stmt -> stmt.setInt(page * pageSize).setInt(pageSize)) - .readRow(row -> byId(row.getLong("guild_id"))) - .allSync(); + .single(call().bind(page * pageSize).bind(pageSize)) + .map(row -> byId(row.getLong("guild_id"))) + .all(); } public void invalidate(long guild) { diff --git a/src/main/java/de/chojo/repbot/dao/provider/Metrics.java b/src/main/java/de/chojo/repbot/dao/provider/Metrics.java index 8332ba7de..a5406f4cd 100644 --- a/src/main/java/de/chojo/repbot/dao/provider/Metrics.java +++ b/src/main/java/de/chojo/repbot/dao/provider/Metrics.java @@ -11,11 +11,10 @@ import de.chojo.repbot.dao.access.metrics.Service; import de.chojo.repbot.dao.access.metrics.Statistic; import de.chojo.repbot.dao.access.metrics.Users; -import de.chojo.sadu.base.QueryFactory; import javax.sql.DataSource; -public class Metrics extends QueryFactory { +public class Metrics { private final Commands commands; private final Messages messages; private final Users users; @@ -24,13 +23,12 @@ public class Metrics extends QueryFactory { private final Service service; public Metrics(DataSource dataSource) { - super(dataSource); - commands = new Commands(this); - messages = new Messages(this); - users = new Users(this); - statistic = new Statistic(this); - reputation = new Reputation(this); - service = new Service(this); + commands = new Commands(); + messages = new Messages(); + users = new Users(); + statistic = new Statistic(); + reputation = new Reputation(); + service = new Service(); } public Commands commands() { diff --git a/src/main/java/de/chojo/repbot/dao/provider/Voice.java b/src/main/java/de/chojo/repbot/dao/provider/Voice.java index 9d0815d31..17079ff44 100644 --- a/src/main/java/de/chojo/repbot/dao/provider/Voice.java +++ b/src/main/java/de/chojo/repbot/dao/provider/Voice.java @@ -6,24 +6,22 @@ package de.chojo.repbot.dao.provider; import de.chojo.repbot.config.Configuration; -import de.chojo.sadu.base.QueryFactory; -import de.chojo.sadu.wrapper.stage.ResultStage; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.User; import org.slf4j.Logger; -import javax.sql.DataSource; import java.util.List; +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; import static org.slf4j.LoggerFactory.getLogger; -public class Voice extends QueryFactory { +public class Voice { private static final Logger log = getLogger(Voice.class); private final Configuration configuration; - public Voice(DataSource dataSource, Configuration configuration) { - super(dataSource); + public Voice(Configuration configuration) { this.configuration = configuration; } @@ -34,25 +32,18 @@ public Voice(DataSource dataSource, Configuration configuration) { * @param seen the members which were seen by the user lately */ public void logUser(Member source, List seen) { - var baseId = source.getIdLong(); - var builder = builder(); - ResultStage resultStage = null; - for (var user : seen) { - var otherId = user.getIdLong(); - resultStage = builder - .query(""" - INSERT INTO voice_activity(relation_key, guild_id, user_id_1, user_id_2) VALUES (?,?,?,?) - ON CONFLICT(relation_key, guild_id) - DO UPDATE - SET seen = NOW() - """) - .parameter(stmt -> stmt.setLong(baseId ^ otherId) - .setLong(source.getGuild().getIdLong()) - .setLong(baseId).setLong(otherId)); - } - if (resultStage == null) return; - resultStage.update() - .sendSync(); + query(""" + INSERT INTO voice_activity(relation_key, guild_id, user_id_1, user_id_2) VALUES (?,?,?,?) + ON CONFLICT(relation_key, guild_id) + DO UPDATE + SET seen = now() + """) + .batch(seen.stream().map(member -> call() + .bind(source.getIdLong() ^ member.getIdLong()) + .bind(source.getGuild().getIdLong()) + .bind(source.getIdLong()) + .bind(member.getIdLong()))) + .insert(); } /** @@ -65,41 +56,38 @@ ON CONFLICT(relation_key, guild_id) * @return list of ids */ public List getPastUser(User user, Guild guild, int minutes, int limit) { - return builder(Long.class) - .query(""" - SELECT - user_id_1, user_id_2 - FROM - voice_activity - WHERE - guild_id = ? - AND (user_id_1 = ? - OR user_id_2 = ? - ) - AND seen > NOW() - (? || 'minute')::interval - ORDER BY - seen DESC - LIMIT ?; - """) - .parameter(stmt -> stmt.setLong(guild.getIdLong()).setLong(user.getIdLong()).setLong(user.getIdLong()) - .setInt(minutes).setInt(limit)) - .readRow(rs -> { + return query(""" + SELECT + user_id_1, user_id_2 + FROM + voice_activity + WHERE + guild_id = ? + AND (user_id_1 = ? + OR user_id_2 = ? + ) + AND seen > now() - (? || 'minute')::INTERVAL + ORDER BY + seen DESC + LIMIT ?; + """) + .single(call().bind(guild.getIdLong()).bind(user.getIdLong()).bind(user.getIdLong()) + .bind(minutes).bind(limit)) + .map(rs -> { var id1 = rs.getLong("user_id_1"); var id2 = rs.getLong("user_id_2"); return id1 == user.getIdLong() ? id2 : id1; - }).allSync(); + }).all(); } /** * Cleanup the voice activity */ public void cleanup() { - builder() - .query(""" - DELETE FROM voice_activity WHERE seen < NOW() - ?::interval - """) - .parameter(stmt -> stmt.setString("%d hours".formatted(configuration.cleanup().voiceActivityHours()))) - .update() - .send(); + query(""" + DELETE FROM voice_activity WHERE seen < now() - ?::INTERVAL + """) + .single(call().bind("%d hours".formatted(configuration.cleanup().voiceActivityHours()))) + .update(); } } diff --git a/src/main/java/de/chojo/repbot/dao/snapshots/RepProfile.java b/src/main/java/de/chojo/repbot/dao/snapshots/RepProfile.java index bb1fb372b..4069edcd5 100644 --- a/src/main/java/de/chojo/repbot/dao/snapshots/RepProfile.java +++ b/src/main/java/de/chojo/repbot/dao/snapshots/RepProfile.java @@ -13,7 +13,7 @@ import de.chojo.repbot.config.Configuration; import de.chojo.repbot.dao.access.guild.reputation.sub.RepUser; import de.chojo.repbot.util.Text; -import de.chojo.sadu.wrapper.util.Row; +import de.chojo.sadu.mapper.wrapper.Row; import net.dv8tion.jda.api.EmbedBuilder; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.IMentionable; diff --git a/src/main/java/de/chojo/repbot/dao/snapshots/ReputationLogEntry.java b/src/main/java/de/chojo/repbot/dao/snapshots/ReputationLogEntry.java index 008896f84..bac7e8dbd 100644 --- a/src/main/java/de/chojo/repbot/dao/snapshots/ReputationLogEntry.java +++ b/src/main/java/de/chojo/repbot/dao/snapshots/ReputationLogEntry.java @@ -7,16 +7,18 @@ import de.chojo.repbot.analyzer.results.match.ThankType; import de.chojo.repbot.dao.access.guild.reputation.sub.Log; -import de.chojo.sadu.base.QueryFactory; -import de.chojo.sadu.wrapper.util.Row; +import de.chojo.sadu.mapper.wrapper.Row; import java.sql.SQLException; import java.time.LocalDateTime; +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; + /** * A log entry representing a single reputation. */ -public class ReputationLogEntry extends QueryFactory { +public class ReputationLogEntry { private static final String PATH = "https://discord.com/channels/%s/%s/%s"; private static final long DISCORD_EPOCH = 1420070400000L; private final long guildId; @@ -29,7 +31,6 @@ public class ReputationLogEntry extends QueryFactory { private final LocalDateTime received; public ReputationLogEntry(Log log, long guildId, long channelId, long donorId, long receiverId, long messageId, long refMessageId, ThankType type, LocalDateTime received) { - super(log); this.guildId = guildId; this.channelId = channelId; this.donorId = donorId; @@ -105,19 +106,17 @@ public String timestamp() { * Removes the log entry */ public void delete() { - builder() - .query("DELETE FROM reputation_log WHERE message_id = ? AND receiver_id = ? AND donor_id = ?;") - .parameter(stmt -> stmt.setLong(messageId).setLong(receiverId).setLong(donorId)) - .update().send(); + query("DELETE FROM reputation_log WHERE message_id = ? AND receiver_id = ? AND donor_id = ?;") + .single(call().bind(messageId).bind(receiverId).bind(donorId)) + .update(); } /** * Removes all reputations associated with the message */ public void deleteAll() { - builder() - .query("DELETE FROM reputation_log WHERE message_id = ?") - .parameter(stmt -> stmt.setLong(messageId)) - .update().send(); + query("DELETE FROM reputation_log WHERE message_id = ?") + .single(call().bind(messageId)) + .update(); } } diff --git a/src/main/java/de/chojo/repbot/dao/snapshots/ReputationRank.java b/src/main/java/de/chojo/repbot/dao/snapshots/ReputationRank.java index 614f9c174..937779d62 100644 --- a/src/main/java/de/chojo/repbot/dao/snapshots/ReputationRank.java +++ b/src/main/java/de/chojo/repbot/dao/snapshots/ReputationRank.java @@ -7,8 +7,8 @@ import de.chojo.repbot.dao.access.guild.settings.sub.Ranks; import de.chojo.repbot.dao.components.GuildHolder; -import de.chojo.sadu.base.QueryFactory; -import de.chojo.sadu.wrapper.util.Row; +import de.chojo.sadu.mapper.wrapper.Row; +import de.chojo.sadu.queries.api.call.Call; import net.dv8tion.jda.api.entities.Guild; import net.dv8tion.jda.api.entities.Role; import org.jetbrains.annotations.NotNull; @@ -16,19 +16,21 @@ import java.sql.SQLException; import java.util.Optional; +import static de.chojo.sadu.queries.api.call.Call.call; +import static de.chojo.sadu.queries.api.query.Query.query; + /** * Representing a repuration rank. *

    * A rank is {@link Comparable} and will be sorted from the highest reputation to lowest. */ -public class ReputationRank extends QueryFactory implements GuildHolder, Comparable { +public class ReputationRank implements GuildHolder, Comparable { private final long roleId; private final long reputation; private final Ranks ranks; private Role role; public ReputationRank(Ranks ranks, long roleId, long reputation) { - super(ranks); this.ranks = ranks; this.roleId = roleId; this.reputation = reputation; @@ -66,11 +68,9 @@ public Optional role() { * @return true */ public boolean remove() { - var success = builder() - .query("DELETE FROM guild_ranks WHERE guild_id = ? AND role_id = ?;") - .parameter(stmt -> stmt.setLong(guildId()).setLong(roleId)) + var success = query("DELETE FROM guild_ranks WHERE guild_id = ? AND role_id = ?;") + .single(call().bind(guildId()).bind(roleId)) .update() - .sendSync() .changed(); ranks.refresh(); return success; diff --git a/src/main/java/de/chojo/repbot/dao/snapshots/statistics/CommandStatistic.java b/src/main/java/de/chojo/repbot/dao/snapshots/statistics/CommandStatistic.java index 374456b38..11ae30126 100644 --- a/src/main/java/de/chojo/repbot/dao/snapshots/statistics/CommandStatistic.java +++ b/src/main/java/de/chojo/repbot/dao/snapshots/statistics/CommandStatistic.java @@ -5,7 +5,8 @@ */ package de.chojo.repbot.dao.snapshots.statistics; -import de.chojo.sadu.wrapper.util.Row; + +import de.chojo.sadu.mapper.wrapper.Row; import java.sql.SQLException; import java.time.LocalDate; diff --git a/src/main/java/de/chojo/repbot/dao/snapshots/statistics/CountStatistics.java b/src/main/java/de/chojo/repbot/dao/snapshots/statistics/CountStatistics.java index 91f8e1b3f..abbdbbb4e 100644 --- a/src/main/java/de/chojo/repbot/dao/snapshots/statistics/CountStatistics.java +++ b/src/main/java/de/chojo/repbot/dao/snapshots/statistics/CountStatistics.java @@ -5,7 +5,7 @@ */ package de.chojo.repbot.dao.snapshots.statistics; -import de.chojo.sadu.wrapper.util.Row; +import de.chojo.sadu.mapper.wrapper.Row; import org.jetbrains.annotations.NotNull; import java.sql.SQLException; diff --git a/src/main/java/de/chojo/repbot/dao/snapshots/statistics/DowStatistics.java b/src/main/java/de/chojo/repbot/dao/snapshots/statistics/DowStatistics.java index a631419e9..511db41a6 100644 --- a/src/main/java/de/chojo/repbot/dao/snapshots/statistics/DowStatistics.java +++ b/src/main/java/de/chojo/repbot/dao/snapshots/statistics/DowStatistics.java @@ -5,7 +5,7 @@ */ package de.chojo.repbot.dao.snapshots.statistics; -import de.chojo.sadu.wrapper.util.Row; +import de.chojo.sadu.mapper.wrapper.Row; import org.jetbrains.annotations.NotNull; import java.sql.SQLException; diff --git a/src/main/java/de/chojo/repbot/dao/snapshots/statistics/UserStatistic.java b/src/main/java/de/chojo/repbot/dao/snapshots/statistics/UserStatistic.java index 77db66ffc..0f5746a44 100644 --- a/src/main/java/de/chojo/repbot/dao/snapshots/statistics/UserStatistic.java +++ b/src/main/java/de/chojo/repbot/dao/snapshots/statistics/UserStatistic.java @@ -5,7 +5,7 @@ */ package de.chojo.repbot.dao.snapshots.statistics; -import de.chojo.sadu.wrapper.util.Row; +import de.chojo.sadu.mapper.wrapper.Row; import org.jetbrains.annotations.NotNull; import java.sql.SQLException; diff --git a/src/main/java/de/chojo/repbot/service/GdprService.java b/src/main/java/de/chojo/repbot/service/GdprService.java index 46af88c3d..cfdec8d8e 100644 --- a/src/main/java/de/chojo/repbot/service/GdprService.java +++ b/src/main/java/de/chojo/repbot/service/GdprService.java @@ -74,7 +74,7 @@ public CompletableFuture cleanupGuildUsers(Guild guild) { guild.retrieveMemberById(user).complete(); } catch (RuntimeException e) { log.info("Removing user {} data during guild prune", user); - RemovalTask.anonymExecute(gdpr, guild.getIdLong(), user); + RemovalTask.anonymExecute(guild.getIdLong(), user); pruned++; } } @@ -85,7 +85,7 @@ public CompletableFuture cleanupGuildUsers(Guild guild) { public void cleanupGuildUser(Guild guild, Long user) { log.info("User data of {} was pruned on guild {}.", user, guild.getIdLong()); - CompletableFuture.runAsync(() -> RemovalTask.anonymExecute(gdpr, guild.getIdLong(), user), executorService); + CompletableFuture.runAsync(() -> RemovalTask.anonymExecute(guild.getIdLong(), user), executorService); } private void cleanupGuilds() { diff --git a/src/main/java/de/chojo/repbot/statistic/Statistic.java b/src/main/java/de/chojo/repbot/statistic/Statistic.java index c4afdebd9..c7ce89221 100644 --- a/src/main/java/de/chojo/repbot/statistic/Statistic.java +++ b/src/main/java/de/chojo/repbot/statistic/Statistic.java @@ -39,7 +39,7 @@ public static Statistic of(ShardManager shardManager, Metrics metrics, Scheduled private ShardStatistic getShardStatistic(JDA jda) { var shardId = jda.getShardInfo().getShardId(); - var analyzedMessages = metrics.messages().hour(1, 1).join().get(0).count(); + var analyzedMessages = metrics.messages().hour(1, 1).get(0).count(); return new ShardStatistic( shardId + 1, diff --git a/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Commands.java b/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Commands.java index 83c058fa3..f218ee4a7 100644 --- a/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Commands.java +++ b/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Commands.java @@ -26,7 +26,7 @@ public Commands(Metrics metrics, MetricCache cache) { } public void usageWeek(Context ctx) { - var stats = metrics().commands().week(offset(ctx, MAX_WEEK_OFFSET)).join(); + var stats = metrics().commands().week(offset(ctx, MAX_WEEK_OFFSET)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -35,7 +35,7 @@ public void usageWeek(Context ctx) { } public void usageMonth(Context ctx) { - var stats = metrics().commands().month(offset(ctx, MAX_MONTH_OFFSET)).join(); + var stats = metrics().commands().month(offset(ctx, MAX_MONTH_OFFSET)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -44,7 +44,7 @@ public void usageMonth(Context ctx) { } public void countWeek(Context ctx) { - var stats = metrics().commands().week(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)).join(); + var stats = metrics().commands().week(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -53,7 +53,7 @@ public void countWeek(Context ctx) { } public void countMonth(Context ctx) { - var stats = metrics().commands().month(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)).join(); + var stats = metrics().commands().month(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { diff --git a/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Messages.java b/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Messages.java index 54414de91..75ba93105 100644 --- a/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Messages.java +++ b/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Messages.java @@ -29,7 +29,7 @@ public Messages(Metrics metrics, MetricCache cache) { } public void countHour(Context ctx) { - var stats = metrics().messages().hour(offset(ctx, MAX_HOUR_OFFSET), count(ctx, MAX_HOURS)).join(); + var stats = metrics().messages().hour(offset(ctx, MAX_HOUR_OFFSET), count(ctx, MAX_HOURS)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -38,7 +38,7 @@ public void countHour(Context ctx) { } public void countDay(Context ctx) { - var stats = metrics().messages().day(offset(ctx, MAX_DAY_OFFSET), count(ctx, MAX_DAYS)).join(); + var stats = metrics().messages().day(offset(ctx, MAX_DAY_OFFSET), count(ctx, MAX_DAYS)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -47,7 +47,7 @@ public void countDay(Context ctx) { } public void countWeek(Context ctx) { - var stats = metrics().messages().week(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)).join(); + var stats = metrics().messages().week(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -56,7 +56,7 @@ public void countWeek(Context ctx) { } public void countMonth(Context ctx) { - var stats = metrics().messages().month(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)).join(); + var stats = metrics().messages().month(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -65,7 +65,7 @@ public void countMonth(Context ctx) { } public void totalDay(Context ctx) { - var stats = metrics().messages().totalDay(offset(ctx, MAX_DAY_OFFSET), count(ctx, MAX_DAYS)).join(); + var stats = metrics().messages().totalDay(offset(ctx, MAX_DAY_OFFSET), count(ctx, MAX_DAYS)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -74,7 +74,7 @@ public void totalDay(Context ctx) { } public void totalWeek(Context ctx) { - var stats = metrics().messages().totalWeek(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)).join(); + var stats = metrics().messages().totalWeek(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -83,7 +83,7 @@ public void totalWeek(Context ctx) { } public void totalMonth(Context ctx) { - var stats = metrics().messages().totalMonth(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)).join(); + var stats = metrics().messages().totalMonth(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { diff --git a/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Reputation.java b/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Reputation.java index 9fdcc54ef..a0b445105 100644 --- a/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Reputation.java +++ b/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Reputation.java @@ -31,7 +31,7 @@ public Reputation(Metrics metrics, MetricCache cache) { This function is broken, but I don't know how to fix it. */ public void countWeek(Context ctx) { - var stats = metrics().reputation().week(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)).join(); + var stats = metrics().reputation().week(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -40,7 +40,7 @@ public void countWeek(Context ctx) { } public void countMonth(Context ctx) { - var stats = metrics().reputation().month(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)).join(); + var stats = metrics().reputation().month(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -49,7 +49,7 @@ public void countMonth(Context ctx) { } public void countTypeWeek(Context ctx) { - var stats = metrics().reputation().typeWeek(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)).join(); + var stats = metrics().reputation().typeWeek(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -58,7 +58,7 @@ public void countTypeWeek(Context ctx) { } public void countTypeMonth(Context ctx) { - var stats = metrics().reputation().typeMonth(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)).join(); + var stats = metrics().reputation().typeMonth(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -67,7 +67,7 @@ public void countTypeMonth(Context ctx) { } public void totalWeek(Context ctx) { - var stats = metrics().reputation().totalWeek(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)).join(); + var stats = metrics().reputation().totalWeek(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -76,7 +76,7 @@ public void totalWeek(Context ctx) { } public void totalMonth(Context ctx) { - var stats = metrics().reputation().totalMonth(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)).join(); + var stats = metrics().reputation().totalMonth(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -85,7 +85,7 @@ public void totalMonth(Context ctx) { } public void totalTypeWeek(Context ctx) { - var stats = metrics().reputation().typeTotalWeek(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)).join(); + var stats = metrics().reputation().typeTotalWeek(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -94,7 +94,7 @@ public void totalTypeWeek(Context ctx) { } public void totalTypeMonth(Context ctx) { - var stats = metrics().reputation().typeTotalMonth(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)).join(); + var stats = metrics().reputation().typeTotalMonth(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -103,7 +103,7 @@ public void totalTypeMonth(Context ctx) { } public void changesWeek(Context ctx) { - var stats = metrics().reputation().weekChanges(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)).join(); + var stats = metrics().reputation().weekChanges(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -112,7 +112,7 @@ public void changesWeek(Context ctx) { } public void changesMonth(Context ctx) { - var stats = metrics().reputation().monthChanges(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)).join(); + var stats = metrics().reputation().monthChanges(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -121,7 +121,7 @@ public void changesMonth(Context ctx) { } public void dowWeek(Context ctx) { - var stats = metrics().reputation().dowWeek(offset(ctx, MAX_WEEK_OFFSET)).join(); + var stats = metrics().reputation().dowWeek(offset(ctx, MAX_WEEK_OFFSET)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -130,7 +130,7 @@ public void dowWeek(Context ctx) { } public void dowMonth(Context ctx) { - var stats = metrics().reputation().dowMonth(offset(ctx, MAX_MONTH_OFFSET)).join(); + var stats = metrics().reputation().dowMonth(offset(ctx, MAX_MONTH_OFFSET)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -139,7 +139,7 @@ public void dowMonth(Context ctx) { } public void dowYear(Context ctx) { - var stats = metrics().reputation().dowYear(offset(ctx, MAX_YEAR_OFFSET)).join(); + var stats = metrics().reputation().dowYear(offset(ctx, MAX_YEAR_OFFSET)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { diff --git a/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Service.java b/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Service.java index c90314376..b86ba4450 100644 --- a/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Service.java +++ b/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Service.java @@ -29,7 +29,7 @@ public Service(Metrics metrics, MetricCache cache) { } public void countHour(Context ctx) { - var stats = metrics().service().hour(offset(ctx, MAX_HOUR_OFFSET), count(ctx, MAX_HOURS)).join(); + var stats = metrics().service().hour(offset(ctx, MAX_HOUR_OFFSET), count(ctx, MAX_HOURS)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -38,7 +38,7 @@ public void countHour(Context ctx) { } public void countDay(Context ctx) { - var stats = metrics().service().day(offset(ctx, MAX_DAY_OFFSET), count(ctx, MAX_DAYS)).join(); + var stats = metrics().service().day(offset(ctx, MAX_DAY_OFFSET), count(ctx, MAX_DAYS)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -47,7 +47,7 @@ public void countDay(Context ctx) { } public void countWeek(Context ctx) { - var stats = metrics().service().week(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)).join(); + var stats = metrics().service().week(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -56,7 +56,7 @@ public void countWeek(Context ctx) { } public void countMonth(Context ctx) { - var stats = metrics().service().month(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)).join(); + var stats = metrics().service().month(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { diff --git a/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Users.java b/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Users.java index d95adf773..ffbad85df 100644 --- a/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Users.java +++ b/src/main/java/de/chojo/repbot/web/routes/v1/metrics/Users.java @@ -25,7 +25,7 @@ public Users(Metrics metrics, MetricCache cache) { } public void activeWeek(Context ctx) { - var stats = metrics().users().week(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)).join(); + var stats = metrics().users().week(offset(ctx, MAX_WEEK_OFFSET), count(ctx, MAX_WEEKS)); if ("application/json".equalsIgnoreCase(ctx.header("Accept"))) { ctx.json(stats); } else { @@ -34,7 +34,7 @@ public void activeWeek(Context ctx) { } public void activeMonth(Context ctx) { - var stats = metrics().users().month(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)).join(); + var stats = metrics().users().month(offset(ctx, MAX_MONTH_OFFSET), count(ctx, MAX_MONTH)); if ("application/json".equals(ctx.header("Accept"))) { ctx.json(stats); } else { From 61e3c4c6999e6d5bb2527c875d01cb3249ebbff6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 15 Jul 2024 20:12:41 +0000 Subject: [PATCH 22/23] Update sadu to v2.2.1 --- settings.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 9bda9b772..5c20c6244 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,7 +8,7 @@ dependencyResolutionManagement { versionCatalogs { create("libs") { // misc - version("sadu", "2.2.0") + version("sadu", "2.2.1") library("sadu-queries", "de.chojo.sadu", "sadu-queries").versionRef("sadu") library("sadu-updater", "de.chojo.sadu", "sadu-updater").versionRef("sadu") library("sadu-postgresql", "de.chojo.sadu", "sadu-postgresql").versionRef("sadu") From fb69da9fe851c864758f9525bfd7fdf4ccef2c68 Mon Sep 17 00:00:00 2001 From: Niklas van Schrick Date: Mon, 5 Aug 2024 20:08:39 +0200 Subject: [PATCH 23/23] Fix analyzer command (#617) --- .../java/de/chojo/repbot/commands/log/handler/BaseAnalyzer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/chojo/repbot/commands/log/handler/BaseAnalyzer.java b/src/main/java/de/chojo/repbot/commands/log/handler/BaseAnalyzer.java index e842bbed2..f9f3709ef 100644 --- a/src/main/java/de/chojo/repbot/commands/log/handler/BaseAnalyzer.java +++ b/src/main/java/de/chojo/repbot/commands/log/handler/BaseAnalyzer.java @@ -14,7 +14,7 @@ public class BaseAnalyzer { public void onSlashCommand(SlashCommandInteractionEvent event, EventContext context, Reputation reputation) { - var optMessageId = ValueParser.parseLong(event.getOption("message_id").getAsString()); + var optMessageId = ValueParser.parseLong(event.getOption("messageid").getAsString()); if (optMessageId.isEmpty()) { event.reply(context.localize("error.invalidMessage")).setEphemeral(true).queue(); return;