diff --git a/.editorconfig b/.editorconfig index 7b1f03c..921c743 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,8 +12,6 @@ indent_style = tab [*.kt] indent_style = tab -ij_kotlin_name_count_to_use_star_import = 99999 -ij_kotlin_name_count_to_use_star_import_for_members = 99999 [*.properties] indent_style = space diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..f1d37cc --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: ['https://www.buymeacoffee.com/hyacinthbots'] diff --git a/.github/ISSUE_TEMPLATES/blank-issue.md b/.github/ISSUE_TEMPLATES/blank-issue.md new file mode 100644 index 0000000..6561b31 --- /dev/null +++ b/.github/ISSUE_TEMPLATES/blank-issue.md @@ -0,0 +1,8 @@ +--- +name: Blank issue +about: Please only use this template if you know what you're doing. +title: '' +labels: '' +assignees: '' + +--- diff --git a/.github/ISSUE_TEMPLATES/bug_report.yml b/.github/ISSUE_TEMPLATES/bug_report.yml new file mode 100644 index 0000000..5ac0293 --- /dev/null +++ b/.github/ISSUE_TEMPLATES/bug_report.yml @@ -0,0 +1,49 @@ +name: Bug Report +description: Create a bug report to help us improve ModMail! +labels: + - bug +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to fill out this bug report! + + Before continuing, please make sure there are no similar issues on [the issue tracker](https://github.com/HyacinthBots/ModMailBot/issues). If there are, consider contributing your information there instead! + - type: textarea + id: what-happened + attributes: + label: What happened? + description: 'Also tell us what did you expect to happen? PLease provide some reproduction steps: What did you do to trigger this bug?' + placeholder: Tell us what you see! + value: "A bug happened!" + validations: + required: true + - type: textarea + id: screenshots + attributes: + label: Screenshots + description: If applicable, add screenshots to help explain your issue. + placeholder: You can add images by clicking on the button in the bar that will appear one you click this textbox. + validations: + required: false + - type: textarea + id: logs + attributes: + label: Relevant log output + description: If your running your own ModMail instance, please provide some logs that generated when your issue occurred. This will be automatically formatted into code, so no need for backticks. + render: bash + - type: input + id: modmail-version + attributes: + label: ModMail Version + description: If you're running your own ModMail instance, place provided the version of the JAR you are running + placeholder: ModMail-1.5-all.jar + validations: + required: false + - type: textarea + id: additional-context + attributes: + label: Additional context + description: Add any other context about the problem here. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATES/config.yml b/.github/ISSUE_TEMPLATES/config.yml new file mode 100644 index 0000000..403553c --- /dev/null +++ b/.github/ISSUE_TEMPLATES/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: Join the Discord for support with ModMail! + url: https://discord.gg/jQJnav2jPu + about: If you're looking for support, it's easier for the ModMail development team to support you through our Discord server. If you're not looking for support, please check for a duplicate of your issue before opening a report. Thanks! diff --git a/.github/ISSUE_TEMPLATES/feature_request.md b/.github/ISSUE_TEMPLATES/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATES/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..0be0f36 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,17 @@ +version: 2 +enable-beta-ecosystems: true + +updates: + - package-ecosystem: "gradle" + target-branch: "main" + directory: "/" + + schedule: + interval: "weekly" + + - package-ecosystem: "github-actions" + target-branch: "main" + directory: "/" + + schedule: + interval: "weekly" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e59d51c..8fadb9d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -10,20 +10,20 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v2 + - name: Set up JDK 21 + uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: 17 + java-version: 21 - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build Artifacts - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v4 with: arguments: build --stacktrace @@ -35,8 +35,8 @@ jobs: id: ref - name: Upload build artifacts - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: build-artifacts-${{ steps.ref.outputs.branch }} - path: build/libs/*[0-9].jar \ No newline at end of file + path: build/libs/*[0-9].jar diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0cfc8d6..d4dc65e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -11,26 +11,26 @@ jobs: steps: - name: Checkout sources - uses: actions/checkout@v2 + uses: actions/checkout@v4 - - name: Set up JDK 17 - uses: actions/setup-java@v2 + - name: Set up JDK 21 + uses: actions/setup-java@v4 with: distribution: 'temurin' - java-version: 17 + java-version: 21 - name: Grant execute permission for gradlew run: chmod +x gradlew - name: Build Artifacts - uses: gradle/gradle-build-action@v2 + uses: gradle/gradle-build-action@v4 with: arguments: build --stacktrace - name: Upload artifacts GitHub - uses: AButler/upload-release-assets@v2.0 + uses: AButler/upload-release-assets@v3.0 with: files: 'build/libs/*.jar' - repo-token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file + repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/HEADER b/HEADER index 55fe5df..cff704d 100644 --- a/HEADER +++ b/HEADER @@ -1,4 +1,4 @@ -Copyright (c) 2022 NoComment1105 +Copyright (c) 2022-2025 NoComment1105 This file is part of ModMail. diff --git a/LICENSE b/LICENSE index ce49288..4f3d055 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2022 NoComment1105 +Copyright (c) 2022-2025 NoComment1105 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/build.gradle.kts b/build.gradle.kts index 5dfbbb2..d929f6e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,4 +1,5 @@ +import dev.kordex.gradle.plugins.kordex.DataCollection import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.KotlinVersion import org.jetbrains.kotlin.gradle.tasks.KotlinCompile @@ -6,22 +7,39 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { application - kotlin("jvm") - kotlin("plugin.serialization") - - id("com.github.jakemarsden.git-hooks") - id("com.github.johnrengelman.shadow") - id("io.gitlab.arturbosch.detekt") - id("org.cadixdev.licenser") + alias(libs.plugins.kotlin) + alias(libs.plugins.kotlin.serialization) + alias(libs.plugins.shadow) + alias(libs.plugins.detekt) + alias(libs.plugins.git.hooks) + alias(libs.plugins.kord.extensions.plugin) + alias(libs.plugins.licenser) } group = "org.hyacinthbots.modmailbot" version = "1.0-SNAPSHOT" -val javaTarget = 17 + +val className = "org.hyacinthbots.modmailbot.ModMailBotKt" +val javaTarget = "21" repositories { mavenCentral() + maven { + name = "Kord Snapshots" + url = uri("https://repo.kord.dev/snapshots") + } + + maven { + name = "Kord Extensions (Releases)" + url = uri("https://releases-repo.kordex.dev") + } + + maven { + name = "Kord Extensions (Snapshots)" + url = uri("https://snapshots-repo.kordex.dev") + } + maven { name = "Sonatype Snapshots (Legacy)" url = uri("https://oss.sonatype.org/content/repositories/snapshots") @@ -29,7 +47,7 @@ repositories { maven { name = "Sonatype Snapshots" - url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots") + url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots/") } } @@ -41,17 +59,36 @@ dependencies { // Logging dependencies implementation(libs.logback) + implementation(libs.logback.groovy) implementation(libs.logging) + implementation(libs.groovy) + implementation(libs.jansi) // Formatting detektPlugins(libs.detekt) // Database - implementation(libs.kmongo) + implementation(libs.mongo.driver) + implementation(libs.mongo.bson) +} + +kordEx { + addDependencies = false + addRepositories = false + ignoreIncompatibleKotlinVersion = true + + bot { + dataCollection(DataCollection.None) + } + + i18n { + classPackage = "modmailbot.i18n" + translationBundle = "modmailbot.strings" + } } application { - mainClass.set("org.hyacinthbots.modmailbot.ModMailBot.kt") + mainClass.set(className) } gitHooks { @@ -60,14 +97,10 @@ gitHooks { ) } -kotlin { - jvmToolchain(javaTarget) -} - tasks { withType { compilerOptions { - jvmTarget.set(JvmTarget.fromTarget(javaTarget.toString())) + jvmTarget.set(JvmTarget.fromTarget(javaTarget)) languageVersion.set(KotlinVersion.fromVersion(libs.versions.kotlin.get().substringBeforeLast("."))) freeCompilerArgs.add("-opt-in=kotlin.RequiresOptIn") incremental = true @@ -75,9 +108,7 @@ tasks { } jar { manifest { - attributes( - "Main-Class" to "org.hyacinthbots.modmailbot.ModMailBot.kt" - ) + attributes("Main-Class" to className) } } @@ -93,7 +124,7 @@ tasks { detekt { buildUponDefaultConfig = true - config = files("$rootDir/detekt.yml") + config.setFrom("$rootDir/detekt.yml") autoCorrect = true } diff --git a/detekt.yml b/detekt.yml index 6215aa6..2f5db37 100644 --- a/detekt.yml +++ b/detekt.yml @@ -175,6 +175,7 @@ formatting: active: true autoCorrect: true NoWildcardImports: + active: false packagesToUseImportOnDemandProperty: "" PackageName: active: true @@ -211,12 +212,9 @@ naming: active: true functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$' excludeClassPattern: '$^' - ignoreOverridden: true InvalidPackageDeclaration: active: true - rootPackage: 'org.hyacinthbots' - MemberNameEqualsClassName: - active: false + rootPackage: 'org.HyacinthBots' NonBooleanPropertyPrefixedWithIs: active: true PackageNaming: @@ -285,7 +283,7 @@ style: includeLineWrapping: true ForbiddenComment: active: false - values: ['TODO:', 'FIXME:', 'STOPSHIP:'] + comments: ['TODO:', 'FIXME:', 'STOPSHIP:'] allowedPatterns: '' ForbiddenImport: active: false @@ -312,14 +310,10 @@ style: ignoreNamedArgument: true ignoreEnums: true ignoreRanges: false - MandatoryBracesIfStatements: - active: true MandatoryBracesLoops: active: true OptionalUnit: active: false - OptionalWhenBraces: - active: true PreferToOverPairSyntax: active: true RedundantExplicitType: @@ -368,3 +362,5 @@ style: active: true UseRequireNotNull: active: true + WildcardImport: + active: false diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..9635251 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,62 @@ +[versions] +# Base +kotlin = "2.1.0" +kord-extensions = "2.3.1-20250108.190136-25" +kord-extensions-plugin = "1.6.1" +kordx-emoji = "0.5.0" +# --- +# Logging +logging = "7.0.3" +logback = "1.5.16" +logback-groovy = "1.14.5" +groovy = "3.0.22" +jansi = "2.4.1" +# --- +# Formatting +detekt = "1.23.7" +# --- +# Database +mongo = "5.3.0" +# --- +# Extra Utilities +git-hooks = "0.0.2" +shadow = "8.1.1" +licenser = "0.6.1" +# --- + +[libraries] +# Base +kord-extensions = { module = "dev.kordex:kord-extensions", version.ref = "kord-extensions" } +kord-extensions-unsafe = { module = "dev.kordex.modules:dev-unsafe", version.ref = "kord-extensions" } +kordx-emoji = { module = "dev.kord.x:emoji", version.ref = "kordx-emoji"} +kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib" } +# --- +# Logging +logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } +logback-groovy = { module = "io.github.virtualdogbert:logback-groovy-config", version.ref = "logback-groovy" } +logging = { module = "io.github.oshai:kotlin-logging", version.ref = "logging" } +groovy = { module = "org.codehaus.groovy:groovy", version.ref = "groovy" } +jansi = { module = "org.fusesource.jansi:jansi", version.ref = "jansi" } +# --- +# Formatting +detekt = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } +# --- +# Database +mongo-driver = { module = "org.mongodb:mongodb-driver-kotlin-coroutine", version.ref = "mongo" } +mongo-bson = { module = "org.mongodb:bson-kotlin", version.ref = "mongo"} +# --- + +[plugins] +# Base +kotlin = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } +kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } +kord-extensions-plugin = { id = "dev.kordex.gradle.kordex", version.ref = "kord-extensions-plugin"} +# --- +# Formatting +detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" } +# --- +# Extra Utilities +git-hooks = { id = "com.github.jakemarsden.git-hooks", version.ref = "git-hooks" } +shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" } +licenser = { id = "org.cadixdev.licenser", version.ref = "licenser"} +# --- diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index ccebba7..a4b76b9 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index fc10b60..94113f2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11-bin.zip networkTimeout=10000 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 79a61d4..f5feea6 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/. @@ -83,10 +85,9 @@ done # This is normally unused # shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -133,10 +134,13 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. @@ -144,7 +148,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac @@ -152,7 +156,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then '' | soft) :;; #( *) # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC3045 + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -197,11 +201,15 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ diff --git a/gradlew.bat b/gradlew.bat index 93e3f59..9d21a21 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,11 +59,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 goto fail diff --git a/libs.versions.toml b/libs.versions.toml deleted file mode 100644 index 4090e7f..0000000 --- a/libs.versions.toml +++ /dev/null @@ -1,33 +0,0 @@ -[versions] -# Base -kord-extensions = "1.5.6-SNAPSHOT" -kotlin = "1.8.10" # Note: Plugin versions must be updated in the settings.gradle.kts too -kordx-emoji = "0.5.0" -# --- -# Logging -logging = "3.0.5" -logback = "1.4.5" -# --- -# Formatting -detekt = "1.22.0" # Note: Plugin versions must be updated in the settings.gradle.kts too -# --- -# Database -kmongo = "4.8.0" - -[libraries] -# Base -kord-extensions = { module = "com.kotlindiscord.kord.extensions:kord-extensions", version.ref = "kord-extensions" } -kord-extensions-unsafe = { module = "com.kotlindiscord.kord.extensions:unsafe", version.ref = "kord-extensions" } -kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib" } -kordx-emoji = { module = "dev.kord.x:emoji", version.ref = "kordx-emoji"} -# --- -# Logging -logback = { module = "ch.qos.logback:logback-classic", version.ref = "logback" } -logging = { module = "io.github.microutils:kotlin-logging", version.ref = "logging" } -# --- -# Formatting -detekt = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" } -# --- -# Database -kmongo = { module = "org.litote.kmongo:kmongo-coroutine-serialization", version.ref = "kmongo" } -# --- diff --git a/settings.gradle.kts b/settings.gradle.kts index a219d60..b1a93f1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,27 +1,12 @@ pluginManagement { - plugins { - // Update this in libs.version.toml when you change it here - kotlin("jvm") version "1.8.10" - kotlin("plugin.serialization") version "1.8.10" - - // Update this in libs.version.toml when you change it here - id("io.gitlab.arturbosch.detekt") version "1.22.0" - - id("com.github.jakemarsden.git-hooks") version "0.0.2" - id("com.github.johnrengelman.shadow") version "7.1.2" - id("org.cadixdev.licenser") version "0.6.1" - } repositories { gradlePluginPortal() + mavenCentral() + + maven("https://snapshots-repo.kordex.dev") + maven("https://releases-repo.kordex.dev") } } rootProject.name = "modmail" -dependencyResolutionManagement { - versionCatalogs { - create("libs") { - from(files("libs.versions.toml")) - } - } -} diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/ModMailBot.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/ModMailBot.kt index 2c276d7..9a78c00 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/ModMailBot.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/ModMailBot.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * @@ -11,11 +11,11 @@ package io.github.nocomment1105.modmailbot -import com.kotlindiscord.kord.extensions.ExtensibleBot import dev.kord.cache.map.MapLikeCollection import dev.kord.cache.map.internal.MapEntryCache import dev.kord.gateway.Intent import dev.kord.gateway.PrivilegedIntent +import dev.kordex.core.ExtensibleBot import io.github.nocomment1105.modmailbot.extensions.commands.CloseCommands import io.github.nocomment1105.modmailbot.extensions.commands.ReplyCommands import io.github.nocomment1105.modmailbot.extensions.events.MessageEditing @@ -23,7 +23,7 @@ import io.github.nocomment1105.modmailbot.extensions.events.MessageReceiving import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.io.FileInputStream -import java.util.Properties +import java.util.* val file = FileInputStream("config.properties") val config = Properties() diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/_Constants.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/_Constants.kt index ab03c4f..7c05aea 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/_Constants.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/_Constants.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/_Utils.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/_Utils.kt index 1d20fc3..9afa403 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/_Utils.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/_Utils.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * @@ -9,20 +9,20 @@ package io.github.nocomment1105.modmailbot -import com.kotlindiscord.kord.extensions.DISCORD_RED -import com.kotlindiscord.kord.extensions.builders.ExtensibleBotBuilder -import com.kotlindiscord.kord.extensions.commands.application.slash.EphemeralSlashCommandContext -import com.kotlindiscord.kord.extensions.utils.getTopRole -import com.kotlindiscord.kord.extensions.utils.loadModule import dev.kord.common.Color import dev.kord.common.entity.DiscordPartialMessage import dev.kord.common.entity.Snowflake import dev.kord.core.entity.Message import dev.kord.core.entity.User import dev.kord.rest.builder.message.EmbedBuilder +import dev.kordex.core.DISCORD_RED +import dev.kordex.core.builders.ExtensibleBotBuilder +import dev.kordex.core.commands.application.slash.EphemeralSlashCommandContext +import dev.kordex.core.utils.getTopRole +import dev.kordex.core.utils.loadModule import io.github.nocomment1105.modmailbot.database.Database import io.github.nocomment1105.modmailbot.database.collections.MetaCollection -import io.github.nocomment1105.modmailbot.database.collections.OpenThreadCollection +import io.github.nocomment1105.modmailbot.database.collections.OpenThreadsCollection import kotlinx.coroutines.runBlocking import kotlinx.datetime.Clock import org.koin.dsl.bind @@ -37,7 +37,7 @@ import org.koin.dsl.bind fun EmbedBuilder.messageEmbed(message: Message) { author { name = message.author?.tag - icon = message.author?.avatar!!.url + icon = message.author?.avatar?.cdnUrl?.toUrl() } description = message.content timestamp = Clock.System.now() @@ -70,7 +70,7 @@ suspend fun EmbedBuilder.messageEmbed( author { if (!anonymous) { name = author.tag - icon = author.avatar!!.url + icon = author.avatar?.cdnUrl?.toUrl() } else { name = author.asMember(MAIL_SERVER).getTopRole()!!.name } @@ -124,7 +124,7 @@ fun EmbedBuilder.editedMessageEmbed( * @since 1.0.0 */ suspend fun EphemeralSlashCommandContext<*, *>.inThreadChannel(): Snowflake? = - OpenThreadCollection().getDmFromThreadChannel(channel.id) + OpenThreadsCollection().getDmFromThreadChannel(channel.id) /** * This function sets up the database fully for use and runs migrations if requested. @@ -144,7 +144,7 @@ suspend inline fun ExtensibleBotBuilder.database(migrate: Boolean) { loadModule { single { MetaCollection() } bind MetaCollection::class - single { OpenThreadCollection() } bind OpenThreadCollection::class + single { OpenThreadsCollection() } bind OpenThreadsCollection::class } if (migrate) { diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/Database.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/Database.kt index 284e8fe..187cf13 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/Database.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/Database.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * @@ -7,14 +7,15 @@ * please see the LICENSE file or https://mit-license.org/ */ +@file:Suppress("MemberNameEqualsClassName") + package io.github.nocomment1105.modmailbot.database import com.mongodb.ConnectionString import com.mongodb.MongoClientSettings +import com.mongodb.kotlin.client.coroutine.MongoClient import io.github.nocomment1105.modmailbot.MONGO_URI import org.bson.UuidRepresentation -import org.litote.kmongo.coroutine.coroutine -import org.litote.kmongo.reactivestreams.KMongo class Database { /** The settings to connect to the database with. */ @@ -24,7 +25,7 @@ class Database { .applyConnectionString(ConnectionString(MONGO_URI)) .build() - private val client = KMongo.createClient(settings).coroutine + private val client = MongoClient.create(settings) /** The database. */ val database get() = client.getDatabase("modmail") diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/DatabaseUtils.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/DatabaseUtils.kt new file mode 100644 index 0000000..df63962 --- /dev/null +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/DatabaseUtils.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2022-2025 NoComment1105 + * + * This file is part of ModMail. + * + * Licensed under the MIT license. For more information, + * please see the LICENSE file or https://mit-license.org/ + */ + +package io.github.nocomment1105.modmailbot.database + +import com.mongodb.client.model.Filters +import com.mongodb.kotlin.client.coroutine.MongoCollection +import kotlinx.coroutines.flow.firstOrNull +import org.bson.BsonDocument +import org.bson.conversions.Bson +import kotlin.reflect.KProperty + +/** + * This object contains lots of useful functions that keep the collections looking cleaner and mimics some old KMongo + * functionality, to keep me happy and my code looking nicer. + */ +object DatabaseUtils { + /** + * This function mimics the old functionality of the KMongo infix function without being infix. It also keeps the + * collections clean as there aren't lots of `.name` calls in them. + * + * @param field The database table field + * @param value The value to compare against + * @return A [Bson] object of the comparison + */ + fun eq(field: KProperty, value: T?): Bson = Filters.eq(field.name, value) + + /** + * This extension function gets rid of the need to have lots of `firstOrNull()` calls in the equation and keeps it + * similar to the old KMongo function. + * + * @param filter The [BsonDocument] to filter the find + * @return The collection [T] + */ + suspend fun MongoCollection.findOne(filter: Bson = BsonDocument()): T? = find(filter).firstOrNull() +} diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/Migrator.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/Migrator.kt index 4f788ae..d96e06e 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/Migrator.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/Migrator.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * @@ -9,11 +9,11 @@ package io.github.nocomment1105.modmailbot.database -import com.kotlindiscord.kord.extensions.koin.KordExKoinComponent +import dev.kordex.core.koin.KordExKoinComponent import io.github.nocomment1105.modmailbot.database.collections.MetaCollection import io.github.nocomment1105.modmailbot.database.entities.MetaData import io.github.nocomment1105.modmailbot.database.migrations.v1 -import mu.KotlinLogging +import io.github.oshai.kotlinlogging.KotlinLogging import org.koin.core.component.inject object Migrator : KordExKoinComponent { diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/CloseQueueCollection.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/CloseQueueCollection.kt index 9946ce7..6e2ea0d 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/CloseQueueCollection.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/CloseQueueCollection.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * @@ -9,12 +9,13 @@ package io.github.nocomment1105.modmailbot.database.collections -import com.kotlindiscord.kord.extensions.koin.KordExKoinComponent import dev.kord.common.entity.Snowflake +import dev.kordex.core.koin.KordExKoinComponent import io.github.nocomment1105.modmailbot.database.Database +import io.github.nocomment1105.modmailbot.database.DatabaseUtils.eq import io.github.nocomment1105.modmailbot.database.entities.CloseQueueData +import kotlinx.coroutines.flow.toList import org.koin.core.component.inject -import org.litote.kmongo.eq /** * The class for interacting with the CloseQueue database. @@ -23,7 +24,7 @@ class CloseQueueCollection : KordExKoinComponent { private val db: Database by inject() @PublishedApi - internal val collection = db.database.getCollection() + internal val collection = db.database.getCollection(name) /** * Gets all the threads in the queue. @@ -52,5 +53,7 @@ class CloseQueueCollection : KordExKoinComponent { * @since 1.0.0 */ suspend inline fun removeThreadFromQueue(threadId: Snowflake) = - collection.deleteOne(CloseQueueData::threadId eq threadId) + collection.deleteOne(eq(CloseQueueData::threadId, threadId)) + + companion object : CollectionBase("close-queue") } diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/CollectionBase.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/CollectionBase.kt new file mode 100644 index 0000000..9a8d4fd --- /dev/null +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/CollectionBase.kt @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2022-2025 NoComment1105 + * + * This file is part of ModMail. + * + * Licensed under the MIT license. For more information, + * please see the LICENSE file or https://mit-license.org/ + */ + +package io.github.nocomment1105.modmailbot.database.collections + +abstract class CollectionBase(val name: String) diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/MetaCollection.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/MetaCollection.kt index 092f000..4be34f4 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/MetaCollection.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/MetaCollection.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * @@ -9,11 +9,12 @@ package io.github.nocomment1105.modmailbot.database.collections -import com.kotlindiscord.kord.extensions.koin.KordExKoinComponent +import dev.kordex.core.koin.KordExKoinComponent import io.github.nocomment1105.modmailbot.database.Database +import io.github.nocomment1105.modmailbot.database.DatabaseUtils.eq +import io.github.nocomment1105.modmailbot.database.DatabaseUtils.findOne import io.github.nocomment1105.modmailbot.database.entities.MetaData import org.koin.core.component.inject -import org.litote.kmongo.eq /** * The class stores the functions for interacting with the Meta database. @@ -22,7 +23,7 @@ class MetaCollection : KordExKoinComponent { private val db: Database by inject() @PublishedApi - internal val collection = db.database.getCollection() + internal val collection = db.database.getCollection(name) /** * Gets the Meta from the database. @@ -53,7 +54,8 @@ class MetaCollection : KordExKoinComponent { */ suspend fun update(meta: MetaData) = collection.findOneAndReplace( - MetaData::id eq "meta", - meta + eq(MetaData::id, "meta"), meta ) + + companion object : CollectionBase("metadata") } diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/OpenThreadCollection.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/OpenThreadsCollection.kt similarity index 75% rename from src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/OpenThreadCollection.kt rename to src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/OpenThreadsCollection.kt index 421a211..94e3856 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/OpenThreadCollection.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/OpenThreadsCollection.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * @@ -9,21 +9,23 @@ package io.github.nocomment1105.modmailbot.database.collections -import com.kotlindiscord.kord.extensions.koin.KordExKoinComponent import dev.kord.common.entity.Snowflake +import dev.kordex.core.koin.KordExKoinComponent import io.github.nocomment1105.modmailbot.database.Database +import io.github.nocomment1105.modmailbot.database.DatabaseUtils.eq +import io.github.nocomment1105.modmailbot.database.DatabaseUtils.findOne import io.github.nocomment1105.modmailbot.database.entities.OpenThreadData +import kotlinx.coroutines.flow.first import org.koin.core.component.inject -import org.litote.kmongo.eq /** * This class stores the functions for interacting with the open thread database. */ -class OpenThreadCollection : KordExKoinComponent { +class OpenThreadsCollection : KordExKoinComponent { private val db: Database by inject() @PublishedApi - internal val collection = db.database.getCollection() + internal val collection = db.database.getCollection(name) /** * Adds a thread to the database. @@ -40,7 +42,7 @@ class OpenThreadCollection : KordExKoinComponent { * @author NoComment1105 * @since 1.0.0 */ - suspend fun removeByUser(userId: Snowflake) = collection.deleteOne(OpenThreadData::userId eq userId) + suspend fun removeByUser(userId: Snowflake) = collection.deleteOne(eq(OpenThreadData::userId, userId)) /** * Removes a thread from the database. @@ -49,7 +51,7 @@ class OpenThreadCollection : KordExKoinComponent { * @author NoComment1105 * @since 1.0.0 */ - suspend fun removeByThread(threadId: Snowflake) = collection.deleteOne(OpenThreadData::threadId eq threadId) + suspend fun removeByThread(threadId: Snowflake) = collection.deleteOne(eq(OpenThreadData::threadId, threadId)) /** * Gets the open thread for the user. There should only ever be one at a time, hence we return the [first] element @@ -60,7 +62,7 @@ class OpenThreadCollection : KordExKoinComponent { * @since 1.0.0 */ suspend fun getOpenThreadsForUser(userId: Snowflake): OpenThreadData? = - collection.find(OpenThreadData::userId eq userId).first() + collection.find(eq(OpenThreadData::userId, userId)).first() /** * Gets the DM id from the [threadId]. @@ -71,5 +73,7 @@ class OpenThreadCollection : KordExKoinComponent { * @since 1.0.0 */ suspend fun getDmFromThreadChannel(threadId: Snowflake): Snowflake? = - collection.findOne(OpenThreadData::threadId eq threadId)?.userId + collection.findOne(eq(OpenThreadData::threadId, threadId))?.userId + + companion object : CollectionBase("open-threads") } diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/SentMessageCollection.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/SentMessagesCollection.kt similarity index 74% rename from src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/SentMessageCollection.kt rename to src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/SentMessagesCollection.kt index 5c6466d..54a8f4f 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/SentMessageCollection.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/collections/SentMessagesCollection.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * @@ -9,21 +9,24 @@ package io.github.nocomment1105.modmailbot.database.collections -import com.kotlindiscord.kord.extensions.koin.KordExKoinComponent +import com.mongodb.client.model.Filters.and import dev.kord.common.entity.Snowflake +import dev.kordex.core.koin.KordExKoinComponent import io.github.nocomment1105.modmailbot.database.Database +import io.github.nocomment1105.modmailbot.database.DatabaseUtils.eq +import io.github.nocomment1105.modmailbot.database.DatabaseUtils.findOne import io.github.nocomment1105.modmailbot.database.entities.SentMessageData +import kotlinx.coroutines.flow.toList import org.koin.core.component.inject -import org.litote.kmongo.eq /** * The class for interacting with the SentMessageData database. */ -class SentMessageCollection : KordExKoinComponent { +class SentMessagesCollection : KordExKoinComponent { private val db: Database by inject() @PublishedApi - internal val collection = db.database.getCollection() + internal val collection = db.database.getCollection(name) /** * Adds a message to the database. @@ -45,7 +48,7 @@ class SentMessageCollection : KordExKoinComponent { * @since 1.0.0 */ suspend inline fun getMessagesInThread(threadId: Snowflake): List = - collection.find(SentMessageData::threadId eq threadId).toList() + collection.find(eq(SentMessageData::threadId, threadId)).toList() /** * Gets every message internal sent in a thread. @@ -56,7 +59,8 @@ class SentMessageCollection : KordExKoinComponent { * @since 1.0.0 */ suspend inline fun getInternalSentMessages(threadId: Snowflake): List = - collection.find(SentMessageData::threadId eq threadId, SentMessageData::wasSentByStaff eq true).toList() + collection.find(and(eq(SentMessageData::threadId, threadId), eq(SentMessageData::wasSentByStaff, true))) + .toList() /** * Gets every message sent by an external user in a thread. @@ -67,7 +71,8 @@ class SentMessageCollection : KordExKoinComponent { * @since 1.0.0 */ suspend inline fun getUserSentMessages(threadId: Snowflake): List = - collection.find(SentMessageData::threadId eq threadId, SentMessageData::wasSentByStaff eq false).toList() + collection.find(and(eq(SentMessageData::threadId, threadId), eq(SentMessageData::wasSentByStaff, false))) + .toList() /** * Gets a message sent by in a thread by its number identifier. @@ -79,7 +84,7 @@ class SentMessageCollection : KordExKoinComponent { * @since 1.0.0 */ suspend inline fun getMessageByNumber(threadId: Snowflake, number: Int): SentMessageData? = - collection.findOne(SentMessageData::threadId eq threadId, SentMessageData::messageNumber eq number) + collection.findOne(and(eq(SentMessageData::threadId, threadId), eq(SentMessageData::messageNumber, number))) /** * Gets a DM message by the ID of its linked thread message. Primarily for use when going from thread message to dm @@ -92,7 +97,12 @@ class SentMessageCollection : KordExKoinComponent { * @since 1.0.0 */ suspend inline fun getDmMessageById(threadId: Snowflake, messageId: Snowflake): Snowflake? = - collection.findOne(SentMessageData::threadId eq threadId, SentMessageData::threadMessageId eq messageId)?.dmMessageId + collection.findOne( + and( + eq(SentMessageData::threadId, threadId), + eq(SentMessageData::threadMessageId, messageId) + ) + )?.dmMessageId /** * Gets a thread message by the ID of its linked DM message. Primarily for use when going from dm message to thread @@ -105,7 +115,12 @@ class SentMessageCollection : KordExKoinComponent { * @since 1.0.0 */ suspend inline fun getInternalMessageById(threadId: Snowflake, messageId: Snowflake): Snowflake? = - collection.findOne(SentMessageData::threadId eq threadId, SentMessageData::dmMessageId eq messageId)?.threadMessageId + collection.findOne( + and( + eq(SentMessageData::threadId, threadId), + eq(SentMessageData::dmMessageId, messageId) + ) + )?.threadMessageId /** * Gets the next number id for sent messages. @@ -117,7 +132,7 @@ class SentMessageCollection : KordExKoinComponent { */ suspend inline fun getNextMessageNumber(threadId: Snowflake): Int = try { getMessagesInThread(threadId).last()!!.messageNumber + 1 - } catch (e: NoSuchElementException) { + } catch (_: NoSuchElementException) { 1 } @@ -130,5 +145,7 @@ class SentMessageCollection : KordExKoinComponent { * @since 1.0.0 */ suspend inline fun removeMessages(threadId: Snowflake) = - collection.deleteMany(SentMessageData::threadId eq threadId) + collection.deleteMany(eq(SentMessageData::threadId, threadId)) + + companion object : CollectionBase("sent-messages") } diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/entities/CloseQueueData.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/entities/CloseQueueData.kt index 868808b..783a653 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/entities/CloseQueueData.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/entities/CloseQueueData.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/entities/MetaData.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/entities/MetaData.kt index b02e621..9a1c3ba 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/entities/MetaData.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/entities/MetaData.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/entities/OpenThreadData.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/entities/OpenThreadData.kt index 3f3f7f1..80b9f45 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/entities/OpenThreadData.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/entities/OpenThreadData.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/entities/SentMessageData.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/entities/SentMessageData.kt index 753a09c..42af50f 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/entities/SentMessageData.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/entities/SentMessageData.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/migrations/v1.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/migrations/v1.kt index 25938ad..468114c 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/database/migrations/v1.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/database/migrations/v1.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * @@ -9,9 +9,9 @@ package io.github.nocomment1105.modmailbot.database.migrations -import org.litote.kmongo.coroutine.CoroutineDatabase +import com.mongodb.kotlin.client.coroutine.MongoDatabase @Suppress("UnusedPrivateMember") -suspend fun v1(db: CoroutineDatabase) { +suspend fun v1(db: MongoDatabase) { // Currently no migration needed. This is in preparation for actually needing one } diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/extensions/commands/CloseCommands.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/extensions/commands/CloseCommands.kt index 9a74d8b..6b6d496 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/extensions/commands/CloseCommands.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/extensions/commands/CloseCommands.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * @@ -9,27 +9,28 @@ package io.github.nocomment1105.modmailbot.extensions.commands -import com.kotlindiscord.kord.extensions.commands.Arguments -import com.kotlindiscord.kord.extensions.commands.converters.impl.coalescingOptionalDuration -import com.kotlindiscord.kord.extensions.commands.converters.impl.defaultingBoolean -import com.kotlindiscord.kord.extensions.commands.converters.impl.optionalString -import com.kotlindiscord.kord.extensions.extensions.Extension -import com.kotlindiscord.kord.extensions.extensions.ephemeralSlashCommand -import com.kotlindiscord.kord.extensions.utils.scheduling.Scheduler -import com.kotlindiscord.kord.extensions.utils.scheduling.Task import dev.kord.core.behavior.channel.createMessage import dev.kord.core.entity.channel.DmChannel import dev.kord.core.entity.channel.MessageChannel -import dev.kord.rest.builder.message.create.embed +import dev.kord.rest.builder.message.embed +import dev.kordex.core.commands.Arguments +import dev.kordex.core.commands.converters.impl.coalescingOptionalDuration +import dev.kordex.core.commands.converters.impl.defaultingBoolean +import dev.kordex.core.commands.converters.impl.optionalString +import dev.kordex.core.extensions.Extension +import dev.kordex.core.extensions.ephemeralSlashCommand +import dev.kordex.core.utils.scheduling.Scheduler +import dev.kordex.core.utils.scheduling.Task import io.github.nocomment1105.modmailbot.MAIL_SERVER import io.github.nocomment1105.modmailbot.database.collections.CloseQueueCollection -import io.github.nocomment1105.modmailbot.database.collections.OpenThreadCollection -import io.github.nocomment1105.modmailbot.database.collections.SentMessageCollection +import io.github.nocomment1105.modmailbot.database.collections.OpenThreadsCollection +import io.github.nocomment1105.modmailbot.database.collections.SentMessagesCollection import io.github.nocomment1105.modmailbot.database.entities.CloseQueueData import io.github.nocomment1105.modmailbot.inThreadChannel import kotlinx.datetime.Clock import kotlinx.datetime.TimeZone import kotlinx.datetime.plus +import modmailbot.i18n.Translations class CloseCommands : Extension() { override val name = "close-commands" @@ -44,8 +45,8 @@ class CloseCommands : Extension() { task = scheduler.schedule(30, pollingSeconds = 1, repeat = true, callback = ::closeThreads) ephemeralSlashCommand(::CloseArgs) { - name = "close" - description = "Close this thread" + name = Translations.Commands.Close.name + description = Translations.Commands.Close.description guild(MAIL_SERVER) @@ -81,19 +82,19 @@ class CloseCommands : Extension() { inner class CloseArgs : Arguments() { val delay by coalescingOptionalDuration { - name = "delay" - description = "How long until you want to close this thread" + name = Translations.Commands.Close.Args.Delay.name + description = Translations.Commands.Close.Args.Delay.description } val silent by defaultingBoolean { - name = "silent" - description = "Whether to close this thread silently" + name = Translations.Commands.Close.Args.Silent.name + description = Translations.Commands.Close.Args.Silent.description defaultValue = false } val message by optionalString { - name = "message" - description = "The closing message to send to the user." + name = Translations.Commands.Close.Args.Message.name + description = Translations.Commands.Close.Args.Message.description } } @@ -135,10 +136,10 @@ class CloseCommands : Extension() { suspend fun sendThreadCloseMessage(dmChannel: DmChannel, channel: MessageChannel, closeMessage: String?) { dmChannel.createMessage { embed { - title = "Modmail thread closed" + title = Translations.Commands.Close.ClosedEmbed.title.translate() if (closeMessage != null) description = closeMessage footer { - text = "Replying will create a new thread" + text = Translations.Commands.Close.ClosedEmbed.footer.translate() } timestamp = Clock.System.now() } @@ -162,7 +163,7 @@ suspend fun deleteThread(channel: MessageChannel, closeMessage: String?) { // TODO create a log in some way for moderators to look back on - SentMessageCollection().removeMessages(channel.id) - OpenThreadCollection().removeByThread(channel.id) + SentMessagesCollection().removeMessages(channel.id) + OpenThreadsCollection().removeByThread(channel.id) CloseQueueCollection().removeThreadFromQueue(channel.id) } diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/extensions/commands/ReplyCommands.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/extensions/commands/ReplyCommands.kt index 7c8d76c..7ea6352 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/extensions/commands/ReplyCommands.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/extensions/commands/ReplyCommands.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * @@ -9,27 +9,27 @@ package io.github.nocomment1105.modmailbot.extensions.commands -import com.kotlindiscord.kord.extensions.DISCORD_GREEN -import com.kotlindiscord.kord.extensions.commands.Arguments -import com.kotlindiscord.kord.extensions.commands.converters.impl.string -import com.kotlindiscord.kord.extensions.extensions.Extension -import com.kotlindiscord.kord.extensions.extensions.ephemeralSlashCommand -import com.kotlindiscord.kord.extensions.types.respond import dev.kord.core.behavior.channel.createMessage -import dev.kord.rest.builder.message.create.embed +import dev.kord.rest.builder.message.embed +import dev.kordex.core.DISCORD_GREEN +import dev.kordex.core.commands.Arguments +import dev.kordex.core.commands.converters.impl.string +import dev.kordex.core.extensions.Extension +import dev.kordex.core.extensions.ephemeralSlashCommand import io.github.nocomment1105.modmailbot.MAIL_SERVER -import io.github.nocomment1105.modmailbot.database.collections.SentMessageCollection +import io.github.nocomment1105.modmailbot.database.collections.SentMessagesCollection import io.github.nocomment1105.modmailbot.database.entities.SentMessageData import io.github.nocomment1105.modmailbot.inThreadChannel import io.github.nocomment1105.modmailbot.messageEmbed +import modmailbot.i18n.Translations class ReplyCommands : Extension() { override val name = "reply-commands" override suspend fun setup() { ephemeralSlashCommand(::ReplyArgs) { - name = "reply" - description = "Reply to the user this thread channel is owned by" + name = Translations.Commands.Reply.Reply.name + description = Translations.Commands.Reply.Reply.description guild(MAIL_SERVER) @@ -50,10 +50,10 @@ class ReplyCommands : Extension() { } } - SentMessageCollection().addMessage( + SentMessagesCollection().addMessage( SentMessageData( channel.id, - SentMessageCollection().getNextMessageNumber(channel.id), + SentMessagesCollection().getNextMessageNumber(channel.id), dmChannelMessage.id, threadMessageId.id, wasSentByStaff = true, @@ -61,13 +61,13 @@ class ReplyCommands : Extension() { ) ) - respond { content = "Message sent" } + respond { content = Translations.Commands.Reply.sent.translate() } } } ephemeralSlashCommand(::ReplyArgs) { - name = "anonreply" - description = "Reply anonymously to the user this thread channel is owned by" + name = Translations.Commands.Reply.Anonreply.name + description = Translations.Commands.Reply.Anonreply.name guild(MAIL_SERVER) @@ -89,10 +89,10 @@ class ReplyCommands : Extension() { } } - SentMessageCollection().addMessage( + SentMessagesCollection().addMessage( SentMessageData( channel.id, - SentMessageCollection().getNextMessageNumber(channel.id), + SentMessagesCollection().getNextMessageNumber(channel.id), dmChannelMessage.id, threadMessageId.id, wasSentByStaff = true, @@ -100,15 +100,15 @@ class ReplyCommands : Extension() { ) ) - respond { content = "Message sent" } + respond { content = Translations.Commands.Reply.sent.translate() } } } } inner class ReplyArgs : Arguments() { val content by string { - name = "message" - description = "What you'd like to reply with" + name = Translations.Commands.Reply.Reply.Args.Content.name + description = Translations.Commands.Reply.Reply.Args.Content.description mutate { it.replace("\\n", "\n").replace("\n", "\n") diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/extensions/events/MessageEditing.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/extensions/events/MessageEditing.kt index 18c5233..8b3f36a 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/extensions/events/MessageEditing.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/extensions/events/MessageEditing.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * @@ -9,36 +9,31 @@ package io.github.nocomment1105.modmailbot.extensions.events -import com.kotlindiscord.kord.extensions.DISCORD_GREEN -import com.kotlindiscord.kord.extensions.checks.noGuild -import com.kotlindiscord.kord.extensions.commands.Arguments -import com.kotlindiscord.kord.extensions.commands.converters.impl.snowflake -import com.kotlindiscord.kord.extensions.extensions.Extension -import com.kotlindiscord.kord.extensions.extensions.event -import com.kotlindiscord.kord.extensions.modules.unsafe.annotations.UnsafeAPI -import com.kotlindiscord.kord.extensions.modules.unsafe.extensions.unsafeSlashCommand -import com.kotlindiscord.kord.extensions.modules.unsafe.types.InitialSlashCommandResponse -import com.kotlindiscord.kord.extensions.utils.waitFor -import dev.kord.common.entity.TextInputStyle import dev.kord.core.behavior.edit import dev.kord.core.behavior.getChannelOf -import dev.kord.core.behavior.interaction.modal -import dev.kord.core.behavior.interaction.response.createEphemeralFollowup -import dev.kord.core.behavior.interaction.response.respond import dev.kord.core.entity.channel.GuildMessageChannel -import dev.kord.core.event.interaction.ModalSubmitInteractionCreateEvent import dev.kord.core.event.message.MessageUpdateEvent -import dev.kord.rest.builder.message.create.embed -import dev.kord.rest.builder.message.modify.embed +import dev.kord.rest.builder.message.embed import dev.kord.x.emoji.Emojis import dev.kord.x.emoji.addReaction +import dev.kordex.core.DISCORD_GREEN +import dev.kordex.core.checks.noGuild +import dev.kordex.core.commands.Arguments +import dev.kordex.core.commands.converters.impl.snowflake +import dev.kordex.core.components.forms.ModalForm +import dev.kordex.core.extensions.Extension +import dev.kordex.core.extensions.ephemeralSlashCommand +import dev.kordex.core.extensions.event +import dev.kordex.core.i18n.toKey +import dev.kordex.core.i18n.types.Key +import dev.kordex.modules.dev.unsafe.annotations.UnsafeAPI import io.github.nocomment1105.modmailbot.MAIL_SERVER -import io.github.nocomment1105.modmailbot.database.collections.OpenThreadCollection -import io.github.nocomment1105.modmailbot.database.collections.SentMessageCollection +import io.github.nocomment1105.modmailbot.database.collections.OpenThreadsCollection +import io.github.nocomment1105.modmailbot.database.collections.SentMessagesCollection import io.github.nocomment1105.modmailbot.editedMessageEmbed import io.github.nocomment1105.modmailbot.messageEmbed import io.github.nocomment1105.modmailbot.trimmedContents -import kotlin.time.Duration.Companion.seconds +import modmailbot.i18n.Translations class MessageEditing : Extension() { override val name = "message-editing" @@ -52,9 +47,9 @@ class MessageEditing : Extension() { } action { - val userThread = OpenThreadCollection().getOpenThreadsForUser(event.getMessage().author!!.id) + val userThread = OpenThreadsCollection().getOpenThreadsForUser(event.getMessage().author!!.id) val threadMessageIdToEdit = - SentMessageCollection().getInternalMessageById(userThread!!.threadId, event.messageId)!! + SentMessagesCollection().getInternalMessageById(userThread!!.threadId, event.messageId)!! val threadMessageToEdit = kord.getGuildOrNull(MAIL_SERVER)!!.getChannelOf(userThread.threadId) .getMessage(threadMessageIdToEdit) @@ -63,12 +58,12 @@ class MessageEditing : Extension() { embed { author { name = event.getMessage().author?.tag - icon = event.getMessage().author?.avatar?.url + icon = event.getMessage().author?.avatar?.cdnUrl?.toUrl() } editedMessageEmbed( event.getMessage(), - event.old?.trimmedContents(512) ?: "Failed to get content of message", - event.new.trimmedContents(512) ?: "Failed to get content of message" + event.old?.trimmedContents(512) ?: Translations.Events.Edit.contentFailed.translate(), + event.new.trimmedContents(512) ?: Translations.Events.Edit.contentFailed.translate() ) } } @@ -77,57 +72,36 @@ class MessageEditing : Extension() { } } - unsafeSlashCommand(::EditArgs) { - name = "edit" - description = "Edit a message you sent" + ephemeralSlashCommand(::EditArgs, ::EditModal) { + name = Translations.Events.Edit.name + description = Translations.Events.Edit.description - initialResponse = InitialSlashCommandResponse.None + locking = true - action { - val userDmId = OpenThreadCollection().getDmFromThreadChannel(channel.id) - val dmMessageIdToEdit = SentMessageCollection().getDmMessageById(channel.id, arguments.messageId) + action { modal -> + val userDmId = OpenThreadsCollection().getDmFromThreadChannel(channel.id) + val dmMessageIdToEdit = SentMessagesCollection().getDmMessageById(channel.id, arguments.messageId) val dmMessageToEdit = event.kord.getUser(userDmId!!)!!.getDmChannel().getMessage(dmMessageIdToEdit!!) val threadMessageToEdit = channel.getMessage(arguments.messageId) // Get all messages from the thread and find the one that has the same ID as the message being edited - val originalSentMessage = SentMessageCollection().getMessagesInThread(channel.id).find { + val originalSentMessage = SentMessagesCollection().getMessagesInThread(channel.id).find { it?.threadMessageId == arguments.messageId } val originalMessageEmbed = dmMessageToEdit.embeds[0] - val response = event.interaction.modal("Edit message", "editMessageModal") { - actionRow { - textInput(TextInputStyle.Paragraph, "editInput", "New message content") { - value = originalMessageEmbed.description - } - } - } - - val interaction = - response.kord.waitFor(120.seconds.inWholeMilliseconds) { - interaction.modalId == "editMessageModal" - }?.interaction ?: run { - response.createEphemeralFollowup { - embed { - description = "Editing timed out" - } - } - return@action - } - - val newMessageContent = interaction.textInputs["editInput"]!!.value!! - val modalResponse = interaction.deferEphemeralResponse() + EditModal().newContents.initialValue = originalMessageEmbed.description?.toKey() threadMessageToEdit.edit { embed { author { name = user.asUser().tag - icon = user.asUser().avatar?.url + icon = user.asUser().avatar?.cdnUrl?.toUrl() } editedMessageEmbed( dmMessageToEdit, - originalMessageEmbed.description ?: "Failed to get original message content", - newMessageContent, + originalMessageEmbed.description ?: Translations.Events.Edit.contentFailed.translate(), + modal?.newContents.toString(), DISCORD_GREEN ) } @@ -136,17 +110,17 @@ class MessageEditing : Extension() { dmMessageToEdit.edit { embed { messageEmbed( - newMessageContent, + modal?.newContents.toString(), user.asUser(), MAIL_SERVER, DISCORD_GREEN, - originalSentMessage?.isAnonymous ?: false + originalSentMessage?.isAnonymous == true ) } } - modalResponse.respond { - content = "Message edited" + respond { + content = Translations.Events.Edit.edited.translate() } } } @@ -154,8 +128,16 @@ class MessageEditing : Extension() { inner class EditArgs : Arguments() { val messageId by snowflake { - name = "message-id" - description = "The ID of the message you're editing" + name = Translations.Events.Edit.Args.MessageId.name + description = Translations.Events.Edit.Args.MessageId.description + } + } + + inner class EditModal : ModalForm() { + override var title: Key = Translations.Events.Edit.Modal.title + + val newContents = paragraphText { + label = Translations.Events.Edit.Modal.NewContents.label } } } diff --git a/src/main/kotlin/io/github/nocomment1105/modmailbot/extensions/events/MessageReceiving.kt b/src/main/kotlin/io/github/nocomment1105/modmailbot/extensions/events/MessageReceiving.kt index afd1bd8..8c6d82d 100644 --- a/src/main/kotlin/io/github/nocomment1105/modmailbot/extensions/events/MessageReceiving.kt +++ b/src/main/kotlin/io/github/nocomment1105/modmailbot/extensions/events/MessageReceiving.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 NoComment1105 + * Copyright (c) 2022-2025 NoComment1105 * * This file is part of ModMail. * @@ -9,39 +9,37 @@ package io.github.nocomment1105.modmailbot.extensions.events -import com.kotlindiscord.kord.extensions.DISCORD_BLURPLE -import com.kotlindiscord.kord.extensions.checks.noGuild -import com.kotlindiscord.kord.extensions.extensions.Extension -import com.kotlindiscord.kord.extensions.extensions.event -import com.kotlindiscord.kord.extensions.time.TimestampType -import com.kotlindiscord.kord.extensions.time.toDiscord -import com.kotlindiscord.kord.extensions.utils.createdAt import dev.kord.core.behavior.channel.createMessage import dev.kord.core.behavior.createTextChannel import dev.kord.core.behavior.getChannelOf import dev.kord.core.entity.channel.TextChannel import dev.kord.core.event.message.MessageCreateEvent -import dev.kord.rest.builder.message.create.embed +import dev.kord.rest.builder.message.embed import dev.kord.x.emoji.Emojis import dev.kord.x.emoji.addReaction +import dev.kordex.core.DISCORD_BLURPLE +import dev.kordex.core.checks.noGuild +import dev.kordex.core.extensions.Extension +import dev.kordex.core.extensions.event +import dev.kordex.core.time.TimestampType +import dev.kordex.core.time.toDiscord +import dev.kordex.core.utils.createdAt import io.github.nocomment1105.modmailbot.MAIL_SERVER import io.github.nocomment1105.modmailbot.MAIN_SERVER -import io.github.nocomment1105.modmailbot.database.collections.OpenThreadCollection -import io.github.nocomment1105.modmailbot.database.collections.SentMessageCollection +import io.github.nocomment1105.modmailbot.database.collections.OpenThreadsCollection +import io.github.nocomment1105.modmailbot.database.collections.SentMessagesCollection import io.github.nocomment1105.modmailbot.database.entities.OpenThreadData import io.github.nocomment1105.modmailbot.database.entities.SentMessageData import io.github.nocomment1105.modmailbot.messageEmbed import kotlinx.coroutines.flow.toList import kotlinx.datetime.Clock -import mu.KotlinLogging +import modmailbot.i18n.Translations class MessageReceiving : Extension() { override val name = "message-receiving" override suspend fun setup() { - val logger = KotlinLogging.logger("Message Receiving") - event { check { noGuild() @@ -50,16 +48,18 @@ class MessageReceiving : Extension() { action { // Check to see if the user has any threads open already val openThread: Boolean = - OpenThreadCollection().getOpenThreadsForUser(event.message.author!!.id) != null + OpenThreadsCollection().getOpenThreadsForUser(event.message.author!!.id) != null val mailChannel: TextChannel + val translations = Translations.Events.Receiving + if (!openThread) { // Get the mail channel mailChannel = kord.getGuildOrNull(MAIL_SERVER)!!.createTextChannel(event.message.author!!.tag) // Store the users thread in the database - OpenThreadCollection().add( + OpenThreadsCollection().add( OpenThreadData( userId = event.message.author!!.id, threadId = mailChannel.id @@ -70,22 +70,24 @@ class MessageReceiving : Extension() { content = "@here" // TODO Implement a config options system // Provide some information about the user in an initial embed embed { - description = "${event.message.author!!.mention} was created " + + description = + translations.description.translate(event.message.author!!.mention) + event.message.author!!.fetchUser().createdAt.toDiscord(TimestampType.LongDateTime) timestamp = Clock.System.now() color = DISCORD_BLURPLE field { - name = "Nickname" - value = event.message.author!!.asMember(MAIN_SERVER).nickname ?: "none" + name = translations.nickname.translate() + value = event.message.author!!.asMember(MAIN_SERVER).nickname + ?: Translations.Utils.none.translate() inline = true } field { val roles = event.message.author!!.asMember(MAIN_SERVER).roles.toList().map { it } - name = "Roles" + name = translations.roles.translate() value = if (roles.isEmpty()) { - "None" + Translations.Utils.none.translate() } else { "${roles.forEach { "${it.name}\n" }}" } @@ -94,12 +96,14 @@ class MessageReceiving : Extension() { author { name = event.message.author?.tag - icon = event.message.author?.avatar!!.url + icon = event.message.author?.avatar?.cdnUrl?.toUrl() } footer { - text = "User ID: ${event.message.author!!.id} |" + - " DM ID: ${event.message.author!!.getDmChannel().id}" + text = translations.footer.translate( + event.message.author!!.id, + event.message.author!!.getDmChannel().id + ) } } } @@ -111,10 +115,10 @@ class MessageReceiving : Extension() { } } - SentMessageCollection().addMessage( + SentMessagesCollection().addMessage( SentMessageData( mailChannel.id, - SentMessageCollection().getNextMessageNumber(mailChannel.id), + SentMessagesCollection().getNextMessageNumber(mailChannel.id), event.message.id, mailChannelMessage.id, wasSentByStaff = false, @@ -127,7 +131,7 @@ class MessageReceiving : Extension() { } else { // Get the mail server from the config file mailChannel = kord.getGuildOrNull(MAIL_SERVER)!!.getChannelOf( - OpenThreadCollection().getOpenThreadsForUser(event.message.author!!.id)!!.threadId + OpenThreadsCollection().getOpenThreadsForUser(event.message.author!!.id)!!.threadId ) // Send the user's message through to the mail server @@ -137,10 +141,10 @@ class MessageReceiving : Extension() { } } - SentMessageCollection().addMessage( + SentMessagesCollection().addMessage( SentMessageData( mailChannel.id, - SentMessageCollection().getNextMessageNumber(mailChannel.id), + SentMessagesCollection().getNextMessageNumber(mailChannel.id), event.message.id, mailChannelMessage.id, false, diff --git a/src/main/resources/logback.groovy b/src/main/resources/logback.groovy new file mode 100644 index 0000000..a077899 --- /dev/null +++ b/src/main/resources/logback.groovy @@ -0,0 +1,27 @@ +import ch.qos.logback.core.joran.spi.ConsoleTarget + +def environment = System.getenv().getOrDefault("ENVIRONMENT", "production") + +def defaultLevel = INFO +def defaultTarget = ConsoleTarget.SystemOut +def loggers = ["CONSOLE"] + +if (environment == "development") { + defaultLevel = DEBUG + defaultTarget = ConsoleTarget.SystemErr + + // Silence warning about missing native PRNG + logger("io.ktor.util.random", ERROR) +} + +appender("CONSOLE", ConsoleAppender) { + encoder(PatternLayoutEncoder) { + pattern = "%boldMagenta(%d{dd-MM-yyyy HH:mm:ss}) %gray(|) %boldCyan(%-30.-30thread) %gray(|) %highlight(%-5level) %gray(|) %boldGreen(%-40.40logger{40}) %gray(|) %msg%n" + + withJansi = true + } + + target = defaultTarget +} + +root(defaultLevel, loggers) diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml deleted file mode 100644 index 1a38a6e..0000000 --- a/src/main/resources/logback.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - %boldMagenta(%d{dd-MM-yyyy HH:mm:ss}) %gray(|) %boldCyan(%-30.-30thread) %gray(|) %highlight(%-5level) %gray(|) %boldGreen(%-40.40logger{40}) %gray(|) %msg%n - - - - - - - diff --git a/src/main/resources/logbackCompiler.groovy b/src/main/resources/logbackCompiler.groovy new file mode 100644 index 0000000..103667a --- /dev/null +++ b/src/main/resources/logbackCompiler.groovy @@ -0,0 +1,453 @@ +importsAcceptList = [ + + 'ch.qos.logback.core.testUtil.SampleConverter', + + 'ch.qos.logback.core.testUtil.StringListAppender', + 'java.lang.Object', + 'org.springframework.beans.factory.annotation.Autowired', + 'java.nio.charset.Charset.forName', + 'com.logentries.logback.LogentriesAppender', + 'grails.util.BuildSettings', + 'grails.util.Environment', + 'io.micronaut.context.env.Environment', + 'org.slf4j.MDC', + 'org.springframework.boot.logging.logback.ColorConverter', + 'org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter', + 'java.nio.charset.Charset', + 'java.nio.charset.StandardCharsets', + + 'ch.qos.logback.core.BasicStatusManager', + 'ch.qos.logback.core.ConsoleAppender', + 'ch.qos.logback.core.hook.ShutdownHook', + 'ch.qos.logback.core.hook.ShutdownHookBase', + 'ch.qos.logback.core.hook.DelayingShutdownHook', + 'ch.qos.logback.core.spi.PropertyContainer', + 'ch.qos.logback.core.spi.ContextAwareBase', + 'ch.qos.logback.core.spi.LogbackLock', + 'ch.qos.logback.core.spi.FilterAttachableImpl', + 'ch.qos.logback.core.spi.ContextAwareImpl', + 'ch.qos.logback.core.spi.ScanException', + 'ch.qos.logback.core.spi.DeferredProcessingAware', + 'ch.qos.logback.core.spi.ContextAware', + 'ch.qos.logback.core.spi.LifeCycle', + 'ch.qos.logback.core.spi.FilterReply', + 'ch.qos.logback.core.spi.PreSerializationTransformer', + 'ch.qos.logback.core.spi.AppenderAttachable', + 'ch.qos.logback.core.spi.CyclicBufferTracker', + 'ch.qos.logback.core.spi.FilterAttachable', + 'ch.qos.logback.core.spi.ComponentTracker', + 'ch.qos.logback.core.spi.AppenderAttachableImpl', + 'ch.qos.logback.core.spi.PropertyDefiner', + 'ch.qos.logback.core.spi.AbstractComponentTracker', + 'ch.qos.logback.core.property.FileExistsPropertyDefiner', + 'ch.qos.logback.core.property.ResourceExistsPropertyDefiner', + 'ch.qos.logback.core.CoreConstants', + 'ch.qos.logback.core.layout.EchoLayout', + 'ch.qos.logback.core.Appender', + 'ch.qos.logback.core.joran.JoranConfiguratorBase', + 'ch.qos.logback.core.joran.spi.ActionException', + 'ch.qos.logback.core.joran.spi.HostClassAndPropertyDouble', + 'ch.qos.logback.core.joran.spi.JoranException', + 'ch.qos.logback.core.joran.spi.NoAutoStart', + 'ch.qos.logback.core.joran.spi.EventPlayer', + 'ch.qos.logback.core.joran.spi.XMLUtil', + 'ch.qos.logback.core.joran.spi.ConsoleTarget', + 'ch.qos.logback.core.joran.spi.Interpreter', + 'ch.qos.logback.core.joran.spi.SimpleRuleStore', + 'ch.qos.logback.core.joran.spi.InterpretationContext', + 'ch.qos.logback.core.joran.spi.RuleStore', + 'ch.qos.logback.core.joran.spi.NoAutoStartUtil', + 'ch.qos.logback.core.joran.spi.ElementSelector', + 'ch.qos.logback.core.joran.spi.ConfigurationWatchList', + 'ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry', + 'ch.qos.logback.core.joran.spi.DefaultClass', + 'ch.qos.logback.core.joran.spi.ElementPath', + 'ch.qos.logback.core.joran.conditional.ThenOrElseActionBase', + 'ch.qos.logback.core.joran.conditional.Condition', + 'ch.qos.logback.core.joran.conditional.PropertyWrapperForScripts', + 'ch.qos.logback.core.joran.conditional.ThenAction', + 'ch.qos.logback.core.joran.conditional.PropertyEvalScriptBuilder', + 'ch.qos.logback.core.joran.conditional.ElseAction', + 'ch.qos.logback.core.joran.conditional.IfAction', + 'ch.qos.logback.core.joran.util.beans.BeanDescriptionCache', + 'ch.qos.logback.core.joran.util.beans.BeanDescriptionFactory', + 'ch.qos.logback.core.joran.util.beans.BeanDescription', + 'ch.qos.logback.core.joran.util.beans.BeanUtil', + 'ch.qos.logback.core.joran.util.PropertySetter', + 'ch.qos.logback.core.joran.util.ConfigurationWatchListUtil', + 'ch.qos.logback.core.joran.util.StringToObjectConverter', + 'ch.qos.logback.core.joran.GenericConfigurator', + 'ch.qos.logback.core.joran.action.ImplicitAction', + 'ch.qos.logback.core.joran.action.IncludeAction', + 'ch.qos.logback.core.joran.action.NOPAction', + 'ch.qos.logback.core.joran.action.IADataForBasicProperty', + 'ch.qos.logback.core.joran.action.TimestampAction', + 'ch.qos.logback.core.joran.action.AbstractEventEvaluatorAction', + 'ch.qos.logback.core.joran.action.ParamAction', + 'ch.qos.logback.core.joran.action.AppenderAction', + 'ch.qos.logback.core.joran.action.DefinePropertyAction', + 'ch.qos.logback.core.joran.action.StatusListenerAction', + 'ch.qos.logback.core.joran.action.ContextPropertyAction', + 'ch.qos.logback.core.joran.action.NestedComplexPropertyIA', + 'ch.qos.logback.core.joran.action.NestedBasicPropertyIA', + 'ch.qos.logback.core.joran.action.Action', + 'ch.qos.logback.core.joran.action.AppenderRefAction', + 'ch.qos.logback.core.joran.action.ActionUtil', + 'ch.qos.logback.core.joran.action.ShutdownHookAction', + 'ch.qos.logback.core.joran.action.IADataForComplexProperty', + 'ch.qos.logback.core.joran.action.ConversionRuleAction', + 'ch.qos.logback.core.joran.action.ActionConst', + 'ch.qos.logback.core.joran.action.PropertyAction', + 'ch.qos.logback.core.joran.action.NewRuleAction', + 'ch.qos.logback.core.joran.node.ComponentNode', + 'ch.qos.logback.core.joran.event.EndEvent', + 'ch.qos.logback.core.joran.event.SaxEventRecorder', + 'ch.qos.logback.core.joran.event.SaxEvent', + 'ch.qos.logback.core.joran.event.BodyEvent', + 'ch.qos.logback.core.joran.event.StartEvent', + 'ch.qos.logback.core.joran.event.InPlayListener', + 'ch.qos.logback.core.joran.event.stax.EndEvent', + 'ch.qos.logback.core.joran.event.stax.StaxEventRecorder', + 'ch.qos.logback.core.joran.event.stax.BodyEvent', + 'ch.qos.logback.core.joran.event.stax.StartEvent', + 'ch.qos.logback.core.joran.event.stax.StaxEvent', + 'ch.qos.logback.core.LogbackException', + 'ch.qos.logback.core.PropertyDefinerBase', + 'ch.qos.logback.core.helpers.CyclicBuffer', + 'ch.qos.logback.core.helpers.ThrowableToStringArray', + 'ch.qos.logback.core.helpers.Transform', + 'ch.qos.logback.core.helpers.NOPAppender', + 'ch.qos.logback.core.net.LoginAuthenticator', + 'ch.qos.logback.core.net.DefaultSocketConnector', + 'ch.qos.logback.core.net.ssl.KeyStoreFactoryBean', + 'ch.qos.logback.core.net.ssl.SSLParametersConfiguration', + 'ch.qos.logback.core.net.ssl.SSLComponent', + 'ch.qos.logback.core.net.ssl.SSLNestedComponentRegistryRules', + 'ch.qos.logback.core.net.ssl.SSLConfigurableSocket', + 'ch.qos.logback.core.net.ssl.SSLConfigurableServerSocket', + 'ch.qos.logback.core.net.ssl.SSLConfiguration', + 'ch.qos.logback.core.net.ssl.ConfigurableSSLSocketFactory', + 'ch.qos.logback.core.net.ssl.ConfigurableSSLServerSocketFactory', + 'ch.qos.logback.core.net.ssl.SecureRandomFactoryBean', + 'ch.qos.logback.core.net.ssl.SSLContextFactoryBean', + 'ch.qos.logback.core.net.ssl.SSL', + 'ch.qos.logback.core.net.ssl.SSLConfigurable', + 'ch.qos.logback.core.net.ssl.TrustManagerFactoryFactoryBean', + 'ch.qos.logback.core.net.ssl.KeyManagerFactoryFactoryBean', + 'ch.qos.logback.core.net.SMTPAppenderBase', + 'ch.qos.logback.core.net.SyslogAppenderBase', + 'ch.qos.logback.core.net.SocketConnector', + 'ch.qos.logback.core.net.SyslogOutputStream', + 'ch.qos.logback.core.net.QueueFactory', + 'ch.qos.logback.core.net.HardenedObjectInputStream', + 'ch.qos.logback.core.net.AbstractSocketAppender', + 'ch.qos.logback.core.net.AbstractSSLSocketAppender', + 'ch.qos.logback.core.net.ObjectWriterFactory', + 'ch.qos.logback.core.net.ObjectWriter', + 'ch.qos.logback.core.net.AutoFlushingObjectWriter', + 'ch.qos.logback.core.net.SyslogConstants', + 'ch.qos.logback.core.net.server.ServerRunner', + 'ch.qos.logback.core.net.server.Client', + 'ch.qos.logback.core.net.server.ServerListener', + 'ch.qos.logback.core.net.server.RemoteReceiverStreamClient', + 'ch.qos.logback.core.net.server.AbstractServerSocketAppender', + 'ch.qos.logback.core.net.server.ClientVisitor', + 'ch.qos.logback.core.net.server.RemoteReceiverClient', + 'ch.qos.logback.core.net.server.RemoteReceiverServerRunner', + 'ch.qos.logback.core.net.server.SSLServerSocketAppenderBase', + 'ch.qos.logback.core.net.server.ConcurrentServerRunner', + 'ch.qos.logback.core.net.server.ServerSocketListener', + 'ch.qos.logback.core.net.server.RemoteReceiverServerListener', + 'ch.qos.logback.core.UnsynchronizedAppenderBase', + 'ch.qos.logback.core.AsyncAppenderBase', + 'ch.qos.logback.core.util.CloseUtil', + 'ch.qos.logback.core.util.DatePatternToRegexUtil', + 'ch.qos.logback.core.util.StatusListenerConfigHelper', + 'ch.qos.logback.core.util.SystemInfo', + 'ch.qos.logback.core.util.DefaultInvocationGate', + 'ch.qos.logback.core.util.CachingDateFormatter', + 'ch.qos.logback.core.util.InterruptUtil', + 'ch.qos.logback.core.util.LocationUtil', + 'ch.qos.logback.core.util.TimeUtil', + 'ch.qos.logback.core.util.COWArrayList', + 'ch.qos.logback.core.util.Loader', + 'ch.qos.logback.core.util.CharSequenceState', + 'ch.qos.logback.core.util.StatusPrinter', + 'ch.qos.logback.core.util.Duration', + 'ch.qos.logback.core.util.ContentTypeUtil', + 'ch.qos.logback.core.util.FileUtil', + 'ch.qos.logback.core.util.DynamicClassLoadingException', + 'ch.qos.logback.core.util.InvocationGate', + 'ch.qos.logback.core.util.OptionHelper', + 'ch.qos.logback.core.util.IncompatibleClassException', + 'ch.qos.logback.core.util.ExecutorServiceUtil', + 'ch.qos.logback.core.util.StringCollectionUtil', + 'ch.qos.logback.core.util.CharSequenceToRegexMapper', + 'ch.qos.logback.core.util.FixedDelay', + 'ch.qos.logback.core.util.FileSize', + 'ch.qos.logback.core.util.DelayStrategy', + 'ch.qos.logback.core.util.EnvUtil', + 'ch.qos.logback.core.util.ContextUtil', + 'ch.qos.logback.core.util.AggregationType', + 'ch.qos.logback.core.util.PropertySetterException', + 'ch.qos.logback.core.LifeCycleManager', + 'ch.qos.logback.core.LayoutBase', + 'ch.qos.logback.core.encoder.NonClosableInputStream', + 'ch.qos.logback.core.encoder.Encoder', + 'ch.qos.logback.core.encoder.ByteArrayUtil', + 'ch.qos.logback.core.encoder.EncoderBase', + 'ch.qos.logback.core.encoder.EchoEncoder', + 'ch.qos.logback.core.encoder.LayoutWrappingEncoder', + 'ch.qos.logback.core.recovery.RecoveryCoordinator', + 'ch.qos.logback.core.recovery.ResilientOutputStreamBase', + 'ch.qos.logback.core.recovery.ResilientSyslogOutputStream', + 'ch.qos.logback.core.recovery.ResilientFileOutputStream', + 'ch.qos.logback.core.AppenderBase', + 'ch.qos.logback.core.subst.Node', + 'ch.qos.logback.core.subst.Parser', + 'ch.qos.logback.core.subst.Token', + 'ch.qos.logback.core.subst.NodeToStringTransformer', + 'ch.qos.logback.core.subst.Tokenizer', + 'ch.qos.logback.core.FileAppender', + 'ch.qos.logback.core.sift.AppenderFactory', + 'ch.qos.logback.core.sift.SiftingAppenderBase', + 'ch.qos.logback.core.sift.SiftingJoranConfiguratorBase', + 'ch.qos.logback.core.sift.AbstractDiscriminator', + 'ch.qos.logback.core.sift.Discriminator', + 'ch.qos.logback.core.sift.AbstractAppenderFactoryUsingJoran', + 'ch.qos.logback.core.sift.AppenderTracker', + 'ch.qos.logback.core.sift.DefaultDiscriminator', + 'ch.qos.logback.core.html.CssBuilder', + 'ch.qos.logback.core.html.NOPThrowableRenderer', + 'ch.qos.logback.core.html.HTMLLayoutBase', + 'ch.qos.logback.core.html.IThrowableRenderer', + 'ch.qos.logback.core.rolling.TriggeringPolicyBase', + 'ch.qos.logback.core.rolling.helper.Compressor', + 'ch.qos.logback.core.rolling.helper.PeriodicityType', + 'ch.qos.logback.core.rolling.helper.TokenConverter', + 'ch.qos.logback.core.rolling.helper.IntegerTokenConverter', + 'ch.qos.logback.core.rolling.helper.CompressionMode', + 'ch.qos.logback.core.rolling.helper.ArchiveRemover', + 'ch.qos.logback.core.rolling.helper.FileFilterUtil', + 'ch.qos.logback.core.rolling.helper.RenameUtil', + 'ch.qos.logback.core.rolling.helper.DateTokenConverter', + 'ch.qos.logback.core.rolling.helper.FileNamePattern', + 'ch.qos.logback.core.rolling.helper.RollingCalendar', + 'ch.qos.logback.core.rolling.helper.FileStoreUtil', + 'ch.qos.logback.core.rolling.helper.SizeAndTimeBasedArchiveRemover', + 'ch.qos.logback.core.rolling.helper.TimeBasedArchiveRemover', + 'ch.qos.logback.core.rolling.helper.MonoTypedConverter', + 'ch.qos.logback.core.rolling.RollingPolicyBase', + 'ch.qos.logback.core.rolling.RollingFileAppender', + 'ch.qos.logback.core.rolling.FixedWindowRollingPolicy', + 'ch.qos.logback.core.rolling.TimeBasedFileNamingAndTriggeringPolicyBase', + 'ch.qos.logback.core.rolling.TimeBasedFileNamingAndTriggeringPolicy', + 'ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy', + 'ch.qos.logback.core.rolling.RollingPolicy', + 'ch.qos.logback.core.rolling.TimeBasedRollingPolicy', + 'ch.qos.logback.core.rolling.DefaultTimeBasedFileNamingAndTriggeringPolicy', + 'ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy', + 'ch.qos.logback.core.rolling.RolloverFailure', + 'ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP', + 'ch.qos.logback.core.rolling.TriggeringPolicy', + 'ch.qos.logback.core.pattern.ReplacingCompositeConverter', + 'ch.qos.logback.core.pattern.ConverterUtil', + 'ch.qos.logback.core.pattern.parser.Compiler', + 'ch.qos.logback.core.pattern.parser.Node', + 'ch.qos.logback.core.pattern.parser.Parser', + 'ch.qos.logback.core.pattern.parser.Token', + 'ch.qos.logback.core.pattern.parser.OptionTokenizer', + 'ch.qos.logback.core.pattern.parser.TokenStream', + 'ch.qos.logback.core.pattern.parser.CompositeNode', + 'ch.qos.logback.core.pattern.parser.FormattingNode', + 'ch.qos.logback.core.pattern.parser.SimpleKeywordNode', + 'ch.qos.logback.core.pattern.Converter', + 'ch.qos.logback.core.pattern.PatternLayoutEncoderBase', + 'ch.qos.logback.core.pattern.LiteralConverter', + 'ch.qos.logback.core.pattern.PostCompileProcessor', + 'ch.qos.logback.core.pattern.util.RegularEscapeUtil', + 'ch.qos.logback.core.pattern.util.AsIsEscapeUtil', + 'ch.qos.logback.core.pattern.util.AlmostAsIsEscapeUtil', + 'ch.qos.logback.core.pattern.util.IEscapeUtil', + 'ch.qos.logback.core.pattern.util.RestrictedEscapeUtil', + 'ch.qos.logback.core.pattern.SpacePadder', + 'ch.qos.logback.core.pattern.CompositeConverter', + 'ch.qos.logback.core.pattern.PatternLayoutBase', + 'ch.qos.logback.core.pattern.DynamicConverter', + 'ch.qos.logback.core.pattern.color.YellowCompositeConverter', + 'ch.qos.logback.core.pattern.color.ANSIConstants', + 'ch.qos.logback.core.pattern.color.BoldYellowCompositeConverter', + 'ch.qos.logback.core.pattern.color.BoldBlueCompositeConverter', + 'ch.qos.logback.core.pattern.color.BoldWhiteCompositeConverter', + 'ch.qos.logback.core.pattern.color.CyanCompositeConverter', + 'ch.qos.logback.core.pattern.color.MagentaCompositeConverter', + 'ch.qos.logback.core.pattern.color.BlueCompositeConverter', + 'ch.qos.logback.core.pattern.color.BlackCompositeConverter', + 'ch.qos.logback.core.pattern.color.ForegroundCompositeConverterBase', + 'ch.qos.logback.core.pattern.color.GrayCompositeConverter', + 'ch.qos.logback.core.pattern.color.BoldMagentaCompositeConverter', + 'ch.qos.logback.core.pattern.color.BoldCyanCompositeConverter', + 'ch.qos.logback.core.pattern.color.RedCompositeConverter', + 'ch.qos.logback.core.pattern.color.BoldGreenCompositeConverter', + 'ch.qos.logback.core.pattern.color.BoldRedCompositeConverter', + 'ch.qos.logback.core.pattern.color.GreenCompositeConverter', + 'ch.qos.logback.core.pattern.color.WhiteCompositeConverter', + 'ch.qos.logback.core.pattern.FormattingConverter', + 'ch.qos.logback.core.pattern.IdentityCompositeConverter', + 'ch.qos.logback.core.pattern.FormatInfo', + 'ch.qos.logback.core.OutputStreamAppender', + 'ch.qos.logback.core.boolex.JaninoEventEvaluatorBase', + 'ch.qos.logback.core.boolex.Matcher', + 'ch.qos.logback.core.boolex.EventEvaluatorBase', + 'ch.qos.logback.core.boolex.EvaluationException', + 'ch.qos.logback.core.boolex.EventEvaluator', + 'ch.qos.logback.core.read.CyclicBufferAppender', + 'ch.qos.logback.core.read.ListAppender', + 'ch.qos.logback.core.Context', + 'ch.qos.logback.core.ContextBase', + 'ch.qos.logback.core.status.StatusListenerAsList', + 'ch.qos.logback.core.status.StatusBase', + 'ch.qos.logback.core.status.NopStatusListener', + 'ch.qos.logback.core.status.StatusUtil', + 'ch.qos.logback.core.status.OnPrintStreamStatusListenerBase', + 'ch.qos.logback.core.status.StatusManager', + 'ch.qos.logback.core.status.ViewStatusMessagesServletBase', + 'ch.qos.logback.core.status.ErrorStatus', + 'ch.qos.logback.core.status.Status', + 'ch.qos.logback.core.status.StatusListener', + 'ch.qos.logback.core.status.InfoStatus', + 'ch.qos.logback.core.status.OnConsoleStatusListener', + 'ch.qos.logback.core.status.WarnStatus', + 'ch.qos.logback.core.status.OnErrorConsoleStatusListener', + 'ch.qos.logback.core.filter.EvaluatorFilter', + 'ch.qos.logback.core.filter.Filter', + 'ch.qos.logback.core.filter.AbstractMatcherFilter', + 'ch.qos.logback.core.Layout', + 'ch.qos.logback.classic.ViewStatusMessagesServlet', + 'ch.qos.logback.classic.ClassicConstants', + 'ch.qos.logback.classic.layout.TTLLLayout', + 'ch.qos.logback.classic.helpers.MDCInsertingServletFilter', + 'ch.qos.logback.classic.Level', + 'ch.qos.logback.classic.Level.off', + 'ch.qos.logback.classic.Level.error', + 'ch.qos.logback.classic.Level.warn', + 'ch.qos.logback.classic.Level.info', + 'ch.qos.logback.classic.Level.debug', + 'ch.qos.logback.classic.Level.trace', + 'ch.qos.logback.classic.Level.all,', + 'ch.qos.logback.classic.net.SSLSocketReceiver', + 'ch.qos.logback.classic.net.ReceiverBase', + 'ch.qos.logback.classic.net.SimpleSocketServer', + 'ch.qos.logback.classic.net.SimpleSSLSocketServer', + 'ch.qos.logback.classic.net.SocketNode', + 'ch.qos.logback.classic.net.SMTPAppender', + 'ch.qos.logback.classic.net.SocketReceiver', + 'ch.qos.logback.classic.net.SocketAcceptor', + 'ch.qos.logback.classic.net.SSLSocketAppender', + 'ch.qos.logback.classic.net.LoggingEventPreSerializationTransformer', + 'ch.qos.logback.classic.net.server.RemoteAppenderStreamClient', + 'ch.qos.logback.classic.net.server.RemoteAppenderServerListener', + 'ch.qos.logback.classic.net.server.SSLServerSocketAppender', + 'ch.qos.logback.classic.net.server.RemoteAppenderClient', + 'ch.qos.logback.classic.net.server.HardenedLoggingEventInputStream', + 'ch.qos.logback.classic.net.server.ServerSocketAppender', + 'ch.qos.logback.classic.net.server.SSLServerSocketReceiver', + 'ch.qos.logback.classic.net.server.RemoteAppenderServerRunner', + 'ch.qos.logback.classic.net.server.ServerSocketReceiver', + 'ch.qos.logback.classic.net.SocketAppender', + 'ch.qos.logback.classic.net.SyslogAppender', + 'ch.qos.logback.classic.PatternLayout', + 'ch.qos.logback.classic.util.ContextSelectorStaticBinder', + 'ch.qos.logback.classic.util.StatusViaSLF4JLoggerFactory', + 'ch.qos.logback.classic.util.JNDIUtil', + 'ch.qos.logback.classic.util.LevelToSyslogSeverity', + 'ch.qos.logback.classic.util.LoggerNameUtil', + 'ch.qos.logback.classic.util.LogbackMDCAdapter', + 'ch.qos.logback.classic.util.CopyOnInheritThreadLocal', + 'ch.qos.logback.classic.util.ContextInitializer', + 'ch.qos.logback.classic.util.EnvUtil', + 'ch.qos.logback.classic.util.DefaultNestedComponentRules', + 'ch.qos.logback.classic.AsyncAppender', + 'ch.qos.logback.classic.jul.JULHelper', + 'ch.qos.logback.classic.jul.LevelChangePropagator', + 'ch.qos.logback.classic.encoder.PatternLayoutEncoder', + 'ch.qos.logback.classic.db.names.DBNameResolver', + 'ch.qos.logback.classic.db.names.ColumnName', + 'ch.qos.logback.classic.db.names.TableName', + 'ch.qos.logback.classic.db.names.DefaultDBNameResolver', + 'ch.qos.logback.classic.db.names.SimpleDBNameResolver', + 'ch.qos.logback.classic.log4j.XMLLayout', + 'ch.qos.logback.classic.LoggerContext', + 'ch.qos.logback.classic.turbo.TurboFilter', + 'ch.qos.logback.classic.turbo.MDCFilter', + 'ch.qos.logback.classic.turbo.ReconfigureOnChangeFilter', + 'ch.qos.logback.classic.turbo.DuplicateMessageFilter', + 'ch.qos.logback.classic.turbo.MarkerFilter', + 'ch.qos.logback.classic.turbo.MDCValueLevelPair', + 'ch.qos.logback.classic.turbo.DynamicThresholdFilter', + 'ch.qos.logback.classic.turbo.MatchingFilter', + 'ch.qos.logback.classic.turbo.LRUMessageCache', + 'ch.qos.logback.classic.selector.servlet.LoggerContextFilter', + 'ch.qos.logback.classic.selector.servlet.ContextDetachingSCL', + 'ch.qos.logback.classic.selector.ContextJNDISelector', + 'ch.qos.logback.classic.selector.DefaultContextSelector', + 'ch.qos.logback.classic.selector.ContextSelector', + 'ch.qos.logback.classic.sift.MDCBasedDiscriminator', + 'ch.qos.logback.classic.sift.SiftingJoranConfigurator', + 'ch.qos.logback.classic.sift.JNDIBasedContextDiscriminator', + 'ch.qos.logback.classic.sift.AppenderFactoryUsingJoran', + 'ch.qos.logback.classic.sift.ContextBasedDiscriminator', + 'ch.qos.logback.classic.sift.SiftingAppender', + 'ch.qos.logback.classic.sift.SiftAction', + 'ch.qos.logback.classic.html.UrlCssBuilder', + 'ch.qos.logback.classic.html.HTMLLayout', + 'ch.qos.logback.classic.html.DefaultCssBuilder', + 'ch.qos.logback.classic.html.DefaultThrowableRenderer', + 'ch.qos.logback.classic.Logger', + 'ch.qos.logback.classic.pattern.ThrowableHandlingConverter', + 'ch.qos.logback.classic.pattern.ContextNameConverter', + 'ch.qos.logback.classic.pattern.LocalSequenceNumberConverter', + 'ch.qos.logback.classic.pattern.ClassOfCallerConverter', + 'ch.qos.logback.classic.pattern.PrefixCompositeConverter', + 'ch.qos.logback.classic.pattern.LineOfCallerConverter', + 'ch.qos.logback.classic.pattern.EnsureExceptionHandling', + 'ch.qos.logback.classic.pattern.TargetLengthBasedClassNameAbbreviator', + 'ch.qos.logback.classic.pattern.FileOfCallerConverter', + 'ch.qos.logback.classic.pattern.LevelConverter', + 'ch.qos.logback.classic.pattern.ExtendedThrowableProxyConverter', + 'ch.qos.logback.classic.pattern.NamedConverter', + 'ch.qos.logback.classic.pattern.ClassicConverter', + 'ch.qos.logback.classic.pattern.NopThrowableInformationConverter', + 'ch.qos.logback.classic.pattern.RootCauseFirstThrowableProxyConverter', + 'ch.qos.logback.classic.pattern.MethodOfCallerConverter', + 'ch.qos.logback.classic.pattern.CallerDataConverter', + 'ch.qos.logback.classic.pattern.ClassNameOnlyAbbreviator', + 'ch.qos.logback.classic.pattern.MarkerConverter', + 'ch.qos.logback.classic.pattern.RelativeTimeConverter', + 'ch.qos.logback.classic.pattern.DateConverter', + 'ch.qos.logback.classic.pattern.PropertyConverter', + 'ch.qos.logback.classic.pattern.ThreadConverter', + 'ch.qos.logback.classic.pattern.LineSeparatorConverter', + 'ch.qos.logback.classic.pattern.MDCConverter', + 'ch.qos.logback.classic.pattern.color.HighlightingCompositeConverter', + 'ch.qos.logback.classic.pattern.ThrowableProxyConverter', + 'ch.qos.logback.classic.pattern.Abbreviator', + 'ch.qos.logback.classic.pattern.Util', + 'ch.qos.logback.classic.pattern.LoggerConverter', + 'ch.qos.logback.classic.pattern.SyslogStartConverter', + 'ch.qos.logback.classic.pattern.MessageConverter', + 'ch.qos.logback.classic.gaffer.GafferUtil', + 'ch.qos.logback.classic.boolex.OnMarkerEvaluator', + 'ch.qos.logback.classic.boolex.JaninoEventEvaluator', + 'ch.qos.logback.classic.boolex.OnErrorEvaluator', + 'ch.qos.logback.classic.boolex.GEventEvaluator', + 'ch.qos.logback.classic.boolex.IEvaluator', + 'ch.qos.logback.classic.filter.ThresholdFilter', + 'ch.qos.logback.classic.filter.LevelFilter', + 'java.lang.System', + 'java.lang.System.getenv', + 'java.lang.System.getProperty', + 'java.lang.System.getenv', + 'java.util.Map.getOrDefault', + 'dev.kordex.core.utils._EnvironmentKt.envOrNull', +] diff --git a/src/main/resources/translations/modmailbot/strings.properties b/src/main/resources/translations/modmailbot/strings.properties new file mode 100644 index 0000000..3614958 --- /dev/null +++ b/src/main/resources/translations/modmailbot/strings.properties @@ -0,0 +1,38 @@ +commands.close.name=close +commands.close.description=Close this thread +commands.close.args.delay.name=delay +commands.close.args.delay.description=How long until you want to close this thread +commands.close.args.silent.name=silent +commands.close.args.silent.description=Whether to close this thread silently +commands.close.args.message.name=message +commands.close.args.message.description=The closing message to send to the user. +commands.close.closedEmbed.title=Modmail thread closed +commands.close.closedEmbed.footer=Replying will create a new thread + +commands.reply.sent=Message sent +commands.reply.reply.name=reply +commands.reply.reply.description=Reply to the user this thread channel is owned by +commands.reply.reply.args.content.name=message +commands.reply.reply.args.content.description=What you'd like to reply with +commands.reply.anonreply.name=anonreply +commands.reply.anonreply.description=Reply anonymously to the user this thread channel is owned by + +events.edit.contentFailed=Failed to get content of message +events.edit.originalContentFailed=Failed to get original message content +events.edit.name=edit +events.edit.description=Edit a message you sent +events.edit.edited=Message edited +events.edit.args.messageId.name=message-id +events.edit.args.messageId.description=The ID of the message you're editing +events.edit.modal.title=Edit message +events.edit.modal.newContents.label=New message contents + +events.receiving.description={0} was created +events.receiving.nickname=Nickname +events.receiving.roles=Roles +events.receiving.footer=User ID: {0} | DM ID: {1} + +utils.messageId=Message ID: {0} +utils.editedMessage.previous=Previous content +utils.editedMessage.new=New content +utils.none=None