diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..09a36aa
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,12 @@
+root = true
+
+[*]
+charset = utf-8
+insert_final_newline = true
+indent_style = tab
+trim_trailing_whitespace = true
+max_line_length = 120
+
+[*.yml]
+indent_style = space
+indent_size = 2
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..a77ad0b
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,7 @@
+# THIS IS A TEMPLATE FILE. MAKE A COPY AND RENAME TO `.env` TO USE.
+
+# https://discord.com/developers
+TOKEN=BOT_TOKEN
+
+# ID of the server to use for testing
+TEST_SERVER=123
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..00a51af
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,6 @@
+#
+# https://help.github.com/articles/dealing-with-line-endings/
+#
+# These are explicitly windows files and should use crlf
+*.bat text eol=crlf
+
diff --git a/.github/boring-cyborg.yml b/.github/boring-cyborg.yml
new file mode 100644
index 0000000..b6071de
--- /dev/null
+++ b/.github/boring-cyborg.yml
@@ -0,0 +1,2 @@
+# Boring Cyborg: https://probot.github.io/apps/boring-cyborg/
+_extends: ".github"
diff --git a/.github/config.yml b/.github/config.yml
new file mode 100644
index 0000000..b3cd5de
--- /dev/null
+++ b/.github/config.yml
@@ -0,0 +1,2 @@
+# to-do: https://probot.github.io/apps/todo/
+_extends: ".github"
diff --git a/.github/settings.yml b/.github/settings.yml
new file mode 100644
index 0000000..862a003
--- /dev/null
+++ b/.github/settings.yml
@@ -0,0 +1,2 @@
+# Settings: https://probot.github.io/apps/settings/
+_extends: ".github"
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..d521601
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,42 @@
+name: Build (CI)
+
+on:
+ push:
+ branches-ignore:
+ - root
+ - develop
+
+ pull_request:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v1
+
+ - name: Set up Java
+ uses: actions/setup-java@v1
+
+ with:
+ java-version: 1.17
+
+ - name: Gradle (Build)
+ uses: gradle/gradle-build-action@v2
+
+ with:
+ arguments: build
+
+ - name: Upload artifacts (Main JAR)
+ uses: actions/upload-artifact@v2
+
+ with:
+ name: Main JAR
+ path: build/libs/*-all.jar
+
+ - name: Upload artifacts (JARs)
+ uses: actions/upload-artifact@v2
+
+ with:
+ name: JARs
+ path: build/libs/*.jar
diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml
new file mode 100644
index 0000000..409f750
--- /dev/null
+++ b/.github/workflows/develop.yml
@@ -0,0 +1,39 @@
+name: Build & Publish
+
+on:
+ push:
+ branches:
+ - develop
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v1
+
+ - name: Set up Java
+ uses: actions/setup-java@v1
+
+ with:
+ java-version: 1.17
+
+ - name: Gradle (Build)
+ uses: gradle/gradle-build-action@v2
+
+ with:
+ arguments: build
+
+ - name: Upload artifacts (Main JAR)
+ uses: actions/upload-artifact@v2
+
+ with:
+ name: Main JAR
+ path: build/libs/*-all.jar
+
+ - name: Upload artifacts (JARs)
+ uses: actions/upload-artifact@v2
+
+ with:
+ name: JARs
+ path: build/libs/*.jar
diff --git a/.github/workflows/root.yml b/.github/workflows/root.yml
new file mode 100644
index 0000000..da08301
--- /dev/null
+++ b/.github/workflows/root.yml
@@ -0,0 +1,40 @@
+name: Build & Publish
+
+on:
+ push:
+ branches:
+ - root
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v1
+
+ - name: Set up Java
+ uses: actions/setup-java@v1
+
+ with:
+ java-version: 1.17
+
+ - name: Gradle (Build)
+ uses: gradle/gradle-build-action@v2
+
+ with:
+ arguments: build
+ dependency-graph: generate-and-submit
+
+ - name: Upload artifacts (Main JAR)
+ uses: actions/upload-artifact@v2
+
+ with:
+ name: Main JAR
+ path: build/libs/*-all.jar
+
+ - name: Upload artifacts (JARs)
+ uses: actions/upload-artifact@v2
+
+ with:
+ name: JARs
+ path: build/libs/*.jar
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e798797
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,112 @@
+# Created by .ignore support plugin (hsz.mobi)
+### Kotlin template
+# Compiled class file
+*.class
+
+# Log file
+*.log
+
+# BlueJ files
+*.ctxt
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.nar
+*.ear
+*.zip
+*.tar.gz
+*.rar
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+### JetBrains template
+# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
+# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
+
+# User-specific stuff
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/**/usage.statistics.xml
+.idea/**/dictionaries
+.idea/**/shelf
+
+# Generated files
+.idea/**/contentModel.xml
+
+# Sensitive or high-churn files
+.idea/**/dataSources/
+.idea/**/dataSources.ids
+.idea/**/dataSources.local.xml
+.idea/**/sqlDataSources.xml
+.idea/**/dynamic.xml
+.idea/**/uiDesigner.xml
+.idea/**/dbnavigator.xml
+
+# Gradle
+.idea/**/gradle.xml
+.idea/**/libraries
+
+# Gradle and Maven with auto-import
+# When using Gradle or Maven with auto-import, you should exclude module files,
+# since they will be recreated, and may cause churn. Uncomment if using
+# auto-import.
+# .idea/artifacts
+# .idea/compiler.xml
+# .idea/jarRepositories.xml
+# .idea/modules.xml
+# .idea/*.iml
+# .idea/modules
+# *.iml
+# *.ipr
+
+# CMake
+cmake-build-*/
+
+# Mongo Explorer plugin
+.idea/**/mongoSettings.xml
+
+# File-based project format
+*.iws
+
+# IntelliJ
+out/
+
+# mpeltonen/sbt-idea plugin
+.idea_modules/
+
+# JIRA plugin
+atlassian-ide-plugin.xml
+
+# Cursive Clojure plugin
+.idea/replstate.xml
+
+# Crashlytics plugin (for Android Studio and IntelliJ)
+com_crashlytics_export_strings.xml
+crashlytics.properties
+crashlytics-build.properties
+fabric.properties
+
+# Editor-based Rest Client
+.idea/httpRequests
+
+# Android studio 3.1+ serialized cache file
+.idea/caches/build_file_checksums.ser
+
+### Custom rules
+
+# Don't ignore Gradle wrapper
+!gradle/**/*
+
+# Local Gradle cache
+.gradle
+
+# Gradle build output folder
+build/
+
+# Env vars - don't commit this!
+.env
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..c074b8e
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Datasource local storage ignored files
+/../../../../../../../:\Users\coles\IdeaProjects\kordex\template\.idea/dataSources/
+/dataSources.local.xml
+# Editor-based HTTP Client requests
+/httpRequests/
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..1533e95
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/discord.xml b/.idea/discord.xml
new file mode 100644
index 0000000..cd711a0
--- /dev/null
+++ b/.idea/discord.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/file.template.settings.xml b/.idea/file.template.settings.xml
new file mode 100644
index 0000000..550b607
--- /dev/null
+++ b/.idea/file.template.settings.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/.idea/fileTemplates/KordEx Extension.kt b/.idea/fileTemplates/KordEx Extension.kt
new file mode 100644
index 0000000..a1b0a11
--- /dev/null
+++ b/.idea/fileTemplates/KordEx Extension.kt
@@ -0,0 +1,14 @@
+#if (${PACKAGE_NAME} && ${PACKAGE_NAME} != "")package ${PACKAGE_NAME}
+
+#end
+import com.kotlindiscord.kord.extensions.extensions.Extension
+
+#set($extName = $NAME.toLowerCase())
+#parse("File Header.java")
+class ${NAME}Extension : Extension() {
+ override val name = "$extName"
+
+ override suspend fun setup() {
+ TODO("Not yet implemented")
+ }
+}
diff --git a/.idea/git_toolbox_prj.xml b/.idea/git_toolbox_prj.xml
new file mode 100644
index 0000000..bcb1d9d
--- /dev/null
+++ b/.idea/git_toolbox_prj.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
new file mode 100644
index 0000000..651f717
--- /dev/null
+++ b/.idea/jarRepositories.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
new file mode 100644
index 0000000..fdf8d99
--- /dev/null
+++ b/.idea/kotlinc.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries-with-intellij-classes.xml b/.idea/libraries-with-intellij-classes.xml
new file mode 100644
index 0000000..9fa3156
--- /dev/null
+++ b/.idea/libraries-with-intellij-classes.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/markdown.xml b/.idea/markdown.xml
new file mode 100644
index 0000000..1e34094
--- /dev/null
+++ b/.idea/markdown.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..b10e46f
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Build.xml b/.idea/runConfigurations/Build.xml
new file mode 100644
index 0000000..1c9f4d2
--- /dev/null
+++ b/.idea/runConfigurations/Build.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Run.xml b/.idea/runConfigurations/Run.xml
new file mode 100644
index 0000000..b76231b
--- /dev/null
+++ b/.idea/runConfigurations/Run.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..fdddb29
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,24 @@
+This is free and unencumbered software released into the public domain.
+
+Anyone is free to copy, modify, publish, use, compile, sell, or
+distribute this software, either in source code form or as a compiled
+binary, for any purpose, commercial or non-commercial, and by any
+means.
+
+In jurisdictions that recognize copyright laws, the author or authors
+of this software dedicate any and all copyright interest in the
+software to the public domain. We make this dedication for the benefit
+of the public at large and to the detriment of our heirs and
+successors. We intend this dedication to be an overt act of
+relinquishment in perpetuity of all present and future rights to this
+software under copyright law.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+For more information, please refer to
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..107d1ca
--- /dev/null
+++ b/README.md
@@ -0,0 +1,54 @@
+# KordEx Bot Template
+
+This repository contains a basic KordEx example bot for you to use as a template for your own KordEx bots. This
+includes the following:
+
+* A basic extension that allows you to slap other people, using both message commands and slash commands.
+* A basic bot configuration that enables slash commands and shows you how to conditionally provide a different
+ message command prefix for different guilds.
+* A Gradle Kotlin build script that uses the Kotlin Discord public maven repo, Detekt for linting (with a
+ fairly strict configuration) and a Git commit hook plugin that runs Detekt when you make a commit - this uses Gradle
+ 7's new version catalog feature, for easy configuration of dependencies.
+* GitHub CI scripts that build the bot and publish its artefacts.
+* A reasonable `.gitignore` file, including one in the `.idea` folder that ignores files that you shouldn't commit -
+ if you're using IDEA yourself, you should install the Ignore plugin to handle changes to this for you.
+* A Groovy-based Logback config, so you've reasonable logging out of the box.
+
+**Note:** This template includes a `.editorconfig` file that defaults to using tabs for indentation in almost all file
+types. This is because tabs are more accessible for the blind, or those with impaired vision. We won't accept
+feedback or PRs targeting this approach.
+
+## Potential Changes
+
+* The `.yml` files in `.github/` are used to configure GitHub apps. If you're not using them, you can remove them.
+* The provided `LICENSE` file contains The Unlicense, which makes this repository public domain. You will probably want
+ to change this - we suggest looking at [Choose a License](https://choosealicense.com/) if you're not sure where to start.
+* In the `build.gradle.kts`:
+ * Set the `group` and `version` properties as appropriate.
+ * If you're not using this to test KordEx builds, you can remove the `mavenLocal()` from the `repositories` block.
+ * In the `application` and `tasks.jar` blocks, update the main class path/name as appropriate.
+ * To target a newer/older Java version, change the options in the `KotlinCompile` configuration and `java` blocks
+* In the `settings.gradle.kts`, update the name of the root project as appropriate.
+* The bundled Detekt config is pretty strict - you can check over `detekt.yml` if you want to change it, but you need to
+ follow the TODOs in that file regardless.
+* The Logback configuration is in `src/main/resources/logback.groovy`. If the logging setup doesn't suit, you can change
+ it there.
+
+## Bundled Bot
+
+* `App.kt` includes a basic bot, which uses environment variables (or variables in a `.env` file) for the testing guild
+ ID (`TEST_SERVER`) and the bot's token (`TOKEN`). You can specify these either directly as environment variables, or
+ as `KEY=value` pairs in a file named `.env`. Some example code is also included that shows one potential way of
+ providing different command prefixes for different servers.
+* `TestExtension.kt` includes an example extension that creates a `slap` command - this command works as both a
+ message command and slash command, and allows you to slap other users with whatever you wish, defaulting to a
+ `large, smelly trout`.
+
+To test the bot, we recommend using a `.env` file that looks like the following:
+
+```dotenv
+TOKEN=abc...
+TEST_SERVER=123...
+```
+
+Create this file, fill it out, and run the `run` gradle task for testing in development.
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 0000000..3a1a000
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,75 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+plugins {
+ application
+
+ kotlin("jvm")
+ kotlin("plugin.serialization")
+
+ id("com.github.johnrengelman.shadow")
+ id("io.gitlab.arturbosch.detekt")
+}
+
+group = "template"
+version = "1.0-SNAPSHOT"
+
+repositories {
+ google()
+ mavenCentral()
+
+ maven {
+ name = "Sonatype Snapshots (Legacy)"
+ url = uri("https://oss.sonatype.org/content/repositories/snapshots")
+ }
+
+ maven {
+ name = "Sonatype Snapshots"
+ url = uri("https://s01.oss.sonatype.org/content/repositories/snapshots")
+ }
+}
+
+dependencies {
+ detektPlugins(libs.detekt)
+
+ implementation(libs.kord.extensions)
+ implementation(libs.kotlin.stdlib)
+ implementation(libs.kx.ser)
+
+ // Logging dependencies
+ implementation(libs.groovy)
+ implementation(libs.jansi)
+ implementation(libs.logback)
+ implementation(libs.logback.groovy)
+ implementation(libs.logging)
+}
+
+application {
+ mainClass.set("template.AppKt")
+}
+
+tasks.withType {
+ // Current LTS version of Java
+ kotlinOptions.jvmTarget = "17"
+
+ kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
+}
+
+tasks.jar {
+ manifest {
+ attributes(
+ "Main-Class" to "template.AppKt"
+ )
+ }
+}
+
+java {
+ // Current LTS version of Java
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
+}
+
+detekt {
+ buildUponDefaultConfig = true
+
+ config.from(rootProject.files("detekt.yml"))
+}
diff --git a/detekt.yml b/detekt.yml
new file mode 100644
index 0000000..e3ddb48
--- /dev/null
+++ b/detekt.yml
@@ -0,0 +1,679 @@
+# TODO: Update `rootPackage` in naming -> InvalidPackageDeclaration
+
+build:
+ maxIssues: 0
+ excludeCorrectable: false
+ weights:
+ # complexity: 2
+ # LongParameterList: 1
+ # style: 1
+ # comments: 1
+
+config:
+ validation: true
+ # when writing own rules with new properties, exclude the property path e.g.: 'my_rule_set,.*>.*>[my_property]'
+ excludes: ''
+
+processors:
+ active: true
+ exclude:
+ - 'DetektProgressListener'
+ - 'FunctionCountProcessor'
+ - 'PropertyCountProcessor'
+ - 'ClassCountProcessor'
+ - 'PackageCountProcessor'
+ - 'KtFileCountProcessor'
+
+console-reports:
+ active: true
+ exclude:
+ - 'ProjectStatisticsReport'
+ - 'NotificationReport'
+ - 'FileBasedFindingsReport'
+
+output-reports:
+ active: true
+ exclude:
+ # - 'HtmlOutputReport'
+ - 'TxtOutputReport'
+ # - 'XmlOutputReport'
+
+comments:
+ active: true
+
+ AbsentOrWrongFileLicense:
+ active: false
+ licenseTemplateFile: 'license.template'
+ CommentOverPrivateFunction:
+ active: false
+ CommentOverPrivateProperty:
+ active: false
+ EndOfSentenceFormat:
+ active: true
+ endOfSentenceFormat: '([.?!][ \t\n\r\f<])|([.?!:]$)'
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ UndocumentedPublicClass:
+ active: false
+ searchInNestedClass: true
+ searchInInnerClass: true
+ searchInInnerObject: true
+ searchInInnerInterface: true
+ UndocumentedPublicFunction:
+ active: false
+ UndocumentedPublicProperty:
+ active: false
+
+complexity:
+ active: true
+ ComplexCondition:
+ active: true
+ threshold: 10
+ ComplexInterface:
+ active: false
+ threshold: 10
+ includeStaticDeclarations: false
+ includePrivateDeclarations: false
+ ComplexMethod:
+ active: false
+ threshold: 15
+ ignoreSingleWhenExpression: false
+ ignoreSimpleWhenEntries: false
+ ignoreNestingFunctions: false
+ nestingFunctions: [ run, let, apply, with, also, use, forEach, isNotNull, ifNull ]
+ LabeledExpression:
+ active: false
+ ignoredLabels: [ ]
+ LargeClass:
+ active: false
+ threshold: 600
+ LongMethod:
+ active: false
+ threshold: 60
+ LongParameterList:
+ active: false
+ functionThreshold: 6
+ constructorThreshold: 7
+ ignoreDefaultParameters: false
+ ignoreDataClasses: true
+ ignoreAnnotated: [ ]
+ MethodOverloading:
+ active: false
+ threshold: 6
+ NestedBlockDepth:
+ active: false
+ threshold: 4
+ ReplaceSafeCallChainWithRun:
+ active: true
+ StringLiteralDuplication:
+ active: true
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ threshold: 3
+ ignoreAnnotation: true
+ excludeStringsWithLessThan5Characters: true
+ ignoreStringsRegex: '$^'
+ TooManyFunctions:
+ active: false
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ thresholdInFiles: 11
+ thresholdInClasses: 11
+ thresholdInInterfaces: 11
+ thresholdInObjects: 11
+ thresholdInEnums: 11
+ ignoreDeprecated: false
+ ignorePrivate: false
+ ignoreOverridden: false
+
+coroutines:
+ active: true
+ GlobalCoroutineUsage:
+ active: true
+ RedundantSuspendModifier:
+ active: true
+ SuspendFunWithFlowReturnType:
+ active: true
+
+empty-blocks:
+ active: true
+ EmptyCatchBlock:
+ active: true
+ allowedExceptionNameRegex: '^(_|(ignore|expected).*)'
+ EmptyClassBlock:
+ active: true
+ EmptyDefaultConstructor:
+ active: true
+ EmptyDoWhileBlock:
+ active: true
+ EmptyElseBlock:
+ active: true
+ EmptyFinallyBlock:
+ active: true
+ EmptyForBlock:
+ active: true
+ EmptyFunctionBlock:
+ active: true
+ ignoreOverridden: false
+ EmptyIfBlock:
+ active: true
+ EmptyInitBlock:
+ active: true
+ EmptyKtFile:
+ active: true
+ EmptySecondaryConstructor:
+ active: true
+ EmptyTryBlock:
+ active: true
+ EmptyWhenBlock:
+ active: true
+ EmptyWhileBlock:
+ active: true
+
+exceptions:
+ active: true
+ ExceptionRaisedInUnexpectedLocation:
+ active: true
+ methodNames: [ toString, hashCode, equals, finalize ]
+ InstanceOfCheckForException:
+ active: true
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ NotImplementedDeclaration:
+ active: false
+ PrintStackTrace:
+ active: true
+ RethrowCaughtException:
+ active: true
+ ReturnFromFinally:
+ active: true
+ ignoreLabeled: false
+ SwallowedException:
+ active: false
+ ignoredExceptionTypes:
+ - InterruptedException
+ - NumberFormatException
+ - ParseException
+ - MalformedURLException
+ allowedExceptionNameRegex: '^(_|(ignore|expected).*)'
+ ThrowingExceptionFromFinally:
+ active: true
+ ThrowingExceptionInMain:
+ active: true
+ ThrowingExceptionsWithoutMessageOrCause:
+ active: true
+ exceptions:
+ - IllegalArgumentException
+ - IllegalStateException
+ - IOException
+ ThrowingNewInstanceOfSameException:
+ active: true
+ TooGenericExceptionCaught:
+ active: true
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ exceptionNames:
+ - ArrayIndexOutOfBoundsException
+ - Error
+ - Exception
+ - IllegalMonitorStateException
+ - NullPointerException
+ - IndexOutOfBoundsException
+ - RuntimeException
+ - Throwable
+ allowedExceptionNameRegex: '^(_|(ignore|expected).*)'
+ TooGenericExceptionThrown:
+ active: true
+ exceptionNames:
+ - Error
+ - Exception
+ - Throwable
+ - RuntimeException
+
+formatting:
+ active: true
+ android: false
+ autoCorrect: true
+ AnnotationOnSeparateLine:
+ active: true
+ autoCorrect: true
+ AnnotationSpacing:
+ active: true
+ autoCorrect: true
+ ArgumentListWrapping:
+ active: false # It's wrong!
+ autoCorrect: true
+ ChainWrapping:
+ active: true
+ autoCorrect: true
+ CommentSpacing:
+ active: true
+ autoCorrect: true
+ EnumEntryNameCase:
+ active: true
+ autoCorrect: true
+ Filename:
+ active: true
+ FinalNewline:
+ active: true
+ autoCorrect: true
+ insertFinalNewLine: true
+ ImportOrdering:
+ active: true
+ autoCorrect: true
+ layout: "*,java.**,javax.**,kotlin.**,^"
+ Indentation:
+ active: false
+ autoCorrect: false
+ indentSize: 4
+ continuationIndentSize: 4
+ MaximumLineLength:
+ active: true
+ maxLineLength: 120
+ ModifierOrdering:
+ active: true
+ autoCorrect: true
+ MultiLineIfElse:
+ active: true
+ autoCorrect: true
+ NoBlankLineBeforeRbrace:
+ active: true
+ autoCorrect: true
+ NoConsecutiveBlankLines:
+ active: true
+ autoCorrect: true
+ NoEmptyClassBody:
+ active: true
+ autoCorrect: true
+ NoEmptyFirstLineInMethodBlock:
+ active: true
+ autoCorrect: true
+ NoLineBreakAfterElse:
+ active: true
+ autoCorrect: true
+ NoLineBreakBeforeAssignment:
+ active: true
+ autoCorrect: true
+ NoMultipleSpaces:
+ active: false
+ autoCorrect: false
+ NoSemicolons:
+ active: true
+ autoCorrect: true
+ NoTrailingSpaces:
+ active: true
+ autoCorrect: true
+ NoUnitReturn:
+ active: true
+ autoCorrect: true
+ NoUnusedImports:
+ active: true
+ autoCorrect: true
+ NoWildcardImports:
+ active: false
+ PackageName:
+ active: true
+ autoCorrect: true
+ ParameterListWrapping:
+ active: true
+ autoCorrect: true
+ indentSize: 4
+ SpacingAroundColon:
+ active: true
+ autoCorrect: true
+ SpacingAroundComma:
+ active: true
+ autoCorrect: true
+ SpacingAroundCurly:
+ active: true
+ autoCorrect: true
+ SpacingAroundDot:
+ active: true
+ autoCorrect: true
+ SpacingAroundDoubleColon:
+ active: true
+ autoCorrect: true
+ SpacingAroundKeyword:
+ active: true
+ autoCorrect: true
+ SpacingAroundOperators:
+ active: true
+ autoCorrect: true
+ SpacingAroundParens:
+ active: true
+ autoCorrect: true
+ SpacingAroundRangeOperator:
+ active: true
+ autoCorrect: true
+ SpacingBetweenDeclarationsWithAnnotations:
+ active: true
+ autoCorrect: true
+ SpacingBetweenDeclarationsWithComments:
+ active: true
+ autoCorrect: true
+ StringTemplate:
+ active: true
+ autoCorrect: true
+
+naming:
+ active: true
+ ClassNaming:
+ active: true
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ classPattern: '[A-Z$][a-zA-Z0-9$]*'
+ ConstructorParameterNaming:
+ active: true
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ parameterPattern: '[a-z][A-Za-z0-9]*'
+ privateParameterPattern: '[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+ EnumNaming:
+ active: true
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*'
+ ForbiddenClassName:
+ active: false
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ forbiddenName: [ ]
+ FunctionMaxLength:
+ active: false
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ maximumFunctionNameLength: 30
+ FunctionMinLength:
+ active: false
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ minimumFunctionNameLength: 3
+ FunctionNaming:
+ active: true
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+ FunctionParameterNaming:
+ active: true
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ parameterPattern: '[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+
+ InvalidPackageDeclaration:
+ active: true
+ # TODO: Update this with your project's base package
+ rootPackage: 'template'
+
+ MatchingDeclarationName:
+ active: true
+ mustBeFirst: true
+ MemberNameEqualsClassName:
+ active: true
+ ignoreOverridden: true
+ NonBooleanPropertyPrefixedWithIs:
+ active: true
+ ObjectPropertyNaming:
+ active: true
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ constantPattern: '[A-Za-z][_A-Za-z0-9]*'
+ propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
+ privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
+ PackageNaming:
+ active: true
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ packagePattern: '^[a-z]+(\.[a-z][A-Za-z0-9]*)*$'
+ TopLevelPropertyNaming:
+ active: true
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ constantPattern: '[A-Z][_A-Z0-9]*'
+ propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
+ privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
+ VariableMaxLength:
+ active: false
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ maximumVariableNameLength: 64
+ VariableMinLength:
+ active: false
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ minimumVariableNameLength: 1
+ VariableNaming:
+ active: true
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ variablePattern: '[a-z][A-Za-z0-9]*'
+ privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
+ excludeClassPattern: '$^'
+ ignoreOverridden: true
+
+performance:
+ active: true
+ ArrayPrimitive:
+ active: true
+ ForEachOnRange:
+ active: true
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ SpreadOperator:
+ active: true
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ UnnecessaryTemporaryInstantiation:
+ active: true
+
+potential-bugs:
+ active: true
+ Deprecation:
+ active: true
+ DuplicateCaseInWhenExpression:
+ active: true
+ EqualsAlwaysReturnsTrueOrFalse:
+ active: true
+ EqualsWithHashCodeExist:
+ active: true
+ ExplicitGarbageCollectionCall:
+ active: true
+ HasPlatformType:
+ active: true
+ IgnoredReturnValue:
+ active: true
+ ImplicitDefaultLocale:
+ active: false
+ ImplicitUnitReturnType:
+ active: true
+ allowExplicitReturnType: true
+ InvalidRange:
+ active: true
+ IteratorHasNextCallsNextMethod:
+ active: true
+ IteratorNotThrowingNoSuchElementException:
+ active: true
+ LateinitUsage:
+ active: false
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ excludeAnnotatedProperties: [ ]
+ ignoreOnClassesPattern: ''
+ MapGetWithNotNullAssertionOperator:
+ active: true
+ MissingWhenCase:
+ active: true
+ NullableToStringCall:
+ active: true
+ RedundantElseInWhen:
+ active: true
+ UnconditionalJumpStatementInLoop:
+ active: true
+ UnnecessaryNotNullOperator:
+ active: true
+ UnnecessarySafeCall:
+ active: true
+ UnreachableCode:
+ active: true
+ UnsafeCallOnNullableType:
+ active: true
+ UnsafeCast:
+ active: true
+ UselessPostfixExpression:
+ active: true
+ WrongEqualsTypeParameter:
+ active: true
+
+style:
+ active: true
+ ClassOrdering:
+ active: true
+ CollapsibleIfStatements:
+ active: true
+ DataClassContainsFunctions:
+ active: true
+ conversionFunctionPrefix: 'to'
+ DataClassShouldBeImmutable:
+ active: true
+ EqualsNullCall:
+ active: true
+ EqualsOnSignatureLine:
+ active: true
+ ExplicitCollectionElementAccessMethod:
+ active: true
+ ExplicitItLambdaParameter:
+ active: true
+ ExpressionBodySyntax:
+ active: true
+ includeLineWrapping: false
+ ForbiddenComment:
+ active: false
+ values: [ 'TODO:', 'FIXME:', 'STOPSHIP:' ]
+ allowedPatterns: ''
+ ForbiddenImport:
+ active: false
+ imports: [ ]
+ forbiddenPatterns: ''
+ ForbiddenMethodCall:
+ active: false
+ methods: [ ]
+ ForbiddenPublicDataClass:
+ active: false
+ ignorePackages: [ '*.internal', '*.internal.*' ]
+ ForbiddenVoid:
+ active: true
+ ignoreOverridden: true
+ ignoreUsageInGenerics: false
+ FunctionOnlyReturningConstant:
+ active: true
+ ignoreOverridableFunction: true
+ excludedFunctions: 'describeContents'
+ excludeAnnotatedFunction: [ 'dagger.Provides' ]
+ LibraryCodeMustSpecifyReturnType:
+ active: true
+ LibraryEntitiesShouldNotBePublic:
+ active: true
+ LoopWithTooManyJumpStatements:
+ active: true
+ maxJumpCount: 3
+ MagicNumber:
+ active: true
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ ignoreNumbers: [ '-1', '0', '1', '2' ]
+ ignoreHashCodeFunction: true
+ ignorePropertyDeclaration: false
+ ignoreLocalVariableDeclaration: false
+ ignoreConstantDeclaration: true
+ ignoreCompanionObjectPropertyDeclaration: true
+ ignoreAnnotation: true
+ ignoreNamedArgument: true
+ ignoreEnums: true
+ ignoreRanges: false
+ MandatoryBracesIfStatements:
+ active: true
+ MandatoryBracesLoops:
+ active: true
+ MaxLineLength:
+ active: true
+ maxLineLength: 120
+ excludePackageStatements: true
+ excludeImportStatements: true
+ excludeCommentStatements: false
+ MayBeConst:
+ active: true
+ ModifierOrder:
+ active: true
+ NestedClassesVisibility:
+ active: true
+ NewLineAtEndOfFile:
+ active: true
+ NoTabs:
+ active: false
+ OptionalAbstractKeyword:
+ active: true
+ OptionalUnit:
+ active: false
+ OptionalWhenBraces:
+ active: true
+ PreferToOverPairSyntax:
+ active: true
+ ProtectedMemberInFinalClass:
+ active: true
+ RedundantExplicitType:
+ active: true
+ RedundantVisibilityModifierRule:
+ active: false
+ ReturnCount:
+ active: false
+ max: 2
+ excludedFunctions: 'equals'
+ excludeLabeled: false
+ excludeReturnFromLambda: true
+ excludeGuardClauses: false
+ SafeCast:
+ active: true
+ SerialVersionUIDInSerializableClass:
+ active: true
+ SpacingBetweenPackageAndImports:
+ active: true
+ ThrowsCount:
+ active: false
+ max: 2
+ TrailingWhitespace:
+ active: true
+ UnderscoresInNumericLiterals:
+ active: true
+ acceptableDecimalLength: 5
+ UnnecessaryAbstractClass:
+ active: true
+ excludeAnnotatedClasses: [ 'dagger.Module' ]
+ UnnecessaryAnnotationUseSiteTarget:
+ active: true
+ UnnecessaryApply:
+ active: true
+ UnnecessaryInheritance:
+ active: true
+ UnnecessaryLet:
+ active: true
+ UnnecessaryParentheses:
+ active: true
+ UntilInsteadOfRangeTo:
+ active: true
+ UnusedImports:
+ active: true
+ UnusedPrivateClass:
+ active: true
+ UnusedPrivateMember:
+ active: true
+ allowedNames: '(_|ignored|expected|serialVersionUID)'
+ UseArrayLiteralsInAnnotations:
+ active: true
+ UseCheckNotNull:
+ active: true
+ UseCheckOrError:
+ active: true
+ UseDataClass:
+ active: true
+ excludeAnnotatedClasses: [ ]
+ allowVars: false
+ UseEmptyCounterpart:
+ active: true
+ UseIfInsteadOfWhen:
+ active: true
+ UseRequire:
+ active: true
+ UseRequireNotNull:
+ active: true
+ UselessCallOnNotNull:
+ active: true
+ UtilityClassWithPublicConstructor:
+ active: true
+ VarCouldBeVal:
+ active: true
+ WildcardImport:
+ active: false
+ excludes: [ '**/test/**', '**/androidTest/**', '**/*.Test.kt', '**/*.Spec.kt', '**/*.Spek.kt' ]
+ excludeImports: [ 'java.util.*', 'kotlinx.android.synthetic.*' ]
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..ee99404
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,4 @@
+# Gradle props
+org.gradle.jvmargs=-XX:MaxMetaspaceSize=1536m
+org.gradle.parallel=true
+kotlin.incremental=true
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 0000000..e565e09
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,22 @@
+[versions]
+detekt = "1.23.1" # Note: Plugin versions must be updated in the settings.gradle.kts too
+kotlin = "1.9.0" # Note: Plugin versions must be updated in the settings.gradle.kts too
+
+groovy = "3.0.14"
+jansi = "2.4.0"
+kord-extensions = "1.5.9-SNAPSHOT"
+kx-ser = "1.5.1"
+logging = "3.0.5"
+logback = "1.4.5"
+logback-groovy = "1.14.4"
+
+[libraries]
+detekt = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" }
+groovy = { module = "org.codehaus.groovy:groovy", version.ref = "groovy" }
+jansi = { module = "org.fusesource.jansi:jansi", version.ref = "jansi" }
+kord-extensions = { module = "com.kotlindiscord.kord.extensions:kord-extensions", version.ref = "kord-extensions" }
+kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8" }
+kx-ser = { module = "org.jetbrains.kotlinx:kotlinx-serialization-core", version.ref = "kx-ser" }
+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.microutils:kotlin-logging", version.ref = "logging" }
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..e708b1c
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..9b0a13f
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.2.1-all.zip
+networkTimeout=10000
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..4f906e0
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,185 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=`expr $i + 1`
+ done
+ case $i in
+ 0) set -- ;;
+ 1) set -- "$args0" ;;
+ 2) set -- "$args0" "$args1" ;;
+ 3) set -- "$args0" "$args1" "$args2" ;;
+ 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..107acd3
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,89 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 0000000..a7bdf2b
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,15 @@
+pluginManagement {
+ plugins {
+ // Update this in libs.version.toml when you change it here.
+ kotlin("jvm") version "1.9.0"
+ kotlin("plugin.serialization") version "1.9.0"
+
+ // Update this in libs.version.toml when you change it here.
+ id("io.gitlab.arturbosch.detekt") version "1.23.1"
+
+ id("com.github.jakemarsden.git-hooks") version "0.0.1"
+ id("com.github.johnrengelman.shadow") version "8.1.1"
+ }
+}
+
+rootProject.name = "template"
diff --git a/src/main/kotlin/template/App.kt b/src/main/kotlin/template/App.kt
new file mode 100644
index 0000000..6f10daf
--- /dev/null
+++ b/src/main/kotlin/template/App.kt
@@ -0,0 +1,40 @@
+/*
+ * This Kotlin source file was generated by the Gradle 'init' task.
+ */
+package template
+
+import com.kotlindiscord.kord.extensions.ExtensibleBot
+import com.kotlindiscord.kord.extensions.utils.env
+import dev.kord.common.entity.Snowflake
+import template.extensions.TestExtension
+
+val TEST_SERVER_ID = Snowflake(
+ env("TEST_SERVER").toLong() // Get the test server ID from the env vars or a .env file
+)
+
+private val TOKEN = env("TOKEN") // Get the bot' token from the env vars or a .env file
+
+suspend fun main() {
+ val bot = ExtensibleBot(TOKEN) {
+ chatCommands {
+ defaultPrefix = "?"
+ enabled = true
+
+ prefix { default ->
+ if (guildId == TEST_SERVER_ID) {
+ // For the test server, we use ! as the command prefix
+ "!"
+ } else {
+ // For other servers, we use the configured default prefix
+ default
+ }
+ }
+ }
+
+ extensions {
+ add(::TestExtension)
+ }
+ }
+
+ bot.start()
+}
diff --git a/src/main/kotlin/template/extensions/TestExtension.kt b/src/main/kotlin/template/extensions/TestExtension.kt
new file mode 100644
index 0000000..2561d8a
--- /dev/null
+++ b/src/main/kotlin/template/extensions/TestExtension.kt
@@ -0,0 +1,85 @@
+package template.extensions
+
+import com.kotlindiscord.kord.extensions.commands.Arguments
+import com.kotlindiscord.kord.extensions.commands.converters.impl.coalescingDefaultingString
+import com.kotlindiscord.kord.extensions.commands.converters.impl.defaultingString
+import com.kotlindiscord.kord.extensions.commands.converters.impl.user
+import com.kotlindiscord.kord.extensions.extensions.Extension
+import com.kotlindiscord.kord.extensions.extensions.chatCommand
+import com.kotlindiscord.kord.extensions.extensions.publicSlashCommand
+import com.kotlindiscord.kord.extensions.types.respond
+import com.kotlindiscord.kord.extensions.utils.respond
+import template.TEST_SERVER_ID
+
+class TestExtension : Extension() {
+ override val name = "test"
+
+ override suspend fun setup() {
+ chatCommand(::SlapArgs) {
+ name = "slap"
+ description = "Ask the bot to slap another user"
+
+ check { failIf(event.message.author == null) }
+
+ action {
+ // Don't slap ourselves on request, slap the requester!
+ val realTarget = if (arguments.target.id == event.kord.selfId) {
+ message.author!!
+ } else {
+ arguments.target
+ }
+
+ message.respond("*slaps ${realTarget.mention} with their ${arguments.weapon}*")
+ }
+ }
+
+ publicSlashCommand(::SlapSlashArgs) {
+ name = "slap"
+ description = "Ask the bot to slap another user"
+
+ guild(TEST_SERVER_ID) // Otherwise it will take up to an hour to update
+
+ action {
+ // Don't slap ourselves on request, slap the requester!
+ val realTarget = if (arguments.target.id == event.kord.selfId) {
+ member
+ } else {
+ arguments.target
+ }
+
+ respond {
+ content = "*slaps ${realTarget?.mention} with their ${arguments.weapon}*"
+ }
+ }
+ }
+ }
+
+ inner class SlapArgs : Arguments() {
+ val target by user {
+ name = "target"
+ description = "Person you want to slap"
+ }
+
+ val weapon by coalescingDefaultingString {
+ name = "weapon"
+
+ defaultValue = "large, smelly trout"
+ description = "What you want to slap with"
+ }
+ }
+
+ inner class SlapSlashArgs : Arguments() {
+ val target by user {
+ name = "target"
+ description = "Person you want to slap"
+ }
+
+ // Slash commands don't support coalescing strings
+ val weapon by defaultingString {
+ name = "weapon"
+
+ defaultValue = "large, smelly trout"
+ description = "What you want to slap with"
+ }
+ }
+}
diff --git a/src/main/resources/logback.groovy b/src/main/resources/logback.groovy
new file mode 100644
index 0000000..5f5bce9
--- /dev/null
+++ b/src/main/resources/logback.groovy
@@ -0,0 +1,27 @@
+import ch.qos.logback.core.joran.spi.ConsoleTarget
+import ch.qos.logback.core.ConsoleAppender
+
+def environment = System.getenv("ENVIRONMENT") ?: "production"
+
+def defaultLevel = INFO
+def defaultTarget = ConsoleTarget.SystemErr
+
+if (environment == "dev") {
+ defaultLevel = DEBUG
+ defaultTarget = ConsoleTarget.SystemOut
+
+ // Silence warning about missing native PRNG
+ logger("io.ktor.util.random", ERROR)
+}
+
+appender("CONSOLE", ConsoleAppender) {
+ encoder(PatternLayoutEncoder) {
+ pattern = "%boldGreen(%d{yyyy-MM-dd}) %boldYellow(%d{HH:mm:ss}) %gray(|) %highlight(%5level) %gray(|) %boldMagenta(%40.40logger{40}) %gray(|) %msg%n"
+
+ withJansi = true
+ }
+
+ target = defaultTarget
+}
+
+root(defaultLevel, ["CONSOLE"])
diff --git a/src/main/resources/logbackCompiler.groovy b/src/main/resources/logbackCompiler.groovy
new file mode 100644
index 0000000..9eeb39c
--- /dev/null
+++ b/src/main/resources/logbackCompiler.groovy
@@ -0,0 +1,452 @@
+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',
+ 'com.kotlindiscord.kord.extensions.utils._EnvironmentKt.envOrNull',
+]