diff --git a/.editorconfig b/.editorconfig index e38ba784..b50596b0 100644 --- a/.editorconfig +++ b/.editorconfig @@ -7,6 +7,15 @@ indent_style = tab trim_trailing_whitespace = true max_line_length = 120 +quote_type = double +ij_html_quote_style = double +ij_javascript_force_quote_style = true +ij_typescript_force_quote_style = true + [{*.yml,*.yaml}] indent_style = space indent_size = 2 + +[{*.md,*.mdx}] +indent_style = space +indent_size = 2 diff --git a/.github/set-version.main.kts b/.github/set-version.main.kts index c2bb3f02..79753048 100644 --- a/.github/set-version.main.kts +++ b/.github/set-version.main.kts @@ -12,10 +12,13 @@ import javax.xml.xpath.XPathConstants import javax.xml.xpath.XPathFactory import kotlin.io.path.Path +// TODO: Switch this to something Docusaurus can handle, or do it client-side? + val client = OkHttpClient() val gson = Gson() val PLUGIN_URL = "https://plugins.gradle.org/m2/dev/kordex/gradle/plugins/kordex/maven-metadata.xml" +val I18N_URL = "https://plugins.gradle.org/m2/dev/kordex/gradle/plugins/i18n/maven-metadata.xml" val RELEASES_URL = "https://releases-repo.kordex.dev/dev/kordex/kord-extensions/maven-metadata.xml" val SNAPSHOTS_URL = "https://snapshots-repo.kordex.dev/dev/kordex/kord-extensions/maven-metadata.xml" @@ -72,12 +75,13 @@ if (latestSnapshot == null && latestRelease == null) { val latest = if (latestSnapshot == null) { latestRelease!! } else if (latestRelease == null) { - latestSnapshot!! + latestSnapshot } else { - maxOf(latestSnapshot!!, latestRelease!!) + maxOf(latestSnapshot, latestRelease) } val latestPlugin = getLatest(PLUGIN_URL)?.let { Version.parse(it) } +val latesti18n = getLatest(I18N_URL)?.let { Version.parse(it) } println("Latest snapshot version: $latestSnapshot") println("Latest release version: $latestRelease") @@ -105,6 +109,7 @@ if ("-SNAPSHOT" in latest.toString()) { } println("Updating Gradle plugins version in Writerside/v.list to $latestPlugin") +println("Updating i18n version in Writerside/v.list to $latesti18n") println("Updating KordEx version in Writerside/v.list to $latest") println("Updating Java version in Writerside/v.list to $javaVersion") @@ -117,6 +122,7 @@ val fileContents = file.readText() file.writeText( fileContents.replace("{VERSION}", latest.toString()) .replace("{JAVA_VERSION}", javaVersion) + .replace("{I18N_VERSION}", latesti18n.toString()) .replace("{PLUGIN_VERSION}", latestPlugin.toString()) ) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7fb3eb18..b265ecb4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -9,12 +9,6 @@ on: pull_request: merge_group: -env: - ALGOLIA_ARTIFACT: "algolia-indexes-KORDEX.zip" - ARTIFACT: "webHelpKORDEX2-all.zip" - DOCKER_VERSION: "241.18775" - INSTANCE: "Writerside/kordex" - jobs: build: runs-on: ubuntu-latest @@ -22,31 +16,41 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Kotlin - uses: fwilhe2/setup-kotlin@main - - - name: Calculate latest KordEx version - run: kotlin .github/set-version.main.kts +# TODO: Figure out what to do here +# - name: Set up Kotlin +# uses: fwilhe2/setup-kotlin@main +# +# - name: Calculate latest KordEx version +# run: kotlin .github/set-version.main.kts - - uses: JetBrains/writerside-github-action@v4 + - name: Set up pnpm + uses: pnpm/action-setup@v4 with: - instance: ${{ env.INSTANCE }} - artifact: ${{ env.ARTIFACT }} - docker-version: ${{ env.DOCKER_VERSION }} + run_install: false - - uses: actions/upload-artifact@v4 + - name: Set up Node + uses: actions/setup-node@v4 with: - name: artifact - path: artifacts/${{ env.ARTIFACT }} + node-version-file: "package.json" + cache: "pnpm" + cache-dependency-path: "pnpm-lock.yaml" - retention-days: 7 + - name: Install Dependencies + run: pnpm i - - uses: actions/upload-artifact@v4 + - name: Build Site + run: pnpm run build + +# TODO: Cloudflare like the site repo + - name: Upload Build Site + uses: actions/upload-artifact@v4 with: - name: algolia-indexes - path: artifacts/${{ env.ALGOLIA_ARTIFACT }} + name: docs + + path: | + build/ retention-days: 7 diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index fd8b3777..c5933e3c 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -7,12 +7,6 @@ on: workflow_dispatch: -env: - ALGOLIA_ARTIFACT: "algolia-indexes-KORDEX.zip" - ARTIFACT: "webHelpKORDEX2-all.zip" - DOCKER_VERSION: "241.18775" - INSTANCE: "Writerside/kordex" - permissions: contents: write id-token: write @@ -24,56 +18,47 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Kotlin - uses: fwilhe2/setup-kotlin@main +# TODO: Figure out what to do here +# - name: Set up Kotlin +# uses: fwilhe2/setup-kotlin@main +# +# - name: Calculate latest KordEx version +# run: kotlin .github/set-version.main.kts - - name: Calculate latest KordEx version - run: kotlin .github/set-version.main.kts - - - uses: JetBrains/writerside-github-action@v4 + - name: Set up pnpm + uses: pnpm/action-setup@v4 with: - instance: ${{ env.INSTANCE }} - artifact: ${{ env.ARTIFACT }} - docker-version: ${{ env.DOCKER_VERSION }} + run_install: false - - uses: actions/upload-artifact@v4 + - name: Set up Node + uses: actions/setup-node@v4 with: - name: docs + node-version-file: "package.json" + cache: "pnpm" + cache-dependency-path: "pnpm-lock.yaml" - path: | - artifacts/${{ env.ARTIFACT }} - artifacts/report.json + - name: Install Dependencies + run: pnpm i - retention-days: 7 + - name: Build Site + run: pnpm run build - - uses: actions/upload-artifact@v4 - - with: - name: algolia-indexes - path: artifacts/${{ env.ALGOLIA_ARTIFACT }} - - retention-days: 7 - test: - needs: build - runs-on: ubuntu-latest - - steps: - - uses: actions/download-artifact@v4 + - name: Upload Build Site + uses: actions/upload-artifact@v4 with: name: docs - path: artifacts - - uses: JetBrains/writerside-checker-action@v1 + path: | + build/ - with: - instance: ${{ env.INSTANCE }} + retention-days: 7 deploy: # Requires the build job results - needs: test + needs: build runs-on: ubuntu-latest steps: @@ -99,41 +84,22 @@ jobs: with: args: unzip -qq ${{ env.ARTIFACT }} -d docs + - name: Calculate latest KordEx version + run: kotlin .github/set-version.main.kts + - name: Copy files, commit and push +# TODO: Cloudflare like the site repo run: | git config --global user.name "GitHub Actions" git config --global user.email "actions-user@users.noreply.github.com" + sudo rm -rf .docusaurus .github .idea about bots build i18n node_modules src static .editorconfig .gitignore ./*.* + cp -r docs/* . - sudo rm -rf docs Writerside .github .idea .editorconfig .gitignore README.md + sudo rm -rf docs/ git add . git commit -am "Automatic deployment" git push --force origin deployment - - publish-indexes: - needs: [build, test, deploy] - runs-on: ubuntu-latest - - container: - image: registry.jetbrains.team/p/writerside/builder/algolia-publisher:2.0.32-3 - - steps: - - name: Download artifact - uses: actions/download-artifact@v4 - with: - name: algolia-indexes - - - name: Unzip artifact - run: | - unzip -O UTF-8 -qq '${{ env.ALGOLIA_ARTIFACT }}' -d algolia-indexes - env algolia-key='${{ secrets.ALGOLIA_ADMIN_KEY }}' java -jar /opt/builder/help-publication-agent.jar \ - update-index \ - --application-name '00TQKB9RBW' \ - --index-name 'KordEx Docs' \ - --product 'kordex' \ - --version 'root' \ - --index-directory algolia-indexes/ \ - 2>&1 | tee algolia-update-index-log.txt diff --git a/.gitignore b/.gitignore index ac3895d8..bf82e854 100644 --- a/.gitignore +++ b/.gitignore @@ -126,4 +126,7 @@ docs/site style.css style.css.map +/.docusaurus +/build /image +node_modules/ diff --git a/.idea/docs.iml b/.idea/docs.iml index 918bfe56..5b0824b4 100644 --- a/.idea/docs.iml +++ b/.idea/docs.iml @@ -1,7 +1,11 @@ - + + + + + diff --git a/.idea/misc.xml b/.idea/misc.xml index 030f9a4e..9f5a19bf 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/README.md b/README.md index aef126fb..9a8c2d66 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Kord Extensions Documentation This repository contains the source files for the Kord Extensions project documentation. -It is being built using [JetBrains Writerside](https://lp.jetbrains.com/writerside/). +It is being built using [Docusaurus](https://docusaurus.io/). If you'd like to contribute, feel free to submit a pull request. You may open an issue if you have any questions. diff --git a/Writerside/c.list b/Writerside/c.list deleted file mode 100644 index 6c48274e..00000000 --- a/Writerside/c.list +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - diff --git a/Writerside/cfg/buildprofiles.xml b/Writerside/cfg/buildprofiles.xml deleted file mode 100644 index 27a43d92..00000000 --- a/Writerside/cfg/buildprofiles.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - 77906f3f11b79df631efbdc8d382ffc5 - 00TQKB9RBW - KordEx Docs - - contrast - custom.css - logo.png - - #5865F2 - #5865F2 - - https://github.com/Kord-Extensions/kord-extensions - GitHub - logo.png - false - true - true - - - - - -
- ⚖️ CC0 - - Discord - GitHub - Ko-Fi -
-
diff --git a/Writerside/cfg/static/custom.css b/Writerside/cfg/static/custom.css deleted file mode 100644 index 49da1395..00000000 --- a/Writerside/cfg/static/custom.css +++ /dev/null @@ -1,54 +0,0 @@ -.table__th { - font-weight: 610; -} - -tr[id*=":div:bottom"] { - td, th { - border-bottom: 4px double var(--wh-color-border-default) !important; - } -} - -tr[id*=":div:top"] { - td, th { - border-top: 4px double var(--wh-color-border-default) !important; - } -} - -tr[id*=":header"] { - td, th { - background-color: var(--wh-color-backlight-secondary) !important; - font-weight: 610; - } -} - -tr[id*=":text:center"] { - td, th { - text-align: center; - - p { - display: inline; - } - } -} - -.toc li { - &[data-toc-scroll*=":div:bottom"] { - border-bottom: 1px solid var(--wh-color-border-default) !important; - margin-bottom: 22px; - padding-bottom: 22px; - } - - &[data-toc-scroll*=":div:top"] { - border-top: 1px solid var(--wh-color-border-default) !important; - margin-top: 22px; - padding-top: 22px; - } -} - -li.list__item:not(:first-child) { - margin-top: 0.5em; -} - -.list > li:before { - font-weight: 900; -} diff --git a/Writerside/images/logo.svg b/Writerside/images/logo.svg deleted file mode 100644 index d74b711b..00000000 --- a/Writerside/images/logo.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/Writerside/kordex.tree b/Writerside/kordex.tree deleted file mode 100644 index 2525d4e2..00000000 --- a/Writerside/kordex.tree +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Writerside/labels.list b/Writerside/labels.list deleted file mode 100644 index 28fcef98..00000000 --- a/Writerside/labels.list +++ /dev/null @@ -1,24 +0,0 @@ - - - - - New in Kord Extensions 2 - - - - New in Kord Extensions v2.3.0 - - - - New in v1.1.0 - - - - New in v1.2.0 - - - - New in v1.5.0 - - diff --git a/Writerside/redirection-rules.xml b/Writerside/redirection-rules.xml deleted file mode 100644 index 1d713093..00000000 --- a/Writerside/redirection-rules.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - diff --git a/Writerside/topics/About.topic b/Writerside/topics/About.topic deleted file mode 100644 index 8b200395..00000000 --- a/Writerside/topics/About.topic +++ /dev/null @@ -1,207 +0,0 @@ - - - - - - About the Project - - - -

- Latest version: %version% -

- -

- Maven Repo: https://snapshots-repo.kordex.dev -

- -

- Coordinate: - dev.kordex:kord-extensions -

-
- -

- Kord Extensions is a Discord bot framework built on top of - the excellent Kord library. - It provides a framework for Discord bots of all sizes, with easy-to-use commands, rich argument parsing, and - many more useful features. -

- -

- Kord Extensions takes a somewhat different approach when compared to other Kotlin-based Discord bot frameworks, - with inspiration taken from - PyCord's Cogs feature instead of a - light DSL meant for prototyping or implementing small bots. - However, regardless of this inspiration, Kord Extensions still provides an idiomatic API that makes full use of - Kotlin's niceties. -

- - - Why not other Kord-based frameworks? - -

- We built Kord Extensions from the ground up, prioritising the developer experience above all else, and we - believe it speaks for itself. - However, not every framework is right for every bot, so we've created a - - Framework Comparison Matrix - . - This matrix includes all the major Kord-based frameworks, so be sure to inform yourself about them before - deciding on a specific framework. -

-
- - - - -

- The recently released Kord Extensions 2 includes quite a few changes, which we've detailed below. -

- - -
  • -

    - The root package for all projects is no longer com.kotlindiscord.kord.extensions. - It has been changed to dev.kordex. -

    - - -
  • Core package: dev.kordex.core
  • -
  • Modules package: dev.kordex.modules
  • -
  • Tokenizing parser package: dev.kordex.parser
  • -
    - - -
  • -

    - Kord Extensions is now licensed under - the EUPL, version 1.2, - with the specific provision (EUPL articles 14 & 15) - that the applicable law is the (Republic of) Irish law and the - Jurisdiction is Dublin. -

    - -

    - For more information on this license, how we interpret it, and what it means for your project, - see the license information page. -

    -
  • - -
  • - Due to - changes that are coming with the new Maven Central platform, - we no longer publish Kord Extensions to Maven Central or OSSRH. - Instead, you should add the following repository URLs: - - -
  • Releases: https://releases-repo.kordex.dev
  • -
  • Snapshots: https://snapshots-repo.kordex.dev
  • - - - -
  • -

    - Version 2 includes a new system, intended to guide development - foci and provide interesting statistics for the community. -

    - -

    - If you're concerned about data collection in general, this system may be - configured in multiple ways - and is easily disabled. -

    - -

    - Our public statistics dashboard - contains aggregated statistics from the data collected. -

    -
  • - -
  • - The modules have been reorganized and renamed to make it easier to figure out their coordinates - and generally keep the Git repository cleaner. - For more information, - see the README in the modules folder. -
  • - -
  • - Translations have been - moved to their own repository - and are still licensed under the Mozilla Public License 2.0. -
  • - -
  • - The Contribution Guidelines - have been updated with information on the expected code style. - We'll continue to update the guidelines when other questions arise. -
  • - -
  • -

    - Kord Extensions 2 has a greater emphasis on the plugin system, which will be worked on and - documented more over the coming days. -

    - -

    - There are a lot of plans in motion around this system, so keep an eye out for announcements! -

    -
  • - -
  • - The Unsafe Module has been updated to support - and . -
  • - -
  • - A default about command is now included with Kord Extensions — to help - you meet the EUPL's licensing requirements and provide a standard way for your bots to provide - information about themselves. -
  • - -
    - - -

    - If you need to use Kord Extensions 1 for any reason, it can be found in the same Maven repository as - version 2. - You may also continue to use the Gradle plugin — just be sure to specify - the Kord Extensions version you need. -

    - - -
  • Coordinate: com.kotlindiscord.kord.extensions:kord-extensions
  • -
  • Last Version: 1.9.0-SNAPSHOT
  • -
  • Kord Version: 0.15.0-SNAPSHOT
  • -
    - -

    - Version 1 is licensed under the Mozilla Public License, version 2.0. - The code will always be available - under the v1 branch on GitHub. -

    -
    - - - - - - - - - -
    diff --git a/Writerside/topics/Changes.topic b/Writerside/topics/Changes.topic deleted file mode 100644 index 369d34ea..00000000 --- a/Writerside/topics/Changes.topic +++ /dev/null @@ -1,11 +0,0 @@ - - - - -

    - TODO: Write a proper changelog -

    -
    \ No newline at end of file diff --git a/Writerside/topics/Community-Projects.topic b/Writerside/topics/Community-Projects.topic deleted file mode 100644 index d33b8f71..00000000 --- a/Writerside/topics/Community-Projects.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/Core-Documentation.topic b/Writerside/topics/Core-Documentation.topic deleted file mode 100644 index bc92a9de..00000000 --- a/Writerside/topics/Core-Documentation.topic +++ /dev/null @@ -1,72 +0,0 @@ - - - - - - Learn about everything Kord Extensions has to offer - Core Documentation ✏️ - - -
    - - - - - - Main Topics - - - - - - - - - - - - - - - - - - Dig Deeper - - - - - diff --git a/Writerside/topics/Data-Adapters.topic b/Writerside/topics/Data-Adapters.topic deleted file mode 100644 index a2a9c3a1..00000000 --- a/Writerside/topics/Data-Adapters.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/Data-Collection.topic b/Writerside/topics/Data-Collection.topic deleted file mode 100644 index 4f3bd6a5..00000000 --- a/Writerside/topics/Data-Collection.topic +++ /dev/null @@ -1,293 +0,0 @@ - - - - - - - - - -

    - If you'd like to keep track of changes to the data collection system, please take a look at - this GitHub Discussion. - Feel free to subscribe to the thread if you'd like to receive notifications. -

    - -

    - If you're curious about our statistics, feel free to take a look at - the public dashboard. -

    - - - - -

    - If you intend your bot to be run by other people, - you are responsible for informing them about the data collection system! -

    - -

    - The easiest way to do this is to link to this page, but the way you handle this is up to you. -

    -
    - -

    - As of version 2, Kord Extensions includes a data collection feature. - Every 30 minutes, this feature collects some statistical data from your bots, submitting it to - an open-source server run and controlled by the - Kord Extensions team. -

    - -

    - This feature is intended to provide the developers with guidance on where to focus their efforts as well - as interesting statistical information that will be made available to everyone. -

    - - -

    - Data collected is stored on a server located in Germany, owned by - Hetzner. - This data is encrypted at rest (along with all backups) and is secured through containerization, network - isolation, and other standard security practices. -

    - -

    - The data collected is not sold or shared with any third-parties, and direct access is limited to - the Kord Extensions lead maintainer. - However, graphs and summaries based on that data, without bot/team information, are made available to - the public. -

    - -

    - All data submissions are assigned a UUID, which is stored by your Kord Extensions bot via a storage unit. - While we believe the data collected is not considered personal information under the GDPR, we provide - several mechanisms that allow you to use this UUID for data retrieval and removal: -

    - - -
  • -

    - By changing your data collection level to None (as explained below), your Kord Extensions bot - will stop collecting data and will use the UUID to delete any stored data from the data collection - server. -

    -
  • - -
  • -

    - The UUID may be used to directly access the data collection API: -

    - - -
  • - HTTP GET: https://data.kordex.dev/data/:uuid - retrieve the stored data for the - given UUID. -
  • - -
  • - HTTP DELETE: https://data.kordex.dev/data/:uuid - delete the stored data for the - given UUID. -
  • -
    - -

    - We may decide to create a simple UI for this later. -

    - - -
  • - If all else fails or you have any questions, contact us via Discord, or - email our GDPR officer at gdpr (at) - kordex.dev. -
  • - - -

    - The default data collection level (assuming nothing has been configured) is Standard. -

    -
    - - -

    - Kord Extensions provides the following data collection levels. -

    - - -

    - Setting this level will disable data collection. -

    - -

    - If you had a different level set previously, Kord Extensions will delete your data from the - data collection server. -

    -
    - - -

    - This level collects the following data in all cases: -

    - - -
  • The date of submission and the last updated date.
  • -
  • Whether your bot is running in development mode.
  • -
  • The versions of Kord and Kord Extensions your bot uses.
  • -
    - -

    - This level also collects the following data if the bot was built using - the KordEx Gradle plugin: -

    - - -
  • A list of first-party modules the bot is using.
  • -
    -
    - - -

    - This level collects the data collected by the Minimal level, as well as the following: -

    - - -
  • The Discord ID and the name of your bot.
  • -
  • The number of extensions and plugins your bot has loaded.
  • -
  • The number of guilds your bot can see.
  • -
  • A list of intents enabled by your bot.
  • -
  • Number of top-level chat commands registered.
  • -
  • Number of global application commands registered, split into message, slash, and user commands.
  • - -
  • -

    - Data relating to your bot's runtime environment: -

    - - -
  • Current JVM and Kotlin versions.
  • -
    - - -
    - - -

    - This level collects the data collected by the Minimal and Standard levels, as well as the following: -

    - - -
  • - A list of event types your bot is currently handling. -
  • - -
  • - The names of the extensions and plugins your bot has loaded. -
  • - -
  • -

    - Bot ownership information: -

    - - -
  • - If the bot is owned by a team: The ID and name of that team. -
  • - -
  • - If the bot is owned by a single person: No ownership data - is collected. -
  • -
    - - -
  • -

    - Data relating to your bot's runtime environment: -

    - - -
  • Total available memory.
  • -
  • Physical CPU count, thread count, and processor speed.
  • - - - -
    -
    - - - - -

    - If you're working in a containerized environment, or you otherwise absolutely cannot store the - required state, you can still participate: -

    - - -
  • - Generate a version 4 UUID, either directly using Java/Kotlin or via - a website like this one. -
  • - -
  • - Set the dataCollectionUUID system property or the - DATA_COLLECTION_UUID environmental variable to the UUID at runtime. -
  • -
    - -

    - Please ensure that each instance of your bot has a unique UUID! -

    -
    -

    - Your bot needs to store some information relating to data collection for it to function - properly: -

    - - -
  • The latest data collection level used.
  • -
  • A UUID representing the bot's data on the data collection server.
  • -
    - -

    - This data is stored through a storage unit, via the data adapter system. - By default, this means data will be stored in ./data/, but you can change that by - supplying another data adapter, such as - the official MongoDB data adapter. -

    -
    - - -

    - There are several ways to configure your bot's data collection level. - They're listed below in order, with earlier options overridden by later ones: -

    - - -
  • - The preferred approach: Set it via - the KordEx Gradle plugin. -
  • - -
  • - Set the DATA_COLLECTION environmental variable to extra, - standard, minimal, or none. -
  • - -
  • - Set the dataCollection system property to extra, - standard, minimal, or none. -
  • - -
  • - Set the dataCollectionMode property in your bot's builder, as explained in - the configuration section. -
  • -
    -
    -
    - \ No newline at end of file diff --git a/Writerside/topics/Functional-Modules.topic b/Writerside/topics/Functional-Modules.topic deleted file mode 100644 index 6456ab90..00000000 --- a/Writerside/topics/Functional-Modules.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    diff --git a/Writerside/topics/Gradle-Plugins.topic b/Writerside/topics/Gradle-Plugins.topic deleted file mode 100644 index a59e142d..00000000 --- a/Writerside/topics/Gradle-Plugins.topic +++ /dev/null @@ -1,30 +0,0 @@ - - - - -

    - One part of the Kord Extensions ecosystem is a set of Gradle plugins that are designed to be used when working - with Kord Extensions. -

    - - -
  • - — A Gradle plugin that provides a type-safe DSL for generating - Dockerfiles. -
  • - -
  • - — A much easier way to set up your project. - This is the recommended way to configure Gradle when working with Kord Extensions. -
  • -
    - -

    - For more information on each plugin, please select it from the navigation menu, or from the list above. - All our Gradle extensions are open-source and - may be found on GitHub. -

    -
    \ No newline at end of file diff --git a/Writerside/topics/I18n-Generator.topic b/Writerside/topics/I18n-Generator.topic deleted file mode 100644 index f69fb424..00000000 --- a/Writerside/topics/I18n-Generator.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    diff --git a/Writerside/topics/Other-Libraries.topic b/Writerside/topics/Other-Libraries.topic deleted file mode 100644 index 7ce242a7..00000000 --- a/Writerside/topics/Other-Libraries.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    diff --git a/Writerside/topics/Plugin-Loader.topic b/Writerside/topics/Plugin-Loader.topic deleted file mode 100644 index e31c460b..00000000 --- a/Writerside/topics/Plugin-Loader.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/Snippets.topic b/Writerside/topics/Snippets.topic deleted file mode 100644 index 3361e15a..00000000 --- a/Writerside/topics/Snippets.topic +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - This page describes one aspect of bot configuration. For more information on how to create and set up a bot, - see the relevant documentation. - - - - - -

    - When using Gradle, please note that it will automatically pull in the following transitive dependencies, - as they're also used by Kord Extensions. - In most situations, it is best to remove these dependencies from your build script and let Gradle - handle it, to avoid depending on an incompatible version. -

    - - -
  • The Kord Extensions annotations and token-parser modules
  • - -
  • ICU4J
  • -
  • JEmoji
  • -
  • Koin (Core and Logging)
  • -
  • Kord
  • -
  • Kotlin Logging
  • - -
  • - kotlinx.serialization; you'll still - need to configure the Gradle plugin yourself if you're using this. -
  • - -
  • OSHI (Operating System and Hardware Information)
  • -
  • PF4J
  • -
  • Sentry for Java
  • -
  • tomlkt
  • -
    -
    -
    - - - -

    - When using Gradle, please note that it will automatically pull in the following transitive dependencies, - as they're also used by Kord Extensions. - In most situations, it is best to remove these dependencies from your build script and let Gradle - handle it, so you can avoid depending on an incompatible version. -

    - - -
  • The Kord Extensions annotations and token-parser modules
  • - -
  • ICU4J
  • -
  • JEmoji
  • -
  • Koin (Core and Logging)
  • - -
  • - Kord; - see here for - ways to configure a different version. -
  • - -
  • Kotlin Logging
  • - -
  • - kotlinx.serialization; you'll still - need to configure the Gradle plugin yourself if you're using this. -
  • - -
  • - OSHI (Operating System and Hardware Information); only in - version 2 and later -
  • - -
  • PF4J
  • -
  • Sentry for Java
  • -
  • tomlkt
  • -
    -
    -
    -
    \ No newline at end of file diff --git a/Writerside/topics/Utility-Modules.topic b/Writerside/topics/Utility-Modules.topic deleted file mode 100644 index 009c3c6e..00000000 --- a/Writerside/topics/Utility-Modules.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/Welcome.topic b/Writerside/topics/Welcome.topic deleted file mode 100644 index 499d76da..00000000 --- a/Writerside/topics/Welcome.topic +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - Welcome - - - Kord Extensions is a Discord bot framework, with powerful (and easy-to-use) tooling and utilities for - writing Discord bots. - - - - General Information - - - - - Support the Developer - - - - - First Steps - - - - - - - - Topics and Components - - - - - - - - - - - - - Relevant links - - Official Website - - Official Discord Server - - GitHub Organization - - Template Project - - Hyacinth - docs generator - - - - Kord Links - - Kord Website - - Kord on GitHub - - Kord Discord Server - - - - - diff --git a/Writerside/topics/about/Framework-Comparison.topic b/Writerside/topics/about/Framework-Comparison.topic deleted file mode 100644 index 4a99417d..00000000 --- a/Writerside/topics/about/Framework-Comparison.topic +++ /dev/null @@ -1,238 +0,0 @@ - - - - -

    - This table acts as a framework comparison matrix, allowing you to make an informed decision about which - Discord bot framework to use for your project. - We've tried to be as objective as possible, but it is challenging to keep a table like this up to date. - If you notice any errors or have any suggestions, please feel free to contact us. -

    - -

    - The comparison matrix used to include abandoned projects, but we've decided to remove them going forward. If - you'd still like to check out these projects, you can find links to them at the bottom of the page. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     DiscordKtKord Extensions
    WebsiteWebsiteWebsite
    OriginThird-PartyThird-Party
    SupportGitHub, Kord Discord, official DiscordGitHub, Kord Discord, official Discord
    DocumentationWebsite, example project, template projectWebsite, template project
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     DiscordKtKord Extensions
    MethodologyAn easy, pleasurable API without the boilerplateUser-friendly and feature-packed, with its own ecosystem
    Release PolicyFollows Kord releasesSemi-independent release schedule following Kord snapshots
    Status✅ Active✅ Active
    API Version✅ v10✅ v10
    Gateway CouplingKord gateway only, no REST-only supportKord gateway only (with replaceable Kord instance), no REST-only support
    Logical StructureNo prescribed structure, handlers are created using DSL functionsFunctionality split into extension classes, optional plugins
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
     DiscordKtKord Extensions
    Application Commands
    Chat Commands
    Dynamic Chat Command Prefix
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     DiscordKtKord Extensions
    Autocomplete
    Bot Docs Generator✅ Markdown tables for commands - 🟡 - - Third-party Markdown command doc generator - , - first-party planned later -
    Components API
    Contextual Predicates🟡 Global with manual filtering
    Conversations API❌ Out of scope
    Data Storage API
    Dependency Injection✅ Integrated - ✅ - Koin-based -
    Modals API
    Paginator API
    Plugin System🟡 Currently in development
    Rich Command Arguments✅ (Customizable)✅ (Customizable)
    Translations✅ Single bot locale✅ Dynamic locales
    Multiple Bots in One Process❌ Currently impossible to implement
    -
    - - - - Discord InteraKTions - KFox - kordx.commands - - - -
    diff --git a/Writerside/topics/about/Licensing.topic b/Writerside/topics/about/Licensing.topic deleted file mode 100644 index a1e1fc93..00000000 --- a/Writerside/topics/about/Licensing.topic +++ /dev/null @@ -1,367 +0,0 @@ - - - - - - - -

    - We understand that software licensing can be confusing at the best of times. - This is especially true when dealing with an uncommon or poorly-documented software license — such as the EUPL, - which is the primary license we use as of Kord Extensions v2. -

    - - -

    - While we've tried to provide as much information as we can under our understanding of the EUPL, - we are not lawyers and this document does NOT constitute legal advice. -

    - -

    - The EUPL text always takes - priority over anyone's opinions, but we hope to clarify a few things by explaining how we interpret it. -

    -
    - - -

    - According to the European Commission, - the EUPL is a copyleft license, designed to be compatible with other copyleft licenses when combined - downstream into a larger work — and to be interoperable, according to European copyright law. - It includes multiple translated versions which hold equal legal value, covers distribution through a - network (such as Discord), and includes a Developer Certificate of Origin. -

    - -

    - What this ultimately means is that the EUPL functions like a more compatible and interoperable AGPL, - including a network use clause, but also a linking exception. - Instead of containing its own definition for what a derivative work is, it defers to European copyright - law — specifically, - Directive 91/250/EEC, - re-codified - 2009/24/EC. -

    - -

    - It additionally defers to the copyright law of the EU member state where the project owner resides, or - the one defined within the files' license headers, if provided. - In our case, this means that the applicable law applies to the Republic of Ireland, specifically within - Dublin's jurisdiction for legal challenges. -

    -
    - - -

    - While our interpretation cannot be treated as legal advice, we feel it may - be helpful to explain what we expect from our users. -

    - - -
  • - The EUPL is a copyleft license. - This means you can freely copy and distribute Kord Extensions, as long as you comply with the - conditions set forth in the license text. -
  • - -
  • -

    - European law allows for the reproduction of "interfaces" without the permission of the - rights-holders. - This means that the copying of names from an API (such as what happens with linking) does not - require that your project be licensed under the EUPL, or indeed any specific license. -

    - -

    - This was particularly important to us, as we didn't want to force any specific license onto bots, - modules, plugins, or other related projects using Kord Extensions, and we wanted to allow for - use by proprietary and commercial projects. -

    -
  • - -
  • -

    - The EUPL includes a "network use" clause — distribution and communication are treated as equal, and - communications of functionality over a network (such as Discord) require you to follow all the - distribution requirements. -

    - -

    - Due to the linking exception described above, this doesn't apply to your bots' code, but it does - apply to Kord Extensions itself, as explained below. -

    -
  • - -
  • -

    - The EUPL states that all distributions or communications of a work must include all copyright, - patent or trademark notices that refer to the license and to the disclaimer of warranties. - In our case, we simply ask for a mention of Kord Extensions and a link back to our site, which is - provided by default in the bundled about command. -

    - -

    - The GitHub Discussion used to - collect contributors' permission to relicense under the EUPL also asked for permission to provide - alternative licenses pending an application process. - This process has not yet been defined (as nobody has asked for an alternative license), but the - option remains open if this arrangement doesn't work for your project. -

    -
  • - -
  • -

    - When creating a combined work (such as a fat JAR containing your bot along with Kord Extensions) for - public distribution or use, the EUPL version 1.2 defines a number of compatible downstream licenses - you may use. -

    - - -

    - It's worth pointing out that - this does not mean that you can relicense Kord Extensions. -

    - -

    - Instead, this means that you can provide a larger combined work that includes Kord Extensions - under one of the compatible licenses, as long as you're following the terms set out by the - license applied to Kord Extensions. -

    - -

    - - Failure to follow the Kord Extensions license means you lose the right to distribute it! - -

    -
    - - -
  • - GNU Licenses: - - -
  • AGPL version 3
  • -
  • GPL version 2 or 3
  • -
  • LGPL version 2.1 or 3.0
  • -
    - - -
  • - Other Software Licenses: - - -
  • Cea Cnrs Inria Logiciel Libre License (CeCILL) version 2.0 or 2.1
  • -
  • Eclipse Public License (EPL) version 1.0
  • -
  • European Union Public Licence (EUPL) version 1.1 or 1.2
  • -
  • Mozilla Public Licence (MPL) version 2
  • -
  • Open Software License (OSL) version 2.1 or 3.0
  • -
  • Québec Free and Open-Source Licence (LiLiQ-R or LiLiQ-R+)
  • - - - - -

    - It's worth noting that the aforementioned linking exception means that, in the vast majority of - cases, you won't need to worry about this — as, generally, Discord bots aren't distributed - in a runnable binary format. -

    - - -
    - - -

    - While the EUPL applies specifically to Kord Extensions above, we felt it was worth explaining the specifics - of what it means for your projects. -

    - - -

    - While the legal definition for linking hasn't been tested in Europe, we understand it to mean - the copying of names and API definitions from another project yours is compiled against, without - actually distributing the project you're linking to. -

    - -

    - Under European law, this doesn't require rights-holder permission, and it doesn't impact how your - project must be distributed or licensed. -

    - -

    - Ultimately, this means that you're welcome to keep your bots and plugins closed-source or open-source - them under any license you wish, - as long as they don't include a distribution of Kord Extensions. -

    -
    - - -

    - If you modify Kord Extensions and use it in a bot or other distribution or - communication, - you must provide access to the source code of your modified version and state what changes you've - made to it. - - - Additionally, - you are not allowed to remove the references to Kord Extensions in - tne about command (including removing the command or extension), - unless you reproduce those references in another public command with equal visibility. -

    - -

    - The simplest way to do provide your source code and list of changes is to - fork the project on GitHub, - and this is what we'd prefer. - However, you may also provide a link and state your changes in - your bot's about command. -

    - -

    - We believe that Kord Extensions is at its best when it accounts for as many project types as possible, - and we always prefer contributions that make Kord Extensions more suitable, rather than multiple - diverging forks. -

    - -

    - As all Kord Extensions forks must be similarly licensed under the EUPL version 1.2, we may inspect - forks and make changes that are either inspired by those forks or taken from contributions to them, - without notice. - However, we will do our best to credit you by listing you as an additional author in each relevant - commit. -

    -
    - - -

    - While we don't really see any realistic reason for someone to create a public combined work, we want - to ensure that every project type is accounted for. -

    - -

    - We understand a combined work to be a larger work (such as a bot) that itself includes a distribution - of Kord Extensions — regardless of whether that distribution is modified — in either source code, - compiled or binary format. - Examples of combined works include: -

    - - -
  • - Distributions such as those created by - the Gradle distribution plugin - or the Gradle Shadow plugin -
  • - -
  • - Containers, including - Docker containers, regardless of - how they're intended to be deployed. -
  • - -
  • - A distribution of your project's source code that contains the Kord Extensions source code in-tree. -
  • -
    - -

    - Where a combined work is made available to the public, - it must be distributed under one of - the EUPL's compatible downstream licenses. - If you aren't distributing Kord Extensions' source code along with your project's, then this doesn't - mean your project's code must be licensed this way, or that your project must be open-source. - However, your project must be distributed in a way that follows the terms of the collective license - you're using. -

    - - -

    - It's worth pointing out that - this does not mean that you can relicense Kord Extensions. -

    -

    - - Failure to follow the Kord Extensions license means you lose the right to distribute it! - -

    -
    - -

    - We recommend against distributing Kord Extensions in source code format, and you should dynamically - link to it whenever possible, ideally using the KordEx Gradle plugin. -

    -
    - - -

    - Under the terms of the EUPL, a "distribution" or "communication" are treated as equal, and the same - terms are applied to both. - To quote the EUPL directly: -

    - - - ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, - renting, distributing, communicating, transmitting, or otherwise making - available, online or offline, copies of the Work or providing access to its - essential functionalities at the disposal of any other natural or legal - person. - - -

    - This means that running a Discord bot using Kord Extensions counts as a "distribution" or - "communication," as you are exposing its functionality to users on Discord. - As a result, you will need to comply with the terms of the EUPL, even if your project's source code - is not publicly available, and you're not using a modified version of Kord Extensions. - This is true even if it's a small bot for a private server, as Discord - ultimately decides who has access to your bot, rather than you having direct control over it. -

    - -

    - To make this easier, Kord Extensions provides an about command, which - includes a small footer mentioning Kord Extensions, linking to its website, and mentioning its - license. - You should configure this command with your bot's information, otherwise it'll look strange and a little - barren. -

    - - -

    - To remain compliant with the terms of the EUPL, your bot must have an easily accessible notice - including the following: -

    - - -
  • A link to the Kord Extensions website or GitHub repository
  • - -
  • - A note that your bot uses Kord Extensions and that it is licenced under the EUPL version 1.2 -
  • -
    - -

    - For this reason, - we strongly recommend that you do not attempt to remove - the about command from your bots or any modified version of - Kord Extensions in any way. -

    -
    -
    -
    - - -

    - If you have any questions regarding Kord Extensions and its licensing, please feel free to contact us - on Discord or - via GitHub Discussions. -

    - -

    - If you have any questions about the EUPL's wording or how to use it, the European Commission - provides a contact form you can use to ask legal questions. -

    -
    -
    diff --git a/Writerside/topics/core/Beginner-Tutorial.topic b/Writerside/topics/core/Beginner-Tutorial.topic deleted file mode 100644 index 5e1aa449..00000000 --- a/Writerside/topics/core/Beginner-Tutorial.topic +++ /dev/null @@ -1,525 +0,0 @@ - - - - -

    - This tutorial is for absolute beginners — people who are still learning about Kotlin and its build tools. If - you're already familiar with the basics, you should skip this and look at - the template project instead. -

    - - -

    - Before getting started, you'll need to install and set up some pieces of software. -

    - - - This tutorial assumes you'll be using a standard Kotlin setup, and recommends the use - of IntelliJ IDEA by JetBrains — but you can use another IDE that you're more familiar - with, as long as it supports Kotlin. - - - -
  • - Java Development Kit (JDK): - At least version %java-version%, but at most version 20 — we recommend - Adoptium -
  • - -
  • - IDE: We recommend - IntelliJ IDEA Community (via - JetBrains Toolbox), which - is a free IDE created by the same people that maintain Kotlin — but - this page provides more information on which - editors you can use for Kotlin development -
  • -
    -
    - - -

    - Once you've set up the above requirements, it's time to create your project. -

    - - - For more relevant information, select your editor using the menu at the top of the page. - - - -

    - Once you've installed IntelliJ IDEA, you're ready to create a project. If you already have a project - open, please close it before following these steps: -

    - - -
  • - Select Projects on the left, and click on the - New Project button at the top -
  • -
  • -

    - Select New Project on the left, and set the following options: -

    - - -
  • Name: example-bot
  • -
  • Location: A folder you'd like to use for your projects
  • -
  • Language: Kotlin
  • -
  • Build System: Gradle
  • -
  • JDK: %java-version%
  • -
  • Gradle DSL: Kotlin
  • -
    - -

    - Under Advanced Settings, set the following options: -

    - - -
  • Gradle version: ✅ Auto-select
  • -
  • GroupId: com.example.bot
  • -
  • ArtifactId: example-bot
  • -
    - - -
  • - Click the Create button -
  • - -
  • - If you're on Windows, allow IDEA to configure Windows Firewall when prompted -
  • - - - - To ensure that the IDE remains fast and responsible, we recommend that you wait for IDEA to - finish indexing before continuing. - If you're on a slow computer, this may take a while — IDEA will show its progress at the bottom of the - window while it continues to index your project. - - -

    - Your project has now been created, and you can move on to the next section. -

    -
    - - -

    - If you're not using IntelliJ IDEA, then you may need to - manually install Gradle to generate - a new Gradle project. If this is the case, once you have Gradle installed, you can follow - these steps to create your project: -

    - - -
  • - Open a terminal (or Command Prompt on Windows) in an empty folder that you've created - for your project -
  • - -
  • -

    - Type gradle init and hit enter, and answer the questions as follows: -

    - - -
  • Type: Application
  • -
  • Implementation language: Kotlin
  • -
  • Split functionality: No
  • -
  • Build script DSL: Kotlin
  • -
  • Project name: bot
  • -
  • Package: com.example.bot
  • -
    - - -
  • - Type gradlew wrapper --gradle-version VERSION, replacing - VERSION with the latest version available from the - Gradle releases page, and hit - enter -
  • - - -

    - Your project has now been created, and you can move on to the next section. -

    -
    -
    - - -

    - Once you've created your project, you'll need to configure Gradle and set up your project's dependencies. - This is done by creating and editing some of your project's files. -

    - - -
  • -

    - First, set up your dependencies. Within the gradle/ folder, create a file named - libs.versions.toml with the following contents, replacing KOTLIN_VERSION - with - - - the same version used by Kord Extensions - : -

    - - - [versions] - kotlin = "KOTLIN_VERSION" # Kotlin version must be updated in build.gradle.kts too - - kord-extensions = "%version%" - slf4j = "2.0.9" - - [libraries] - kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib-jdk8" } - - slf4j = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" } - -
  • - -
  • -

    - Next, configure your build script by editing your build.gradle.kts file. - You'll need to replace its contents with the following, replacing KOTLIN_VERSION - with - - - the same version used by Kord Extensions - : -

    - - - plugins { - // Update this if you change the Kotlin version in libs.versions.toml - kotlin("jvm") version "KOTLIN_VERSION" - - // The KordEx plugin - id("dev.kordex.gradle.kordex") version "%plugin-version%" - } - - dependencies { - implementation(libs.kotlin.stdlib) - implementation(libs.slf4j) - } - - kordEx { - mainClass = "com.example.bot.AppKt" - } - -
  • - -
  • - In IntelliJ IDEA, click on the Gradle tab on the right side of the - window, and then click the Reload All Gradle Projects button — this - will tell IDEA to refresh the project and index its dependencies -
  • - -
  • - If your editor supports Gradle, you may need to tell it to reload your Gradle project. - If this is the case, then please see your editor's documentation for more information. -
  • -
    -
    - - -

    - If a folder named src/test was created, delete it for now. If you'd like to write unit tests - later, you can always recreate it. -

    - -

    - Before continuing, create a file containing a main function: -

    - - -
  • - Right-click the src/main/kotlin folder in the left sidebar, select - New -> Package, and enter com.example.bot -
  • - -
  • - Right-click the bot folder you just created, select - New -> Kotlin Class/File, - select File from the list, and enter App -
  • - -
  • -

    - Add the following code to the file you just created: -

    - - - suspend fun main() { - // We'll add code here later - } - -
  • -
    - - -
  • - Within the src/main/kotlin folder, create folders matching this structure: - com/example/bot -
  • - -
  • - Within the bot folder you just created, create a new file named App.kt - (the path should be src/main/kotlin/com/example/bot) -
  • - -
  • -

    - Add the following code to the file you just created: -

    - - - package com.example.bot - - suspend fun main() { - // We'll add code here later - } - -
  • -
    - -

    - Let's make sure your project compiles. - In the Gradle tab on the right side of the window, - expand example-bot -> Tasks -> build, and double-click on the - build task. - Once Gradle has finished building your project, you should see BUILD SUCCESS in the new - pane at the bottom of the window. -

    - - - If your editor supports Gradle, you may be able to run the build task from directly within it. - If this is the case, then please see your editor's documentation for more information. - - -

    - Let's make sure your project compiles. - Open a terminal (or Command Prompt on Windows) within your project's folder, and run the following: -

    - - -
  • Mac/Linux: ./gradlew build
  • -
  • Windows: ./gradlew.bat build
  • -
    - -

    - Once Gradle has finished building your project, you should see BUILD SUCCESS in the output. -

    - -

    - If this is not the case, please review the steps above. - You may also ask for help on our Discord server. -

    -
    - - -

    - Now that your project has been set up, let's get down to business and create your first bot. -

    - -

    - Within the App.kt file you created earlier, add the following code before your - main function: -

    - - - import dev.kordex.core.ExtensibleBot - import dev.kordex.core.utils.env - - private val TOKEN = env("TOKEN") // Get the bot's token from environment variables or a .env file - - -

    - Within the main function, replace the comment with the following code: -

    - - - val bot = ExtensibleBot(TOKEN) { - // We'll add code here later - } - - bot.start() - - -

    - Your file should now look like this: -

    - - - package com.example.bot - - import dev.kordex.core.ExtensibleBot - import dev.kordex.core.utils.env - - private val TOKEN = env("TOKEN") // Get the bot's token from environment variables or a .env file - - suspend fun main { - val bot = ExtensibleBot(TOKEN) { - // We'll add code here later - } - - bot.start() - } - - -

    - Next, create a new file named .env in the same folder as your build.gradle.kts - file. Add the following to that file, replacing DISCORD_TOKEN with the token for a Discord bot - you created via the Discord Developer Portal. -

    - - - If you're using Git or another version-control system, remember not to push the .env file to - an online repository. - To avoid this, you may wish to set up a gitignore, or use - whatever similar system your version-control system provides. - - - - TOKEN=DISCORD_TOKEN - - -

    - At this point, you should have the simplest possible bot. You can start it by running the run - Gradle task, but it won't do very much yet. - In the next section, let's add some functionality to your bot. -

    -
    - - -

    - Using "Extensions," let's create a basic command. - This command is an updated version of the classic !slap command that was a common feature - present in IRC bots a long time ago. -

    - -

    - First, create a folder named extensions within your bot folder, and then create - a new file within it named SlapExtension.kt, containing the following code: -

    - - - // import ... - - class SlapExtension : Extension() { - override val name = "slap" - - override suspend fun setup() { - // We'll add code here later - } - } - - -

    - The above code creates a new extension named "slap," but it doesn't currently do anything. - Let's start by adding a basic command, by replacing the comment in the setup function: -

    - - - publicSlashCommand { - name = "slap" - description = "Get slapped!" - - action { - respond { - content = "_Slaps ${user.mention} around a bit with a smelly trout!_" - } - } - } - - -

    - Let's also make the bot load this extension. Head back to App.kt, and replace the comment - in the main function with the following: -

    - - - extensions { - add(::SlapExtension) - } - - -

    - If you were to run the bot now, a new command should be available on all servers you've added it to. - It should be named /slap — go ahead and give it a try! -

    - - - It can take Discord up to an hour to notice that you've registered a command. - If your bot is running without any errors, and you're testing it within a server you've added it to, you - may need to wait some time before the command will show up. - -
    - - -

    - Our slap command is now functional, but it's not very fun. - Let's add an argument, so we can slap other users! -

    - -

    - After the setup function in SlapExtension.kt, add the following inner class: -

    - - - inner class SlapArgs : Arguments() { - val target by user { - name = "user" - description = "User to slap" - } - } - - -

    - Next, modify the command you registered earlier: -

    - - - publicSlashCommand(::SlapArgs) { - name = "slap" - description = "Get slapped!" - - action { - respond { - content = "_Slaps ${arguments.target.mention} around a bit with a smelly trout!_" - } - } - } - - -

    - If you restart the bot, you should now be able to slap other users. Give it a try! -

    -
    - - -

    - As you may have realized, the above tutorial only showcases a tiny fraction of what you can do with - Kord Extensions. - We recommend you experiment with the code you've written above for a while, and then read the rest of this - documentation. -

    - -

    - If you run into any problems, feel free to - join our Discord server for support! -

    -
    - - - - Template Project - - -
    diff --git a/Writerside/topics/core/Checks.topic b/Writerside/topics/core/Checks.topic deleted file mode 100644 index a397198d..00000000 --- a/Writerside/topics/core/Checks.topic +++ /dev/null @@ -1,1472 +0,0 @@ - - - - - - -

    - Checks provide a way to define a reusable, generic set of predicates that may be applied to - event handlers, commands, - components, and other APIs. -

    - -

    - In its simplest form, a check is an extension or receiver function against the CheckContext or - CheckContextWithCache types, making use of the predefined check functions or APIs provided. -

    - - - check { - hasPermissions( - // Same as the perms required to mass-ban members - // using the "Members" management panel in the Discord - // client. - - Permission.BanMembers + Permission.ManageGuild - ) - } - - -

    - The default state for all checks is passed, with the passed property - set to true. - A check will not fail unless passed is set to false, but this property is - automatically set for you when using the provided API functions. -

    - - -

    - When writing your own checks, the following APIs are available. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    fail -

    - Mark this check as having failed. -

    - -

    - This function optionally takes a message - - Key object explaining the failure, - which will be displayed to the user in most situations. -

    -
    failIf -

    - Mark this check as having failed if the callable block returns true. -

    - -

    - This function optionally takes a message - - Key object - explaining the failure, - which will be displayed to the user in most situations. -

    -
    failIfNot -

    - Mark this check as having failed if the callable block returns false. -

    - -

    - This function optionally takes a message - - Key object - explaining the failure, - which will be displayed to the user in most situations. -

    -
    pass - Mark this check as having passed. -
    passIf - Mark this check as having passed if the callable block returns true. -
    passIfNot - Mark this check as having passed if the callable block returns false. -
    Boolean.whenFalse - If the receiver is false, execute the provided callable block. - Otherwise, do nothing. -
    Boolean.whenTrue - If the receiver is true, execute the provided callable block. - Otherwise, do nothing. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    cacheMutableStringKeyedMap<Any> -

    - For CheckContextWithCache, cache object shared - with other checks that are part of the current invocation, and provided within any - relevant contexts if all checks pass. -

    - -

    - You may store data in this cache to be shared between your checks or contexts as required. - In some cases, this may help you to avoid making long-running calculations or API calls - multiple times, which will speed up your bot. -

    -
    errorResponseKeyKey - - Translation Key object - to use when generating an error message for display on Discord, intended only - for formatting. - Defaults to CoreTranslations.Checks.responseTemplate, and must contain a single - ordinal placeholder to be replaced with the error message provided by the various - fail functions. -
    eventT: Event - Event object that triggered this check. -
    localeLocale - Relevant locale object for this check. - Provided by whatever system the check belongs to and used to translate messages. -
    messageString? - Error message to return to the user, typically set by the fail functions. - Will not be automatically translated unless you pass a - - Key object - to the failure functions. -
    passedBoolean - Whether this check is currently passing. - Set by the fail and pass functions. - Defaults to true. -
    -
    -
    - - -

    - Kord Extensions provides a large number of predefined check functions. - These checks are generic and work with all event types, with some caveats that are documented below. -

    - -

    - Before writing your own checks, it's best to make sure that there are no existing checks that cover your - use-case! -

    - - -

    - All functions in this section take either a Snowflake argument representing a channel ID, - or a suspending callable block that returns a ChannelBehavior. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    channelAbove -

    - Check whether the channel the event fired in is above the given channel in the channel list. - This check attempts to calculate the channel positions in the same way Discord does: -

    - - -
  • Compares channels within the same category by their relative positions.
  • - -
  • - Compares channels within different categories by the categories' relative positions, - where channels not in a category are always above channels - that are within categories. -
  • -
    - -

    - Always fails when the event doesn't concern a channel, or - concerns a DM channel. -

    -
    channelBelow -

    - Check whether the channel the event fired in is below the given channel in the channel list. - - This check attempts to calculate the channel positions in the same way Discord does: -

    - - -
  • Compares channels within the same category by their relative positions.
  • - -
  • - Compares channels within different categories by the categories' relative positions, - where channels not in a category are always above channels - that are within categories. -
  • -
    - -

    - Always fails when the event doesn't concern a channel, or - concerns a DM channel. -

    -
    inCategory -

    - Check whether the event fired within the given category. -

    - -

    - Always fails when the event doesn't concern a channel, or - concerns a DM channel. -

    -
    inChannel -

    - Check whether the event fired within the given channel. -

    - -

    - Always fails when the event doesn't concern a channel. -

    -
    notInCategory -

    - Check whether the event didn't fire within the given category. -

    - -

    - Always passes when the event doesn't concern a channel, or - concerns a DM channel. -

    -
    notInChannel -

    - Check whether the event didn't fire within the given channel. -

    - -

    - Always passes when the event doesn't concern a channel. -

    -
    -
    - - -

    - All functions in this section take a variadic argument, allowing you to specify more than one channel - type. -

    - - - - - - - - - - - - - - - - - - -
    NameDescription
    channelType -

    - Check whether the channel the event fired in is one of the given channel types. -

    - -

    - Always fails when the event doesn't concern a channel. -

    -
    notChannelType -

    - Check whether the channel the event fired in is not one of - the given channel types. -

    - -

    - Always passes when the event doesn't concern a channel. -

    -
    -
    - - -

    - All functions that take arguments in this section take either a Snowflake argument - representing a guild ID, or a suspending callable block that returns a GuildBehavior. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    inGuild -

    - Check whether the event fired within the given guild. -

    - -

    - Always fails when the event doesn't concern a guild. -

    -
    notInGuild -

    - Check whether the event didn't fire within the given guild. -

    - -

    - Always passes when the event doesn't concern a guild. -

    -
    anyGuild -

    - Check whether the event fired within a guild. -

    - -

    - Always fails when the event doesn't concern a guild, or when - the corresponding GuildBehavior can't be retrieved. -

    - -

    - If you're not seeing the results you expected, please double-check your - cache configuration. -

    -
    noGuild -

    - Check whether the event didn't fire within a guild. -

    - -

    - Always passes when the event doesn't concern a guild, or when - the corresponding GuildBehavior can't be retrieved. -

    - -

    - If you're not seeing the results you expected, please double-check your - cache configuration. -

    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    hasPermission -

    - Check whether the guild member an event fired for has the given permission, or the - Administrator permission. -

    - -

    - Always fails when the event doesn't concern a guild member, - or when the corresponding MemberBehavior can't be retrieved. -

    - -

    - If you're not seeing the results you expected, please double-check your - cache configuration and - intents configuration. -

    -
    hasPermissions -

    - Check whether the guild member an event fired for has the given permissions set, or the - Administrator permission. -

    - -

    - Always fails when the event doesn't concern a guild member, - or when the corresponding MemberBehavior can't be retrieved. -

    - -

    - If you're not seeing the results you expected, please double-check your - cache configuration and - intents configuration. -

    -
    notHasPermission -

    - Check whether the guild member an event fired for doesn't - have the given permission, - and doesn't have the Administrator permission. -

    - -

    - Always passes when the event doesn't concern a guild member, - or when the corresponding MemberBehavior can't be retrieved. -

    - -

    - If you're not seeing the results you expected, please double-check your - cache configuration and - intents configuration. -

    -
    notHasPermissions -

    - Check whether the guild member an event fired for doesn't - have the given permissions set, - and doesn't have the Administrator permission. -

    - -

    - Always passes when the event doesn't concern a guild member, - or when the corresponding MemberBehavior can't be retrieved. -

    - -

    - If you're not seeing the results you expected, please double-check your - cache configuration and - intents configuration. -

    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    isBot -

    - Check whether the user an event fired for is a bot. -

    - -

    - Always fails when the event doesn't concern a user. -

    -
    isBotAdmin -

    - Check whether the user an event fired for is one of the current bot's application admins. - The team the application belongs to on the Discord developer dashboard defines the - application's admins. -

    - -

    - Always fails when the event doesn't concern a user, or the - bot's application doesn't belong to a team. -

    -
    isBotOwner -

    - Check whether the user an event fired for is the current bot's application owner. - For applications that don't belong to a team, this is the person that owns the bot's - application on the Discord developer dashboard. -

    - -

    - Always fails when the event doesn't concern a user, or the - bot's application belongs to a team. -

    -
    isInThread -

    - Check whether the event fired within a thread. -

    - -

    - Always fails when the event doesn't concern a channel. -

    -
    isNotBot -

    - Check whether the user an event fired for is not a bot. -

    - -

    - Always passes when the event doesn't concern a user. -

    -
    isNotBotAdmin -

    - Check whether the user an event fired for is not one of the - current bot's application admins. - The team the application belongs to on the Discord developer dashboard defines the - application's admins. -

    - -

    - Always passes when the event doesn't concern a user, or the - bot's application doesn't belong to a team. -

    -
    isNotBotOwner -

    - Check whether the user an event fired for is not the current - bot's application owner. - For applications that don't belong to a team, this is the person that owns the bot's - application on the Discord developer dashboard. -

    - -

    - Always passes when the event doesn't concern a user, or the - bot's application belongs to a team. -

    -
    isNotInThread -

    - Check whether the event didn't fire within a thread. -

    - -

    - Always passes when the event doesn't concern a channel. -

    -
    -
    - - - -

    - The guild NSFW level comparison checks below compare NSFW levels based on their severity, rather - than Discord's integer values for this enumeration. - It uses the following order: -

    - - -
  • NsfwLevel.Safe: -1
  • -
  • NsfwLevel.Default: 0
  • -
  • NsfwLevel.AgeRestricted: 1
  • -
  • NsfwLevel.Explicit: 2
  • -
    - -

    - Kord Extensions uses verbatim comparison against the above integers for NSFW levels, which may - result in unexpected behaviour. -

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    channelIsNsfw -

    - Check whether the event fired in an age-restricted channel. -

    - -

    - Always fails when the event doesn't concern a channel. -

    -
    channelOrGuildIsNsfw -

    - Check whether the event fired in an age-restricted channel, or an NSFW-marked guild. -

    - -

    - Always fails when the event doesn't concern a channel or - guild. -

    - -

    - This check combines the channelIsNsfw and - guildNsfwLevelHigherOrEqual checks, with the latter function being called with - NsfwLevel.AgeRestricted as the first argument. -

    -
    guildNsfwLevelHigher -

    - Check whether the event fired in a guild that has a more severe NSFW level than the given - one. - See the note at the top of this section for information on how Kord Extensions compares - NSFW levels. -

    - -

    - Always fails when the event doesn't concern a guild. -

    -
    guildNsfwLevelHigherOrEqual -

    - Check whether the event fired in a guild that has an equal or more severe NSFW level than - the given one. - See the note at the top of this section for information on how Kord Extensions compares - NSFW levels. -

    - -

    - Always fails when the event doesn't concern a guild. -

    -
    guildNsfwLevelLower -

    - Check whether the event fired in a guild that has a less severe NSFW level than the given - one. - See the note at the top of this section for information on how Kord Extensions compares - NSFW levels. -

    - -

    - Always fails when the event doesn't concern a guild. -

    -
    guildNsfwLevelLowerOrEqual -

    - Check whether the event fired in a guild that has an equal or less severe NSFW level than - the given one. - See the note at the top of this section for information on how Kord Extensions compares - NSFW levels. -

    - -

    - Always fails when the event doesn't concern a guild. -

    -
    hasGuildNsfwLevel -

    - Check whether the event fired in a guild with the given NSFW level. -

    - -

    - Always fails when the event doesn't concern a guild. -

    -
    notChannelIsNsfw -

    - Check whether the event didn't fire in an age-restricted - channel. -

    - -

    - Always passes when the event doesn't concern a channel. -

    -
    notChannelOrGuildIsNsfw -

    - Check whether the event didn't fire in an age-restricted - channel or an NSFW-marked guild. -

    - -

    - This check combines the notChannelIsNsfw and - guildNsfwLevelLower checks, with the latter function being called with - NsfwLevel.AgeRestricted as the first argument. -

    -
    notHasGuildNsfwLevel -

    - Check whether the event didn't fire in a guild with the given - NSFW level. -

    - -

    - Always passes when the event doesn't concern a guild. -

    -
    -
    - - -

    - All functions in this section take either a Snowflake argument representing a role ID, - or a suspending callable block that returns a RoleBehavior. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    hasRole -

    - Check whether the guild member an event fired for has the given role. -

    - -

    - Always fails when the event doesn't concern a guild member, - when the corresponding MemberBehavior can't be retrieved, or when the given - role doesn't exist or can't be retrieved. -

    - -

    - If you're not seeing the results you expected, please double-check your - cache configuration. -

    -
    notHasRole -

    - Check whether the guild member an event fired for doesn't have - the given role. -

    - -

    - Always passes when the event doesn't concern a guild member, - when the corresponding MemberBehavior can't be retrieved, or when the given - role doesn't exist or can't be retrieved. -

    - -

    - If you're not seeing the results you expected, please double-check your - cache configuration. -

    -
    topRoleEqual -

    - Check whether the guild member an event fired for has the given role as their top role. -

    - -

    - Always fails when the event doesn't concern a guild member, - when the guild member doesn't have any roles, when the corresponding - MemberBehavior can't be retrieved, or when the given role doesn't exist or - can't be retrieved. -

    -
    topRoleHigher -

    - Check whether the guild member an event fired for has a top role listed above the given - role. -

    - -

    - Always fails when the event doesn't concern a guild member, - when the guild member doesn't have any roles, when the corresponding - MemberBehavior can't be retrieved, or when the given role doesn't exist or - can't be retrieved. -

    -
    topRoleHigherOrEqual -

    - Check whether the guild member an event fired for has a top role equal to or listed above - the given role. -

    - -

    - Always fails when the event doesn't concern a guild member, - when the guild member doesn't have any roles, when the corresponding - MemberBehavior can't be retrieved, or when the given role doesn't exist or - can't be retrieved. -

    -
    topRoleLower -

    - Check whether the guild member an event fired for has a top role listed below the given - role. -

    - -

    - Always fails when the event doesn't concern a guild member, - when the guild member doesn't have any roles, when the corresponding - MemberBehavior can't be retrieved, or when the given role doesn't exist or - can't be retrieved. -

    -
    topRoleLowerOrEqual -

    - Check whether the guild member an event fired for has a top role equal to or listed below - the given role. -

    - -

    - Always fails when the event doesn't concern a guild member, - when the guild member doesn't have any roles, when the corresponding - MemberBehavior can't be retrieved, or when the given role doesn't exist or - can't be retrieved. -

    -
    topRoleNotEqual -

    - Check whether the guild member an event fired for doesn't have - the given role as their top role. -

    - -

    - Always passes when the event doesn't concern a guild member, - when the guild member doesn't have any roles, when the corresponding - MemberBehavior can't be retrieved, or when the given role doesn't exist or - can't be retrieved. -

    -
    -
    - - -

    - All functions in this section take either a Snowflake argument representing a role ID, - or a suspending callable block that returns a ChannelBehavior. -

    - - - - - - - - - - - - - - - - - - -
    NameDescription
    inTopChannel -

    - Check whether the event fired within the given top channel. - Resolves the parent channel when the event fired within a thread, and uses that for the - comparison. -

    - -

    - Always fails when the event doesn't concern a channel. -

    -
    notInTopChannel -

    - Check whether the event didn't fire within the given top channel. - Resolves the parent channel when the event fired within a thread, and uses that for the - comparison. -

    - -

    - Always passes when the event doesn't concern a channel. -

    -
    -
    -
    - - -

    - Kord Extensions provides several utilities to help you when working with checks. -

    - - -

    - These functions are extension functions against the CheckContext type. -

    - - - - - - - - - - - - - - - - - - -
    NameDescription
    or -

    - Convenience function intended for combining checks. - This allows you to supply multiple checks, where only one of them needs to pass. -

    - - - public suspend fun CheckContext<*>.channelOrGuildIsNsfw() { - channelIsNsfw() - or { guildNsfwLevelHigherOrEqual(NsfwLevel.AgeRestricted) } - } - - -

    - Combines failure messages when there's already a check failure message and the check within - the or block sets a failure message. -

    - -

    - If any previous checks have passed, doesn't run the code within the or block. -

    -
    silence - If a check has set a failure message, this function will clear it. - This prevents an error message from being sent if the check fails. -
    -
    - - -

    - These functions exist to provide easy ways to extract entities from generic events. - While these functions are particularly useful for implementing checks, they may also be useful in other - scenarios. -

    - - -

    - Because Kord doesn't provide interfaces that describe what data is available in each event type, - these functions are essentially huge when blocks. - We maintain these functions by hand, so if we've missed any events, please let us know! -

    - -

    - If you're writing custom events, please make sure to implement the relevant - . -

    -
    - - - Some events may return a null value for their corresponding entities. - There's no way to tell the difference between an event that returns a null value, or an event that - doesn't support the given event type. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    channelFor - Retrieve the corresponding ChannelBehavior for the given event, or - null when the event doesn't concern a channel or the ChannelBehavior - can't be retrieved. -
    guildFor - Retrieve the corresponding GuildBehavior for the given event, or - null when the event doesn't concern a guild or the GuildBehavior - can't be retrieved. -
    interactionFor - Retrieve the corresponding Interaction for the given event, or - null when the event doesn't concern an interaction. -
    memberFor - Retrieve the corresponding MemberBehavior for the given event, or - null when the event doesn't concern a guild member or the - MemberBehavior can't be retrieved. -
    messageFor - Retrieve the corresponding MessageBehavior for the given event, or - null when the event doesn't concern a message or the MessageBehavior - can't be retrieved. -
    roleFor - Retrieve the corresponding RoleBehavior for the given event, or - null when the event doesn't concern a role or the RoleBehavior - can't be retrieved. -
    threadFor - Retrieve the corresponding ThreadChannelBehavior for the given event, or - null when the event doesn't concern a role or the - ThreadChannelBehavior can't be retrieved. -
    topChannelFor -

    - Retrieve the corresponding ChannelBehavior for the given event, or - null when the event doesn't concern a channel or the - ChannelBehavior can't be retrieved. -

    - -

    - Retrieves the parent channel when the event concerns a thread. -

    -
    userFor - Retrieve the corresponding UserBehavior for the given event, or - null when the event doesn't concern a user or the UserBehavior can't - be retrieved. -
    -
    - - -

    - These functions are extension functions against the KLogger type, part of the Kotlin - Logging library. - Designed for specific situations, these functions log predefined messages for you. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    failed - Log a failing check, along with a message that explains why. -
    noCategoryId - Log that this check failed because the given ID doesn't correspond with a known category. -
    noChannelId - Log that this check failed because the given ID doesn't correspond with a known channel. -
    noGuildId - Log that this check failed because the given ID doesn't correspond with a known guild. -
    noRoleId - Log that this check failed because the given ID doesn't correspond with a known role. -
    nullChannel - Log that the event's channel is null, and that this check may not support the event. -
    nullGuild - Log that the event's guild is null, and that this check may not support the event. -
    nullMember - Log that the event's member is null, and that this check may not support the event. -
    nullMessage - Log that the event's message is null, and that this check may not support the event. -
    passed - Log a passing check, optionally along with a message. -
    -
    -
    - - -

    - Sometimes, it can be useful to define your own check functions. - This allows you to create custom, reusable checks for your own use-cases. -

    - -

    - All check functions are extension functions against the CheckContext type, and they may or - may not take arguments as required. - There's no mandated structure for these functions, but you may wish to use a similar structure to the - bundled checks: -

    - - - public suspend fun <T: Event> CheckContext<T>.myCheck() { - if (!passed) { - // This check context has already failed, so bail early. - return - } - - // Create a logger that's exclusive to this check. - val logger = KotlinLogging.logger("package.and.function.myCheck") - - // Extract the relevant entities from the event. - val eventChannel = channelFor(event) - - // Do a null check, if necessary. - if (eventChannel == null) { - logger.nullChannel(event) - - fail() - } else { - // Do your check logic here. - - if (eventChannel.id.toString().startsWith("1")) { - // When the check passes, remember to log it. - logger.passed() - - pass() - } else { - // When the check faiils, remember to log exactly why it failed. - logger.failed("Channel ID did not start with 1") - - fail("Channel ID did not start with 1") - } - } - } - - -

    - If you need more examples, feel free to - - take a look at the bundled checks. -

    - -
    diff --git a/Writerside/topics/core/Commands.topic b/Writerside/topics/core/Commands.topic deleted file mode 100644 index a2fc5b2b..00000000 --- a/Writerside/topics/core/Commands.topic +++ /dev/null @@ -1,487 +0,0 @@ - - - - -

    - Kord Extensions provides a rich, type-safe commands framework which is only as complex as you need it to be. - At the moment, these basic types of commands are supported: -

    - - -
  • - , prefix-based commands that are sent directly within messages on Discord. -
  • - -
  • -

    - , which are based on Discord's interactions system and show up in - the client's UI. -

    - - -
  • - , which are special commands that appear in right-click (or - tap-and-hold) menus in the Discord client. -
  • - -
  • - , which are invoked by typing a slash character (/) - into the Discord chat input. -
  • -
    - - - -

    - Each type of command has a specific set of use-cases and functionality, - and thus provides a different API surface. - However, all commands have quite a few things in common. -

    - -

    - The following sections describe commands in generic terms. For more information on the specific types of - supported commands (and how to use them), please see the pages linked above. -

    - - -

    - While don't support command arguments, the other command types do. To - specify your command's arguments, you'll need to create a class that extends the Arguments - type. -

    - -

    - This class must make use of the available converter builder functions. - Kord Extensions provides a large number of these, but it's possible to write your own or use those provided - by third-party libraries. -

    - -

    - See below for an example Arguments subclass. -

    - - - inner class MyArguments : Arguments() { - val target by user { - name = "target" - description = "Target user" - } - } - - -

    - Your argument class' constructor must then be provided to your command's builder function. - The parsed arguments will be available via the arguments property in your command's - action block. -

    - - - publicSlashCommand(arguments = ::MyArguments) { - name = "my-command" - - action { - respond { - content = "Target user: ${arguments.user.mention}" - } - } - } - - - The Arguments type provides the following API properties: - - - - - - - - - - - - - - -
    NameTypeDescription
    parseForAutocompleteBoolean -

    - When working with , - your arguments may make use of Discord's autocomplete system. - If you have later arguments with an autocompletion that requires the value of a previous - argument, setting this to true will tell Kord Extensions to fill in those - arguments before calling your autocomplete block. -

    - -

    - As some converter types provide a default autocomplete block, you may need to - enable this when you're using them, even when you don't provide your own. - For more information, see the dedicated documentation. -

    -
    - -

    - For more information on the converter system (and the available argument types), - see the dedicated documentation. -

    - - - -

    - A modal form is Kord Extensions' way of representing the settings and contents of a - - Discord modal. - - Chat commands don't support modals, but all other command types do. -

    - -

    - If you want to make use of modals, you'll need to create a class that extends the `ModalForm` type. - This class must make use of the provided input functions to define the modal's contents. - It may also override the properties defined by the ModalForm type. -

    - - - class MyModal : ModalForm() { - override var title: Key = Translations.Modals.myModal - - val line = lineText { - label = "Line of Text" - placeholder = "Enter something..." - } - } - - -

    - Just like with command arguments, the constructor for your ModalForm subtype must be passed - to your command's builder function. - When your command is executed, the modal is provided as an argument to your command's - action function. -

    - - - publicSlashCommand(modal = ::MyModal) { - name = "my-command" - - action { modal -> - respond { - content = "Text: ${modal.line.value}" - } - } - } - - -

    - For more information on the Modal system (and to get a few tips on how else to use it), see - the dedicated documentation. -

    -
    - - -

    - When you register a command, the builder function will return an object that extends the - Command type. - This type provides generic access to basic command data and functionality. -

    - -

    - The intended public API is described below. - However, it's possible to extend this type (and the other command types) if you want to implement your - own customized command types. -

    - -

    - The Command type extends the Lockable and - KordExKoinComponent - types. - - For more information on the other APIs provided by this type, we recommend examining it in your IDE. -

    - - - - - - - - - - - - - - - - -
    NameArgumentsDescription
    requireBotPermissions - varargPermission - - If your bot requires any permissions to be able to execute the command, use this function to - register those permissions. - If the command is executed while the bot does not have the specified permissions, an error will - be sent to the user, explaining which permissions the bot is missing. -
    -
    - - -

    - Mutable properties may be set in the builder function used to register your command. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    extensionExtensionThe extension that this command belongs to.
    kord - - Kord - - Quick access to the bot's Kord instance.
    lockingBoolean - Whether to use a - - Mutex - to prevent this command from being run concurrently with itself. - - Defaults to false, allowing the command to run multiple times concurrently. -
    nameKey - - Key object - representing the command's name, which is used to invoke it. - This property must be set for the command to be registered and has no default value. -
    sentry - - SentryAdapter - - Quick access to the bot's Sentry adapter.
    translationsProvider - - TranslationsProvider - - Quick access to the bot's translation provider.
    -
    -
    - - -

    - When a command is executed, the provided action block is run. - This block is a receiver function against a subtype of the CommandContext type, which provides - an API that allows you to work with the command's execution data and respond to the user. -

    - -

    - The CommandContext type extends the - - - TranslatableContext - - - and - - - KordExKoinComponent - - - types. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    getChannel - Returns the relevant - - ChannelBehavior - - for this command execution. -
    getGuild - Returns the relevant - - GuildBehavior - - for this command execution, or null if this command was executed in a DM. -
    getLocale - Resolves and returns the relevant Locale object for this command execution, - using the bot's - - - configured locale resolvers. -
    getMember - Returns the relevant - - MemberBehavior - - for this command execution, or null if this command was executed in a DM. -
    getUser - Returns the relevant - - UserBehavior - - for this command execution, or null if this command was executed by a webhook. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    cacheMutableStringKeyedMap <Any> - The cache object used for this execution, which may contain data set by the current command's - defined . -
    commandCommand - Quick access to the object representing the current command. - No generic is used for this property, but subtypes will provide a separate property with a - more concrete type. -
    commandNameKey - - Key object - representing the command's name. - For chat commands, this will be the name used to execute the command, which may be an alias. -
    eventObj - - Event - - - The event object that caused this command execution. - No generic is used for this property, but subtypes will provide a separate property with a - more concrete type, generally named event. -
    sentry - - SentryContext - - Quick access to the bot's Sentry context.
    translationsProvider - - TranslationsProvider - - Quick access to the bot's translations provider.
    -
    -
    -
    diff --git a/Writerside/topics/core/Components.topic b/Writerside/topics/core/Components.topic deleted file mode 100644 index 5c4ea83f..00000000 --- a/Writerside/topics/core/Components.topic +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - -

    - Discord provides several - Message Components, - allowing for more advanced forms of user interactions. - Kord Extensions exposes these via its components system. -

    - - -

    - As Discord states, components are a framework for adding interactive elements to the messages your app or - bot sends. - Several types of components exist, and they're only available in specific contexts: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    TypeUnitsDescriptionMessages
    Action RowFive - Container for other components, up to five rows per message - or Modal. - Each row has a width of five units. - ✅ Yes✅ Yes
    ButtonOneClickable button.✅ Yes❌ No
    Select MenuFive - Dropdown menu for selecting zero, one, or multiple items. - Supports multiple data types: channels, mentionables, roles, strings, and users. - ✅ Yes❌ No
    Text InputFiveField that a user may use to input one or multiple lines of text.❌ No✅ Yes
    - - -

    - Action rows act as a container for other components, and they must be used anywhere you wish to use a - component. - You may only have up to five rows of components in any single context. -

    - - Graphic depicting an action row. It shows a row with five empty slots. - -

    - Each component has a width measured in "units." - Action rows are five units wide, and thus may contain components up to a total width of five units. - If an action row contains multiple components, there shouldn’t be any gaps between them. -

    - - Graphic depicting an example message with five empty action rows positioned below. -
    - - -

    - Buttons are clickable/tappable components, which the user may interact with to trigger an action or - open a link. - They’re the only component type with a width of one unit, and thus they’re the only type of - component that can appear multiple times in a single row. -

    - -

    - You may only add Buttons to messages. - You can't add them to . -

    - - Graphic depicting an action row with a single button and four empty slots. - - Graphic depicting an action row with three buttons and two empty slots. - -

    - Several types of buttons exist: -

    - - -
  • - Action Buttons: - Buttons which trigger an event when interacted with, - which your bot may react to. -
  • - -
  • - Disabled Buttons: - Buttons which don't do anything when interacted - with. -
  • - -
  • - Link Buttons: - Buttons which open a predefined URL in the user's - browser when interacted with. -
  • -
    -
    - - -

    - Select Menus represent dropdown menus, allowing users to select zero, one, or multiple options from a - set of 25. - Select Menus have a width of five units, meaning they take up an entire row. -

    - -

    - You may only add Select Menus to messages. - You can't add them to . -

    - - Graphic depicting an action row with a single select menu. The select menu spans all five slots, with no empty slots. - -

    - Select menus support several types of data: -

    - - -
  • Channels
  • -
  • Mentionables (channels, roles and users)
  • -
  • Roles
  • -
  • Strings
  • -
  • Users
  • -
    -
    - - -

    - Text Inputs represent editable text fields, allowing users to input arbitrary data. - Text Inputs have a width of five units, meaning they take up an entire row. -

    - -

    - You may only add Text Inputs to Modals. - You can't add them to messages. -

    - - Graphic depicting an action row with a single text input. The text input spans all five slots, with no empty slots. - -

    - Text Inputs support two different form factors: -

    - - -
  • Line: for a single line of text.
  • -
  • Paragraph: for a larger, multi-line block of text.
  • -
    - - -
    diff --git a/Writerside/topics/core/Configuration.topic b/Writerside/topics/core/Configuration.topic deleted file mode 100644 index 5f400897..00000000 --- a/Writerside/topics/core/Configuration.topic +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - Configuration - - - Learn how to configure your bot. - - - - - - - - - - Standard Options - - - - - - - - - - - - - - - - - - - - Advanced Options - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Writerside/topics/core/Events.topic b/Writerside/topics/core/Events.topic deleted file mode 100644 index a5f440d0..00000000 --- a/Writerside/topics/core/Events.topic +++ /dev/null @@ -1,411 +0,0 @@ - - - - - - -

    - To react to things happening on Discord (and within your bot), you can register event handlers in your - extensions. - This is done simply: -

    - - - override suspend fun setup() { - event<MessageCreateEvent> { - action { - if (event.message.content == "hello") { - event.message.respond("Hello!") - } - } - } - } - - -

    - The event function takes several parameters: -

    - - -
  • T: Event - the type of event to listen for.
  • - -
  • - constructor - allows you to provide a callable object (usually a constructor) that returns - an alternative implementation of EventHandler. -
  • - -
  • - body - the function receiver which binds this tho - EventHandler <T>. -
  • -
    - - -

    - The EventHandler type provides several APIs that aid in working with events. -

    - - - - - - - - - - - - - - - - - - - - - - - - -
    BuilderReceiverDescription
    action - - EventContext <T> - - - Provide the code that should be run when the event defined by T is fired. - Note: All event handlers must have an action. -
    check - - CheckContext <T> - - - Register a check that must pass for the event's action - to be run. -
    -
    - - -

    - The following functions are extension functions on the Event type, and must be called - against an Event object. - For brevity, this object will be represented by E below. -

    - - - - - - - - - - - - - - - - - - -
    FunctionDescription
    E.getLocale - Resolve the contextual locale for the given event, using the resolvers defined in - the bot's configuration. - Will default to the configured default bot locale if one can't be resolved. - This may be useful for work related to . -
    E.translate - There are a number of overloads for this function. - It allows you to translate a given string key based on the event's locale, or - another given locale. - For more information on translations, see - the dedicated documentation, - and examine the functions in your IDE for more information on their parameters. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    extension - - Extension - - - The extension this event handler belongs to. -
    kord - - Kord - - - Quick access to the bot's current Kord object. -
    sentry - - SentryAdapter - - - Quick access to the bot's Sentry adapter. -
    translationsProvider - - TranslationsProvider - - - Quick access to the bot's translations provider. -
    -
    -
    - - -

    - The API above provides an action builder, which is a receiver on the EventContext - type. - This type provides some APIs for you to use when handling an event. -

    - - - - - - - - - - - - - -
    FunctionDescription
    getLocale - Resolve the contextual locale for the current event, using the resolvers defined in - the bot's configuration. - Will default to the configured default bot locale if one can't be resolved. - This may be useful for work related to . -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    cache - - MutableStringKeyedMap <Any> - - - Data cache shared with the checks defined for the current event handler. - For more information, see the dedicated documentation. -
    eventHandlerEventHandler <T> - The current event handler object. -
    eventT: Event - The event currently being handled. -
    sentry - - SentryContext - - - Sentry context object, allowing you to provide extra context (such as breadcrumbs) to your - action. - For more information, see the dedicated documentation. -
    translationsProvider - - TranslationsProvider - - - Quick access to the bot's translations provider. -
    -
    -
    - - -

    - Kord Extensions sets the customContext property for all events (Kord or otherwise) to an empty - MutableStringKeyedMap <Any>. - This allows you to store extra data on your events as part of processing. -

    - -

    - Kord Extensions provides extension APIs to make this easier and more convenient. -

    - - -

    - The following properties are extensions on the Event type. - The current event object will be represented using E below. -

    - - - - - - - - - - - - - -
    NameTypeDescription
    E.extraDataMutableStingKeyedMap <Any> - Quick access to the properly-cast customContext property. - Prefer this instead of direct access, to avoid casting it yourself. -
    -
    - - -

    - The following functions are extensions on the StringKeyedMap <*> type. - This will be represented using S below. - Additionally, MutableStringKeyedMap <*> will be represented using M - below. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameParametersDescription
    S.getOf - T: Any, - String - - Retrieve the value for the given key, casting it to T for you. - Throws if the key is missing or the value cannot be cast. -
    S.getOfOrDefault - T: Any, - String, - T - - Retrieve the value for the given key, casting it to T for you. - Returns the provided default value if the key is missing or the value cannot be cast. -
    M.getOfOrDefault - V: Any, - T: V, - String, - T, - Boolean - - Retrieve the value for the given key, casting it to T for you. - Returns the provided default value if the key is missing or the value cannot be cast. - Allows you to additionally store the default value in the map when it is returned. -
    S.getOfOrNull - T: Any, - String - - Retrieve the value for the given key, casting it to T for you. - Returns null if the key is missing or the value cannot be cast. -
    -
    -
    -
    diff --git a/Writerside/topics/core/Exceptions.topic b/Writerside/topics/core/Exceptions.topic deleted file mode 100644 index c4abf75a..00000000 --- a/Writerside/topics/core/Exceptions.topic +++ /dev/null @@ -1,483 +0,0 @@ - - - - - - -

    - Kord Extensions provides a number of extra exception types. - These are useful for understanding precisely what went wrong in specific situations, but there are some - exceptions that provide extra functionality when writing your extensions. -

    - -

    - The global base type for extensions provided by Kord Extensions is KordExException. - This type provides no extra functions or properties, and it directly extends Exception. -

    - - -

    - The following exceptions don't necessarily represent programming errors. - Instead, they exist to implement a framework feature, and you can use them freely for their intended - purposes. -

    - - -

    - This exception is used when your extension code needs to bail for some reason, whilst - providing a message that should be relayed to the actioning user if there is one. -

    - -

    - The type for this exception is DiscordRelayedException. -

    - -

    - This exception supports the following extra properties: -

    - - - - - - - - - - - - - - -
    NameTypeDescription
    reasonKey? -

    - - Translation Key object - used to generate the reason string. -

    -
    -
    -
    - - - -

    - This exception is thrown when something goes wrong during command argument parsing. -

    - -

    - The type for this exception is ArgumentParsingException. - Additionally, this exception extends DiscordRelayedException, rather than the base - KordExException type. -

    - -

    - This exception supports the following extra properties: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    argumentArgument <*>? - The Argument object representing the argument that failed to parse. -
    argumentsArguments - The Arguments object containing the full set of arguments defined for this command. -
    parserStringParser? - The tokenizing string parser which was used to parse the given argument. - Will be null if this isn't a chat command. -
    -
    - - -

    - This exception is thrown when an attempt to register a chat command - fails. - This may happen when: -

    - - -
  • A command with the given name was registered previously.
  • -
  • A command with the given alias was registered previously.
  • - -
  • - The command was previously registered under another name. - An alias should be used instead of trying to do this. -
  • -
    - -

    - The type for this exception is CommandRegistrationException. -

    - -

    - This exception supports the following extra properties: -

    - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    nameKey - - Key object - representing the name of the command that failed to be registered. -
    reasonKey - - Key object - representing the human-readable string explaining why the command couldn't be registered. -
    - -

    - There is no equivalent exception thrown for other command types. -

    - - - -

    - This exception is thrown when an attempt to register an event handler fails. - This may happen when: -

    - - -
  • The event handler was already registered previously.
  • -
    - -

    - The type for this exception is EventHandlerRegistrationException. -

    - -

    - This exception supports the following extra properties: -

    - - - - - - - - - - - - - - -
    NameTypeDescription
    reasonString - The human-readable string explaining why the event handler couldn't be registered. -
    -
    - - -

    - This exception is thrown when a converter builder hasn't been set up properly, thus resulting in - an invalid command argument. - This usually happens when a required property is missing from the converter builder, or a property - has otherwise been configured incorrectly. -

    - -

    - The type for this exception is InvalidArgumentException. -

    - -

    - This exception supports the following extra properties: -

    - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    builderConverterBuilder <*> - The converter builder that failed to validate. - For more information, see the dedicated documentation. -
    reasonString - The human-readable string explaining why this argument failed to validate. -
    -
    - - -

    - This exception is thrown when a command fails to validate, thus resulting in an invalid command. - This happens when a command's registration requirements aren't met. - For more information, see the relevant section in - the commands framework documentation. -

    - -

    - The type for this exception is InvalidCommandException. -

    - -

    - This exception supports the following extra properties: -

    - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    nameKey? - - Key object - representing the name of the command that failed to validate. - This will be null if the command failed to validate because it wasn't given a name. -
    reasonString - The human-readable string explaining why this command failed to validate. -
    -
    - - -

    - This exception is thrown when an event handler fails to validate, thus resulting in an invalid event - handler. - This may happen when: -

    - - -
  • The event handler does not have a defined action.
  • -
    - -

    - The type for this exception is InvalidEventHandlerException. -

    - -

    - This exception supports the following extra properties: -

    - - - - - - - - - - - - - - -
    NameTypeDescription
    reasonString - The human-readable string explaining why this event handler failed to validate. -
    -
    - - -

    - This exception is thrown when an extension fails to validate, thus resulting in an invalid extension. -

    - - -

    - This is a legacy exception that was created but never implemented. - It's documented here in case it does end up being used someday. -

    - -

    - To detect an extension that failed to load, you should use the - extension state event instead. -

    -
    - - -

    - The type for this exception is InvalidExtensionException. -

    - -

    - This exception supports the following extra properties: -

    - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    clazzKClass <out Extension> - The invalid extension's class. -
    reasonString? - The human-readable string explaining why this extension failed to validate. -
    -
    -
    - - - -

    - Kord Extensions contains a number of useful that are mostly used by - . - When a parser fails to parse a value, it will throw an extension that extends the - BaseParserException type. -

    - -

    - This type provides no extra functions or properties, and it directly extends KordExException. -

    - - -

    - This is a generic exception, extended by more concrete types. - It's also thrown directly by duration parsers when: -

    - - -
  • - The parser is provided with a string containing a different number of units and values. - Duration parsers require an equal number of units and values, as bare numbers are not supported. -
  • -
    - -

    - The type for this exception is DurationParserException. -

    - -

    - This exception supports the following extra properties: -

    - - - - - - - - - - - - - - -
    NameTypeDescription
    errorKey - - Key object - representing the human-readable string explaining what went wrong. -
    -
    - - -

    - This exception is thrown when a duration parser encounters an invalid or unsupported time unit when - parsing. -

    - -

    - The type for this exception is InvalidTimeUnitException. - Additionally, this exception extends DurationParserException, rather than the base - KordExException type. -

    - -

    - This exception supports the following extra properties: -

    - - - - - - - - - - - - - - -
    NameTypeDescription
    unitString - The invalid or unsupported time unit encountered. -
    -
    -
    - diff --git a/Writerside/topics/core/Extensions.topic b/Writerside/topics/core/Extensions.topic deleted file mode 100644 index 74e224e4..00000000 --- a/Writerside/topics/core/Extensions.topic +++ /dev/null @@ -1,413 +0,0 @@ - - - - - - -

    - Once you've created your bot and - configured it to your needs, you can start adding functionality. - Kord Extensions requires functionality to be separated out into - Extensions, allowing commands and event handlers to be grouped by function and - easily loaded and unloaded. -

    - -

    - Extensions are the building blocks that make up your bot's features, and represent single units of - functionality. - For example: -

    - - -
  • - A help extension that implements - HelpProvider and contains all help-related commands and event handlers. -
  • - -
  • - A PluralKit extension that provides an API for working with PluralKit, - including code that fires custom events, and any relevant commands and event handlers. -
  • - -
  • - An anti-phishing extension that downloads and maintains a database of known - phishing domains, - and contains the relevant commands and event handlers needed to automatically moderate messages containing - them. -
  • -
    - - -

    - While you can organize your extensions in any way you wish, the following is worth keeping in mind: -

    - - -
  • -

    - Consider placing your extensions within a package named extensions. -

    - - -
  • - If the extension only requires one file or class, name it - $lt;Name$gt;Extension and place it directly within the extensions - package. -
  • - -
  • - If the extension requires multiple files or classes, create a subpackage for it within the - extensions package, named after the extension and containing it and everything - it needs. -
  • -
    - - -
  • - Extension classes should have descriptive names and end with the word "Extension." - For example, use MinecraftExtension instead of MEx or - Minecraft. -
  • - -
  • -

    - Extension names should be easy to guess, and written in lowered-kebab-case. - For example, use minecraft or log-rotation instead of - Mine_craft or LogRotation. -

    - -

    - If your extension is intended to be used by others, consider prefixing the name to distinguish - it from extensions written by other people. - For example, if your org is named Acme Corp., consider using acme-minecraft - or acme-log-rotation. -

    -
  • - -
  • -

    - Extensions should be designed with a single purpose in mind, and shared functionality (for - example, APIs) should be extracted into an interface or abstract class. -

    - -

    - Don't put all of your bot's functionality into a single extension, especially if it has a lot - of features. - For example, use ModerationExtension, MinecraftExtension and - LogRotationExtension instead of just MyBotExtension. -

    -
  • - -
  • -

    - While your extension class can be laid out however you prefer, we suggest the following order: -

    - - -
  • - At the top, simple variables and constants, followed by variables and constants with custom - getters and setters. -
  • - -
  • - Your setup function, followed by any other functions your extension needs. - It's also generally a good idea to split up public API and private functions. -
  • - -
  • - Any command argument classes used by your extension, marked as inner classes. - Alternatively, it may be useful to factor these out into separate classes in their own files. -
  • - -
  • - Any modal form classes used by your extension, marked as inner classes. - Alternatively, it may be useful to factor these out into separate classes in their own files. -
  • - - - - class MyExtension : Extension() { - // Simple variables and constants - override val name: String = "my" - - val chatCommands: ChatCommandRegistry by inject() - - // Setup function - override suspend fun setup() { } - - // Public API - suspend fun myAction() { } - - // Private functions - private suspend fun doAction() { } - - // Command argument classes - inner class CommandArgs : Arguments() { - - } - - // Modal form classes - inner class MyForm : ModalForm() { - - } - } - - - -
    - - -

    - All extensions must extend the Extension type. - This type provides most of the logic behind your extensions, and it implements Kord Extensions' - KoinComponent types, giving you direct access to Koin's API. -

    - -

    - All extensions must implement the following: -

    - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    nameString - Human-readable short extension name. - This should be lowered-kebab-case and must be unique, as it's used for logging and by - parts of the ExtensibleBot API to index and retrieve loaded extensions. -
    setupsuspend () -> Unit - This function is called when your extension is set up, and it should be used to register all of - its commands, event handlers, and other functionality. -
    - - -

    - While you may be able to access the bot's - Kord instance in - your setup function, - it is not safe to use during extension setup. - This is because extensions are set up before the bot attempts to connect to Discord, meaning you will - no data will be available from Discord until later. -

    - -

    - Instead, consider creating an event handler for Kord's - - ReadyEvent - , - which will be fired when the bot has connected to Discord. -

    - -

    - Note: Once the bot has connected, feel free to use the bot's - Kord instance as - you please. - This includes within event handler and command action blocks, as these will not be called - unless the bot is connected to Discord. -

    -
    - -

    - To react and respond to actions taken on Discord, you'll need to register the relevant abstractions in your - setup function: -

    - - -
  • - Event handlers, which allow your bot to react to all kinds of things as they - happen. -
  • - -
  • - Commands, which may be executed by users that share servers with your bot. -
  • -
    - -

    - For more information, please see the relevant links above. -

    -
    - - -

    - The Extension type provides a fairly comprehensive API, which you can use to build out your - bot's functionality. - It also extends KoinComponent, providing easy access to other parts of Kord - Extensions as necessary. -

    - - -

    - For the sake of brevity, the below tables do not provide an exhaustive list of APIs. - You can find more information on the APIs that have been omitted throughout this documentation, - including: -

    - - -
  • -
  • -
  • -
  • -
  • -
    -
    - - - - - - - - - - - - - - - - - - - -
    FunctionDescription
    setup - Extension setup function, as explained above. -
    unload - If your extension requires tear-down or cleanup steps to be run when it's unloaded, override - this function. - Called whenever the extension is disabled. -
    -
    - - -

    - The following properties are marked open and are intended to be overridden by extensions. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDefaultDescription
    allowApplicationCommandsInDMsBooleantrue - Whether application commands registered by this extension should - be allowed in DMs by default. - May be overridden or set by your setup function. -
    intentsMutableSet <Intent>{ } - Intents required by this extension, which will be automatically requested at bot startup. - This is populated automatically by event handlers and chat commands, but you can always add - other required intents if needed. -
    nameString - Human-readable short extension name, as explained above. -
    - -

    - The following properties are set automatically, and you shouldn't need to modify them. -

    - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    loadedBoolean - Whether the extension is currently loaded. - This is a short-hand reference to state. -
    stateExtensionState - The extension's loading state, which describes which state of its lifecycle it's currently in. -
    - -

    - The following properties provide quick access to various parts of Kord Extensions, via - Koin. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameType
    applicationCommandRegistryApplicationCommandRegistry
    botExtensibleBot
    chatCommandRegistryChatCommandRegistry
    kordKord
    -
    - - diff --git a/Writerside/topics/core/Getting-Started.topic b/Writerside/topics/core/Getting-Started.topic deleted file mode 100644 index fd17c7b2..00000000 --- a/Writerside/topics/core/Getting-Started.topic +++ /dev/null @@ -1,112 +0,0 @@ - - - - - Getting Started - - -

    - Plugin ID: - dev.kordex.gradle.kordex -

    - -

    - Plugin Version: - %plugin-version% -

    - -

    - Minimum JDK: - %java-version% -

    -
    - -

    Before getting started, please ensure you have the following installed:

    - - -
  • Git
  • -
  • -

    - Java Development Kit (version %java-version% or later) -

    - - - Ensure that the version of Java you install is - compatible with Gradle. - It can take a while for Gradle to add support for the latest versions of Java, so this is important! - -
  • -
    - - -

    - If you're starting a new project, - we highly recommend that you use - the template project. -

    - -

    - If you have a GitHub account, you can click the "Use this template" button to quickly create a new - repository using the contents of the template project. - This will get you started quickly, with a basic bot provided for you to play with. -

    - -

    - Alternatively, you can clone or download the repository and push it up to any repository host with your - tooling of choice. -

    -
    - - - -

    - We recommend you use the KordEx Gradle plugin, - as briefly covered below. - However, we only support using this plugin in Kotlin Gradle build scripts. -

    - -

    - If you don't want to use the plugin, you're using Groovy build scripts, or you're using a different - build tool, - please see this page - to learn how to manually set up your project. -

    -
    - -

    - First, add the KordEx Gradle plugin to your build.gradle.kts. -

    - - - plugins { - id("dev.kordex.gradle.kordex") version "%plugin-version%" - } - - -

    - Then, configure it as needed. - At minimum, you should specify your bot's main class, via the mainClass property in the - bot builder. - You may also wish to configure the level. -

    - - - kordEx { - bot { - dataCollection(DataCollection.Standard) - - mainClass = "my.package.here.FileWithMainFunctionKt" - } - } - - - - -

    - For more information on the Gradle plugin, what it does and how to configure it, please see - the KordEx plugin documentation. -

    -
    -
    diff --git a/Writerside/topics/core/Interactions.topic b/Writerside/topics/core/Interactions.topic deleted file mode 100644 index 9ac767ca..00000000 --- a/Writerside/topics/core/Interactions.topic +++ /dev/null @@ -1,317 +0,0 @@ - - - - -

    - To give users richer ways to interact with your bot (when compared to sending messages), Discord provides a - system known as - Interactions. - This system is designed to allow for public and private responses to rich Interaction types. -

    - -

    - Interactions are an important concept to understand, as they will make up the bulk of user-triggered actions - that your bot needs to deal with. -

    - - -

    - Your bot receives an - Interaction in - response to one of the following actions taken on Discord: -

    - - -
  • - A user executes an application command belonging to your bot. -
  • - -
  • - A user enters data into a slash command argument that supports - auto-completion. -
  • - -
  • - A user makes use of a message component, such as an interactive button - or select menu. -
  • - -
  • - A user submits a modal form. -
  • -
    - -

    - When an Interaction is received from Discord, Kord will fire an event that extends - - InteractionCreateEvent - . - These events provide an - - interaction - property, allowing you to respond directly to the Interaction. -

    - - - While it will sometimes be necessary to respond to these events directly, most bots will only need to use - Kord Extensions' built-in abstractions. - For more information on those abstractions, see the list above. - - -

    - When responding to an Interaction, your response may be one of the following main types: -

    - - -
  • Ephemeral, meaning only the user that triggered the Interaction is able to see the response.
  • - -
  • - Public, meaning everyone with access to the current channel is able to see the response and the user - that triggered the Interaction. -
  • -
    -
    - - -

    - Internally, Discord's Interaction system is a bit of a mess. - While it is technically possible to work with the Interaction API directly via some of the objects - exposed by Kord Extensions (especially via the Unsafe Module), the built-in - abstractions are provided to lower the risk of unexpected behavior. -

    - -

    - Known instances of unexpected behavior include: -

    - - -
  • - Responding to an Interaction using the wrong response type, after an initial response has been made. - Even though the API technically allows you to respond using mixed response types, Discord will - often ignore the type you provided if you aren't consistent. -
  • - -
  • - Responding to an ephemeral Interaction after editing the initial response, which may result in a public - response in some situations. -
  • -
    - -

    - Additionally, as the Interaction system is internally implemented using Discord's webhook system, changes - that Discord makes to how webhook messages work may also affect your Interactions. -

    - -

    - For example, a 2023 change to webhook permissions was made because slash command responses were able to - mention @everyone when they otherwise shouldn't be able to. - In this instance, Discord normalized webhook permissions to match those of the user who created the webhook. - This fixed the problem with slash commands but limited the emoji that may be sent using a webhook to those - from guilds the webhook creator is present on. -

    -
    - - -

    - Discord supports two main types of Interaction response — ephemeral and public. - Ephemeral responses are only visible to the user that triggered the Interaction, whereas public responses - are visible to everyone that has access to the current channel. -

    - -

    - Because Discord's Interactions can behave strangely if they aren't used how Discord expects them to be, - Kord Extensions provides specific context types that help you to avoid unexpected behaviors. - These types expect you to work with one of the main Interaction types, warning you when you attempt to do - something that might not do what you expect. -

    - -

    - These context types inherit the InteractionContext type, and are split by Interaction response - types: -

    - - -
  • EphemeralInteractionContext for ephemeral Interaction responses.
  • -
  • PublicInteractionContext for public Interaction responses.
  • -
    - -

    - These types provide an identical API surface, allowing you to respond to Interactions easily without - accidentally specifying the wrong type of response. - The InteractionContext type takes several generic type parameters, with the following used - in the tables below: -

    - - -
  • - ResponseBehavior - A subtype of Kord's - - InteractionResponseBehavior - type, corresponding with the current Interaction response type. -
  • -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameReceiverDescription
    editInteractionResponseModifyBuilder - Edit the first Interaction response, regardless of whether (or how many times) this - Interaction has been responded to. - May be called before a response has been sent, as the first Interaction response is always - a "bot is thinking" message until this function - (or one of the response functions) is called. -
    respondFollowupMessageCreateBuilder - Respond to the Interaction with a follow-up matching the current Interaction response type. -
    respondOppositeFollowupMessageCreateBuilder - Respond to the Interaction with a follow-up - with the opposite type to the current Interaction response type. - While Discord's API allows you to do this, - it will rarely do what you expect. - This function is provided only for advanced use-cases that need it. -
    - Paginator Builders -
    editingPaginatorPaginatorBuilder -

    - Convenience function, allowing you to easily create a button-based - paginator - by editing the first response to this Interaction. - The following parameters are supported: -

    - - -
  • - defaultGroup = "" - The default paginator group. -
  • - -
  • - locale = null - The locale to use for this paginator's buttons. - In a command or component context, you'll want to provide the result of - getLocale() here. -
  • -
    - -

    - Note: - This will not send the paginator automatically. - You'll need to call the send function on this function's return value to - send it. -

    - -

    - For more information on paginators (and the paginator builder), see - the dedicated documentation. -

    -
    respondingPaginatorPaginatorBuilder - -

    - If you're working within an ephemeral Interaction context, then it's important to note - that - this function will always result in a public response. - This is due to a Discord API limitation, wherein Discord has made it impossible to edit - ephemeral Interaction follow-ups. -

    - -

    - If you want to use a paginator in your ephemeral Interaction responses, you will need - to use editingPaginator instead. -

    -
    - -

    - Convenience function, allowing you to easily create a button-based - paginator in an Interaction follow-up message. - The following parameters are supported: -

    - - -
  • - defaultGroup = "" - The default paginator group. -
  • - -
  • - locale = null - The locale to use for this paginator's buttons. - In a command or component context, you'll want to provide the result of - getLocale() here. -
  • -
    - -

    - Note: - This will not send the paginator automatically. - You'll need to call the send function on this function's return value to - send it. -

    - -

    - For more information on paginators (and the paginator builder), see - the dedicated documentation. -

    -
    -
    - - - - - - - - - - - - - - - -
    NameTypeDescription
    interactionResponseResponseBehavior - The current Interaction response object that this context is working with. -
    -
    -
    -
    \ No newline at end of file diff --git a/Writerside/topics/core/Internationalization.topic b/Writerside/topics/core/Internationalization.topic deleted file mode 100644 index b85e2d54..00000000 --- a/Writerside/topics/core/Internationalization.topic +++ /dev/null @@ -1,812 +0,0 @@ - - - - - - - -

    - While some smaller bots are only designed with one language in mind, many larger bots will need to support - guilds and users using a variety of languages and locales. -

    - -

    - To help with this, Kord Extensions was designed with internationalization (i18n) in mind, and it provides - many relevant tools and systems. -

    - - -

    - Before you can begin to localize your bots, you'll need to do some planning: -

    - - -
  • -

    - Decide on a default locale. -

    - -

    - This will be US English for most bots, - but you're free to use something else if there's a more suitable locale. -

    -
  • - -
  • -

    - Consider the depth of your localization work. -

    - -

    - Making bots suitable for use in other locales requires extra consideration. - Different languages and locales require very different approaches, and it's important to consider - what may change between them. - For example: -

    - - -
  • - Number formats, including things such as currencies, and dates and times. -
  • - -
  • - Units, such as those used for weights, volumes and temperatures. -
  • - -
  • - Text direction for right-to-left languages. -
  • -
    - -

    - This isn't an exhaustive list — it's important to do research and chat with folks that speak your - bot's target locales. -

    - - -
  • -

    - Consider what external tooling you'll need. -

    - -

    - Automatic translation tools like DeepL and Google Translate can be useful in a small number of - situations, - but their output is usually inaccurate or otherwise unsuitable for use in your translations. -

    - -

    - Instead, consider crowdsourcing translations using tools like - Weblate and - Crowdin — and get your community involved! - There are many tools that support Java Properties files and the ICU Message Format, and lots of them - can integrate with your version control system. - We recommend shopping around and finding something that suits your project and community. -

    -
  • - - -

    - Localizing a bot is often a pretty large undertaking, but it can be very worthwhile! -

    -
    - - -

    - Kord Extensions approaches i18n in a rather specific way, so it's important to understand a few things. -

    - - -

    - Locales represent specific geographical, political, or cultural regions. - In the context of programming, Locale objects are used to represent a set of language, - script, region, variant, and extensions, which can be used together to format all kinds of data or - to look up translations. -

    - -

    - Discord has a predefined set of supported locales, - which may be found here. - This list of locales represents those supported by the Discord client and isn't an exhaustive list - of all possible locales. -

    - -

    - Kord Extensions uses locale resolvers to find the relevant Locale object for any given - event. - You can configure them in - your bot's i18n builder. -

    - -

    - You may want to consider writing a custom resolver and allowing users to configure their preferred - locale using commands if you want to support locales other than those in the Discord client. -

    -
    - - - - Translation bundles are UTF-8! - -

    - Prior to Java 9, properties files were assumed to be written using the ISO-8859-1 - encoding. - For this reason, some editors may assume that all properties files are encoded using - ISO-8859-1, and you'll need to reconfigure them. - We recommend using Editorconfig for this, which JetBrains - tools have first-party support for. -

    - -

    - Kord Extensions assumes all translation bundles use properties files encoded using - UTF-8. You will run into problems if you use any other encoding! -

    -
    - -

    - The most important unit in the i18n system is the translation bundle. - By default, Kord Extensions uses properties-based Java Resource Bundles, where translations are stored - in a set of .properties files with similarly prefixed names. - Translation bundles are represented using Bundle objects in code. -

    - -

    - The first thing you'll need to do is pick a name for your translation bundle. - The name should contain two parts — the name of the containing folder, and the bundle itself — split - using a dot or period (.). - This name is used to locate the resource bundle in your bot's resources, within the - translations/ base directory. -

    - -

    - For example, mybot.strings refers to a resource bundle named strings stored - within translations/mybot/ in your bot's resource files. -

    - -

    - Your translation bundle must contain a base translation file, with a set of default translations. - This file should be named without a locale, so in the above example, it'd be named - strings.properties. - The translations in this file will be used as a fallback when a translation is missing. -

    - -

    - All other translations must be placed in translation files containing the relevant locale code. - For example, the locale code for German is de, so a translation file for German in our - example would be named strings_de.properties. -

    - -

    - For more information on the properties file format, - see this page on Wikipedia. -

    -
    - - -

    - A translation key represents a single translation. - When you store translations in a properties file, they're referred to by name. - For example: -

    - - - command.about.name=about - command.about.description=Learn about this bot. - - -

    - In the above properties file, command.about.name is a translation key, referring to a - specific translation. -

    - -

    - Translation keys are represented using Key objects in code. - These objects may also contain bundle and locale information with predefined replacement values, as - explained in below. -

    -
    -
    - - -

    - To quickly recap the previous section: Translations are (by default) written to UTF-8-encoded - .properties files, grouped together into a translation bundle. - These translations are referred to using a combination of the bundle name and translation key. -

    - -

    - Translations are written using - ICU Message Format, a - standard message formatter provided by the Unicode Consortium. - This format provides a few useful (but understated) tools that you can use to write better contextual - translations. -

    - - -

    - For translation keys that support an empty value for specific locales, you may provide a special - value for the translation. - This value consists of three empty set characters (∅∅∅) and should contain nothing else. -

    - -

    - Note: This value is not supported for all translations. - You can use it in the following places: -

    - - -
  • - Chat command aliases -
  • - -
  • - Ignored words in duration parsers -
  • - -
  • - Initial values for text input widgets -
  • - -
  • - Reset words for the PluralKit module -
  • -
    -
    - - -

    - ICU Message Format allowed for two types of placeholders: -

    - - -
  • -

    - Ordinal placeholders: Hello, {0}! -

    - -

    - Ordinal placeholders are filled using an array of arbitrary values. - Array indices are specified by placing them within curly braces. -

    - -

    - For example, to specify the first element of the array, you would use {0}. -

    -
  • - -
  • -

    - Named placeholders: Hello, {name}! -

    - -

    - Named placeholders are filled using a string-keyed map of arbitrary values. - The map's keys are specified by placing them within curly braces. -

    - -

    - For example, to specify the name key of the map, you would use {name}. -

    -
  • -
    - -

    - Mixed placeholder types are unsupported, so you'll need to pick one or the other. - We recommend using named placeholders, as they result in more readable - translations. -

    - -

    - Placeholders are automatically formatted using the default ICU formatter for the corresponding type. - However, you can further format placeholders by supplying a style modifier. -

    -
    - - -

    - ICU Message Format provides several useful modifiers you can use to format numeric types. - The following examples use 0 in place of any ordinal or named placeholder you may use. -

    - - - -
  • Automatic: {0, number} (12.5)
  • -
  • Integer: {0, number, integer} (13)
  • -
  • Currency: {0, number, currency} ($12.50)
  • -
  • Percent: {0, number, percent} (1,250%)
  • - -
  • - Custom: {0, number, ###,###.###} (uses - - Java's DecimalFormat) -
  • -
    -
    - - - -
  • Automatic: {0, date} (Sep 22, 2024)
  • -
  • Short: {0, date, short} (9/22/24)
  • -
  • Medium: {0, date, medium} (Sep 22, 2024)
  • -
  • Long: {0, date, long} (September 22, 2024)
  • - -
  • - Full: {0, date, full} (Sunday, September 22, 2024) -
  • - -
  • - Custom: {0, date, dd/MM/yyyy} (uses - - Java's SimpleDateFormat) -
  • -
    -
    - - - -
  • Automatic: {0, time} (4:30:00 PM)
  • -
  • Short: {0, time, short} (4:30 PM)
  • -
  • Medium: {0, time, medium} (4:30:00 PM)
  • -
  • Long: {0, time, long} (4:30:00 PM GMT+1)
  • -
  • Full: {0, time, full} (4:30:00 PM GMT+1)
  • - -
  • - Custom: {0, time, kk:mm:ss} (uses - - Java's SimpleDateFormat) -
  • -
    -
    - - -

    - Durations: {0, duration} - for formatting numbers - (representing seconds) as simple durations. - For example, 123 becomes 02:03 in English. -

    - -

    - Ordinals: {0, ordinal} - for formatting numbers - with ordinal suffixes. - For example, 3 becomes 3rd in English. -

    - -

    - Spellouts: {0, spellout} - for formatting numbers - as text. - For example, 100 becomes one hundred in English. -

    - -

    - For more advanced number formatting, see - - RuleBasedNumberFormat - . -

    -
    -
    - - -

    - Selections are a more advanced formatting construct that allows you to provide different translations - based on a set of rules. - The following examples use 0 in place of any ordinal or named placeholder you may use. -

    - - -

    - Keyword selections allow you to provide a different translation based on the contents of a - placeholder. - This is done by configuring the select modifier with matching patterns. -

    - -

    - A matching pattern uses the form of keyword {translation}, and you may provide multiple - patterns separated by spaces. - The other pattern is used when no other keywords match the placeholder, and it must - always be included in your keyword selection. -

    - -

    - Translations may themselves contain placeholders, selectors, and other patterns. -

    - -

    - For example, to pick a pronoun based on the given gender, you could do something like this: -

    - - - {0, select, male {he} female {she} doll {it} other {they}} - -
    - - -

    - Plural selections allow you to provide a separate translation based on the value of a numeric - placeholder. - This is done by configuring the plural modifier with various options and some matching - patterns. -

    - -

    - Plural selections support a number of options, provided using the form name:value - before the matching patterns: -

    - - -
  • - offset - By providing a number, it will be subtracted from the placeholder's value - before being used to match the given cases. -
  • -
    - -

    - A matching pattern uses the form of case {translation}, and you may provide multiple - patterns separated by spaces. - The other case is used if no other keywords match the placeholder, and it must always - be included in your keyword selection. - The following default cases are supported: -

    - - -
  • zero - Matched when the value is 0.
  • -
  • one - Matched when the value is 1.
  • -
  • two - Matched when the value is 2.
  • -
  • few - Matched when you'd normally say "a few" to represent the value.
  • -
  • many - Matched when you'd normally say "many" to represent the value.
  • -
    - -

    - You can also match a specific value by prefixing it with =. - For example, to match a value of 7, you could use =7. -

    - -

    - Translations may themselves contain placeholders, selectors, and other patterns. - Additionally, providing a # in the translation will insert the value of the - placeholder. -

    - -

    - As an example, if you were hosting a party, you might do something like this: -

    - - - {guests, plural, offset:1 - zero {{host} does not throw a party.} - one {{host} invites {guest} to the party.} - two {{host} invites {guest} and one other person to the party.} - other {{host} invites {guest} and # other people to the party.} - } - -
    -
    -
    - - -

    - At its most basic, the translations system only requires you to provide a Key object to a - translatable property. - The Key objects are instances of an immutable data class, which can contain the following - information: -

    - - -
  • A String representing a translation key in a bundle.
  • -
  • A Bundle representing the corresponding translation bundle
  • -
  • A Locale representing the locale to translate this Key to.
  • -
  • A pre-defined array of ordinal placeholders or map of named placeholders.
  • - -
  • - A setting (presetPlaceholderPosition) which defines whether to place pre-defined ordinal - placeholders before or after the placeholders provided to a translate call. -
  • - -
  • -

    - A setting (translateNestedKeys) which defines whether nested Key objects - in translation placeholders should also be translated before use. -

    - -

    - Note: This only supports a single level of nesting. -

    -
  • -
    - - -

    - If you're using the KordEx Gradle plugin, you can - configure it to generate translation classes - from your translation bundle. -

    - -

    - This is the recommended approach and will result in a tree of translation classes containing generated - Key objects that you can reference in your code. - If you can't use the Gradle plugin, you can find a CLI tool and an API - in this GitHub repository, to integrate with your - own tooling. -

    - -

    - Alternatively, Key objects can be manually constructed as required, or created from - String keys using the - String.toKey(bundle?, locale?, presetPlaceholderPosition?, translateNestedKeys?) - extension function. -

    -
    - - -

    - Key objects are immutable, but they provide several functions that allow you to configure - cloned objects. -

    - -

    - To create a copy of the Key with a bundle or locale, you can use the - withBundle, withLocale or withBoth functions, or with the - withContext function to copy the locale from a command context. - Creating a copy without either a bundle or locale can be done by calling the corresponding - without functions. -

    - -

    - To create a copy with more pre-defined placeholders, use the withOrdinalPlaceholders - or withNamedPlaceholders functions. - You can create a copy without pre-defined placeholders by calling the corresponding without - functions, or create a copy with a set of filtered placeholders using the corresponding - filter functions. -

    - -

    - To create a copy with updated settings, use the withPresetPlaceholderPosition or - withTranslateNestedKeys functions. -

    -
    - - -

    - When you find a property or function expecting a Key object, it's enough to configure the - key as explained above and use it directly. - For situations where you need to translate a Key yourself, you'll need to use one of - the provided translation functions. -

    - -

    - The translate and translateArray functions can be used to translate the - current Key with a set of ordinal placeholders. - If any pre-defined ordinal placeholders are present, they'll be used based on the - presetPlaceholderPosition setting. -

    - -

    - The translateNamed function can be used to translate the current Key with a - set of named placeholders. - If any pre-defined named placeholders are present, they'll be used based on the - presetPlaceholderPosition setting. -

    - - - If you call the translate function without any placeholders, and you have pre-defined - named placeholders set, this function will call translateNamed for you. - - -

    - All translation functions include a version suffixed with Locale, which will use the - given locale for translation if the Key doesn't already have a locale set. -

    -
    - - -

    - To better illustrate how you might use translation keys, consider this example: -

    - - - // Assume we're in an Extension's `setup` function - - publicSlashCommand { - // Name and description properties take Keys directly. - name = Translations.Commands.About.name - description = Translations.Commands.About.description - - action { - respond { - embed { - // Translate keys for String properties. - title = Translations.Embeds.About.title - .withContext(this@action) - .translateNamed("botName" to "My Bot") - - description = Translations.Embeds.About.description - .withContext(this@action) - .translateNamed("triggeringUser" to user.mention) - } - } - } - } - -
    -
    - - -

    - In addition to the normal translation bundles explained above, it's possible to override - translations provided by other bundles. - You can do this by creating your own translation bundle files, suffixing _override to the - bundle name. -

    - -

    - For example, to override the default translations in the kordex.strings bundle, you - could create translations/kordex/strings_override.properties, and add the keys you - wish to override. -

    - - -

    - Because of the complexities involved with loading translations from plugins, please note that - override bundles may not work quite as expected when they exist in a different class-loader - from the original bundle. -

    - -

    - We generally recommend against overriding translations unless absolutely necessary. - If you notice an issue with a translation, please report it to the relevant project instead - if possible. -

    -
    -
    - - -

    - A number of locale-aware formatters are available for formatting rich types into human-readable strings. -

    - - -
  • - DateTimePeriod.format(Locale) - uses - - ICU4J Measures to create a duration-like string. -
  • -
    -
    - - -

    - A number of locale-aware parsers are available for parsing strings into rich types. - These parsers are cached when appropriate and allow you to convert various values without worrying about - writing translations yourself. -

    - - -
  • -

    - BooleanParser - a string parser which can parse truthy and falsey values from strings - in a locale-aware manner. - This parser is used by the boolean converters, and it's also used by - String.parseBoolean(Locale). -

    - -

    - The relevant translation keys are utils.string.false and - utils.string.true, and multiple comma-separated values are supported for each key. -

    - -

    - If a value can't be parsed, this parser returns null. -

    -
  • - -
  • -

    - ColorParser - a string parser which can parse Discord branding colours from strings in - a locale-aware manner. - This parser is used by the color converters and it supports black, blurple, fuchsia, green, red, - white, and yellow. -

    - -

    - The relevant translation keys are utils.colors.black, utils.colors.blurple, - utils.colors.fuchsia, utils.colors.green, utils.colors.red, - utils.colors.white, and utils.string.yellow, and multiple - comma-separated values are supported for each key. -

    - -

    - Translations for blurple include "purple," and translations for fuchsia include "pink." -

    - -

    - If a value can't be parsed, this parser returns null. -

    -
  • - -
  • -

    - DurationParser - a string parser which can parse DateTimePeriod objects - from strings in a locale-aware manner. - This parser is used by the duration converters. -

    - -

    - This (surprisingly complex) parser supports positive and negative durations specified in a number - of forms. - Bare numbers are not supported. -

    - - -
  • Long: 1 day, 2 hours, 3 minutes, 4 seconds
  • -
  • Short: 1d2h3m4s
  • -
    - -

    - All supported forms may be written right-to-left or left-to-right, as long as they're written - consistently. -

    - -

    - The relevant translation keys are utils.units.day, utils.units.week, - utils.units.month, utils.units.year, utils.units.hour, - utils.colors.minute, and utils.string.second, and multiple - comma-separated values are supported for each key. -

    - - -
    - - -

    - Kord Extensions provides translations for multiple Kord types, which may be useful for display to users - on Discord. - This is exposed via toTranslationKey and translate extension functions against - the following types: -

    - - -
  • NsfwLevel - translation keys starting with nsfwLevel.
  • -
  • Permission - translation keys starting with permission.
  • -
    -
    - - -

    - A number of miscellaneous utilities for working with the translations system are available. -

    - - -
  • - EMPTY_KEY - a Key object referencing the empty string, useful as a default - value in some situations (such as paginators with a single group). -
  • - -
  • - EMPTY_VALUE_STRING - a special string value representing an "empty" or "missing" value. - This is a standard "nothing" value, and you should check for it when you need one. - The string contains three empty set characters (∅∅∅). -
  • - -
  • - SupportedLocales - a collection of Locale objects representing locales with partial or - full translations bundled with Kord Extensions. -
  • -
    -
    - - -

    - While we recommend sticking with the bundled ResourceBundleTranslations provider, we - recognize that there may be cases where the default provider is unsuitable. -

    - -

    - In situations like that, you can extend the ResourceBundleTranslations or - TranslationsProvider types, and configure - your bot's translation provider. -

    -
    -
    diff --git a/Writerside/topics/core/Registries.topic b/Writerside/topics/core/Registries.topic deleted file mode 100644 index 7809562c..00000000 --- a/Writerside/topics/core/Registries.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/core/The-Bot.topic b/Writerside/topics/core/The-Bot.topic deleted file mode 100644 index 0d22834c..00000000 --- a/Writerside/topics/core/The-Bot.topic +++ /dev/null @@ -1,272 +0,0 @@ - - - - -

    - At the core of your bot is the ExtensibleBot class. - This class is the most important part of Kord Extensions, as the jumping-off point for your bot. - Among other things, it dispatches commands and event handlers, and keeps track of extensions. - It also contains several useful APIs. -

    - - -

    - Before you do anything else, you'll need to create a bot via the ExtensibleBot factory - function. - This function is a builder that always requires a Discord bot token parameter. The builder can then be - used to configure the bot. -

    - - - val bot = ExtensibleBot(TOKEN) { - applicationCommands { - defaultGuild(envOrNull("TESTING_GUILD")) - } - - extensions { - add(::MyExtension) - - help { - color { getRandomColor() } - } - } - - hooks { - beforeKoinSetup { - loadModule { - single { Database() } bind Database::class - } - } - } - - @OptIn(PrivilegedIntent::class) - intents { - +Intent.GuildMembers - +Intent.MessageContent - } - } - -
    - - -

    - The ExtensibleBot builder contains your bot's configuration, which is split up into a series - of builder functions. - For more information on what options are available and how to use them, see - the Configuration section. -

    -
    - - -

    - The ExtensibleBot type exposes a number of APIs that may be helpful for advanced use-cases. - If you'd like to use them, you can get access to the current object via . -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    FunctionParametersDescription
    close - Stop the bot by shutting down the Kord instance and stopping the Kord Extensions - Koin context. - A bot that has been closed can no longer be used, and a new bot must be constructed. - This function exists to close and clean up the bot, so a new one may be constructed. -
    on - Boolean, - CoroutineScope, - suspend T.() -> Unit - - Manually subscribe to an event. - Most developers will never need to use this directly. - If you can register an event handler in an Extension, - then that's a far better option. -
    send - - Event - - - Send an event to the handlers listening for it. - You can use this to send a custom event to be acted upon elsewhere. - If possible, use the KordExEvent base type instead of - Kord's - - Event - , as it handles filling out the parameters for you. -
    start - Start up the bot and log into Discord. - Blocks the current coroutine until the bot is shut down. -
    startAsync - Start up the bot and log into Discord. - Launches the start function via Kord's coroutine scope, instead of blocking. - Can be used to start the bot from synchronous code. -
    stop - Stop the bot by telling the Kord instances to log out of Discord. - This will leave the Koin context intact, so the bot may be restarted - later. -
    - -

    - The following functions are all related to extension management. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    FunctionParametersDescription
    addExtension() -> Extension - Manually install an extension by constructing and loading it. - This is only exposed for advanced use-cases, and you should prefer - - loading extensions via the bot's configuration - - instead where possible. -
    findExtensionreified T - Find the first loaded extension that is an instance of (or extend) T. -
    findExtensionsreified T - Find all loaded extensions that are instances of (or extend) T. -
    loadExtensionString - Reload a previously loaded extension by name. - If the extension isn't found or is already loaded, nothing happens. -
    removeExtensionString - Unload and remove a registered extension by name. - This will prevent the extension from being loaded in the future. - If the extension isn't found or loaded, nothing happens. -
    unloadExtensionString - Unloaded a currently installed extension by name. - If the extension isn't found or loaded, nothing happens. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    PropertyTypeDescription
    settingsExtensibleBotBuilder - The builder used when configuring the bot. - Provided as a way to access the user-supplied configuration, excluding the bot token. -
    eventsSharedFlow <Any> - Shared flow which combined the events sent by both Kord and Kord Extensions. -
    eventPublisherMutableSharedFlow <Any> - Mutable shared flow used for event publishing. - For most use-cases, you should use the send function instead of accessing this - directly. -
    -
    -
    -
    diff --git a/Writerside/topics/core/Utilities.topic b/Writerside/topics/core/Utilities.topic deleted file mode 100644 index 9152b41e..00000000 --- a/Writerside/topics/core/Utilities.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/core/commands/Application-Commands.topic b/Writerside/topics/core/commands/Application-Commands.topic deleted file mode 100644 index 39bf50d4..00000000 --- a/Writerside/topics/core/commands/Application-Commands.topic +++ /dev/null @@ -1,241 +0,0 @@ - - - - -

    - Application commands are a special type of command that makes use of Discord's interaction system. - Depending on the type, these commands are displayed in the Discord client in one of these ways: -

    - - -
  • - : Above the chat input box when a user types a slash (/) - character. -
  • - -
  • - : In a menu named "Apps" displayed when a user - right-clicks or taps-and-holds on a message. -
  • - -
  • - : In a menu named "Apps" displayed when a user - right-clicks or taps-and-holds on a username or avatar. -
  • - - -

    - Before diving into working with these command types, it's important to understand - what Discord interactions are and how they work. -

    - - -

    - When you register an application command, the builder function will return an object that extends the - ApplicationCommand type. This type extends the Command type, providing access - to basic data and functionality specific to application commands. -

    - -

    - The intended public API is described below. - However, it's possible to extend this type (and the other command types) - if you want to implement your own customized command types. -

    - -

    - The ApplicationCommand type takes a generic type parameter based on the - InteractionCreateEvent type, which is provided by the classes that extend - ApplicationCommand. - This generic type parameter will be referred to as E below. -

    - -

    - The following API definitions do not include the APIs provided by any extended supertypes. - For more information on the command API, see . -

    - - - - - - - - - - - - - - - -
    NameReceiverDescription
    checkCheckWithCache <E> - Register a check that must pass for the command's action to be run, which is - defined within the classes that extend ApplicationCommand. - For more information on checks, see the dedicated documentation. -
    -
    - - - - - - - - - - - - - - - - - - - - - - -
    NameArgumentsDescription
    guildSnowflake? - Specify a specific guild to register this application command to. - Guild-specific commands are only available in a single guild, and will not work in DMs with - the bot. - Provide null to clear the registration guild. -
    requirePermissionvararg Permission - Specify one or more permissions that a user must have before they may execute this command by - default. - Note: This setting is simply a default value that may be - configured in the "Integrations" tab, within the guild's settings in the Discord client. - It is not enforced by the command framework — use a check for that. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    - Required Properties -
    nameKey - Required: - - Key object - representing the command's name, which is used to invoke the command - and is displayed on Discord. - Your command's name must follow - - Discord's naming requirements. -
    - Optional Properties -
    allowByDefaultBoolean - Whether to allow all members of a guild to execute this command by default. - When set, this property will overwrite any permissions provided by the - requirePermission function - Returns true if no required permissions have been set using - requirePermission. -
    allowInDmsBoolean - Whether to allow this command to be used in a DM with the bot. - Always returns false if a registration guild has been set via the - guild function, regardless of whether you modify this property's value yourself. -
    -
    -
    - - -

    - When an application command is executed, the provided action block is run. - This block is a receiver function against a subtype of the ApplicationCommandContext type, - which itself is a subtype of the CommandContext type. - This type provides an API that allows you to work with the command's execution data and respond to the - user. -

    - -

    - Application command context objects always extend one of the - interaction context types, - unless you're using a command from the unsafe module. -

    - -

    - The following API definitions do not include the APIs provided by any extended supertypes. - For more information on the command API, see . -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    appPermissionsPermissions? - The permissions applicable to your bot in the current command execution context, as defined - by the bot's roles and current channel permissions. - Will be null if the command was executed in a DM with the bot. -
    genericCommandApplicationCommand <*> - Generic command object, representing the command this context belongs to. - A more specific reference will be provided in this type's subtypes. -
    genericEventApplicationCommandInteractionCreateEvent - Generic event object, representing the Discord event that triggered the current command - execution. - A more specific reference will be provided in this type's subtypes. -
    -
    - - diff --git a/Writerside/topics/core/commands/Chat-Commands.topic b/Writerside/topics/core/commands/Chat-Commands.topic deleted file mode 100644 index b80b18b5..00000000 --- a/Writerside/topics/core/commands/Chat-Commands.topic +++ /dev/null @@ -1,663 +0,0 @@ - - - - - -

    - Privileged Intents and You -

    - -

    - Chat commands require the use of the "message content" privileged intent. - While chat commands were traditionally how users interacted with bots on Discord, - this is no longer what Discord recommends or encourages for your bots. -

    - -

    - If your bot is on 75 or more guilds (or you plan to grow it to that point), you should consider using - instead of chat commands as much as possible. -

    - -

    - Your bot will need to go through a verification process (which starts at 75 guilds) before it can join - over 100 guilds. - Discord - will not verify bots - that are using the "message content" privileged - intent without a good reason for it. -

    - -

    - The only situations that we advise you to use chat commands for are - - those where the other types of commands provide an objectively worse user experience. - - This largely only applies to commands that need to take a large amount of text as input (where modals do - not suffice), or where your bot needs - to make use of file attachments uploaded to Discord without downloading them. -

    - -

    - Additionally, it's worth noting that - - the Discord client encourages users to make use of - - when they attempt to use a chat command. - This means that users will expect your bots - to support as a default mode of interaction. -

    - - - -

    - Maintenance status -

    - -

    - Due to the situation explained in the above warning, - - chat commands are not regularly used or tested by Kord Extensions' users or developers. - - As a result, there may be small bugs or formatting problems that we haven't noticed or had time to fix. -

    - -

    - If you run into any problems with the chat commands framework or its help extension, please let us know - or submit a Pull Request to address the issue. -

    -
    - -

    - Please don't skip over the above admonitions — they're important! -

    - - -

    - Chat commands are a fairly simple concept. - When a user sends a message starting with a command prefix (or bot mention), the command name and arguments, - Kord Extensions can process it accordingly. -

    - -

    - All builder functions should be called within your bot's setup function when you're - registering commands before the bot starts up. - However, it is also possible to register commands later on in the bot's lifecycle by calling the same - functions. -

    - -

    - Chat commands support command arguments, which are specified as explained on - the commands overview page. - However, unlike the other command types, they do not support . -

    - -

    - As Discord recommends against using chat commands, they must be enabled before they can be used. - For more information, see the configuration documentation. -

    - - - chatCommand(::MyArguments) { - name = "my-command" - description = "Description explaining command usage." - - check { hasPermission(Permission.MentionEveryone) } - - aliases = arrayOf("my", "mycommand") - - action { - message.respond( - "Hey, ${arguments.user.mention}! Get pinged!" - ) - } - } - - - - -

    - The following APIs are available on the Extension type, which you can use to define your - chat commands and modify their behavior. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameReceiverDescription
    chatCommandCheckCheckWithCache <MessageCreateEvent> - Register a check that must pass for the action - belonging to all chat commands defined within this extension to be run. - For more information on checks, see the dedicated documentation. -
    chatCommandChatCommand - Register a standalone chat command. - This builder function optionally takes the constructor of an - arguments class as the first argument, - representing this command's arguments to be supplied on Discord. -
    chatGroupCommandChatGroupCommand - Register a grouped chat command. - This builder function optionally takes the constructor of an - arguments class as the first argument, - representing this command's arguments to be supplied on Discord. -
    -
    -
    - - -

    - Standalone commands are the simplest type of chat command, and they're defined using the - chatCommand builder function. - This builder is a receiver function against the ChatCommand type, which provides a number of - APIs that can be used to configure your command and its metadata. -

    - -

    - At minimum, a command must have a name, description and action - defined. -

    - -

    - The following APIs are available for use when writing a chat command, in addition to those provided by - the base Command type. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameReceiverDescription
    - Required Builders -
    actionChatCommandContext <Arguments> - Required: - Register the command's action, which will be run when - the command is invoked. -
    - Optional Builders -
    checkCheckWithCache <MessageCreateEvent> - Register a check that must pass for the command's action to be run. - For more information on checks, see the dedicated documentation. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    - Required Properties -
    descriptionKey - Required: - - Key object - representing the command's description. - This should explain what the command does. - If the string spans multiple lines, the first line should be a short summary of what the - command does, with a blank line underneath. -
    nameKey - Required: - - Key object - representing the command's name, which is used to identify the command internally and - for invoking the command on Discord. -
    - Optional Properties -
    aliasKeyKey -

    - - Key object - representing a comma-separated list of alternative names that may be used to invoke - this command. -

    - -

    - If an alias conflicts with the name of another command, the other command will take - priority. -

    -
    allowKeywordArgumentsBoolean - Whether to allow the parser to parse arguments in the form of key=value or - --key value. - Defaults to true. -
    enabledBoolean - Whether this command is currently enabled, which can be changed at runtime if needed. - When disabled, a command can't be invoked and won't be shown in help commands. - Defaults to true. -
    hiddenBoolean - Whether this command should be hidden from help command listings. - Hidden commands may still be invoked. - Defaults to false. -
    localeFallbackBoolean - By default, translated command names may be invoked based on the bot's configured - locale resolvers. - By setting this property to true, Kord Extensions will attempt to find a - command using the bot's default locale when it can't be found under the resolved one. - Defaults to false. -
    - Translation-Related Properties -
    aliasKeyString? - Translation key - referencing a comma-separated list of alternative command names. - If this property is set, the aliases list is ignored. -
    signatureKeyKey? -

    - - Key object - representing the command signature to be shown in help commands. - By default, Kord Extensions will generate a signature automatically. - This property may be set if you wish to override it. -

    - -

    - Command signatures should not include the command name. - Example signature: <required: user> [option: string = default...] -

    -
    -
    -
    - - -

    - Grouped commands are a special type of chat command that contains subcommands. - They work just like a standalone chat command, but with the addition of a - set of functions that allow you to nest other commands within them. - They're defined using the chatGroupCommand builder function. -

    - -

    - To execute a grouped command, you provide the command prefix, name and arguments the same way you do with - a standalone command. - However, the first argument provided to the grouped command will be matched against the names of any - nested commands, and removed from the list of arguments provided to the found command. -

    - -

    - Grouped commands may contain subcommands or nested grouped commands. - There is no limit on how deep you may nest grouped commands, but we recommend you avoid nesting them - more than once, for the sake of clarity and ease of use. -

    - -

    - The grouped command's action is executed when the command is executed without any arguments, - or when none of the nested commands match the first argument provided to it. - A default action block is provided, which shows help information about the command and its - nested commands. - However, you may provide your own if needed or desirable. -

    - -

    - The following APIs are available for use when writing a grouped chat command, in addition to those - provided by the base ChatCommand type.. -

    - - - - - - - - - - - - - - - - - - - -
    NameDescription
    chatCommand - Define a subcommand. - This builder function mirrors the behavior of the chatCommand builder function - defined at Extension level, which you'd normally use to register standalone - commands. - Subcommands inherit the checks that are defined in their parent commands. -
    chatGroupCommand - Define a grouped subcommand. - This builder function allows you to nest grouped commands as deeply as is required. - It mirrors the behavior of the chatGroupCommand builder function defined at - Extension level. - Subcommands inherit the checks that are defined in their parent commands. -
    -
    -
    - - -

    - The ChatCommandContext type is the receiver for all chat command action blocks. - This type provides relevant APIs on top of the - base command context object type, allowing you to - efficiently respond to command invocations. -

    - -

    - The following additional APIs are provided: -

    - - - - - - - - - - - - - - - -
    NameReceiverDescription
    paginatorPaginatorBuilder -

    - Convenience function, allowing you to easily create a button-based - paginator. - The following parameters are supported: -

    - - -
  • - defaultGroup = "" - The default paginator group. -
  • - -
  • - pingInReply = true - Whether to ping the author of the - targetMessage in reply, if it's specified. -
  • - -
  • - targetChannel - Channel to send the paginator to. - Required if targetMessage is not specified. -
  • - -
  • - targetMessage - Message to send the paginator in response to. - Required if targetChannel is not specified. -
  • -
    - -

    - Note: This will not send the paginator automatically. - You'll need to call the send function on this function's return value to - send it. -

    - -

    - For more information on paginators (and the paginator builder), see - the dedicated documentation. -

    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameParametersDescription
    sendHelp - Generate and send help information for this command. - Returns true when a - help extension is loaded and the help - message was sent, and false otherwise. -
    - Translation-Related Functions -
    Message.respondTranslated - - key - , - - - placeholders - , - - useReply = true - -

    - Convenience function that allows you to respond to a message with translated content, using - the resolved locale for this command context. - This makes use of the respond - message utility function, with the useReply - parameter directly provided to it. -

    - -

    - Overloads are provided which support both ordinal and named placeholders. - For more information, - see the dedicated documentation. -

    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    argumentsT: Arguments -

    - The command's arguments, parsed into the - arguments class - you specified the constructor for when you registered this command. -

    - -

    - If you don't specify the aforementioned constructor, this will be an empty - Arguments object. -

    -
    - Data Properties -
    channelMessageChannelBehavior - Object representing the channel this command was invoked within. -
    guildGuildBehavior? - Object representing the guild this command was invoked within, or null if it was - invoked in a DM. -
    memberMemberBehavior? - Object representing the member that executed this command, or null if it was - invoked in a DM or by a webhook. -
    messageMessage - Object representing the message that invoked this command. -
    userUserBehavior? - Object representing the user that executed this command, or null if it was - invoked by a webhook. -
    -
    -
    - diff --git a/Writerside/topics/core/commands/Converters.topic b/Writerside/topics/core/commands/Converters.topic deleted file mode 100644 index b8b1d590..00000000 --- a/Writerside/topics/core/commands/Converters.topic +++ /dev/null @@ -1,1662 +0,0 @@ - - - - - - -

    - Converters are a core part of the Kord Extensions command framework. - They provide a way to convert arguments provided by Discord into rich data types, handling parsing and - validation, and making sure you get the types you expect. -

    - - -

    - As converters support multiple parsing strategies, they're divided into specific types. - Each type handles its arguments in a different way, and some are limited to specific types of commands: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    TypeCommandsDescription
    SingleAll - The default converter type, which represents a single, mandatory command argument. -
    DefaultingAll - A converter type that represents an optional command argument, with a specified default value. - The default value will be used when no argument is provided. -
    OptionalAll - A converter type that represents an optional command argument, with a default value of - null. - The default value will be used when no argument is provided. -
    CoalescingAll -

    - A converter type that attempts to consume as many arguments as possible, combining them into a - single value. - This converter type will stop at the first argument that it can't parse, passing control to the - next converter. -

    - -

    - Note: Coalescing converters will only ever parse a single value - when used in a slash command, as Discord does not support argument types that accept multiple - values. -

    -
    ListAll -

    - A converter type that acts like a coalescing converter, but which returns a list of parsed arguments - instead of a single combined value. -

    - -

    - Note: List converters will only ever parse a single value - when used in a slash command, as Discord does not support argument types that accept multiple - values. -

    -
    ChoiceAll - A converter type that represents a single argument that is limited to a predefined set of specific - choices. - When used with , user input will be limited to the given choices. -
    -
    - - -

    - Converters provide builder functions that you can use to define your command arguments. - These functions are available for use within classes that extend Arguments, as briefly - explained on the commands overview page. -

    - -

    - Once you've created your arguments class, you'll need to figure out which converter functions you'll need. - You can figure out the name of the relevant function by following these rules: -

    - - -
  • - Single converters simply use the name of the argument type — for example, string, - int, message. -
  • - -
  • - List converters use the single converter name with the List suffix — for example, - stringList, intList, messageList. -
  • - -
  • - All other converter types add prefixes to the single converter name — for example, - coalescingString, defaultingInt, optionalMessage. -
  • - -
  • - Defaulting and optional converters may be combined with other types, with an additional suffix - added to the single converter name — for example, coalescingOptionalString. -
  • -
    - - - If you're not sure which converter function you need, it's often quicker to use your IDE's auto-completion - functionality. - Start typing and your IDE should help you out! - - - - inner class WelcomeArgs : Arguments() { - val target by user { - name = "target" - description = "User to welcome" - } - - val message by defaultingString { - name = "message" - description = "Message to send to the user" - - defaultValue = "Welcome to the server, {TARGET}!" - } - } - -
    - - -

    - All converters must be specified via delegation, as explained in the example above. - Each converter builder function exposes a set of options that can be used to customize the behavior of the - converter in question. -

    - - - While the APIs defined below are standard APIs provided by Kord Extensions, it's worth noting that - individual converters may provide extra settings. - These extra settings should be provided in the documentation for the converter in question, but you can - always inspect the builder functions in your IDE if you need to. - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    - Required Properties -
    descriptionKey - Required: - - Key object - representing a short description of the argument, which explains - what it's for. -
    nameKey -

    - Required: - - Key object - representing the argument's name. -

    - -

    - For , this name is displayed in help commands and used - to refer to the argument by name when specified as a keyword argument. -

    - -

    - For , this name is displayed within the Discord client, - where it's used to identify the argument. -

    -
    -
    -
    - - -

    - When working with slash commands, Discord supports the ability to provide suggestions to the user - when they're filling out specific arguments. - You can use the autoComplete builder when defining your argument if you wish to - support this. -

    - - - If you're using a simple argument type (and you only need a specific, predefined set of options), - consider using a choice converter instead. - Choice converters do not need to contact your bot to retrieve the list of possible options, which - makes your command a lot faster to work with. - - -

    - The autoComplete builder is a receiver function against the - AutoCompleteInteraction type, with an event argument of the - AutoCompleteInteractionCreateEvent type. - A number of APIs are available to make things easier. -

    - - - class AutoCompleteArgs : Arguments() { - private val optionsMap = mapOf( - "One" to "1", - "Two" to "2", - "Three" to "3", - "Four" to "4", - "Five" to "5", - "Six" to "6", - "Seven" to "7", - "Eight" to "8", - "Nine" to "9", - "Ten" to "10", - ) - - val arg by string { - name = "input" - description = "Autocomplete argument" - - autoComplete { - suggestStringMap(optionsMap) - } - } - } - - - -

    - All the below functions are used to suggest potential argument values to the user. - They automatically limit themselves to suggesting up to the maximum possible number of values, - and they support the following arguments: -

    - - -
  • -

    - strategy - the filtering strategy to use, based on the data the user has - entered so far. - While it's possible to define your own, the following are provided: -

    - - -
  • - FilterStrategy.Contains - Filter options based on whether they contain - the provided data. -
  • - -
  • - FilterStrategy.Prefix - Filter options based on whether they start - with the provided data. -
  • - -
  • - FilterStrategy.Suffix - Filter options based on whether they end - with the provided data. -
  • -
    - - -
  • -

    - suggestInputWithoutMatches - When the given filtering strategy doesn't - match anything, whether to suggest the user's provided data as a possible option. - This may be useful for arguments that represent a form of data that will be created if - an existing value isn't found. -

    - -

    - Defaults to false. -

    -
  • - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    suggestDoubleCollection - Use a collection (such as a list) of doubles to suggest potential argument values to - the user. -
    suggestDoubleMap -

    - Use a map of strings to doubles to suggest potential argument values to the user. -

    - -

    - The string keys are displayed to the user, and mapped to their corresponding value - when the argument is filled. - They should be a human-readable representation of the given value. -

    -
    suggestIntCollection - Use a collection (such as a list) of integers to suggest potential argument values to - the user. -
    suggestIntMap -

    - Use a map of strings to integers to suggest potential argument values to the user. -

    - -

    - The string keys are displayed to the user, and mapped to their corresponding value - when the argument is filled. - They should be a human-readable representation of the given value. -

    -
    suggestLongCollection - Use a collection (such as a list) of longs to suggest potential argument values to - the user. -
    suggestLongMap -

    - Use a map of strings to longs to suggest potential argument values to the user. -

    - -

    - The string keys are displayed to the user, and mapped to their corresponding value - when the argument is filled. - They should be a human-readable representation of the given value. -

    -
    suggestNumberCollection - Use a collection (such as a list) of doubles (Discord's default number type) to suggest - potential argument values to the user. -
    suggestNumberMap -

    - Use a map of strings to doubles (Discord's default number type) - to suggest potential argument values to the user. -

    - -

    - The string keys are displayed to the user, and mapped to their corresponding value - when the argument is filled. - They should be a human-readable representation of the given value. -

    -
    suggestStringCollection - Use a collection (such as a list) of strings to suggest potential argument values to - the user. -
    suggestStringMap -

    - Use a map of string keys to values to suggest potential argument values to the user. -

    - -

    - The string keys are displayed to the user, and mapped to their corresponding value - when the argument is filled. - They should be a human-readable representation of the given value. -

    -
    -
    - - - - - - - - - - - - - - - -
    NameTypeDescription
    focusedOptionOptionValue<*> - The argument that's currently being focused by the user. - You can use this property to retrieve the data the user has entered for this argument - so far. -
    -
    -
    - - -

    - Mutators allow you to modify the argument's value after it's been filled. - This is only useful in a limited number of use-cases, but may be handy for providing light - customizations for existing converter types. -

    - -

    - To define a mutator, call the mutate builder function. - This builder takes the existing value as its first argument, expecting the modified value to be - the return value. -

    - -

    - No additional APIs are provided. -

    - - - class MutatedArgs : Arguments() { - val quoted by string { - name = "input" - description = "Mutated argument" - - mutate { value -> - "Quoted: \"$value\"" - } - } - } - -
    - - -

    - Validators allow you to validate the argument's value after it's been filled, potentially returning - an error to the user if the value fails to validate. -

    - -

    - To define a validator, call the validate builder function. - This builder is a receiver function against the ValidationContext type, which provides - an API similar to that of a check context. -

    - - - class ValidatedArgs : Arguments() { - val odd by int { - name = "input" - description = "Validated argument" - - validate { - failIf("Odd numbers only, please!") { - value % 2 == 0 - } - } - } - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    fail - Mark this validator as having failed, optionally providing a message for the user. -
    failIf - If the given value (or result of the given callback) is true, mark this - validator as having failed, optionally providing a message for the user. -
    failIfNot - If the given value (or result of the given callback) is false, mark this - validator as having failed, optionally providing a message for the user. -
    pass - Mark this validator as having passed successfully. - As this is the default state of a validator, this function is only useful when the - validator may have failed previously. -
    passIf - If the given value (or result of the given callback) is true, mark this - validator as having passed successfully. - As this is the default state of a validator, this function is only useful when the - validator may have failed previously. -
    passIfNot - If the given value (or result of the given callback) is false, mark this - validator as having passed successfully. - As this is the default state of a validator, this function is only useful when the - validator may have failed previously. -
    Boolean.whenFalse - Call the given block only when the Boolean receiver is false. - Returns null instead of the result of the block if the - Boolean is true. -
    Boolean.whenTrue - - Call the given block only when the Boolean receiver is true. - Returns null instead of the result of the block if the - Boolean is false. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    contextCommandContext - Context object representing the current command execution. -
    errorResponseKeyKey -

    - - Translation Key object, - which is used to format the error message when validation fails. -

    - -

    - A plain string may not work here, as a single replacement value (the error message) - must be inserted by the translation system. -

    - -

    - Defaults to checks.responseTemplate. -

    -
    messageString? -

    - Human-readable error message. - This message is usually provided by the relevant API functions and isn't usually - specified manually. -

    - -

    - Defaults to null, which will result in a generic failure message. -

    -
    passedBoolean - Whether this validator has passed. - This property is modified by the relevant API functions, and isn't usually modified - manually. -
    valueT - The current argument's value. -
    -
    -
    - - -

    - Choice converters allow you to specify a set of predefined command options via a simple API. -

    - - - - - - - - - - - - - - - - - - - -
    NameDescription
    choice -

    - Add a choice. -

    - -

    - This function takes a - - Key object, - which will be shown to the user on Discord when using - . -

    - -

    - When the command is submitted, the corresponding value will be provided - as the value for your argument. -

    -
    choices -

    - If you have a pre-defined map of choices, this function will use that map as the - predefined argument options. - This will replace any choices that may have been defined previously. -

    - -

    - This map's - - Key objects - must represent a human-readable representation of their corresponding - values, as they'll be shown to the user on Discord. -

    - -

    - If preferred, the choices property may be set directly instead. -

    -
    -
    -
    - - - - - - - - - - - - - - - - -
    NameTypeDescription
    ignoreErrorsBoolean - Whether to ignore errors thrown while attempting to parse a given value, assuming no values - have been parsed yet. - This is true by default, but you may wish to set it to false - for coalescing arguments at the end of the list. -
    -
    -
    - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    defaultValueT - Value to use when the user doesn't provide this argument, or when it fails to parse and - ignoreErrors is set to true. -
    ignoreErrorsBoolean - Whether to ignore errors thrown while attempting to parse a given value. - This is false by default. - When set to true, the provided defaultValue will be used as the - argument value instead of returning an error to the user. -
    -
    -
    - - - - - - - - - - - - - - - - -
    NameTypeDescription
    ignoreErrorsBoolean - Whether to ignore errors thrown while attempting to parse a given value, assuming no values - have been parsed yet. - This is false by default. - When set to true, an empty list will be used as the argument value instead of - returning an error to the user. -
    -
    -
    - - - - - - - - - - - - - - - - -
    NameTypeDescription
    ignoreErrorsBoolean - Whether to ignore errors thrown while attempting to parse a given value. - This is false by default. - When set to true, null will be used as the argument value instead - of returning an error to the user. -
    -
    -
    -
    - - -

    - Kord Extensions provides a number of converters for common data types, which you can make use of in your - bots. -

    - - - Discord's slash commands do not have wide support for rich argument types. - As a result, any converters that don't directly map to Discord entity types or primitive data types will - appear as string arguments on Discord. - For this reason, it's important that you provide a well-written description for your arguments, to explain - exactly what data you want the user to provide. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    TypeDescription
    General Converters
    Boolean -

    - Boolean argument, with support for translated values. -

    - - - -

    - The list below shows all supported inputs in English. - Translated inputs are also supported. -

    - - -
  • - Truthy responses: 1, y, yes, t, - true -
  • - -
  • - Falsey responses: 0, n, no, f, - false -
  • -
    -
    -
    -
    Color -

    - Color argument, supporting hex codes prefixed with # or 0x, plain RGB - integers, - or color names matching - Discord's branding colors. -

    - -

    - The values returned by this converter will be - - Kord Color objects. -

    - -

    - Converter functions matching both US English ("color") and UK English ("colour") are available. - Translated color names are also supported. -

    -
    Decimal -

    - Decimal argument, parsing the provided value into a Double. -

    - - - - -
  • - maxValue (Double?) — Optionally, the maximum value that may be - provided. -
  • - -
  • - minValue (Double?) — Optionally, the minimum value that may be - provided. -
  • -
    -
    -
    -
    Duration -

    - Duration argument, using a complex parsing strategy to transform the provided value into a - - DateTimePeriod object. - This object is timezone-agnostic, and may need to be normalized against a specific timezone for - some use-cases. -

    - -

    - This is also available as a coalescing converter. -

    - - - When working with , it may be beneficial to use the coalescing - version of this converter, - which will allow users to supply spaces in their input. - - - - - -
  • - longHelp (Boolean, default: true) — Whether to - respond with a long help message explaining precisely how to specify a duration when the - provided value cannot be parsed. - If this has been set to false, only a short error stating that the duration - could not be parsed will be provided. -
  • - -
  • - positiveOnly (Boolean, default: true) — Whether a - positive duration is required. - This may be set to false if you wish to allow users to specify a negative - duration. -
  • -
    -
    -
    - -

    - In addition to - Discord-formatted timestamps, - this converter supports - specifying durations as a series of units and durations. -

    - - - -

    - Durations may be specified within a string, - as pairs containing both a unit and a numeric value. - Commas, spaces, and the plus character ("+") are removed from the provided string - before parsing. -

    - -

    - Pairs may be specified in any order, and duplicate pairs will be added together (or - subtracted, in the case of a negative numeric value). - Both ordered forms are supported to allow for natural input in right-to-left languages: -

    - - -
  • Value first: 12 days
  • -
  • Unit first: d12
  • -
    - -

    - Negative values may be specified by prefixing the numeric value with a minus sign - ("-"). - Only base-10 ASCII integers are supported. -

    -
    - - -

    - The list below shows all supported units, along with the names that may be used to refer - to them in English. - Translated names are also supported. -

    - - -
  • - Seconds: s, sec, secs, second, - seconds -
  • - -
  • - Minutes: m, mi, min, mins, - minute, minutes -
  • - -
  • - Hours: h, hr, hour, hours -
  • - -
  • - Days: d, day, days -
  • - -
  • - Weeks: w, wk, week, weeks -
  • - -
  • - Months: mo, mth, month, months -
  • - -
  • - Years: y, yr, year, years -
  • -
    -
    - - -

    - The following examples are valid duration strings: -

    - - -
  • - 12d 4h 30m - 12 days, 4 hours and 30 minutes. -
  • - -
  • - 12d 4h 30m -1 w - 5 days (12 - 7), 4 hours and 30 minutes. -
  • - -
  • - -2d 1w 6h -2h - 5 days (7 - 2) and 4 hours (6 - 2). -
  • - -
  • - 周3 日2 分30 - in Simplified Chinese, 23 days (3 * 7 + 2) and 30 - minutes. -
  • -
    - -

    - The following examples are invalid: -

    - - -
  • - 12 - bare numbers are not supported, a unit is required. -
  • - -
  • - 12m d w - all units require a numerical value. -
  • - -
  • - -5 days with positiveOnly = true - the current converter - only accepts positive durations. -
  • -
    -
    -
    -
    Email -

    - Email argument, validated using - - the Apache Commons email validator. -

    -
    Enum -

    - Arbitrary enum argument, allowing for the specification of enum value names. - By default, enum value names will be matched case-insensitively. - This is also available as a choice converter. -

    - -

    - The enum to use is specified by providing a generic type parameter to the converter's builder - function. - This will be referred to as E below. -

    - - - - -
  • - Required: typeName - (Key) — - - Key object - representing a human-readable short name for whatever the given enum - represents. - Should be no more than two or three words. -
  • - -
  • - getter (suspend (String) -> E?) — Optional getter - function used to transform the input value into an enum value. - By default, case-insensitively matches the input value against the enum value names. -
  • -
    -
    -
    -
    Int -

    - Integer argument, parsing the provided value into an Int. -

    - - - - -
  • - maxValue (Int?) — Optionally, the maximum value that - may be provided. -
  • - -
  • - minValue (Int?) — Optionally, the minimum value that - may be provided. -
  • - -
  • - radix (Int, default: 10) — Optionally, the - radix (base) to use to parse the value. -
  • -
    -
    -
    -
    Long -

    - Long argument, parsing the provided value into a Long. - This is also available as a choice converter, as a converter named number. -

    - - - - -
  • - maxValue (Int?) — Optionally, the maximum value that - may be provided. -
  • - -
  • - minValue (Int?) — Optionally, the minimum value that - may be provided. -
  • - -
  • - radix (Int, default: 10) — Optionally, the - radix (base) to use to parse the value. -
  • -
    -
    -
    -
    Regex -

    - Regex argument, allowing users to specify a regular expression. -

    - - -

    - Due to the inherent risks associated with making use of regular expressions from untrusted - sources - (such as - catastrophic backtracking), - it's best to limit access to commands that use this argument type. -

    - -

    - If you still need to handle user-submitted regular expressions, it may be best to write - a custom converter using - Google's RE2 library. -

    -
    - - - - -
  • - options (MutableSet<RegexOption>) — Set of regular - expression options. - Options added to this set will be used when creating the Regex object. -
  • -
    -
    -
    -
    String -

    - String argument, providing users' input verbatim. - This is also available as a choice converter or coalescing converter. -

    - - - - -
  • - maxLength (Int?) — Maximum length for the input string. -
  • - -
  • - minLength (Int?) — Minimum length for the input string. -
  • -
    -
    -
    -
    Supported Locale -

    - Supported locale argument, allowing users to provide one of Kord Extensions' supported locales. - This converter will return a Locale object if the specified locale is one that - Kord Extensions supports. -

    - -

    - If a locale you need isn't supported, please feel free to - - contribute translations for it. -

    -
    Discord Entities
    Channel -

    - Discord channel argument, supporting mentions, IDs, and names. - Also supports "this" to refer to the current channel. -

    - -

    - When working with slash commands, the Discord client resolves the channel object instead of the - bot. - This means that the converter will only ever return the channel object provided by the client, - and cannot resolve the special name "this." - It will also ignore the requiredGuild and requireSameGuild properties. -

    - - - - -
  • - requireChannelType(type: ChannelType) — Require that the resolved channel be - of the specified type. - May be called multiple times for multiple channel types. -
  • -
    -
    - - - -
  • - requiredChannelTypes (MutableSet<ChannelType>) — Require - that the resolved channel be of one of the supplied types. - Calls to requireChannelType will add the provided type to this set. -
  • - -
  • - requiredGuild (suspend () -> Snowflake) — Limit channel - resolution to a specific guild ID. -
  • - -
  • - requireSameGuild (Boolean, default: true) — Limit - channel resolution to channels on the same guild the command was executed within. -
  • -
    -
    -
    -
    Emoji -

    - Emoji argument, supporting Unicode emojis, guild emoji IDs, guild emoji names, and Unicode - emoji names as used by Discord's emoji picker. - Guild emoji IDs may be specified with or without surrounding colons, or specified using the - emoji mentions that the Discord client inserts into messages. -

    - -

    - When a guild emoji is specified, it must come from a guild the bot is present on. - If a guild emoji is specified only by name, the first emoji the bot finds from its guilds is - used. -

    - - - When a guild emoji is specified, Kord Extensions will always fail to find it unless your bot - has the GuildEmojis intent enabled in - the intents configuration. - -
    Guild -

    - Discord guild argument, supporting IDs and names. - Also supports "this" to refer to the current guild. -

    - -

    - The guild specified must be a guild the bot is present on. -

    -
    Member -

    - Discord guild member argument, supporting mentions, IDs, usernames, tags, and names. - Also supports "me" to refer to the member running the command, - and "you" to refer to the bot. -

    - -

    - When working with slash commands, the Discord client resolves the member object instead of the - bot. - This means that the converter will only ever return the member object provided by the client, - and cannot resolve the special names "me" or "you." -

    - - - - -
  • - requiredGuild (suspend () -> Snowflake) — Limit member - resolution to a specific guild ID. - This will additionally require the command to be run on the guild represented by - that ID. -
  • - -
  • - requireSameGuild (Boolean, default: true) — - Limit member resolution to users on the same guild the command was executed - within. -
  • - -
  • - useReply (Boolean, default: true) — For - , whether to use the author of the replied-to - message (when there is one). -
  • -
    -
    -
    -
    Message -

    - Discord message argument, supporting message jump URLs and IDs. - When a message ID is specified, it's assumed to be in the same channel the command was executed - within. -

    - -

    - Messages will be retrieved if they're not already in Kord's cache, which requires the bot to - be able to see the message in question. - When using a URL, the bot cannot resolve messages in DMs. -

    - - - - -
  • - requiredGuild (suspend () -> Snowflake) — Limit message - resolution to a specific guild ID. - This will additionally require the command to be run on the guild represented by - that ID. -
  • - -
  • - requireGuild (Boolean, default: true) — - If requiredGuild is specified, limit message resolution to the guild - returned by that property. - Otherwise, limit message resolution to the same guild the command was executed - within. -
  • - -
  • - useReply (Boolean, default: true) — For - , whether to use the replied-to message - (when there is one). -
  • -
    -
    -
    -
    Role -

    - Discord role argument, supporting role mentions, names, and IDs. - Role resolution via commands run in DMs is not supported. -

    - -

    - When working with slash commands, the Discord client resolves the role object instead of the - bot. - This means that the converter will only ever return the role object provided by the client, - and the requiredGuild property will be ignored. -

    - - - - -
  • - requiredGuild (suspend () -> Snowflake) — Limit role - resolution to a specific guild ID. - When omitted, this will default to the guild the command was executed within. -
  • -
    -
    -
    -
    Snowflake -

    - Discord snowflake argument. - Parses the given value into a Snowflake object. -

    -
    Tag -

    - Discord forum channel tag argument. - Provides built-in support for tags based on the - current channel or the channelGetter property. -

    - - - - -
  • - channelGetter (suspend () -> ForumChannel?) — Limit tag - resolution to the given forum channel. - This is used for both auto-completion and tag resolution on command execution. -
  • -
    -
    -
    -
    Timestamp -

    - Discord timestamp argument. - Parses a - - Discord-formatted timestamp into a FormattedTimestamp object, - which includes the corresponding Instant and TimestampType. -

    - -

    - This converter only supports Discord-formatted timestamps, and is unable to parse any other - form of timestamp. -

    -
    User -

    - Discord user argument supporting mentions, IDs, usernames, and tags. - Also supports "me" to refer to the member running the command, - and "you" to refer to the bot. -

    - -

    - When working with slash commands, the Discord client resolves the user object instead of the - bot. - This means that the converter will only ever return the user object provided by the client, - and cannot resolve the special names "me" or "you." -

    - - - - -
  • - useReply (Boolean, default: true) — For - , whether to use the author of the replied-to - message (when there is one). -
  • -
    -
    -
    -
    Slash Commands Only
    Attachment -

    - A file attachment, as provided in a slash command invocation. - Discord will display a drop target for this type of argument. -

    -
    -
    -
    diff --git a/Writerside/topics/core/commands/application/Context-Commands.topic b/Writerside/topics/core/commands/application/Context-Commands.topic deleted file mode 100644 index bf55c988..00000000 --- a/Writerside/topics/core/commands/application/Context-Commands.topic +++ /dev/null @@ -1,207 +0,0 @@ - - - - -

    - Context commands are a form of application command, triggered by - users via a right-click/tap-and-hold menu in their Discord client. - Unlike other types of command, context commands do not take arguments — instead, they're provided with whatever - the user was targeting when they opened the menu. -

    - -

    - When registered by your bot, context commands are visible in a menu named Apps. - There are two types of context command — message commands and user commands. - They have identical APIs, aside from the target data supplied to them by Discord. -

    - - - The following APIs are available on the Extension type, which you can use to define your - context commands and modify their behavior. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameReceiverDescription
    - Checks -
    messageCommandCheckCheckWithCache <MessageCommandInteractionCreateEvent> - Register a check that must pass for the action - belonging to all message commands defined within this extension to be run. - For more information on checks, see the dedicated documentation. -
    userCommandCheckCheckWithCache <UserCommandInteractionCreateEvent> - Register a check that must pass for the action - belonging to all user commands defined within this extension to be run. - For more information on checks, see the dedicated documentation. -
    - Commands -
    ephemeralMessageCommandEphemeralMessageCommand - Register an ephemeral message command. - - This builder function optionally takes the constructor of a - modal form class as the first argument, - representing the modal form to display and await input for when the command is executed. -
    ephemeralUserCommandEphemeralUserCommand - Register an ephemeral user command. - - This builder function optionally takes the constructor of a - modal form class as the first argument, - representing the modal form to display and await input for when the command is executed. -
    publicMessageCommandPublicMessageCommand - Register a public message command. - - This builder function optionally takes the constructor of a - modal form class as the first argument, - representing the modal form to display and await input for when the command is executed. -
    publicUserCommandPublicUserCommand - Register a public user command. - - This builder function optionally takes the constructor of a - modal form class as the first argument,s - representing the modal form to display and await input for when the command is executed. -
    -
    -
    - - -

    - Message commands are registered by calling the relevant functions in - your extension's setup function: -

    - - -
  • - ephemeralMessageCommand for message commands that respond ephemerally. -
  • - -
  • - publicMessageCommand for message commands that respond publicly. -
  • -
    - - - publicMessageCommand { - name = "My Command" - - action { - val target = targetMessages.first() - - respond { - content = "Target: ${target.getJumpUrl()}" - } - } - } - - -

    - Message commands have an identical API to that which is provided by , - but message command context objects provide a targetMessages property. - This property corresponds with the Discord API, which provides a list that contains the message that - was being targeted when the command was executed. -

    - -

    - Message commands support modal forms, as described in - the commands overview page. -

    -
    - - -

    - User commands are registered by calling the relevant functions in - your extension's setup function: -

    - - -
  • - ephemeralUserCommand for user commands that respond ephemerally. -
  • - -
  • - publicUserCommand for user commands that respond publicly. -
  • -
    - - - publicUserCommand { - name = "My Command" - - action { - val target = targetUsers.first() - - respond { - content = "Target: ${target.mention}" - } - } - } - - -

    - User commands have an identical API to that which is provided by , - but user command context objects provide a targetUsers property. - This property corresponds with the Discord API, which provides a list that contains the Discord user that - was being targeted when the command was executed. -

    - -

    - User commands support modal forms, as described in - the commands overview page. -

    -
    -
    \ No newline at end of file diff --git a/Writerside/topics/core/commands/application/Slash-Commands.topic b/Writerside/topics/core/commands/application/Slash-Commands.topic deleted file mode 100644 index 8026d0dd..00000000 --- a/Writerside/topics/core/commands/application/Slash-Commands.topic +++ /dev/null @@ -1,447 +0,0 @@ - - - - -

    - Slash commands are a form of application command, triggered by users - by typing a slash (/) followed by the command's name and arguments, and sending it via - Discord's message input box. -

    - -

    - When registered by your bot, slash commands are visible in a menu displayed above Discord's message - input box when a user types a slash (/) into it. - This menu allows users to browse a list of all available slash commands, and provides access to their - descriptions and arguments. -

    - - - Slash commands support command arguments and - modal forms. - However, there are features and types of argument converters that are unique to slash commands, - and other types of converters that slash commands do not support. - For more information on this, see the dedicated documentation. - - -

    - All builder functions should be called within your extension's setup function when you're - registering commands before the bot starts up. - However, it is also possible to register commands later on in the bot's lifecycle by calling the same - functions. -

    - - - publicSlashCommand(::MyArguments) { - name = "my-command" - description = "Description explaining command usage." - - check { hasPermission(Permission.MentionEveryone) } - - action { - respond { - // Note: Slash commands aren't actually able to mention - // users in most cases - - content = "Hey, ${arguments.user.mention}! Get pinged!" - } - } - } - - - - The following APIs are available on the Extension type, which you can use to define your - slash commands and modify their behavior. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameReceiverDescription
    - Checks -
    slashCommandCheckCheckWithCache <ChatInputCommandInteractionCreateEvent> - Register a check that must pass for the action - belonging to all slash commands defined within this extension to be run. - For more information on checks, see the dedicated documentation. -
    - Commands -
    ephemeralSlashCommandEphemeralSlashCommand - Register an ephemeral slash command. - This builder function optionally takes the constructor of an - arguments class, - the constructor of a modal form class, - or both. -
    publicSlashCommandPublicSlashCommand - Register a public slash command. - This builder function optionally takes the constructor of an - arguments class, - the constructor of a modal form class, - or both. -
    -
    -
    - - -

    - Standalone commands are the simplest type of slash command, and they're defined using the builder function - corresponding with the intended interaction response type: -

    - - -
  • ephemeralSlashCommand for slash commands that respond ephemerally.
  • -
  • publicSlashCommand for slash commands that respond publicly.
  • -
    - -

    - Slash commands extend - the ApplicationCommand type, - and provide some additional APIs. - However, both ephemeral and public slash commands provide an identical API. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameReceiverDescription
    - Required Builders -
    actionSlashCommandContext <Arguments> -

    - Required: Register the command's action, which will be run - when the command is invoked. - If you provide the constructor for a - modal form class to the registration - function, it will be passed to this builder as the first argument. -

    - -

    - Note: If your command contains any - subcommands or - command groups, - then you may not specify an action block. - Slash commands containing nested commands are not executable on their own. -

    -
    - Optional Builders -
    checkCheckWithCache <ChatInputCommandInteractionCreateEvent> - Register a check that must pass for this command's action to be run. - For more information on checks, see the dedicated documentation. -
    - Nested Command Builders -
    groupSlashGroup - Register a command group. - This builder function takes the name of the command group as its first argument. - Nested commands inherit the checks that are defined in their parent commands. -
    ephemeralSubCommandEphemeralSlashCommand - Register an ephemeral subcommand. - Nested commands inherit the checks that are defined in their parent commands. -
    publicSubCommandPublicSlashCommand - Register a public subcommand. - Nested commands inherit the checks that are defined in their parent commands. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    - Required Properties -
    descriptionKey - Required: - - Key object - representing the command's description, which should explain what the command does. - The description may not be longer than 100 characters. -
    - Other Properties -
    parentCommandSlashCommand? - This command's parent slash command, if it's a subcommand. -
    parentGroupSlashGroup? - This command's parent command group, if it's a - subcommand that's part of a group. -
    -
    -
    - - -

    - Slash commands may contain subcommands, which allow you to create a tree of executable commands. - Subcommands are executed by providing the name of the subcommand as an argument to its parent command. - They may be registered by calling the relevant builder functions from within a - standalone command or command group: -

    - - -
  • ephemeralSubCommand for slash commands that respond ephemerally.
  • -
  • publicSubCommand for slash commands that respond publicly.
  • -
    - -

    - Subcommands have an identical API to standalone commands. - Additionally, they inherit the checks defined in their parent commands. -

    -
    - - -

    - Slash commands may contain command groups, which allow you to provide a nested group of - subcommands. - Grouped commands inherit the checks that are defined in the group's parent command. -

    - -

    - Command groups are not executable on their own, acting only as containers for related subcommands. - Because of this, they provide a much smaller set of APIs. -

    - - - - - - - - - - - - - - - - - - - - - - -
    NameReceiverDescription
    ephemeralSubCommandEphemeralSlashCommand - Register an ephemeral subcommand to this group. - Grouped commands inherit the checks that are defined in the group's parent command. -
    publicSubCommandPublicSlashCommand - Register a public subcommand to this group. - Grouped commands inherit the checks that are defined in the group's parent command. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    - Required Properties -
    descriptionKey -

    - Required: - - Key object - representing the command group's description, which should explain what the group - represents. - The description may not be longer than 100 characters. -

    - -

    - Command group descriptions are not currently displayed in the Discord client, but the API - still requires that you provide them. - Discord has stated that they may be displayed someday, so it's a good idea to write - proper descriptions regardless. -

    -
    - Other Properties -
    parentCommandSlashCommand? - This group's parent slash command. -
    -
    -
    - - -

    - The SlashCommandContext type is extended by types that match the intended interaction response - type, and these types are used as the receiver for the relevant slash command action blocks. - This type provides relevant APIs on top of the - base application command context object type, - allowing you to efficiently respond to command invocations. -

    - -

    - As mentioned in the - base application command context documentation, - all slash command context types inherit the relevant - interaction context type. -

    - -

    - The following additional APIs are provided: -

    - - - - - - - - - - - - - - - -
    NameTypeDescription
    argumentsT: Arguments -

    - The command's arguments, parsed into the - arguments class - you specified the constructor for when you registered this command. -

    - -

    - If you don't specify the aforementioned constructor, this will be an empty - Arguments object. -

    -
    -
    -
    -
    diff --git a/Writerside/topics/core/commands/converters/Custom-Converters.topic b/Writerside/topics/core/commands/converters/Custom-Converters.topic deleted file mode 100644 index 884cd6de..00000000 --- a/Writerside/topics/core/commands/converters/Custom-Converters.topic +++ /dev/null @@ -1,736 +0,0 @@ - - - - - - - - This section will use - - the bundled Snowflake converter as an example below. - - -

    - When the bundled converters don't meet your needs, you can - create your own. - Kord Extensions provides utilities that make creating your own converters easier, generating converter builder - functions automatically. -

    - - -

    - Before getting started, you'll need to set up KSP and the converter annotation processor. -

    - - - -

    - First, add the dependencies to your gradle/libs.versions.toml. - Check GitHub for the latest version of KSP. -

    - -
    - - - [versions] - kord-extensions = "%version%" - ksp = "KSP VERSION HERE" - - [libraries] - ksp = { - module = "com.google.devtools.ksp:symbol-processing-api", - version.ref = "ksp" - } - - kord-extensions-processor = { - module = "dev.kordex:annotation-processor", - version.ref = "kord-extensions" - } - - -

    - Then, update your build.gradle.kts. -

    - -
    - - - plugins { - // ... - - idea - id("com.google.devtools.ksp") version "KSP VERSION HERE" - } - - dependencies { - // ... - - ksp(libs.kord.extensions.processor) - } - - idea { // Fixes IntelliJ indexing and build optimisation - module { - // Not using += due to https://github.com/gradle/gradle/issues/8749 - // (Gradle closed this as fixed, but they broke it again) - sourceDirs = sourceDirs + - file("${layout.buildDirectory}/generated/ksp/main/kotlin") - - testSources.setFrom( - testSources.from + file("${layout.buildDirectory}/generated/ksp/test/kotlin") - ) - - generatedSourceDirs = generatedSourceDirs + - file("${layout.buildDirectory}/generated/ksp/main/kotlin") + - file("${layout.buildDirectory}/generated/ksp/test/kotlin") - } - } - -
    - - -

    - First, add the dependencies to your gradle/libs.versions.toml. - Check GitHub for the latest version of KSP. -

    - -
    - - - [versions] - kord-extensions = "%version%" - ksp = "KSP VERSION HERE" - - [libraries] - ksp = { - module = "com.google.devtools.ksp:symbol-processing-api", - version.ref = "ksp" - } - - kord-extensions-processor = { - module = "dev.kordex:annotation-processor", - version.ref = "kord-extensions" - } - - -

    - Then, update your build.gradle. -

    - -
    - - - plugins { - // ... - - id "idea" - id "com.google.devtools.ksp" version "KSP VERSION HERE" - } - - dependencies { - // ... - - ksp libs.kord.extensions.processor - } - - idea { // Fixes IntelliJ indexing and build optimisation - module { - // Not using += due to https://github.com/gradle/gradle/issues/8749 - // (Gradle closed this as fixed, but they broke it again) - sourceDirs = sourceDirs + - file("${layout.buildDirectory}/generated/ksp/main/kotlin") - - testSources.setFrom( - testSources.from + file("${layout.buildDirectory}/generated/ksp/test/kotlin") - ) - - generatedSourceDirs = generatedSourceDirs + - file("${layout.buildDirectory}/generated/ksp/main/kotlin") + - file("${layout.buildDirectory}/generated/ksp/test/kotlin") - } - } - -
    - - - -

    - Kord Extensions is developed using Gradle, and none of the developers are familiar with Maven. -

    - -

    - If you know how to set this up using Maven, and you've tested that it all works, please feel free - to contribute this section. -

    -
    -
    -
    -
    - - -

    - All converters extend one of the converter base types. - Most of your converters will inherit the SingleConverter type, and follow a strict structure. - Once you've written your converter, you'll add the @Converter annotation, which will configure - the annotation processor to generate your converter's builders and DSL functions. -

    - -

    - All converter types take a generic type parameter, referred to as T. - This type represents the resulting rich type your converter provides to users when they finish parsing - arguments. - The only restriction for this type is that it can’t be nullable. -

    - - -

    - Your converter's constructor must take a single parameter, the - validator provided by the user. - This parameter should be nullable, default to null, and the validator's generic type - parameter must match the one provided to your converter. -

    - -

    - The converter base types define this constructor parameter, so you'll need to override it. -

    - - - public class SnowflakeConverter( - override var validator: Validator<Snowflake> = null - ) : SingleConverter<Snowflake>() { - // ... - } - - -

    - Your converter's constructor may take additional parameters, as defined in the - builderConstructorArguments property in the @Converter annotation, and - explained below. -

    -
    - - -

    - Your converter must override some properties defined by the base converter types: -

    - - - - - - - - - - - - - - -
    NameTypeDescription
    signatureTypeKey - - Key object - referring to a short description - explaining the type of data this converter handles. - Shown in help commands for and errors for all command types. -
    - -

    - Additionally, you may override the following properties as required: -

    - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    errorTypeKey? - - Key object - representing a longer description for - this converter's handled type than signatureType. - Used in "invalid value" errors instead of signatureType if provided. - Defaults to null. -
    showTypeInSignatureBoolean - Whether the signatureType should be shown in help commands for - . - Defaults to true -
    - - - override val signatureType: Key = "converters.snowflake.signatureType" - -
    - - -

    - Your converter must override some functions defined by the base converter types. -

    - - -

    - All converter types must implement the parse function, responsible for parsing - arguments from a stream of string-based tokens. - use this function to parse arguments from Discord messages into - rich types. -

    - -

    - This function takes the following parameters: -

    - - -
  • -

    - parser: StringParser? - Instance of Kord Extensions' String parser. - Call parser.parseNext() to attempt to retrieve the next argument string token - provided by the user. -

    - -

    - May be null when named is provided. - Wrapping converters (e.g. SingleToOptional) used internally may also call - parse with a parser value of null. -

    -
  • - -
  • -

    - context: CommandContext - - representing the command being executed. -

    - -

    - Note: It is important your converter can parse arguments using - only the API provided by the base CommandContext type. - The converter may check the type and respond accordingly, but this parameter can be of - an unusual context type that may exist for any number of purposes, and your converter - can't break in these scenarios. -

    -
  • - -
  • - named: String? - Set when users provide values using keyword arguments. - Note: It's important your converter prioritizes this - parameter when provided, instead of trying to parse values from the parser. -
  • - - -

    - This function must return a Boolean representing whether your converter managed to successfully - parse a value - true if it did, and false otherwise. - If your converter managed to parse a value, it must store that value in the parsed - class property. -

    - -

    - To provide specific errors to users, your converter may throw a - - DiscordRelayedException. -

    - - - override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { - // Use `named`, try the parser, then give up if neither are present. - val arg: String = named ?: parser?.parseNext()?.data ?: return false - - try { - // Try to parse the value and store it. - this.parsed = Snowflake(arg) - } catch (e: NumberFormatException) { - // Invalid Snowflake, supply a relevant error to the user. - throw DiscordRelayedException( - context.translate("converters.snowflake.error.invalid", replacements = arrayOf(arg)) - ) - } - - // Parsing was successful, report that by returning `true`. - return true - } - -
    - - -

    - All converter types that extend SlashCommandConverter (which includes all converters - that extend SingleConverter) must implement two additional functions. -

    - - -
  • - toSlashOption - Convert the given Argument parameter to the - corresponding Kord - - OptionsBuilder subtype. - - For most converter types, - - StringChoiceBuilder will be the best option. -
  • - -
  • - parseOption - Using the given - command context object, parse the Kord - - OptionValue into the converter's rich type. - This function behaves similarly to the parse function mentioned in - the previous section. -
  • -
    - - - override suspend fun toSlashOption(arg: Argument<*>): OptionsBuilder = - StringChoiceBuilder(arg.displayName, arg.description).apply { required = true } - - override suspend fun parseOption(context: CommandContext, option: OptionValue<*>): Boolean { - // Get the correct option value type, and give up if an unexpected type is provided. - val optionValue = (option as? StringOptionValue)?.value ?: return false - - try { - // Try to parse the value and store it. - this.parsed = Snowflake(optionValue) - } catch (e: NumberFormatException) { - // Invalid Snowflake, supply a relevant error to the user. - throw DiscordRelayedException( - context.translate("converters.snowflake.error.invalid", replacements = arrayOf(optionValue)) - ) - } - - // Parsing was successful, report that by returning `true`. - return true - } - -
    -
    -
    - - -

    - Once you've written your converter, it should something like the example below. -

    - - - public class SnowflakeConverter( - override var validator: Validator<Snowflake> = null - ) : SingleConverter<Snowflake>() { - override val signatureType: Key = "converters.snowflake.signatureType" - - override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { - val arg: String = named ?: parser?.parseNext()?.data ?: return false - - try { - this.parsed = Snowflake(arg) - } catch (e: NumberFormatException) { - throw DiscordRelayedException( - context.translate("converters.snowflake.error.invalid", replacements = arrayOf(arg)) - ) - } - - return true - } - - override suspend fun toSlashOption(arg: Argument<*>): OptionsBuilder = - StringChoiceBuilder(arg.displayName, arg.description).apply { required = true } - - override suspend fun parseOption(context: CommandContext, option: OptionValue<*>): Boolean { - val optionValue = (option as? StringOptionValue)?.value ?: return false - - try { - this.parsed = Snowflake(optionValue) - } catch (e: NumberFormatException) { - throw DiscordRelayedException( - context.translate("converters.snowflake.error.invalid", replacements = arrayOf(optionValue)) - ) - } - - return true - } - } - - -

    - This converter is functional, but it still needs converter functions before users can define arguments with - it. - To make this easier, Kord Extensions provides a special @Converter annotation that will - generate everything you need. -

    - -

    - To use it, annotate your converter class with @Converter and provide the relevant parameters. -

    - - - @Converter( - "snowflake", - - types = [ConverterType.DEFAULTING, ConverterType.LIST, ConverterType.OPTIONAL, ConverterType.SINGLE] - ) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    - Required Parameters -
    namesvararg String -

    - Converter name, used to generate the names of the converter DSL functions. - Ideally a single, lower-case word. -

    - -

    - When specified, multiple names will result in a set of DSL functions for each provided name. - This may be useful for converters using words that differ in different English dialects, such - as "color" and "colour." -

    -
    typesArray<ConverterType> -

    - Array of ConverterType enum entries. -

    - - -
  • -

    - First, provide SINGLE or COALESCING based on your converter's - base type. -

    - - -
  • - SingleConverter - provide SINGLE. -
  • - -
  • - CoalescingConverter - provide COALESCING. -
  • -
    - -

    - - These options are mutually exclusive and cannot be provided together. - -

    - - -
  • - Converters extending ChoiceConverter must additionally provide - CHOICE. -
  • - -
  • - Any combination of DEFAULTING, LIST and OPTIONAL may - be additionally provided as desired. -
  • - -
    - Optional Parameters -
    importsArray<String> - Extra imports required by your converter. - These will be provided in all generated files. -
    builderConstructorArgumentsArray<String> -

    - Arguments to add to the generated builders' constructors. - This must be a full definition (including visibility modifiers and val/var). -

    - -

    - Prefix an argument with !! to prevent it from being passed into the converter's - constructor. -

    -
    builderGenericString - Generic type parameter that the generated builders should take. - This must be a full definition (including name and type bound), and multiple may be provided. -
    builderFieldsArray<String> -

    - Extra fields that will be defined within the generated builders. - This must be a full definition (including visibility modifiers and val/var). -

    - -

    - For required values that users must provide, use lateinit var. -

    -
    builderSuffixedWhereString - Extra bounds for the generated builders' generic type parameters, - to be provided after where in their signatures. -
    builderBuildFunctionPreStatementsArray<String> - Extra lines of code to add to the generated builders' build functions before the - converter object is constructed. -
    builderBuildFunctionStatementsArray<String> - Extra lines of code to add to the generated builders' build functions after the - converter object is constructed. -
    builderInitStatementsArray<String> - Extra lines of code to add to the generated builders' init blocks. -
    builderExtraStatementsArray<String> - Extra lines of code to add to the generated builders' class bodies, after their init - blocks and fields. -
    functionBuilderArgumentsArray<String> - Arguments to add to the generated builder functions, which will be passed into the builders' - constructors. - This must be a full definition (including name and type). -
    functionGenericString -

    - Generic type parameter that the generated builder functions should take. - This must be a full definition (including name and type bound), and it will be - reified automatically. -

    - -

    - Only one type parameter is currently supported. -

    -
    functionSuffixedWhereString - Extra bounds for the generated builder functions' type parameters, - to be provided after where in their signatures. -
    - -

    - Code will be generated when the build project is compiled, and you can find it in your - project's build/ folder, under generated/main/kotlin/. - Kord Extensions aims to generate well-formatted code, and it includes comments to illustrate precisely - where code will be injected. -

    - -

    - As the bundled converters make use of the annotation - processor, you can look at them for more examples. - - They can be found - - on GitHub. -

    -
    - - -

    - Your custom converters may be used in the same manner described on the - converters page. - Create an Arguments class, define arguments using - your converter's builder functions, and use it in your command definitions. -

    -
    -
    diff --git a/Writerside/topics/core/components/Message-Components.topic b/Writerside/topics/core/components/Message-Components.topic deleted file mode 100644 index 51b2d38a..00000000 --- a/Writerside/topics/core/components/Message-Components.topic +++ /dev/null @@ -1,1137 +0,0 @@ - - - - - - -

    - Messages support a specific set of components. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    TypeActionableHas ID
    Action Row❌ No❌ No
    Button (Disabled)❌ No✅ Yes
    Button (Interaction)✅ Yes✅ Yes
    Button (Link)❌ No❌ No
    Select Menu✅ Yes✅ Yes
    - -

    - Kord Extensions provides a convenient API for shorter-lived message components. - The ComponentContainer class is the core type representing this API. -

    - -

    - To get started, use the components builder. - This builder is available for use in all message creation and editing builders. -

    - - -

    - Kord Extensions doesn't provide an atomic API for modifying your components after you've sent them to - Discord. - This is mostly due to technical limitations within Kotlin's type system, and it is unlikely this - will change any time soon. -

    - -

    - If you wish to edit components, we suggest keeping a reference to the ComponentContainer - returned by the components builder, and any relevant components that you create. - The components within any given ComponentContainer may be applied to any message - creation/editing builder via the applyComponents extension function. -

    -
    - - - channel.createMessage { - components { - publicButton { - label = "OK" - style = ButtonStyle.Success - - action { - respond { - content = "Clicked: **OK**" - } - } - } - - publicButton { - label = "Cancel" - style = ButtonStyle.Danger - - action { - respond { - content = "Clicked: **Cancel**" - } - } - } - } - } - - - -

    - The components DSL function accepts extra arguments: -

    - - -
  • - timeout, an optional timeout Duration. - This argument allows you to specify how long to wait (since the components were last interacted - with) until the bot should stop waiting for interactions. - Defaults to null, meaning the components will work until the bot restarts. -
  • -
    - -

    - This function requires a callable block, a receiver on the ComponentContainer type. - You should use this block to define the components to add to the message. - The ComponentContainer will be returned by the function. -

    - -

    - The ComponentContainer class is open, and may be extended for advanced - use-cases. - If you need to create your own objects based on this class, you can use the applyComponents - extension function available in all message creation/editing builders to apply its contents. -

    - - -

    - When adding components to messages directly, you need to define action rows for your components, - and space them out yourself. - The ComponentContainer still allows you to specify the rows your components belong to. - However, for components with undefined rows, it will attempt to automatically pack them as tightly - as possible, in the order they were defined. - This automatic packing happens after your components are defined, -

    - -

    - For example, if you provide a select menu, six buttons and another select menu, then the container - packs your components into rows that look like this: -

    - - -
  • Select menu
  • -
  • Five buttons
  • -
  • One button
  • -
  • Select menu
  • -
  • Nothing
  • -
    - -

    - If needed, you can provide row numbers when you define your components. - When you provide a row number, the container packs the component within the specified row - immediately. - If you combine this approach by also defining unsorted components (as mentioned above), the - container packs the unsorted components - alongside those with defined rows, attempting to fill the rows up as - tightly as possible. -

    - -

    - For example, if you provide a select menu on row 2, six unsorted buttons, and another select menu - on row 3, then the container packs your components into rows that look like this: -

    - - -
  • Five buttons
  • -
  • Select menu
  • -
  • Select menu
  • -
  • One button
  • -
  • Nothing
  • -
    -
    - - -

    - The following functions are available for component creation. - We've split these by interaction type, just like the - Application Command DSL functions. -

    - -

    - All functions take the following arguments: -

    - - -
  • Optional: ModalForm class constructor
  • -
  • Optional: Row number
  • -
  • Component builder
  • -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameComponent
    disabledButtonButton (Disabled)
    ephemeralButtonButton (Interactive)
    linkButtonButton (Link)
    publicButtonButton (Interactive)
    ephemeralChannelSelectMenuSelect Menu (Channel)
    ephemeralRoleSelectMenuSelect Menu (Role)
    ephemeralStringSelectMenuSelect Menu (String)
    ephemeralUserSelectMenuSelect Menu (User)
    publicChannelSelectMenuSelect Menu (Channel)
    publicRoleSelectMenuSelect Menu (Role)
    publicStringSelectMenuSelect Menu (String)
    publicUserSelectMenuSelect Menu (User)
    -
    - - -

    - The DSL functions above take a callable block as their last argument. - This block provides a set of properties and functions that allow you to configure the component. - Each DSL function also returns the configured component. -

    - - -

    - The following APIs are available within all component builders. -

    - - - - - - - - -
    NameTypeDescription
    -
    -
    - - -

    - The following APIs are available within component builders for actionable components, as defined - by the table at the top of this page. -

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    action - Specify a block of code to run when a user interacts with this component. - This is a receiver function against the corresponding context type for the component, - with the first argument set to an instance of the ModalForm provided to - the DSL function, if any. -
    check - Define a Check that must pass for the action to - be run. -
    requireBotPermissions - If your bot requires any permissions to be able to execute the action, use - this function to register those permissions. - If the component is interacted with while the bot doesn’t have the specified - permissions, an error will be sent to the user, explaining which permissions the bot is - missing. -
    -
    - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    deferredAckBoolean - Whether to respond with a deferred acknowledgement, which will hide the "Thinking..." - message when a user interacts with the component. - Defaults to true. -
    lockingBoolean - Whether to use a - - Mutex - to prevent this component's action from being run concurrently with itself. - - Defaults to false, allowing the component's action to run multiple - times concurrently. -
    -
    -
    - - -

    - The following APIs are available within component builders for components with IDs, as defined - by the table at the top of this page. -

    - - - - - - - - - - - - - - - -
    NameTypeDescription
    idString - A unique identifier for this component. - Kord Extensions uses this ID to figure out what code to execute when a user interacts - with a components. - Defaults to a randomly generated UUID. -
    -
    -
    - - -

    - The following APIs are available within component builders for all types of buttons. -

    - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    labelKey -

    - - Key object - representing the button's label, for display on discord. - You must provide either this or partialEmoji, or both. -

    - -

    - Note: Locales will not be automatically added to the - Key object. - Remember to use .withLocale(locale) when passing a Key object to this - property. -

    -
    partialEmojiDiscordPartialEmoji - The button's emoji, for display on discord. - You must provide either this or label, or both. -
    -
    - - -

    - Besides the APIs available to actionable components, - the following APIs are available to actionable button types. -

    - - - - - - - - - - - - - - - - - - - -
    NameDescription
    disable - Disable this button. -
    enable - Enable this button. -
    -
    - - - - - - - - - - - - - - - -
    NameTypeDescription
    disabledBoolean - Whether this button should render as disabled, and be impossible to interact with. - Defaults to false. -
    -
    -
    - - -

    - Besides the APIs available to components with IDs, - the following APIs are available to disabled buttons. -

    - - - - - - - - - - - - - - - -
    NameTypeDescription
    styleButtonStyle - Button style, used to render the button on Discord. - Defaults to Primary. -
    -
    -
    - - -

    - Besides the APIs available to components with IDs and - actionable components, the following APIs are available - to interaction buttons. -

    - - - - - - - - - - - - - -
    NameDescription
    initialResponse - Static callable representing the initial response to provide to Discord. - If you don't call this, Kord Extensions will acknowledge the interaction without - a message. -
    -
    - - - - - - - - - - - - - - - -
    NameTypeDescription
    styleButtonStyle - Button style, used to render the button on Discord. - Defaults to Primary. -
    -
    -
    - - -

    - The following APIs are available to link buttons. -

    - - - - - - - - - - - - - - - -
    NameTypeDescription
    urlString - URL to send the user to when they interact with the button. -
    -
    -
    -
    - - -

    - Besides the APIs available to actionable components, the - following APIs are available to all types of select menu. -

    - - - - - - - - - - - - - - - - - - - -
    NameDescription
    disable - Disable this select menu. -
    enable - Enable this select menu. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    disabledBoolean - Whether this select menu should render as disabled, and be impossible to interact with. - Defaults to false. -
    maximumChoicesInt? - The maximum number of options a user can pick. - Provide null to remove the limit. - Defaults to 1. -
    minimumChoicesInt - The minimum number of options a user can pick. - Must be at least 1. - Defaults to 1. -
    placeholderKey? -

    - - Key object - representing the placeholder text to show before the user has selected any - options. - Defaults to null. -

    - -

    - Note: Locales will not be automatically added to the - Key object. - Remember to use .withLocale(locale) when passing a Key object to this - property. -

    -
    -
    - - -

    - The following APIs are available to channel select menus. -

    - - - - - - - - - - - - - -
    NameDescription
    channelType - Add the given channel type to the list of allowed channel types for this menu. -
    -
    - - - - - - - - - - - - - - - -
    NameTypeDescription
    channelTypesMutableList<ChannelType> - List of allowed channel types for this menu. - Mutated by the channelType function. -
    -
    -
    - - -

    - The following APIs are available to string select menus. -

    - - - - - - - - - - - - - -
    NameDescription
    option - Add an option to the list of potential options for this menu. - This is optionally a builder function, used to configure the option's - description, emoji, and default selection state. -
    -
    - - - - - - - - - - - - - - - -
    NameTypeDescription
    optionsMutableList<SelectOptionBuilder> - List of potential options for this menu. - Add options using the option function. -
    -
    -
    -
    -
    - - -

    - The ComponentContainer type exposes several useful APIs. - The components DSL function is a receiver for an object of this type, and it also - returns that object. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    add - Manually add a component to this container. -
    cancel - Cancel the timeout task, and remove all components from this container. - This is equivalent to timing out the container early, but it will not run the - callback defined via the onTimeout function. - Note: This will not update the components in any related - messages — you'll need to edit them to clear their components manually. -
    onTimeout - Register the callback to be run when the container times out. - Note: The cancel function won't run this - callback. -
    remove - Remove the given component from the container, and prevent it from being used. - Note: This will not update the components in any related - messages — use the applyToMessage function to do so. -
    removeAll - Remove all components from the container, and prevent them from being used. - Note: This will not update the components in any related - messages — use the applyToMessage function to do so. -
    replace - Replace a component within this container with another. - Note: This will not update the components in any related - messages — use the applyToMessage function to do so. -
    sort - Pack any unsorted components into rows. - Note: You generally won't need to call this function - yourself. -
    - - - - - - - - - - - - - - -
    ReceiverNameDescription
    MessageBuilderapplyToMessage - Apply the components within this container to the given message builder. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    rowsArray< MutableList<Component> > - An array representing sorted rows of components. - The sort function fills this. -
    timeoutDuration? - If defined, the container will remove and unregister its components once it reaches the - timeout, unless component interactions have happened. - When interactions happen, the timeout resets. -
    timeoutCallbackComponentContainer.() -> Unit - A callback run when the timeout expires. - Defined by the onTimeout function. -
    timeoutTaskTask? - The scheduled timeout task, if a timeout exists. -
    unsortedComponentsMutableList<Component> - A list containing unsorted components, to be sorted by the sort function. -
    -
    -
    -
    - - -

    - For longer-lived message components, including those that must work after the bot restarts, it may be - better to create your components manually, and write your own Event Handlers. -

    - - -

    - You can define components using Kord's built-in APIs in any message creation/editing builder. - You'll need to define the action rows first, and then place components within them. -

    - - -

    - If your extension creates multiple long-lived components, you should think about the IDs you use - for them. -

    - -

    - Since you can parse the component ID when you receive an interaction event, it is a good idea to - use a system of prefixed IDs containing additional contextual information. - However, it is important to remember that your component IDs mustn’t clash with the components - defined in other extensions. -

    - -

    - Component IDs may be up to 100 characters long. -

    -
    - - - actionRow { - interactionButton(ButtonStyle.Success, "roles/add") { - label = "Add" - } - - interactionButton(ButtonStyle.Danger, "roles/remove") { - label = "Remove" - } - } - - -

    - You may use the following functions to define components: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDescription
    channelSelect - Create a channel select menu. -
    interactionButton -

    - Create an interaction button. - You may create a disabled button by setting disabled to true in - the builder. -

    - - - Providing a style of Link will result in an error. - For link buttons, use linkButton instead. - -
    linkButton - Create a link button. -
    mentionableSelect - Create a select menu for all mentionable entity types. -
    roleSelect - Create a role select menu. -
    stringSelect - Create a string select menu. -
    userSelect - Create a user select menu. -
    - -

    - For more information, see - - the Kord docs. -

    -
    - - -

    - Once you've created your components, you'll need to create some event handlers. - Kord provides several event types that are suitable for this: -

    - - -
  • - ComponentInteractionCreateEvent, fired when a user interacts with any of this bot's - components. -
  • - -
  • - ButtonInteractionCreateEvent, fired when a user interacts with any of this bot's - buttons. -
  • - -
  • - SelectMenuInteractionCreateEvent, fired when a user interacts with any of this bot's - select menus. -
  • -
    - -

    - These events also come in global and guild variants. -

    - - - val matchRegex = "role/\\w+".toRegex() - - event<GuildButtonInteractionCreateEvent> { - check { - failIf(!matchRegex.matches(event.interaction.componentId)) - } - - action { - val action = matchRegex - .matchEntire(event.interaction.componentId)!! - .groupValues[1] - - when (action) { - "add" -> { - // Add the role - } - - "remove" -> { - // Remove the role - } - - else -> event.interaction.respondEphemeral { - content = "Unknown action: $action" - } - } - } - } - - -

    - For more information, see - - the Kord docs. -

    -
    -
    -
    diff --git a/Writerside/topics/core/components/Modals.topic b/Writerside/topics/core/components/Modals.topic deleted file mode 100644 index 30b29534..00000000 --- a/Writerside/topics/core/components/Modals.topic +++ /dev/null @@ -1,570 +0,0 @@ - - - - - - -

    - The term "Modal" refers to a pop-up window within the Discord client. - Modals contain a title and a number of . -

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    TypeInputHas ID
    Action Row ❌ No
    Text Input (Line)Single Line✅ Yes
    Text Input (Paragraph)Multiple Lines✅ Yes
    - -

    - As the ModalForm type attempts to expose an API similar to the concept of UI forms; - Modal components are referred to as Widgets, to be placed on a grid. -

    - -

    - As of this writing, this concept hasn't yet been fully explored. - We plan to expand upon the concept later. -

    - - -

    - There are a number of limitations to keep in mind when working with Discord modals. -

    - - -
  • - Discord - won't tell the bot when a user closes a Modal without submitting it. - The only way around this is to use a timeout, and assume that the Modal won't be submitted once it - expires. -
  • - -
  • - As Modals use interactions, - they must be submitted within 15 minutes. - The interaction will fail if the user takes too long to submit a Modal. -
  • - -
  • - Modals have a very limited number of compatible Widgets. - They used to support Select Menus, but this support was suddenly removed without explanation. -
  • - -
  • - Modals may only be sent as the first response to an interaction. - This means they must be created and sent within 5 seconds, and they cannot be sent once the interaction - has been deferred, edited, or responded to. -
  • -
    -
    - - -

    - Kord Extensions represents Modals using the ModalForm type. - This type provides a container for a Modal's Widgets, settings, and data. - Similarly to command arguments classes, you'll - need to extend ModalForm when creating your Modals. -

    - - - class MyModal : ModalForm() { - override var title: String = "Test Modal" - - val line = lineText { - label = "Line Text" - placeholder = "A single line of text" - } - - val block = paragraphText { - label = "Paragraph Text" - placeholder = "A block of text which may span multiple lines" - } - } - - -

    - The ModalForm type exposes a number of APIs. -

    - - - - - - - - - - - - - - - - - - - -
    NameDescription
    lineText - Create a text input Widget which supports a single line of text. -
    paragraphText - Create a text input Widget which supports multiple lines of text. -
    - - - -

    - The following properties are available on all Widget types. -

    - - - - - - - - - - - - - - -
    NameTypeDescription
    valueT: Any? - The value provided by the user when the Modal is submitted. -
    -
    - - -

    - The following properties are available on all text-based Widget types. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    idString - The Widget's ID, used as the component ID on Discord. - Defaults to a randomly generated UUID. -
    initialValueKey? -

    - - Key object - representing an optional value to provide for this Widget. - This will show as a pre-filled value on Discord, for the user to edit. -

    - -

    - -

    -
    maxLengthInt - The maximum number of characters that a user may provide as a value. -
    minLengthInt - The minimum number of characters that a user may provide as a value. -
    placeholderKey? -

    - - Key object - representing some optional placeholder text. -

    - -

    - This will show as text within the component on Discord, in a lighter font, and - will be hidden when the user enters text into the component. -

    -
    requiredBoolean - Whether the user must provide a value for this Widget to submit the Modal. - Defaults to true. -
    translateInitialValueBoolean -

    - Whether to attempt to translate the initialValue when the Modal is sent - to Discord. - Defaults to false. -

    - -

    - When this is set to false, the key property on the - - Key object - provided to initialValue will be used, instead of translating it. -

    -
    valueString? - The value provided by the user when the Modal is submitted. - Will be null when no value was provided. -
    -
    -
    -
    - - - - - - - - - - - - - -
    NameDescription
    sendAndAwait -

    - Convenience function to send the Modal to the current interaction, wait for its completion, - and call the provided callback block. -

    - -

    - The callback block will receive a ModalSubmitInteraction as its first - parameter if the Modal was submitted on time, or null otherwise. -

    - -

    - Several overrides are provided, which will fill the function's parameters using the given - event or - command context object. -

    -
    -
    - - - - - - - - - - - - - - - - - - - -
    NameDescription
    sendAndDeferEphemeral - Calls sendAndAwait, returning a deferred ephemeral interaction response if the - Modal was submitted on time, or null otherwise. -
    sendAndDeferPublic - Calls sendAndAwait, returning a deferred public interaction response if the - Modal was submitted on time, or null otherwise. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    idString - Modal ID, - used to associate the ModalForm with the corresponding interaction events. - This is a randomly generated UUID by default. -
    timeoutDuration - How long to wait before assuming that the used closed the Modal. - Defaults to 15 minutes. -
    titleKey - Required: - Override this - Key object - to set the Modal's title, which will be shown in the Discord client. -
    -
    - - - -

    - There are several ways to support Modals in your bots. - We've detailed the major approaches below. -

    - - -

    - The simplest way to add a Modal to your bot is to create a ModalForm as described above, - and pass the constructor to your Application Command or - Component builder functions. -

    - -

    - This causes a Modal to be sent as the first response to that command invocation or component - interaction, - with the filled-in ModalForm (or null if timed out) provided - as an argument to the action block. -

    - - - publicButton(::MyModal) { - // Button configuration - - action { modal -> - // Button body - } - } - - -

    - For , - you may also combine this with an Arguments subtype. -

    - - - publicSlashCommand(::MyArguments, ::MyModal) { - // Command configuration - - action { modal -> - // Command body - } - } - -
    - - -

    - Sometimes, the fully automatic approach isn't appropriate. - For example, perhaps your Modal needs to be modified based on the arguments provided to the command, - or needs to be pre-filled with data from your database. -

    - -

    - For these cases, you can instantiate your ModalForm subtype yourself, using the types - provided by the Unsafe Module to delay the interaction response. -

    - -

    - This approach should be combined with the use of the sendAndDefer functions. -

    - - - unsafeSlashCommand { - // Command configuration - - action { - val modal = MyModal() - val result = modal.sendAndDeferEphemeral(this) - - if (result == null) { - // Modal timed out - } else { - // Modal was submitted, make use of it - } - } - } - - -

    - This approach is also appropriate for relevant event handlers. -

    - - - event<ButtonInteractionCreateEvent> { - // Event handler configuration - - action { - val modal = MyModal() - val result = modal.sendAndDeferEphemeral(this) - - if (result == null) { - // Modal timed out - } else { - // Modal was submitted, make use of it - } - } - } - -
    - - -

    - For greater control than either of the above approaches, you can also retain control over how your bot - responds to the Modal submission interaction. -

    - -

    - You can do this by following the semi-automatic approach, replacing the sendAndDefer - functions with sendAndAwait. -

    - - - - unsafeSlashCommand { - // Command configuration - - action { - val modal = MyModal() - - val result = modal.sendAndAwait(this) { interaction -> - // interaction will be `null` if the Modal timed out - interaction?.deferEphemeralResponse() - } - - if (result == null) { - // Modal timed out - } else { - // Modal was submitted, make use of it - } - } - } - -
    - - -

    - While there are very few situations where this is useful, it is possible to define and handle Modals - without using Kord Extensions' abstractions. - This is a relatively complex process in comparison, but it's still an approachable strategy. -

    - - - interaction.modal("title", "modalId") { - actionRow { - textInput( - TextInputStyle.Short, - "componentId", - "label" - ) { - allowedLength = 0 ... 1000 - placeholder = "Placeholder Text" - required = true - value = "Initial Value" - } - } - } - - // ... - - event<ModalSubmitInteractionCreateEvent> { - check { - failIfNot(event.interaction.modalId == "modalId") - } - - action { - // Value is null if empty/missing/wrong ID - val value = event.interaction.textInputs["componentId"]?.value - - // Event handler body - } - } - -
    - -
    diff --git a/Writerside/topics/core/configuration/Config-About.topic b/Writerside/topics/core/configuration/Config-About.topic deleted file mode 100644 index c72c50b7..00000000 --- a/Writerside/topics/core/configuration/Config-About.topic +++ /dev/null @@ -1,243 +0,0 @@ - - - - - - - - -

    - The about builder allows you to configure your bot's about command. -

    - - -

    - The about command includes a mandatory copyright section, mentioning Kord Extensions and - the libraries it uses. -

    - -

    - Because of how Kord Extensions is licensed, this ensures that your bot contains the required notice and way - to get the Kord Extensions source code. - You must not work around this, regardless of how easy or difficult it may be. -

    - -

    - If this is a big problem for you or your company, please get in touch via - the Discord server, and we'll try to figure out an alternative license for - you. -

    -
    - -

    - By default, Kord Extensions will create an about chat and slash command, with a - copyright subcommand. - You can configure this command using the about { } builder, adding your own sections and copyright - notices as appropriate. -

    - - - about { - ephemeral = false - - general { - message { locale -> - content = "General message content goes here!" - } - } - - section("another") { - description = "Command description goes here!" - - message { locale -> - content = "More content here!" - - embed { - description = "Maybe even an embed?" - } - } - } - } - - - -

    - The about { } builder provides the following properties. -

    - - - - - - - - - - - - - - -
    PropertyTypeDescription
    ephemeralBoolean - Whether commands (including the copyright command) should respond to slash commands ephemerally. - Defaults to true. -
    -
    - - -

    - While Kord Extensions provides the copyright subcommand by default, you're free to add any - other informational commands you wish. - These commands are referred to as "sections," and each section is represented by a subcommand on the - about command. -

    - -

    - To configure a section, use the section function. - This function takes the section name and description as arguments, and provides a builder you can use to - configure the section's options and provide a message. -

    - - - section("name", "description") { - description = "section description" - - message { - embed { - title = "Embed Title" - description = translate("a.translation.key") - } - } - } - - -

    - The about { } builder also provides a general builder, providing a standard - "general" section with a translated command name and description. -

    - - - - - - - - - - - - - -
    BuilderDescription
    message -

    - Call this DSL function to provide a message builder, - used to construct the message returned to the user - when they access the current section. -

    - -

    - This builder is a receiver against the MessageBuilder type, and takes the - current event context's Locale as the first argument. - This Locale object can be used to translate - - Key objects - if you need to use the i18n system. -

    -
    -
    - - - - - - - - - - - - - - - -
    PropertyTypeDescription
    ephemeralBoolean - Whether this section should respond to slash commands ephemerally. - If not provided, defaults to the setting provided in the about { } builder. -
    -
    -
    - - -

    - The contents of the copyright section are generated based on a list of copyright entries, - provided via the copyright function. -

    - -

    - Kord Extensions will automatically populate this list with the following data: -

    - - -
  • The dependencies it uses, along with those used by any first-party modules included with your bot.
  • -
  • A list of currently loaded plugins, without versioning information.
  • -
    - -

    - Copyright items may be added by calling the copyright function with a name, - SPDX license identifier, - item type (Framework, Library, Plugin/Module, or Tool), and optionally a URL. -

    - - - about { - copyright( - "Time4J", - "LGPL-2.1", - CopyrightType.Library, - "http://time4j.net/" - ) - } - - - -

    - If you're writing a module, library, or meta-framework, feel free to include whatever information you - feel is relevant by default. -

    - -

    - A simple way to do this is by creating an extension function, checking a top-level boolean value, and - calling this function in a setup function or class initializer. - For example: -

    - - - private var copyrightAdded = false - - internal fun AboutBuilder.addCopyright() { - if (!copyrightAdded) { - copyright( - "Time4J", - "LGPL-2.1", - CopyrightType.Library, - "http://time4j.net/" - ) - } - - copyrightAdded = true - } - - // ... - - class MyExtension : Extension() { - override fun setup() { - bot.settings.aboutBuilder.addCopyright() - } - } - -
    -
    -
    diff --git a/Writerside/topics/core/configuration/Config-Basic.topic b/Writerside/topics/core/configuration/Config-Basic.topic deleted file mode 100644 index 5d35af0b..00000000 --- a/Writerside/topics/core/configuration/Config-Basic.topic +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - -

    - You can directly set some basic configuration options by modifying their variables. -

    - - - - - - - - - - - - - - - - - - - - - - -
    NameDefaultDescription
    constructor::ExtensibleBot - If you've created your own bot type extending ExtensibleBot, provide its constructor - here, and it'll be used instead of the default implementation. -
    koinLogLevelERROR - The logging level Koin should use. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - -
    NameDefaultDescription
    dataCollectionModeStandard - -

    - For more information on the data collection system, what data is collected, and how you - can work with it, see the dedicated documentation. -

    -
    - -

    - Represents your bot's data collection mode. - This is normally set automatically by - the KordEx Gradle plugin. -

    - -

    - You can set this manually or let Kord Extensions figure it out for you based on the following - rules: -

    - - -
  • - Check the value of the dataCollection property. -
  • - -
  • - Check the value of the DATA_COLLECTION environmental variable. -
  • - -
  • - Check the value of the settings.dataCollection property in your bot's - kordex.properties resource file, generated by - the KordEx Gradle plugin. -
  • -
    - -

    - All checked values must be one of: extra, standard, - minimal, or none. -

    -
    devModefalse -

    - Represents whether your bot is running in development mode. - You can set this manually or let Kord Extensions figure it out for you based on the following - rules: -

    - - -
  • - Check whether the devMode property is set. -
  • - -
  • - Check whether the DEV_MODE environmental variable is set. -
  • - -
  • - Check whether the ENVIRONMENT environmental variable is set to - dev or development. -
  • -
    -
    -
    -
    \ No newline at end of file diff --git a/Writerside/topics/core/configuration/Config-Cache.topic b/Writerside/topics/core/configuration/Config-Cache.topic deleted file mode 100644 index a9f13968..00000000 --- a/Writerside/topics/core/configuration/Config-Cache.topic +++ /dev/null @@ -1,162 +0,0 @@ - - - - - - -

    - The cache builder allows you to change - - Kord's caching settings - . - Kord Extensions provides some additional default settings over Kord's own: -

    - - -
  • Configures a lruCache for caching up to 10,000 recent messages
  • -
  • - Changes the default caching strategy to cacheWithCachingRestFallback, which means that - Kord will cache all entities that are retrieved via REST calls in addition to hose provided by the - gateway. -
  • -
    - - -

    - It's possible that using this strategy may give you incomplete data when dealing with types of data that - aren't fully cached automatically. - For example, if a member is banned from a guild, and you don't yet have all the guild's bans in the - cache, - Kord will only keep track of bans that the bot has seen happen. - If this is a problem for you, then you have a few options: -

    - - -
  • - Fill the cache yourself. For example, for guild members, you could configure the - members builder to request all guild members from the gateway, and set up the required - intents. -
  • - -
  • - Change the default strategy to cacheWithRestFallback, which won't cache anything - retrieved via REST calls. -
  • - -
  • - Temporarily use another supply strategy when retrieving these types of data. For example, you could - use something like guild.withStrategy(EntitySupplyStrategy.cachingRest).bans. -
  • - -
  • - Use one of the equivalent fetch functions to skip the cache entirely. -
  • - -
    - -

    - For most purposes, the first approach will likely be the most sensible — but the correct approach will - heavily depend on your bot and how you need the cache to function. -

    -
    - - - ExtensibleBot(TOKEN) { - // ... - - cache { - cachedMessages = 10_000 - defaultStrategy = EntitySupplyStrategy.cacheWithCachingRestFallback - } - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    BuilderReceiverParametersDescription
    kord - - KordCacheBuilder - - - - ClientResources - - Register a callback that will be used to directly configure Kord's cache.
    transformCache - - Kord - - - - DataCache - - Register a callback that will be used to directly modify Kord's data cache settings.
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDefaultDescription
    cachedMessagesInt?10,000 - Number of recent messages Kord's cache should store by default. Use null to - disable. -
    defaultStrategy - - EntitySupplyStrategy - - - - cacheWithCachingRestFallback - - Kord's default caching strategy.
    -
    -
    \ No newline at end of file diff --git a/Writerside/topics/core/configuration/Config-Commands.topic b/Writerside/topics/core/configuration/Config-Commands.topic deleted file mode 100644 index 90a6a5c3..00000000 --- a/Writerside/topics/core/configuration/Config-Commands.topic +++ /dev/null @@ -1,341 +0,0 @@ - - - - - - - - -

    - The command builders allow you to configure Kord Extensions' command frameworks. Currently, Kord Extensions - supports two primary command types: -

    - - -
  • - Application commands, which include message, slash and user commands -
  • -
  • - Chat commands, which are commands via messages in Discord channels -
  • -
    - - -

    - With the introduction of slash commands, Kord Extensions has elected to de-prioritize chat commands. - This means that chat commands are disabled by default, and you'll need to enable them if you want to - use them. -

    - -

    - We intend to support chat commands indefinitely. - However, please note that most bots don't make use of them, and this means that there may be subtle - or unknown bugs present within the chat commands framework. - If you notice anything unusual or broken, please don't hesitate to submit a Pull Request, open an issue, - or contact us on Discord. -

    -
    - -

    - For more information on commands and how to write them, see - the dedicated documentation for them. -

    - - -

    - The applicationCommands builder allows you to configure the Kord Extensions application - commands framework, which includes - context (user and message) commands and - slash commands. -

    - - - ExtensibleBot(TOKEN) { - // ... - - applicationCommands { - defaultGuild(TEST_GUILD_ID) - } - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    BuilderReceiverDescription
    applicationCommandRegistry - Register a callable object (usually a constructor) that returns an alternative - implementation of the ApplicationCommandRegistry type. -
    messageCommandCheckMessageCommandCheck - Register a check that will apply to all registered - message commands. -
    slashCommandCheckSlashCommandCheck - Register a check that will apply to all registered - slash commands. -
    userCommandCheckUserCommandCheck - Register a check that will apply to all registered - user commands. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    FunctionParametersDescription
    defaultGuild - - Snowflake? - - - Specify a guild to register all global application commands to. - This is intended for testing, - and will apply to all application commands that don't have a registration guild specified. -
    defaultGuildString? - Specify a guild to register all global application commands to. - This is intended for testing, - and will apply to all application commands that don't have a registration guild specified. -
    defaultGuildULong? - Specify a guild to register all global application commands to. - This is intended for testing, - and will apply to all application commands that don't have a registration guild specified. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDefaultDescription
    enabledBooleantrue - Whether the application commands framework should be enabled. - If your bot does not use application commands, - setting this to false will completely disable them. -
    registerBooleantrue - Whether application commands should be registered with Discord automatically. Set this to - false if you only want your bot to process commands, rather than registering - them. -
    -
    -
    - - -

    - The applicationCommands builder allows you to configure the Kord Extensions chat - commands framework, which handles the parsing and processing of - chat commands. -

    - - - Discord has stated that all verified Discord bots must use - application commands instead of chat commands wherever possible. - However, your bot may still use chat commands for situations where application commands don't make - sense or provide an objectively worse user experience. - - - - ExtensibleBot(TOKEN) { - // ... - - chatCommands { - defaultPrefix = "!" - enabled = true - invokeOnMention = true - } - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    BuilderReceiverParametersDescription
    check - - MessageCreateEvent - - - Register a check that will apply to all registered - chat commands. -
    prefix - - MessageCreateEvent - - String - Set the prefix callback, which allows you to provide different command prefixes for - different contexts. - The String parameter represents the configured default prefix, which should - be returned by the callback if no custom prefix is required. -
    registry - - MessageCreateEvent - - - Register a callable object (usually a constructor) that returns an alternative - implementation of the ChatCommandRegistry type. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDefaultDescription
    defaultPrefixString"!" - The default command prefix, required before the name of a chat command at the start of a - message. - The prefix builder may be used when dynamic command prefixes are required. -
    enabledBooleanfalse - Whether the chat commands framework should be enabled. - If your bot uses chat commands, setting this to true will enable them. -
    ignoreSelfBooleantrue - Whether to ignore messages sent by the bot, and thus avoid executing commands invoked by it. -
    invokeOnMentionBooleantrue - Whether users should be able to run chat commands by mentioning the bot at the start of - their message, instead of using the configured prefix. -
    -
    -
    -
    diff --git a/Writerside/topics/core/configuration/Config-Components.topic b/Writerside/topics/core/configuration/Config-Components.topic deleted file mode 100644 index 7b6b0592..00000000 --- a/Writerside/topics/core/configuration/Config-Components.topic +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - -

    - The components builder allows you to change the component registry configuration, by swapping - out your own implementation. -

    - - - ExtensibleBot(TOKEN) { - // ... - - components { - registry(::MyComponentRegistrySubclass) - } - } - - - - - - - - - - - - - - -
    BuilderDescription
    registry - Register a callable object (usually a constructor) that returns an alternative - implementation of the ComponentRegistry type. -
    -
    -
    \ No newline at end of file diff --git a/Writerside/topics/core/configuration/Config-Data-Adapter.topic b/Writerside/topics/core/configuration/Config-Data-Adapter.topic deleted file mode 100644 index 01607b6c..00000000 --- a/Writerside/topics/core/configuration/Config-Data-Adapter.topic +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - -

    - The dataAdapter builder allows you to register a custom data adapter class, by providing a - callable object (usually a constructor) that returns an alternative implementation of - DataAdapter. -

    - -

    - For more information on data adapters and what they do, see - the dedicated documentation. -

    - - - ExtensibleBot(TOKEN) { - // ... - - dataAdapter(::TomlDataAdapter) - } - -
    \ No newline at end of file diff --git a/Writerside/topics/core/configuration/Config-Error-Responses.topic b/Writerside/topics/core/configuration/Config-Error-Responses.topic deleted file mode 100644 index 559f330f..00000000 --- a/Writerside/topics/core/configuration/Config-Error-Responses.topic +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - -

    - The errorResponse function allows you to register a custom failure response callback. This - callback takes the form of a receiver function against a - MessageCreateBuilder, - and it takes several parameters: -

    - - -
  • - message: A - Key object - representing the error message text. -
  • - -
  • - type: A FailureReason representing the type of error, and containing the - relevant exception. -
  • -
    - -

    - By default, Kord Extensions will relay the message text to Discord without editing it, preventing anyone - from being pinged by any mentions in the message text. - If you'd like, you can customize this by treating it as if you were sending any other message. -

    - - - ExtensibleBot(TOKEN) { - // ... - - errorResponse { - allowedMentions { } - - embed { - title = "Oopsie Woopsie!" - color = DISCORD_MAGENTA - - description = message - } - } - } - -
    diff --git a/Writerside/topics/core/configuration/Config-Extensions.topic b/Writerside/topics/core/configuration/Config-Extensions.topic deleted file mode 100644 index e89365af..00000000 --- a/Writerside/topics/core/configuration/Config-Extensions.topic +++ /dev/null @@ -1,463 +0,0 @@ - - - - - - -

    - The extensions builder allows you to register extensions, and configure bundled and - third-party extensions. -

    - - -

    - If you're developing an extension for others to use (and you aren't using the plugin system), - it's expected that you provide an extension function with this builder as the receiver. - For example: -

    - - - fun ExtensibleBotBuilder.ExtensionsBuilder.extMyExtension(/* args */) { - // ...any extra configuration.. - - add(::MyExtension) - } - -
    - - - ExtensibleBot(TOKEN) { - // ... - - extensions { - add(::MyExtension) - - extMyExtension() - - help { - pingInReply = false - - color { DISCORD_BLURPLE } - } - - sentry { - enable = true - dsn = "abc123" - } - } - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    BuilderReceiverDescription
    add - Register an extension to be loaded via a callable object (usually a constructor) that returns an - implementation of the Extension type. -
    helpHelpExtensionBuilder - Configure the currently loaded help extension, and enable or disable the bundled one. -
    sentrySentryExtensionBuilder - Configure the Kord Extensions Sentry extension and integration. -
    -
    - - -

    - The help builder allows you to configure the currently loaded chat command help extension, - and enable or disable the bundled one. -

    - - -

    - Kord Extensions provides an off-the-shelf extension for displaying chat command help. - However, you're free to use a third-party extension or build your own. - You may do so by creating an extension that implements the HelpProvider type, and - registering it as usual. -

    - -

    - If you develop a help extension meant for external use, you should disable the bundled one in your - extension function. -

    - - - fun ExtensibleBotBuilder.ExtensionsBuilder.myHelpExtension() { - help { - enableBundledExtension = false - } - - add(::MyHelpExtension) - } - -
    - - - - - - - - - - - - - - - - - - - - - - - -
    BuilderReceiverDescription
    colo(u)r - - MessageCreateEvent - - - Register a callback that returns a - Color object - to use for display in help embeds. - Will be called for every embed generated. -
    checkChatCommandCheck - Register a check that must pass for help commands to be - executed. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDefaultDescription
    deleteInvocationOnPaginatorTimeoutBooleanfalse - Whether to delete the message containing the help command invocation once the paginator - timeout has been reached. -
    deletePaginatorOnTimeoutBooleanfalse - Whether to delete the paginator output by the help command after the paginator timeout has - been reached. -
    enableBundledExtensionBooleantrueWhether to enable the bundled help extension.
    paginatorTimeoutLong60 - How long since the user's last button press to wait before the help paginator times out, - in seconds. -
    pingInReplyBooleantrue - Whether to mention (ping) users in reply to their help command invocation. -
    -
    -
    - - -

    - The sentry builder is used to configure the - Sentry integration, as well as enabling or disabling the - bundled extension used for collecting feedback from users when errors occur. -

    - - - Ensure that you've properly configured the Sentry integration before enabling - it. - Failure to do so will result in strange and undecipherable errors! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    BuilderReceiverDescription
    builder - Register a callable object (usually a constructor) that returns an alternative - implementation of the SentryAdapter type. -
    defaultDataTypesSentryDataTypeBuilder - Configure the default data types that Kord Extensions is allowed to send to Sentry in - breadcrumbs and exception reports. - This is done by setting the relevant Boolean properties that are exposed - on the provided SentryDataTypeBuilder object. -
    dataTypeTransformerSentryDataTypeBuilder.(SentryCapture) -

    - Register a Sentry data type transformer, allowing you to change the types of data that - may be submitted to Sentry based on the given SentryCapture object. -

    - -

    - Data type transformers are run before Kord Extensions adds the data defined in the - SentryCapture object's properties is added to the relevant Sentry object. -

    -
    predicateSentryCapture -

    - Register a Sentry submission predicate, allowing you to prevent the data in the given - SentryCapture object from being submitted to Sentry by returning - false. -

    - -

    - Sentry submission predicates are run just before Kord Extensions attempts to submit the - data defined in the SentryCapture object to Sentry, and thus runs after - the relevant data has been sanitized and processed. -

    -
    setupSentryAdapter - Replace the bundled setup callback with your own function, which must configure the - SentryAdapter by calling its init function. - This should only be used if you need to customize precisely what gets passed to that - function. -
    -
    - - -

    - The following properties are used to configure the - Kord Extensions Sentry integration, - rather than the Sentry API itself. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDefaultDescription
    enableBooleanfalse - Whether to enable the Kord Extensions Sentry integration. -
    feedbackExtensionBooleanfalse - Whether to enable the bundled Sentry feedback extension, which adds feedback - chat and slash commands, and provides error IDs with messages for errors submitted to - Sentry, - which users can use with the commands to submit feedback for. -
    pingInReplyBooleantrue - Whether to ping users in response to their feedback chat command invocations. -
    sampleRateDouble1.0 - How many submitted events to send to Sentry, as a percentage. - 1.0 means all events, 0.1 means 10% of the events, and so on. -
    - -

    - The following properties are used to configure the Sentry API directly. - Only the most common options are exposed here, but you can use the setup builder if - you need to configure other options. - For more information, see - the Sentry documentation. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDefaultDescription
    debugBooleanfalse - Whether to enable - Sentry's debug mode. -
    dsnString?null - Your - Sentry DSN, - required to enable the integration. -
    distributionString?null - Sentry's - Distribution - option. -
    environmentString?null - Sentry's - Environment - option. -
    releaseString?null - Sentry's - Release - option. -
    serverNameString?null - Sentry's - Server Name - option. -
    -
    -
    -
    \ No newline at end of file diff --git a/Writerside/topics/core/configuration/Config-Hooks.topic b/Writerside/topics/core/configuration/Config-Hooks.topic deleted file mode 100644 index cd4940ab..00000000 --- a/Writerside/topics/core/configuration/Config-Hooks.topic +++ /dev/null @@ -1,155 +0,0 @@ - - - - - - -

    - The hooks builder allows you to register blocks of code to be executed at various stages of - your bot's lifecycle. - This has a number of important use-cases, which will be explored below. -

    - - - ExtensibleBot(TOKEN) { - // ... - - hooks { - kordShutdownHook = true - - beforeKoinSetup { - loadModule { - single { Database() } bind Database::class - } - } - } - } - - - -

    - The following builders relate to extension loading states. -

    - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameReceiverDescription
    beforeExtensionsAddedExtensibleBotRun during startup, before any registered extensions are loaded.
    extensionAdded Run whenever an extension is loaded, at any point in the bot's lifecycle.
    afterExtensionsAddedExtensibleBotRun during startup, after all registered extensions are loaded.
    - -

    - The following builders relate to Koin setup progress. - You can learn more about Koin in our dedicated Koin documentation, which - explains how you should use it and what Kord Extensions does differently to the norm. -

    - - - - - - - - - - - - - - - - - -
    NameDescription
    beforeKoinSetup - Run during startup, after Koin has been started, - but before the default Koin modules have been registered. - This is a great place to register any extra Koin modules you'll need. -
    afterKoinSetupRun during startup, after the default Koin modules have been registered.
    - -

    - The following builders relate to the bot lifecycle itself. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameReceiverDescription
    createdExtensibleBot - Run during startup, right after the ExtensibleBot object has been created, and - before its setup function has been run. -
    setupExtensibleBot - Run during startup, right after the ExtensibleBot's setup function has been run. -
    beforeStartExtensibleBot - Run during startup, just before the ExtensibleBot tries to connect to Discord. - This hook will also be run if the ExtensibleBot has been stopped and restarted - via the relevant functions. -
    -
    - - - - - - - - - - - - - - - - - -
    NameTypeDefaultDescription
    kordShutdownHookBooleantrue - Whether to register Kord's shutdown hook, which formally disconnects from the gateway when the - process shuts down. -
    -
    -
    \ No newline at end of file diff --git a/Writerside/topics/core/configuration/Config-Intents.topic b/Writerside/topics/core/configuration/Config-Intents.topic deleted file mode 100644 index 4baf04b2..00000000 --- a/Writerside/topics/core/configuration/Config-Intents.topic +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - -

    - The intents builder allows you to configure which - Gateway Intents your bot - requires. - Intents define what events your bot is sent by the gateway, and what data it has access to. - Defining a limited set of intents can be useful to cut down on event processing or to help ensure your - users' privacy, by preventing your bot from processing data it doesn't strictly need. -

    - -

    - Additionally, there are a number of - privileged intents, - which must be enabled in the developer console for your bot. - Verified bots must justify to Discord why they need these intents, and their access to them may be - restricted by Discord. -

    - - - ExtensibleBot(TOKEN) { - // ... - - intents(true, true) { - +Intent.GuildMembers - +Intent.GuildPresences - } - } - - -

    - Intents may be added and removed using the unary + and - operators, respectively. - There are also some additional parameters you can pass to the intents builder function: -

    - - - - - - - - - - - - - - - - - - - - - - - - -
    ParameterTypeDefaultDescription
    addDefaultIntentsBooleantrue - Whether to add all non-privileged intents to the builder by default. -
    addExtensionIntentsBooleantrue - Whether to automatically add the intents required by the bot's loaded extensions to the builder. -
    - -

    - The above parameters mirror the default behavior of the intents builder, if you choose not to - provide your own. -

    -
    \ No newline at end of file diff --git a/Writerside/topics/core/configuration/Config-Internationalization.topic b/Writerside/topics/core/configuration/Config-Internationalization.topic deleted file mode 100644 index f9a0f3d3..00000000 --- a/Writerside/topics/core/configuration/Config-Internationalization.topic +++ /dev/null @@ -1,181 +0,0 @@ - - - - - - -

    - The i18n builder allows you to configure how the bot should handle translations and other types - of internationalization and localization. - Kord Extensions supports a number of locales. - You can find the current translation progress (or contribute your own translations) - on Weblate. -

    - - - ExtensibleBot(TOKEN) { - // ... - - i18n { - defaultLocale = SupportedLocales.SPANISH - - applicationCommandLocale(SupportedLocales.ALL_LOCALES_SET) - - interactionUserLocaleResolver() - interactionGuildLocaleResolver() - } - } - - - - - - - - - - - - - - - - - - - - - - - -
    NameParametersDescription
    localeResolver - - GuildBehavior? - - - - ChannelBehavior? - - - - UserBehavior? - - - - Interaction? - - -

    - Register a locale resolver. Locale resolvers are callables that return a - - Locale - object based on the parameters passed to it. - For more information, see the i18n documentation. -

    - - - Multiple locale resolvers may be registered. - They'll be tried in the order they were registered - if a resolver returns null, - the next one will be tried, until no more are left. - -
    translationsProvider - - Locale - - - Register a callable object (usually a constructor) that returns an alternative implementation of - the TranslationsProvider type. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameParametersDescription
    applicationCommandLocale - Collection / vararg: - -
    - - - Java Locale - - or - - Kord Locale - -
    - Registers the given locales as application command locales. - These locales will be used to provide command name translations when they're being registered - with Discord. - Do not provide the defaultLocale, as it will be used - automatically. -
    interactionGuildLocaleResolver - Registers a locale resolver (via localeResolver) which attempts to resolve locales - using the current guild's server settings. -
    interactionUserLocaleResolver - Registers a locale resolver (via localeResolver) which attempts to resolve locales - using the current user's Discord language setting. -
    -
    - - - - - - - - - - - - - - - - - - - -
    NameTypeDefaultDescription
    defaultLocale - - Locale - - SupportedLocales.ENGLISH - The default - - Locale - - to fall back to if the configured locale resolvers don't return one, and to use as the default - language when registering application commands. -
    -
    -
    diff --git a/Writerside/topics/core/configuration/Config-Kord.topic b/Writerside/topics/core/configuration/Config-Kord.topic deleted file mode 100644 index e6ba4be4..00000000 --- a/Writerside/topics/core/configuration/Config-Kord.topic +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - -

    - For advanced use-cases, Kord Extensions allows you to customize the underlying - Kord - instance in a number of ways. -

    - - -

    - Be careful! -

    -

    - For most bots, these customizations will not be useful. - They're provided for advanced use-cases and complex bots with specific requirements, but it's important to be - careful when customizing your bot's - Kord - instance. -

    - -

    - - Careless use of these customizations may result in strange breakages! - - - If you understand and are happy to take responsibility for this, feel free to click/tap on the "Builders" - header below for more information. -

    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    BuilderReceiverParametersDescription
    customKordBuilder - token: String, - builder: KordBuilder.() - -

    - Register a callable that constructs and returns a - Kord - instance. - This is intended to be used to replace the - Kord - instance with a subtype. -

    - -

    - Note: - Failure to call the supplied builder parameter will prevent Kord Extensions from - running its own - - KordBuilder - - hooks, or those that were registered via the kord function. -

    -
    eventFilterEvent -

    - Register a predicate which filters Kord events from the Kord Extensions events flow, which - may be an optimization in some situations. -

    - -

    - Only one predicate may be registered. - Registering a new predicate will overwrite the previous one. -

    -
    kord - - KordBuilder - - -

    - Register a hook that allows you to directly interact with the - - KordBuilder - - being used to construct the current - Kord - instance, allowing you to customize it without replacing the - Kord - with your own custom subtype. -

    - -

    - Registered hooks will be run in order after Kord Extensions' own hooks, so you can use this to - override the changes it makes if required. -

    -
    -
    -
    diff --git a/Writerside/topics/core/configuration/Config-Members.topic b/Writerside/topics/core/configuration/Config-Members.topic deleted file mode 100644 index fbe3c62f..00000000 --- a/Writerside/topics/core/configuration/Config-Members.topic +++ /dev/null @@ -1,165 +0,0 @@ - - - - - - -

    - The members builder allows you to configure a few settings related to guild members. -

    - - - ExtensibleBot(TOKEN) { - // ... - - members { - fillPresences = false - - fill(TEST_GUILD) - } - } - - - -

    - This builder requires the use of privileged intents, which must be enabled - in your bot's developer dashboard. - If your bot uses privileged intents, you may be required to justify your use of them to Discord. -

    - - -
  • - GuildMembers: Required to request guild member entities from - the gateway or REST API. -
  • - -
  • - GuildPresences: Required along with - GuildMembers to request the presence status information - belonging to guild member entities. - This includes online/away/DnD statuses, as well as what application a user is using, what game they're - playing, what they're listening to on Spotify, and their rich presence information. -
  • -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameParametersDescription
    all Specify that you want to receive all member entities from all guilds.
    fill - Collection: - - - Snowflake - , - - String or - ULong - - Specify multiple guild IDs to request all member entities from. - May be called multiple times. -
    fill - - Snowflake - , - - String or - ULong - - Specify a guild ID to request all member entities from. - May be called multiple times. -
    none - Specify that you do not want to receive any member entities from all - guilds. - This is the default behavior. -
    -
    - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDefaultDescription
    fillPresencesBoolean?null - Whether to request presence information from member entities requested from the gateway. - Supply null to defer to Kord's recommended default behavior, which is currently - equivalent to false. -
    lockMemberRequestsBooleanfalse -

    - Whether to force guild member requests to be sequential via a - - Mutex - , preventing concurrent requests for guild members. - This will significantly slow down requests for guild members, but may help larger bots to avoid - hitting a rate limit. -

    - - -

    - We believe there are probably better ways to do this, but we lack the experience with large - bots to come up with another solution. -

    - -

    - If you have any ideas, we'd appreciate it if you'd open an issue or submit a pull request! -

    -
    -
    -
    -
    \ No newline at end of file diff --git a/Writerside/topics/core/configuration/Config-Plugins.topic b/Writerside/topics/core/configuration/Config-Plugins.topic deleted file mode 100644 index fc950bff..00000000 --- a/Writerside/topics/core/configuration/Config-Plugins.topic +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - -

    - The plugins builder allows you to configure the dynamic plugin system. - This system allows you to create plugin JARs, which may be placed in a folder and loaded at runtime. -

    - - -

    - The plugin system is not ready for use. - While there is a plugin system currently present in Kord Extensions, we're planning to replace it with a - new, more custom solution. -

    - -

    - Until that point, we're leaving this system undocumented intentionally. -

    -
    -
    \ No newline at end of file diff --git a/Writerside/topics/core/configuration/Config-Presence.topic b/Writerside/topics/core/configuration/Config-Presence.topic deleted file mode 100644 index b6214661..00000000 --- a/Writerside/topics/core/configuration/Config-Presence.topic +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - -

    - The presence builder allows you to configure the bot's initial presence, which will be set when - it connects to Discord. - The builder provides direct access to Kord's - - PresenceBuilder - . -

    - - - ExtensibleBot(TOKEN) { - // ... - - presence { - status = PresenceStatus.DoNotDisturb - - competing("the Uptime League") - } - } - -
    diff --git a/Writerside/topics/core/configuration/Config-Sharding.topic b/Writerside/topics/core/configuration/Config-Sharding.topic deleted file mode 100644 index 892c8294..00000000 --- a/Writerside/topics/core/configuration/Config-Sharding.topic +++ /dev/null @@ -1,33 +0,0 @@ - - - - - - -

    - The sharding builder allows you to configure Kord's sharding settings, which by default will - automatically shard your bot across multiple gateways in a single process. -

    - -

    - This corresponds with Kord's - - sharding - - function. - However, the default settings should be sufficient for most bots. -

    - - - ExtensibleBot(TOKEN) { - // ... - - sharding { recommended -> - Shards(recommended) - } - } - -
    \ No newline at end of file diff --git a/Writerside/topics/core/events/Custom-Events.topic b/Writerside/topics/core/events/Custom-Events.topic deleted file mode 100644 index f80c3c04..00000000 --- a/Writerside/topics/core/events/Custom-Events.topic +++ /dev/null @@ -1,154 +0,0 @@ - - - - - - -

    - If you'd like to create your own events, you have two options: -

    - - -
  • - Extend Kord's - Event type. - This is provided as an option mostly so that you can integrate other Kord-based libraries with Kord - Extensions. -
  • - -
  • - Extend the KordExEvent type, which itself extends Kord's Event type. - This also extends the KordExKoinComponent type, providing access. -
  • -
    - - - Events based on the KordExEvent type are not gateway-relevant and always have a shard - property with a value of -1. - If your event is gateway-relevant, then you may be better off extending Kord's - Event type - instead, depending on the situation. - - -

    - The KordExEvent type has an identical API to the Kord - Event type. -

    - - - public data class MyEvent( - public val data: String - ) : KordExEvent - - -

    - Once you've constructed an event object, you can fire it using the send function that's part of - the ExtensibleBot type. - If you need to get a reference to this, you can do so via . -

    - - - class MyClass : KordExKoinComponent { - val bot: ExtensibleBot by inject() - - fun myFunction() { - val event = MyEvent("data") - - bot.send(event) - } - } - - - -

    - When an event deals with specific entity types, it's important that it implements the relevant rich base - types. -

    - - - Failure to implement the correct types will make your events incompatible with
    . - It's extremely important to implement the correct types! - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameParent TypesDescription
    GuildEvent - Base type for events that contain guild information. - This must be provided in the form of a - - GuildBehavior - - object. -
    MemberEventGuildEvent, UserEvent - Base type for events that contain guild member information. - This must be provided in the form of a - - MemberBehavior - - object. -
    MessageEvent - Base type for events that contain message information. - This must be provided in the form of a - - MessageBehavior - - object. -
    RoleEvent - Base type for events that contain role information. - This must be provided in the form of a - - RoleBehavior - - object. -
    UserEvent - Base type for events that contain user information. - This must be provided in the form of a - - UserEvent - - object. -
    - -
    \ No newline at end of file diff --git a/Writerside/topics/core/events/Extra-Events.topic b/Writerside/topics/core/events/Extra-Events.topic deleted file mode 100644 index adafc8b6..00000000 --- a/Writerside/topics/core/events/Extra-Events.topic +++ /dev/null @@ -1,707 +0,0 @@ - - - - - - -

    - Kord Extensions provides a number of extra event types. You can make use of these in your extensions - in the usual way. -

    - - -

    - There's a huge number of extra events, especially in relation to commands. - For the sake of brevity (and the author's wrists), only the base types and a description of the behaviors - represented by the very dense event types have been provided. -

    - -

    - For more information, inspect the base types in your IDE and navigate to their implementations. -

    -
    - - -

    - Command events relate to various stages in a command's execution. -

    - - -

    - The generic command event types represent the different actions command events may be fired for. - These base types include the properties you'll need to work with the relevant commands and events, - along with other shared properties. -

    - -

    - All generic command events take two generic types, which will be used later: -

    - - -
  • C, the Command type.
  • - -
  • - E, the - - - Event - - - type. -
  • -
    - -

    - The above types are used to define two properties that are common to all command events: -

    - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    commandCThe relevant command object.
    eventEThe relevant event object.
    - - -

    - The type for this event is CommandInvocationEvent. -

    - -

    - This event is fired when a command is invoked, before any processing happens. - As this event is fired as soon as the command is invoked, you'll receive it regardless of whether - the invocation succeeds or fails. -

    - -

    - This event does not support any extra properties. -

    -
    - - -

    - The type for this event is CommandSucceededEvent. -

    - -

    - This event is fired after a successful command invocation. -

    - -

    - This event does not support any extra properties. -

    -
    - - -

    - The type for this event is CommandFailedEvent. -

    - -

    - This event is fired after a failed command invocation. - As command invocations may fail for a number of reasons, this is a base type that's extended by - more specific event types. -

    - -

    - This event does not support any extra properties. -

    - - -

    - The type for this event is CommandFailedChecksEvent. -

    - -

    - This event is fired after a failed command invocation, where the invocation failed because - a required check didn't pass. -

    - -

    - This event supports the following extra properties: -

    - - - - - - - - - - - - - - -
    NameTypeDescription
    reasonKey - - Key object - representing a human-readable string, which is the error string returned by the - failing check. -
    -
    - - -

    - The type for this event is CommandFailedWithExceptionEvent. -

    - -

    - This event is fired after a failed command invocation, where the invocation failed because - an unexpected exception was thrown -

    - -

    - This event supports the following extra properties: -

    - - - - - - - - - - - - - - -
    NameTypeDescription
    throwableThrowable - The unexpected Throwable that was thrown. - This will usually be an Exception, but an extension can realistically - throw anything that extends Throwable, so this isn't guaranteed. -
    -
    - - -

    - The type for this event is CommandFailedParsingEvent. -

    - -

    - This event is fired after a failed command invocation, where the invocation failed because - of a command argument processing failure. - This event type will only be fired by slash and chat commands, as other command types - do not support argument parsing. -

    - -

    - This event supports the following extra properties: -

    - - - - - - - - - - - - - - -
    NameTypeDescription
    exceptionArgumentParsingException - An exception object representing the argument parsing failure. - Contains the reason for the failure. -
    -
    -
    -
    - - -

    Chat command events extend the generic command events, with the following generic types supplied:

    - - -
  • C: ChatCommand<*>
  • - -
  • - - E: MessageCreateEvent - -
  • -
    - -

    The following subtypes are supported for chat commands:

    - - -
  • ChatCommandInvocationEvent
  • -
  • ChatCommandSucceededEvent
  • -
  • ChatCommandFailedChecksEvent
  • -
  • ChatCommandFailedParsingEvent
  • -
  • ChatCommandFailedWithExceptionEvent
  • -
    -
    - - -

    Message command events extend the generic command events, with the following generic types supplied:

    - - -
  • C: MessageCommand<*, *>
  • - -
  • - - E: MessageCommandInteractionCreateEvent - -
  • -
    - -

    The following generic base types are supported for message commands:

    - - -
  • MessageCommandInvocationEvent
  • -
  • MessageCommandSucceededEvent
  • -
  • MessageCommandFailedEvent
  • -
  • MessageCommandFailedChecksEvent
  • -
  • MessageCommandFailedWithExceptionEvent
  • -
    - -

    - The above generic base types exist because message commands are a form of application command. - Concrete types exist, with the following prefixes based on the current interaction type: -

    - - -
  • Ephemeral for the ephemeral interaction type.
  • -
  • Public for the public interaction type.
  • -
    -
    - - -

    Slash command events extend the generic command events, with the following generic types supplied:

    - - -
  • C: SlashCommand<*, *, *>
  • - -
  • - - E: ChatInputCommandInteractionCreateEvent - - - (Discord calls these interactions "chat input command interactions", so Kord mirrors that naming - scheme). -
  • -
    - -

    The following generic base types are supported for slash commands:

    - - -
  • SlashCommandInvocationEvent
  • -
  • SlashCommandSucceededEvent
  • -
  • SlashCommandFailedEvent
  • -
  • SlashCommandFailedChecksEvent
  • -
  • SlashCommandFailedParsingEvent
  • -
  • SlashCommandFailedWithExceptionEvent
  • -
    - -

    - The above generic base types exist because slash commands are a form of application command. - Concrete types exist, with the following prefixes based on the current interaction type: -

    - - -
  • Ephemeral for the ephemeral interaction type.
  • -
  • Public for the public interaction type.
  • -
    -
    - - -

    User command events extend the generic command events, with the following generic types supplied:

    - - -
  • C: UserCommand<*, *>
  • - -
  • - - E: UserCommandInteractionCreateEvent - -
  • -
    - -

    The following generic base types are supported for user commands:

    - - -
  • UserCommandInvocationEvent
  • -
  • UserCommandSucceededEvent
  • -
  • UserCommandFailedEvent
  • -
  • UserCommandFailedChecksEvent
  • -
  • UserCommandFailedWithExceptionEvent
  • -
    - -

    - The above generic base types exist because user commands are a form of application command. - Concrete types exist, with the following prefixes based on the current interaction type: -

    - - -
  • Ephemeral for the ephemeral interaction type.
  • -
  • Public for the public interaction type.
  • -
    -
    - -
    - - -

    - Extension events directly relate to extensions, rather than the components that belong to them. -

    - - -

    - The type for this event is ExtensionStateEvent. -

    - -

    - This event is fired when an extension's loading state changes. - This may happen during any point in the bot's lifecycle, as extensions are loaded and unloaded. -

    - -

    - This event supports the following extra properties: -

    - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    extensionExtensionThe extension which has had its state changed.
    stateExtensionState - The extension's new state, one of: FAILED_LOADING, FAILED_UNLOADING, - LOADED, LOADING, UNLOADED or UNLOADING. -
    -
    - -
    - - -

    - Modal events relate to the Modals framework, and its various states of execution. -

    - - -

    - The type for this event is ModalInteractionCompleteEvent. -

    - -

    - This event is fired when a modal interaction has completed. - Generally, this is used by the commands framework - to figure out when a modal action should be run. - You can use this to respond to modals yourself as well. - For more information on modals, see the dedicated documentation. -

    - -

    - This event supports the following extra properties: -

    - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    idStringThe modal form's ID.
    interaction - - ModalSubmitInteraction - - - The interaction object provided by - - - the corresponding Kord event. -
    -
    -
    - - -

    - Supplementary events fill in the gaps, providing extra events for situations Kord doesn't provide a useful - event for. - This may include experimental Discord features, or Discord features that aren't widely supported. -

    - -

    - If Kord doesn't support a Discord event, it will fire an - - UnknownEvent - . - If you're listening for this event, make sure you avoid processing it when you're listening for the - corresponding supplementary event. -

    - - -

    - Guild join request events are fired during the various stages of onboarding a user must pass through - before being granted access to a guilds. -

    - -

    - These events also include the data for an old, experimental Discord feature, which allowed servers to - create a set of questions users must answer when joining a guilds, preventing them from interacting - until the guilds's staff team reviewed them. -

    - -

    - Discord ultimately decided against going through with this feature, as it negatively impacted user - retention (and thus made the guilds using it less profitable for the platform). - However, the guilds that took part in the experiment no longer have a way to configure or disable it, - necessitating access to this event data. -

    - -

    - For guilds not taking part in the above experiment, these events may still be fired as part of the - normal onboarding process. -

    - - -

    - The events in this section represent the following Discord event types, as provided by the Kord - - UnknownEvent - - type: -

    - - -
  • GUILD_JOIN_REQUEST_DELETE
  • -
  • GUILD_JOIN_REQUEST_UPDATE
  • -
    -
    - - -

    - The type for this event is GuildJoinRequestDeleteEvent. - It extends the following types: -

    - - -
  • - KordExEvent -
  • - -
  • - MemberEvent -
  • - -
  • - - Strategizable - -
  • -
    - -

    - This event is fired when a user's guild join request is deleted, which happens when they leave - the server before submitting or finishing their application. - No delta information is provided by Discord, so you may need to keep track of your own state along - with the relevant member's pending property. -

    - -

    - This event supports the following extra properties: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    dataGuildJoinRequestDeleteContainer class representing the data from Discord.
    guildId - - Snowflake - - The relevant guild ID.
    requestId - - Snowflake - - The relevant guild request ID.
    userId - - Snowflake - - The relevant user ID.
    -
    - - -

    - The type for this event is GuildJoinRequestUpdateEvent. - It extends the following types: -

    - - -
  • - KordExEvent -
  • -
  • - MemberEvent -
  • -
  • - - Strategizable - -
  • -
    - -

    - This event is fired when a user's guild join request is created or updated. - No delta information is provided by Discord, so you may need to keep track of your own state along - with the relevant member's pending property. -

    - -

    - This event supports the following extra properties: -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    NameTypeDescription
    dataGuildJoinRequestUpdateContainer class representing the data from Discord.
    guildId - - Snowflake - - The relevant guild ID.
    requestGuildJoinRequest - Guild join request data class, mostly containing data relating to guild access application - questions. -
    requestId - - Snowflake - - The relevant guild request ID.
    statusApplicationStatusEnum representing the status of the current guild join request.
    userId - - Snowflake - - The relevant user ID.
    -
    -
    - -
    - diff --git a/Writerside/topics/core/utilities/Koin.topic b/Writerside/topics/core/utilities/Koin.topic deleted file mode 100644 index 39828c2e..00000000 --- a/Writerside/topics/core/utilities/Koin.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/core/utilities/Messages.topic b/Writerside/topics/core/utilities/Messages.topic deleted file mode 100644 index a52b65a8..00000000 --- a/Writerside/topics/core/utilities/Messages.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/core/utilities/Paginators.topic b/Writerside/topics/core/utilities/Paginators.topic deleted file mode 100644 index 0a36a1b5..00000000 --- a/Writerside/topics/core/utilities/Paginators.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/core/utilities/Parsers.topic b/Writerside/topics/core/utilities/Parsers.topic deleted file mode 100644 index 1364c951..00000000 --- a/Writerside/topics/core/utilities/Parsers.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/core/utilities/Schedulers.topic b/Writerside/topics/core/utilities/Schedulers.topic deleted file mode 100644 index 82a9a298..00000000 --- a/Writerside/topics/core/utilities/Schedulers.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/core/utilities/Sentry.topic b/Writerside/topics/core/utilities/Sentry.topic deleted file mode 100644 index 5f8f9a24..00000000 --- a/Writerside/topics/core/utilities/Sentry.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/data-adapters/MongoDB.topic b/Writerside/topics/data-adapters/MongoDB.topic deleted file mode 100644 index f6497f9f..00000000 --- a/Writerside/topics/data-adapters/MongoDB.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/functional-modules/Mappings.topic b/Writerside/topics/functional-modules/Mappings.topic deleted file mode 100644 index 1a76aa5a..00000000 --- a/Writerside/topics/functional-modules/Mappings.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/functional-modules/Phishing.topic b/Writerside/topics/functional-modules/Phishing.topic deleted file mode 100644 index cd8844b4..00000000 --- a/Writerside/topics/functional-modules/Phishing.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/functional-modules/PluralKit.topic b/Writerside/topics/functional-modules/PluralKit.topic deleted file mode 100644 index 8c64f2de..00000000 --- a/Writerside/topics/functional-modules/PluralKit.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/getting-started/Manual-Setup.topic b/Writerside/topics/getting-started/Manual-Setup.topic deleted file mode 100644 index 5943f876..00000000 --- a/Writerside/topics/getting-started/Manual-Setup.topic +++ /dev/null @@ -1,243 +0,0 @@ - - - - - Manual Setup - - -

    - Latest Version: - %version% -

    -

    - Minimum JDK: - %java-version% -

    - -

    - Repo: - https://repo.kordex.dev/snapshots/ -

    - -

    - Coordinate: - dev.kordex:kord-extensions -

    -
    - - - This page is about manually setting up Kord Extensions in your Gradle projects. - If you're using a Kotlin Gradle build script, we recommend - using the Gradle plugin instead - if possible. - - -

    Before getting started, please ensure you have the following installed:

    - - -
  • Git
  • -
  • -

    - Java Development Kit (version %java-version% or later) -

    - - - Ensure that the version of Java you install is - compatible with Gradle. - It can take a while for Gradle to add support for the latest versions of Java, so this is important! - -
  • -
    - - - - - - -

    - First, add a dependency to your gradle/libs.versions.toml. -

    -
    - - - [versions] - kord-extensions = "%version%" - - [libraries] - kord-extensions = { module = "dev.kordex:kord-extensions", version.ref = "kord-extensions" } - - -

    - Then, update your build.gradle.kts. -

    -
    - - - repositories { - // ... - - mavenCentral() - - maven { - name = "KordEx (Releases)" - url = uri("https://repo.kordex.dev/releases") - } - - maven { - name = "KordEx (Snapshots)" - url = uri("https://repo.kordex.dev/snapshots") - } - - maven { - name = "Sonatype Snapshots (Legacy)" - url = uri("https://oss.sonatype.org/content/repositories/snapshots") - } - } - - dependencies { - // ... - - implementation(libs.kord.extensions) - } - -
    - - -

    - First, add a dependency to your gradle/libs.versions.toml. -

    -
    - - - [versions] - kord-extensions = "%version%" - - [libraries] - kord-extensions = { module = "dev.kordex:kord-extensions", version.ref = "kord-extensions" } - - -

    - Then, update your build.gradle. -

    -
    - - - repositories { - // ... - - mavenCentral() - - maven { - name "KordEx (Releases)" - url "https://repo.kordex.dev/releases" - } - - maven { - name "KordEx (Snapshots)" - url "https://repo.kordex.dev/snapshots" - } - - maven { - name "Sonatype Snapshots (Legacy)" - url "https://oss.sonatype.org/content/repositories/snapshots" - } - } - - dependencies { - // ... - - implementation(libs.kord.extensions) - } - -
    - - - -

    - Kord Extensions is developed using Gradle, and none of the developers are familiar with Maven. -

    - -

    - While Gradle is highly recommended, if you do decide to use Maven, we'd appreciate feedback - on the below instructions — - especially if corrections or more detail is required. -

    -
    - -

    - Add the following to your pom.xml. - For more information, see - the official Maven guide for Kotlin. -

    -
    - - - <repositories> - <!-- ... --> - - <repository> - <id>kordex-snapshots</id> - <name>KordEx (Snapshots)</name> - <url>https://repo.kordex.dev/snapshots</url> - </repository> - - <repository> - <id>kordex-releases</id> - <name>KordEx (Releases)</name> - <url>https://repo.kordex.dev/releases</url> - </repository> - - <repository> - <id>sonatype-snapshots-legacy</id> - <name>Sonatype Snapshots (Legacy)</name> - <url>https://oss.sonatype.org/content/repositories/snapshots</url> - </repository> - </repositories> - - <dependencies> - <!-- ... --> - - <dependency> - <groupId>dev.kordex</groupId> - <artifactId>kord-extensions</artifactId> - <version>%version%</version> - </dependency> - </dependencies> - -
    - - - -

    - Kord Extensions is developed using Gradle, and none of the developers are familiar with SBT. -

    - -

    - While Gradle is highly recommended, if you do decide to use SBT, we'd appreciate feedback - on the below instructions — - especially if corrections or more detail is required. -

    -
    - -

    - Add the following to your build.sbt. - For more information, see - the Kotlin SBT plugin. -

    -
    - - - resolvers += "KordEx (Releases)" at "https://repo.kordex.dev/releases" - resolvers += "KordEx (Snapshots)" at "https://repo.kordex.dev/snapshots" - resolvers += Resolver.sonatypeOssRepos("snapshots") - - libraryDependencies ++= Seq( - "dev.kordex" % "kord-extensions" % "%version%" - ) - -
    -
    -
    -
    diff --git a/Writerside/topics/gradle-plugins/Docker-Plugin.topic b/Writerside/topics/gradle-plugins/Docker-Plugin.topic deleted file mode 100644 index c3a1d147..00000000 --- a/Writerside/topics/gradle-plugins/Docker-Plugin.topic +++ /dev/null @@ -1,646 +0,0 @@ - - - - - - - - - Docker Plugin - - -

    - Plugin ID: - dev.kordex.gradle.docker -

    - -

    - Plugin Version: - %plugin-version% -

    -
    - -

    - The Docker Gradle plugin is a Gradle plugin that helps to centralize your project's configuration by allowing - you to generate a Dockerfile via a Kotlin DSL. -

    - - -

    - First, add the Docker Gradle plugin to your build.gradle.kts. -

    - - - plugins { - id("dev.kordex.gradle.docker") version "%plugin-version%" - } - - -

    - Then configure it as explained below. -

    - - - import dev.kordex.gradle.plugins.docker.file.* - import dev.kordex.gradle.plugins.docker.file.commands.* - - docker { - // Settings go here. - - file(rootProject.file("Dockerfile")) - - commands { - from("openjdk:21-jdk-slim") - - // ... - } - } - -
    - - -

    - All settings must be provided within the docker { } builder. -

    - - -

    - The following settings are set directly within the docker { } builder. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    PropertyDefaultDescription
    - Required Properties -
    target -

    - A File object representing the Dockerfile to be generated. -

    -
    - Optional Properties -
    generateOnBuildtrue -

    - Whether to automatically generate the Dockerfile at build time, by adding the - createDockerfile task as a finalizer for the build task. -

    -
    -
    - - -

    - To add commands, call their respective functions within the commands { } builder. - Each command has its own function and configuration. -

    - - -

    - There are several functions which don't directly map to Dockerfile commands: -

    - - -
  • comment(text)
  • -
  • emptyLine()
  • -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • add(sources, target) { }
  • -
    -
    - - -

    - Within the builder, you may use the option function to add the following command - options: -

    - - -
  • Checksum(checksum)
  • -
  • Chmod(perms)
  • -
  • Chown(user, group)
  • -
  • Exclude(path)
  • -
  • KeepGitDir
  • -
  • Link
  • -
    -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • arg(key, value)
  • -
    -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • cmdExec(instruction, ...)
  • -
  • cmdShell(instructions)
  • -
    -
    - - -

    - This command supports both the exec-style and shell-style command formats, supplied via the - corresponding functions. -

    -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • copy(source, target) { }
  • -
  • copy(sources, target) { }
  • -
    -
    - - -

    - Within the builder, you may use the option function to add the following command - options: -

    - - -
  • Chmod(perms)
  • -
  • Chown(user, group)
  • -
  • Exclude(path)
  • -
  • From(source)
  • -
  • Link
  • -
  • Parents
  • -
    -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • entryPointExec(instruction, ...)
  • -
  • entryPointShell(instructions)
  • -
    -
    - - -

    - This command supports both the exec-style and shell-style command formats, supplied via the - corresponding functions. -

    -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • env(variables)
  • -
  • env { add(key, value) }
  • -
    -
    - - -

    - This command supports directly passing a map or using a builder, which provides an - add(key, value) function you can use. -

    -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • expose(port, protocol?, comment?)
  • -
    -
    - - -

    - The protocol parameter takes an instance of the Protocol sealed - class: -

    - - -
  • TCP
  • -
  • UDP
  • -
    -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • from(image, alias?, platform?)
  • -
    -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • healthcheck { }
  • -
    -
    - - -

    - Within the builder, you must call either check { } or none() to - define the health-check command. -

    - -

    - The check { } builder provides the cmdExec and cmdShell - functions to define the health-check command, and you may use the option function - to add the following command options: -

    - - -
  • Interval(duration)
  • -
  • Timeout(duration)
  • -
  • StartPeriod(duration)
  • -
  • StartInterval(duration)
  • -
  • Retries(number)
  • -
    -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • label { }
  • -
    -
    - - -

    - Within the builder, you may use the label(key, value) function to define labels. -

    -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • onBuild { }
  • -
    -
    - - -

    - Within the builder, you may use the other command functions documented in this section to add - on-build commands, with the following restrictions: -

    - - -
  • copy commands must not have the From option supplied.
  • -
  • You may not add from commands.
  • -
  • You may not add other onBuild commands.
  • -
    -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • runExec(command, ...) { }
  • -
  • runShell(command) { }
  • -
    -
    - - -

    - This command supports both the exec-style and shell-style command formats, supplied via the - corresponding functions. -

    - -

    - The following builders are available to configure the command: -

    - - -
  • - bindMount { } - -

    Required Settings:

    - - -
  • from
  • -
  • target
  • -
    - -

    Optional Settings:

    - - -
  • readWrite = false
  • -
  • source = null
  • -
    - -
    - - -
  • - cacheMount { } - -

    Required Settings:

    - - -
  • from
  • -
  • target
  • - - -

    Optional Settings:

    - - -
  • gid = 0
  • -
  • id = null
  • -
  • mode = "0755"
  • -
  • readOnly = false
  • - -
  • - sharing = Sharing.Shared - (Locked, Private or Shared) -
  • - -
  • source = null
  • -
  • uid = 0
  • -
    - -
    - - -
  • - tmpfsMount { } - -

    Required Settings:

    - - -
  • size
  • -
  • target
  • - - -
    - - -
  • - secretMount { } - -

    Optional Settings:

    - - -
  • gid = 0
  • -
  • id = null
  • -
  • mode = "0400"
  • -
  • required = false
  • -
  • target = null
  • -
  • uid = 0
  • - - -
    - - -
  • - sshMount { } - -

    Required Settings:

    - - -
  • target
  • - - -

    Optional Settings:

    - - -
  • gid = 0
  • -
  • id = null
  • -
  • mode = "0600"
  • -
  • required = false
  • -
  • uid = 0
  • -
    - -
    - - - -

    - The following functions are available to configure the command options: -

    - - -
  • - networkType(NetworkType) - (Default, Host or None) -
  • - -
  • - securityType(SecurityType) - (Insecure or Sandbox) -
  • -
    -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • shell(command, ...)
  • -
    -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • stopSignal(signal)
  • -
    -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • user(user, group?)
  • -
    -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • volume(volume, ...)
  • -
    -
    -
    - - -

    - This section corresponds with - the relevant Dockerfile reference section. -

    - - - -
  • workdir(dir)
  • -
    -
    -
    -
    - - - -

    - The Docker Gradle plugin automatically provides default values for the escape and - syntax directives, and you may run into unexpected behavior if you change them. -

    - - -
  • escape is set to /
  • -
  • syntax is set to docker/dockerfile:1
  • -
    -
    - -

    - If you want to add parser directives, call the directive function with the name and value - of the directive. -

    - - - docker { - directive("escape", "\\") - } - - -

    - To learn more about directives, see - the Dockerfile reference documentation. -

    -
    -
    -
    diff --git a/Writerside/topics/gradle-plugins/KordEx-Plugin.topic b/Writerside/topics/gradle-plugins/KordEx-Plugin.topic deleted file mode 100644 index 6b2da0eb..00000000 --- a/Writerside/topics/gradle-plugins/KordEx-Plugin.topic +++ /dev/null @@ -1,885 +0,0 @@ - - - - - - - KordEx Plugin - - -

    - Plugin ID: - dev.kordex.gradle.kordex -

    - -

    - Plugin Version: - %plugin-version% -

    -
    - -

    - The KordEx Gradle plugin is a Gradle plugin designed to make it easier to correctly configure Gradle projects - containing Kord Extensions bots and plugins. - This plugin is the recommended way to configure your project, but - you can also set things up manually if you're not using a Kotlin Gradle build - script, or otherwise can't (or don't wish to) use the plugin. -

    - - -

    - The KordEx plugin is designed to automatically set up your Gradle project to work with Kord Extensions. - As of this writing, it can handle the following for you: -

    - - -
  • -

    - Automatic configuration of Maven repositories: -

    - - -
  • KordEx, Google, Maven Central and OSSRH.
  • - -
  • - When using the Mappings module: FabricMC, QuiltMC, Shedaniel and - JitPack. -
  • -
    - - -
  • -

    - Automatic configuration of relevant dependencies: -

    - - -
  • Both Kord and Kord Extensions.
  • - -
  • -

    Any dependencies required by configured first-party modules:

    - - -
  • - When using the MongoDB data adapter: The latest versions - of the Kotlin Coroutines MongoDB driver, and the BSON adapter for kotlinx.serialization. -
  • - - - - - -
  • -

    - Configuration of several Gradle plugins: -

    - - -
  • - The Kotlin plugin — Setting the required Kotlin compiler arguments and setting the same JVM - target used by Kord Extensions. -
  • - -
  • - The Java plugin — Setting the same Java source/target compatibility settings used by Kord - Extensions. -
  • - -
  • - If the KSP plugin is applied, automatically adding the Kord Extensions annotation processor. -
  • - - - -
  • - Validation checks, including checking whether you're using the correct version of the Kotlin plugin for - the version of Kord Extensions you're working with. - You can disable this if needed. -
  • - -
  • -

    When configured for bot development:

    - - -
  • - Automatic configuration of the application plugin and jar task. -
  • - -
  • -

    - New in v1.3.1: - Creation of the dev task, used to run your bot in development mode. -

    - -

    - This task will recursively parse .env files in your project directories, - adding them to your in-development bot's runtime environment variables: -

    - - -
  • First, it parses the .env file in your project's root directory.
  • - -
  • - Then, if your bot is in a subproject/submodule, it'll parse .env files - found while traversing the directory tree down to it, and overriding any previously set - variables. -
  • - -
  • - Finally, it'll parse the .env file in your bot's module, as previously - described. -
  • - -
  • - This may be configured by setting processDotEnv - as explained below. -
  • - - - -
  • - Easy configuration of Kord Extensions' default data collection settings. -
  • - -
  • -

    - Generation of the kordex.properties resource file, which is automatically - added to your bot's resources. - This file contains: -

    - - -
  • Your configured data collection settings.
  • -
  • The version of Kord and Kord Extensions you're using.
  • -
  • A list of first-party Kord Extensions modules you're using.
  • - - -

    - You should not provide this yourself, as missing some data will break parts of - Kord Extensions. -

    - - - - -
  • - When configured for i18n work: automatic generation of translation classes containing your - translation bundle and translation keys. -
  • - -
  • -

    When configured for plugin development:

    - - -
  • - Automatic configuration of the distribution plugin. -
  • - -
  • - Generation of the plugin.properties resource file, which is automatically added to - your plugin's distribution. - This file contains plugin metadata based on your configuration. -
  • - -
  • -

    - Automatic plugin packaging via the distZip task, including: -

    - - -
  • Your plugin's classes, placed within the classes directory.
  • -
  • Your plugin's resources, also placed within the classes directory.
  • -
  • Your plugin's runtime dependencies, placed within the lib directory.
  • -
  • Your plugin's metadata, in the plugin.properties file.
  • - - - - - -
    - - -

    - First, add the KordEx Gradle plugin to your build.gradle.kts. -

    - - - plugins { - id("dev.kordex.gradle.kordex") version "%plugin-version%" - } - - -

    - Then, if needed, configure it as explained below. -

    - - - kordEx { - // Settings go here. - } - -
    - - - - -

    - All settings must be provided within the kordEx { } builder. - If you don't configure it for either bot or plugin development, then the plugin won't do anything. -

    - -

    - All settings are defined using Gradle Property objects. -

    - - -

    - You may only configure a Gradle project for either bot or plugin development, not both. - If you need to build both in the same project, you should - - separate them using Gradle subprojects - . -

    -
    - - -

    - The following settings apply to both the bot and plugin modes, - but they'll also be used when neither mode is specified. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    PropertyDefaultDescription
    addDependenciestrue -

    - New in v1.2.1: - Whether to automatically add the required dependencies for a standard Kord Extensions setup. -

    - -

    - If you need to create a "common" module depended on by bots, you may wish to set this - property to false in your bot modules to avoid duplicate dependencies. -

    -
    addRepositoriestrue -

    - Whether to automatically add the required Maven repositories for a standard Kord - Extensions setup. -

    - -

    - If you need to use a fork of either Kord or Kord Extensions, you may want to disable this - and set up your repositories manually. -

    -
    configurations -

    - A list of dependency configurations to use for the automatically added dependencies, - overriding the default configuration used. -

    - -

    - The default configuration is implementation, or compileOnly in - plugin mode. -

    -
    ignoreIncompatibleKotlinVersionfalse -

    - By default, your build will fail if the version of the Kotlin Gradle plugin you're using - doesn't match the version of Kotlin used to compile Kord Extensions. -

    - -

    - If you wish to use a different Kotlin version (and you know what you're doing), setting - this property to true will make this a warning instead. -

    -
    jvmTarget -

    - An integer, representing the JVM version your project should target. -

    - -

    - By default, this will be set to the minimum JVM version required by Kord Extensions, but - you may need to change this setting if one of your dependencies requires a newer JVM - version. -

    - -

    - The minimum JDK version required by Kord Extensions is version %java-version%. -

    -
    kordVersion -

    - Specify a specific Kord version if you need to. - Alternatively, - supply "latest" and the plugin will try to find - the latest non-feature-branch version of Kord. -

    - -

    - By default, the Kord version used to build the selected version of Kord Extensions is used. -

    - -

    - You'll want to specify a version yourself if you need to use your own fork of Kord. -

    -
    kordExVersionlatest -

    - Specify a specific Kord Extensions version if you need to. - Alternatively, - supply "latest" and the plugin will try to find the latest version of Kord - Extensions. -

    - -

    - By default, the latest released version of Kord Extensions is used, which will usually be - a snapshot. -

    - -

    - Depending on snapshots is the intended way to use Kord Extensions, but you'll want to - specify a version yourself if you need to hold back an update or use your own fork of - Kord Extensions. -

    -
    - - -

    - Many first-party Kord Extensions modules exist that provide extra development tools and various - user-facing bot functions. -

    - -

    - The repository layout is currently - a bit messy, - but it'll be cleaned up for the upcoming 2.0.0 release of Kord Extensions. -

    - -

    - In the meantime, check the above link for information on how to find our first-party modules. - Modules are named according to their containing folder — - for example, the PluralKit module is located in extra-modules/extra-pluralkit, - so the module name is extra-pluralkit. -

    - - - - - - - - - - - - -
    FunctionDescription
    module -

    - Add a first-party Kord Extensions module by name. - The KordEx plugin will also automatically add any extra dependencies and repositories - that the specified module requires. -

    -
    -
    -
    - - - - -

    - To configure your project for bot development, use the bot { } builder in the - kordEx { } builder. - For example: -

    - -

    - Bots should be built by running the build task. -

    - - - kordEx { - bot { - // https://kordex.dev/blog/2024-07-23/kordex-2#levels - dataCollection(DataCollection.Standard) - - mainClass = "my.package.MyMainClass" - } - } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    PropertyDefaultDescription
    - Required Properties -
    mainClass - Your bot's main class, which will usually refer to a top-level main function. - Providing this will automatically apply and configure the application plugin, - and add the main class reference to your bot's JAR manifest, making the built JAR executable. -
    - Optional Properties -
    processDotEnvtrue - New in v1.3.1: - -

    - Whether to recursively parse .env files when setting up the dev - task, as explained here. -

    -
    voicetrue -

    - Whether to use Kord's voice-enabled core module when depending on it. -

    - -

    - By default, this is enabled, which means that kord-core-voice will be used. - However, if your bot doesn't need to support Discord voice, you can set this property to - false and kord-core will be used instead. -

    - -

    - Using a version of Kord that doesn't support Discord voice may result in a smaller JAR - when your bot is built. -

    -
    - - - - - -

    - For in-depth information on the data collection system, please see - the dedicated documentation. -

    -
    - -

    - The default data collection level is Standard. -

    - - - - - - - - - - - - -
    FunctionDescription
    dataCollection -

    - Set your bot's default level to the given argument. -

    - -

    - Note: Users may override this at runtime using a - system property or an environmental variable. - It may also be manually configured in code, - as explained here. -

    -
    -
    -
    - - - - -

    - To configure your project for plugin development, use the plugin { } builder in the - kordEx { } builder. - For example: -

    - - - kordEx { - plugin { - id = "my-plugin-id" - pluginClass = "my.package.MyPluginClass" - version = "1.0.0" - } - } - - -

    - Plugins should be built by running the distZip task. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    PropertyDescription
    - Required Properties -
    id -

    - A unique ID that identifies your plugin. - This should be a short string, written using lower-kebab-case. -

    -
    pluginClass -

    - A reference to your plugin's main class, which must extend KordExPlugin. - This must include both the package and class name — for example, - my.package.here.ClassNameHere -

    -
    version -

    - Your plugin's version number, which must be - a valid semantic version number. -

    -
    - Optional Properties -
    author -

    - Whoever is responsible for this plugin. - This can be a single person, a comma-separated list of people, an email address, - an organization, or anything else you feel is appropriate. -

    -
    description -

    - A block of text that explains what your plugin is, what it does, and provides any other - relevant information. -

    -
    license -

    - The license your plugin is distributed under. - This does not have to be an open-source license. -

    -
    - Optional Functions -
    dependency -

    - Call this function to add a dependency on another plugin. - This function expects the following arguments: -

    - - -
  • id — the ID of the plugin you wish to depend on.
  • - -
  • - versionSpecifier (Optional) — A - JSemver range expression - describing which versions of the specified plugin are compatible with your plugin. -
  • - -
  • -

    - optional (Default: false) — Whether this is an optional - dependency. -

    - -

    - Optional dependencies are useful when your plugin optionally integrates with others, - ensuring that the other plugin is a compatible version before your plugin is - loaded. -

    -
  • -
    -
    kordExVersion -

    - Call this function to specify which versions of Kord Extensions your plugin is compatible - with. - You must provide a - JSemver range expression - as the first argument to this function. -

    -
    -
    - - - - - -

    - The Gradle plugin only supports i18n work when using Kord Extensions 2.3.0 or later. -

    -
    - -

    - To configure your project for i18n work, use the i18n { } builder in the - kordEx { } builder. - For example: -

    - - - kordEx { - i18n { - classPackage = "template.i18n" - translationBundle = "template.strings" - } - } - - -

    - Once configured for i18n work, the Gradle plugin will automatically generate a tree of translation - classes, containing your translation bundle and all of your translation keys. - The translation keys are taken from the corresponding base translation properties file in your - project's src/main/resources/translations/ directory. -

    - -

    - A translation class tree looks something like this: -

    - - - public object Translations { - public val bundle: Bundle = Bundle("template.strings") - - public object Commands { - public object Slap { - /** - * slaps {0} with their {1} - */ - public val action: Key = Key("commands.slap.action") - .withBundle(Translations.bundle) - - /** - * Ask the bot to slap another user - */ - public val description: Key = Key("commands.slap.description") - .withBundle(Translations.bundle) - } - } - } - - -

    - For more information on the i18n system and how translations work, see - the dedicated documentation. -

    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    PropertyDescription
    - Required Properties -
    classPackage -

    - The package the plugin should use when generating your translation classes. - Generated classes will be placed within this package. -

    -
    translationBundle -

    - The name of your - translation bundle. - This is used to locate your default translations file, and is automatically added to each - Key object in the generated classes. -

    -
    - Optional Properties -
    className -

    - The name to use for the root class of the generated translation class tree. -

    - -

    - Defaults to Translations. -

    -
    configureSourceSet -

    - Whether to automatically configure your project's source-sets to include the generated - translation classes. -

    - -

    - Defaults to true. -

    -
    outputDirectory -

    - Where to place the generated translation classes. - Regardless of this setting, generated classes are additionally placed within a generated - directory structure matching the configured classPackage setting. -

    - -

    - Defaults to build/generated/kordex/main/kotlin/. -

    -
    -
    -
    -
    diff --git a/Writerside/topics/utility-modules/Datetime-Converters.topic b/Writerside/topics/utility-modules/Datetime-Converters.topic deleted file mode 100644 index 8901052e..00000000 --- a/Writerside/topics/utility-modules/Datetime-Converters.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/utility-modules/Token-Parser.topic b/Writerside/topics/utility-modules/Token-Parser.topic deleted file mode 100644 index 1da5e797..00000000 --- a/Writerside/topics/utility-modules/Token-Parser.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/topics/utility-modules/Unsafe.topic b/Writerside/topics/utility-modules/Unsafe.topic deleted file mode 100644 index df9361f2..00000000 --- a/Writerside/topics/utility-modules/Unsafe.topic +++ /dev/null @@ -1,9 +0,0 @@ - - - - -

    Start typing here...

    -
    \ No newline at end of file diff --git a/Writerside/v.list b/Writerside/v.list deleted file mode 100644 index 65225a45..00000000 --- a/Writerside/v.list +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/Writerside/writerside.cfg b/Writerside/writerside.cfg deleted file mode 100644 index c9acccd3..00000000 --- a/Writerside/writerside.cfg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/about-sidebars.ts b/about-sidebars.ts new file mode 100644 index 00000000..b4287aef --- /dev/null +++ b/about-sidebars.ts @@ -0,0 +1,19 @@ +import type {SidebarsConfig} from "@docusaurus/plugin-content-docs"; + +// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) + +/** + * Creating a sidebar enables you to: + - create an ordered group of docs + - render a sidebar for each doc of that group + - provide next/previous navigation + + The sidebars can be generated from the filesystem, or explicitly defined here. + + Create as many sidebars as you want. + */ +const sidebars: SidebarsConfig = { + docSidebar: [{type: "autogenerated", dirName: "."}], +}; + +export default sidebars; diff --git a/about/community/code-of-conduct.mdx b/about/community/code-of-conduct.mdx new file mode 100644 index 00000000..9f1c327f --- /dev/null +++ b/about/community/code-of-conduct.mdx @@ -0,0 +1,117 @@ +--- +sidebar_position: 2 +--- + +# Code of Conduct + +All contributions — whether they're pull requests, issue submissions, comment on issues, +or any other interactions with our community or projects — must follow our Code of Conduct. + +We will approach all Code of Conduct violations with the gravity that the specific +situation demands. We are serious about our responsibility to provide a friendly and +inclusive space for our users, and we'll deal with all violations appropriately. + +--- + +## Introduction + +Our Code of Conduct outlines the types of behaviour we expect from members of our community and forms the basis for our +other policies. +It is important everyone using our services reads over this document and understands the thought process behind it. + +While this document does go into detail on the types of behaviour we encourage (and that we find unacceptable), +it is impossible to list every possible situation in a single document. +For that reason, there will be situations involving unacceptable behaviour that we don't explicitly mention in our +policies. +We'll still action these problematic forms of behaviour, and we'll update our documentation as needed when these +situations arise. + +In general, treat others how you wish to be treated — if you wouldn't appreciate being treated in a particular way, +don't treat others that way yourself! + +When we update this document, we'll announce it on Discord. +If you have any concerns, questions, or suggestions about our policies, feel free to contact a staff member directly, +create a post in `#kordex-meta` on the Discord server. + +## Code of Conduct + +### Our Pledge + +We as members, contributors, and staff members pledge to make participation in our community a harassment-free +experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, +gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, +race, religion, or sexual identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community, +and we're committed to promoting these concepts. + +### Our Standards + +We expect the members of our community to participate in a positive, authentic, and active way. +Consideration, respect, and empathy are key when it comes to building and supporting a healthy community, and we +request and expect that our members exercise this through their actions. + +Please remember that online communities bring together people from many cultures and backgrounds. +It is important to understand that sometimes the combination of cultural differences and online interaction can lead +to misunderstandings — that is why having empathy is crucial. + +Examples of behaviour that contributes to a positive environment for our community include: + +* Demonstrating empathy, patience, and kindness toward other people. +* Being respectful of differing opinions, viewpoints, and experiences. +* Giving and gracefully accepting constructive feedback. +* Accepting responsibility and apologising to those affected by our mistakes, and learning from the experience. +* Focusing on what is best not just for us as individuals, but for the overall community. + +Examples of unacceptable behavior include: + +* Using sexualised language or imagery, and sexual attention or advances of any kind. +* Trolling, insulting, or derogatory comments, and personal or political attacks. +* Threats of violence, or violent acts towards others. +* Public or private harassment. +* Intentionally dead-naming or misgendering others. +* Publishing others' private information, such as a physical or email address, without their explicit permission ("doxing"). +* Advocating for, or encouraging, any unacceptable forms of behaviour. +* Other conduct, where one could reasonably consider it inappropriate in a professional setting. + +### Enforcement Responsibilities + +Staff members are responsible for clarifying and enforcing our standards of acceptable behaviour and will take +appropriate and fair corrective action in response to any behaviour that they deem inappropriate, threatening, +offensive, or harmful. + +If a participant engages in behaviour violating this Code of Conduct, the staff members of this community may take any +action they deem appropriate. +Actions may range from a simple warning to the most drastic: expulsion from the community, exclusion from any +interaction, and loss of all rights in the community. + +### Scope + +This Code of Conduct applies within all community spaces (both online and offline), and applies when an individual is +officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, posting via an official social media +account, or acting as an appointed representative at an online or offline event. + +This Code of Conduct applies to all projects, repositories, and community spaces that Kord Extensions manages, +regardless of whether we explicitly state this code applies. +We may also enforce the Code of Conduct based on the behaviour exhibited by any individual outside our community +spaces, to protect the members of our community. + +### Enforcement + +Anyone may report instances of abusive, harassing, or otherwise unacceptable behaviour to the Kord Extensions staff +team, directly on Discord. +If your report concerns a specific staff member or group of staff members, please send your report to a staff member +that is not part of the report. + +We will review and promptly and fairly investigate all complaints, and we require all staff members to respect the +privacy and security of anyone submitting a report. + +### Attribution + +This Code of Conduct is a heavily modified version of the +[Contributor Covenant v2.0](https://www.contributor-covenant.org/version/2/0/code_of_conduct.html), +with inspiration taken from the +[JetBrains Open Source and Community Code of Conduct](https://confluence.jetbrains.com/display/ALL/JetBrains+Open+Source+and+Community+Code+of+Conduct), +[Microsoft Open Source Code of Conduct](https://microsoft.github.io/codeofconduct/), and +[Open Source Design Code of Conduct](https://opensourcedesign.net/code-of-conduct/). diff --git a/about/community/index.mdx b/about/community/index.mdx new file mode 100644 index 00000000..73b9fb0f --- /dev/null +++ b/about/community/index.mdx @@ -0,0 +1,15 @@ +--- +sidebar_position: 1 +--- + +# Our Community + +To try to cater to our diverse community members, the Kord Extensions community exists in several places. +You can connect with us anywhere you prefer! + +- **Bluesky:** [Our Bluesky profile](https://bsky.app/profile/kordex.dev/post/3lcfj62wlyk2y). +- **Discord:** [Our Discord server](https://discord.gg/nYzQWcjAmK). +- **Codeberg:** Our [Codeberg Organisation](https://codeberg.org/Kord-Extensions), +- **GitHub:** Our [GitHub Organisation](https://github.com/Kord-Extensions). + +We also plan to set up on the ActivityPub-based Fediverse at some point, but we're not ready for that yet. diff --git a/about/community/questions-and-answers.mdx b/about/community/questions-and-answers.mdx new file mode 100644 index 00000000..e3f0446b --- /dev/null +++ b/about/community/questions-and-answers.mdx @@ -0,0 +1,29 @@ +--- +sidebar_position: 4 +--- + +# Questions and Answers + +This section contains some potential questions you may have, and our answers to them. +You can check back occasionally if you have more questions, but remember that we're always open to chat! + +## Who runs this organisation? + +Kord Extensions is primarily the brainchild of [gdude2002 (Gareth Coles)](https://gareth-coles.dev), who is the current +sole owner of the organisation, and he's directly responsible for running and maintaining it. + +[Ayfri](https://ayfri.com/) and [NoComment](https://nocomment1105.github.io) act as Discord moderators, and they +sometimes help out when he's busy. + +## Don't your rules censor some beliefs or opinions? + +Yes. + +Kord Extensions is an explicitly progressive organisation, and our policies reflect that. +We don't believe there's any value in bigotry, and that fact doesn't change when you present your hateful beliefs and +opinions as [sealioning](https://en.wikipedia.org/wiki/Sealioning), "common sense", "reasonable concerns", or something +you were "dared to send". + +If you see this as censorship, then we have no problem with that. +In fact, if you're using that kind of charged language when discussing our moderation policies, you're making our jobs +easier for us — it'll be abundantly clear you don't belong in our community! diff --git a/about/community/rules-and-reporting.mdx b/about/community/rules-and-reporting.mdx new file mode 100644 index 00000000..fa780f8f --- /dev/null +++ b/about/community/rules-and-reporting.mdx @@ -0,0 +1,291 @@ +--- +sidebar_position: 3 +--- + +import Link from "@docusaurus/Link"; + +# Rules and Reporting + +We base our community rules on our +Code of Conduct. +If you have any questions on the mindset behind our rules, that would be a good place to start looking! + +We usually provide an abridged version of these rules directly within our community spaces. +If the shortened version appears to conflict with the rules written below, this document takes priority, and you should +consider it to be the primary source of truth. + +We reserve the right to pre-emptively deny or restrict access to our community spaces to users who violate our rules +elsewhere, even if those violations happen in completely unrelated spaces. +While this may seem drastic, we do this to better protect the minorities in our spaces, who're often the target of +political violence or harassment campaigns. + +## Our Rules + +:::warning + +These rules act as enforcement guidelines, and to help you figure out how we expect you to behave under our +Code of Conduct. +However, to help keep our staff members safe, **the staff team reserves the right to remove anyone from our community +spaces for any reason.** + +The following rules apply to all community interactions in all Kord Extensions spaces, including its social media +profiles and any services it runs. +Not having read the rules is not an excuse for breaking them — by interacting in any of our spaces, you agree to our +rules. + +::: + +### 1. Follow Platform Rules + +As a community, we must follow the rules and terms set out by the platforms we use. +These rules will differ between each platform, and we recommend making yourself familiar with them. + +- **Bluesky:** [Terms of Service](https://bsky.social/about/support/tos) and + [Community Guidelines](https://bsky.social/about/support/community-guidelines) +- **Discord:** [Terms of Service](https://discord.com/terms) and [Community Guidelines](https://discord.com/guidelines) +- **GitHub:** [Terms of Service](https://docs.github.com/en/site-policy/github-terms/github-terms-of-service) and + [Acceptable Use Policies](https://docs.github.com/en/site-policy/acceptable-use-policies/github-acceptable-use-policies) + +### 2. Speak English if Possible + +We understand that not everyone speaks English and that it can be a challenging language to get used to if you don't +speak it natively. +However, English is the common language in our community spaces, which means we can't moderate content written in +other languages. +For that reason, we'd appreciate it if you could try to speak English in our spaces. + +If another community member speaks your native language, and they don't mind translating for you, feel free to accept +their help. +Otherwise, you can use translation tools like [Kagi Translate](https://translate.kagi.com/), +[Google Translate](https://translate.google.com/), or [DeepL](https://www.deepl.com/en/translator) if you need to. + +We don't tolerate users that make fun of others for their level of English, and we'll moderate any harassment or +bullying. + +### 3. Legal and Ethical Projects Only + +All tools and projects shared or worked on in our community spaces must be legal, legitimate, appropriate, and ethical. +For example, don't promote or ask for help with any of the following: + +- Projects or code built around a generative AI tool, such as ChatGPT, DeepSeek, Midjourney, or Synthesia. + - This doesn't include light, ethical generative AI tools, such as the individual line completion feature bundled + with IntelliJ IDEA. +- Projects or code built around NFTs or cryptocurrencies. +- Projects or code designed to manipulate, defraud, misinform, financially disadvantage, spam, or scam others. +- Projects or code designed to violate the terms of service for any platform. + - This doesn't include anything that explicitly falls under progressive activism, but it must be clear and obvious. +- Projects or code that otherwise violate [Rule 1](#1-follow-platform-rules). + +We reserve the right to update this list as trends change. +However, this list simply contains examples, and we reserve the right to moderate and remove anything we feel doesn't +meet our standards. + +### 4. Respect Our Community + +Kord Extensions is a progressive, collaborative organisation, and it is important everyone respects each other's +differences and accounts for others' accessibility needs. +To give specific examples: + +- We absolutely will not tolerate any bigotry — including, but not limited to, racism, antisemitism, body-shaming, +ableism, sexism, anti-feminism, classism, transphobia, pluralphobia +(including discrimination towards "endogenic" systems), and queerphobia. + - We don't believe in reverse-isms, including "reverse racism" and "heterophobia". + "Cis" isn't a slur, and minorities of colour protecting themselves from the actions of the white majority are + not racist. + - Our definition of bigotry includes expressions of support for fascists and fascism, both historical and modern. + This is true regardless of the context — and regardless of whether you pose it as "differing views" or + "just an opinion". + - Yes, 2025's self-crowned King of America, his lackeys, and the President of Twitter count. +- Keep all discussions and content "safe for work" — sexual content and behaviour, gore, violence and abuse content + have no place in our community spaces, except: + - Discussions about newsworthy events, at our discretion. + - Educational content and discussions, at our discretion. + - Projects that support sex workers and their content, when posted in relevant showcase channels and spaces, and + when correctly tagged. +- There's no room in our community spaces for blackmail, piracy, or any other manipulative or illegal behaviour. +- Don't post images, emotes, videos, or other media with fast-moving animations or flashing colours, or anything else + likely to be a problem for photosensitive users. +- Respect any accessibility tools we provide, and the community members who use them. For example: + - **Discord:** [PluralKit](https://pluralkit.me), meant for [plural systems](https://moderation.wiki/a/plurality), or as + a mental health tool. +- Don't use any of our community's content to train AI models. + - The only exception we make to this is for + [our Answer Overflow page](https://www.answeroverflow.com/c/1121419906995458098), which plans to eventually use AI + to make its search tools more useful, but + [you can opt out of it](https://docs.answeroverflow.com/user-settings/ignore-account-in-server). +- Remember that all staff members are imperfect entities — and understand that, while we can and do make mistakes, we're + all trying to do what we feel is best for everyone. + +You should always report any violations of this rule to our staff team. +We appreciate your reports, and we encourage our community remembers to talk with us if they have any concerns. + +For more information on how to make a report and how we handle your reports, see +[the reporting section below](#reporting-violations). + +### 5. Respect Our Spaces + +If we want to have productive and respectful discussions, then everyone must respect our community spaces. +To give more specific examples: + +- Everyone needs to pull together to help keep our community spaces healthy, regardless of whether they're on the staff + team. + While this largely means following our rules and interacting authentically, it also means that we expect our community + members to [report any rule violations they run into](#reporting-violations), rather than joining in or ignoring + issues. +- Do not spam our community spaces. This includes: + - Unsolicited advertising, including promoting your projects outside channels or spaces designed for that. + - Posting or dumping memes into off-topic channels or spaces without any intent or relevance to discussion. + - Pinging or mentioning community members as a joke or to harass them. + - Taking over channels or spaces with personal grievances, or other issues best dealt with privately. +- Read through any relevant posting guidelines, including channel topics and category descriptions, and adhere to them. + While discussions sometimes tend to drift off-topic, we appreciate it when users do their best to keep things relevant + to the channel or space they're using. +- Do not attack, exploit, or otherwise technically hinder our spaces or work. + If you find a security or moderation exploit, or a loophole in our rules, report them instead of using them. + This also includes [rules-lawyering](https://en.wikipedia.org/wiki/Rules_lawyer), + [gaming the system](https://en.wikipedia.org/wiki/Letter_and_spirit_of_the_law#Gaming_the_system), and + [malicious compliance](https://en.wikipedia.org/wiki/Malicious_compliance). + + If you feel inclined towards these tactics, please [give us feedback](#giving-feedback) and let us know where we went + wrong instead. + +### 6. Respect Our Projects + +Kord Extensions is, at its core, a project-oriented organisation. +Development work is already challenging, and you should avoid making that work harder. +To give some examples: + +- Don't violate our copyright or software licences. + You can read more about how we approach licencing in the licencing section. +- Disclose any security problems responsibly. + You can do this by privately reporting them to our staff members or via the "Security" tab on our GitHub repositories. +- Don't maliciously take advantage of our work. + For example, this can include creating a copycat project just because you don't like our licences, or releasing a + successful commercial product that primarily relies on our work without attributing us or contributing back. +- Don't use our work to train AI models. + This includes any code, images, videos, audio, or anything else we might publish. + + Due to the nature of GitHub Copilot, we may someday decide to move to a different platform. + This isn't reasonable for us right now, but it is in the back of our minds. + +We reserve the right to act on any violations of our rights, or our contributors' rights. +However, we'll always try to get in contact and resolve problems peacefully before resorting to take-downs or otherwise +pursuing enforcement. + +## Reporting Violations + +As mentioned above, we expect our community members to contribute to the overall health of our community spaces, in part +by reporting any rule violations they witness. +Our staff team can't be everywhere all the time — we have our own lives and jobs to deal with, and sometimes we won't +notice a violation happening. + +When you notice an issue, please keep this section in mind and report violations appropriately. + +### Submitting a Report + +:::info[Not everything is an emergency] + +Please note that not all rule violations need immediate attention. + +While we always appreciate your reports, sometimes our staff team is busy. +Please exercise restraint for small violations when you know someone has already submitted a report, even if we don't +immediately address the problem. + +However, if you're not sure, please do feel free to submit your report regardless. + +::: + +It is important to put in the effort to write a proper and useful report: + +- Provide as much relevant information as possible, including reference links and screenshots where necessary. +- If you have any commentary or suggestions, place them at the end of your report. + +Reports can be submitted using one of the following approaches: + +- **Discord:** Send a private message to any of the admins or moderators, or ping `@Moderators` in a Discord channel, as + you feel is appropriate. +- **GitHub:** Email your report to `moderation@kordex.dev`. + Only gdude2002 (Gareth Coles) monitors this inbox right now, but this may change as moderation needs evolve. + +If the incident you're reporting breaks the platform's rules, then you should also report it directly to the platform. + +- **Discord:** Right-click / tap-and-hold the relevant message, select the "Report" option, and follow the instructions. +- **GitHub:** Expand the menu displayed on the comment or post, select "Report Content", and follow the instructions. + +#### Reporting Staff Members + +If you need to send in a report about our staff members, please send it to the highest-ranking staff member possible, +aside from the staff members in question. + +For example, if your report is about Moderators A and B, send your report to Admin C. + +If you have feedback or a complaint about our policies or staffing methodology, it may make more sense to provide +direct feedback [as detailed in the feedback section below](#giving-feedback). + +### How We Handle Reports + +When a staff member receives a report, the first step is to read the report and begin a small investigation. + +In simpler situations, we'll act immediately without much discussion — otherwise, we'll share more complex reports +(or those requiring larger investigations) with the rest of the moderation team and chat about its merits. + +We may need to contact you for more details or to have a discussion with you, so please ensure we can contact you after +you send in your report. + +#### Data Management + +We're committed to keeping all reports private, and we're especially wary of sharing details of any reports with +anyone outside our staff team. + +However, we believe it is important for communities to work together, and we reserve the right to share some details of +our moderation actions (and the reports leading up to them) with other moderation teams and community moderation +organisations. +When we do this, we don't share information on who submitted the report, and we try to only share details that are +specifically relevant to the moderation case. + +### Potential Moderation Actions + +We reserve the right to use the full suite of moderation tools available to us when dealing with violations. +This includes but is not limited to: + +- Giving community members public or private warnings. +- Limiting how often a community member may interact with our spaces. +- Limiting the parts of our community that are available to specific community members. +- Removing community members from our spaces. +- Removing community members' ability to interact without removing them from our spaces, often known as "muting". +- Reporting community members' activity to platforms relevant to the violation. +- Warning other communities we work with about severe violations. + +Our team reserves the right to take whichever action seems most appropriate, and we may decide to act across all our +community spaces at once if needed. + +## Appealing a Moderation Action + +:::danger + +If you use our email or any other contact method to harass, bribe, threaten, or manipulate us, we'll +automatically deny your appeal, remove you from all community spaces, and block you from any further communication. + +::: + +If we decide to ban you from our community spaces or otherwise limit your access, and you feel we took that action in +error, or you'd like to make a case for undoing that action, then please email `moderation@kordex.dev`. + +Please note that sending an appeal doesn't guarantee that we'll undo the action in question. +If we decide to deny your appeal, then we'll try to provide as much detail as possible — but in some situations, we may +not be able to. + +Donating or otherwise contributing to our projects will not make your appeal more likely to be successful. +Instead, please be civil and patient when writing your appeal and be as clear as possible when explaining why we +should undo our moderation action. + +## Giving Feedback + +If you have any feedback for our staff team, whether it concerns moderation, community management, a staff application, +or any other subject, please do let us know via one of the following channels: + +- Any of [the reporting channels detailed above](#submitting-a-report). +- **Discord:** The `#kordex-meta` forum channel, or the `#kordex-discussion` text channel. + +Please be patient with us while we discuss your feedback with you, especially as many members of our staff team are +neurodivergent. diff --git a/about/contributing/code/gradle-plugins.mdx b/about/contributing/code/gradle-plugins.mdx new file mode 100644 index 00000000..0628c1f7 --- /dev/null +++ b/about/contributing/code/gradle-plugins.mdx @@ -0,0 +1,16 @@ +--- +sidebar_position: 2 +--- + +import Link from "@docusaurus/Link"; + +# Gradle Plugins + +:::note + +We don't have any formalised standards for our Gradle plugins yet, +other than our Kotlin contribution guidelines. + +Watch this space! + +::: diff --git a/about/contributing/code/index.mdx b/about/contributing/code/index.mdx new file mode 100644 index 00000000..fd927284 --- /dev/null +++ b/about/contributing/code/index.mdx @@ -0,0 +1,91 @@ +--- +sidebar_position: 1 +--- + +import Link from "@docusaurus/Link"; + +# Code + +If you're a software developer, and you'd like to contribute to one of our projects, you're in the right place! + +All our libraries and tools are open-source, and we're happy to accept contributions. +However, we have some standards you'll need to meet before we can accept your work. + +## Getting Started + +Before contributing to our projects, please make sure you've already done the following: + +- [ ] Look for relevant GitHub issues and check our community spaces to make sure + nobody's already working on your idea. + - If someone is already working on your idea, **do not create a separate contribution.** + Instead, if possible, work together to create a better, combined contribution. + - If you see a claimed issue that nobody has worked on for a long time, let us know and we'll assign it to you. +- [ ] If nobody's working on it, present your idea to the project maintainers by opening a GitHub issue or using + our community spaces to float it. +- [ ] Once discussions have concluded, and we've approved your idea, fork the relevant GitHub repository. +- [ ] Immediately create a new branch for your changes and begin working on your idea. +- [ ] As soon as you push your first commit, open your pull request. + Remember to tag it as a work-in-progress! +- [ ] Once you're happy your changes are ready, remove the work-in-progress tag from your pull request and leave a + comment to let us know that you're ready for reviews. +- [ ] When we review your changes, address them. + Try not to let your pull request go stale by failing to address our reviews. + +Once we're happy with your changes, we'll merge them into our repository. +Thanks for your help! + +## General Guidelines + +When you're working on our projects, you should keep the following points in mind: + +- **Follow our Policies:** Adhere to our Code of Conduct and + Community Rules when creating a contribution. + This applies both to your conduct and your code! + +- **Don't fight the framework:** If a project is using a specific framework or + toolset, then we expect you to stick with it. + If you feel like there's a better option, + *please open an issue instead of trying to contribute the necessary changes.* + + We've picked the tools we're using for a reason, and the project is probably too far ahead + to make a rewrite in another framework reasonable. + For that reason, it is important to open an issue — otherwise, + you may be working on code that ultimately never gets used. + +- **Adhere to the prevailing style:** To keep things readable and maintainable, we enforce a + set of style guidelines using a linter (for Kotlin, this is a combination of + Detekt and KtLint). + + If your code doesn't lint, then it will fail to build, and we won't merge your contribution + until the build passes. + + Additionally, individual projects and project types may have their own prevailing styles. + For more information, please read the specific guidelines below. + + Finally, prioritise readability in your code. + Don't [golf your code](https://en.wikipedia.org/wiki/Code_golf), + and try to add whitespace where it makes things more readable. + +- **Avoid feature creep:** When authoring a pull request, ensure that it addresses a + well-defined issue. + Do not submit solutions for more than one problem in a single PR, unless they're all related. + Instead, create multiple branches and submit a separate PR for each one. + + If your pull request is blocking another pull request that you wish to work on, we + recommend you hold back and wait for us to review and merge the current PR. + While it is possible to work on both, it can be a heavier workload — especially if the + blocking PR requires a lot of changes. + +- **Avoid trivial contributions:** We will be unlikely to accept PRs that address tiny + issues, such as a single-line comment or documentation edit, spelling corrections, + and so on. + Instead, please open an issue or contact a staff member directly. + + We will accept PRs that address many trivial changes, however. + +## Specific Guidelines + +Please read the following pages for specific information on our expectations for each type of project. + +- Gradle plugins +- Kotlin projects diff --git a/about/contributing/code/kotlin.mdx b/about/contributing/code/kotlin.mdx new file mode 100644 index 00000000..f542622e --- /dev/null +++ b/about/contributing/code/kotlin.mdx @@ -0,0 +1,346 @@ +--- +sidebar_position: 3 +--- + +import Link from "@docusaurus/Link"; + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Kotlin + +This document provides in-depth information about what we expect from you when working on projects written in Kotlin, +or Kotlin code in mixed-language projects. + +We've written this document because we've seen a lot of confusion from contributors when we review their projects. +If you don't understand something in this document (or you think we missed something), +please let us know! + +## Indentation + +All Kord Extensions projects use **single tabs** for indentation, except in some situations: + +- Markdown (`.md`, `.mdx`) files use **two spaces** for list item and wrapping indentation. + We feel this makes these files more readable, as wrapped list items line up. +- YAML (`.yml`) files use **two spaces** for indentation, as YAML doesn't support tab-based indents, + and these files tend to be deeply nested. + +The main reason we decided to primarily use tabs is for better code accessibility. +Specifically, all good editors allow you to configure how they look, and they tend to be better for visually impaired +or blind folks, who may need to use large fonts or braille screen readers. + +## Blank Lines + +While some style conventions try to minimise blank lines in code files (such as +[the one used by `kotlinx.coroutines`](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CONTRIBUTING.md)), +we think this makes code difficult to read, and thus is bad practice. + +Instead, you should separate your code into logically defined blocks, split with singular blank lines. +We've provided some examples below, but please note that we use a somewhat vibes-based approach for this, and we'll +need to update this document over time. + +### Data vs Logic + +**Split field definitions and functional expressions where possible and when logic allows.** +For example: + + + + + ```kt + val hello = "hello" + val world = "world" + println("$hello, $world!") + val question = "What's up?" + println(question) + ``` + + + + + + ```kt + val hello = "hello" + val world = "world" + val question = "What's up?" + + println("$hello, $world!") + println(question) + ``` + + + + +### Functions vs Fields + +**Split function calls and field access where possible and when logic allows.** +For example: + + + + + ```kt + val x: SomeObj + x.doThing() + x.variable = 42 + val something: x.getThing() + x.doOtherThing(something) + x.takeMap( + mapOf("a" to "b"), + "c" + ) + ``` + + + + + + ```kt + val x: SomeObj + val something: x.getThing() + + x.doThing() + x.doOtherThing(something) + + x.variable = 42 + + x.takeMap( + mapOf("a" to "b"), + + "c" + ) + ``` + + + + +### Lines vs Blocks + +**Split single-line expressions and blocks, and split separate blocks.** +For example: + + + + + ```kt + val x: Something + x.doSomething { + // ... + } + x.doThing() + x.doSomethingElse{ + // ... + } + ``` + + + + + + ```kt + val x: Something + + x.doSomething { + // ... + } + + x.doThing() + + x.doSomethingElse{ + // ... + } + ``` + + + + +### Long Wrapped Statements + +**When dealing with statements wrapped within symbol characters (such as parentheses), don't let them get too long.** +Additionally, add commas to the end of lists, and follow the other rules. +For example: + + + + + ```kt + private fun addGeneratedFiles(target: Project, extension: KordExExtension, kordVersion: Version?, kordExVersion: Version) { + // ... + + doSomething(1, 2, "a", "b", listOf(null), true, null, false) + } + ``` + + + + + + ```kt + private fun addGeneratedFiles( + target: Project, + extension: KordExExtension, + kordVersion: Version?, + kordExVersion: Version, + ) { + // ... + + doSomething( + 1, 2, + "a", "b", + + listOf(null), + + true, false, + ) + } + ``` + + + + +### Grouped Calls + +**When using the same function/property multiple times, split them from other function/property uses.** +For example: + + + + + ```kt + val properties = Properties() + properties.setProperty("settings.dataCollection", extension.dataCollection.readable) + properties.setProperty("modules", extension.modules.joinToString()) + properties.setProperty("versions.kordEx", kordExVersion.version) + properties.setProperty("versions.kord", kordVersion?.version) + properties.store(outputFile.get().asFile.writer(), null) + ``` + + + + + + ```kt + val properties = Properties() + + properties.setProperty("settings.dataCollection", extension.dataCollection.readable) + properties.setProperty("modules", extension.modules.joinToString()) + properties.setProperty("versions.kordEx", kordExVersion.version) + properties.setProperty("versions.kord", kordVersion?.version) + + properties.store(outputFile.get().asFile.writer(), null) + ``` + + + + +### Chained Access and Complex Parameters + +**When chaining multiple function/field uses in one statement, split them onto separate lines.** +Also, if you're chaining a lot of functions in the same statement, split them into groupings as explained earlier. +This also applies when you're passing complex arguments to functions. + +For example: + + + + + ```kt + val sourceSet = target.extensions.getByType(SourceSetContainer::class.java).first { it.name == "main" } + sourceSet.output.dir(mapOf("builtBy" to task), outputDir) + ``` + + + + + + ```kt + val sourceSet = target + .extensions + .getByType(SourceSetContainer::class.java) + .first { it.name == "main" } + + sourceSet.output.dir( + mapOf("builtBy" to task), + + outputDir + ) + ``` + + + + +## Complete Example + + + + + ```kt + private fun addGeneratedFiles(target: Project, extension: KordExExtension, kordVersion: Version?, kordExVersion: Version) { + val outputDir = target.layout.buildDirectory.dir("generated") + val outputFile = target.layout.buildDirectory.file("generated/kordex.properties") + val task = target.tasks.create("generateMetadata") { + group = "generation" + description = "Generate KordEx metadata." + outputs.file(outputFile) + doLast { + val properties = Properties() + properties.setProperty("settings.dataCollection", extension.dataCollection.readable) + properties.setProperty("modules", extension.modules.joinToString()) + properties.setProperty("versions.kordEx", kordExVersion.version) + properties.setProperty("versions.kord", kordVersion?.version) + properties.store(outputFile.get().asFile.writer(), null) + } + } + + val sourceSet = target.extensions.getByType(SourceSetContainer::class.java).first { it.name == "main" } + sourceSet.output.dir(mapOf("builtBy" to task), outputDir) + } + ``` + + + + + + ```kt + private fun addGeneratedFiles( + target: Project, + extension: KordExExtension, + kordVersion: Version?, + kordExVersion: Version + ) { + val outputDir = target.layout.buildDirectory.dir("generated") + val outputFile = target.layout.buildDirectory.file("generated/kordex.properties") + + val task = target.tasks.create("generateMetadata") { + group = "generation" + description = "Generate KordEx metadata." + + outputs.file(outputFile) + + doLast { + val properties = Properties() + + properties.setProperty("settings.dataCollection", extension.dataCollection.readable) + properties.setProperty("modules", extension.modules.joinToString()) + properties.setProperty("versions.kordEx", kordExVersion.version) + properties.setProperty("versions.kord", kordVersion?.version) + + properties.store(outputFile.get().asFile.writer(), null) + } + } + + val sourceSet = target + .extensions + .getByType(SourceSetContainer::class.java) + .first { it.name == "main" } + + sourceSet.output.dir( + mapOf("builtBy" to task), + + outputDir + ) + } + ``` + + + diff --git a/about/contributing/index.mdx b/about/contributing/index.mdx new file mode 100644 index 00000000..d8af458d --- /dev/null +++ b/about/contributing/index.mdx @@ -0,0 +1,20 @@ +--- +sidebar_position: 1 +--- + +import Link from "@docusaurus/Link"; + +# Contributing + +If you'd like to contribute to Kord Extensions or any of its projects, +we have plenty of ways to get involved or support us: + +- [Donate to support us.](https://donate.stripe.com/3csg29aPp95Rgxi7ss?client_reference_id=docs) +- Contribute code to our software projects. +- Contribute translations. +- Contribute writing work. +- Enable data collection in our Discord bot framework. +- Send us feedback. + +Regardless, we appreciate it when folks use and enjoy our work, and we don't expect anything in return. +If you've used any of our tools and libraries in your projects, please let us know! diff --git a/about/contributing/translations.mdx b/about/contributing/translations.mdx new file mode 100644 index 00000000..f61b7ac9 --- /dev/null +++ b/about/contributing/translations.mdx @@ -0,0 +1,49 @@ +--- +sidebar_position: 2 +--- + +import Link from "@docusaurus/Link"; + +# Translations + +:::tip We're looking for help! + +Kord Extensions is looking for translators and reviewers. + +We're happy to support [any language with an ISO 639-3 code](https://iso639-3.sil.org/code_tables/639/data/all), +including constructed languages like Toki Pona, so please don't be shy! + +- If you'd like to contribute to our translations, please continue with this document. +- If you'd like to help us review new and updated translations, +please get in touch. + +::: + +You can find all Kord Extensions project translations files +[in the relevant GitHub repository](https://github.com/Kord-Extensions/translations). +However, we don't accept pull requests to this project. + +Instead, please contribute translations via [our Weblate project](https://hosted.weblate.org/engage/kord-extensions/), +which provides extra validation and will credit your contribution if you log in using your GitHub account. + +## Translating the Docs + +:::note + +It is technically possible to translate our documentation site, and we plan on setting that up when we can. + +We'll figure this out and write some documentation when the English version is ready. + +::: + +## Notes + +Please take note of the following points when writing translations: + +- **For the Discord bot framework:** + - Some translations refer to things that have names in the Discord client. + If Discord supports your target language, you should try to use the same translation the client does. +- While many translations contain placeholders, it is more important your translation meets the original meaning of the + English version, rather than closely matching the structure and wording. +- If our translations are subpar, or we forgot to localise something, + please let us know. diff --git a/about/contributing/writing.mdx b/about/contributing/writing.mdx new file mode 100644 index 00000000..4c5ff2ef --- /dev/null +++ b/about/contributing/writing.mdx @@ -0,0 +1,14 @@ +--- +sidebar_position: 3 +--- + +# Writing + +:::note + +We don't have any writing projects yet, aside from this documentation. +We'll update this page when that changes, or after we release this version of the documentation. + +Watch this space! + +::: diff --git a/about/index.mdx b/about/index.mdx new file mode 100644 index 00000000..a09f99ec --- /dev/null +++ b/about/index.mdx @@ -0,0 +1,21 @@ +--- +sidebar_position: 0 +--- + +# About the Project + +Kord Extensions (henceforth KordEx) is, at its core, a set of open-source projects that exist to solve problems in +ethical ways. +While our first project was a Discord bot framework, KordEx has expanded to encompass a much larger set of projects. + +As a community-driven organisation, Kord Extensions strives to meet the needs of its users, where possible and +reasonable. +Many of our projects are development tools and libraries, and we design them with our users in mind, rather than +narrowly focusing on a specific use-case. + +Kord Extensions is also a progressive organisation. +We stand with people of colour, queer folks, the neurodiverse and disability communities, sex workers, feminists, +and other progressive and minority groups in their struggles. +We are open about our politics, and we refuse to compromise on them. + +If your views don't align with the above values, this is not the organisation for you. diff --git a/about/licencing/cc-zero.mdx b/about/licencing/cc-zero.mdx new file mode 100644 index 00000000..14c836a5 --- /dev/null +++ b/about/licencing/cc-zero.mdx @@ -0,0 +1,58 @@ +--- +sidebar_position: 2 +--- + +import Link from "@docusaurus/Link"; + +# Creative Commons Zero Licence + +We use the Creative Commons Zero "Licence" (CC0) for our documentation site (and its contents). + +## About the Licence + +[According to Creative Commons](https://creativecommons.org/public-domain/cc0/), CC0 is a public-domain dedication +that acts as a best-effort attempt to opt out of copyright and database protection and the exclusive rights +automatically granted to creators. + +The problem with publishing any work as "truly" public domain is that many countries have restrictions on what rights +you can waive and how you should declare a work as public domain, if that's possible at all. +The CC0 provides the best (and most complete) alternative, allowing anyone to relinquish as many rights as legally +possible, while skipping any complex registration processes. + +## Our Mindset + +:::tip + +This section encompasses our own opinions. +We're sure some people will disagree with us, but we're firm in our beliefs — +so find something else to spend your energy on other rather than trying to change our minds! + +::: + +We only use the CC0 for projects where another licence could cause serious issues for our users. + +We use the CC0 with the documentation site because it contains many code examples, snippets of useful information, and +quotable passages. +We didn't want another licence to potentially interfere with our users' projects in situations where they need to refer +to our documentation, and we wanted to enable them to copy any relevant code snippets into their projects as needed. + +## Our Interpretation + +This licence is uncomplicated — you can do whatever you like with our documentation, without restriction. + +However, please note that the CC0 doesn't surrender any trademark rights — you can't take our documentation and then +claim to be us. +We also don't take kindly to scammers attempting to sell copies of our documentation — +most jurisdictions would make that illegal. + +## Questions and Concerns + +If you have any questions regarding Kord Extensions and its licencing, please feel free to contact us +via any of our community spaces. + +If you have any questions about the CC0's wording or how to use it, Creative Commons provides +[an FAQ](https://wiki.creativecommons.org/wiki/CC0_FAQ), a +[list of considerations](https://wiki.creativecommons.org/wiki/Considerations_for_licensors_and_licensees#Considerations_for_licensees), +and [a summary](https://creativecommons.org/publicdomain/zero/1.0/). + +If those links don't help, [you can also contact them directly](https://creativecommons.org/mission/contact/). diff --git a/about/licencing/eupl.mdx b/about/licencing/eupl.mdx new file mode 100644 index 00000000..444f7195 --- /dev/null +++ b/about/licencing/eupl.mdx @@ -0,0 +1,276 @@ +--- +sidebar_position: 3 +--- + +import Link from "@docusaurus/Link"; + +# European Public Licence + +As of version 2.x, we use the European Public Licence (The EUPL) for our primary project, +our Discord bot framework. + +Previous versions were licenced under +the Mozilla Public Licence. +You can find the source code for the last version of that project we licenced under the MPL +[in the v1 branch on GitHub](https://github.com/Kord-Extensions/kord-extensions/tree/v1). + +We understand that software licencing can be confusing at the best of times. +This is especially true when dealing with an uncommon or poorly-documented software licence — such as the EUPL. + +:::danger + +While we've tried to provide as much information as we can under our understanding of the EUPL, +**we aren't lawyers and this document doesn't constitute legal advice.** + +[The EUPL text](https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12) always takes priority over anyone's +opinions, but we hope to clarify a few things by explaining how we interpret it. + +::: + +## About the Licence + +[According to the European Commission](https://joinup.ec.europa.eu/collection/eupl/introduction-eupl-licence), +the EUPL is a copyleft licence, +designed to be compatible with other copyleft licences when combined downstream into a larger work — +and to be interoperable, according to European copyright law. +It includes multiple translated versions (which hold equal legal value), +covers distribution through a network (such as Discord), and includes a Developer Certificate of Origin. + +What this ultimately means is that the EUPL functions like a more compatible and interoperable AGPL, +including a network use clause, but also a linking exception. +Instead of containing its own definition for what a derivative work is, +it defers to European copyright law — specifically, +[Directive 91/250/EEC](https://eur-lex.europa.eu/legal-content/EN/ALL/?uri=celex:31991L0250), +re-codified [2009/24/EC](https://eur-lex.europa.eu/legal-content/EN/ALL/?uri=CELEX:32009L0024). + +It additionally defers to the copyright law of the EU member state where the project owner resides, +or the one defined within the files' licence headers, if provided. +In our case, this means that the applicable law applies to the Republic of Ireland, +specifically within Dublin's jurisdiction for legal challenges. + +## Our Mindset + +:::tip + +This section encompasses our own opinions. +We're sure some people will disagree with us, but we're firm in our beliefs — +so find something else to spend your energy on other rather than trying to change our minds! + +::: + +It is no secret that politics in open source software has deeply altered today's technological landscape. +While we feel that many of these changes have been positive, some of them have definitely been negative. + +The GNU Project has, for a long time, steered the discussion on what it means to write open-source software and what +it means for software to be "free" — trying to move the needle in whatever direction Richard Stallman +desires. +Given that [free software is inherently political](https://steemit.com/programming/@crell/free-software-is-political), +and the self-crowned leader of that political movement +[can't meet even basic moral standards](https://stallman-report.org/), we wonder whether we could do better. + +Additionally, the modern open-source movement has largely diverged from the original ideals of the so-called free +software movement. +This means we must deal with things as they are, not how Stallman claims they are or should be. + +Unfortunately, we're not in a great place right now. +The rise of generative AI (AKA theft-as-a-service) has laid bare companies' intentions to strip-mine our community and +industry until there's nothing left of it. +The [open-source sustainability crisis](https://openpath.quest/2024/the-open-source-sustainability-crisis/) shows +that large companies take advantage of open-source projects and provide nothing in return — +no code contributions, no donations, no marketing, nothing. + +The open-source movement (maybe intentionally, we think) enables this, along with many of the licences that have the +privilege of the "open-source licence" label. + +**We are no longer willing to be a source of labour for the corporate world to use without giving anything back.** + +The normal response to these concerns is to simply tell us to use the GPL or AGPL. +However, these are "viral" licences, requiring any public downstream dependent project to use the same licence, or a +similar one. + +To maintain downstream developer freedom, we decided against picking any viral licences. +The only remaining GNU licence was the LGPL, which we felt didn't provide adequate protection for our projects. + +Ultimately, the only suitable licence we could find was the EUPL. +It guarantees that any downstream project or product must provide an attribution notice at minimum, assuming it is +available to the public in some way. +If anyone modifies one of our EUPL-licenced projects, and they make that modification available to the public in some +way, the EUPL guarantees that they must make those modifications publicly accessible as well. + +We hope that this will help to direct a handful of new users to our projects and allow us to incorporate any changes +that would be helpful to our users, but the developer didn't contribute back. + +## Our Interpretation + +While our interpretation **cannot be treated as legal advice**, +we feel it may be helpful to explain what we expect from our users. + +- The EUPL is a copyleft licence. + This means you can freely copy and distribute Kord Extensions, as long as you follow the conditions in the licence + text. + +- European law allows for the reproduction of "interfaces" without the permission of the rights-holders. + This means that the copying of names from an API (such as what happens with linking) + doesn't require that your project be licenced under the EUPL, or indeed any specific licence. + + This was particularly important to us, + as we didn't want to force any specific licence onto bots, + modules, plugins, or other related projects using Kord Extensions, + and we wanted to allow for use by proprietary and commercial projects. + +- The EUPL includes a "network use" clause — it treats distribution and communication as equal, + and communications of functionality over a network (such as Discord) + require you to follow all the distribution requirements. + + Due to the linking exception described above, this doesn't apply to your bots' code, + but it does apply to Kord Extensions itself, as explained below. + +- The EUPL states that all distributions or communications of a work must include all copyright, + patent or trademark notices that refer to the licence and to the disclaimer of warranties. + In our case, we simply ask for a mention of Kord Extensions, and a link back to our site, + which the bundled `/about` command provides by default. + + The GitHub Discussion used to collect contributors' permission to re-licence under the EUPL also asked for permission + to provide alternative licences pending an application process. + We haven't created a process for this yet (as nobody has asked for an alternative licence), + but the option remains open if this arrangement doesn't work for your project. + +### Combining + +When creating a combined work (such as a fat JAR containing your bot along with Kord Extensions) for public +distribution or use, the EUPL version 1.2 defines several compatible downstream licences you may use. + +:::danger + +It is worth pointing out that **this doesn't mean that you can re-licence Kord Extensions.** + +Instead, this means that you can provide a larger combined work including Kord Extensions under one of the +compatible licences, as long as you follow the terms set out by the licence applied to Kord Extensions. + +**Failure to follow the Kord Extensions licence means you lose the right to distribute it!** + +::: + +- GNU licences: + - AGPL version 3 + - GPL version 2 or 3 + - LGPL version 2.1 or 3.0 + +- Other Software licences: + - Cea Cnrs Inria Logiciel Libre licence (CeCILL) version 2.0 or 2.1 + - Eclipse Public licence (EPL) version 1.0 + - European Union Public Licence (EUPL) version 1.1 or 1.2 + - Mozilla Public Licence (MPL) version 2 + - Open Software licence (OSL) version 2.1 or 3.0 + - Québec Free and Open-Source Licence (LiLiQ-R or LiLiQ-R+) + +## Your Work + +While the EUPL applies specifically to Kord Extensions above, +we felt it was worth explaining the specifics of what it means for your projects. + +### Linking + +While no European court has tested the legal definition for linking, +we understand it to mean the copying of names and API definitions from another project yours is compiled against, +without actually distributing the project you're linking to. + +Under European law, this doesn't require rights-holder permission, +and it doesn't impact how you must distribute or licence your project. + +Ultimately, this means that you're welcome to keep your bots and plugins closed-source or open-source them under any +licence you wish, **as long as they don't include a distribution of Kord Extensions.** + +### Modifying + +If you modify Kord Extensions and use it in a bot or other distribution or communication, +**you must provide access to the source code of your modified version and state what changes you've made to it. +Additionally, you are not allowed to remove the references to Kord Extensions** in the `/about` command +(including removing the command or extension), +unless you reproduce those references in another public command with equal visibility. + +The simplest way to do provide your source code and list of changes is to +[fork the project on GitHub](https://github.com/Kord-Extensions/kord-extensions/fork), and this is what we'd prefer. +However, you may also provide a link and state your changes in your bot's `/about` command. + +We believe that Kord Extensions is at its best when it accounts for as many project types as possible, +and we always prefer contributions that make Kord Extensions more suitable, rather than multiple diverging forks. + +As all developers maintaining Kord Extensions forks must licence them under the EUPL version 1.2, +we may inspect forks and integrate their changes with Kord Extensions or make our own changes inspired by the forks, +without notice. +However, we will do our best to credit you by listing you as an additional author in each relevant commit. + +### Combining + +While we don't see any realistic reason to create a public combined work, +we want to ensure that we also account for that project type. + +We understand a combined work to be a larger work (such as a bot) +that itself includes a distribution of Kord Extensions — regardless of whether that distribution is modified — +in either source code, compiled or binary format. +Examples of combined works include: + +- Distributions such as those created by the Gradle distribution plugin or the Gradle Shadow plugin. +- Containers, including Docker containers, regardless of how a developer may intend to deploy them. +- A distribution of your project's source code that contains the Kord Extensions source code in-tree. + +Where you make a combined work available to the public, +you must distribute it under one of the EUPL's compatible downstream licences. +If you aren't distributing Kord Extensions' source code along with your project's, +then this doesn't mean you must licence your project's code this way, or that your project must be open-source. +However, you must distribute your project in a way that follows the terms of the collective licence you're using. + +:::danger + +It is worth pointing out that **this doesn't mean that you can re-licence Kord Extensions.** + +**Failure to follow the Kord Extensions licence means you lose the right to distribute it!** + +::: + +We recommend against distributing Kord Extensions in source code format, +and you should dynamically link to it whenever possible, ideally using the KordEx Gradle plugin. + +### Running + +Under the terms of the EUPL, a "distribution" or "communication" are treated as equal, +and the same terms apply to both. +To quote the EUPL directly: + +> ‘Distribution’ or ‘Communication’: any act of selling, giving, lending, renting, distributing, communicating, +> transmitting, or otherwise making available, online or offline, copies of the Work or providing access to its +> essential functionalities at the disposal of any other natural or legal person. + +This means that running a Discord bot using Kord Extensions counts as a "distribution" or "communication", +as you are exposing its functionality to users on Discord. +As a result, you will need to follow the terms of the EUPL, +even if your project's source code is not publicly available, +and you're not using a modified version of Kord Extensions. +**This is true even if it is a small bot for a private server,** +as Discord ultimately decides who has access to your bot, rather than you having direct control over it. + +To make this easier, Kord Extensions provides the `/about` command, +which includes a small footer mentioning Kord Extensions, linking to its website, and mentioning its licence. +You should configure this command with your bot's information, otherwise it'll look strange and a little barren. + +:::danger + +To remain compliant with the terms of the EUPL, your bot must have an easily accessible notice including the following: + +- A link to the Kord Extensions website or GitHub repository. +- A note that your bot uses Kord Extensions and that it is licenced under the EUPL version 1.2. + +For this reason, **we strongly recommend that you don't attempt to remove the `/about` command from your bots or any +modified version of Kord Extensions in any way.** + +::: + +## Questions and Concerns + +If you have any questions regarding Kord Extensions and its licencing, please feel free to contact us +via any of our community spaces. + +If you have any questions about the EUPL's wording or how to use it, the European Commission +[provides a contact form you can use +to ask legal questions](https://joinup.ec.europa.eu/collection/eupl/joinup-legal-support-questions-will-be-answered). diff --git a/about/licencing/index.mdx b/about/licencing/index.mdx new file mode 100644 index 00000000..850a1805 --- /dev/null +++ b/about/licencing/index.mdx @@ -0,0 +1,24 @@ +--- +sidebar_position: 1 +--- + +import Link from "@docusaurus/Link"; + +# Licencing Information + +Kord Extensions uses a small, specific set of open/public source licences. +We chose these licences to best match our personal intentions for how you should use each project, weighed against the +potential risks and harms that bad actors could cause to us. + +We've put together a set of pages to better explain how we think about each licence: + +- Creative Commons Zero Licence, a public domain dedication we use for this + documentation. +- European Public Licence, an open-source license that fills the gap + between the LGPL and the AGPL. +- Mozilla Public Licence, a less-restrictive license mainly applied to our + library projects. +- The Unlicense, a public domain dedication meant for software projects. + +If you have any questions regarding Kord Extensions and its licencing, please feel free to contact us +via any of our community spaces. diff --git a/about/licencing/mpl.mdx b/about/licencing/mpl.mdx new file mode 100644 index 00000000..e988a189 --- /dev/null +++ b/about/licencing/mpl.mdx @@ -0,0 +1,83 @@ +--- +sidebar_position: 4 +--- + +import Link from "@docusaurus/Link"; + +# Mozilla Public License + +:::warning + +Several versions of the Mozilla Public License exist. +Our projects only use version 2.0, and this documentation likewise only talks about that version. + +We don't use version 1.1, or any other past (or future) version of the MPL. + +::: + +We use the Mozilla Public License (the MPL) for projects we feel are less important, less likely to be stolen from, +good learning resources, or where a more permissive licence than the EUPL +makes sense. + +This includes our Gradle plugins, and our internationalisation framework. + +## About the Licence + +[According to Mozilla](https://www.mozilla.org/en-US/MPL/), the MPL is a simple copyleft licence, intended to be applied +to individual files rather than sitting at the root of a project. +This makes it easier for users and contributors to modify project code, while still allowing them to combine it with +code under other licences with minimal restrictions. + +It sits in-between the Apache licence and the GNU family of licences, providing a middle ground for projects that wish +to use it. + +## Our Mindset + +:::tip + +This section encompasses our own opinions. +We're sure some people will disagree with us, but we're firm in our beliefs — +so find something else to spend your energy on other rather than trying to change our minds! + +::: + +In many ways, we treat the MPL as a lighter version of the EUPL. +While we feel strongly about the culture of exploitation that is a necessary part of the open source movement, we +recognise that a restrictive licence with strong protections isn't suitable for every project or situation. +We apply the MPL to projects in those situations. + +We feel that it is important to maintain developer freedom in many situations. +Using a stronger licence for a library (or some other project types) would significantly impact developers' +ability to maintain their own projects as they see fit. + +Despite this, we feel that the MPL provides strong enough protections to stop malicious developers from taking +advantage of us in most of the situations we care about. + +## Our Interpretation + +Despite being a relatively permissive copyleft licence, the MPL does provide one important distribution requirement: +all public distributions must include a link to the source code. +If the distribution includes a modified version, the source code for that modified version must also be available and +linked to. + +Notably, the licence considers methods of distribution that add cost and complexity without a specific, +necessary purpose as unreasonable, and these methods of distribution don't meet the licence terms. +For example, you can't meet the licence terms by only distributing source code via a courier or other physical +mechanisms, when you could more easily upload the source code to a website and provide a link. + +Unlike the EUPL, "distribution" only covers situations where you deliver +a copy of the software in question to another person or entity. +It doesn't include a "network use" clause, so making the functionality available over a network doesn't trigger the +distribution requirements. + +Otherwise, this is a fairly standard copyleft licence, +and we recommend [reading the licence text](https://www.mozilla.org/en-US/MPL/2.0/) yourself. + +## Questions and Concerns + +If you have any questions regarding Kord Extensions and its licencing, please feel free to contact us +via any of our community spaces. + +If you have any questions about the MPL's wording or how to use it, Mozilla provides +[an in-depth FAQ](https://www.mozilla.org/en-US/MPL/2.0/FAQ/), as well as a contact email address +[on the overview page](https://www.mozilla.org/en-US/MPL/). diff --git a/about/licencing/unlicense.mdx b/about/licencing/unlicense.mdx new file mode 100644 index 00000000..e979c4ca --- /dev/null +++ b/about/licencing/unlicense.mdx @@ -0,0 +1,53 @@ +--- +sidebar_position: 4 +--- + +import Link from "@docusaurus/Link"; + +# The Unlicense + +We use The Unlicense for our template projects. + +## About the Licence + +[According to the website](https://unlicense.org/), The Unlicense is a public domain dedication meant for software +projects, combined with a no-warranty statement. + +In many ways, this is +the CC0 of the software world, +though it's far from the only option. + +## Our Mindset + +:::tip + +This section encompasses our own opinions. +We're sure some people will disagree with us, but we're firm in our beliefs — +so find something else to spend your energy on other rather than trying to change our minds! + +::: + +Our mindset for this licence is very much like our mindset for the CC0. +We only use The Unlicense for projects where another licence could cause serious issues for our users. + +As the CC0 isn't meant for software projects, we instead apply The Unlicense to our template projects. +We intend for users to use these projects to bootstrap their own work, and we didn't want to force any particular +licence onto them. + +## Our Interpretation + +This licence is uncomplicated — you can do whatever you like with our template projects, without restriction. + +However, please note that The Unlicense doesn't surrender any trademark rights — you can't take our template projects +and then claim to be us. +We also don't take kindly to scammers attempting to sell copies of our template projects — most jurisdictions would +make that illegal. + +## Questions and Concerns + +If you have any questions regarding Kord Extensions and its licencing, please feel free to contact us +via any of our community spaces. + +If you have any questions about The Unlicense itself or how to use it, they provide +[a website](https://unlicense.org/), including some +[additional resources, and social links](https://unlicense.org/#unlicensing-resources). diff --git a/about/licensing/index.mdx b/about/licensing/index.mdx new file mode 100644 index 00000000..e92f07d6 --- /dev/null +++ b/about/licensing/index.mdx @@ -0,0 +1,8 @@ +--- +sidebar_custom_props: + hidden: true +--- + +import {Redirect} from "@docusaurus/router"; + + diff --git a/bots-sidebars.ts b/bots-sidebars.ts new file mode 100644 index 00000000..b4287aef --- /dev/null +++ b/bots-sidebars.ts @@ -0,0 +1,19 @@ +import type {SidebarsConfig} from "@docusaurus/plugin-content-docs"; + +// This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) + +/** + * Creating a sidebar enables you to: + - create an ordered group of docs + - render a sidebar for each doc of that group + - provide next/previous navigation + + The sidebars can be generated from the filesystem, or explicitly defined here. + + Create as many sidebars as you want. + */ +const sidebars: SidebarsConfig = { + docSidebar: [{type: "autogenerated", dirName: "."}], +}; + +export default sidebars; diff --git a/bots/about/changelog.mdx b/bots/about/changelog.mdx new file mode 100644 index 00000000..a56a75f6 --- /dev/null +++ b/bots/about/changelog.mdx @@ -0,0 +1,1032 @@ +--- +sidebar_position: 2 +--- + +import Link from "@docusaurus/Link"; + +# Changelog + +This page contains historical changelogs for the Kord Extensions Discord Bot Framework, taken from GitHub, Discord, and +this documentation site, slightly edited for better grammar and more sensible styling. + +:::note + +Please note that changelogs taken from Discord will have a more conversational tone. + +::: + +## v2.3.1-SNAPSHOT + +_Published to Discord on October 28th, 2024._ + +I've gone ahead and pushed a new KordEx version in line with several dependency updates, including Kord: + +- **Gradle:** 8.8 → 8.10.2 +- **ICU4J:** 75.1 → 76.1 +- **JUnit:** 5.11.2 → 5.11.3 +- **Kord:** 0.15.0-SNAPSHOT → 0.16.0-SNAPSHOT +- **KSP:** 2.0.20-1.0.25 → 2.0.21-1.0.26 +- **Logback:** 1.5.9 → 1.5.12 +- **PF4J:** 3.12.0 → 3.12.1 +- **Sentry:** 7.15.0 → 7.16.0 + +As always, if you directly depend on anything KordEx needs, remember to update your dependencies! + +## v2.3.0-SNAPSHOT + +_Published to Discord on October 20th, 2024._ + +It's here, y'all! This version brings a huge number of internal and external changes. + +I apologise in advance for the upcoming work, but Kotlin's type system prevents me from implementing a deprecation +process. +**This version will require a large one-time refactor of much of your bots' code,** +replacing translatable strings with `Key` objects. + +Before getting started, I recommend reading [the new i18n documentation page (TODO)](#), +which will introduce you to the new translation system and explain how everything works. + +**If you absolutely need to be on the bleeding edge but don't have time to translate your project,** +feel free to use the `String.toKey(bundle)` extension function temporarily. + +### Gradle Plugin Users + +If you're using the KordEx Gradle plugin, start by **updating to version 1.5.0,** which will be released shortly. + +- If you haven't created a translation bundle, you'll want to create one + [as described here](https://docs.kordex.dev/internationalization.html#basic-concepts-bundles). +- After that, configure the new `i18n` builder in the Gradle plugin, providing values for at least the `classPackage` + and `translationBundle` properties. +- Next, run the `generateTranslationsClass` Gradle task, which will generate a new `Translations` object containing + references to the keys in your translation bundle. + +- Finally, replace references to translatable strings with the `Key` objects in your generated `Translations` object. + + +### Other Users + +If you can't (or don't want to) use the KordEx Gradle plugin, you'll need to create the `Key` objects yourself. +This can be done in one of the following ways: + +- **Recommended:** Take a look at [the i18n tools repo](https://github.com/Kord-Extensions/i18n) and use the API or + CLI tool to integrate the generator, then follow the other steps as if you were using the Gradle plugin. + +- Alternatively, create `Key` objects yourself, storing them in a central location and being sure to provide them with + the `bundle` constructor parameter. + + +### Changes + +Here's an overview of what's changed: + +- All translatable strings now take `Key` objects instead. + - This allows us to provide a much more human-friendly API and covers many advanced use-cases that simply aren't + possible when you can only pass a string. + +- All `bundle` properties have been removed. + - Instead, the required translation bundle should be stored in your `Key` objects. This is the default behavior when + using the generator, as explained above. + +- All `translate` convenience functions have been removed. + - Instead, use the `with` functions on `Key` objects to create a newly configured object, and then use the + corresponding `translate` functions to translate that `Key` in place. + +- The translation provider API has been updated to support translating `Key` objects instead of strings. + String-based functions have been deprecated. +- Component labels, placeholders, and initial values may now be translated as required. +- All modules have been translated and [added to Weblate](https://hosted.weblate.org/engage/kord-extensions). + +This is a **huge** update, and a lot of work has gone into it. While it is true this may be a painful update for +those of you maintaining large bots, using a custom type means that no such refactors should be required going forward. + +I truly believe that this update will be a huge benefit to most of you, and I hope you enjoy working with the new tools. +**As this is such a huge update, it is possible I may have missed something in my testing,** so please let me know how +you get on! + +If you're curious about my mindset while designing this system, +[I wrote a blog post about it!](https://blog.gareth-coles.dev/a-better-i18n-api) + +As always, thanks for sticking with Kord Extensions, and I hope you're all having a good spooky season! + +## v2.2.1-SNAPSHOT + +_Published to Discord on August 31st, 2024._ + +**KordEx 2.2.1-SNAPSHOT** has been pushed and will be published in about 15 minutes. It includes these changes: + +- Clean up the translation provider `translate` functions, which were a tricky mess to navigate at times. +- Make the default translations provider scan through loaded plugins (if the plugin system is enabled) to locate + bundles provided by them. + - This also comes with some extra debug-level logging to help you figure out what's going on. + - If you're unfamiliar with the translations system, + [the plugin template repo](https://github.com/Kord-Extensions/template-plugin) now contains some bundled + translations to help you figure out how things work. + +As always, let me know how you get on! + +## v2.2.0-SNAPSHOT + +_Published to Discord on August 23rd, 2024._ + +It is time for another version bump, specifically to `2.2.0-SNAPSHOT`. That was fast, huh? + +This release contains the following changes: + +- Update to Kotlin `2.0.20`. Finally, a stable release with the fixes we needed! + +- Move all the nested builders out of the `ExtensibleBotBuilder` class, and into their own separate class files, + including their corresponding nested types. + Naturally, if you've written any extension functions using one of these classes as a receiver, you'll need to update + them. + I tried to make this deprecate cleanly, but unfortunately, I couldn't — you can't place type aliases anywhere other + than the top-level of a file! + +- Rewrite the `about { }` builder for the corresponding extension. + This removes the default "general" information command (and mandatory footer), instead allowing you to create your + own fully custom commands while providing a default, configurable "copyright" subcommand. + For more information on this, please [read the new docs](/bots/config/about). + I feel like it is a lot less obnoxious, personally! + +- [#324](https://github.com/Kord-Extensions/kord-extensions/issues/324) — Fix an error thrown by the data collection + system when the plugin system was disabled. + Instead of simply not creating a plugin manager, KordEx will now always create one but won't try to load any plugins + if the plugin system is disabled. + +As always, this release will be available once GitHub builds it, in about 15 minutes. Let me know how you get on! + +## v2.1.0-SNAPSHOT + +_Published to Discord on August 20th, 2024._ + +I've gone ahead and pushed an update to KordEx v2, which will be released as `2.1.0-SNAPSHOT`. This version provides the following updates: + +- **Kotlin:** `2.0.20-Beta1` → `2.0.20-RC2` +- **JEmoji:** `1.4.1` → `1.5.1` +- **KAML:** `0.60.0` → `0.61.0` +- **KSP:** `2.0.20-Beta1-1.0.22` → `2.0.20-RC2-1.0.24` +- **Logback:** `1.5.6` → `1.5.7` +- **MongoDB:** `5.1.2` → `5.1.3` +- **OSHI:** `6.6.2` → `6.6.3` +- **Sentry:** `7.12.0` → `7.14.0` +- **SLF4J:** `2.0.13` → `2.0.16` + +I didn't update Gradle to 8.10 as the licencing plugin we're using runs into a StackOverflowError with that version. + +If you're using the KordEx Gradle plugin, there's nothing you need to do other than to update your Kotlin version to +`2.0.20-RC2` (or set it to ignore the Kotlin version if this isn't possible for you). + +## v2.0.0-SNAPSHOT + +_Published to the docs on August 13th, 2024._ + +This update's summary was published on this page. + +## v1.9.0-SNAPSHOT + +_Published to Discord on July 1st, 2024._ + +This release targets **Kotlin `2.0.20-Beta1`** and **Kord `0.15.0-SNAPSHOT`.** +Due to our distribution model and Kord updating `kotlinx.serialization`, you must update to Kotlin 2.0.20-Beta1 or +later to use this release! + +Additionally, as we're forced to use a beta version of Kotlin 2, please note that IDEA's Kotlin plugin will erroneously +display compiler errors that were caused by bugs in earlier versions of Kotlin 2. +To be specific, expect errors regarding broken typing for generic types explicitly defined using `T & Any`. +If you're working with the KordEx code directly, please rely on build output rather than IDE hints for the time being. + +Thanks are due in particular to LustigerLurch for helping with the K2 porting work. +Cheers! + +This release is available _right now!_ Have at it, and let me know how you get on! + +#### Other Changes + +- Checks have been updated to support more of Kord's built-in events and now support more data from previously supported events, including: + - `AutoModerationActionExecutionEvent` + - `AutoModerationEvent` + - `GuildAuditLogEntryCreateEvent` + - **Note:** Kord doesn't support the `guildId` field on audit log entries. + + - `GuildScheduledEventEvent` + - `InviteCreateEvent` + - `PresenceUpdateEvent` + - `ThreadChannelDeleteEvent` + - `ThreadMembersUpdateEvent` + - `ThreadUpdateEvent` + +- A new `FixedLengthQueue` container type. If you need this, please read the KDoc comments! + +#### Dependency Updates + +- **Apache Commons Validator:** `1.9.0` +- **Groovy:** `3.0.22` +- **JUnit:** `5.10.3` +- **KAML:** `0.60.0` +- **Kord:** `0.15.0-SNAPSHOT` +- **KSP:** `2.0.20-Beta1-1.0.22` +- **Ktor:** `2.3.12` +- **Kotlin:** `2.0.20-Beta1` +- **Kotlin Logging:** `7.0.0` +- **kotlinx.serialization:** `1.7.1` +- **MongoDB:** `5.1.1` +- **Sentry Java:** `7.10.0` + +## v1.8.1-SNAPSHOT + +_Published to Discord on June 3rd, 2024._ + +It is time for a new snapshot - `1.8.1-SNAPSHOT` to be precise. + +This snapshot cycle begins with a change to choice converters. Specifically: + +- Choice converters now support chat commands. + ([#137](https://github.com/Kord-Extensions/kord-extensions/issues/137)) + - When used with chat commands, choice converters will accept either keys (case-insensitively) or values + (case-insensitively where appropriate), in that order. + - When used with chat commands and provided with an invalid value, choice converters will provide an error message + along with a list of possible options. + +This is a new snapshot cycle because I want to be sure it doesn't break your existing bots. +If this is some functionality you've been waiting for, please thoroughly test your bots before updating! + +This should be available in around 15 minutes, as usual. + +And sorry for keeping you all waiting! + +## v1.8.0-SNAPSHOT + +_Published to Discord on February 21st, 2024._ + +I've just pushed a new version of KordEx, to update dependencies and align with Kord's new `0.14.0-SNAPSHOT` releases. + +- `jemoji`: 1.3.3 → 1.3.4 +- `junit`: 5.10.1 → 5.10.2 +- `kaml`: 0.56.0 → 0.57.0 +- `kotlin-logging`: 6.0.2 → 6.0.3 +- `kord`: 0.13.0-SNAPSHOT → 0.14.0-SNAPSHOT +- `ktor`: 2.3.7 → 2.3.8 +- `kotlinx-coroutines`: 1.7.3 → 1.8.0 +- `kotlinx-serialization`: 1.6.2 → 1.6.3 +- `logback`: 1.4.14 → 1.5.0 +- `sentry`: 7.1.0 → 7.3.0 +- `slf4j`: 2.0.11 → 2.0.12 +- `time4j`: 5.9.3 → 5.9.4 + +This release will be available under `1.8.0-SNAPSHOT` once it's been built, in about 10–15 minutes. + +## v1.7.2-SNAPSHOT + +_Published to Discord on February 21st, 2024._ + +A new paginator feature! + +This is a quick message to announce the release of KordEx `1.7.2-SNAPSHOT`, +which includes some breaking changes to the paginator API for those of you with custom paginators. + +This also brings a new user-facing change — paginator chunking! Requested by @beerpsi, this allows you to specify a +chunkedPages property in your paginator builders, which will cause the paginator to display its pages in chunks of the +given size. +This may be useful for paginators with a lot of small embeds. + +When `chunkedPages` is set to `1` (the default), the paginator will behave as it always has. +Set it to any value up to 9, however, and that many pages will be displayed to your users at once. +When you do this, an extra embed will be generated at the bottom, containing information about the current/total pages, +switchable groups, and so on — instead of placing this at the bottom of each page embed. + +As always, `1.7.2-SNAPSHOT` is building and will be available in 10–15 minutes. +Math is hard, so please let me know how you get on! + +## v1.7.1-SNAPSHOT + +_Published to Discord on November 30th, 2023._ + +Hello! Today's update is potentially exciting for those of you using the Sentry integration, bringing a few long-overdue changes. + +Today's changes are part of [issue #260](https://github.com/Kord-Extensions/kord-extensions/issues/260), +submitted by @Pyrrha yesterday. + +It is worth noting that **these are breaking changes for anyone using the Sentry integration!** +Please review your integration usage before pushing any updates! + +- The Sentry `Scope` and `Breadcrumb` objects are no longer exposed. + Instead, a set of classes based on the new `SentryCapture` type have been added, + and these have replaced the previously mentioned Sentry objects in all builders and functions. + +- It is now possible to define what types of data may be submitted to Sentry via the new `defaultDataTypes` builder, + which you can find in the extensions → sentry builder. + Use the following properties to change what can be submitted: `arguments`, `channels`, `guilds`, `roles`, and `users`. + - For chat commands, command arguments and message content are both considered `arguments`. + This is also true for Mappings Extension query strings. + Slash command arguments aren't currently submitted. + +- The Sentry builder now also provides the `dataTypeTransformer` and `predicate` builders, + to further customize what can be submitted to Sentry based on an individual `SentryCapture` subtype. + +- The new `SentryCapture` types expose more of the Sentry APIs than before, + allowing you to provide scope hints and severity levels, among other things. + +- When adding data to the `data`, `hints`, and `tags` maps defined in the `SentryCapture` subtypes, + you can now define how the integration should filter the values within based on their keys, using one of two methods: + - Prefix the key with the type and a dot (e.g. `channel.key`) — the key will not be modified if it's submitted. + - Suffix the key with the type and two colons (e.g. `key::channel`) — the colons and type will be removed from the key + if it's submitted. + +**Note:** Key-based filtering does not support nested container types, as this doesn't appear to be possible within +Kotlin's type system. However, it is generally best not to nest data too far regardless. + +The [configuration docs](https://docs.kordex.dev/sentry.html) have already been updated if you need them. + +As always, please give things a try and let me know how you get on! + +:::danger Disclaimer + +While these new changes are designed to make it easier for you to follow data protection laws (such as the GDPR), +please note that I'm not a lawyer. +I'm also not qualified to make privacy-related decisions about your bots on your behalf. + +I'm happy to fix any data leakage issues or improve the above systems, +but it is your responsibility to ensure that your usage of Sentry complies with any relevant privacy laws. + +::: + +## v1.7.0-SNAPSHOT + +_Published to Discord on November 24th, 2023._ + +The new KordEx snapshot cycle begins with `1.7.0-SNAPSHOT` targeting Kord `0.13.0-SNAPSHOT`. +This release contains breaking deprecations, so remember to rebuild your code before pushing an update! + +As always, feel free to give things a try, and let us know if you run into any issues! + +## v1.6.0 + +_Published to GitHub on November 24th, 2023._ + +This release targets Kord `0.12.0`, the latest stable release for Kord. +Hot off the presses for those of you who rely on stable releases! + +**Highlights from this release:** + +- Transitioned to a new Discord server. + You can also now [support development via Ko-fi!](https://ko-fi.com/gsc) + +- Began work on [a new documentation site with Writerside.](https://docs.kordex.dev/) + You can find the source [in its own repository.](https://github.com/kord-extensions/docs) + +- We now use Kotlin 1.9.20 and Java 17, and you should too. +- We've updated a bunch of dependencies. Read more in the `libs.versions.toml` file. +- Created an experimental `ChangeSet` type, used to compare voice states. We need feedback on this! +- Introduced extra first-party data adapters, such as the MongoDB data adapter. +- An in-development custom plugin system to better meet KordEx's needs. +- A generic interaction context type to support more generalised code. +- A paginator mutation system, allowing for changes to pages, paginators, and paginator components. +- New checks that match the bot application's owner and team roles. +- New supported locale: Korean by @Penta0308. +- Many small fixes, quality-of-life features and updates that aren't large enough to detail here. + +**Community Contributions:** + +- `#200` by @DeDiamondPro — More select menu types. +- `#201` by @NoComment and @sschr15 — Convert mappings extension configuration to storage units. +- `#211` by @Lukellmann — Fix breaking changes from Kord Kotlin/JS support. +- `#212` by @Lukellmann — Fix compilation error caused by source-incompatible Kord change. +- `#222` by @ADudeCalledLeo — Allow slash command groups and subcommands at the same level. +- `#243` by @sschr15 — Update Linkie and add three new namespaces. + +As always, if you run into any problems, please let us know! + +## v1.6.0-SNAPSHOT + +_Published to Discord on October 27th, 2023._ + +I've just gone ahead and pushed KordEx version `1.6.0-SNAPSHOT`. + +This release was pushed in part to address [#216,](https://github.com/Kord-Extensions/kord-extensions/issues/216) +which required breaking structural changes. + +- `EphemeralInteractionContext` and `PublicInteractionContext` now both extend the new `InteractionContext` interface, + which provides a common base type exposing the functionality present in both subtypes. + +- As the interaction context functions have been moved to a base interface, they're no longer inline. + You'll find them directly within their respective types, and you will need to remove the imports you were using. + +- `respondPublic` and `respondEphemeral`, which were used to deliberately send Discord the wrong follow-up type, + have been moved to a single base function named `respondOpposite`. + +- New opt-in annotations have been added to warn you when a function call may result in unexpected behavior. + For the time being, this is only being used for some interaction context functions, but they may be used more widely + later. + +- \[[#214](https://github.com/Kord-Extensions/kord-extensions/issues/214)\] `ArgumentParsingException` now includes the + relevant `locale` and `bundle` properties, which can be used for further customisation of error messages. + +As always, let me know if you run into any issues. + +## v1.5.12-SNAPSHOT + +_Published to Discord on October 27th, 2023._ + +Another quick update, KordEx version `1.5.11-SNAPSHOT`. + +This version tracks Kord `0.12.0-SNAPSHOT`, and contains the following dependency updates: + +- `jansi` 2.4.1 +- `jsoup` 1.16.2 +- `kord` 0.12.0-SNAPSHOT +- `ktor` 2.3.5 +- `mongodb` 4.11.0 +- `sentry` 6.32.0 + +I've also begun rewriting the documentation using Writerside — you can keep an eye on that progress +[here.](https://github.com/Kord-Extensions/docs) + +As always, let me know if you run into any issues. + +## v1.5.10-SNAPSHOT + +_Published to Discord on October 5th, 2023._ + +Quick notification to announce the release of KordEx version `1.5.10-SNAPSHOT`. + +- Work has begun on a new plugin system, supporting proper constraints, and a more reasonable class-loading pattern. + This system is self-contained, and you'll be able to use it in any project if you wish to — but there's more work to + be done on it right now. + **If you have any feedback or information on what might be useful from a plugin system, please let me know!** + +- KordEx now requires **at least Java 13** as it now uses APIs only available since that version. +- KordEx has been updated to Kord version `0.11.1`. + +As always, let me know if you run into any issues. + +## v1.5.9-SNAPSHOT + +_Published to Discord on August 9th, 2023._ + +I've just pushed KordEx `1.5.9-SNAPSHOT` to GitHub, and it should be available on Maven in 15-20 minutes. + +This is a dependency update version — KordEx is now built against Kord 0.11.0-SNAPSHOT and makes use of Kotlin 1.9.0. +As always, please let me know if you run into any issues! + +## v1.5.8-SNAPSHOT + +_Published to Discord on June 18th, 2023._ + +`1.5.8-SNAPSHOT` has been released, to follow Kord's new snapshot policy (and track Kord `0.10.0-SNAPSHOT`). +As usual, it should be available on Sonatype Snapshots in about 15 minutes. + +I've also added a small util - `User.tagOrUsername()` - which returns a username with or without a discriminator, +depending on whether a user has migrated to the new username system. + +This will be removed pretty quickly once Discord has fully migrated everyone, so be warned. + +## v1.5.7-SNAPSHOT + +_Published to Discord on March 25th, 2023._ + +`1.5.7-SNAPSHOT` (based on Kord `0.9.x-SNAPSHOT`) has been published, and that'll be the working version for now. + +## v1.5.6 + +_Published to GitHub on March 25th, 2023._ + +This release targets Kord `0.8.0`, the latest stable release for Kord. +It has been over a year since the previous stable Kord release, +so it has also been a year since the previous stable KordEx release. +How time flies, right? + +Please note that as of this release, the `develop` branch is going away. +Please re-parent your PRs to target the `root` branch. + +**A note about Maven repos:** + +Please be aware that as of this release, KordEx is now being deployed to these Maven servers: + +- **Releases:** Maven Central +- **Snapshots:** Sonatype Snapshots + +KordEx releases will no longer be published to the Kotlin Discord maven repo. + +**Highlights of this release:** + +- We now use Kotlin 1.8.0, and you should too. +- Support for Modal forms has been added, + which function similarly to argument classes and can be provided to your commands and components in the same way. + +- KordEx is now capable of intercepting unknown Kord events and submitting its own events in their place. + Initially, this just includes the experimental member screening events available on a handful of servers, + but PRs will be accepted for other event types that Kord doesn't plan on supporting. + +- A data adapter and storage unit system have been added, allowing extensions to more easily store data, + with the ability for individual bots to define how that data should be stored. + Note, however, that this is an early version of that system, and it will likely be changed significantly later. + +- A module, `extra-pluralkit`, has been created to make things easier for bots that wish to support PluralKit, + a widespread accessibility tool on Discord mainly used by plural systems. + +- Additional checks have been added that allow you to test for NSFW channels and guild NSFW levels, among other things. + +- A cache map object with an extra typed API has been added, allowing you to cache data between checks and use it in + the body of your commands. + This also allows you to cache data between task runs and provide extra event context for Kord events. + +- Commands were given their own translation bundles, which fall back to their extension's if missing. +- Slash commands may be fully localised. +- Many misc utils have been added, and fixes and additions have been made for existing utils. +- Individual converts have been updated with new features and fixes. +- The test bot has been moved to its own module, which makes it easier to look over and modify. + +As always, if you run into any problems, please let us know! + +## v1.5.6-SNAPSHOT + +_Published to Discord on December 15th, 2022._ + +I've been hard at work, finally bringing an abstraction for Discord's modals to KordEx. +As of this message, `1.5.6-SNAPSHOT` has been pushed, which includes my first attempt at getting an abstraction together. + +To use modals, create a class that extends `ModalForm`, and pass the constructor into your command and component +functions — just how you would with your argument classes. +If you're using this with a command that already has arguments, then this should go **after** your `Arguments` class +constructor reference — `ephemeralSlashCommand(::MyArgs, MyModal) { ...`. + +Within your new class, you can create variables that store widgets, which can be created by using the `lineText` and +`paragraphText` builder functions. +Remember to provide a label for all of your widgets! + +This introduces a (nullable due to Kotlin's type system) parameter to your `action` blocks — which you can get at like +this: `action { modal → ...`. +You can access the input data provided by the user by accessing the `value` property on each widget. + +Modal titles, widget labels, etc. are translatable and will inherit the bundle you're using in the definition +context — so, the bundle your component or command is using. +You can also define the bundle in your `ModalForm` subtype instead if you prefer. + +## v1.5.5-SNAPSHOT + +_Published to Discord on July 8th, 2022._ + +I've just pushed up `1.5.5-SNAPSHOT`, which uses Kotlin `1.7.10` and is based on Kord's `0.8.x-SNAPSHOT` versions again. + +Please note that all KordEx dependencies have been updated — as usual, you'll find them in the `libs.versions.toml` + +Let me know if you run into any issues! + +## v1.5.3-SNAPSHOT + +_Published to Discord on April 17th, 2022._ + +KordEx 1.5.3-SNAPSHOT has been pushed to GitHub and is building now. +However, there's several things to be aware of: + +- Support for Java versions older than Java 11 had to be dropped, due to dependencies also dropping support. +- This version of KordEx **was not built with Kotlin 1.6.20;** however, everything else has been updated. + +That last point is particularly notable because it turns out that Kotlin 1.6.20 is causing a lot of problems for several +projects. + +- There's a compiler bug that breaks compilation for classes containing functions that take a suspending callable + argument with a default value. + +- KSP has a number of serious problems on 1.6.20 which entirely break our annotation processor. + +- Both KSP and the Kotlin compiler have developed issues which don't show up unless you're doing a clean build, which + makes me worry that there are serious consistency issues in build output. + +In short, Kotlin 1.6.20 has showstopping issues that make me feel like it should never have been pushed as a stable +release. +The same goes for KSP 1.0.5, which only supports Kotlin 1.6.20. + +While it's possible that your bots may build just fine with Kotlin 1.6.20, and it's true that Kord has managed to move +to Kotlin 1.6.20, **I do not recommend moving to it for your bots.** +I'd advise that you stick with Kotlin 1.6.10 until these issues are fixed, which may require another Kotlin compiler +release cycle. + +## v1.5.2-RC1 + +_Published to GitHub on February 6th, 2022._ + +This release targets Kord `0.8.0-M9`, the latest stable release for Kord. +We've been waiting for this one for some time — it is nice to get something new out there for you! + +**A note about Maven repos:** + +Please be aware: As of this release, the following Maven repos are no longer being mirrored by the Kotlin Discord Maven +server: + +- Bintray: Exposed, Kord, KTor +- Google +- Maven Central +- Sonatype Snapshots + +You'll need to update your buildscripts to correspond with these changes. +If you run into any trouble, you can always take a look at the KordEx template project again. + +**Highlights of this release:** + +- KordEx is now licensed under the Mozilla Public License. + It previously used The MIT License, but I felt that this was a little too weak. + +- Rewritten converter function syntax, which now uses a DSL instead of a function call. + This makes things overall much cleaner and allows us to add additional features to the argument system easily. Speaking of which... + +- Command arguments now support `mutate { }` and `autoComplete { }` (for slash commands) options. + Additionally, validation now uses `validate { }`, which uses an API that's much closer to how checks work elsewhere. + +- Improvements to application command registration, including not trying to remove commands that were already removed via a batch update. + +- Easier disabling of components, optional locking for guild member requests, Sentry support for scheduled tasks, + and the removal of the requirement for paginator page groups to have the same number of pages. + +- Extra utilities, such as better names for timeout-related fields, an async start function that launches, + convenience single-action functions for modifying Members quickly, easy functions for supplying maps to autocomplete + interactions, and more! + +- An early version of a dynamic plugin loading system. Because you can't get at classes from loaded plugins, this + isn't as useful as it could be, but we'll be expanding upon it later. + +- A myriad of bug fixes and new translations. + +- **Mappings Extension:** Support for Quilt Mappings, Hashed Mojmap and slash command autocomplete for MC versions. +- **Phishing Extension:** Better filtering, redirect support, and usage of the Sinking Yachts websocket for instant domain list updates. + +At the moment, the wiki hasn't been updated for these changes. +Unfortunately, I lack the time to update them quickly, but they'll get there! + +As always, if you run into any problems, please let us know! + +## v1.5.2-SNAPSHOT + +_Published to Discord on January 16th, 2022._ + +I've just merged KordEx 1.5.2-SNAPSHOT. +This new version of KordEx contains the new DSL-style converter builder system, +which additionally requires that your custom converters make use of KSP and the `@Converter` annotation. +It also adds check-style `validate {}` builders for validation (eg via `failIf()`) and a `mutate {}` +function allowing you to return a changed final value. + +This system is _not_ documented yet and may not be for some time. +However, I encourage you to try this out with bots that don't have custom converters just to see how things work. +For the most part, it should be easy to figure things out — let's say you have a converter like this: + +```kt +val username by string("username", "Username to delete from the universe") +``` + +You would update this by switching to the new syntax: + +```kt +val username by string { + name = "username" + description = "Username to delete from the universe" +} +``` + +Of course, your IDE autocomplete will help you out here. +Additionally, please note that all coalescing converter functions have been renamed, +replacing `coalesced` with `coalescing` in the function name. + +## v1.5.1-RC1 + +_Published to GitHub on October 12th, 2021._ + +This release is our first "stable" release in a long time, targeting Kord `0.8.0-M7`. +The reason is largely due to Kord's long snapshot cycle, which itself was caused by many changes to Discord's APIs. +In turn, this means that this release contains a mind-boggling number of internal changes. + +We've done our best to keep things as compatible as possible, API-wise. +Despite this, though, we've had no choice but to break a few things. + +Highlights of this release: + +- With a lot of help from @ByteAlex, we've been able to eliminate many, many unnecessary cache hits, +using Behaviors rather than entities wherever possible. +This makes KordEx far more suitable for large bots with different caching requirements. + +- Full support for message and user commands have been added, +which comes with a full rewrite of the application commands system. +Application commands now always require a `public` or `ephemeral` type, to help keep things safe. +Additionally, our old message commands are now named chat commands and have their functions prefixed with `chat`. + +- The components system has been fully rewritten, including a similar typing requirement to application commands. + It comes with a `ComponentContainer` type which makes it easier to re-use components, + as well as a callback registry for advanced use-cases (such as components that need to work after a restart). + +- The Sentry integration has been rewritten, and you'll find a `SentryContext` provided everywhere you'd expect to be + able to use Sentry, instead of a plain list of breadcrumbs. + This, along with several other improvements, should make Sentry much more pleasant to work with. + +- Our translation platform [has been switched to Weblate](https://hosted.weblate.org/engage/kord-extensions/). + If you're a translator (or would like to help with translations), please head over there! + +- Lots of deprecated things have now been removed. + If you were still using them, well, you were warned! + +There are far too many changes to list here. +The existing pages on the wiki have been rewritten for this release, +and we'd suggest taking a look at them to refresh your knowledge. +There's still documentation work which needs doing, but we'll get there! + +As always, if you run into any problems, please let us know! + +## v1.5.1-SNAPSHOT + +_Published to Discord on October 7th, 2021._ + +**KordEx 1.5.1-SNAPSHOT is now live!** + +I decided to bump the version in line with the update to Kord `0.8.0-M6`. +This release beings a bunch of underlying changes that people have been waiting for in Kord, +and it also brings the following fixes on top of the current snapshot work: + +- Fix for `deferredAck` being inverted for ephemeral components. +- Load order has now returned to what it was before the automatic intent generation feature was added, + so the `Kord` instance is once again created a lot earlier. + +## v1.5.0-SNAPSHOT + +_Published to Discord on September 6th, 2021._ + +As of the latest push, **KordEx 1.5.0-SNAPSHOT is ready for testing!** + +A lot of things have changed in this release. For an overview, take a look at +[the tracking issue](https://github.com/Kord-Extensions/kord-extensions/issues/72) — +I'll list some things you may be wondering below. + +- Every single command function has been replaced: + + - Message commands are now chat commands, with the `command` DSL function being renamed to `chatCommand` - and + they're disabled by default, in line with Discord's requirements for larger bots. + + - Slash commands have been split into `publicSlashCommand` and `ephemeralSlashCommand`. + - Message and user commands have been added, with `public*` and `ephemeral*` DSL functions. + +- Interaction-based commands/components no longer have `autoAck`, but you can use the `initialResponse` DSL to ack + with response or call the `edit` function in your `action` to edit the acknowledged response. + +- Components have been completely revamped, and everything is typed similarly to how the interaction-based commands are + now — though, since components are centralized now, no timeout mechanism has been provided for them. + +- Scheduled tasks for the `Scheduler` now have an easy `restart` function. + +- Paginators have been split out into four types (chat, public followup, public response, and ephemeral response), + matching the contexts you'd use them in. + This means you can have an ephemeral interaction, and the paginator will edit its initial response, finally! + +There are many, many other changes, big and small. +We'd massively appreciate it if people could start testing this release and provide us with feedback on what's not +working properly — and what needs changing. + +If something is missing, please leave a comment on [the tracking issue](https://github.com/Kord-Extensions/kord-extensions/issues/72) + +## v1.4.4-RC4 + +_Published to GitHub on August 21st, 2021._ **Changes since v1.4.4-RC3:** + +- Fix an error when you have paginator pages with empty footer text. +- Add suspending paginator DSL functions in both types of command context. +- Set up VSC Web for medium-complexity PRs — head to the `develop` branch on GitHub and hit `.` on your keyboard! +- Make translation debug logging less verbose by moving it to `trace` logging. +- Allow for enabling the Sentry integration without adding the Sentry extension. +- Allow for customising the KordBuilder during setup. +- **Mappings module:** Fix a bad Linkie dependency. + +The following issues were solved in this release: + +- [#69 (Nice!)](https://github.com/Kord-Extensions/kord-extensions/issues/69): + Allow selecting a custom HTTP engine for Kord (niche request!) + +- [#70](https://github.com/Kord-Extensions/kord-extensions/issues/70): + Less verbose translation logging. + +- [#71](https://github.com/Kord-Extensions/kord-extensions/issues/69): + Allow disabling feedback command when using sentry extension. + +## v1.4.4-RC3 + +_Published to GitHub on August 9th, 2021._ **Changes since v1.4.4-RC2:** + +- Update to Kord 0.8.0-M4 +- Checks are now consistent in behavior regarding events that they don't support — negating checks + (containing the word `not`) will always pass in this situation, and all other checks will always fail in this situation. + +- Paginator `Page` objects are now backed by a Kord `EmbedBuilder`, and should be constructed using the builder syntax. + For example: `Page(bundle) { description = "This is a page." }`. + +- The `ExtensionEvent` class now extends Kord's `Event` type, with a shard number of `-1`. + +- The `guildFor()` function for `InteractionCreateEvent` was changed to manually get the guild from the interaction + data, as it can otherwise be missing in some situations. + + +Documentation work is still ongoing, and KordEx will see more changes as things are documented - the documentation +process makes it easy to see (and change) things that don't make intuitive sense. + +The following issues were solved in this release: + +- [#65](https://github.com/Kord-Extensions/kord-extensions/issues/65): + Make ActionableComponentBuilder's ID customizable. + +- [#66](https://github.com/Kord-Extensions/kord-extensions/issues/66): + Add the addition emoji helpers from `ButtonBuilder` to `SelectOptionBuilder`. + +- [#67](https://github.com/Kord-Extensions/kord-extensions/issues/67): + Paginators, pages do no support embed fields. + + +## v1.4.4-RC2 + +_Published to GitHub on August 5th, 2021._ **Changes since v1.4.4-RC1:** +- Update to Kord 0.8.0-M3 + +This is a release to match Kord's hotfix, which fixes a regression that broke interaction acknowledgements. + +## v1.4.4-RC1 + +_Published to GitHub on August 4th, 2021._ + +:::note + +This release contains breaking changes for the checks system. +**All custom checks must be rewritten!** +If you need help figuring this out, please let us know on Discord. + +::: + +**Changes since v1.4.2:** + +- Update to Kord `0.8.0-M1`. +- Default to rest-aware caching and allow configuration of caching strategy. +- Add kotlinx.datetime converters and utils. +- Add support for select menus in the `Components` builder. +- Add support for Discord's slash command perms, including enforcing them at the framework level. +- Add support for threads, including extra checks that only operate on threads or flatten them to top channels. +- Trim slash command arguments to workaround inconsistent data from Discord. + +- Redo the checks system, requiring calls to one of the `fail` functions to fail a check. + - **This is breaking, checks must be redone.** + - Checks may now also return a failure message for commands. + +This release includes commits that address the following issues: + +- [#58](https://github.com/Kord-Extensions/kord-extensions/issues/58) — Checks overhaul +- [#62](https://github.com/Kord-Extensions/kord-extensions/issues/62) — + Make automatic acknowledge type naming more consistent + + +The following PRs were merged: +- [#61](https://github.com/Kord-Extensions/kord-extensions/pull/61) — + Use epochSeconds for toDiscord timestamp extension function + +- [#64](https://github.com/Kord-Extensions/kord-extensions/pull/64) — insert semicolon in Lclass;method for mixin target + + +## v1.4.1 + +_Published to GitHub on July 2nd, 2021._ + +This is a huge release, and a massive milestone for KordEx. +There have been far too many changes to detail in this changelog — nearly 210 commits in total. Some highlights include: + +- Support for slash commands. +- A completely rewritten argument parser. +- Support for button components and new paginators that use them. +- Decoupling of KordEx classes using Koin. +- An annotation processor for generating converter functions. +- Full support for i18n, including translations. + +... and much, much more! + +If you're migrating from the previous stable version, there's a slightly more in-depth overview of the changes in the +docs. +Otherwise, we suggest treating this release version as an entirely new iteration of the KordEx concept — +most things have been changed, and almost everything will need at least a minor tweak to get things to work. + +As always, if you need help, contact us on Discord! + +## v1.4.0-RC7 + +_Published to GitHub on March 2nd, 2021._ + +Chances since KordEx 1.4.0-RC6: + +- Slash command support! + As we now have two types of command, the old `Command` class has been renamed to `MessageCommand`. + Additionally, `CommandContext` is now `MessageCommandContext`. + You'll find similar classes that relate to slash commands as well. + +- All arguments specified in `Arguments` objects now require a description. + +- Command arguments classes (and just classes in general) are now nicer to pass around, + as they're treated like builders and received in more logical places. + +- The help extension and sentry functionality have both been updated to support command argument descriptions and + slash commands for feedback. + +- The paginator has been completely rewritten (and documented), with support for switchable page sets! +- Far, far, far too many other changes to simply list here. + +This release has tons of improvements, deprecations, new features and breaking changes. +Some of them are listed above, but you should read the migration guide instead of simply relying on this release description. + +**Note:** This release targets Kord's latest snapshots as of the time of this release. +As they aren't formal releases, there's no way to link to release notes. +This release mostly exists to provide an anchor point for users that wish to stick to stable versions while still +existing within the middle of Kord's extensive snapshot cycle. +Hopefully, Discord starts making their API more stable soon! + +## v1.4.0-RC6 + +_Published to GitHub on January 3rd, 2021._ + +Chances since KordEx 1.4.0-RC5: + +- **BREAKING:** Rewrote the `bot.start()` function. + If you wish to supply an intent builder or presence builder, this function now takes a single builder lambda + (and is thus a DSL function) instead of taking two separate lambdas. + It was not possible to deprecate the old version of this function due to conflicting parameter lists, + so you'll have to update your bots. For more information, please see the bot object documentation. + +- **DEPRECATION:** Rewrote the `bot.addExtension()` function. + The old function is still there and has been deprecated, while the new function takes a builder function instead of + a reflection-based class object. + Functionally, this just means changing `bot.addExtension(ExtensionClass::class)` to + `bot.addExtension(::ExtensionClass)` in most cases. + +- Added extra checks regarding member permissions on guilds — although, please note that these only look at guild + permissions and ignore channel overwrites. + For more information, please see the checks documentation. + +This release targets Kord `0.7.0-RC`. + +## v1.4.0-RC5 + +_Published to GitHub on December 29th, 2020._ + +Chances since KordEx 1.4.0-RC4: + +- Added Koin integration for those that need it. For more information, please read the docs! + +This release targets Kord 0.7.0-RC. + +## v1.4.0-RC4 + +_Published to GitHub on December 23rd, 2020._ + +Chances since KordEx 1.4.0-RC3: + +- Completely rewrote the paginator, allowing for switchable page groups and full customization of page embeds, + among other things. + There's a lot to this, so I recommend you read over the docs and integration test and take it out for a spin. + +This release targets Kord 0.7.0-RC. + +## v1.4.0-RC3 + +_Published to GitHub on December 19th, 2020._ + +Chances since KordEx 1.4.0-RC2: + +- Add `outputError` property to `OptionalConverter` (but not coalescing variants — that wouldn't make sense) + and expose it in `Arguments` extension functions. + +This will make optional converters still output parse errors and fail command parsing if `true` is provided +(`false` is the default), if that's something you end up needing in your command parsing logic. + +This release targets Kord `0.7.0-RC`. + +## v1.4.0-RC2 + +_Published to GitHub on December 16th, 2020._ + +Chances since KordEx 1.4.0-RC1: + +- Add `commandName` to `CommandContext` objects, allowing you to get the (lowered) name the user gave for invocation. +- Fix `breadcrumb()` not actually adding the breadcrumb to the `MutableList` in both `CommandContext` and + `EventContext`. + +This release targets Kord `0.7.0-RC`. + +## v1.4.0-RC1 + +_Published to GitHub on December 12th, 2020._ + +This is our very first properly versioned release! + +KordEx 1.4.0 brings Kord 0.7.x support (but we'll only release RCs until Kord releases a stable version). +Some changes: + +- Completely reworked command argument parsing system, with a fully documented argument conversion system. +- First-class integration with Sentry. +- Actual documentation, finally. + +As this is our first real release, this changelog will be a little lacking — it does encompass **174 commits**, +after all. +Future releases will be more detailed! + +This release targets Kord `0.7.0-RC`. + +## v1.0 + +_Published to GitHub on June 22nd, 2020._ + +This is the final version of Kord Extensions for Kord `0.4.x`. diff --git a/bots/about/comparison.mdx b/bots/about/comparison.mdx new file mode 100644 index 00000000..82e14a5b --- /dev/null +++ b/bots/about/comparison.mdx @@ -0,0 +1,8 @@ +--- +sidebar_position: 3 +sidebar_custom_props: + tags: + - wip +--- + +# Framework Comparison diff --git a/bots/about/data-collection.mdx b/bots/about/data-collection.mdx new file mode 100644 index 00000000..901ea996 --- /dev/null +++ b/bots/about/data-collection.mdx @@ -0,0 +1,167 @@ +--- +sidebar_position: 2 +sidebar_custom_props: + tags: + - v-2 +--- + +import Link from "@docusaurus/Link"; + +# Data Collection + +:::info + +If you'd like to keep track of changes to the data collection system, +please look at [this GitHub Discussion](https://github.com/orgs/Kord-Extensions/discussions/5). +Feel free to subscribe to the thread if you'd like to receive notifications. + +If you're curious about our statistics, feel +free to look at [the public dashboard](https://stats.kordex.dev/public/dashboard/4b0a4faa-45fb-45af-ab96-43cfbec11779). + +::: + +:::danger + +If you intend your bot to be run by other people, +**you are responsible for informing them about the data collection system!** + +The easiest way to do this is to link to this page, but the way you handle this is up to you. + +::: + +As of version 2, Kord Extensions includes a data collection feature. +Every 30 minutes, this feature collects some statistical data from your bots, +submitting it to an open-source server run and controlled by the Kord Extensions team. + +We intend this feature to provide the developers with guidance on where to focus their efforts, +as well as interesting statistical information, which +[we make available to everyone](https://stats.kordex.dev/public/dashboard/4b0a4faa-45fb-45af-ab96-43cfbec11779). + +:::tip Maintaining a fork? + +If you're maintaining a fork of Kord Extensions, +we'd appreciate it if you could update your fork to identify itself in our collected statistics. +To learn how to do this, +please [look at the data collector](https://github.com/Kord-Extensions/kord-extensions/blob/root/kord-extensions/src/main/kotlin/dev/kordex/core/datacollection/DataCollector.kt#L99). + +If the above link doesn't bring you to the correct line, search for `fork-name` in `DataCollector.kt`. + +::: + +## General Info + +We store collected data on a server located in Germany, owned by [Hetzner](https://www.hetzner.com/). +We keep this data secure through containerisation, network isolation, backup encryption, and other standard security +practices. + +We don't sell any collected data, and we only share it with the Kord developers. +The following people have direct access to the data via Metabase: + +- The Kord Extensions lead maintainer, [@gdude2002](https://github.com/gdude2002). +- One of Kord's core developers, [@DRSchlaubi](https://github.com/DRSchlaubi). + +We make graphs and summaries based on that data, without bot/team information, +[available to the public](https://stats.kordex.dev/public/dashboard/4b0a4faa-45fb-45af-ab96-43cfbec11779). + +We assign a UUID to all data submissions, which your KordEx bot will store via a storage unit. +While we don't believe the GDPR considers the data collected to be personal information, +we provide several mechanisms that allow you to use this UUID for data retrieval and removal: + +- By changing your data collection level to None (as explained below), your Kord Extensions bot will stop collecting + data and will use the UUID to delete any stored data from the data collection server. +- You can use the UUID may to directly access the data collection API. + We may decide to create a simple UI for this later. + - HTTP GET: `https://data.kordex.dev/data/:uuid` - retrieve the stored data for the given UUID. + - HTTP DELETE: `https://data.kordex.dev/data/:uuid` - delete the stored data for the given UUID. +- If all else fails or you have any questions, contact us [via Discord](https://discord.gg/nYzQWcjAmK), + or email our GDPR officer at `gdpr` **(at)** `kordex.dev`. + +The default data collection level is **Standard**. + +## Collection Levels + +KordEx provides several data collection levels. + +### None + +Setting this level will disable data collection. + +If you had a different level set previously, Kord Extensions will delete your data from the data collection server. + +### Minimal + +This level collects the following data: + +- The date of submission and the last updated date. +- Whether your bot is running in development mode. +- The versions of Kord and Kord Extensions your bot uses. +- The name of the fork you're using, if you're using a fork, and it has modified the data collector to add its name. + +This level also collects the following data if the bot was built using the KordEx Gradle plugin: + +- A list of first-party modules the bot is using. + +### Standard + +This level collects the data collected by the **Minimal** level, as well as the following: + +- The Discord ID and the name of your bot. +- The number of extensions and plugins your bot has loaded. +- The number of guilds your bot can see. +- A list of intents enabled by your bot. +- Total number of top-level chat commands registered. +- Total number of global application commands registered, split into message, slash, and user commands. +- Data about your bot's runtime environment: + - Current JVM and Kotlin versions. + +### Extra + +This level collects the data collected by the **Minimal** and **Standard** levels, as well as the following: + +- A list of event types your bot is handling. +- The names of the extensions and plugins your bot has loaded. +- Bot ownership information: + - **If the bot is owned by a team:** The ID and name of that team. + - **If the bot is owned by a single person:** No ownership data. +- Data about your bot's runtime environment: + - Total available memory. + - Physical CPU count, thread count, and processor speed. + +## Configuration + +### Storage + +:::tip Immutable environment? + +If you're working in a containerised environment, +or you otherwise absolutely can't store the required state, you can still participate: + +1. Generate a version 4 UUID, either directly using Java/Kotlin or via + [a website like this one](https://fusionauth.io/dev-tools/uuid-generator). +2. Set the `dataCollectionUUID` system property or the `DATA_COLLECTION_UUID` environmental variable to the UUID at + runtime. + +**Please ensure that each instance of your bot has a unique UUID!** + +::: + +Your bot needs to store some information about the data collection for it to function properly: + +- A UUID representing the bot's data on the data collection server. + +KordEx uses a storage unit to store this data, via the data adapter system. +By default, this means it'll store data in files within `./data/`, +but you can change that by supplying another data adapter, +such as [the first-party MongoDB data adapter](https://github.com/Kord-Extensions/kord-extensions/tree/root/modules/data/data-mongodb). + +### Level + +We provide several ways to configure your bot's data collection level. +We've listed them below in order, with earlier options overridden by later ones: + +- The preferred approach: + Set it via [the KordEx Gradle plugin (TODO)](#). +- Set the `DATA_COLLECTION` environmental variable to `extra`, `standard`, `minimal`, or `none`. +- Set the `dataCollection` system property to `extra`, `standard`, `minimal`, or `none`. +- Set the `dataCollectionMode` property in your bot's builder, as explained in + [the configuration section](/bots/config/general). diff --git a/bots/about/faq.mdx b/bots/about/faq.mdx new file mode 100644 index 00000000..f3b5d739 --- /dev/null +++ b/bots/about/faq.mdx @@ -0,0 +1,144 @@ +--- +sidebar_position: 4 + +sidebar_custom_props: + tags: + - wip +--- + +# Questions (FAQ) + +If you have a question, we might have answered it below — so do take a look! + +## Why don't message components work after my bot restarts? + +We designed the convenience functions available for working with components for short-lived interactions, +and they aren't appropriate for long-lived components. Some examples of long-lived components include: + +- A role picker in a welcome channel. +- Management buttons provided by a bot at the start of a new thread. +- Moderation action pickers/buttons on logged infractions. + +You need to manually define these components and write your own event handlers to respond to their interactions. +For more information, please check [the page on handling components yourself](/bots/components/messages-manual). + +## Why can't I respond to command/component interactions with a modal? + +Because Discord requires that your bot responds to all interactions within 15 seconds, Kord Extensions will respond to +all expected interactions immediately, with an acknowledgement or deferred acknowledgement as appropriate. +You must send a modal as the first response to an interaction, and you can't send it later, meaning we have limitations +on how you can define commands and components. + +### Potential Strategies + +You can pick from multiple implementation strategies, depending on what you need. +For full details on these, please see [the Modals page](/bots/components/modals). + +### Notes + +When working with modals, it is important to keep a few things in mind: + +- Discord does not tell your bot when a user closes a modal without submitting it, + and it may take **up to 15 minutes** before your bot stops waiting for it. +- Due to Discord limitations, a modal open for 15 minutes **may not be submitted**, + and your bot won't receive an event for this. +- The Unsafe module is undocumented, though I intend to fix this eventually. +- The Unsafe module doesn't include alternative component types, + as handling component interaction events manually isn't overly complex. + +## Should I worry about the GDPR when using the Sentry integration? + +Before answering this question, please note that **nobody working on Kord Extensions is a lawyer, +this is not legal advice, and you should review any code you have any doubts about.** + +You need to consider several things when it comes to the GDPR: + +1. Read [Sentry's official guide to GDPR compliance](https://sentry.io/trust/privacy/gdpr-best-practices/). +2. Use the `channel`, `guild`, `role` and `user` properties when adding data to breadcrumbs, exception and scope + captures, where possible. +3. If you can't use the above properties, prefix the keys you're adding to the `data`, `hints` and `tags` maps with + the relevant type followed by `::` or `.` - for example, `user::target.name` or `channel.name`. + - The supported types are `argument`, `channel`, `guild`, `role` and `user`. + - Keys prefixed like `type::` will have the type and `::` removed when submitted to Sentry, but keys prefixed like + `type.` will retain the type and `.`. +4. Configure your bot's Sentry extension appropriately, via the `defaultDataTypes {}` builder function. + This will allow you to prevent the previously mentioned types from being submitted to Sentry once provided as explained above. +5. Optionally add any relevant transformers via the `dataTypeTransformer {}` builder function in your bot's Sentry + extension configuration. + This allows you to conditionally filter data-types based on the contents of the given Sentry capture object. + +Ultimately, it is important to be aware of the data you're handling, storing, and submitting to third-party services. +While we've done our best to provide a useful and comprehensive API and configuration, +**it is critical to review all third-party extensions. +No Kord Extensions contributor can be held responsible for third-party code that provides data incorrectly or +otherwise results in data being submitted to Sentry unexpectedly.** + +For more information on how to configure the Sentry integration, [please see the documentation (TODO)](#). + +## Why does the KordEx Gradle plugin break dependency resolution? +**(Or, how do I specify a JVM target newer than 13?)** + +As explained [in the documentation](/bots/gradle-plugin), the KordEx Gradle plugin will, by default, +configure your project's JVM compatibility to match the JVM version KordEx is compatible with. +If you need your project to be compatible with a later JVM version, you'll need to configure the plugin to match. + +For example, if your project is compatible with JVM version 21, you'd do this: + +```kt +kordEx { + jvmTarget = 21 +} +``` + +## Why can't Gradle resolve the dependencies required by the KordEx plugin? + +Because the Gradle plugin requires a dependency present on the KordEx Maven repositories, +simply adding the plugin to your build script isn't enough. +You'll also need to set up a `pluginManagement` block in your `settings.gradle.kts` as follows: + +```kt +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + + maven("https://snapshots-repo.kordex.dev") + maven("https://releases-repo.kordex.dev") + } +} +``` + +## How do I get rid of the /about command, or the copyright subcommand? + +The short answer is: **you don't**. + +Kord Extensions uses +[the EUPL (European Union Public License)](https://interoperable-europe.ec.europa.eu/collection/eupl). +We decided on this licence as it allows for the appropriate amount of freedom, while adequately protecting the project +from exploitation — at least in the ways we feel are relevant. + +The EUPL requires that all distributions include an attribution block that provides a link to the project's source code. +To quote: +> The Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to the Licence +> and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the Licence +> with every copy of the Work he/she distributes or communicates. +> ... +> When distributing or communicating copies of the Work, the Licensee will provide a machine-readable copy of the +> Source Code or indicate a repository where this Source will be easily and freely available for as long as the +> Licensee continues to distribute or communicate the Work. + +This is the main reason the `/about copyright` command exists — to make it easy for your projects to follow the terms +of the EUPL. +While it is true you can follow the licence in your own way by meeting its terms with some other implementation, +we prefer that you avoid breaking the licence — accidentally or otherwise. +Because of this, we don't provide any built-in way to disable the command. + +We've tried to take the least invasive approach possible, and many options are available that allow you to customise +the command or add your own subcommands. +For more information, [see this page](/bots/config/about). + +Kord Extensions won't attempt to stop you from tampering with the relevant command or extension objects, +but this may change if we notice users committing licence violations through any such attempt. + +For more information on our understanding of the EUPL and what we believe you should be doing, please +[see this page](/about/licencing/eupl). diff --git a/bots/about/index.mdx b/bots/about/index.mdx new file mode 100644 index 00000000..26e46774 --- /dev/null +++ b/bots/about/index.mdx @@ -0,0 +1,118 @@ +--- +sidebar_position: 04 + +sidebar_custom_props: + divider_below: true +--- + +import HeaderWithTag from "/src/components/HeaderWithTag"; +import Link from "@docusaurus/Link"; + +# About the Framework + +The Kord Extensions Discord Bot framework ("KordEx" or "the Framework") is a relatively mature framework for writing +Discord bots in Kotlin. +It builds upon [the excellent Kord protocol and caching library](https://kord.dev/), taking a bells-and-whistles +approach to supporting your bots. + +KordEx takes a somewhat different approach when compared to other Kotlin-based Discord bot frameworks, focusing on +modular and encapsulated bot functionality rather than relying entirely on a set of lightweight DSLs. +This approach means that KordEx can provide many deeply integrated features and utilities while still providing an +idiomatic API that takes advantage of Kotlin's niceties. + +:::info + +While we prioritise the developer experience and believe KordEx is a great match for most projects, no framework +can perfectly meet the needs of every potential project. +For that reason, +we've created a framework comparison page. +Feel free to take a look if you're curious what other options you have! + +::: + +## Version History + +KordEx has gone through multiple iterations and development cycles. +We like to bump version numbers when we break an API or swap to a new development focus, reserving major version numbers +for serious changes and refactors. + + + +### Version 2 + + + +Version 2 is the latest version of KordEx, and it includes many changes, some of which we've detailed below. + +- The root package for all projects is no longer `com.kotlindiscord.kord.extensions`. + We changed it to `dev.kordex` to match our domain name. + - **Core package:** `dev.kordex.core` + - **Modules package:** `dev.kordex.modules` + - **Tokenizing parser package:** `dev.kordex.parser` + +- We re-licensed KordEx under + [the EUPL, version 1.2](https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12), + with the specific provision (EUPL articles 14 & 15) that the applicable law is the (Republic of) Irish law and the + Jurisdiction is Dublin. + + For more information on this licence, how we interpret it, + and what it means for your project, see + the licence information page. + +- Due to [upcoming changes that are part of the new Maven Central platform](https://central.sonatype.org/faq/snapshot-releases/#question), + we no longer publish KordEx to Maven Central or OSSRH. + Instead, you should add the following repository URLs: + - **Releases:** `https://releases-repo.kordex.dev` + - **Snapshots:** `https://snapshots-repo.kordex.dev` + +- Version 2 includes a new data collection system, + intended to guide development focus and provide interesting statistics for the community. + + If you worry about (or dislike) data collection in general, + you can configure this system may in multiple ways and easily disable it. + + Our [public statistics dashboard](https://stats.kordex.dev/public/dashboard/4b0a4faa-45fb-45af-ab96-43cfbec11779) + contains aggregated statistics from the data collected. + +- We reorganised and renamed the first-party KordEx modules to make it easier to figure out their coordinates + and generally keep the Git repository cleaner. + For more information, + see [the README in the modules folder](https://github.com/Kord-Extensions/kord-extensions/blob/root/modules/README.md). + +- We moved translations [to their own repository](https://github.com/Kord-Extensions/translations), still licenced + under the MPL 2.0. + +- The contribution guidelines have been rewritten and updated. + We'll continue to update the guidelines when other questions arise. + +- KordEx 2 has a greater emphasis on the plugin system, which we'll work on and document more over the coming + days. + +- Lots of plans are in motion around this system, so keep an eye out for announcements! + +- We updated [the Unsafe Module (TODO)](#) to support [Components](/bots/components) and [Modals](/bots/commands/modals). + +- We now include [a default about command](/bots/config/about) — to help you meet the EUPL's licensing requirements + and provide a standard way for your bots to provide information about themselves. + +- We've redesigned and rewritten [the i18n system (TODO)](#) with a cleaner, more type-safe, and feature-packed API. + +- We now ship [a Web module (TODO)](#) and [corresponding health-check APIs (TODO)](#). + +As this is the current KordEx development cycle, we're adding new features all the time. +If we forget to add something to this list, please let us know! + +### Version 1 + +The releases from KordEx's first development cycle are unsupported and likely don't work with Discord any more. +However, if you need to use it for any reason, you can find it in the same Maven repository as version 2. + +You may also continue to use [the Gradle plugin (TODO)](#) — just be sure to specify the KordEx version you need. + +- **Coordinate:** `com.kotlindiscord.kord.extensions:kord-extensions` +- **Last Version:** `1.9.0-SNAPSHOT` +- **Kord Version:** `0.15.0-SNAPSHOT` + +We licenced version 1 under the MPL, version 2.0. +The code will always be available +[under the v1 branch on GitHub](https://github.com/Kord-Extensions/kord-extensions/tree/v1). diff --git a/bots/anatomy/behaviours.mdx b/bots/anatomy/behaviours.mdx new file mode 100644 index 00000000..9974550e --- /dev/null +++ b/bots/anatomy/behaviours.mdx @@ -0,0 +1,13 @@ +--- +description: "Information on Kord's entity structure." +sidebar_position: 3 +sidebar_custom_props: + card_group: "Kord" + icon: "fluent:thinking-24-filled" + tags: + - wip +--- + +import Link from "@docusaurus/Link"; + +# Behaviours diff --git a/bots/anatomy/general.mdx b/bots/anatomy/general.mdx new file mode 100644 index 00000000..b9818702 --- /dev/null +++ b/bots/anatomy/general.mdx @@ -0,0 +1,175 @@ +--- +description: "How Kord Extensions structures things." +sidebar_position: 1 +sidebar_custom_props: + card_group: "Basics" + icon: "fluent:settings-cog-multiple-24-filled" +--- + +import Link from "@docusaurus/Link"; + +# General + +Before getting started, it is important to understand how you should structure your bot. +Kord Extensions is an opinionated framework, +and designing to the expected structure will help you keep things organised. + +![Typical bot structure diagram](/img/kordex-structure.png) + +## Configuration + +The first thing you'll need to figure out is your bot's configuration. +Start by creating an `ExtensibleBot` object using the provided builder: + +```kotlin +val bot = ExtensibleBot(env("TOKEN")) { + // ... +} +``` + +For more information on this builder and the settings you can configure, please read +the configuration section. + +## Starting + +Next, consider how you want the bot to be started: +- If you're creating the bot in a suspending function and want it to block the coroutine, call `bot.start()`. +- If you're not in a suspending function, or you want the bot to run asynchronously, you can launch the bot within + Kord's coroutine scope using `bot.startAsync()`. + +```kt +/** +* Block the suspending function's execution. +* The function won't return until the bot dies. +*/ +suspend fun main() { + val bot = ExtensibleBot(env("TOKEN")) { + // ... + } + + bot.start() +} + +/** +* Launch the bot in Kord's coroutine scope. +* The function will return immediately. +*/ +fun startBot() { + val bot = ExtensibleBot(env("TOKEN")) { + // ... + } + + bot.startAsync() +} +``` + +## Adding Functionality + +While some other Kotlin Discord bot frameworks provide a lightweight DSL, Kord Extensions requires that you split +your bot's functionality into distinct units. +We call these **Extensions**, and they are the core building blocks for your bots. + +However, as you'll see, this isn't the only unit available to you. + +### Extensions + +Extensions exist as units that group similar functionality together. +They contain commands, command arguments, event handlers, and other things — and these should relate to each other. + +For example, let's say your bot needs to have some moderation features, and you'd also like it to be able to notify +a channel when your favourite streamer goes live. +We recommend splitting this functionality into two extensions, as follows: + +#### ModerationExtension + +This extension would contain all moderation-related commands, event handlers, and other features. + +- Commands: + - `/ban` + - `/kick` + - `/mute` +- Event Handlers: + - `MemberJoinEvent` + - `MemberLeaveEvent` + - `MemberUpdateEvent` + - `MessageCreateEvent` + - `MessageDeleteEvent` + - `MessageUpdateEvent` +- Scheduled Jobs: + - `expireMutesJob` + +#### StreamExtension + +This extension would contain all stream-related commands, event handlers, and other features. + +- Commands: + - `/stream` + - `subscribe` + - `unsubscribe` +- Scheduled Jobs: + - `streamCheckJob` + +--- + +For more information on writing your own extensions, see [the Extensions page](/bots/extensions) + +### Plugins + +[//]: # (TODO: Page explaining plugins) + +Plugins are a higher-level encapsulation unit, specifically designed to allow you to distribute extensions for other +people to use. +These are `.jar` or `.zip` files that users can place in their `plugins/` directory, to be automatically loaded by +their bots. + +Plugins should avoid making assumptions about the bot they're running under and should use Kord Extensions' various +abstracted APIs for things like data storage. + +For example, let's say you want to design a plugin containing various Minecraft-related extensions, including notifying +channels about new Minecraft versions, and allowing modders to look up function names in various mappings sets: + +#### MinecraftPlugin + +##### MinecraftMappingsExtension + +- Commands: + - `/class` + - `/method` + - `/property` +- Scheduled Jobs: +- `cleanupMappingsJob` + +##### MinecraftVersionExtension + +- Commands: + - `/minecraft` + - `lookup-version` + - `subscribe` + - `unsubscribe` +- Scheduled Jobs: + - `newVersionJob` + +## Packaging & Distribution + +:::warning + +We strongly recommend against using [the popular Gradle Shadow plugin](https://gradleup.com/shadow/), +as its recent new developers appear to be [vibe coders](https://en.wikipedia.org/wiki/Vibe_coding), +and we expect a future tainted by +[slopsquatting](https://www.theregister.com/2025/04/12/ai_code_suggestions_sabotage_supply_chain/), +and other bugs and security issues. + +Instead, if you can, please stick with +[Gradle's Distribution plugin](https://docs.gradle.org/current/userguide/distribution_plugin.html), +or the equivalent for your build system. + +::: + +You should package your Kord Extensions bot in a `.tar`- or `.zip`-based distribution bundle, +like the bundles generated by the +[Gradle Distribution plugin](https://docs.gradle.org/current/userguide/distribution_plugin.html). +These bundles contain a `bin/` directory with easy launch scripts, and a `lib/` directory containing your bot's +JAR alongside the JARs containing its dependencies. + +If you're using Docker, you can copy the distribution bundle into your image, extract it, and launch your bot via +checking it for hints if you need help. diff --git a/bots/anatomy/index.mdx b/bots/anatomy/index.mdx new file mode 100644 index 00000000..4077468f --- /dev/null +++ b/bots/anatomy/index.mdx @@ -0,0 +1,15 @@ +--- +sidebar_position: 11 +--- + +import DocCardList from '@theme/DocCardList'; +import { DocCardListWithDescriptions } from "/src/theme/DocCardList"; + +# Anatomy + +Learn the basics of how Discord bots work and how to structure yours. + + diff --git a/bots/anatomy/interactions.mdx b/bots/anatomy/interactions.mdx new file mode 100644 index 00000000..fe8751a4 --- /dev/null +++ b/bots/anatomy/interactions.mdx @@ -0,0 +1,186 @@ +--- +description: "Learn about the rich interactions system." +sidebar_position: 2 +sidebar_custom_props: + card_group: "Basics" + divider_below: true + icon: "fluent:sparkle-action-24-filled" + +# TODO: Move some properties/functions to other pages +--- + +import Link from "@docusaurus/Link"; +import * as Doc from "/src/components/Doc"; + +# Interactions + +To give users richer ways to interact with your bot (when compared to sending messages), Discord provides a system +known as [Interactions](https://discord.com/developers/docs/interactions/receiving-and-responding). +Interactions allow for public and private responses to rich Interaction types. + +Interactions are an important concept to understand, as they will make up the bulk of user-triggered actions that your +bot needs to deal with. + +## Interactions 101 + +Your bot receives an Interaction in response to one of the following actions taken on Discord: + +- A user executes an [application command](/bots/commands/application/) belonging to your bot. +- A user enters data into a slash [command argument](/bots/commands/arguments) that supports auto-completion. +- A user uses a [message component](/bots/components/messages-dsl), such as an interactive button or a select menu. +- A user submits a [modal form](/bots/commands/modals). + +When your bot receives an Interaction from Discord, Kord will fire an event that extends +[`InteractionCreateEvent`](https://dokka.kord.dev/core/dev.kord.core.event.interaction/-interaction-create-event/index.html). +These events provide an +[`interaction`](https://dokka.kord.dev/core/dev.kord.core.entity.interaction/-interaction/index.html) property, +letting your bot respond directly to the Interaction. + +:::note + +While it will sometimes be necessary to respond to these events directly, most bots will only need to use +Kord Extensions' built-in abstractions. +For more information on those abstractions, see the list above. + +::: + +When responding to an Interaction, your response may be one of the following main types: + +- Ephemeral, meaning only the user that triggered the Interaction can see the response. +- Public, meaning everyone with access to the current channel can see the response, along with the user that +triggered the Interaction and how they triggered it. + +## Unexpected Behaviours + +Internally, Discord's Interaction system is a bit of a mess. +While it is technically possible to work with the Interaction API directly via the objects exposed by Kord Extensions +(especially via [the Unsafe Module (TODO)](#)), +we recommend you use the core abstractions to avoid unexpected behaviour. + +Known instances of unexpected behaviour include: + +- Responding to an Interaction using the wrong response type, after you make an initial response. +Even though the API technically allows you to respond using mixed response types, +Discord will often ignore the type you provided if you aren't consistent. +- Responding to an ephemeral Interaction after editing the initial response, +which may result in a public response in some situations. + +Additionally, as the Interaction system internally uses Discord's webhook system, +changes that Discord makes to how webhook messages work may also affect your Interactions. + +:::info Example + +For example, a Discord made a change in 2023 because slash command responses could mention `@everyone` when they +otherwise shouldn't be able to. +In this instance, Discord normalised webhook permissions to match those of the user who created the webhook. +This fixed the problem with slash commands but limited the emoji that webhooks could send to those from guilds the +webhook creator is present on. + +::: + +## Context Types + +Discord supports two main types of Interaction response — ephemeral and public. +Ephemeral responses are only visible to the user that triggered the Interaction, +whereas public responses are visible to everyone that has access to the current channel. + +Because Discord's Interactions can behave strangely if you don't use them how Discord expects, +Kord Extensions provides specific context types that help you to avoid unexpected behaviours. +These types expect you to work with one of the main Interaction types, +warning you when you attempt to do something that might not do what you expect. + +These context types inherit the `InteractionContext` type, and are split by Interaction response types: + +- `EphemeralInteractionContext` for ephemeral Interaction responses. +- `PublicInteractionContext` for public Interaction responses. + +These types provide an identical API surface, +allowing you to respond to Interactions easily without accidentally specifying the wrong type of response. + +### Base Type + + + + Interface representing an interaction context, exposing generic APIs to respond to the interaction. + + + + Generic representing the relevant interaction response behaviour. + + + + Generic representing the response type for the current interaction type. + + + + Generic representing the follow-up type for the current interaction type. + + + + Generic representing the opposite follow-up type for the current interaction type. + + + + + +### Builders + + + + Edit the first Interaction response, regardless of whether (or how many times) your bot has already responded. + May be called before a response has been sent, as the first Interaction response is always a "bot is thinking" + message until this function (or one of the response functions) is called. + + + + Respond to the Interaction with a follow-up matching the current Interaction response type. + + + + Respond to the Interaction with a follow-up **with the opposite type to the current Interaction response type**. + While Discord's API allows you to do this, **it will rarely do what you expect**. + This function is provided only for advanced use-cases that need it. + + +
    +
    + + + Convenience function allowing you to easily create a button-based paginator by editing the first response to this + Interaction. + + For more information, see [the paginator documentation (TODO)](#). + + + + The default paginator group to use for pages. + + + + Locale to use for paginator translations. + Usually, this should be the locale resolved by the current interaction event. + + + + + + Convenience function allowing you to easily create a **public** button-based paginator by responding with a + follow-up. + + Discord makes it impossible to edit ephemeral follow-ups, so **this function always sends a public paginator.** + If you're in an ephemeral Interaction context and need an ephemeral paginator, use `editingPaginator` instead. + + For more information, see [the paginator documentation (TODO)](#). + + + + The default paginator group to use for pages. + + + + Locale to use for paginator translations. + Usually, this should be the locale resolved by the current interaction event. + + + +
    diff --git a/bots/checks/basics.mdx b/bots/checks/basics.mdx new file mode 100644 index 00000000..3db013f6 --- /dev/null +++ b/bots/checks/basics.mdx @@ -0,0 +1,48 @@ +--- +description: "Learn how the checks system works, and how to get started." +sidebar_position: 1 +sidebar_custom_props: + card_group: "Basics" + icon: "fluent:new-24-filled" +--- + +import Link from "@docusaurus/Link"; + +# Basics + +Checks are re-usable, generic predicates that you can apply to many Kord Extensions APIs, including: + +- [Argument validators](/bots/commands/converters/basics#shared-settings) +- [Commands](/bots/commands/) +- [Components](/bots/components/) +- [Event handlers](/bots/events/) + +These predicates prevent code from running unless they pass. +In a sense, they act like re-usable conditionals that you can use in any context triggered by an event. + +## Usage + +All check builders are receivers against the `CheckContext` or `ValidationContext` types. +These types provide an API you can [learn more about here](/bots/checks/), +but the extension functions that represent individual checks also use these types as a receiver. +You apply a check by calling the relevant builders and functions in your check builders: + +```kt +check { + // AND structure — all check functions must pass. + hasPermission(Permission.Administrator) + inGuild(CONTROL_GUILD) +} + +check { + // OR structure — either check function must pass. + hasPermission(Permission.Administrator) + + or { + hasRole(MOD_ROLE_ID) + } +} +``` + +All check contexts default to a "passing" state, which check functions can update. +They also support failure messages, which your bot will send to users if the check fails. diff --git a/bots/checks/bundled.mdx b/bots/checks/bundled.mdx new file mode 100644 index 00000000..be42443c --- /dev/null +++ b/bots/checks/bundled.mdx @@ -0,0 +1,400 @@ +--- +description: "All checks included with Kord Extensions." +sidebar_position: 2 +sidebar_custom_props: + card_group: "Basics" + icon: "fluent:code-block-24-filled" + divider_below: true +--- + +import * as Doc from "/src/components/Doc"; + +# Bundled Checks + +Kord Extensions provides many built-in checks that you can use in all kinds of situations. +To use them, call their functions as described on [the basics page](/bots/checks/basics). + +## Channels + + + + Checks whether the current event happened in a channel ordered **above** the given channel in the channel list. + If the current event happened in a thread or the given channel is a thread, this check will use the threads' + parent channels. + + **Note:** This check doesn't make sure both channels are in the same guild. + + + ChannelBehavior"]}> + Channel to check against, either in ID or builder form. + + + + + Always fails. + + + + + Checks whether the current event happened in a channel ordered **below** the given channel in the channel list. + If the current event happened in a thread or the given channel is a thread, this check will use the threads' + parent channels. + + **Note:** This check doesn't make sure both channels are in the same guild. + + + ChannelBehavior"]}> + Channel to check against, either in ID or builder form. + + + + + Always fails. + + + + + Checks whether the current event happened in a channel within the given category. + If the current event happened in a thread, this check will use the thread's parent channel. + + + CategoryBehavior"]}> + Category to check against, either in ID or builder form. + + + + + Always fails. + Always passes. + + + + + Checks whether the current event happened in the given channel. + If the current event happened in a thread, this check will use the thread's parent channel. + + + ChannelBehavior"]}> + Channel to check against, either in ID or builder form. + + + + + Always fails. + Always passes. + + + + +### Channel Types + + + + Checks whether the current event happened in a channel matching the given channel types. + + + + Channel types to check. + + + + + Always fails. + Always passes. + + + + +### Flat Checks + +These checks don't resolve the parent channel if the event happened in a thread, which can be useful if you need to +match a specific thread. + + + + Checks whether the current event happened in the given channel. + + **Note:** If the current event happened in a thread, this check won't use the thread's parent channel. + If you need this, use `inTopChannel` or `notInTopChannel`. + + + ChannelBehavior"]}> + Channel to check against, either in ID or builder form. + + + + + Always fails. + Always passes. + + + + +## Guilds + + + + Checks whether the current event happened in a guild. + + + Always fails. + Always passes. + + + + + Checks whether the current event happened in the given guild. + + + GuildBehavior"]}> + Guild to check against, either in ID or builder form. + + + + + Always fails. + Always passes. + + + + +## Members + + + + Checks whether the member triggering the current event has either the given permission, or the admin permission. + + + + Permission to check for. + + + + + Always fails. + Always passes. + + + + + Checks whether the member triggering the current event has either the given permissions set, or the admin + permission. + + + + Permissions to check for. + + + + + Always fails. + Always passes. + + + + +## Misc + + + + Checks whether the user triggering the current event is the bot's sole owner. + + + Fails if the bot is owned by a team. + Passes if the bot is owned by a team. + + + + Always fails. + Always passes. + + + + + Checks whether the user triggering the current event belongs to the bot's owning team. + + + Fails if the bot isn't owned by a team, or the team has no admins. + Passes if the bot isn't owned by a team, or the team has no admins. + + + + Always fails. + Always passes. + + + + + Checks whether the user triggering the current event is a bot. + + + Always fails. + Always passes. + + + + + Checks whether the current event happened in a thread. + + + Always fails. + Always passes. + + + + +## NSFW + +These checks use [Kord's NsfwLevel type](https://dokka.kord.dev/common/dev.kord.common.entity/-nsfw-level/index.html), +which Kord Extensions [customises to allow comparisons and translations (TODO)](#). + + + + Checks whether the current event happened in a guild with the given NSFW level. + + + Always fails. + Always passes. + + + + + Checks whether the current event happened in a guild with an NSFW level higher than the one provided. + + + Always fails. + + + + + Checks whether the current event happened in a guild with an NSFW level higher than or equal to the one provided. + + + Always fails. + + + + + Checks whether the current event happened in a guild with an NSFW level lower than the one provided. + + + Always fails. + + + + + Checks whether the current event happened in a guild with an NSFW level lower than or equal to the one provided. + + + Always fails. + + + + + Checks whether the current event happened in an NSFW channel. + + + Always fails. + Always passes. + + + + + Checks whether the current event happened in an NSFW channel or guild. + + + Combines `channelIsNsfw()` and `guildNsfwLevelHigherOrEqual(NsfwLevel.AgeRestricted)`. + Combines `notChannelIsNsfw()` and `guildNsfwLevelLower(NsfwLevel.AgeRestricted)`. + + + + +## Roles + + + + Checks whether the user triggering the current event has the given role. + + + RoleBehavior"]}> + Role to check against, either in ID or builder form. + + + + + Always fails. + Always passes. + + + + + Checks whether the user triggering the current event has a top role matching the given role. + + + RoleBehavior"]}> + Role to check against, either in ID or builder form. + + + + + Always fails. + Always passes. + + + + + Checks whether the user triggering the current event has a top role higher than the given role. + + + RoleBehavior"]}> + Role to check against, either in ID or builder form. + + + + + Always fails. + + + + + Checks whether the user triggering the current event has a top role higher than or equal to the given role. + + + RoleBehavior"]}> + Role to check against, either in ID or builder form. + + + + + Always fails. + + + + + Checks whether the user triggering the current event has a top role lower than the given role. + + + RoleBehavior"]}> + Role to check against, either in ID or builder form. + + + + + Always fails. + + + + + Checks whether the user triggering the current event has a top role lower than or equal to the given role. + + + RoleBehavior"]}> + Role to check against, either in ID or builder form. + + + + + Always fails. + + + diff --git a/bots/checks/custom.mdx b/bots/checks/custom.mdx new file mode 100644 index 00000000..5b952f1a --- /dev/null +++ b/bots/checks/custom.mdx @@ -0,0 +1,326 @@ +--- +description: "Learn how to write your own re-usable checks." +sidebar_position: 3 +sidebar_custom_props: + card_group: "Customisation" + icon: "fluent:code-block-edit-24-filled" +--- + +import * as Doc from "/src/components/Doc"; + +# Custom Checks + +You can create your own custom checks in one of several ways, using the [context API](#context-api) +and [check utilities](/bots/checks/utilities). + +## Writing Checks + +### Inline + +If you don't need to re-use a check, you can define it inline in your `check {}` or `validate {}` builder. + +- `check {}` builders use a `CheckContext` receiver. +- `validate {}` builders use a `ValidationContext` receiver, which extends `CheckContext` with some extra APIs. + +```kt +check { + val channel = guildFor(event) + + if (channel == null) { + fail(CoreTranslations.Checks.AnyGuild.failed) + } +} +``` + +### Re-usable + +If you need a re-usable check, define an extension function against the `CheckContext` type. +Checks written this way should be more in-depth and include logging. + +```kt +suspend fun CheckContext<*>.anyGuild() { + if (!passed) { + return // If the check context is already failing, we can skip this check. + } + + // Create a logger with the containing package and function name. + val logger = KotlinLogging.logger("dev.kordex.core.checks.anyGuild") + + if (guildFor(event) != null) { + logger.passed() // Logging a pass is as important as logging a failure! + + pass() + } else { + logger.failed("Event did not happen within a guild.") + + fail(CoreTranslations.Checks.AnyGuild.failed) + } +} +``` + +## Context API + +All `check {}` builders use `CheckContext` as their receiver type. + + + + Check context type, containing everything you need to write your own checks. + + + + Generic type representing the event being checked. + + + + + + Event object to be checked. + + + + Locale for the current event, retrieved via your bot's [locale resolvers](/bots/config/i18n). + + + + + +### Builders + + + + Fail this check if the provided lambda returns `false`. + Calls `fail()` internally. + + Returns `true` if the check is now failing, `false` otherwise. + + + + + Message to respond with explaining why the check failed. + + + + + + + Fail this check if the provided lambda returns `true`. + Calls `fail()` internally. + + Returns `true` if the check is now failing. + + + + + Message to respond with explaining why the check failed. + + + + + +
    +
    + + + Passes this check if the provided lambda returns `true`. + Calls `pass()` internally. + + Returns `true` if the check is now passing. + + + + Passes this check if the provided lambda returns `false`. + Calls `pass()` internally. + + Returns `true` if the check is now passing. + + +
    +
    + + + Calls the provided lambda only if this boolean is `false`, returning whatever the lambda does. + + If this boolean is `true`, simply returns `null`. + + + + Calls the provided lambda only if this boolean is `true`, returning whatever the lambda does. + + If this boolean is `false`, simply returns `null`. + +
    + +### Functions + + + + Fails this check, setting `passed` to `true`. + + + + Message to respond with explaining why the check failed. + + + + + + Fails this check if the provided boolean is `true`. + Calls `fail()` internally. + + Returns `true` if the check is now failing. + + + + Boolean value to check. + + + + Message to respond with explaining why the check failed. + + + + + + Fails this check if the provided boolean is `false`. + Calls `fail()` internally. + + Returns `true` if the check is now failing. + + + + Boolean value to check. + + + + Message to respond with explaining why the check failed. + + + + +
    +
    + + + Passes this check, setting `passed` to `true`. + + + + Passes this check if the provided boolean is `true`. + Calls `pass()` internally. + + Returns `true` if the check is now passing. + + + + Boolean value to check. + + + + + + Passes this check if the provided boolean is `false`. + Calls `pass()` internally. + + Returns `true` if the check is now passing. + + + + Boolean value to check. + + + +
    + + + + Get a pre-translation `Key` representing the current failure message, provided the check is failing and a message + was set. + + Otherwise, this function returns `null`. + + + + Returns a translated string based on the value returned by `getMessageKey()`, returning `null` in the same + situations. + + + + Throws a [`DiscordRelayedException`](/bots/exceptions#discord-relayed) with the value returned by `getMessageKey()`, + if that value isn't `null`. + + + +### Properties + + + + Translation key used to construct an error message to display on Discord. + + You can replace this with your own key if you need to. + Custom keys must support one ordinal placeholder, `{0}`, containing the error message. + + + + Error message to respond with. + + Usually set via the various `fail` builders and functions, and not used if `passed` is `true`. + + + + Whether this check is currently passing, which is also the default state. + + Usually set via the various `pass` and `fail` builders and functions. + + + +### Validation Contexts + +All `validate {}` builders use `ValidationContext` as their receiver type. +This type provides [the same `CheckContext API surface`](#context-api), with some extras. + + + + Validation context type, containing everything you need to write your own validators. + + + + Generic type representing the type of the command argument's value. + + + + + + Converter used to generate the current argument's value. + + + + The current argument's value. + + + + The [command context](/bots/commands/base-context) representing the command invocation triggering this + validator. + + + + Locale for the current event, retrieved via your bot's [locale resolvers](/bots/config/i18n). + + + + + + + + Get a pre-translation `Key` representing the current failure message, provided the validator is failing. + + If `message` is `null`, this returns a default error message. + Returns `null` if the validator is passing. + + + + If this validator is failing, throws a [`DiscordRelayedException`](/bots/exceptions#discord-relayed) with the value + returned by `getMessageKey()`,. + + If the value returned by `getMessageKey()` is `null`, this function throws an `IllegalStateException` instead. + + diff --git a/bots/checks/index.mdx b/bots/checks/index.mdx new file mode 100644 index 00000000..6c5e85c2 --- /dev/null +++ b/bots/checks/index.mdx @@ -0,0 +1,17 @@ +--- +sidebar_position: 35 +--- + +import { DocCardListWithDescriptions } from "/src/theme/DocCardList"; + +# Checks + +Checks are a system of modular predicates, allowing you to write re-usable conditions that must pass for things to +happen. + +For more information, see the pages below. + + diff --git a/bots/checks/utilities.mdx b/bots/checks/utilities.mdx new file mode 100644 index 00000000..8219d131 --- /dev/null +++ b/bots/checks/utilities.mdx @@ -0,0 +1,180 @@ +--- +description: "Learn about utilities that make writing checks easier." +sidebar_position: 4 +sidebar_custom_props: + card_group: "Customisation" + icon: "fluent:code-text-edit-20-filled" +--- + +import * as Doc from "/src/components/Doc"; + +# Utilities + +Kord Extensions provides many utilities that can make writing checks easier and simpler. + +## Event Entities + +You can use the following functions to retrieve entities contained within any generic event, which makes it much easier +to write all kinds of code. +These functions are essentially giant `when {}` blocks, checking against all possible basic event types. + +If you're writing [custom events](/bots/events/custom), +remember to extend [the relevant generic event types](/bots/events/custom#writing-events) +to make sure these functions work with your event. + +All the below functions take one argument — the event to retrieve an entity from. +They all return `null` when the event doesn't contain the relevant entity type, +and try to avoid making any REST requests. + + + + **This function may make REST requests for `ThreadMemberUpdateEvent` events if the current thread isn't in the + cache!** + + + + **Note:** This function can't retrieve a guild for `GuildAuditLogEntryCreateEvent` events, due to an oversight in + how both Kord and Discord treat this event. + + **This function may make REST requests for `ThreadMemberUpdateEvent` events if the current thread isn't in the + cache!** + + + + + + + + + **Note:** For `ThreadChannelCreateEvent` events, this function retrieves the last message in the thread. + + **This function may make REST requests for `MessageUpdateEvent` events if the current message isn't in the cache, + or for `ThreadChannelCreateEvent` events if the current thread isn't in the cache!** + + + + + + **Note:** This uses `channelFor()`, returning `null` if the current channel isn't a thread. + + + + **Note:** This uses `channelFor()`, resolving to the parent channel if the current one is a thread. + + **This function may make REST requests if the current channel isn't in the cache!** + + + + **Note:** Bots can't be in group DMs, so this always uses the first recipient in all DM-specific events. + + **This function may make REST requests for `MessageUpdateEvent` events if the current message isn't in the cache!** + + + +## Logging + +The below functions are extension functions against the [Kotlin Logging](https://github.com/oshai/kotlin-logging) +`KLogger` type. + + + + Log that the current check failed, with the given reason. + + + + Reason the check failed. + + + + + + Log that the current check passed, optionally with the given reason. + + + + Reason the check passed. + You can omit this if needed. + + + + +
    +
    + + + Log that the check failed because a category with the given doesn't exist. + + + + Category ID. + + + + + + Log that the check failed because a channel with the given ID doesn't exist. + + + + Channel ID. + + + + + + Log that the check failed because a guild with the given ID doesn't exist. + + + + Guild ID. + + + + + + Log that the check failed because a role with the given ID doesn't exist. + + + + Role ID. + + + + + + Log that the channel for the current event is `null`, and that it may not be supported by this check. + + + + Log that the guild for the current event is `null`, and that it may not be supported by this check. + + + + Log that the member for the current event is `null`, and that it may not be supported by this check. + + + + Log that the message for the current event is `null`, and that it may not be supported by this check. + +
    + +## Misc + +The below functions can make combining checks easier. + + + + Acts as an "or" conditional, allowing the current check context to pass if the checks defined in this builder pass, + even if the current check context is failing. + + If the current check context is still failing after the function runs the provided lambda, it will combine their + failure messages with a pipe character (`|`), assuming both failures generated a message. + If the current check context doesn't have a message set but the provided lambda sets one, it will use that instead. + + If the current check context is passing, this builder does nothing. + + + + If the current check has a `message` set, clear it. + + diff --git a/bots/commands/application/_category_.yml b/bots/commands/application/_category_.yml new file mode 100644 index 00000000..d9b4f076 --- /dev/null +++ b/bots/commands/application/_category_.yml @@ -0,0 +1,5 @@ +description: "Rich command types, including slash and context commands." + +customProps: + card_group: "Commands" + icon: "fluent:apps-24-filled" diff --git a/bots/commands/application/base-command.mdx b/bots/commands/application/base-command.mdx new file mode 100644 index 00000000..25ed5af4 --- /dev/null +++ b/bots/commands/application/base-command.mdx @@ -0,0 +1,192 @@ +--- +description: 'Basic APIs shared by all application command types.' +sidebar_position: 1 + +sidebar_custom_props: + card_group: "Base APIs" + icon: "fluent:code-block-edit-24-filled" +--- + +import * as Doc from "/src/components/Doc"; + +# Basic Command APIs + +All application command types extend the `ApplicationCommand` type, which provides some basic common APIs. +These APIs are available regardless of command type, so you should familiarise yourself with them! + +The `ApplicationCommand` type extends the base `Command` type, so it inherits the APIs defined there. +For more information on the APIs common to all commands, see [the basic command API page](/bots/commands/base-command). + + + + Abstract base type representing everything that all application commands have in common. + + + + Base type representing the events that can trigger application command invocations. + + + + + + The extension that registered this command. + + + + + +## Builders + + + + Register a [check](/bots/checks), which must pass for the command to execute when invoked. + + + +## Functions + + + + Provide a guild ID to register this command against, or `null` to override any previously-set guild. + + By default, all application commands are global. + This can be changed in your bot's [application command builder](/bots/config/commands#application-commands), + by calling the `defaultGuild()` function. + + + + Provide one or more default required permissions that users must have to be able to run this command. + + **Note:** Kord Extensions doesn't enforce the permissions you specify here. + This is because server staff on Discord can change the permissions required to execute a command. + + If you need to limit command usage to a specific role or set of permissions, consider also writing + [a check](/bots/checks) when you set this. + If you write a check, you should also set this property - it'll hide the command on Discord! + + + + + + Command logic function implemented by the application command sub-types, and called via the `doCall` function. + + This function is responsible for a command's logical flow. + It should run the command's defined checks, respond to the Discord interaction, create the correct command context + object, and ultimately execute your command's `action {}` body. + + + + Event triggering this command execution. + + + + Map representing a data cache shared with the command's registered checks, and made available in the command + context. + + + + + + Attempt to execute this command based on the provided event object. + This function creates a cache map, and passes it to the `call` function along with the event object. + + This is normally called by the `ApplicationCommandRegistry` when someone invokes an application command on Discord. + + + + Translates the given translation key object into your bot's default locale, and all configured application command + locales, lower-casing it if required. + + This is used by the `ApplicationCommandRegistry` to figure out what names and descriptions your commands should use + in each configured locale, and it's also called by the `localisedName` lazy property when first accessed. + + + + Translation key to localise. + + + + Whether the resulting strings should be lower-cased. + + This is generally only used when translating slash command names, which must always be lower-case. + + + + + + By default, this function simply returns the result of the `runStandardChecks()` function, but it can be overridden + if command types need to extend this behaviour. + + + + Runs the checks defined in `checkList` (via the `check` builders and functions), throwing and interrupting command + execution if needed. + + + +## Properties + + + + **We recommend using the `allow...()` functions defined on each command type instead of manually setting this + property, to keep things simple.** + + Whether to allow everyone on a guild to use this command by default. + This property is represented by a set of getters and setters, and has no backing field of its own. + + Setting this to `true` will set `defaultMemberPermissions` to `null` automatically. + Setting it to `false` will set `defaultMemberPermissions` to an empty `Permissions` object instead. + + This property will return `true` if `defaultMemberPermissions` is `null`. + + + + Whether this command should be allowed in DMs. + + This will always return `false` if your command is restricted to a specific guild, + regardless of what you set it to. + + + + **We recommend using the `requirePermission()` function instead of manually setting this property, to keep things + simple.** + + The permissions a guild member must have to be able to execute this command, by default. + + **Note:** Kord Extensions doesn't enforce the permissions you specify here. + This is because server staff on Discord can change the permissions required to execute a command. + + If you need to limit command usage to a specific role or set of permissions, consider also writing + [a check](/bots/checks/) when you set this. + If you write a check, you should also set this property - it'll hide the command on Discord! + + + + + + Quick access to your bot's `ExtensibleBot` object. + + + + List of this command's registered checks. + + + + Guild ID to register this command against. + Usually set via the `guild()` function. + + + + This function's localised name, calculated lazily when accessed. + Slash commands always have lower-cased names. + + + + Quick access to your bot's application command registry. + + + + This command object's Discord command type. + Overridden by each type of application command, and used during registration. + + diff --git a/bots/commands/application/base-context.mdx b/bots/commands/application/base-context.mdx new file mode 100644 index 00000000..e3712211 --- /dev/null +++ b/bots/commands/application/base-context.mdx @@ -0,0 +1,88 @@ +--- +description: 'Basic APIs shared by all application command context types.' +sidebar_position: 3 + +sidebar_custom_props: + card_group: "Base APIs" + divider_below: true + icon: "fluent:code-text-edit-20-filled" +--- + +import * as Doc from "/src/components/Doc"; + +# Basic Context APIs + +All application command contexts extend the `ApplicationCommandContext` type, which provides some basic common APIs. +These APIs are available in the `action { }` block regardless of interaction type, +so you should familiarise yourself with them! + +The `ApplicationCommandContext` type extends the base `CommandContext` type, so it inherits the APIs defined there. +For more information on the APIs common to all command contexts, +see [the basic command context API page](/bots/commands/base-context). + +All application command contexts also extend one of the [interaction context types (TODO)](#), unless you're writing +[an unsafe command (TODO)](#). + + + + Abstract base type representing everything that all application command contexts have in common. + + + + Generic event object representing the interaction that triggered this command execution. + + Usually accessed via a more specific `event` property in each command's specific context type. + + + + Generic event object representing the current command object. + + Usually accessed via a more specific `command` property in each command's specific context type. + + + + Map representing a data cache shared with the command's registered checks. + + + + + +## Properties + + + + The permissions your bot has in this command execution context - that is, based on the current guild, roles and + channel. + + This is always `null` if the command was executed in a DM. + + + + Quick access to [your bot's settings](/bots/config/). + + +
    +
    + + + The channel this command invocation happened in. + + + + The guild this command invocation happened in, or `null` if it happened in a DM. + + + + The guild member that caused this command invocation, or `null` if: + - This command invocation happened in a DM. + - This command invocation was caused by a webhook. + + + + The Discord message that caused this command invocation. + + + + The Discord user that caused this command invocation, or `null` if it was a webhook. + +
    diff --git a/bots/commands/application/context.mdx b/bots/commands/application/context.mdx new file mode 100644 index 00000000..0de485e5 --- /dev/null +++ b/bots/commands/application/context.mdx @@ -0,0 +1,199 @@ +--- +description: 'Commands run via the "Apps" menu in the client.' +sidebar_position: 3 + +sidebar_custom_props: + card_group: "Commands" + icon: "fluent:apps-list-detail-24-filled" +--- + +import * as Doc from "/src/components/Doc"; + +# Context Commands + +Context commands are a special type of [application command](/bots/commands/application/), +invoked by users via a right-click or tap-and-hold "Apps" menu in their Discord client. +Instead of arguments, context commands receive whatever the user was targeting when they opened the menu. + +![Screenshot showing a Discord client, showcasing how context commands show to users.](/img/bots/commands/application-commands.png) + +Two types of context commands exist: `message` and `user`. +They have identical APIs, aside from the type of data Discord supplies to them. + +:::tip Dynamic Registration + +You should register context commands as part of your bot's startup process, by calling the relevant builder functions in +your extension's `setup` function. +However, if necessary, you can also register them dynamically by calling the same functions after your bot connects to +Discord. +**If you do this, your bot will immediately register the command with Discord, +so be aware of Discord's strict rate limits.** + +::: + +## Extension API + + + + Use this builder to register a message command based on an ephemeral interaction flow. + + + + Optional callable returning an instance of your `ModalForm` subtype, + as explained on [the modals page](/bots/commands/modals). + You'll usually want to pass a constructor here. + + + + + + Optional generic type referring to your `ModalForm` subtype, as explained on + [the modals page](/bots/commands/modals). + + + + + + Use this builder to register a user command based on an ephemeral interaction flow. + + + + Optional callable returning an instance of your `ModalForm` subtype, + as explained on [the modals page](/bots/commands/modals). + You'll usually want to pass a constructor here. + + + + + + Optional generic type referring to your `ModalForm` subtype, as explained on + [the modals page](/bots/commands/modals). + + + + + + Use this builder to register a message command based on a public interaction flow. + + + + Optional callable returning an instance of your `ModalForm` subtype, + as explained on [the modals page](/bots/commands/modals). + You'll usually want to pass a constructor here. + + + + + + Optional generic type referring to your `ModalForm` subtype, as explained on + [the modals page](/bots/commands/modals). + + + + + + Use this builder to register a user command based on a public interaction flow. + + + + Optional callable returning an instance of your `ModalForm` subtype, + as explained on [the modals page](/bots/commands/modals). + You'll usually want to pass a constructor here. + + + + + + Optional generic type referring to your `ModalForm` subtype, as explained on + [the modals page](/bots/commands/modals). + + + + +
    +
    + + + Register a [check](/bots/checks) that must pass for any of the message commands in this extension to run. + + + + Register a [check](/bots/checks) that must pass for any of the user commands in this extension to run. + +
    + +## Message Commands + +You can register a message command using the corresponding builder functions explained above. +All message commands must define a `name` and `action`. + +Message commands don't support arguments, but you can still provide [a ModalForm subtype](/bots/commands/modals) +to prompt the user with a modal, similarly to how [slash commands](/bots/commands/application/slash) work. + +```kt +publicMessageCommand { + name = Translations.Commands.GetMessage.name + + action { + val target = targetMessages.first() + + respond { + content = "Target: ${target.getJumpUrl()}" + } + } +} +``` + +Message commands don't expose any extra APIs, aside from an `action { }` builder and those provided by the +[base application command type](/bots/commands/application/base-command) they extend. +However, along with [the basic context APIs](/bots/commands/application/base-context), +the message command context provides a `targetMessages` property containing a list of targeted messages. + +:::note List of Targets + +While Discord provides a list of target messages, it is impossible for users to target more than one message in the +Discord client — at least, as of this writing. +We think Discord intended to make it possible to specify multiple messages, but this never made it into the client. + +We decided to mirror Discord's choice, as this might become possible at some point in the future. + +::: + +## User Commands + +You can register a user command using the corresponding builder functions explained above. +All user commands must define a `name` and `action`. + +User commands don't support arguments, but you can still provide [a ModalForm subtype](/bots/commands/modals) to +prompt the user with a modal, similarly to how [slash commands](/bots/commands/application/slash) work. + +```kt +publicUserCommand(::GreetModal) { + name = Translations.Commands.GreetUser.name + + action { modal -> + modal ?: return@action + + val target = targetUsers.first() + + respond { + content = "${modal.greeting.value} ${target.mention}" + } + } +} +``` + +User commands don't expose any extra APIs, aside from an `action { }` builder and those provided by the +[base application command type](/bots/commands/application/base-command) they extend. +However, along with [the basic context APIs](/bots/commands/application/base-context), +the user command context provides a `targetUsers` property containing a list of targeted user. + +:::note List of Targets + +While Discord provides a list of target users, it is impossible for users to target more than one user in the +Discord client — at least, as of this writing. +We think Discord intended to make it possible to specify multiple users, but this never made it into the client. + +We decided to mirror Discord's choice, as this might become possible at some point in the future. + +::: diff --git a/bots/commands/application/index.mdx b/bots/commands/application/index.mdx new file mode 100644 index 00000000..b14dc45d --- /dev/null +++ b/bots/commands/application/index.mdx @@ -0,0 +1,58 @@ +--- +sidebar_position: 6 +--- + +import { DocCardListWithDescriptions } from "/src/theme/DocCardList" + +# Application Commands + +Discord provides several rich command types, and Kord Extensions provides its own abstractions for them. +We split these abstractions between each command type, and further divide them into ephemeral and public interaction +flows. + +:::warning User Apps + +Discord recently created a new type of bot: +[user-installable apps](https://discord.com/developers/docs/tutorials/developing-a-user-installable-app). +These bots have commands and activities meant to be run by users in group DMs or guilds, rather than requiring them +to add the bot to a server. + +As of this writing, Kord doesn't support user-installable apps, so we don't either. +If you need this feature, +please [review this pull request](https://github.com/kordlib/kord/pull/970) and provide feedback, which will hopefully +let the Kord team know there's demand for it. + +::: + +## Philosophy + +We often see users asking us why we decided to explicitly divide our command types into ephemeral and public +interaction types. + +When we originally designed this command system, the abstractions were pretty barebones. +You would register an application command and handle all interaction logic on your own. +However, we found that this was not a very user-friendly approach, for several reasons: + +- Discord only gives your bot 5 seconds to respond to an interaction event, and some command action blocks need more + time. +- Discord's interaction API can be confusing, and automatically responding to interaction events makes our API + significantly easier to understand and work with. +- Discord expects your commands to stick with either public or ephemeral, rather than mixing them — doing so can + give you some very unexpected behaviours. +- Ephemeral interactions have limitations that public interactions don't, and we wanted to represent this in a + type-safe way. + +This philosophy also applies to [our component abstractions](/bots/components). + +**Note:** Our abstractions do have some limitations, especially when it comes to handling +[modals](/bots/commands/modals). +If you need more control, take a look at the [unsafe module (TODO)](#). + +## Pages + +See the following pages for more information on each application command type, and how you can use them. + + diff --git a/bots/commands/application/slash.mdx b/bots/commands/application/slash.mdx new file mode 100644 index 00000000..27ca3fd0 --- /dev/null +++ b/bots/commands/application/slash.mdx @@ -0,0 +1,291 @@ +--- +description: "Commands prefixed with / in the client." +sidebar_position: 4 + +sidebar_custom_props: + card_group: "Commands" + icon: "fluent:slash-forward-24-filled" +--- + +import * as Doc from "/src/components/Doc"; + +# Slash Commands + +Slash commands are the main type of [application command](/bots/commands/application/), +executed on Discord by typing a slash (`/`), followed by the command's name and arguments. +Slash commands registered by your bot are visible in a menu displayed above Discord's message input box when a user +types a slash (`/`), allowing them to browse a list of all available commands, +and providing information on their descriptions and arguments. + +:::tip Dynamic Registration + +You should register slash commands as part of your bot's startup process, by calling the relevant builder functions in +your extension's `setup` function. +However, if necessary, you can also register them dynamically by calling the same functions after your bot connects to +Discord. +**If you do this, your bot will immediately register the command with Discord, +so be aware of Discord's strict rate limits.** + +::: + +```kt +publicSlashCommand(::MyArguments) { + name = Translations.Commands.Ping.name + description = Translations.Commands.Ping.description + + check { hasPermission(Permission.MentionEveryone) } + + action { + respond { + // Note: Slash commands usually can't mention people, this is just an example. + + content = "Hey, ${arguments.user.mention}! Get pinged!" + } + } +} +``` + +## Argument Parsing + +Like all command types, chat commands parse their arguments in the order you defined them, +allowing converters that take lambdas to reference previous arguments. +However, because Discord handles most of the parsing logic, the work your bot needs to do is minimal. + +Despite this, it is worth remembering a few things: + +- Slash commands don't support list converters, and these arguments will receive a list with a single value at most. +- Most converters for types that Discord doesn't support will look like string arguments in the Discord client. + We recommend you write auto-completion into your arguments whenever possible. +- Choice converters work as you'd expect, but Discord limits them to 25 options. + If you need more, use auto-completion instead. +- Slash commands can't accept newline characters, which means all string arguments will always be on a single line. + - [Modals](/bots/commands/modals) support paragraph inputs, and this is Discord's official solution to that problem. + +When writing your own converters, remember that users can provide mentions in string arguments, which we feel may +be useful in a small handful of situations. + +## Extension API + +The following APIs are available on the `Extension` type, +which you can use to define your chat commands and modify their behaviour. + + + + Use this builder to register a slash command based on an ephemeral interaction flow. + + + + Optional callable returning an instance of your `Arguments` subtype, + as explained on [the arguments page](/bots/commands/arguments). + You'll usually want to pass a constructor here. + + + + Optional callable returning an instance of your `ModalForm` subtype, + as explained on [the modals page](/bots/commands/modals). + You'll usually want to pass a constructor here. + + + + + + Optional generic type referring to your `Arguments` subtype, as explained on + [the arguments page](/bots/commands/arguments). + + + + Optional generic type referring to your `ModalForm` subtype, as explained on + [the modals page](/bots/commands/modals). + + + + + + Use this builder to register a slash command based on a public interaction flow. + + + + Optional callable returning an instance of your `Arguments` subtype, + as explained on [the arguments page](/bots/commands/arguments). + You'll usually want to pass a constructor here. + + + + Optional callable returning an instance of your `ModalForm` subtype, + as explained on [the modals page](/bots/commands/modals). + You'll usually want to pass a constructor here. + + + + + + Optional generic type referring to your `Arguments` subtype, as explained on + [the arguments page](/bots/commands/arguments). + + + + Optional generic type referring to your `ModalForm` subtype, as explained on + [the modals page](/bots/commands/modals). + + + + +
    +
    + + + Register a [check](/bots/checks) that must pass for any of the slash commands in this extension to run. + +
    + +## Standalone Commands + +Standalone commands are the simplest type of slash command, defined using the builder functions above. +These builders are a receiver against the corresponding `SlashCommand` subtype, providing APIs you can use to configure +your command and its metadata. + +At minimum, a slash command must have a `name` and `description` defined. +Standalone commands must also have an `action` defined. + +The following APIs are available for use when writing a slash command, along with those provided by +[the base `ApplicationCommand` type](/bots/commands/application/base-command). + +### Builders + + + + Register your command's action block, defining what it will do when executed. + See [the context API](#context-api) section for more information on what you can do here. + + If your command has any groups or subcommands, it can't have an action, so **don't use this builder.** + + + + If you provided a modal form constructor when you defined the command, users will be prompted with a modal when + they execute it. + This argument will be an instance of your `ModalForm` when they submit the modal. + + This argument will also be `null` if the user takes longer than 15 minutes to submit the modal, + and Discord will prevent your bot from responding. + It will also be `null` if you didn't define a modal for this command. + + + + + +### Properties + + + + This command's description, shown on Discord and in argument parsing errors. + + + + String representing a clickable mention, which you can send on Discord. + + This is meant to be used after the command is registered, and will always be `null` before your bot has finished + setting up. + + It will also be `null` if you aren't using Kord Extensions' default application command registry. + + + +## Nested Commands + +You can nest slash commands in two ways, which you can combine: + +1. Define command groups within your top-level command, and define subcommands within those groups. +2. Define subcommands directly within your top-level command. + +Discord shows nested commands alongside standalone commands, and users can't execute the top-level command. + +![Screenshot showing a Discord client, showcasing how groups and subcommands show to users.](/img/bots/commands/slash-command-structure.png) + +### Command Groups + +To define a command group, use the `group() { }` builder in your top-level command builder. +Command groups must define a `name` and `description`, and must contain at least one subcommand. + + + + + + Group name, shown on Discord. + + + + + +The `SlashGroup` type exposes some APIs, available within your `group() { }` builder. + +#### Builders + + + + Use this builder to register a subcommand based on an ephemeral interaction flow. + + This builder has an identical API to the `ephemeralSlashCommand` builder, used to define + [standalone commands](#standalone-commands). + + + + Use this builder to register a subcommand based on a public interaction flow. + + This builder has an identical API to the `publicSlashCommand` builder, used to define + [standalone commands](#standalone-commands). + + + +#### Properties + + + + Group description, shown on Discord. + + + +### Subcommands + +To define a subcommand, use the corresponding builder function mentioned below. +Subcommands must define a `name`, `description` and `action`. + + + + Use this builder to register a subcommand based on an ephemeral interaction flow. + + This builder has an identical API to the `ephemeralSlashCommand` builder, used to define + [standalone commands](#standalone-commands). + + + + Use this builder to register a subcommand based on a public interaction flow. + + This builder has an identical API to the `publicSlashCommand` builder, used to define + [standalone commands](#standalone-commands). + + + +## Context API + +The `action { }` builder is a receiver against the `SlashCommandContext` type. +This type provides a set of APIs, along with those provided by +[the base `ApplicationCommandContext` type](/bots/commands/application/base-command), +which allow your bot to respond to slash command invocations. + +### Properties + + + + Object containing this command invocation's parsed arguments, + matching the type of the subtype you provided to your command definition, + or an empty `Arguments` object if you didn't provide one. + + + + Reference to the object representing the current command. + + + + Reference to the event that triggered this command invocation. + + diff --git a/bots/commands/arguments.mdx b/bots/commands/arguments.mdx new file mode 100644 index 00000000..2361e4ce --- /dev/null +++ b/bots/commands/arguments.mdx @@ -0,0 +1,117 @@ +--- +description: "How to define and accept commmand arguments." +sidebar_position: 1 + +sidebar_custom_props: + card_group: "Arguments" + icon: "fluent:virtual-network-20-filled" +--- + +import * as Doc from "/src/components/Doc"; + +# Arguments + +:::note Command Types + +While most command types support arguments, [Context Commands](/bots/commands/application/context) don't. +If you need to collect anything from users who run these commands, you'll need to use [Modals](/bots/commands/modals) +instead. + +::: + +Arguments represent the parameters users supply to your commands. +You can define arguments by creating a class extending the `Arguments` type, containing properties created with +converter builder functions: + +```kt +class MyArguments : Arguments() { + val target by user { + name = "target" + description = "Target user" + } +} +``` + +Once you've created an arguments class, provide it to your commands' builder functions. +This will make the parsed arguments available via the `arguments` property in your command's `action { }` block: + +```kt +publicSlashCommand(arguments = ::MyArguments) { + name = "my-command" + + action { + respond { + content = "Target user: ${arguments.user.mention}" + } + } +} +``` + +## Arguments API + +The `Arguments` type exposes a few settings you can use to configure how Kord Extensions should parse your commands' +arguments, among other things. + + + + Base class you should extend to represent a set of command arguments. + + + Whether to attempt to parse previous arguments when handling an autocompletion event. + + When working with [Slash Commands](/bots/commands/application/slash) and Discord's autocomplete system, setting + this to `true` allows your `autoComplete { }` blocks to retrieve the value for the other arguments, if the user + provides them. + + Some converter types provide a default `autoComplete { }` block, and you'll need to set this property to `true` + when using them. + + + + + +

    + The following APIs are intended for internal use, but may be helpful if you're developing your own related + libraries. +

    + + + Adds a converter to this class' list of arguments, wrapping it in an `Argument` object. + Used internally by the converter builder functions. + + + + The data type the given converter returns. + + + + + + Display name shown on Discord, used in help messages, and the key for keyword arguments + in [Chat Commands](/bots/commands/chat). + + + + Description shown on Discord, and used in help messages. + + + + Converter object to add. + + + + + + List of wrapped converter objects representing command arguments, stored in the order they were defined. + + + + Validation function called by Kord Extensions, ensuring your arguments classes make sense. + + + + Resolved locale for the current command execution. + + + +
    diff --git a/bots/commands/base-command.mdx b/bots/commands/base-command.mdx new file mode 100644 index 00000000..9696d9aa --- /dev/null +++ b/bots/commands/base-command.mdx @@ -0,0 +1,106 @@ +--- +description: "Basic APIs shared by all command types." +sidebar_position: 4 + +sidebar_custom_props: + card_group: "Base APIs" + icon: "fluent:code-block-edit-24-filled" +--- + +import * as Doc from "/src/components/Doc"; + +# Basic Command APIs + +All command types extend the `Command` type, which provides some basic common APIs. +These APIs are available regardless of command type, so you should familiarise yourself with them! + + + + Abstract base type representing everything that all commands have in common. + + + + The extension that registered this command. + + + + + +## Functions + + + + Specify any permissions your bot needs to be able to execute the command. + Permissions will be checked against the command's execution context - the current guild and channel. + + + + The permissions your bot requires. + + + + + + + + Check the permissions provided via the `requireBotPermissions` function, throwing a `DiscordRelayedException` + with a suitable error if your boot doesn't have the required permissions. + + + + Command context used to check permissions. + + + + + + Quick shortcut to emit a `CommandEvent` without blocking the current coroutine. + + + + Event object to emit. + + + + + +## Properties + + + + The extension that registered this command. + + + + Command name, used when invoking the command and shown in help commands. + + +
    +
    + + + Quick access to your bot's backing `Kord` instance. + + + + Quick access to [your bot's Sentry adapter (TODO)](#). + + + + Quick access to [your bot's settings](/bots/config). + + + + Quick access to the [registered translations provider (TODO)](#). + +
    + + + + Command name translation cache, used to avoid repeated translation calls. + + + + Set of permissions required to execute the command, provided via `requireBotPermissions`. + + diff --git a/bots/commands/base-context.mdx b/bots/commands/base-context.mdx new file mode 100644 index 00000000..c75e1848 --- /dev/null +++ b/bots/commands/base-context.mdx @@ -0,0 +1,113 @@ +--- +description: "Basic APIs shared by all command context types." +sidebar_position: 4 + +sidebar_custom_props: + card_group: "Base APIs" + divider_below: true + icon: "fluent:code-text-edit-20-filled" +--- + +import * as Doc from "/src/components/Doc"; + +# Basic Context APIs + +All command contexts extend the `CommandContext` type, which provides some basic common APIs. +These APIs are available in the `action { }` block regardless of command type, +so you should familiarise yourself with them! + + + + Abstract base type representing an execution context common to all commands. + + + + Object representing the currently executing command. + + Command context implementations tend to have a property with a more specific type than this. + + + + The event that triggered this command execution. + + Command context implementations tend to have a property with a more specific type than this. + + + + Translation key representing the command's name, as provided by the user and then lower-cased. + + As some command types support aliases, this generally doesn't match `command.name`. + When working with [chat commands](/bots/commands/chat), consider using `commandName.key` to represent the + command name in your responses. + + + + Data cache shared with the command's defined checks. + + + + + +## Functions + + + + Retrieve the relevant channel for this command execution. + + You should generally use the context's corresponding property instead, as this function doesn't do any caching. + + + + Retrieve the relevant guild for this command execution, if any. + + You should generally use the context's corresponding property instead, as this function doesn't do any caching. + + + + Retrieve the relevant guild member for this command execution, if any. + + You should generally use the context's corresponding property instead, as this function doesn't do any caching. + + + + Retrieve the relevant user for this command execution, if any. + + You should generally use the context's corresponding property instead, as this function doesn't do any caching. + + + + Populate any relevant data required by a specific command context subtype. + + + +## Properties + + + + Data cache shared with the command's defined checks. + + + + Object representing the currently executing command. + + Command context implementations tend to have a property with a more specific type than this. + + + + Translation key representing the command's name, as provided by the user and then lower-cased. + + As some command types support aliases, this generally doesn't match `command.name`. + When working with [chat commands](/bots/commands/chat), consider using `commandName.key` to represent the command + name in your responses. + + + + The event that triggered this command execution. + + Command context implementations tend to have a property with a more specific type than this. + + + + Quick access to [your bot's Sentry adapter (TODO)](#). + + diff --git a/bots/commands/chat.mdx b/bots/commands/chat.mdx new file mode 100644 index 00000000..9228edab --- /dev/null +++ b/bots/commands/chat.mdx @@ -0,0 +1,649 @@ +--- +description: "Prefixed commands sent in messages." +sidebar_position: 7 + +sidebar_custom_props: + card_group: "Commands" + icon: "fluent:chat-warning-24-filled" +--- + +import * as Doc from "/src/components/Doc"; + +# Chat Commands + +## Warnings and Notes + +Before getting started, please make sure you read the below notices! + +:::warning Privileged intents required! + +Chat commands require the "message content" privileged intent. +While chat commands were traditionally how users interacted with bots on Discord, +**this is no longer what Discord recommends or encourages for your bots.** + +If your bot is on 75 or more guilds (or you plan to grow it to that point), +you should consider using +[Application Commands](/bots/commands/application/) instead of chat commands as much as possible. + +Your bot will need to go through a verification process (which starts at 75 guilds) before it can join over 100 guilds. +Discord **will not verify bots** that are using the "message content" privileged intent without a good reason. + +The only situations that we advise you to use chat commands for are +**those where the other types of commands provide an objectively worse user experience.** +This largely only applies to commands that need to take a large amount of text as input (if modals do not suffice). + +Additionally, it is worth noting that users will expect your bots to support +[Application Commands](/bots/commands/application/) as a default mode of interaction. + +::: + +:::info Maintenance Status + +Due to the situation explained in the above warning, +**Kord Extensions' users and developers don't regularly use or test chat commands.** +As a result, there may be small bugs or formatting problems that we haven't noticed, or had the time and energy to fix. + +If you run into any problems with the chat commands framework or its help extension, +please let us know or submit a Pull Request to address the issue. + +::: + +--- + +## Introduction + +Chat commands are a fairly simple concept. +When a user sends a message starting with a command prefix (or bot mention), +the command name and arguments, Kord Extensions can process it accordingly. + +Call the builder functions within your `setup` function when you're registering commands before the bot starts up. +You can also register commands later in the bot's lifecycle by calling the same functions. + +Chat commands support command arguments, as explained on [the arguments page](/bots/commands/arguments). +However, unlike the other command types, they do not support [Modals](/bots/commands/modals). + +As Discord recommends against using chat commands, you must enable them before your bot will respond to them. +For more information, see [the configuration documentation](/bots/config/commands#chat-commands). + +```kt +chatCommand(::MyArguments) { + name = Translations.Commands.Ping.name + description = Translations.Commands.Ping.description + aliasKey = Translations.Commands.Ping.aliases + + check { hasPermission(Permission.MentionEveryone) } + + action { + message.respond( + "Hey, ${arguments.user.mention}! Get pinged!" + ) + } +} +``` + +## Argument Parsing + +Like all command types, chat commands parse their arguments in the order you defined them, allowing converters that +take lambdas to reference previous arguments. +This follows a specific string-parsing strategy: + +1. Parse keyword arguments and remove them from the arguments string. +2. Scan the string from left to right, with converters pulling tokens from it as needed. +3. Ensure all required converters fill, and no extra arguments remain. + +Chat commands support keyword arguments in the following forms: + +- Command-line style: `--name value` +- Properties style: `name=value` + +They also support the following extras: + +- Double-quoted values, to allow passing arguments with spaces: `"like this"` +- Escaped double quotes, to allow passing them within a quoted argument: `"like \" this"` + +The parser code +[is available here](https://github.com/Kord-Extensions/kord-extensions/blob/root/token-parser/src/main/kotlin/dev/kordex/parser/StringParser.kt). +We're sure it has some shortcomings, so feel free to let us know if you think you can improve it. + +## Extension API + +The following APIs are available on the `Extension` type, +which you can use to define your chat commands and modify their behaviour. + + + + Use this builder to register a standalone chat command, as explained in the sections below. + + + + Optional callable returning an instance of your `Arguments` subtype, as explained on + [the arguments page](/bots/commands/arguments). + You'll usually want to pass a constructor here. + + + + + + Optional generic type referring to your `Arguments` subtype, as explained on + [the arguments page](/bots/commands/arguments). + + + + + + Use this builder to register a grouped chat command, as explained in the sections below. + + + + Optional callable returning an instance of your `Arguments` subtype, as explained on + [the arguments page](/bots/commands/arguments). + You'll usually want to pass a constructor here. + + + + + + Optional generic type referring to your `Arguments` subtype, as explained on + [the arguments page](/bots/commands/arguments). + + + + +
    +
    + + + Register a [check](/bots/checks) that must pass for any of the chat commands in this extension to run. + +
    + +## Standalone Commands + +Standalone commands are the simplest type of chat command, defined using the `chatCommand` builder function. +This builder is a receiver function against the `ChatCommand` type, providing APIs you can use to configure +your command and its metadata. + +At minimum, a standalone command must have a `name` and `action` defined. + +The following APIs are available for use when writing a chat command, +along with those provided by [the base `Command` type](/bots/commands/base-command). + +### Builders + + + + Register your command's action block, defining what it will do when executed. + + See [the context API](#context-api) section for more information on what you can do here. + + + + Register a [check] which mass pass for the command to execute when invoked. + + + +### Functions + +We consider all plain functions defined in the chat command API internal, but you may find some of them useful for a +small handful of advanced use-cases. + + + + Attempt to execute this command based on the provided arguments. + This function runs the defined checks by default, which will prevent command execution if they fail. + + This function is normally called by the `ChatCommandRegistry` when someone invokes a chat command on Discord. + + + + Message creation event triggering this command execution. + + + + Name used to invoke this command. + This is specifically the text that was sent to invoke this command by a Discord user, and not a translation key. + + + + The parser used to parse this command's arguments. + + + + String containing this command invocation's unparsed arguments. + + + + Whether to skip running the command's registered checks. + + + + Map representing a data cache shared with the command's registered checks, and made available in the command + context. + + + + + + Retrieve the command's translated signature, which explains how users should structure the arguments this command + takes. + Usually used by the `help` command in your bot's [help extension](/bots/config/extensions#help-extension). + + Command signatures are usually generated by the chat command parser, but you can provide one manually when you + define your command. + + This function's output is cached. + + + + Locale used to translate the command name for generated signatures, or the entire signature if provided. + + + + + + Retrieve the command's translated name. + + This function's output is cached. + + + + Locale used to translate the command name. + + + + + + Get a set of the command's translated aliases. + + This function's output is cached. + + + + Locale used to translate the command's aliases. + + + + + + Run this command's registered checks, returning `true` if they all pass, or `false` if any fail. + + + + Message creation event triggering this command execution. + + + + Whether to send any failure messages generated by the checks in response to the command invocation. + + + + Map representing a data cache passed to each check, and made available in the command context. + + + + + + Validation function that throws an `InvalidCommandException` if there are any problems. + + + +### Properties + + + + Translation key that resolves to a comma-separated list of command aliases. + + If a locale shouldn't contain any aliases, set the corresponding translation value to the universal empty + translation string, represented by three "empty set" characters (`∅∅∅`). + + + + Whether to allow users to specify keyword-style arguments, as specified above. + + + + This command's description, used by the `help` command in your + [help extension](/bots/config/extensions#help-extension), and shown in argument parsing errors. + + Defaults to `No description provided`. + We strongly recommend providing your own description! + + + + Whether this command can be executed. + Set this to `false` to disable this command, which will hide it from the `help` command and prevent users from + executing it. + + You can set this property at runtime, by storing the return value of your `chatCommand(...) { }` builder invocation. + + + + Whether to hide this command from the `help` command's output. + + **Users can still run hidden commands, so remember to lock them down with permissions checks as appropriate.** + + + + Whether to fall back to your bot's [configured default locale](/bots/config/i18n) when resolving command names for + execution. + + Enabling this allows users to execute your command by providing its name or aliases in your bot's default locale, + as well as those provided in the locale returned by your bot's [configured locale resolvers](/bots/config/i18n). + + + + Translation key that resolves to a custom signature representing how users should supply command arguments. + + By default, the `ChatCommandParser` will generate a signature suitable for most commands: + + - Argument names will be translated into the user's locale as appropriate. + - Required arguments will be wrapped with <> - e.g. <name> + - Optional arguments will be wrapped with `[]` - e.g. `[name]` + - Converters set to show argument types will: + - Append the type to the name with a colon - e.g. `[name: type]` + - Append any default value with an equals sign - e.g. `[name: type=default]` + - List-based converters will append an ellipsis to show that multiple arguments are accepted - for example: + - `[name...]` + - `[name: type=default...]` + + Provide this property to override the generated signature and provide your own. + + + + Easy access to the current `ChatCommandRegistry` object. + + + + + + Map representing the alias translation cache, used to store translated aliases so they don't have to be + re-translated all the time. + + + + Optional callback returning the `Arguments` subtype this command uses to parse and store arguments. + + + + Your command's body, set using the `action { }` builder. + + + + List of this command's registered checks. + + + + Map representing the signature translation cache, used to store translated signatures so they don't have to be + re-translated all the time. + + + +## Grouped Commands + +Grouped commands work just like standalone commands, but they let you nest other commands within them, creating a tree +of subcommands as deep as you need. +They're defined using the `chatGroupCommand` builder, a receiver function against the `ChatGroupCommand` type, which +itself extends the `ChatCommand` type. + +At minimum, a grouped command must have a `name` defined. + +Grouped commands expose the same API provided by standalone commands, aside from a few additions and changes. + +### Behaviour + +When compared to standalone commands, grouped commands provide a slightly different execution flow. + +When executed, these commands check the first argument provided against their registered subcommands, passing the rest +of the arguments to the matching subcommand if one exists. +If no matching subcommand exists, the grouped command will execute its own `action` block using the full set of +arguments. + +By default, grouped commands provide their own `action` block, which simply calls the `sendHelp()` function provided +by [the context API](#context-api). +However, you can override this behaviour by providing your own `action` block if needed. + +Subcommands always run the [checks] defined on their parent commands along with their own, traversing the entire tree. +They all need to pass for the command to execute. + +### Builders + + + + Use this builder to register a standalone chat subcommand. + + This builder functions identically to the builder provided by [the extension API](#extension-api). + + + + Use this builder to register a nested grouped chat subcommand. + + This builder functions identically to the builder provided by [the extension API](#extension-api). + + + +### Functions + + + + Retrieve the subcommand matching the given name, or `null` if no matching, enabled command exists. + + + + Command name to match on. + Providing `null` makes the function always return `null`. + + + + Message creation event triggering this command execution. + + + + + + Retrieve the command's translated name. + If there are any parent commands, their names will be prepended along with a space. + + + + Locale used to translate the commands' names. + + + + + +### Properties + + + + List containing this command's subcommands. + + + + This command's parent command, if it has one. + + + +## Context API + +The `action { }` builder is a receiver against the `ChatCommandContext` type. +This type provides a set of APIs, along with those provided by +[the base `CommandContext` type](/bots/commands/base-context), +which allow your bot to respond to command invocations. + + + + Context object containing the APIs you need to respond to a chat command. + + + + Generic representing the correct Arguments subtype. + + + + + + Reference to the object representing the current command. + + + + Reference to the event that triggered this command invocation. + + + + Translation key representing the name used to invoke this command. + + This key contains the name as provided on Discord, wrapped into a `Key` object along with the event's locale. + Use `commandName.key` to get the name used. + + + + String parser used to parse the arguments for this command invocation. + + + + The raw, unparsed command arguments as sent by the user on Discord. + + + + Data cache map shared with this command's [checks](/bots/checks). + + + + + +### Builders + + + + Convenience builder for creating a paginator. + + For more information, see [the paginator documentation (TODO)](#). + + + + The default paginator group to use for pages. + + + + Whether to ping the author of the `targetMessage` when sending the paginator, if you provide `targetMessage`. + + + + Channel to send the paginator to. + + If both `targetChannel` and `targetMessage` are omitted, this will default to the channel this command + was invoked in. + + + + Message to respond to when sending the paginator. + + This will override `targetChannel` with the message's channel if you provide both. + + + + + +### Functions + + + + Generate and send the help embed for this command, generated using + [your bot's help extension](/bots/config/extensions#help-extension). + + Returns `true` if your bot has a proper help extension and the message was sent, `false` otherwise. + + + + Convenience function for quickly replying to a message using a translation key. + + + + Translation key representing the message content. + + + ", "Map"]} default="Empty array/map"> + An array or map containing placeholder values to fill. + + + + Whether to explicitly reply to the message. + + + + Whether to ping the message author: + - When `useReply` is `true`, pings the author using the Discord reply feature. + - When `useReply` is `false`, pings the author by adding a mention to the start of the message content. + + + + + + + + Retrieve the message from the `event` property. + Used internally to populate the `message` property. + + + + Internal function used to set the `arguments` property. + + + + Arguments object to store. + + + + + +### Properties + + + + Object containing this command invocation's parsed arguments, matching the type of the subtype you provided to your + command definition, or an empty `Arguments` object if you didn't provide one. + + + + The raw, unparsed command arguments as sent by the user on Discord. + + + + Reference to the object representing the current command. + + + + Reference to the event that triggered this command invocation. + + +
    +
    + + + The channel this command invocation happened in. + + + + The guild this command invocation happened in, or `null` if it happened in a DM. + + + + The guild member that caused this command invocation, or `null` if: + - This command invocation happened in a DM. + - This command invocation was caused by a webhook. + + + + The Discord message that caused this command invocation. + + + + The Discord user that caused this command invocation, or `null` if it was a webhook. + +
    + + + + String parser used to parse the arguments for this command invocation. + + diff --git a/bots/commands/converters/_category_.yml b/bots/commands/converters/_category_.yml new file mode 100644 index 00000000..46474407 --- /dev/null +++ b/bots/commands/converters/_category_.yml @@ -0,0 +1,5 @@ +description: "Argument parsing, completion and validation." + +customProps: + card_group: "Arguments" + icon: "fluent:convert-to-type-24-filled" diff --git a/bots/commands/converters/basics.mdx b/bots/commands/converters/basics.mdx new file mode 100644 index 00000000..457826cf --- /dev/null +++ b/bots/commands/converters/basics.mdx @@ -0,0 +1,287 @@ +--- +description: "Learn what converters are and how to use them." +sidebar_position: 1 + +sidebar_custom_props: + card_group: "Basics" + divider_below: true + icon: "fluent:new-24-filled" +--- + +import * as Doc from "/src/components/Doc"; + +# Converter Basics + +Converters are responsible for converting command arguments from their basic (usually String) representations into rich +types. +They're a core part of Kord Extensions' command system, allowing you to focus on getting your bot working without +worrying about parsing logic. + +Each converter is a class extending the `Converter` type, and annotated with the `@Converter` annotation. +You can read [the custom converters page](/bots/commands/converters/custom) for more information on converter anatomy, +but for now you should understand that converters can: + +- Parse String-based values. +- Transform entities and Snowflakes received from Discord. +- Wrap other converters to change their behaviour. + +## Usage + +You can create a converter using the corresponding builder functions, available in your `Arguments` subtype, +[which you'll need to create to handle command arguments](/bots/commands/arguments). +Builder functions have descriptive names based on the corresponding converter's name, and the behaviour you should +expect from them. + +### Default Behaviour + +The default behaviour type is **single**, which represents a single, mandatory argument. +Builder functions don't have anything special in their names to denote this, as it is the default. + +You can modify argument expectations and behaviour using builder functions that reference other argument types. + +### Optional Types + +You can make arguments optional by specifying their **defaulting** or **optional** variants. +Optional arguments default to `null` when not provided, while defaulting arguments let you provide your own default +value. + +### Multiple Inputs + +:::warning Chat commands only! + +As of this writing, while you can use these argument types with [slash commands](/bots/commands/application/slash), +they can only process and return a single value. +This is because Discord lacks any real way to provide an argument with multiple values. + +If you do use these argument types with slash commands, please note that they will only ever resolve a single value. +For list converters, this value is a `List` with one element. + +::: + +You can make arguments consume or output multiple inputs by specifying their **coalescing** or **list** variants. + +**Coalescing** arguments are meant for use with [chat commands](/bots/commands/chat), +allowing your bot to combine multiple arguments into a single value. +This is helpful when you want to make it easier to provide arguments containing spaces without requiring quoting, +but remember that **coalescing arguments consume as many arguments as possible** until the next value would make +argument data invalid. + +**List** arguments are also meant for use with [chat commands](/bots/commands/chat), +but instead of combining arguments into a single value, they place them into a list. +However, like coalescing arguments, **list arguments consume as many arguments as possible** until the next value would +be an invalid argument. + +### Limited Inputs + +You can limit the values accepted by an argument by specifying the **choice** variant. + +Choice arguments include a limited set of pre-set possible values, and they respond with an error when users provide +other values. + +When used with [slash commands](/bots/commands/application/slash), +Discord shows the first 25 acceptable values to the user. + +## Configuration + +You can configure a converter via the builder functions it provides. +While some converters may support special configuration functions and properties, all converters support a common set +of options. + +### Shared Settings + + +

    + **Required Settings** +

    + + + Translation key representing this argument's name. + + Should be short, lowered-kebab-case, and without spaces. + + + + Translation key representing this argument's description. + + Should be descriptive, but try to avoid making it too long. + + +
    + +

    + **Optional Settings** +

    + + + Register the auto-complete callback for this argument. + Use the APIs provided by the `AutoCompleteInteraction` object to check the provided partial argument, and respond + with some options. + + Extra APIs are available to help with this, which you can read about on [the interaction utils page (TODO)](#). + + + + The event that triggered the auto-completion interaction. + + + + + + Register the mutator for this argument. + A mutator takes the final result of an argument conversion, and either returns that value or a new one based on it. + + The generic type `T` represents the argument value's type. + A mutator may only return the same type. + + + + The value to mutate. + + + + + + Register the validator for this argument. + A validator examines the final result of an argument conversion, and may decide to cause a parsing failure via + the `fail()` function. + + The API matches the one provided by [check contexts](/bots/checks/custom#context-api), but with some additional + properties. + The generic type `T` represents the argument value's type. + + + The command context that triggered this validation. + + + + The current converter object. + + + + The value to examine. + + +
    + +### Settings: Choice Converters + +Choice converter builders additionally extend `ChoiceConverterBuilder`, providing an API that allows you to define +what the possible choices are. +Choices have a human-readable component, represented by a `Key` object, which your bot will translate using +[its locale resolvers](/bots/config/i18n). + +The generic type `T` below represents the argument value's type. + + + + Add a single choice to the collection of possible choices. + + + + Translation key, used to display the name of the choice on Discord. + + + + Corresponding value, used as the argument's value when a user selects this choice. + + + + + + Add multiple choices to the collection of possible choices. + + + + Collection of possible choices to add. + + + + + + The collection of possible choices, which you can mutate directly if needed. + + + +### Settings: Coalescing Converters + +Coalescing converter builders extend `CoalescingConverterBuilder`, instead of directly extending `ConverterBuilder`. + +There may be situations where you'd like to decide whether your coalescing converter should respond with an error when +it can't parse any values, and this builder type provides a property for that. + + + + Whether to ignore parsing errors when no values can be parsed. + + If your argument should always require a value, set this to `false`. + + + +### Settings: Defaulting Converters + +Defaulting converter builders extend `DefaultingConverterBuilder`, instead of directly extending `ConverterBuilder`. +This type provides an API you can use to specify a default value, and what should happen when arguments fail to parse. + +The generic type `T` below represents the argument value's type. + + +

    + **Required Settings** +

    + + + The default value to use when a user doesn't provide one, or when parsing fails and `ignoreErrors` is `true`. + + +
    + +

    + **Optional Settings** +

    + + + Whether to use the default value when parsing fails. + + Set this to `true` to ignore parsing errors, and use the default value instead. + +
    + +### Settings: List Converters + +List converter builders extend `ListConverterBuilder`, instead of directly extending `ConverterBuilder`. + +There may be situations where you'd like to decide whether your list converter should respond with an error when +it can't parse any values, and this builder type provides a property for that. + + + + Whether to ignore parsing errors when no values can be parsed. + + If your argument should always require a value, set this to `false`. + + + +### Settings: Optional Converters + +List converter builders extend `OptionalConverterBuilder`, instead of directly extending `ConverterBuilder`. + +There may be situations where you'd like to decide whether your optional converter should respond with an error when +it can't parse any values, and this builder type provides a property for that. + + + + Whether to ignore parsing errors when no values can be parsed. + + Set this to `true` to ignore parsing errors, and use `null` instead. + + + +### Note: Combined Converters + +Coalescing converters created using the defaulting or optional builder variants are represented using their own +builder types — `DefaultingCoalescingConverterBuilder` and `OptionalCoalescingConverterBuilder`. +These builder types don't extend the `DefaultingConverterBuilder` or `OptionalConverterBuilder` types, but they still +have the same API. + +These types work as you'd expect, but note that while the `ignoreErrors` property defaults to `true` for coalescing +converters, it defaults to `false` for the defaulting and optional variants. diff --git a/bots/commands/converters/bundled.mdx b/bots/commands/converters/bundled.mdx new file mode 100644 index 00000000..dd6962f9 --- /dev/null +++ b/bots/commands/converters/bundled.mdx @@ -0,0 +1,545 @@ +--- +description: "Converters provided by Kord Extensions." +sidebar_position: 2 + +sidebar_custom_props: + card_group: "Converters" + icon: "fluent:collections-empty-24-filled" +--- + +import * as Doc from "/src/components/Doc"; + +# Bundled Converters + +Kord Extensions includes many built-in converters. +They cover the most common use-cases (and some uncommon ones), +but you can always [make your own](/bots/commands/converters/custom) if needed. + +## General + + + + Boolean converter, with support for translated values. + + + + This converter supports the following values, specified below in English. + You can also use names in your own language, provided Kord Extensions has translations for it. + + - True: `1`, `y`, `yes`, `t`, `true` + - False: `0`, `n`, `no`, `f` `false` + + + + + + Colour converter, supporting hex codes prefixed with `#` or `0x`, plain RGB integers, or colour names matching + Discord's historical branding colours. + + This converter returns [Kord Color objects](https://dokka.kord.dev/common/dev.kord.common/-color/index.html). + + + + This converter supports the following values, specified below in English. + You can also use names in your own language, provided Kord Extensions has translations for it. + + - Black: `black`, `blck`, `blk` + - Blurple: `blurple`, `purple`, `purp`, `prpl`, `prp` + - Fuchsia: `fuchsia`, `pink`, `pnk` + - Green: `green`, `grn` + - Light Blurple: `light blurple`, `lblurple`, `light blurp`, `lblurp`, `light purple`, `lpurple`, `light purp`, + `lpurp`, `light prpl`, `lprpl`, `light prp`, `lprp` + - Red: `red`, `rd` + - White: `white`, `wht` + - Yellow: `yellow`, `ylw` + + + + + + Decimal converter, parsing the provided value into a `Double`. + + This converter supports some extra settings: + + + The maximum allowed input value. + + + + The minimum allowed input value. + + + + + Duration converter, using a complex parsing strategy to parse arguments into + [DateTimePeriod objects](https://kotlinlang.org/api/kotlinx-datetime/kotlinx-datetime/kotlinx.datetime/-date-time-period/). + These objects are timezone-agnostic, and you may need to normalise them against a specific timezone. + + If you're writing a [chat command](/bots/commands/chat), consider using the coalescing variant for this converter, allowing + users to supply spaces in their input. + + This converter supports some extra settings: + + + Whether to respond with a long help message on parsing failures, explaining exactly how to specify durations + and what time units are supported. + + Set this to `false` if you only want to tell the user that they provided an invalid time unit. + + + + Whether the converter should only accept a positive duration. + + Set this to `false` if you also want to accept negative durations. + + + Along with [Discord-formatted timestamps](https://discord.com/developers/docs/reference#message-formatting), this + converter supports specifying durations as a series of units and numbers. + + + + Users can specify durations as pairs containing both a unit and a numeric value, + including prefixing numbers with a minus sign (`-`) to make them negative.. + The parser removes commas, spaces, and the plus character (`+`) before parsing. + + Pairs work when specified in any order, and duplicates are added/subtracted. + The parser supports both ordered forms to allow for natural input in right-to-left languages: + + - Value first: `12 days` + - Unit first: `d12` + + Users can prefix numbers with a minus sign (`-`) to make them negative. + The parser only supports base-10 ASCII integers. + + + + The parser supports the following units, which are listed below with their English names. + You can also use names in your own language, provided Kord Extensions has translations for it. + + - Seconds: `s`, `sec`, `secs`, `second`, `seconds` + - Minutes: `m`, `mi`, `min`, `mins`, `minute`, `minutes` + - Hours: `h`, `hr`, `hour`, `hours` + - Days: `d`, `day`, `days` + - Weeks: `w`, `wk`, `week`, `weeks` + - Months: `mo`, `mth`, `month`, `months` + - Years: `y`, `yr`, `year`, `years` + + + + The following examples are valid duration strings: + + - `12d 4h 30m` - 12 days, 4 hours and 30 minutes. + - `12d 4h 30m -1 w` - 5 days (12 - 7), 4 hours and 30 minutes. + - `-2d 1w 6h -2h` - 5 days (7 - 2) and 4 hours (6 - 2). + - `周3 日2 分30` - in Simplified Chinese, 23 days (3 \* 7 + 2) and 30 minutes. + + The following examples are invalid: + + - `12` - bare numbers are not supported, a unit is required. + - `12m d w` - all units require a numerical value. + - `-5 days` with `positiveOnly = true` - the current converter only accepts positive durations. + + + + + + Email converter, validating that the provided input is a well-formatted email address, according to + [Apache Commons' email validator](https://commons.apache.org/proper/commons-validator/apidocs/org/apache/commons/validator/routines/EmailValidator.html). + + + + Arbitrary `enum` converter, converting arguments based on a defined getter. + + Your enum must extend `ChoiceEnum` and implement the `readableName` property, which is a translation key + representing a human-readable name to show on Discord for each enum entry. + + This converter has some **required extra settings**: + + + Translation key representing a short (one or two word) lower-case name for whatever the enum represents to the + user. + + For example, if your enum is named `IssueStatuses` and contains `open`, `closed` and `invalid` entries, your + translation key should, in English, resolve to `status` or `issue status`. + + + It also supports some other settings: + + + Getter used to resolve the correct enum entry. + The default strategy (named `getEnum`) searches through the possible enum entries, returning the first one that + matches one of the following, case-insensitively: + + - The translated string referred to by `readableName`. + - The string translation key referred to by `readableName`. + - The literal name of the enum entry as written in code. + + + + + Integer converter, parsing the provided value into an `Int`. + + This converter supports some extra settings: + + + The maximum allowed input value. + + + + The minimum allowed input value. + + + + The radix (base) used when parsing. + + + + + Long converter, parsing the provided value into a `Long`. + + This converter supports some extra settings: + + + The maximum allowed input value. + + + + The minimum allowed input value. + + + + The radix (base) used when parsing. + + + + + String converter, returning the argument's value verbatim. + + This converter supports some extra settings: + + + The maximum allowable input length. + + + + The minimum allowable input length. + + + + + Supported locale converter, converting arguments into `Locale` objects representing the locales Kord Extensions + supports. + + Supported locales are defined + [here](https://github.com/Kord-Extensions/kord-extensions/blob/root/kord-extensions/src/main/kotlin/dev/kordex/core/i18n/SupportedLocales.kt). + If a locale you need isn't supported, please feel free to + [contribute translations for it](https://hosted.weblate.org/projects/kord-extensions/). + + + +## Discord Entities + + + + Discord channel converter, converting the input to a resolved + [Kord Channel object](https://dokka.kord.dev/core/dev.kord.core.entity.channel/-channel/index.html). + This converter behaves differently depending on command type: + + - [Chat commands](/bots/commands/chat): Supports mentions, IDs, names, and `this` to refer to the current channel. + - [Slash commands](/bots/commands/application/slash): Uses the resolved object Discord provides, + and can't do its own parsing or resolution. + + This converter supports some extra settings: + + + Add a channel type to the set of acceptable types. + + By default, this converter doesn't limit the acceptable channel types. + Providing at least one turns on this requirement. + + + + + + + + + The set of acceptable channel types, usually mutated by `requireChannelType(type)`, but you can set it yourself if + you prefer. + + By default, this converter doesn't limit the acceptable channel types. + Providing at least one turns on this requirement. + + + + **For chat commands:** Callback that returns a `Snowflake` representing a predefined guild the supplied channel + must exist on. + + Channels passed to slash commands are already limited to the same guild the command was executed on, so this + setting does nothing in that case. + + + + **For chat commands:** Whether the provided channel must exist on the same guild used to execute the command. + If the command was executed in DMs, the argument will always be considered invalid. + + Channels passed to slash commands are already limited to the same guild the command was executed on, so this + setting does nothing in that case. + + + + + Emoji converter, parsing the input into a + [Kord Emoji object](https://dokka.kord.dev/core/dev.kord.core.entity/-emoji/index.html). + + **Note:** Your bot must have the `GuildEmojis` intent for custom/guild emojis. + + This converter supports the following input styles: + + - Unicode emojis. + - Discord custom/guild emojis. + - Custom/guild emoji IDs, with or without surrounding colons (`:`). + - Custom/guild emoji names, with or without surrounding colons (`:`), resolved using the first matching emoji your + bot can find on any of its guilds. + - Unicode emoji names, as used in Discord's emoji picker. + + + + Discord guild converter, converting the input to a resolved + [Kord Guild object](https://dokka.kord.dev/core/dev.kord.core.entity/-guild/index.html). + This converter supports IDs, names, and `this` to refer to the current guild. + + + + Discord member converter, converting the input to a resolved + [Kord Guild Member object](https://dokka.kord.dev/core/dev.kord.core.entity/-member/index.html). + This converter behaves differently depending on command type: + + - [Chat commands](/bots/commands/chat): Supports mentions, IDs, names, `you` to refer to the bot, + and `me` to refer to the user executing the command. + - [Slash commands](/bots/commands/application/slash): Uses the resolved object Discord provides, + and can't do its own parsing or resolution. + + This converter supports some extra settings: + + + Callback that returns a `Snowflake` representing a predefined guild the supplied member must exist on. + This will also limit command executions to that guild. + + + + Whether the provided member must exist on the same guild used to execute the command. + If the command was executed in DMs, the argument will always be considered invalid. + + + + **For chat commands:** Whether to use the author of the replied-to message, when there is one. + If the command was executed in DMs, the argument will always be considered invalid. + + + + + Discord message converter, converting the input to a resolved + [Kord Message object](https://dokka.kord.dev/core/dev.kord.core.entity/-message/index.html). + This converter supports jump URLs and IDs, and the converter assumes that provided IDs refer to messages in the + current channel. + + Messages will be retrieved if they're not already in Kord's cache, which requires the bot to be able to see the + message in question. + Your bot cannot resolve jump URLs referring to messages in DMs. + + This converter supports some extra settings: + + + Callback that returns a `Snowflake` representing a predefined guild the supplied message must exist on. + This will also limit command executions to that guild. + + + + When `requiredGuild` is specified, whether the message must exist on guild returned by that callback. + Otherwise, whether the provided message must exist on the same guild used to execute the command. + + If the command was executed in DMs and this setting is `true`, the argument will always be considered invalid. + + + + Whether to use the author of the replied-to message, when there is one. + + + + + Discord role converter, converting the input to a resolved + [Kord Role object](https://dokka.kord.dev/core/dev.kord.core.entity/-role/index.html). + + This converter doesn't support resolving roles via commands run in DMs. + It also behaves differently depending on command type: + + - [Chat commands](/bots/commands/chat): Supports mentions, IDs and names. + - [Slash commands](/bots/commands/application/slash): Uses the resolved object Discord provides, and can't do its own parsing or resolution. + + This converter supports some extra settings: + + + **For chat commands:** Callback that returns a `Snowflake` representing a predefined guild the supplied role + must exist on. + + Roles passed to slash commands are already limited to the same guild the command was executed on, so this + setting does nothing in that case. + + + + + Discord snowflake converter, converting the input to a + [Kord Snowflake object](https://dokka.kord.dev/common/dev.kord.common.entity/-snowflake/index.html). + + + + Discord tag converter, converting the input to a resolved + [Kord ForumTag object](https://dokka.kord.dev/common/dev.kord.common.entity/-forum-tag/index.html). + + This converter supports tag names, supplied via the built-in auto-completion based on its settings: + - **Default:** Use the parent channel for the current forum thread, if the command is being run within one. + - **With Channel Getter:** Always use the forum channel returned by the getter. + + If you don't provide a channel getter and a user runs this command outside of a forum thread, your bot won't be able + to resolve any tags. + + + Callback that returns a `ForumChannel` representing a predefined forum channel the supplied tag must exist on. + + + + + [Discord-formatted timestamp](https://discord.com/developers/docs/reference#message-formatting) converter, + converting the input to a `FormattedTimestamp` object, which includes the corresponding `Instant` and + `TimestampType`. + + This converter only supports Discord-formatted timestamps. + For duration parsing (which includes Discord-formatted timestamps), see the `Duration` converter above. + + + + Discord user converter, converting the input to a resolved + [Kord User object](https://dokka.kord.dev/core/dev.kord.core.entity/-user/index.htm). + This converter behaves differently depending on command type: + + - [Chat commands](/bots/commands/chat): Supports mentions, IDs, usernames, tags, `you` to refer to the bot, and `me` to refer to the user + executing the command. + - [Slash commands](/bots/commands/application/slash): Uses the resolved object Discord provides, and can't do its own parsing or resolution. + + This converter supports some extra settings: + + + **For chat commands:** Whether to use the author of the replied-to message, when there is one. + + + + +## Slash Commands + + + + Discord attachment converter, supplied as a file when invoking a slash command. + Discord provides a drop target for this type of argument. + + This converter doesn't support chat commands. + + + +## Unsafe + +:::danger These converters are dangerous! + +We included these converters because they are useful in limited situations, but they can be dangerous if not used +correctly, with their access limited to trusted users. + +These converters may slow down or crash your bot, or result in data exposure problems when used incorrectly. +Use them at your own risk! + +::: + + + + Regular expression converter, allowing users to supply a regular expression. + + **Remember that untrusted regular expressions are dangerous, and executing them carries risks (such as + [catastrophic backtracking](https://www.regular-expressions.info/catastrophic.html)) that this converter isn't + designed to prevent.** + If you need to execute untrusted regular expressions, consider writing + [a custom converter](/bots/commands/converters/custom) using + [Google's RE2/J library](https://github.com/google/re2j). + + This converter supports some extra settings: + + + Regular expression options used to create the `Regex` object. + + + + + Serialised value converter, converting argument values to the given type using `kotlinx.serialization`. + + **This isn't how `kotlinx.serialization` is supposed to be used.** + **This converter is a best-effort attempt to support new argument types without writing a + [custom converter](/bots/commands/converters/custom), + and it's important to thoroughly test any code using this before deploying it.** + + This converter has some **required extra settings**: + + + Translation key representing a short (one or two word) lower-case name for whatever the value type represents to + the user. + + For example, if your type is named `IssueStatus` and contains `OpenStatus`, `ClosedStatus` and `InvalidStatus` + values, your translation key should, in English, resolve to `status` or `issue status`. + + + diff --git a/bots/commands/converters/custom.mdx b/bots/commands/converters/custom.mdx new file mode 100644 index 00000000..7b20ab53 --- /dev/null +++ b/bots/commands/converters/custom.mdx @@ -0,0 +1,556 @@ +--- +description: "Write your own custom converters." +sidebar_position: 3 + +sidebar_custom_props: + card_group: "Converters" + icon: "fluent:collections-add-24-filled" +--- + +import CodeBlock from "@theme/CodeBlock"; +import EmbeddedVersion from "../../../src/components/EmbeddedVersion"; +import * as Doc from "/src/components/Doc"; + +# Custom Converters + +When the bundled converters don't meet your needs, you can create your own. +Kord Extensions provides utilities that make creating your own converters easier, +generating converter builders functions automatically. + +## Build Configuration + +Before getting started, make sure you're +[using our Gradle plugin](/bots/tutorials/setting-up), +our [bot template](https://codeberg.org/Kord-Extensions/template) or +[plugin template](https://codeberg.org/Kord-Extensions/template-plugin), +or you +[set your project up manually according to our tutorial](/bots/tutorials/setting-up-without-plugin). + +Our annotation processor uses these coordinates: +dev.kordex:annotation-processor: + +## Anatomy + +### Implementation + +All converters extend one of the converter base types: + +- `SingleConverter` - This is the type most converters should inherit. + Always pick this unless you have a specific reason not to. +- `ChoiceConverter` - Type representing a choice-variant converter. + These converters are like single converters, but include a `choices` map. + They're meant primarily to be used with slash commands, but do still work with chat commands. +- `CoalescingConverter` - Type representing a coalescing-variant converter. + These converters are separate from single converters, so pick this type if you're writing one. +- `DefaultingConverter` - Type representing a defaulting-variant converter. + Most converters don't extend this directly, instead relying on the `CoaleascingToDefaultingConverter` and + `SingleToDefaultingConverter` wrapping types, generated via the `@Converter` annotation. +- `ListConverter` - Type representing a list-variant converter. + Most converters don't extend this directly, instead relying on the `SingleToListConverter` wrapping type, + generated via the `@Converter` annotation. +- `OptionalConverter` - Type representing an optional-variant converter. + Most converters don't extend this directly, instead relying on the `CoaleascingToOptionalConverter` and + `SingleToOptionalConverter` wrapping types, generated via the `@Converter` annotation. + +All converter base types take a generic type parameter, representing the final type your converter will transform values +into. + +Once you've picked a base type, you'll need to create a class extending it and implement the required APIs. + +```kt +@Converter( + "snowflake", + + types = [ConverterType.DEFAULTING, ConverterType.LIST, ConverterType.OPTIONAL, ConverterType.SINGLE] +) +public class SnowflakeConverter( + override var validator: Validator = null, +) : SingleConverter() { + override val signatureType: Key = CoreTranslations.Converters.Snowflake.signatureType + + // ... +} +``` + + + "]}> + Your converter must implement all of the APIs specified below, aside from any optional properties with a default + value. + + + + + Validator provided by other developers. + + + + + + Translation key referring to a short, non-title-case name for the type of data your converter handles. + For example, `number`, `ID`, `regex`, `locale name/code`, etc. + + This is used by the `help` command in your [help extension](/bots/config/extensions#help-extension), + and shown in error responses. + + + + Translation key referring to a longer description for the type of data your converter handles, if required. + For example, `yes or no` for `Boolean` converters. + + If provided, this is used instead of `signatureType` in "invalid value" error messages. + + + + Whether the `signatureType` property should be shown by the `help` command in your + [help extension](/bots/config/extensions#help-extension). + + Set this to `false` to hide it. + + + + String parsing function used when handling chat command arguments, as explained below. + + + + Tokenising string parser containing this command invocation's string arguments. + + May be `null` when the argument was provided using keyword syntax, or when values are provided by a wrapping + converter. + + + + Command context representing this command invocation. + + **Do not try to cast this to a simpler type** - stick with the basic APIs available on this type. + This is important because Kord Extensions my pass unusual `CommandContext` subtypes into this function for + specific, niche use-cases. + + + ?"]}> + If the command invocation contains this argument as a keyword argument, this argument won't be `null` and you + should use it instead of trying to consume tokens from the `parser`. + + + + + + Discord option parsing function used when handling slash command arguments, as explained below. + + + + Command context representing this command invocation. + + **Do not try to cast this to a simpler type** - stick with the basic APIs available on this type. + This is important because Kord Extensions my pass unusual `CommandContext` subtypes into this function for + specific, niche use-cases. + + + + Discord option object, representing the data Discord provided for this argument's value. + + You'll need to try to cast this to the type you expect, based on what your converter returns from + `toSlashOption`. + + + + + + Conversion function that takes an `Argument` type, and converts it to a Discord option type, as explained below. + + + + The `Argument` object you'll need to convert to a Discord option. + + + + + + + +### Parsing + +Your converter must implement parsing for both slash command and chat command arguments. + +#### Slash Commands + +Your bot will call the `parseOption` function to convert slash command arguments into the correct rich type, +and `toSlashOption` to convert `Argument` objects to the correct slash command argument type that Discord should use. +These functions together handle your converter's slash commands workflow, and they should follow this outline: + +- First, define `toSlashOption`, converting the `Argument` to the correct `OptionWrapper` type. + - We recommend starting with the return type, which should be `OptionWrapper` where `T` is one of + [Kord's `OptionValue` subtypes](https://dokka.kord.dev/core/dev.kord.core.entity.interaction/-option-value/index.html). + - Then, call `wrapOption(name, description) { ... }`, setting `required = true` unless you're explicitly creating an + always-optional converter type. + Remember to return the resulting value from this function call! +- Then, create the `parseOption` function, and note that the `option` argument must take an `OptionValue<*>` rather than + a more specific type. + - Start by casting this to the correct + [Kord `OptionValue` subtype](https://dokka.kord.dev/core/dev.kord.core.entity.interaction/-option-value/index.html) + using `as?`, and bail out via `return false` if this cast returns `null`. + - Attempt to parse the value, throwing a `DiscordRelayedException` if you need to return a specific error. + - Return `false` to signal that your converter couldn't parse any data, and respond with a generic error. + - Store the parsed value in `this.parsed`. + - Return `true` to signal that parsing was a success. + +When put together, your code should look something like this: + +```kt +override suspend fun toSlashOption(arg: Argument<*>): OptionWrapper = + wrapOption(arg.displayName, arg.description) { + required = true + } + +override suspend fun parseOption(context: CommandContext, option: OptionValue<*>): Boolean { + val optionValue = (option as? StringOptionValue)?.value ?: return false + + try { + this.parsed = Snowflake(optionValue) + } catch (_: NumberFormatException) { + throw DiscordRelayedException( + CoreTranslations.Converters.Snowflake.Error.invalid + .withContext(context) + .withOrdinalPlaceholders(optionValue) + ) + } + + return true +} +``` + +#### Chat Commands + +Your bot will call the `parse` function to parse chat command arguments into the correct rich type. +This function operates on a stream of string-based tokens, as provided by Kord Extensions' +[tokenising string parser (TODO)](#). + +Your parsing workflow must follow this outline: + +- If `named != null`, use this as your parsing value. + - For single converters, call `parser.parseNext()` to retrieve the next value to parse. + - For list/coalescing converters, you can also use `parser.peekNext()` to retrieve the next value without advancing + the cursor. +- Attempt to parse the value, throwing a `DiscordRelayedException` if you need to return a specific error. + - Return `false` (for single converters), or `0` (for list/coalescing converters), to signal that your converter + couldn't parse any data, and respond with a generic error. +- Store the parsed value in `this.parsed`. + - Return `true` (for single converters`), or the number of arguments your converter parsed + (for list/coalescing converters), to signal that parsing was a success. + +When put together, your code should look something like this: + +```kt +override suspend fun parse(parser: StringParser?, context: CommandContext, named: String?): Boolean { + val arg: String = named // Try the keyword argument first. + ?: parser?.parseNext()?.data // Try the string parser next. + ?: return false // If both are null, bail out. + + try { + this.parsed = Snowflake(arg) + } catch (_: NumberFormatException) { + throw DiscordRelayedException( + CoreTranslations.Converters.Snowflake.Error.invalid + .withContext(context) + .withOrdinalPlaceholders(arg) + ) + } + + return true +} +``` + +### @Converter Annotation + +Now you've written your converter, it is time to generate all the builders and functions your users will need to define +arguments with it. +To do that, you'll need to annotate your class with `@Converter`, and provide the relevant arguments to it. + + + {`@Converter( + "snowflake", + + types = [ConverterType.SINGLE, ConverterType.DEFAULTING, ConverterType.LIST, ConverterType.OPTIONAL] +) +`} + + + +

    + **Required Arguments** +

    + + + "]} required> + Converter names, used to generate the corresponding builder functions. + + You can specify multiple names here to generate multiple sets of builder functions, which might be useful when + dealing with names that may differ in different locales - for example, `colour` and `color`. + + + + Converter types to generate builders and builder functions for, following these rules: + + - You must specify exactly one of `SINGLE` or `COALESCING` - not both. + - Choice converters must also specify `CHOICE`. + - You can also provide any combination of `DEFAULTING`, `LIST` and `OPTIONAL` to generate the corresponding wrapping + converters. + + + +
    + +

    + **Optional Arguments** +

    + + + + Extra imports your converter requires, which will be included (along with the default ones) in all generated files. + + ```kt + imports = [ + "dev.kordex.core.commands.converters.impl.getEnum", + "dev.kordex.core.commands.application.slash.converters.ChoiceEnum", + "java.util.Locale", + ] + ``` + + +
    +
    + + + Arguments added to the generated builder types' constructors, including name, type, visibility modifier and `val`/`var`. + + By default, Kord Extensions will also pass these into your converter's constructor, but you can prefix them with + `!!` to prevent this. + + + {`builderConstructorArguments = [ + "public var getter: suspend (String, Locale) -> E?", + "!! argMap: Map", +] +`} + + + + + Generic type parameter used by the generated builder types. + This may either be full definitions including the names and type bounds, or just names if you provide the type + bounds via `builderSuffixedWhere`. + + + {`// Full definition +builderGeneric = "E: Enum" + +// Name only +builderGeneric = "E", +builderSuffixedWhere = "E : Enum, E : ChoiceEnum" +`} + + + + + Extra properties defined within the generated builder types, including name, type, visibility modifier and + `val`/`var`. + + Required properties should use `lateinit var`. + Otherwise, provide a reasonable default value. + + ```kt + builderFields = [ + // Required properties: + "public lateinit var radix: Int", + + // Optional properties: + "public var maxLength: Int? = null", + "public var minLength: Int? = null", + ] + ``` + + + + Extra type bounds for the generic type parameters defined via `builderGeneric`, provided after `where`. + + + {`builderSuffixedWhere = "T: List<*>"`} + + + +
    +
    + + + Extra lines of code added to generated builder types' `build` functions, **before** constructing the converter + object. + + + + Extra lines of code added to generated builder types' `build` functions, **after** constructing the converter + object. + + + + Extra lines of code added to generated builder types' `init { }` blocks. + + ```kt + builderInitStatements = [ + "choices(argMap)", + ] + ``` + + + + Extra lines of code added to generated builder types, after their `init { }` blocks and fields, but before their + functions. + + + {`builderExtraStatements = [ + "/** Add a channel type to the set of types the given channel must match. **/", + "public fun requireChannelType(type: ChannelType) {", + " requiredChannelTypes.add(type)", + "}" +] +`} + + + +
    +
    + + + Arguments to add to the generated builder functions, passed into the generated builder types' constructors, + including name, and type. + + ```kt + functionBuilderArguments = [ + "getter = ::getEnum", + ] + ``` + + + + Generic type parameter used by the generated builder types. + This may either be full definitions including the names and type bounds, or just names if you provide the type + bounds via `functionSuffixedWhere`. + + + {`// Full definition +functionGeneric = "E: Enum" + +// Name only +functionGeneric = "E", +functionSuffixedWhere = "E : Enum, E : ChoiceEnum" +`} + + + + + Extra type bounds for the generic type parameters defined via `functionGeneric`, provided after `where`. + + + {`functionSuffixedWhere = "T: List<*>"`} + + +
    + + + + The code example below is what would normally be generated for the bundled `Boolean` converter, and it should give + you an idea of where each property inserts code into the generated files. + + + {`package dev.kordex.core.commands.converters.impl + +// Original converter class, for safety +import dev.kordex.core.commands.converters.impl.BooleanConverter + +// Imports that all converters need +import dev.kordex.core.InvalidArgumentException +import dev.kordex.core.annotations.UnexpectedFunctionBehaviour +import dev.kordex.core.commands.Arguments +import dev.kordex.core.commands.converters.* +import dev.kordex.core.commands.converters.builders.* +import dev.kordex.core.i18n.types.* +import dev.kord.common.annotation.KordPreview + +// Converter type params +import kotlin.Boolean + +/** @inject: imports **/ + +/** + * Builder class for boolean converters. Used to construct a converter based on the given options. + * + * @see BooleanConverter + */ +public class BooleanConverterBuilder /** @inject: builderGeneric **/ ( + /** @inject: builderConstructorArguments **/ +) : ConverterBuilder() /** @inject: builderSuffixedWhere **/ { + /** @inject: builderFields **/ + + init { + /** @inject: builderInitStatements **/ + } + + /** @inject: builderExtraStatements **/ + + public override fun build(arguments: Arguments): SingleConverter { + /** @inject: builderBuildFunctionPreStatements **/ + + val converter = BooleanConverter( + validator = validator, + ) + + /** @inject: builderBuildFunctionStatements **/ + + return arguments.arg( + displayName = name, + description = description, + + converter = converter.withBuilder(this) + ) + } +} + +/** + * Converter creation function: boolean single converter + * + * @see BooleanConverterBuilder + */ +public fun /** @inject: functionGeneric **/ Arguments.boolean( + /** @inject: functionBuilderArguments **/ + body: BooleanConverterBuilder.() -> Unit +): SingleConverter /** @inject: functionSuffixedWhere **/ { + val builder = BooleanConverterBuilder( /** @inject: functionBuilderArguments **/ ) + + body(builder) + + builder.validateArgument() + + return builder.build(this) +} +`} + + + +
    + +<> +The annotation processor generates code when you run the `build` task, placing it in your project's `build/` folder, +under `generated/ksp/main/kotlin/`. +Kord Extensions aims to generate well-formatted code, including comments explaining where it injects code, to try to +make everything easier to understand. + +If you need more examples, please take a look at +[the code for the bundled converters](https://github.com/Kord-Extensions/kord-extensions/tree/root/kord-extensions/src/main/kotlin/dev/kordex/core/commands/converters/impl). + +## Usage + +You can use your custom converters just like any of the bundled ones, as described on +[the converter basics page](/bots/commands/converters/basics). +Create an [Arguments subtype](/bots/commands/arguments), define arguments using the corresponding builder functions, +and use it in your command definitions. + +If you're developing a library, your users will also be able to use any custom converters you develop for it. + diff --git a/bots/commands/converters/index.mdx b/bots/commands/converters/index.mdx new file mode 100644 index 00000000..a25eb4a0 --- /dev/null +++ b/bots/commands/converters/index.mdx @@ -0,0 +1,15 @@ +--- +sidebar_position: 2 +--- + +import { DocCardListWithDescriptions } from "/src/theme/DocCardList" + +# Converters + +Converters represent individual command arguments, handling parsing and validation, and converting arguments to rich +types with a type-safe, reflection-free API. + + diff --git a/bots/commands/index.mdx b/bots/commands/index.mdx new file mode 100644 index 00000000..bdf6c02b --- /dev/null +++ b/bots/commands/index.mdx @@ -0,0 +1,17 @@ +--- +sidebar_position: 33 +--- + +import { DocCardListWithDescriptions } from "/src/theme/DocCardList" + +# Commands + +Kord Extensions supports multiple command types and provides a rich argument parsing system that is only as complex +as you need it to be. +See the following pages for more information. + + diff --git a/bots/commands/modals.mdx b/bots/commands/modals.mdx new file mode 100644 index 00000000..dc364cbb --- /dev/null +++ b/bots/commands/modals.mdx @@ -0,0 +1,54 @@ +--- +description: "Modal forms - pop up and collect data." +sidebar_position: 3 + +sidebar_custom_props: + card_group: "Arguments" + divider_below: true + icon: "fluent:window-wrench-24-filled" +--- + +# Modals + +:::note Command Types + +While most command types support modals, [Chat Commands](/bots/commands/chat) don't. +However, chat commands can handle multiple lines of text via [Coalescing Converters](/bots/commands/converters/basics#multiple-inputs). + +::: + +A [Discord Modal](https://discord.com/developers/docs/interactions/receiving-and-responding#interaction-response-object-modal) +is a pop-up modal window containing a set of form components, sent in response to any type of Discord Interaction. +They allow your bot to collect information from users without asking them to type out a bunch of command arguments, +and even support paragraph inputs with text spanning multiple lines. + +Kord Extensions represents modals using the `ModalForm` type, which you'll need to extend to define your own modals: + +```kt +class MyModal : ModalForm() { + override var title: Key = Translations.Modals.myModal + + val line = lineText { + label = "Line of Text" + placeholder = "Enter something..." + } +} +``` + +Once you've created your modal class, provide it to your commands' builder functions. +This will make the modal available via the `modal` argument to your command's `action { }` block. + +```kt +publicSlashCommand(modal = ::MyModal) { + name = "my-command" + + action { modal -> + respond { + content = "Text: ${modal.line.value}" + } + } +} +``` + +This page is just a brief overview of how modals interact with commands. +For more information, see [the relevant page in the Components section](/bots/components/modals). diff --git a/bots/components/basics.mdx b/bots/components/basics.mdx new file mode 100644 index 00000000..57f079d4 --- /dev/null +++ b/bots/components/basics.mdx @@ -0,0 +1,222 @@ +--- +description: "Component basics, limitations, and general information." +sidebar_position: 1 + +sidebar_custom_props: + card_group: "Basics" + icon: "fluent:new-24-filled" + divider_below: true +--- + +import { + MessageContainer, + Message, + ActionRow, + Button, + SelectMenu, + Modal, + ModalRow, + LineInput, ParagraphInput +} from "/src/components/Messages" + +# Basics + +Discord components allow for more advanced user interactions, by adding interactive components to your bot's messages. +Kord Extensions exposes these via its components systems. + +:::info Components v1 + +Kord only supports Components v1, so Kord Extensions also only supports Components v1. +If you'd like to use Components v2, please test and review +[this Kord pull request](https://github.com/kordlib/kord/pull/1009). + +::: + +## Understanding Components + +Each Discord message and modal supports up to five rows of components, with each row spanning up to five units. + + + + Message showing the maximum area you can fill with components. + + +