From 956bdcfcb46b7598cd388dcdcb10bbe302c541f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 08:54:44 +0200 Subject: [PATCH 01/37] Bump jacksonVersion from 2.18.1 to 2.19.0 (#1221) Bumps `jacksonVersion` from 2.18.1 to 2.19.0. Updates `com.fasterxml.jackson.dataformat:jackson-dataformat-csv` from 2.18.1 to 2.19.0 - [Commits](https://github.com/FasterXML/jackson-dataformats-text/compare/jackson-dataformats-text-2.18.1...jackson-dataformats-text-2.19.0) Updates `com.fasterxml.jackson.datatype:jackson-datatype-jsr310` from 2.18.1 to 2.19.0 Updates `com.fasterxml.jackson.dataformat:jackson-dataformat-xml` from 2.18.1 to 2.19.0 - [Commits](https://github.com/FasterXML/jackson-dataformat-xml/compare/jackson-dataformat-xml-2.18.1...jackson-dataformat-xml-2.19.0) Updates `com.fasterxml.jackson.core:jackson-databind` from 2.18.1 to 2.19.0 - [Commits](https://github.com/FasterXML/jackson/commits) --- updated-dependencies: - dependency-name: com.fasterxml.jackson.dataformat:jackson-dataformat-csv dependency-version: 2.19.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.fasterxml.jackson.datatype:jackson-datatype-jsr310 dependency-version: 2.19.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.fasterxml.jackson.dataformat:jackson-dataformat-xml dependency-version: 2.19.0 dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: com.fasterxml.jackson.core:jackson-databind dependency-version: 2.19.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 3546b7a0b0..dc56c87c05 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ version '1.0-SNAPSHOT' ext { jooqVersion = '3.19.1' - jacksonVersion = '2.18.1' + jacksonVersion = '2.19.0' chatGPTVersion = '0.18.0' } From 514a6cf3b2dd833d5049fc2b17eb2603fdf7062c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 08:54:52 +0200 Subject: [PATCH 02/37] Bump org.kohsuke:github-api from 1.326 to 1.327 (#1223) Bumps [org.kohsuke:github-api](https://github.com/hub4j/github-api) from 1.326 to 1.327. - [Release notes](https://github.com/hub4j/github-api/releases) - [Changelog](https://github.com/hub4j/github-api/blob/main/CHANGELOG.md) - [Commits](https://github.com/hub4j/github-api/compare/github-api-1.326...github-api-1.327) --- updated-dependencies: - dependency-name: org.kohsuke:github-api dependency-version: '1.327' dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- application/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/build.gradle b/application/build.gradle index 7be2660eee..de7ecae554 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -75,7 +75,7 @@ dependencies { implementation 'com.github.ben-manes.caffeine:caffeine:3.1.1' - implementation 'org.kohsuke:github-api:1.326' + implementation 'org.kohsuke:github-api:1.327' implementation 'org.apache.commons:commons-text:1.12.0' implementation 'com.apptasticsoftware:rssreader:3.8.1' From a995f8b8d7657c958cd8dbd456d06a8ac81d9d25 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 08:55:03 +0200 Subject: [PATCH 03/37] Bump org.jsoup:jsoup from 1.18.1 to 1.20.1 (#1225) Bumps [org.jsoup:jsoup](https://github.com/jhy/jsoup) from 1.18.1 to 1.20.1. - [Release notes](https://github.com/jhy/jsoup/releases) - [Changelog](https://github.com/jhy/jsoup/blob/master/CHANGES.md) - [Commits](https://github.com/jhy/jsoup/compare/jsoup-1.18.1...jsoup-1.20.1) --- updated-dependencies: - dependency-name: org.jsoup:jsoup dependency-version: 1.20.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- application/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/build.gradle b/application/build.gradle index de7ecae554..d0142d74e1 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -57,7 +57,7 @@ dependencies { implementation 'io.mikael:urlbuilder:2.0.9' - implementation 'org.jsoup:jsoup:1.18.1' + implementation 'org.jsoup:jsoup:1.20.1' implementation 'org.scilab.forge:jlatexmath:1.0.7' implementation 'org.scilab.forge:jlatexmath-font-greek:1.0.7' From e1aa007a13fc55fd73009d8b07912a25f164882a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 08:55:10 +0200 Subject: [PATCH 04/37] Bump org.gradle.toolchains.foojay-resolver-convention (#1227) Bumps org.gradle.toolchains.foojay-resolver-convention from 0.8.0 to 0.10.0. --- updated-dependencies: - dependency-name: org.gradle.toolchains.foojay-resolver-convention dependency-version: 0.10.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index fcbfb6d3e2..1cb49c1b87 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.gradle.toolchains.foojay-resolver-convention' version '0.8.0' + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.10.0' } rootProject.name = 'TJ-Bot' From 9cb661c1486ae56548b55936af6f928226cf445a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 08:55:21 +0200 Subject: [PATCH 05/37] Bump org.sonarqube from 6.0.1.5171 to 6.1.0.5360 (#1228) Bumps org.sonarqube from 6.0.1.5171 to 6.1.0.5360. --- updated-dependencies: - dependency-name: org.sonarqube dependency-version: 6.1.0.5360 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index dc56c87c05..ad219ada83 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { id 'java' id "com.diffplug.spotless" version "6.25.0" - id "org.sonarqube" version "6.0.1.5171" + id "org.sonarqube" version "6.1.0.5360" id "name.remal.sonarlint" version "4.2.2" } From 57c3a5fae3497b3791b41fcdaeae814e17e6456b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 08:55:31 +0200 Subject: [PATCH 06/37] Bump org.xerial:sqlite-jdbc from 3.47.1.0 to 3.49.1.0 (#1229) Bumps [org.xerial:sqlite-jdbc](https://github.com/xerial/sqlite-jdbc) from 3.47.1.0 to 3.49.1.0. - [Release notes](https://github.com/xerial/sqlite-jdbc/releases) - [Changelog](https://github.com/xerial/sqlite-jdbc/blob/master/CHANGELOG) - [Commits](https://github.com/xerial/sqlite-jdbc/compare/3.47.1.0...3.49.1.0) --- updated-dependencies: - dependency-name: org.xerial:sqlite-jdbc dependency-version: 3.49.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- application/build.gradle | 2 +- database/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/build.gradle b/application/build.gradle index d0142d74e1..b9ca0f731d 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -1,6 +1,6 @@ buildscript { dependencies { - classpath 'org.xerial:sqlite-jdbc:3.47.1.0' + classpath 'org.xerial:sqlite-jdbc:3.49.1.0' } } diff --git a/database/build.gradle b/database/build.gradle index 9ec27d228d..94ad3ace93 100644 --- a/database/build.gradle +++ b/database/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java' } -var sqliteVersion = "3.47.1.0" +var sqliteVersion = "3.49.1.0" dependencies { implementation 'com.google.code.findbugs:jsr305:3.0.2' From 4bdb4dacc304bdcebfbb8c04e0a8cab8848e035a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 09:05:22 +0200 Subject: [PATCH 07/37] Bump com.apptasticsoftware:rssreader from 3.8.1 to 3.9.3 (#1230) Bumps [com.apptasticsoftware:rssreader](https://github.com/w3stling/rssreader) from 3.8.1 to 3.9.3. - [Release notes](https://github.com/w3stling/rssreader/releases) - [Commits](https://github.com/w3stling/rssreader/compare/v3.8.1...v3.9.3) --- updated-dependencies: - dependency-name: com.apptasticsoftware:rssreader dependency-version: 3.9.3 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- application/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/build.gradle b/application/build.gradle index b9ca0f731d..61aec1dca9 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -78,7 +78,7 @@ dependencies { implementation 'org.kohsuke:github-api:1.327' implementation 'org.apache.commons:commons-text:1.12.0' - implementation 'com.apptasticsoftware:rssreader:3.8.1' + implementation 'com.apptasticsoftware:rssreader:3.9.3' testImplementation 'org.mockito:mockito-core:5.17.0' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.4' From 44eb430a9b2d0a6b4b91650b84c7d16f84a45a0b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 May 2025 09:17:08 +0200 Subject: [PATCH 08/37] Bump org.jooq:jooq from 3.19.1 to 3.20.4 (#1224) Bumps org.jooq:jooq from 3.19.1 to 3.20.4. --- updated-dependencies: - dependency-name: org.jooq:jooq dependency-version: 3.20.4 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index ad219ada83..0c6c1a9a19 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ group 'org.togetherjava' version '1.0-SNAPSHOT' ext { - jooqVersion = '3.19.1' + jooqVersion = '3.20.4' jacksonVersion = '2.19.0' chatGPTVersion = '0.18.0' } From aeb504d854c1cc254edb90b02cf862a28bc2c1c3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 08:42:53 +0200 Subject: [PATCH 09/37] Bump org.flywaydb:flyway-core from 11.1.0 to 11.8.0 (#1237) Bumps [org.flywaydb:flyway-core](https://github.com/flyway/flyway) from 11.1.0 to 11.8.0. - [Release notes](https://github.com/flyway/flyway/releases) - [Commits](https://github.com/flyway/flyway/compare/flyway-11.1.0...flyway-11.8.0) --- updated-dependencies: - dependency-name: org.flywaydb:flyway-core dependency-version: 11.8.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- database/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/build.gradle b/database/build.gradle index 94ad3ace93..3708741e79 100644 --- a/database/build.gradle +++ b/database/build.gradle @@ -7,7 +7,7 @@ var sqliteVersion = "3.49.1.0" dependencies { implementation 'com.google.code.findbugs:jsr305:3.0.2' implementation "org.xerial:sqlite-jdbc:${sqliteVersion}" - implementation 'org.flywaydb:flyway-core:11.1.0' + implementation 'org.flywaydb:flyway-core:11.8.0' implementation "org.jooq:jooq:$jooqVersion" implementation project(':utils') From be58c7bc674c7d0ed210b1d10b2c55d7ea08c54c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 08:43:04 +0200 Subject: [PATCH 10/37] Bump com.github.ben-manes.caffeine:caffeine from 3.1.1 to 3.2.0 (#1233) Bumps [com.github.ben-manes.caffeine:caffeine](https://github.com/ben-manes/caffeine) from 3.1.1 to 3.2.0. - [Release notes](https://github.com/ben-manes/caffeine/releases) - [Commits](https://github.com/ben-manes/caffeine/compare/v3.1.1...v3.2.0) --- updated-dependencies: - dependency-name: com.github.ben-manes.caffeine:caffeine dependency-version: 3.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- application/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/build.gradle b/application/build.gradle index 61aec1dca9..e7abe87ef1 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -73,7 +73,7 @@ dependencies { implementation 'io.github.url-detector:url-detector:0.1.23' - implementation 'com.github.ben-manes.caffeine:caffeine:3.1.1' + implementation 'com.github.ben-manes.caffeine:caffeine:3.2.0' implementation 'org.kohsuke:github-api:1.327' From 54fb954b7163ee3799d897f7c1ac489278c411c9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 08:43:13 +0200 Subject: [PATCH 11/37] Bump org.apache.commons:commons-text from 1.12.0 to 1.13.1 (#1234) Bumps org.apache.commons:commons-text from 1.12.0 to 1.13.1. --- updated-dependencies: - dependency-name: org.apache.commons:commons-text dependency-version: 1.13.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- application/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/build.gradle b/application/build.gradle index e7abe87ef1..afddbc3761 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -77,7 +77,7 @@ dependencies { implementation 'org.kohsuke:github-api:1.327' - implementation 'org.apache.commons:commons-text:1.12.0' + implementation 'org.apache.commons:commons-text:1.13.1' implementation 'com.apptasticsoftware:rssreader:3.9.3' testImplementation 'org.mockito:mockito-core:5.17.0' From a51375efe8aa4a2d231dd7dc195a150c000fae96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 6 May 2025 08:43:20 +0200 Subject: [PATCH 12/37] Bump com.diffplug.spotless from 6.25.0 to 7.0.3 (#1232) Bumps com.diffplug.spotless from 6.25.0 to 7.0.3. --- updated-dependencies: - dependency-name: com.diffplug.spotless dependency-version: 7.0.3 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 0c6c1a9a19..f803c36d95 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,6 @@ plugins { id 'java' - id "com.diffplug.spotless" version "6.25.0" + id "com.diffplug.spotless" version "7.0.3" id "org.sonarqube" version "6.1.0.5360" id "name.remal.sonarlint" version "4.2.2" } From ace5c37b3321833f58346e0b444b61db085d183b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 16 May 2025 07:35:05 +0200 Subject: [PATCH 13/37] Bump org.sonarqube from 6.1.0.5360 to 6.2.0.5505 (#1238) Bumps org.sonarqube from 6.1.0.5360 to 6.2.0.5505. --- updated-dependencies: - dependency-name: org.sonarqube dependency-version: 6.2.0.5505 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index f803c36d95..7858bdea6a 100644 --- a/build.gradle +++ b/build.gradle @@ -1,7 +1,7 @@ plugins { id 'java' id "com.diffplug.spotless" version "7.0.3" - id "org.sonarqube" version "6.1.0.5360" + id "org.sonarqube" version "6.2.0.5505" id "name.remal.sonarlint" version "4.2.2" } From b0a97b0f7764a48dcf3c0334622b7dadd3040c67 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 20 May 2025 08:21:26 +0200 Subject: [PATCH 14/37] Bump org.gradle.toolchains.foojay-resolver-convention (#1239) Bumps org.gradle.toolchains.foojay-resolver-convention from 0.10.0 to 1.0.0. --- updated-dependencies: - dependency-name: org.gradle.toolchains.foojay-resolver-convention dependency-version: 1.0.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- settings.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.gradle b/settings.gradle index 1cb49c1b87..ae0349509c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,5 @@ plugins { - id 'org.gradle.toolchains.foojay-resolver-convention' version '0.10.0' + id 'org.gradle.toolchains.foojay-resolver-convention' version '1.0.0' } rootProject.name = 'TJ-Bot' From 1eae4de88b21a482a171c2cc69d914559c851c5a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 21 May 2025 07:50:34 +0200 Subject: [PATCH 15/37] Bump org.mockito:mockito-core from 5.17.0 to 5.18.0 (#1240) Bumps [org.mockito:mockito-core](https://github.com/mockito/mockito) from 5.17.0 to 5.18.0. - [Release notes](https://github.com/mockito/mockito/releases) - [Commits](https://github.com/mockito/mockito/compare/v5.17.0...v5.18.0) --- updated-dependencies: - dependency-name: org.mockito:mockito-core dependency-version: 5.18.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- application/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/build.gradle b/application/build.gradle index afddbc3761..2f5c8eb868 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -80,7 +80,7 @@ dependencies { implementation 'org.apache.commons:commons-text:1.13.1' implementation 'com.apptasticsoftware:rssreader:3.9.3' - testImplementation 'org.mockito:mockito-core:5.17.0' + testImplementation 'org.mockito:mockito-core:5.18.0' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.4' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.0' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' From 8224f93ae533ba2f1cd02e8b76046b8b798d868c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Jun 2025 11:45:22 +0200 Subject: [PATCH 16/37] Bump net.dv8tion:JDA from 5.2.1 to 5.5.1 (#1236) * Bump net.dv8tion:JDA from 5.2.1 to 5.5.1 Bumps [net.dv8tion:JDA](https://github.com/discord-jda/JDA) from 5.2.1 to 5.5.1. - [Release notes](https://github.com/discord-jda/JDA/releases) - [Commits](https://github.com/discord-jda/JDA/compare/v5.2.1...v5.5.1) --- updated-dependencies: - dependency-name: net.dv8tion:JDA dependency-version: 5.5.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Fixed failing tests * Removed deprecated setOnlyGuild method usage --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Connor Schweighoefer --- application/build.gradle | 2 +- .../tjbot/features/BotCommandAdapter.java | 11 ++++- .../org/togetherjava/tjbot/jda/JdaTester.java | 4 +- .../SlashCommandInteractionEventBuilder.java | 12 ++++- .../tjbot/jda/payloads/PayloadGuild.java | 44 +++++++++++++++++ .../slashcommand/PayloadSlashCommand.java | 48 ++++++++++++++++++- 6 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 application/src/test/java/org/togetherjava/tjbot/jda/payloads/PayloadGuild.java diff --git a/application/build.gradle b/application/build.gradle index 2f5c8eb868..c7666b9819 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -46,7 +46,7 @@ dependencies { implementation project(':utils') implementation project(':formatter') - implementation 'net.dv8tion:JDA:5.2.1' + implementation 'net.dv8tion:JDA:5.5.1' implementation 'org.apache.logging.log4j:log4j-core:2.24.3' runtimeOnly 'org.apache.logging.log4j:log4j-slf4j18-impl:2.18.0' diff --git a/application/src/main/java/org/togetherjava/tjbot/features/BotCommandAdapter.java b/application/src/main/java/org/togetherjava/tjbot/features/BotCommandAdapter.java index 471222df2a..a341f99884 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/BotCommandAdapter.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/BotCommandAdapter.java @@ -5,6 +5,7 @@ import net.dv8tion.jda.api.events.interaction.component.ButtonInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.EntitySelectInteractionEvent; import net.dv8tion.jda.api.events.interaction.component.StringSelectInteractionEvent; +import net.dv8tion.jda.api.interactions.InteractionContextType; import net.dv8tion.jda.api.interactions.commands.Command; import net.dv8tion.jda.api.interactions.commands.build.CommandData; import org.jetbrains.annotations.Contract; @@ -15,6 +16,7 @@ import java.util.List; import java.util.Objects; +import java.util.Set; /** * Adapter implementation of a {@link BotCommand}. The minimal setup only requires implementation of @@ -51,7 +53,14 @@ public abstract class BotCommandAdapter implements BotCommand { * @param visibility the visibility of the command */ protected BotCommandAdapter(CommandData data, CommandVisibility visibility) { - this.data = data.setGuildOnly(visibility == CommandVisibility.GUILD); + this.data = data; + + Set contexts = switch (visibility) { + case GUILD -> Set.of(InteractionContextType.GUILD); + case GLOBAL -> InteractionContextType.ALL; + }; + + data.setContexts(contexts); this.visibility = Objects.requireNonNull(visibility, "The visibility shouldn't be null"); name = data.getName(); diff --git a/application/src/test/java/org/togetherjava/tjbot/jda/JdaTester.java b/application/src/test/java/org/togetherjava/tjbot/jda/JdaTester.java index ce6458fb0f..bf42857817 100644 --- a/application/src/test/java/org/togetherjava/tjbot/jda/JdaTester.java +++ b/application/src/test/java/org/togetherjava/tjbot/jda/JdaTester.java @@ -31,9 +31,9 @@ import net.dv8tion.jda.api.requests.SequentialRestRateLimiter; import net.dv8tion.jda.api.requests.restaction.CacheRestAction; import net.dv8tion.jda.api.requests.restaction.interactions.ReplyCallbackAction; -import net.dv8tion.jda.api.utils.AttachmentProxy; import net.dv8tion.jda.api.utils.ConcurrentSessionController; import net.dv8tion.jda.api.utils.FileUpload; +import net.dv8tion.jda.api.utils.NamedAttachmentProxy; import net.dv8tion.jda.api.utils.cache.CacheFlag; import net.dv8tion.jda.api.utils.messages.MessageCreateData; import net.dv8tion.jda.api.utils.messages.MessageEditData; @@ -770,7 +770,7 @@ public Message clientMessageToReceivedMessageMock(MessageCreateData clientMessag private Message.Attachment clientAttachmentToReceivedAttachmentMock( FileUpload clientAttachment) { Message.Attachment receivedAttachment = mock(Message.Attachment.class); - AttachmentProxy attachmentProxy = mock(AttachmentProxy.class); + NamedAttachmentProxy attachmentProxy = mock(NamedAttachmentProxy.class); when(receivedAttachment.getJDA()).thenReturn(jda); when(receivedAttachment.getFileName()).thenReturn(clientAttachment.getName()); diff --git a/application/src/test/java/org/togetherjava/tjbot/jda/SlashCommandInteractionEventBuilder.java b/application/src/test/java/org/togetherjava/tjbot/jda/SlashCommandInteractionEventBuilder.java index 77533a44df..9206e428da 100644 --- a/application/src/test/java/org/togetherjava/tjbot/jda/SlashCommandInteractionEventBuilder.java +++ b/application/src/test/java/org/togetherjava/tjbot/jda/SlashCommandInteractionEventBuilder.java @@ -5,15 +5,19 @@ import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.User; import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent; +import net.dv8tion.jda.api.interactions.IntegrationOwners; +import net.dv8tion.jda.api.interactions.InteractionContextType; import net.dv8tion.jda.api.interactions.commands.OptionType; import net.dv8tion.jda.api.interactions.commands.build.OptionData; import net.dv8tion.jda.api.interactions.commands.build.SubcommandData; import net.dv8tion.jda.api.utils.data.DataObject; import net.dv8tion.jda.internal.JDAImpl; +import net.dv8tion.jda.internal.interactions.IntegrationOwnersImpl; import net.dv8tion.jda.internal.interactions.command.SlashCommandInteractionImpl; import org.togetherjava.tjbot.features.SlashCommand; import org.togetherjava.tjbot.jda.payloads.PayloadChannel; +import org.togetherjava.tjbot.jda.payloads.PayloadGuild; import org.togetherjava.tjbot.jda.payloads.PayloadMember; import org.togetherjava.tjbot.jda.payloads.PayloadUser; import org.togetherjava.tjbot.jda.payloads.slashcommand.PayloadSlashCommand; @@ -28,6 +32,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.function.Supplier; import java.util.function.UnaryOperator; import java.util.stream.Collectors; @@ -278,6 +283,8 @@ private PayloadSlashCommand createEvent() { "1099511627775", List.of(), false, false, false, null, false, user); PayloadChannel channel = new PayloadChannel(channelId, 1); + PayloadGuild guild = new PayloadGuild(Long.parseLong(guildId), "en-US", Set.of()); + List options; if (subcommand == null) { options = extractOptionsOrNull(nameToOption); @@ -288,8 +295,11 @@ private PayloadSlashCommand createEvent() { PayloadSlashCommandData data = new PayloadSlashCommandData(command.getName(), "1", 1, options, extractResolvedOrNull(nameToOption)); + IntegrationOwners owners = new IntegrationOwnersImpl(DataObject.empty()); + return new PayloadSlashCommand(guildId, "897425767397466123", 2, 1, applicationId, token, - member, channel, data); + member, channel, guild, data, Long.parseLong(channelId), + InteractionContextType.PRIVATE_CHANNEL, owners); } @Nullable diff --git a/application/src/test/java/org/togetherjava/tjbot/jda/payloads/PayloadGuild.java b/application/src/test/java/org/togetherjava/tjbot/jda/payloads/PayloadGuild.java new file mode 100644 index 0000000000..d2524691dd --- /dev/null +++ b/application/src/test/java/org/togetherjava/tjbot/jda/payloads/PayloadGuild.java @@ -0,0 +1,44 @@ +package org.togetherjava.tjbot.jda.payloads; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.Set; + +public class PayloadGuild { + + private long id; + @JsonProperty("preferred_locale") + private String preferredLocale; + private Set features; + + public PayloadGuild(long id, String preferredLocale, Set features) { + this.id = id; + this.preferredLocale = preferredLocale; + this.features = features; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getPreferredLocale() { + return preferredLocale; + } + + public void setPreferredLocale(String preferredLocale) { + this.preferredLocale = preferredLocale; + } + + public Set getFeatures() { + return features; + } + + public void setFeatures(Set features) { + this.features = features; + } + +} diff --git a/application/src/test/java/org/togetherjava/tjbot/jda/payloads/slashcommand/PayloadSlashCommand.java b/application/src/test/java/org/togetherjava/tjbot/jda/payloads/slashcommand/PayloadSlashCommand.java index 8108d2026d..246744d372 100644 --- a/application/src/test/java/org/togetherjava/tjbot/jda/payloads/slashcommand/PayloadSlashCommand.java +++ b/application/src/test/java/org/togetherjava/tjbot/jda/payloads/slashcommand/PayloadSlashCommand.java @@ -1,8 +1,11 @@ package org.togetherjava.tjbot.jda.payloads.slashcommand; import com.fasterxml.jackson.annotation.JsonProperty; +import net.dv8tion.jda.api.interactions.IntegrationOwners; +import net.dv8tion.jda.api.interactions.InteractionContextType; import org.togetherjava.tjbot.jda.payloads.PayloadChannel; +import org.togetherjava.tjbot.jda.payloads.PayloadGuild; import org.togetherjava.tjbot.jda.payloads.PayloadMember; public final class PayloadSlashCommand { @@ -16,11 +19,18 @@ public final class PayloadSlashCommand { private String token; private PayloadMember member; private PayloadChannel channel; + private PayloadGuild guild; private PayloadSlashCommandData data; + @JsonProperty("channel_id") + private long channelId; + private InteractionContextType context; + @JsonProperty("authorizing_integration_owners") + private IntegrationOwners integrationOwners; public PayloadSlashCommand(String guildId, String id, int type, int version, String applicationId, String token, PayloadMember member, PayloadChannel channel, - PayloadSlashCommandData data) { + PayloadGuild guild, PayloadSlashCommandData data, long channelId, + InteractionContextType context, IntegrationOwners integrationOwners) { this.guildId = guildId; this.id = id; this.type = type; @@ -29,7 +39,11 @@ public PayloadSlashCommand(String guildId, String id, int type, int version, this.token = token; this.member = member; this.channel = channel; + this.guild = guild; this.data = data; + this.channelId = channelId; + this.context = context; + this.integrationOwners = integrationOwners; } public String getGuildId() { @@ -96,6 +110,14 @@ public void setChannel(PayloadChannel channel) { this.channel = channel; } + public PayloadGuild getGuild() { + return guild; + } + + public void setGuild(PayloadGuild guild) { + this.guild = guild; + } + public PayloadSlashCommandData getData() { return data; } @@ -104,4 +126,28 @@ public void setData(PayloadSlashCommandData data) { this.data = data; } + public long getChannelId() { + return channelId; + } + + public void setChannelId(long channelId) { + this.channelId = channelId; + } + + public InteractionContextType getContext() { + return context; + } + + public void setContext(InteractionContextType context) { + this.context = context; + } + + public IntegrationOwners getIntegrationOwners() { + return integrationOwners; + } + + public void setIntegrationOwners(IntegrationOwners integrationOwners) { + this.integrationOwners = integrationOwners; + } + } From 8afd27c1ebbf494e5af4cb4b780d72fa77fd3969 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Connor=20Schweigh=C3=B6fer?= Date: Sun, 1 Jun 2025 11:46:34 +0200 Subject: [PATCH 17/37] Optimize auto prune routine to not spam requests for all users (#1208) --- .../features/help/AutoPruneHelperRoutine.java | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/AutoPruneHelperRoutine.java b/application/src/main/java/org/togetherjava/tjbot/features/help/AutoPruneHelperRoutine.java index b04e01776a..6ed381a36e 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/AutoPruneHelperRoutine.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/AutoPruneHelperRoutine.java @@ -84,23 +84,36 @@ public void runRoutine(JDA jda) { } private void pruneForGuild(Guild guild) { - Instant now = Instant.now(); TextChannel selectRoleChannel = getSelectRolesChannelOptional(guild.getJDA()).orElse(null); + guild.loadMembers() + .onSuccess(members -> pruneCategories(guild, members, selectRoleChannel)) + .onError(throwable -> logger.error("Failed to request all members for auto prune.", + throwable)); + } + + private void pruneCategories(Guild guild, List members, + @Nullable TextChannel selectRoleChannel) { + Instant now = Instant.now(); allCategories.stream() .map(category -> helper.handleFindRoleForCategory(category, guild)) .filter(Optional::isPresent) .map(Optional::orElseThrow) - .forEach(role -> pruneRoleIfFull(role, selectRoleChannel, now)); + .forEach(role -> pruneRoleIfFull(members, role, selectRoleChannel, now)); } - private void pruneRoleIfFull(Role role, @Nullable TextChannel selectRoleChannel, Instant when) { - role.getGuild().findMembersWithRoles(role).onSuccess(members -> { - if (isRoleFull(members)) { - logger.debug("Helper role {} is full, starting to prune.", role.getName()); - pruneRole(role, members, selectRoleChannel, when); - } - }); + private void pruneRoleIfFull(List members, Role targetRole, + @Nullable TextChannel selectRoleChannel, Instant when) { + List withRole = filterForRole(members, targetRole); + + if (isRoleFull(withRole)) { + logger.debug("Helper role {} is full, starting to prune.", targetRole.getName()); + pruneRole(targetRole, withRole, selectRoleChannel, when); + } + } + + private List filterForRole(List members, Role role) { + return members.stream().filter(member -> member.getRoles().contains(role)).toList(); } private boolean isRoleFull(Collection members) { From 08a31445446a937a7b212749ee79a9cd5dff8a33 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 1 Jun 2025 11:47:11 +0200 Subject: [PATCH 18/37] Bump com.sigpwned:jackson-modules-java17-sealed-classes (#1242) Bumps [com.sigpwned:jackson-modules-java17-sealed-classes](https://github.com/sigpwned/jackson-modules-java-17-sealed-classes) from 0.0.0 to 2.14.3.0. - [Commits](https://github.com/sigpwned/jackson-modules-java-17-sealed-classes/compare/v0.0.0...v2.14.3.0) --- updated-dependencies: - dependency-name: com.sigpwned:jackson-modules-java17-sealed-classes dependency-version: 2.14.3.0 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- application/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/build.gradle b/application/build.gradle index c7666b9819..3b689ddf0b 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -67,7 +67,7 @@ dependencies { implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion" implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:$jacksonVersion" implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion" - implementation "com.sigpwned:jackson-modules-java17-sealed-classes:0.0.0" + implementation "com.sigpwned:jackson-modules-java17-sealed-classes:2.14.3.0" implementation 'com.github.freva:ascii-table:1.8.0' From 51ce33ab061b338e32c2346596821156e6e698a6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 2 Jun 2025 08:53:37 +0200 Subject: [PATCH 19/37] Bump com.sigpwned:jackson-modules-java17-sealed-classes (#1246) Bumps [com.sigpwned:jackson-modules-java17-sealed-classes](https://github.com/sigpwned/jackson-modules-java-17-sealed-classes) from 2.14.3.0 to 2.19.0.0. - [Commits](https://github.com/sigpwned/jackson-modules-java-17-sealed-classes/compare/v2.14.3.0...v2.19.0.0) --- updated-dependencies: - dependency-name: com.sigpwned:jackson-modules-java17-sealed-classes dependency-version: 2.19.0.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- application/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/build.gradle b/application/build.gradle index 3b689ddf0b..ddc5b93ce5 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -67,7 +67,7 @@ dependencies { implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:$jacksonVersion" implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-xml:$jacksonVersion" implementation "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion" - implementation "com.sigpwned:jackson-modules-java17-sealed-classes:2.14.3.0" + implementation "com.sigpwned:jackson-modules-java17-sealed-classes:2.19.0.0" implementation 'com.github.freva:ascii-table:1.8.0' From f1ce79ded52c7d70a652e078d0c39969abb5bdcf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 3 Jun 2025 08:59:31 +0200 Subject: [PATCH 20/37] Bump org.flywaydb:flyway-core from 11.8.0 to 11.9.0 (#1248) Bumps [org.flywaydb:flyway-core](https://github.com/flyway/flyway) from 11.8.0 to 11.9.0. - [Release notes](https://github.com/flyway/flyway/releases) - [Commits](https://github.com/flyway/flyway/compare/flyway-11.8.0...flyway-11.9.0) --- updated-dependencies: - dependency-name: org.flywaydb:flyway-core dependency-version: 11.9.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- database/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/build.gradle b/database/build.gradle index 3708741e79..535e518c89 100644 --- a/database/build.gradle +++ b/database/build.gradle @@ -7,7 +7,7 @@ var sqliteVersion = "3.49.1.0" dependencies { implementation 'com.google.code.findbugs:jsr305:3.0.2' implementation "org.xerial:sqlite-jdbc:${sqliteVersion}" - implementation 'org.flywaydb:flyway-core:11.8.0' + implementation 'org.flywaydb:flyway-core:11.9.0' implementation "org.jooq:jooq:$jooqVersion" implementation project(':utils') From 7decc98b8477def30ba8fab82074c6c016a17d5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 10 Jun 2025 08:49:56 +0200 Subject: [PATCH 21/37] Bump org.xerial:sqlite-jdbc from 3.49.1.0 to 3.50.1.0 (#1254) Bumps [org.xerial:sqlite-jdbc](https://github.com/xerial/sqlite-jdbc) from 3.49.1.0 to 3.50.1.0. - [Release notes](https://github.com/xerial/sqlite-jdbc/releases) - [Changelog](https://github.com/xerial/sqlite-jdbc/blob/master/CHANGELOG) - [Commits](https://github.com/xerial/sqlite-jdbc/compare/3.49.1.0...3.50.1.0) --- updated-dependencies: - dependency-name: org.xerial:sqlite-jdbc dependency-version: 3.50.1.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- application/build.gradle | 2 +- database/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/build.gradle b/application/build.gradle index ddc5b93ce5..dd4cadb725 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -1,6 +1,6 @@ buildscript { dependencies { - classpath 'org.xerial:sqlite-jdbc:3.49.1.0' + classpath 'org.xerial:sqlite-jdbc:3.50.1.0' } } diff --git a/database/build.gradle b/database/build.gradle index 535e518c89..4d6bad5c16 100644 --- a/database/build.gradle +++ b/database/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java' } -var sqliteVersion = "3.49.1.0" +var sqliteVersion = "3.50.1.0" dependencies { implementation 'com.google.code.findbugs:jsr305:3.0.2' From 76a05906e29c75825090a7b300675c5e538976d9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 07:36:44 +0200 Subject: [PATCH 22/37] Bump org.apache.logging.log4j:log4j-core from 2.24.3 to 2.25.0 (#1255) Bumps org.apache.logging.log4j:log4j-core from 2.24.3 to 2.25.0. --- updated-dependencies: - dependency-name: org.apache.logging.log4j:log4j-core dependency-version: 2.25.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- application/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/build.gradle b/application/build.gradle index dd4cadb725..4febda4793 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -48,7 +48,7 @@ dependencies { implementation 'net.dv8tion:JDA:5.5.1' - implementation 'org.apache.logging.log4j:log4j-core:2.24.3' + implementation 'org.apache.logging.log4j:log4j-core:2.25.0' runtimeOnly 'org.apache.logging.log4j:log4j-slf4j18-impl:2.18.0' implementation 'club.minnced:discord-webhooks:0.8.2' From 96b7f55e1d016c5eec88f9bc2e71a922f57c6c4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 17 Jun 2025 07:47:24 +0200 Subject: [PATCH 23/37] Bump net.dv8tion:JDA from 5.5.1 to 5.6.1 (#1251) Bumps [net.dv8tion:JDA](https://github.com/discord-jda/JDA) from 5.5.1 to 5.6.1. - [Release notes](https://github.com/discord-jda/JDA/releases) - [Commits](https://github.com/discord-jda/JDA/compare/v5.5.1...v5.6.1) --- updated-dependencies: - dependency-name: net.dv8tion:JDA dependency-version: 5.6.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- application/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/build.gradle b/application/build.gradle index 4febda4793..519ec57319 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -46,7 +46,7 @@ dependencies { implementation project(':utils') implementation project(':formatter') - implementation 'net.dv8tion:JDA:5.5.1' + implementation 'net.dv8tion:JDA:5.6.1' implementation 'org.apache.logging.log4j:log4j-core:2.25.0' runtimeOnly 'org.apache.logging.log4j:log4j-slf4j18-impl:2.18.0' From ae350dbb138cd883317d97518e8643bfd382b4c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 24 Jun 2025 08:34:13 +0200 Subject: [PATCH 24/37] Bump org.jsoup:jsoup from 1.20.1 to 1.21.1 (#1256) Bumps [org.jsoup:jsoup](https://github.com/jhy/jsoup) from 1.20.1 to 1.21.1. - [Release notes](https://github.com/jhy/jsoup/releases) - [Changelog](https://github.com/jhy/jsoup/blob/master/CHANGES.md) - [Commits](https://github.com/jhy/jsoup/compare/jsoup-1.20.1...jsoup-1.21.1) --- updated-dependencies: - dependency-name: org.jsoup:jsoup dependency-version: 1.21.1 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- application/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/build.gradle b/application/build.gradle index 519ec57319..d7313540c0 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -57,7 +57,7 @@ dependencies { implementation 'io.mikael:urlbuilder:2.0.9' - implementation 'org.jsoup:jsoup:1.20.1' + implementation 'org.jsoup:jsoup:1.21.1' implementation 'org.scilab.forge:jlatexmath:1.0.7' implementation 'org.scilab.forge:jlatexmath-font-greek:1.0.7' From 99bc173c8e4f2369c54480ee88a431caf44b81ab Mon Sep 17 00:00:00 2001 From: Daniel Tischner Date: Thu, 26 Jun 2025 09:09:09 +0200 Subject: [PATCH 25/37] changed /modmail stay-anonymous to reveal-name for better UX (#1260) --- .../moderation/modmail/ModMailCommand.java | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/modmail/ModMailCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/modmail/ModMailCommand.java index 3395a8c828..c29a7382a8 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/modmail/ModMailCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/modmail/ModMailCommand.java @@ -47,7 +47,7 @@ public final class ModMailCommand extends SlashCommandAdapter { private static final Logger logger = LoggerFactory.getLogger(ModMailCommand.class); public static final String COMMAND_NAME = "modmail"; private static final String OPTION_MESSAGE = "message"; - private static final String OPTION_STAY_ANONYMOUS = "stay-anonymous"; + private static final String OPTION_REVEAL_NAME = "reveal-name"; private static final String OPTION_GUILD = "server"; private static final int COOLDOWN_DURATION_VALUE = 30; private static final ChronoUnit COOLDOWN_DURATION_UNIT = ChronoUnit.MINUTES; @@ -70,8 +70,9 @@ public ModMailCommand(JDA jda, Config config) { OptionData guildOption = new OptionData(OptionType.STRING, OPTION_GUILD, "The server to contact mods from", true); - OptionData anonymousOption = new OptionData(OptionType.BOOLEAN, OPTION_STAY_ANONYMOUS, - "If set, your name is hidden - note that mods then can not get back to you", true); + OptionData revealNameOption = new OptionData(OptionType.BOOLEAN, OPTION_REVEAL_NAME, + "If set, your name is shown to mods - false means mods can not get back to you", + true); List choices = jda.getGuildCache() .stream() @@ -80,7 +81,7 @@ public ModMailCommand(JDA jda, Config config) { guildOption.addChoices(choices); - getData().addOptions(guildOption, anonymousOption); + getData().addOptions(guildOption, revealNameOption); modMailChannelNamePredicate = Pattern.compile(config.getModMailChannelPattern()).asMatchPredicate(); @@ -112,7 +113,7 @@ public void onSlashCommand(SlashCommandInteractionEvent event) { private void sendMessageModal(SlashCommandInteractionEvent event) { long userGuildId = event.getOption(OPTION_GUILD).getAsLong(); - boolean wantsToStayAnonymous = event.getOption(OPTION_STAY_ANONYMOUS).getAsBoolean(); + boolean wantsToRevealName = event.getOption(OPTION_REVEAL_NAME).getAsBoolean(); TextInput message = TextInput.create(OPTION_MESSAGE, "Your message", TextInputStyle.PARAGRAPH) @@ -120,8 +121,8 @@ private void sendMessageModal(SlashCommandInteractionEvent event) { .setMinLength(3) .build(); - String componentId = generateComponentId(String.valueOf(userGuildId), - String.valueOf(wantsToStayAnonymous)); + String componentId = + generateComponentId(String.valueOf(userGuildId), String.valueOf(wantsToRevealName)); Modal modal = Modal.create(componentId, "Send message to moderators") .addActionRow(message) @@ -136,7 +137,7 @@ public void onModalSubmitted(ModalInteractionEvent event, List args) { long userId = event.getUser().getIdLong(); long userGuildId = Long.parseLong(args.getFirst()); - boolean wantsToStayAnonymous = Boolean.parseBoolean(args.get(1)); + boolean wantsToRevealName = Boolean.parseBoolean(args.get(1)); Optional modMailAuditLog = getModMailChannel(event.getJDA(), userGuildId); if (modMailAuditLog.isEmpty()) { @@ -148,7 +149,7 @@ public void onModalSubmitted(ModalInteractionEvent event, List args) { event.deferReply().setEphemeral(true).queue(); MessageCreateAction message = createModMessage(event, userId, userMessage, - wantsToStayAnonymous, modMailAuditLog.orElseThrow()); + wantsToRevealName, modMailAuditLog.orElseThrow()); sendMessage(event, message); } @@ -172,11 +173,11 @@ private Optional getModMailChannel(JDA jda, long guildId) { } private MessageCreateAction createModMessage(ModalInteractionEvent event, long userId, - String userMessage, boolean wantsToStayAnonymous, TextChannel modMailAuditLog) { - User user = wantsToStayAnonymous ? null : event.getUser(); + String userMessage, boolean wantsToRevealName, TextChannel modMailAuditLog) { + User user = wantsToRevealName ? event.getUser() : null; MessageCreateAction message = modMailAuditLog.sendMessageEmbeds(createModMailMessage(user, userMessage)); - if (!wantsToStayAnonymous) { + if (wantsToRevealName) { message.addActionRow(DiscordClientAction.General.USER.asLinkButton("Author Profile", String.valueOf(userId))); } From 0cbe7b26837caa4fca40cc673973611f3eb8962d Mon Sep 17 00:00:00 2001 From: Firas Regaieg Date: Thu, 26 Jun 2025 14:51:11 +0100 Subject: [PATCH 26/37] Feature: Add useful links to ChatGPT response (#1125) --- .../togetherjava/tjbot/features/help/HelpSystemHelper.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java index 70a96aea7c..725b352a87 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java @@ -217,6 +217,10 @@ private Optional prepareChatGptQuestion(ThreadChannel threadChannel, .min(MAX_QUESTION_LENGTH - questionBuilder.length(), originalQuestion.length())); questionBuilder.append(originalQuestion); + + questionBuilder.append( + ". If possible, get, maximum, 5 top links from reliable websites as references in markdown syntax. Put this message on top of the links list \"Here are some links that may help :\"."); + return Optional.of(questionBuilder.toString()); } From f215304907259ccbe7434850dbd3322333c7aaeb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Jun 2025 11:21:07 +0200 Subject: [PATCH 27/37] Bump org.flywaydb:flyway-core from 11.9.0 to 11.10.0 (#1261) Bumps [org.flywaydb:flyway-core](https://github.com/flyway/flyway) from 11.9.0 to 11.10.0. - [Release notes](https://github.com/flyway/flyway/releases) - [Commits](https://github.com/flyway/flyway/compare/flyway-11.9.0...flyway-11.10.0) --- updated-dependencies: - dependency-name: org.flywaydb:flyway-core dependency-version: 11.10.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- database/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/database/build.gradle b/database/build.gradle index 4d6bad5c16..be4adf2b9d 100644 --- a/database/build.gradle +++ b/database/build.gradle @@ -7,7 +7,7 @@ var sqliteVersion = "3.50.1.0" dependencies { implementation 'com.google.code.findbugs:jsr305:3.0.2' implementation "org.xerial:sqlite-jdbc:${sqliteVersion}" - implementation 'org.flywaydb:flyway-core:11.9.0' + implementation 'org.flywaydb:flyway-core:11.10.0' implementation "org.jooq:jooq:$jooqVersion" implementation project(':utils') From 4f03aa8ed1e44682959772cfa12f12fa8c68fe1c Mon Sep 17 00:00:00 2001 From: Daniel Tischner Date: Fri, 27 Jun 2025 16:12:46 +0200 Subject: [PATCH 28/37] Improved ChatGpt requests and response handling (#1262) * improved chatgpt requests and response handling * markdown hint * (removed extra space) --- .../features/chatgpt/ChatGptCommand.java | 8 +-- .../features/chatgpt/ChatGptService.java | 51 ++++++++++--------- .../tjbot/features/help/HelpSystemHelper.java | 49 +++++++++--------- .../moderation/TransferQuestionCommand.java | 13 +++-- 4 files changed, 61 insertions(+), 60 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptCommand.java index d58c7d8377..163220d8a5 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptCommand.java @@ -80,11 +80,11 @@ public void onSlashCommand(SlashCommandInteractionEvent event) { public void onModalSubmitted(ModalInteractionEvent event, List args) { event.deferReply().queue(); - String context = ""; String question = event.getValue(QUESTION_INPUT).getAsString(); - Optional optional = chatGptService.ask(question, context); - if (optional.isPresent()) { + Optional chatgptResponse = + chatGptService.ask(question, "You may use markdown syntax for the response"); + if (chatgptResponse.isPresent()) { userIdToAskedAtCache.put(event.getMember().getId(), Instant.now()); } @@ -93,7 +93,7 @@ public void onModalSubmitted(ModalInteractionEvent event, List args) { Please try again later. """; - String response = optional.orElse(errorResponse); + String response = chatgptResponse.orElse(errorResponse); SelfUser selfUser = event.getJDA().getSelfUser(); MessageEmbed responseEmbed = helper.generateGptResponseEmbed(response, selfUser, question); diff --git a/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptService.java b/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptService.java index e8b02d04bb..a6fdcbcb9d 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptService.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/chatgpt/ChatGptService.java @@ -10,9 +10,10 @@ import org.togetherjava.tjbot.config.Config; +import javax.annotation.Nullable; + import java.time.Duration; import java.util.List; -import java.util.Objects; import java.util.Optional; /** @@ -91,37 +92,33 @@ public ChatGptService(Config config) { * @see ChatGPT * Tokens. */ - public Optional ask(String question, String context) { + public Optional ask(String question, @Nullable String context) { if (isDisabled) { return Optional.empty(); } + String contextText = context == null ? "" : ", Context: %s.".formatted(context); + String fullQuestion = "(KEEP IT CONCISE, NOT MORE THAN 280 WORDS%s) - %s" + .formatted(contextText, question); + + ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), fullQuestion); + ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder() + .model(AI_MODEL) + .messages(List.of(chatMessage)) + .frequencyPenalty(FREQUENCY_PENALTY) + .temperature(TEMPERATURE) + .maxTokens(MAX_TOKENS) + .n(MAX_NUMBER_OF_RESPONSES) + .build(); + logger.debug("ChatGpt Request: {}", fullQuestion); + + String response = null; try { - String instructions = "KEEP IT CONCISE, NOT MORE THAN 280 WORDS"; - String questionWithContext = "context: Category %s on a Java Q&A discord server. %s %s" - .formatted(context, instructions, question); - ChatMessage chatMessage = new ChatMessage(ChatMessageRole.USER.value(), - Objects.requireNonNull(questionWithContext)); - ChatCompletionRequest chatCompletionRequest = ChatCompletionRequest.builder() - .model(AI_MODEL) - .messages(List.of(chatMessage)) - .frequencyPenalty(FREQUENCY_PENALTY) - .temperature(TEMPERATURE) - .maxTokens(MAX_TOKENS) - .n(MAX_NUMBER_OF_RESPONSES) - .build(); - - String response = openAiService.createChatCompletion(chatCompletionRequest) + response = openAiService.createChatCompletion(chatCompletionRequest) .getChoices() .getFirst() .getMessage() .getContent(); - - if (response == null) { - return Optional.empty(); - } - - return Optional.of(response); } catch (OpenAiHttpException openAiHttpException) { logger.warn( "There was an error using the OpenAI API: {} Code: {} Type: {} Status Code: {}", @@ -131,6 +128,12 @@ public Optional ask(String question, String context) { logger.warn("There was an error using the OpenAI API: {}", runtimeException.getMessage()); } - return Optional.empty(); + + logger.debug("ChatGpt Response: {}", response); + if (response == null) { + return Optional.empty(); + } + + return Optional.of(response); } } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java index 725b352a87..7f37d8f8cd 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java @@ -38,6 +38,8 @@ import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.StringJoiner; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; @@ -127,26 +129,27 @@ public HelpSystemHelper(Config config, Database database, ChatGptService chatGpt RestAction constructChatGptAttempt(ThreadChannel threadChannel, String originalQuestion, ComponentIdInteractor componentIdInteractor) { Optional questionOptional = prepareChatGptQuestion(threadChannel, originalQuestion); - Optional chatGPTAnswer; + Optional chatGptAnswer; if (questionOptional.isEmpty()) { return useChatGptFallbackMessage(threadChannel); } String question = questionOptional.get(); - logger.debug("The final question sent to chatGPT: {}", question); ForumTag defaultTag = threadChannel.getAppliedTags().getFirst(); ForumTag matchingTag = getCategoryTagOfChannel(threadChannel).orElse(defaultTag); - String context = matchingTag.getName(); - chatGPTAnswer = chatGptService.ask(question, context); + String context = + "Category %s on a Java Q&A discord server. You may use markdown syntax for the response" + .formatted(matchingTag.getName()); + chatGptAnswer = chatGptService.ask(question, context); - if (chatGPTAnswer.isEmpty()) { + if (chatGptAnswer.isEmpty()) { return useChatGptFallbackMessage(threadChannel); } - StringBuilder idForDismissButton = new StringBuilder(); - RestAction message = + AtomicReference messageId = new AtomicReference<>(""); + RestAction post = mentionGuildSlashCommand(threadChannel.getGuild(), ChatGptCommand.COMMAND_NAME) .map(""" Here is an AI assisted attempt to answer your question 🤖. Maybe it helps! \ @@ -154,9 +157,9 @@ RestAction constructChatGptAttempt(ThreadChannel threadChannel, %s. """::formatted) .flatMap(threadChannel::sendMessage) - .onSuccess(m -> idForDismissButton.append(m.getId())); + .onSuccess(message -> messageId.set(message.getId())); - String answer = chatGPTAnswer.orElseThrow(); + String answer = chatGptAnswer.orElseThrow(); SelfUser selfUser = threadChannel.getJDA().getSelfUser(); int responseCharLimit = MessageEmbed.DESCRIPTION_MAX_LENGTH; @@ -165,9 +168,8 @@ RestAction constructChatGptAttempt(ThreadChannel threadChannel, } MessageEmbed responseEmbed = generateGptResponseEmbed(answer, selfUser, originalQuestion); - return message.flatMap(any -> threadChannel.sendMessageEmbeds(responseEmbed) - .addActionRow( - generateDismissButton(componentIdInteractor, idForDismissButton.toString()))); + return post.flatMap(any -> threadChannel.sendMessageEmbeds(responseEmbed) + .addActionRow(generateDismissButton(componentIdInteractor, messageId.get()))); } /** @@ -204,24 +206,21 @@ private Button generateDismissButton(ComponentIdInteractor componentIdInteractor private Optional prepareChatGptQuestion(ThreadChannel threadChannel, String originalQuestion) { + StringJoiner question = new StringJoiner(" - "); + String questionTitle = threadChannel.getName(); - StringBuilder questionBuilder = new StringBuilder(MAX_QUESTION_LENGTH); + question.add(questionTitle); + question.add(originalQuestion.substring(0, + Math.min(originalQuestion.length(), MAX_QUESTION_LENGTH))); - if (originalQuestion.length() < MIN_QUESTION_LENGTH - && questionTitle.length() < MIN_QUESTION_LENGTH) { + // Not enough content for meaningful responses + if (question.length() < MIN_QUESTION_LENGTH) { return Optional.empty(); } - questionBuilder.append(questionTitle).append(" "); - originalQuestion = originalQuestion.substring(0, Math - .min(MAX_QUESTION_LENGTH - questionBuilder.length(), originalQuestion.length())); - - questionBuilder.append(originalQuestion); - - questionBuilder.append( - ". If possible, get, maximum, 5 top links from reliable websites as references in markdown syntax. Put this message on top of the links list \"Here are some links that may help :\"."); - - return Optional.of(questionBuilder.toString()); + question.add( + "Additionally to answering the question, provide 3 useful links (as markdown list) from reliable websites on the topic. Write \"Useful links:\" as title for this list."); + return Optional.of(question.toString()); } private RestAction useChatGptFallbackMessage(ThreadChannel threadChannel) { diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/TransferQuestionCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/TransferQuestionCommand.java index 58ad3a5766..99e0ed8408 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/TransferQuestionCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/TransferQuestionCommand.java @@ -17,7 +17,6 @@ import net.dv8tion.jda.api.exceptions.ErrorResponseException; import net.dv8tion.jda.api.interactions.commands.build.Commands; import net.dv8tion.jda.api.interactions.components.text.TextInput; -import net.dv8tion.jda.api.interactions.components.text.TextInput.Builder; import net.dv8tion.jda.api.interactions.components.text.TextInputStyle; import net.dv8tion.jda.api.interactions.modals.Modal; import net.dv8tion.jda.api.requests.ErrorResponse; @@ -86,7 +85,6 @@ public TransferQuestionCommand(Config config, ChatGptService chatGptService) { @Override public void onMessageContext(MessageContextInteractionEvent event) { - if (isInvalidForTransfer(event)) { return; } @@ -96,11 +94,12 @@ public void onMessageContext(MessageContextInteractionEvent event) { String originalChannelId = event.getTarget().getChannel().getId(); String authorId = event.getTarget().getAuthor().getId(); String mostCommonTag = tags.getFirst(); - String chatGptPrompt = - "Summarize the following text into a concise title or heading not more than 4-5 words, remove quotations if any: %s" + + String chatGptTitleRequest = + "Summarize the following question into a concise title or heading not more than 5 words, remove quotations if any: %s" .formatted(originalMessage); - Optional chatGptResponse = chatGptService.ask(chatGptPrompt, ""); - String title = chatGptResponse.orElse(createTitle(originalMessage)); + Optional chatGptTitle = chatGptService.ask(chatGptTitleRequest, null); + String title = chatGptTitle.orElse(createTitle(originalMessage)); if (title.length() > TITLE_MAX_LENGTH) { title = title.substring(0, TITLE_MAX_LENGTH); } @@ -112,7 +111,7 @@ public void onMessageContext(MessageContextInteractionEvent event) { .setValue(title) .build(); - Builder modalInputBuilder = + TextInput.Builder modalInputBuilder = TextInput.create(MODAL_INPUT_ID, "Question", TextInputStyle.PARAGRAPH) .setRequiredRange(INPUT_MIN_LENGTH, INPUT_MAX_LENGTH) .setPlaceholder("Contents of the question"); From 8b2d3c5c4a2362c4fa148f37bab82b754adaff85 Mon Sep 17 00:00:00 2001 From: Chris Sdogkos Date: Fri, 27 Jun 2025 17:14:15 +0300 Subject: [PATCH 29/37] fix(logger): friendlier handling of default values (#1263) I have noticed that when inexperienced people just getting started on the project go through all the hoops of cloning the repository, setting up their Discord bots, creating their test server, they end up compiling the code in IDEs like IntelliJ IDEA and assuming they just change the token on their `config.json`, there are stacktraces and errors thrown once they attempt to run the bot. Specifically: Caused by: java.net.URISyntaxException: Illegal character in path at index 0: at java.base/java.net.URI$Parser.fail(URI.java:2995) at java.base/java.net.URI$Parser.checkChars(URI.java:3166) at java.base/java.net.URI$Parser.parseHierarchical(URI.java:3248) at java.base/java.net.URI$Parser.parse(URI.java:3207) at java.base/java.net.URI.(URI.java:645) at java.base/java.net.URI.create(URI.java:930) For the average software engineer, they would be aware that the program is still running and that the bot is ready, but two things: - Someone who is not trained enough and is just starting to contribute will get thrown off by the error and potentially give up (I have experienced it happening) - It's a generally better experience for knowledgable new contributors to set up the project effortlessly so that they can get started with contributing straight away without any stacktraces. This commit prints a friendly warning in the logger and no stacktrace in case the user has not updated the webhooks (which most of them will not have to, as they are primarily concerned with the Discord bot's token). We hide the stacktrace in case there is a "" value in the "logInfoChannelWebhook" and "logErrorChannelWebhook" configuration keys so that IntelliJ IDEA does not put the stacktrace in the spotlight and potentially scare new contributors. Signed-off-by: christolis --- .../tjbot/logging/discord/DiscordLogging.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/logging/discord/DiscordLogging.java b/application/src/main/java/org/togetherjava/tjbot/logging/discord/DiscordLogging.java index 99d2de7970..f7afed7fde 100644 --- a/application/src/main/java/org/togetherjava/tjbot/logging/discord/DiscordLogging.java +++ b/application/src/main/java/org/togetherjava/tjbot/logging/discord/DiscordLogging.java @@ -23,6 +23,7 @@ */ public final class DiscordLogging { private static final Logger logger = LoggerFactory.getLogger(DiscordLogging.class); + private static final String LOG_CHANNEL_WEBHOOK_DEFAULT_VALUE = ""; private DiscordLogging() { throw new UnsupportedOperationException("Utility class"); @@ -45,22 +46,29 @@ public static void startDiscordLogging(Config botConfig) { } private static void addAppenders(Configuration logConfig, Config botConfig) { - parseWebhookUri(botConfig.getLogInfoChannelWebhook()) + parseWebhookUri(botConfig.getLogInfoChannelWebhook(), "info") .ifPresent(webhookUri -> addDiscordLogAppender("DiscordInfo", createInfoRangeFilter(), webhookUri, botConfig.getSourceCodeBaseUrl(), logConfig)); - parseWebhookUri(botConfig.getLogErrorChannelWebhook()) + parseWebhookUri(botConfig.getLogErrorChannelWebhook(), "error") .ifPresent(webhookUri -> addDiscordLogAppender("DiscordError", createErrorRangeFilter(), webhookUri, botConfig.getSourceCodeBaseUrl(), logConfig)); } - private static Optional parseWebhookUri(String webhookUri) { + private static Optional parseWebhookUri(String webhookUri, String webhookName) { + if (webhookUri.equalsIgnoreCase(LOG_CHANNEL_WEBHOOK_DEFAULT_VALUE)) { + logger.warn( + "The {} webhook URL was not setup yet, logs will not be forwarded to Discord. Enter a valid webhook URL in your `config.json` if you want log forwarding.", + webhookName); + return Optional.empty(); + } + try { return Optional.of(URI.create(webhookUri)); } catch (IllegalArgumentException e) { logger.warn(LogMarkers.NO_DISCORD, - "The webhook URL ({}) in the config is invalid, logs will not be forwarded to Discord.", - webhookUri, e); + "The {} webhook URL ({}) in the config is invalid, logs will not be forwarded to Discord.", + webhookName, webhookUri, e); return Optional.empty(); } } From d7d360bc498cd129eb0fcd6f5b64b2be2e002953 Mon Sep 17 00:00:00 2001 From: Nathan <196140351+pot@users.noreply.github.com> Date: Sun, 29 Jun 2025 01:11:54 -0600 Subject: [PATCH 30/37] Switched to better RSS feed (#1264) * switch to oracle rss feed * refractor: javamail -> rss * spotless apply * fix json formatting --- application/config.json.template | 4 ++-- .../main/java/org/togetherjava/tjbot/features/Features.java | 2 +- .../tjbot/features/{javamail => rss}/RSSHandlerRoutine.java | 2 +- .../tjbot/features/{javamail => rss}/package-info.java | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename application/src/main/java/org/togetherjava/tjbot/features/{javamail => rss}/RSSHandlerRoutine.java (99%) rename application/src/main/java/org/togetherjava/tjbot/features/{javamail => rss}/package-info.java (83%) diff --git a/application/config.json.template b/application/config.json.template index a32f8fd440..3e568329a1 100644 --- a/application/config.json.template +++ b/application/config.json.template @@ -167,9 +167,9 @@ "rssConfig": { "feeds": [ { - "url": "https://wiki.openjdk.org/spaces/createrssfeed.action?types=page&types=comment&types=blogpost&types=mail&types=attachment&spaces=JDKUpdates&maxResults=15&title=%5BJDK+Updates%5D+All+Content+Feed&publicFeed=true", + "url": "https://blogs.oracle.com/java/rss", "targetChannelPattern": "java-news-and-changes", - "dateFormatterPattern": "yyyy-MM-dd'T'HH:mm:ssX" + "dateFormatterPattern": "EEE, d MMM yyyy HH:mm:ss z" } ], "fallbackChannelPattern": "java-news-and-changes", diff --git a/application/src/main/java/org/togetherjava/tjbot/features/Features.java b/application/src/main/java/org/togetherjava/tjbot/features/Features.java index 99241f5689..6746c7f8a2 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/Features.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/Features.java @@ -34,7 +34,6 @@ import org.togetherjava.tjbot.features.help.HelpThreadMetadataPurger; import org.togetherjava.tjbot.features.help.MarkHelpThreadCloseInDBRoutine; import org.togetherjava.tjbot.features.help.PinnedNotificationRemover; -import org.togetherjava.tjbot.features.javamail.RSSHandlerRoutine; import org.togetherjava.tjbot.features.jshell.JShellCommand; import org.togetherjava.tjbot.features.jshell.JShellEval; import org.togetherjava.tjbot.features.mathcommands.TeXCommand; @@ -66,6 +65,7 @@ import org.togetherjava.tjbot.features.projects.ProjectsThreadCreatedListener; import org.togetherjava.tjbot.features.reminder.RemindRoutine; import org.togetherjava.tjbot.features.reminder.ReminderCommand; +import org.togetherjava.tjbot.features.rss.RSSHandlerRoutine; import org.togetherjava.tjbot.features.system.BotCore; import org.togetherjava.tjbot.features.system.LogLevelCommand; import org.togetherjava.tjbot.features.tags.TagCommand; diff --git a/application/src/main/java/org/togetherjava/tjbot/features/javamail/RSSHandlerRoutine.java b/application/src/main/java/org/togetherjava/tjbot/features/rss/RSSHandlerRoutine.java similarity index 99% rename from application/src/main/java/org/togetherjava/tjbot/features/javamail/RSSHandlerRoutine.java rename to application/src/main/java/org/togetherjava/tjbot/features/rss/RSSHandlerRoutine.java index e114f53a61..5c77ab9175 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/javamail/RSSHandlerRoutine.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/rss/RSSHandlerRoutine.java @@ -1,4 +1,4 @@ -package org.togetherjava.tjbot.features.javamail; +package org.togetherjava.tjbot.features.rss; import com.apptasticsoftware.rssreader.Item; import com.apptasticsoftware.rssreader.RssReader; diff --git a/application/src/main/java/org/togetherjava/tjbot/features/javamail/package-info.java b/application/src/main/java/org/togetherjava/tjbot/features/rss/package-info.java similarity index 83% rename from application/src/main/java/org/togetherjava/tjbot/features/javamail/package-info.java rename to application/src/main/java/org/togetherjava/tjbot/features/rss/package-info.java index 36305399af..a08df6e828 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/javamail/package-info.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/rss/package-info.java @@ -3,7 +3,7 @@ */ @MethodsReturnNonnullByDefault @ParametersAreNonnullByDefault -package org.togetherjava.tjbot.features.javamail; +package org.togetherjava.tjbot.features.rss; import org.togetherjava.tjbot.annotations.MethodsReturnNonnullByDefault; From 6c630d55f9d6eaaa5046f2caa2eada3e82de6d50 Mon Sep 17 00:00:00 2001 From: Nathan <196140351+pot@users.noreply.github.com> Date: Sun, 29 Jun 2025 14:56:21 -0600 Subject: [PATCH 31/37] Include error in rss feed (#1267) * Update RSSHandlerRoutine.java * Update RSSHandlerRoutine.java --- .../org/togetherjava/tjbot/features/rss/RSSHandlerRoutine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/rss/RSSHandlerRoutine.java b/application/src/main/java/org/togetherjava/tjbot/features/rss/RSSHandlerRoutine.java index 5c77ab9175..567bbbab95 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/rss/RSSHandlerRoutine.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/rss/RSSHandlerRoutine.java @@ -391,7 +391,7 @@ private List fetchRSSItemsFromURL(String rssUrl) { try { return rssReader.read(rssUrl).toList(); } catch (IOException e) { - logger.warn("Could not fetch RSS from URL ({})", rssUrl); + logger.error("Could not fetch RSS from URL ({})", rssUrl, e); return List.of(); } } From d2eb7ef4b7b2b66dde633b7a5582e1cd4a773e17 Mon Sep 17 00:00:00 2001 From: Chris Sdogkos Date: Mon, 30 Jun 2025 11:40:44 +0300 Subject: [PATCH 32/37] build: bump to log4j-slf4j2-impl v2.25.0 (#1269) Throughout this entire time, for anybody cloning the repository, setting up the `config.json.template`, following all the steps from the GitHub wiki we have verbatim and running the bot, leads to a SLF4J(E) stacktrace into the console almost as soon as the program begins execution, regardless of the environment: SLF4J(E): Unexpected problem occurred during version sanity check SLF4J(E): Reported exception: java.lang.AbstractMethodError: Receiver class org.apache.logging.slf4j.SLF4JServiceProvider does not define or inherit an implementation of the resolved method 'abstract java.lang.String getRequestedApiVersion()' of interface org.slf4j.spi.SLF4JServiceProvider. at org.slf4j.LoggerFactory.versionSanityCheck(LoggerFactory.java:385) at org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:189) at org.slf4j.LoggerFactory.getProvider(LoggerFactory.java:511) at org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:497) at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:446) at org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:472) at org.togetherjava.tjbot.Application.(Application.java:35) There has been no major hurdle noted after this exception being thrown, at least not one that I am personally aware of. Fix this by updating `log4j-slf4j2-impl` to the latest stable version as of writing this (2.25.0). As a result, the stacktrace is entirely gone and logging appears to be behaving normally. By doing this we also prevent discouraging novice contributors from contributing by falsely getting them to believe that they have performed something wrong during setup. Signed-off-by: Chris Sdogkos --- application/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/build.gradle b/application/build.gradle index d7313540c0..e4b1b27ae8 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -49,7 +49,7 @@ dependencies { implementation 'net.dv8tion:JDA:5.6.1' implementation 'org.apache.logging.log4j:log4j-core:2.25.0' - runtimeOnly 'org.apache.logging.log4j:log4j-slf4j18-impl:2.18.0' + runtimeOnly 'org.apache.logging.log4j:log4j-slf4j2-impl:2.25.0' implementation 'club.minnced:discord-webhooks:0.8.2' From 3bc3e7ca30fac26fdc308b1520c4b19db06397a1 Mon Sep 17 00:00:00 2001 From: Nathan <196140351+pot@users.noreply.github.com> Date: Mon, 30 Jun 2025 07:50:24 -0700 Subject: [PATCH 33/37] Fix RSS Headers (#1268) * fix: add temp user agent and temp headers * wip(RSSHandlerRoutine.java): add headers to config Better to have it in the configuration file as default for those who need it than hardcoded in the code. Signed-off-by: Chris Sdogkos * docs: add javadocs to rss feed config & update config with more minimal headers * Update to TJ agent * hardcode user agent --------- Signed-off-by: Chris Sdogkos Co-authored-by: Chris Sdogkos --- .../togetherjava/tjbot/features/rss/RSSHandlerRoutine.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/application/src/main/java/org/togetherjava/tjbot/features/rss/RSSHandlerRoutine.java b/application/src/main/java/org/togetherjava/tjbot/features/rss/RSSHandlerRoutine.java index 567bbbab95..770915cabe 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/rss/RSSHandlerRoutine.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/rss/RSSHandlerRoutine.java @@ -74,6 +74,8 @@ public final class RSSHandlerRoutine implements Routine { private static final int MAX_CONTENTS = 1000; private static final ZonedDateTime ZONED_TIME_MIN = ZonedDateTime.of(LocalDateTime.MIN, ZoneId.systemDefault()); + private static final String HTTP_USER_AGENT = + "TJ-Bot/1.0 (+https://github.com/Together-Java/TJ-Bot)"; private final RssReader rssReader; private final RSSFeedsConfig config; private final Predicate fallbackChannelPattern; @@ -101,7 +103,9 @@ public RSSHandlerRoutine(Config config, Database database) { targetChannelPatterns.put(feed, predicate); } }); + this.rssReader = new RssReader(); + this.rssReader.setUserAgent(HTTP_USER_AGENT); } @Override From f206bd44593b2ad18471852fcb8fe209808f4f5e Mon Sep 17 00:00:00 2001 From: Tanish Date: Tue, 1 Jul 2025 15:05:19 +0530 Subject: [PATCH 34/37] Bump Java Version to 24 (#1071) * rebase * upgrade to java 22 * downgraded workflow to java 21 * ignored -> _ * gradle wrapper * 8.8 * workflow * no ignored will get ignored * upgraded docker build java version * upgrade solarlint * . * . * . * . * for some reasons those files had unprintable characters at the beginning * Java 24 and other version bumps * suppressed some false positive sonars see bug report for details: https://community.sonarsource.com/t/a-nullpointerexception-could-be-thrown-ofnullable-can-return-null/134892 --------- Co-authored-by: Zabuzard --- .github/workflows/basic-checks.yml | 2 +- .github/workflows/code-analysis.yml | 2 +- .github/workflows/docker-publish.yaml | 2 +- .github/workflows/docker-verify.yaml | 2 +- .github/workflows/releases.yaml | 2 +- README.md | 2 +- application/build.gradle | 4 +-- .../features/code/CodeMessageHandler.java | 4 +-- .../componentids/ComponentIdStore.java | 2 +- .../features/github/GitHubReference.java | 4 +-- .../help/GuildLeaveCloseThreadListener.java | 2 +- .../tjbot/features/help/HelpSystemHelper.java | 2 +- .../features/help/HelpThreadAutoArchiver.java | 2 +- .../features/help/HelpThreadCommand.java | 11 ++++---- .../help/HelpThreadCreatedListener.java | 8 +++--- .../mediaonly/MediaOnlyChannelListener.java | 2 +- .../features/moderation/ReportCommand.java | 1 + .../moderation/TransferQuestionCommand.java | 2 +- .../BlacklistedAttachmentListener.java | 2 +- .../moderation/modmail/ModMailCommand.java | 1 + .../features/moderation/scam/ScamBlocker.java | 2 +- .../features/reminder/RemindRoutine.java | 2 +- .../tjbot/features/utils/LinkPreviews.java | 3 +- .../features/SlashCommandAdapterTest.java | 1 - .../tjbot/jda/ButtonClickEventBuilder.java | 2 -- build.gradle | 17 +++++++----- gradle/wrapper/gradle-wrapper.jar | Bin 43462 -> 43764 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 12 ++++---- gradlew.bat | 26 ++++++++++-------- 30 files changed, 67 insertions(+), 59 deletions(-) diff --git a/.github/workflows/basic-checks.yml b/.github/workflows/basic-checks.yml index 863a69e0da..2619863c74 100644 --- a/.github/workflows/basic-checks.yml +++ b/.github/workflows/basic-checks.yml @@ -3,7 +3,7 @@ name: Basic checks on: [pull_request] env: - JAVA_VERSION: 21 + JAVA_VERSION: 24 jobs: spotless: diff --git a/.github/workflows/code-analysis.yml b/.github/workflows/code-analysis.yml index ba425f7d88..f51c39e724 100644 --- a/.github/workflows/code-analysis.yml +++ b/.github/workflows/code-analysis.yml @@ -8,7 +8,7 @@ on: - cron: '0 20 * * 4' env: - JAVA_VERSION: 21 + JAVA_VERSION: 24 jobs: sonar: diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index a539fba120..edd933f799 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -7,7 +7,7 @@ on: - 'master' env: - JAVA_VERSION: 21 + JAVA_VERSION: 24 jobs: docker: diff --git a/.github/workflows/docker-verify.yaml b/.github/workflows/docker-verify.yaml index c23528646d..560c6f61d9 100644 --- a/.github/workflows/docker-verify.yaml +++ b/.github/workflows/docker-verify.yaml @@ -3,7 +3,7 @@ name: Docker Verify on: [pull_request] env: - JAVA_VERSION: 21 + JAVA_VERSION: 24 jobs: docker: diff --git a/.github/workflows/releases.yaml b/.github/workflows/releases.yaml index 1f4dfa40fa..fbd106930b 100644 --- a/.github/workflows/releases.yaml +++ b/.github/workflows/releases.yaml @@ -10,7 +10,7 @@ defaults: shell: bash env: - JAVA_VERSION: 21 + JAVA_VERSION: 24 jobs: diff --git a/README.md b/README.md index 846e897549..3c9c50cb40 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # TJ-Bot [![codefactor](https://img.shields.io/codefactor/grade/github/together-java/tj-bot)](https://www.codefactor.io/repository/github/together-java/tj-bot) -![Java](https://img.shields.io/badge/Java-21-ff696c) +![Java](https://img.shields.io/badge/Java-24-ff696c) [![license](https://img.shields.io/github/license/Together-Java/TJ-Bot)](https://github.com/Together-Java/TJ-Bot/blob/master/LICENSE) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/Together-Java/TJ-Bot?label=release) diff --git a/application/build.gradle b/application/build.gradle index e4b1b27ae8..653fde0965 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -6,7 +6,7 @@ buildscript { plugins { id 'application' - id 'com.google.cloud.tools.jib' version '3.4.0' + id 'com.google.cloud.tools.jib' version '3.4.5' id 'com.github.johnrengelman.shadow' version '8.1.1' id 'database-settings' } @@ -18,7 +18,7 @@ repositories { var outputImage = 'togetherjava.org:5001/togetherjava/tjbot:' + System.getenv('BRANCH_NAME') ?: 'latest' jib { - from.image = 'eclipse-temurin:21' + from.image = 'eclipse-temurin:24' to { image = outputImage auth { diff --git a/application/src/main/java/org/togetherjava/tjbot/features/code/CodeMessageHandler.java b/application/src/main/java/org/togetherjava/tjbot/features/code/CodeMessageHandler.java index 5e9e7b69d9..601f91663f 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/code/CodeMessageHandler.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/code/CodeMessageHandler.java @@ -224,7 +224,7 @@ public void onMessageUpdated(MessageUpdateEvent event) { // Re-apply the current action return codeReplyMessage.editMessageEmbeds(maybeCodeAction.orElseThrow().apply(code)); - }).queue(any -> { + }).queue(_ -> { }, failure -> logger.warn( "Attempted to update a code-reply-message ({}), but failed. The original code-message was {}", codeReplyMessageId, originalMessageId, failure)); @@ -253,7 +253,7 @@ public void onMessageDeleted(MessageDeleteEvent event) { // Delete the code reply as well originalMessageToCodeReply.invalidate(originalMessageId); - event.getChannel().deleteMessageById(codeReplyMessageId).queue(any -> { + event.getChannel().deleteMessageById(codeReplyMessageId).queue(_ -> { }, failure -> logger.warn( "Attempted to delete a code-reply-message ({}), but failed. The original code-message was {}", codeReplyMessageId, originalMessageId, failure)); diff --git a/application/src/main/java/org/togetherjava/tjbot/features/componentids/ComponentIdStore.java b/application/src/main/java/org/togetherjava/tjbot/features/componentids/ComponentIdStore.java index 7ada38e67d..2543a4aa8f 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/componentids/ComponentIdStore.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/componentids/ComponentIdStore.java @@ -165,7 +165,7 @@ public void addComponentIdRemovedListener(Consumer listener) { * @throws InvalidComponentIdFormatException if the given component ID was in an unexpected * format and could not be serialized */ - @SuppressWarnings("WeakerAccess") + @SuppressWarnings({"WeakerAccess", "squid:S2259"}) public Optional get(UUID uuid) { synchronized (storeLock) { // Get it from the cache or, if not found, the database diff --git a/application/src/main/java/org/togetherjava/tjbot/features/github/GitHubReference.java b/application/src/main/java/org/togetherjava/tjbot/features/github/GitHubReference.java index 5592e04592..960587e8a4 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/github/GitHubReference.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/github/GitHubReference.java @@ -236,7 +236,7 @@ Optional findIssue(int id, String targetIssueTitle) { if (issue.getTitle().equals(targetIssueTitle)) { return Optional.of(issue); } - } catch (FileNotFoundException ignored) { + } catch (FileNotFoundException _) { return Optional.empty(); } catch (IOException ex) { throw new UncheckedIOException(ex); @@ -255,7 +255,7 @@ Optional findIssue(int id, long defaultRepoId) { issue = repository.getPullRequest(id); } return Optional.of(issue); - } catch (FileNotFoundException ignored) { + } catch (FileNotFoundException _) { return Optional.empty(); } catch (IOException ex) { throw new UncheckedIOException(ex); diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/GuildLeaveCloseThreadListener.java b/application/src/main/java/org/togetherjava/tjbot/features/help/GuildLeaveCloseThreadListener.java index 0e4cff2eba..5d1a4d7260 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/GuildLeaveCloseThreadListener.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/GuildLeaveCloseThreadListener.java @@ -36,7 +36,7 @@ public void onGuildMemberRemove(GuildMemberRemoveEvent event) { .filter(thread -> thread.getOwnerIdLong() == event.getUser().getIdLong()) .filter(thread -> thread.getParentChannel().getName().matches(helpForumPattern)) .forEach(thread -> thread.sendMessageEmbeds(embed) - .flatMap(any -> thread.getManager().setArchived(true)) + .flatMap(_ -> thread.getManager().setArchived(true)) .queue())); } } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java index 7f37d8f8cd..222a8bfb66 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpSystemHelper.java @@ -168,7 +168,7 @@ RestAction constructChatGptAttempt(ThreadChannel threadChannel, } MessageEmbed responseEmbed = generateGptResponseEmbed(answer, selfUser, originalQuestion); - return post.flatMap(any -> threadChannel.sendMessageEmbeds(responseEmbed) + return post.flatMap(_ -> threadChannel.sendMessageEmbeds(responseEmbed) .addActionRow(generateDismissButton(componentIdInteractor, messageId.get()))); } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadAutoArchiver.java b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadAutoArchiver.java index 4b0b6a317a..41792957ff 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadAutoArchiver.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadAutoArchiver.java @@ -151,7 +151,7 @@ private void triggerArchiveFlow(ThreadChannel threadChannel, long authorId, return sendEmbedWithMention.apply(authorResults.get()); }) - .flatMap(any -> threadChannel.getManager().setArchived(true)) + .flatMap(_ -> threadChannel.getManager().setArchived(true)) .queue(); } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCommand.java index 0adcd14a22..fd6b264e0c 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCommand.java @@ -102,7 +102,7 @@ public HelpThreadCommand(Config config, HelpSystemHelper helper) { .collect(Collectors.toMap(Subcommand::getCommandName, Function.identity())); subcommandToCooldownCache = new EnumMap<>(streamSubcommands() .filter(Subcommand::hasCooldown) - .collect(Collectors.toMap(Function.identity(), any -> createCooldownCache.get()))); + .collect(Collectors.toMap(Function.identity(), _ -> createCooldownCache.get()))); subcommandToEventHandler = new EnumMap<>(Map.of(Subcommand.CHANGE_CATEGORY, this::changeCategory, Subcommand.CHANGE_TITLE, this::changeTitle, Subcommand.CLOSE, this::closeThread, Subcommand.RESET_ACTIVITY, this::resetActivity)); @@ -124,6 +124,7 @@ && isHelpThreadOnCooldown(invokedSubcommand, helpThread)) { subcommandToEventHandler.get(invokedSubcommand).accept(event, helpThread); } + @SuppressWarnings("squid:S2259") private boolean isHelpThreadOnCooldown(Subcommand subcommand, ThreadChannel helpThread) { Cache helpThreadIdToLastAction = requireCooldownCache(subcommand); return Optional.ofNullable(helpThreadIdToLastAction.getIfPresent(helpThread.getIdLong())) @@ -158,7 +159,7 @@ private void changeCategory(SlashCommandInteractionEvent event, ThreadChannel he refreshCooldownFor(Subcommand.CHANGE_CATEGORY, helpThread); helper.changeChannelCategory(helpThread, category) - .flatMap(any -> sendCategoryChangedMessage(helpThread.getGuild(), event.getHook(), + .flatMap(_ -> sendCategoryChangedMessage(helpThread.getGuild(), event.getHook(), helpThread, category)) .queue(); } @@ -185,7 +186,7 @@ private RestAction sendCategoryChangedMessage(Guild guild, InteractionH String headsUpPattern = "%s please have a look, thanks."; String headsUpWithoutRole = headsUpPattern.formatted(""); String headsUpWithRole = headsUpPattern.formatted(helperRole.orElseThrow().getAsMention()); - return action.flatMap(any -> helpThread.sendMessage(headsUpWithoutRole) + return action.flatMap(_ -> helpThread.sendMessage(headsUpWithoutRole) .flatMap(message -> message.editMessage(headsUpWithRole))); } @@ -195,7 +196,7 @@ private void changeTitle(SlashCommandInteractionEvent event, ThreadChannel helpT refreshCooldownFor(Subcommand.CHANGE_TITLE, helpThread); helper.renameChannel(helpThread, title) - .flatMap(any -> event.reply("Changed the title to **%s**.".formatted(title))) + .flatMap(_ -> event.reply("Changed the title to **%s**.".formatted(title))) .queue(); } @@ -206,7 +207,7 @@ private void closeThread(SlashCommandInteractionEvent event, ThreadChannel helpT .setColor(HelpSystemHelper.AMBIENT_COLOR) .build(); - event.replyEmbeds(embed).flatMap(any -> helpThread.getManager().setArchived(true)).queue(); + event.replyEmbeds(embed).flatMap(_ -> helpThread.getManager().setArchived(true)).queue(); } private void resetActivity(SlashCommandInteractionEvent event, ThreadChannel helpThread) { diff --git a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCreatedListener.java b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCreatedListener.java index 12ee788fa1..cd453eab63 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCreatedListener.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/help/HelpThreadCreatedListener.java @@ -76,7 +76,7 @@ private boolean wasThreadAlreadyHandled(long threadChannelId) { // the threads we already handled Instant now = Instant.now(); // NOTE It is necessary to do the "check if exists, otherwise insert" atomic - Instant createdAt = threadIdToCreatedAtCache.get(threadChannelId, any -> now); + Instant createdAt = threadIdToCreatedAtCache.get(threadChannelId, _ -> now); return createdAt != now; } @@ -84,9 +84,9 @@ private void handleHelpThreadCreated(ThreadChannel threadChannel) { threadChannel.retrieveStartMessage().flatMap(message -> { registerThreadDataInDB(message, threadChannel); return sendHelperHeadsUp(threadChannel) - .flatMap(any -> HelpThreadCreatedListener.isContextSufficient(message), - any -> createAIResponse(threadChannel, message)) - .flatMap(any -> pinOriginalQuestion(message)); + .flatMap(_ -> HelpThreadCreatedListener.isContextSufficient(message), + _ -> createAIResponse(threadChannel, message)) + .flatMap(_ -> pinOriginalQuestion(message)); }).queue(); } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/mediaonly/MediaOnlyChannelListener.java b/application/src/main/java/org/togetherjava/tjbot/features/mediaonly/MediaOnlyChannelListener.java index 083dd193ec..11f666beaa 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/mediaonly/MediaOnlyChannelListener.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/mediaonly/MediaOnlyChannelListener.java @@ -46,7 +46,7 @@ public void onMessageReceived(MessageReceivedEvent event) { } if (messageHasNoMediaAttached(message)) { - message.delete().flatMap(any -> dmUser(message)).queue(any -> { + message.delete().flatMap(_ -> dmUser(message)).queue(_ -> { }, failure -> tempNotifyUserInChannel(message)); } } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java index 61eeb0f3ae..ced7aaec4a 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/ReportCommand.java @@ -125,6 +125,7 @@ private boolean handleIsOnCooldown(MessageContextInteractionEvent event) { return true; } + @SuppressWarnings("squid:S2259") private boolean isAuthorOnCooldown(long userId) { return Optional.ofNullable(authorToLastReportInvocation.getIfPresent(userId)) .map(sinceCommandInvoked -> sinceCommandInvoked.plus(COOLDOWN_DURATION_VALUE, diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/TransferQuestionCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/TransferQuestionCommand.java index 99e0ed8408..69e59f9358 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/TransferQuestionCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/TransferQuestionCommand.java @@ -149,7 +149,7 @@ public void onModalSubmitted(ModalInteractionEvent event, List args) { // Has been handled if original message was deleted by now. // Deleted messages cause retrieveMessageById to fail. Consumer notHandledAction = - any -> transferFlow(event, channelId, authorId, messageId); + _ -> transferFlow(event, channelId, authorId, messageId); Consumer handledAction = failure -> { if (failure instanceof ErrorResponseException errorResponseException diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/attachment/BlacklistedAttachmentListener.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/attachment/BlacklistedAttachmentListener.java index db1060aefb..c7cd224f18 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/attachment/BlacklistedAttachmentListener.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/attachment/BlacklistedAttachmentListener.java @@ -49,7 +49,7 @@ public void onMessageReceived(MessageReceivedEvent event) { } private void handleBadMessage(Message message) { - message.delete().flatMap(any -> dmUser(message)).queue(any -> warnMods(message)); + message.delete().flatMap(_ -> dmUser(message)).queue(_ -> warnMods(message)); } private RestAction dmUser(Message message) { diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/modmail/ModMailCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/modmail/ModMailCommand.java index c29a7382a8..b43748ec7a 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/modmail/ModMailCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/modmail/ModMailCommand.java @@ -214,6 +214,7 @@ private MessageEmbed createModMailMessage(@Nullable User author, String userMess .build(); } + @SuppressWarnings("squid:S2259") private boolean isChannelOnCooldown(long userId) { return Optional.ofNullable(authorToLastModMailInvocation.getIfPresent(userId)) .map(sinceCommandInvoked -> sinceCommandInvoked.plus(COOLDOWN_DURATION_VALUE, diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/scam/ScamBlocker.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/scam/ScamBlocker.java index 0816e2fb5c..057a71cee6 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/scam/ScamBlocker.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/scam/ScamBlocker.java @@ -273,7 +273,7 @@ private void dmUser(MessageReceivedEvent event) { } private void dmUser(Guild guild, long userId, JDA jda) { - jda.openPrivateChannelById(userId).flatMap(channel -> dmUser(guild, channel)).queue(any -> { + jda.openPrivateChannelById(userId).flatMap(channel -> dmUser(guild, channel)).queue(_ -> { }, failure -> logger.debug( "Unable to send dm message to user {} in guild {} to inform them about a scam message being blocked", userId, guild.getId(), failure)); diff --git a/application/src/main/java/org/togetherjava/tjbot/features/reminder/RemindRoutine.java b/application/src/main/java/org/togetherjava/tjbot/features/reminder/RemindRoutine.java index 1da5871a1d..0f2f0417d2 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/reminder/RemindRoutine.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/reminder/RemindRoutine.java @@ -154,7 +154,7 @@ Failed to send a reminder with (authorID '{}') skipping it. This can be due to a int failureAttempts = pendingReminder.getFailureAttempts() + 1; Instant remindAt = Instant.now().plus(1, ChronoUnit.MINUTES); - database.write(any -> { + database.write(_ -> { pendingReminder.setRemindAt(remindAt); pendingReminder.setFailureAttempts(failureAttempts); pendingReminder.insert(); diff --git a/application/src/main/java/org/togetherjava/tjbot/features/utils/LinkPreviews.java b/application/src/main/java/org/togetherjava/tjbot/features/utils/LinkPreviews.java index cc84fa3083..6126e05e57 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/utils/LinkPreviews.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/utils/LinkPreviews.java @@ -63,7 +63,7 @@ public static CompletableFuture> createLinkPreviews(List extractResults(tasks)).exceptionally(e -> { + return allDoneTask.thenApply(_ -> extractResults(tasks)).exceptionally(e -> { logger.error("Unknown error during link preview creation", e); return List.of(); }); @@ -184,6 +184,7 @@ private static Optional parseMetaProperty(Document doc, String metaPrope .filter(Predicate.not(String::isBlank)); } + @SuppressWarnings("squid:S2259") private static Optional selectFirstMetaTag(Document doc, String key, String value) { return Optional.ofNullable(doc.selectFirst("meta[%s=%s]".formatted(key, value))) .map(element -> element.attr("content")); diff --git a/application/src/test/java/org/togetherjava/tjbot/features/SlashCommandAdapterTest.java b/application/src/test/java/org/togetherjava/tjbot/features/SlashCommandAdapterTest.java index 745731031b..cff9818d17 100644 --- a/application/src/test/java/org/togetherjava/tjbot/features/SlashCommandAdapterTest.java +++ b/application/src/test/java/org/togetherjava/tjbot/features/SlashCommandAdapterTest.java @@ -15,7 +15,6 @@ final class SlashCommandAdapterTest { UserInteractionType.SLASH_COMMAND.getPrefix() + NAME; private static final String DESCRIPTION = "Foo command"; private static final CommandVisibility VISIBILITY = CommandVisibility.GUILD; - private static final int UNIQUE_ID_ITERATIONS = 20; static SlashCommandAdapter createAdapter() { // noinspection AnonymousInnerClass diff --git a/application/src/test/java/org/togetherjava/tjbot/jda/ButtonClickEventBuilder.java b/application/src/test/java/org/togetherjava/tjbot/jda/ButtonClickEventBuilder.java index a2a474dbcc..140bfc8e0f 100644 --- a/application/src/test/java/org/togetherjava/tjbot/jda/ButtonClickEventBuilder.java +++ b/application/src/test/java/org/togetherjava/tjbot/jda/ButtonClickEventBuilder.java @@ -1,6 +1,5 @@ package org.togetherjava.tjbot.jda; -import com.fasterxml.jackson.databind.ObjectMapper; import net.dv8tion.jda.api.entities.Member; import net.dv8tion.jda.api.entities.Message; import net.dv8tion.jda.api.entities.MessageEmbed; @@ -67,7 +66,6 @@ * */ public final class ButtonClickEventBuilder { - private static final ObjectMapper JSON = new ObjectMapper(); private final JdaTester jdaTester; private final Supplier mockEventSupplier; private final UnaryOperator mockMessageOperator; diff --git a/build.gradle b/build.gradle index 7858bdea6a..67759b5bb5 100644 --- a/build.gradle +++ b/build.gradle @@ -1,17 +1,20 @@ plugins { id 'java' - id "com.diffplug.spotless" version "7.0.3" + id "com.diffplug.spotless" version "7.0.4" id "org.sonarqube" version "6.2.0.5505" - id "name.remal.sonarlint" version "4.2.2" + id "name.remal.sonarlint" version "5.1.10" +} +repositories { + mavenCentral() } group 'org.togetherjava' version '1.0-SNAPSHOT' ext { - jooqVersion = '3.20.4' - jacksonVersion = '2.19.0' - chatGPTVersion = '0.18.0' + jooqVersion = '3.20.5' + jacksonVersion = '2.19.1' + chatGPTVersion = '0.18.2' } // Skips sonarlint during the build, useful for testing purposes. @@ -55,7 +58,7 @@ subprojects { java { toolchain { // Nails the Java-Version of every Subproject - languageVersion = JavaLanguageVersion.of(21) + languageVersion = JavaLanguageVersion.of(24) } } @@ -63,7 +66,7 @@ subprojects { sonarLint { rules { disable( - 'java:S1135' // Disables "Track uses of "TO-DO" tags" rule. + 'java:S1135', // Disables "Track uses of "TO-DO" tags" rule. ) } } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index d64cd4917707c1f8861d8cb53dd15194d4248596..1b33c55baabb587c669f562ae36f953de2481846 100644 GIT binary patch delta 34753 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%0AF=STgWRWL+V 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 W(m*lz*HoBlEiM*bU{am~Dx&}eeNGnu diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 1af9e0930b..ff23a68d70 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.14.2-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/gradlew b/gradlew index 1aa94a4269..23d15a9367 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 +# ############################################################################## # @@ -55,7 +57,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/. @@ -84,7 +86,7 @@ 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\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -112,7 +114,7 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar +CLASSPATH="\\\"\\\"" # Determine the Java command to use to start the JVM. @@ -203,7 +205,7 @@ fi DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # and any embedded shellness will be escaped. # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # treated as '${Hostname}' itself on the command line. @@ -211,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" # Stop when "xargs" is not available. diff --git a/gradlew.bat b/gradlew.bat index 93e3f59f13..db3a6ac207 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 ########################################################################## @@ -43,11 +45,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,22 +59,22 @@ 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 :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar +set CLASSPATH= @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* :end @rem End local scope for the variables with windows NT shell From 28eb51829af2f03d13344224b97ae56ad3d857e0 Mon Sep 17 00:00:00 2001 From: Daniel Tischner Date: Tue, 1 Jul 2025 11:39:23 +0200 Subject: [PATCH 35/37] Bump devcontainer from Java 18 to 24 (#1271) --- .devcontainer/devcontainer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index afe5444f91..8b335103a5 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,7 +8,7 @@ }, "features": { "ghcr.io/devcontainers/features/java:1": { - "version": "18.0.2.1-tem", + "version": "24-tem", "jdkDistro": "tem", "installGradle": true }, @@ -35,4 +35,4 @@ "postCreateCommand": { "config": "cp application/config.json.template application/config.json" } -} \ No newline at end of file +} From 39224f17e79d5b5d24f57d8e09a03878eb2d8ab4 Mon Sep 17 00:00:00 2001 From: Daniel Tischner Date: Tue, 1 Jul 2025 12:28:34 +0200 Subject: [PATCH 36/37] version bumps, removed some warnings (#1272) --- application/build.gradle | 9 +++++---- .../tjbot/features/jshell/renderer/RendererUtils.java | 4 ++-- .../tjbot/features/moderation/scam/ScamDetector.java | 2 +- .../tjbot/features/rss/RSSHandlerRoutine.java | 4 ++-- .../tjbot/features/tags/TagManageCommand.java | 4 ++-- build.gradle | 3 +-- 6 files changed, 13 insertions(+), 13 deletions(-) diff --git a/application/build.gradle b/application/build.gradle index 653fde0965..baddb8e2ae 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -51,7 +51,7 @@ dependencies { implementation 'org.apache.logging.log4j:log4j-core:2.25.0' runtimeOnly 'org.apache.logging.log4j:log4j-slf4j2-impl:2.25.0' - implementation 'club.minnced:discord-webhooks:0.8.2' + implementation 'club.minnced:discord-webhooks:0.8.4' implementation "org.jooq:jooq:$jooqVersion" @@ -81,10 +81,10 @@ dependencies { implementation 'com.apptasticsoftware:rssreader:3.9.3' testImplementation 'org.mockito:mockito-core:5.18.0' - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.4' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.0' + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.13.2' + testImplementation 'org.junit.jupiter:junit-jupiter-params:5.13.2' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.11.1' + testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.13.2' implementation "com.theokanning.openai-gpt3-java:api:$chatGPTVersion" implementation "com.theokanning.openai-gpt3-java:service:$chatGPTVersion" @@ -92,4 +92,5 @@ dependencies { application { mainClass = 'org.togetherjava.tjbot.Application' + applicationDefaultJvmArgs = ["--enable-native-access=ALL-UNNAMED"] } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/jshell/renderer/RendererUtils.java b/application/src/main/java/org/togetherjava/tjbot/features/jshell/renderer/RendererUtils.java index 9d44c0df57..c36695af77 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/jshell/renderer/RendererUtils.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/jshell/renderer/RendererUtils.java @@ -23,12 +23,12 @@ private RendererUtils() {} static String abortionCauseToString(JShellEvalAbortionCause abortionCause) { return switch (abortionCause) { - case JShellEvalAbortionCause.TimeoutAbortionCause ignored -> "Allowed time exceeded."; + case JShellEvalAbortionCause.TimeoutAbortionCause _ -> "Allowed time exceeded."; case JShellEvalAbortionCause.UnhandledExceptionAbortionCause(String exceptionClass, String exceptionMessage) -> "Uncaught exception:\n" + exceptionClass + ":" + exceptionMessage; case JShellEvalAbortionCause.CompileTimeErrorAbortionCause(List errors) -> "The code doesn't compile:\n" + String.join("\n", errors); - case JShellEvalAbortionCause.SyntaxErrorAbortionCause ignored -> + case JShellEvalAbortionCause.SyntaxErrorAbortionCause _ -> "The code doesn't compile, there are syntax errors in this code."; }; } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/moderation/scam/ScamDetector.java b/application/src/main/java/org/togetherjava/tjbot/features/moderation/scam/ScamDetector.java index c4ebed3657..33a9a28743 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/moderation/scam/ScamDetector.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/moderation/scam/ScamDetector.java @@ -80,7 +80,7 @@ private void analyzeUrl(String url, AnalyseResults results) { String host; try { host = URI.create(url).getHost(); - } catch (IllegalArgumentException e) { + } catch (IllegalArgumentException _) { // Invalid urls are not scam return; } diff --git a/application/src/main/java/org/togetherjava/tjbot/features/rss/RSSHandlerRoutine.java b/application/src/main/java/org/togetherjava/tjbot/features/rss/RSSHandlerRoutine.java index 770915cabe..c86f305cda 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/rss/RSSHandlerRoutine.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/rss/RSSHandlerRoutine.java @@ -214,7 +214,7 @@ private Optional getLastSavedDateFromDatabaseRecord(RssFeedRecord ZonedDateTime savedDate = getZonedDateTime(rssRecord.getLastDate(), dateFormatterPattern); return Optional.of(savedDate); - } catch (DateTimeParseException e) { + } catch (DateTimeParseException _) { return Optional.empty(); } } @@ -317,7 +317,7 @@ private static boolean isValidDateFormat(Item rssItem, RSSFeed feedConfig) { // that the format pattern defined in the config and the // feed's actual format differ. getZonedDateTime(firstRssFeedPubDate.get(), feedConfig.dateFormatterPattern()); - } catch (DateTimeParseException e) { + } catch (DateTimeParseException _) { return false; } return true; diff --git a/application/src/main/java/org/togetherjava/tjbot/features/tags/TagManageCommand.java b/application/src/main/java/org/togetherjava/tjbot/features/tags/TagManageCommand.java index eb7dbecbe9..c720775ad8 100644 --- a/application/src/main/java/org/togetherjava/tjbot/features/tags/TagManageCommand.java +++ b/application/src/main/java/org/togetherjava/tjbot/features/tags/TagManageCommand.java @@ -130,7 +130,7 @@ private static void sendSuccessMessage(IReplyCallback event, String id, String a private static OptionalLong parseMessageIdAndHandle(String messageId, IReplyCallback event) { try { return OptionalLong.of(Long.parseLong(messageId)); - } catch (NumberFormatException e) { + } catch (NumberFormatException _) { event .reply("The given message id '%s' is invalid, expected a number." .formatted(messageId)) @@ -297,7 +297,7 @@ private Optional getTagContent(Subcommand subcommand, String id) { if (Subcommand.SUBCOMMANDS_WITH_PREVIOUS_CONTENT.contains(subcommand)) { try { return tagSystem.getTag(id); - } catch (NoSuchElementException e) { + } catch (NoSuchElementException _) { // NOTE Rare race condition, for example if another thread deleted the tag in the // meantime logger.warn("Tried to retrieve content of tag '{}', but the content doesn't exist.", diff --git a/build.gradle b/build.gradle index 67759b5bb5..cd6d2afde3 100644 --- a/build.gradle +++ b/build.gradle @@ -31,7 +31,7 @@ sonarqube { } // Install git hooks -task installLocalGitHook(type: Copy) { +tasks.register('installLocalGitHook', Copy) { from new File(rootProject.rootDir, 'scripts/pre-commit') into new File(rootProject.rootDir, '.git/hooks') fileMode 0775 @@ -97,5 +97,4 @@ subprojects { compileTestJava { options.encoding = "UTF-8" } - } From 2e1fb91609ee43c93065c9616dd641189cb9aef4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Connor=20Schweigh=C3=B6fer?= Date: Tue, 1 Jul 2025 13:20:48 +0200 Subject: [PATCH 37/37] Merge junit version to have one consistent, single source of truth version value (#1273) --- application/build.gradle | 6 +++--- build.gradle | 1 + formatter/build.gradle | 7 ++++--- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/application/build.gradle b/application/build.gradle index baddb8e2ae..78c3254c47 100644 --- a/application/build.gradle +++ b/application/build.gradle @@ -81,10 +81,10 @@ dependencies { implementation 'com.apptasticsoftware:rssreader:3.9.3' testImplementation 'org.mockito:mockito-core:5.18.0' - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.13.2' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.13.2' + testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" + testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion" testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.13.2' + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" implementation "com.theokanning.openai-gpt3-java:api:$chatGPTVersion" implementation "com.theokanning.openai-gpt3-java:service:$chatGPTVersion" diff --git a/build.gradle b/build.gradle index cd6d2afde3..d0588c0496 100644 --- a/build.gradle +++ b/build.gradle @@ -15,6 +15,7 @@ ext { jooqVersion = '3.20.5' jacksonVersion = '2.19.1' chatGPTVersion = '0.18.2' + junitVersion = '5.13.2' } // Skips sonarlint during the build, useful for testing purposes. diff --git a/formatter/build.gradle b/formatter/build.gradle index 41344106fc..17c7329792 100644 --- a/formatter/build.gradle +++ b/formatter/build.gradle @@ -6,8 +6,9 @@ dependencies { implementation 'com.google.code.findbugs:jsr305:3.0.2' implementation project(':utils') - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.11.4' - testImplementation 'org.junit.jupiter:junit-jupiter-params:5.11.0' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.11.1' + testImplementation "org.junit.jupiter:junit-jupiter-api:$junitVersion" + testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion" + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" }