From 326664858db5d4fc2ab443b0cc476b32a6acf13e Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Tue, 28 Nov 2023 15:26:36 +0800 Subject: [PATCH 01/71] Update README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9e1cdf42..bf90534f 100644 --- a/README.md +++ b/README.md @@ -29,9 +29,8 @@ - 基于 [`Kotlin coroutines`](https://github.com/Kotlin/kotlinx.coroutines) 与 [`Ktor`](https://ktor.io/) 提供高效易用的API; - 基于 [`Kotlin serialization`](https://github.com/Kotlin/kotlinx.serialization) 进行数据序列化/反序列化操作。 -> **Note** -> -> _下文 `Simple Robot v3` 简称为 `simbot3`_ +> [!Note] +> 下文中 `Simple Robot v3` 简称为 `simbot3` ## 文档 From 7ca4e56145633ef55e7f7730d493af73c24ce238 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Sat, 23 Dec 2023 20:34:00 +0800 Subject: [PATCH 02/71] =?UTF-8?q?=E6=9B=B4=E6=96=B0Gradle=E4=B9=8B?= =?UTF-8?q?=E7=B1=BB=E7=9A=84=E4=B8=9C=E8=A5=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/kdoc.yml.bk | 2 +- .github/workflows/publish-release.yml | 8 ++++---- .github/workflows/publish-snapshot.yml | 4 ++-- .github/workflows/test-branch.yml | 2 +- buildSrc/build.gradle.kts | 6 +++--- gradle/wrapper/gradle-wrapper.properties | 2 +- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/kdoc.yml.bk b/.github/workflows/kdoc.yml.bk index 036e9848..bb6c23fd 100644 --- a/.github/workflows/kdoc.yml.bk +++ b/.github/workflows/kdoc.yml.bk @@ -28,7 +28,7 @@ jobs: - name: Gradle generate documentation uses: gradle/gradle-build-action@v2 with: - gradle-version: 7.6 + gradle-version: 8.5 arguments: | -Porg.gradle.jvmargs="-Xmx4g -Xms4g -XX:MaxMetaspaceSize=2g -Dfile.encoding=UTF-8" -Porg.gradle.daemon=false diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index 0909b98e..e9d40e79 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -40,14 +40,14 @@ jobs: - name: Gradle Run Test uses: gradle/gradle-build-action@v2 with: - gradle-version: 7.6 + gradle-version: 8.5 arguments: assemble test -Porg.gradle.daemon=false # setup Gradle - name: Publish Release uses: gradle/gradle-build-action@v2 with: - gradle-version: 7.6 + gradle-version: 8.5 arguments: | publishToSonatype closeAndReleaseStagingRepository @@ -105,7 +105,7 @@ jobs: - name: Gradle publish snapshot uses: gradle/gradle-build-action@v2 with: - gradle-version: 7.6 + gradle-version: 8.5 arguments: | publishToSonatype closeAndReleaseStagingRepository @@ -141,7 +141,7 @@ jobs: - name: Gradle generate documentation uses: gradle/gradle-build-action@v2 with: - gradle-version: 7.6 + gradle-version: 8.5 arguments: | dokkaHtmlMultiModule --info diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml index 17318160..5e279b8d 100644 --- a/.github/workflows/publish-snapshot.yml +++ b/.github/workflows/publish-snapshot.yml @@ -51,7 +51,7 @@ jobs: - name: Gradle test and publish snapshot uses: gradle/gradle-build-action@v2 with: - gradle-version: 7.6 + gradle-version: 8.5 arguments: | test publishToSonatype @@ -83,7 +83,7 @@ jobs: - name: Gradle generate documentation uses: gradle/gradle-build-action@v2 with: - gradle-version: 7.6 + gradle-version: 8.5 arguments: | -Porg.gradle.jvmargs="-Xmx4g -Xms4g -XX:MaxMetaspaceSize=2g -Dfile.encoding=UTF-8" -Porg.gradle.daemon=false diff --git a/.github/workflows/test-branch.yml b/.github/workflows/test-branch.yml index 00496a29..585cb689 100644 --- a/.github/workflows/test-branch.yml +++ b/.github/workflows/test-branch.yml @@ -38,7 +38,7 @@ jobs: - name: Run All Tests uses: gradle/gradle-build-action@v2 with: - gradle-version: 7.6 + gradle-version: 8.5 arguments: | assemble build diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 769c2dc7..209d4681 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -24,9 +24,9 @@ repositories { gradlePluginPortal() } -val kotlinVersion = "1.8.21" -val dokkaPluginVersion = "1.8.10" -val suspendTransformVersion = "0.3.1" +val kotlinVersion = "1.9.21" +val dokkaPluginVersion = "1.9.10" +val suspendTransformVersion = "0.5.1" val gradleCommon = "0.1.1" //val atomicfuVersion = "0.20.0" diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index f42e62f3..a5952066 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists From bf538d6d9f2b8092e2b1a78485c40ceb3dfdce60 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Tue, 26 Dec 2023 15:11:17 +0800 Subject: [PATCH 03/71] =?UTF-8?q?=E6=96=87=E6=A1=A3=E5=9F=9F=E5=90=8D?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- website/docusaurus.config.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index 29e95e67..ccc50b6a 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -21,16 +21,16 @@ async function config() { // Set the production url of your site here // url: 'https://component-qqguild.simbot.forte.love', - url: 'https://simple-robot.github.io', + url: 'http://component-qqguild.simbot.forte.love/', // Set the // pathname under which your site is served // For GitHub pages deployment, it is often '//' - baseUrl: '/simbot-component-qq-guild/', + baseUrl: '/', // GitHub pages deployment config. // If you aren't using GitHub pages, you don't need these. organizationName: 'Simple Robot', // Usually your GitHub org/username. - projectName: 'simbot component tencent guild website', // Usually your repo name. + projectName: 'simbot component qq guild website', // Usually your repo name. onBrokenLinks: 'warn', onBrokenMarkdownLinks: 'warn', From 5a9acab81e1fc5c9a4fc60165a6123177dc05b35 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Tue, 26 Dec 2023 15:16:45 +0800 Subject: [PATCH 04/71] Website CNAME --- website/static/CNAME | 1 + 1 file changed, 1 insertion(+) create mode 100644 website/static/CNAME diff --git a/website/static/CNAME b/website/static/CNAME new file mode 100644 index 00000000..52649d15 --- /dev/null +++ b/website/static/CNAME @@ -0,0 +1 @@ +component-qqguild.simbot.forte.love From e09a71a2e1bce41b3b08a25c50f414ffb2336301 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Sat, 30 Dec 2023 02:13:41 +0800 Subject: [PATCH 05/71] Create qudana.yaml --- qudana.yaml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 qudana.yaml diff --git a/qudana.yaml b/qudana.yaml new file mode 100644 index 00000000..87f204fb --- /dev/null +++ b/qudana.yaml @@ -0,0 +1,31 @@ +#-------------------------------------------------------------------------------# +# Qodana analysis is configured by qodana.yaml file # +# https://www.jetbrains.com/help/qodana/qodana-yaml.html # +#-------------------------------------------------------------------------------# +version: "1.0" + +#Specify inspection profile for code analysis +profile: + name: qodana.starter + +#Enable inspections +#include: +# - name: + +#Disable inspections +#exclude: +# - name: +# paths: +# - + +projectJDK: azul-11 #(Applied in CI/CD pipeline) + +#Execute shell command before Qodana execution (Applied in CI/CD pipeline) +#bootstrap: sh ./prepare-qodana.sh + +#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline) +#plugins: +# - id: #(plugin id can be found at https://plugins.jetbrains.com) + +#Specify Qodana linter for analysis (Applied in CI/CD pipeline) +linter: jetbrains/qodana-jvm-community:latest From 7c3b3cc9aae583e46e1356990ac082dfae2ace8d Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Fri, 12 Jan 2024 22:44:48 +0800 Subject: [PATCH 06/71] =?UTF-8?q?api=20=E6=A8=A1=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 60 ++- buildSrc/build.gradle.kts | 6 +- .../src/main/kotlin/JVMConstants.kt | 14 +- buildSrc/src/main/kotlin/JsConfig.kt | 62 +++ buildSrc/src/main/kotlin/JvmConfig.kt | 100 ++++ buildSrc/src/main/kotlin/P.kt | 12 +- buildSrc/src/main/kotlin/SuspendTransforms.kt | 121 +++-- ...q-guild-dokka-partial-configure.gradle.kts | 11 +- ...ild-multiplatform-maven-publish.gradle.kts | 38 +- ...tcg-suspend-transform-configure.gradle.kts | 38 +- ...encent-guild.dokka-multi-module.gradle.kts | 6 +- ...bot-tencent-guild.maven-publish.gradle.kts | 131 +---- ...encent-guild.module-conventions.gradle.kts | 78 +-- ...bot-tencent-guild.nexus-publish.gradle.kts | 54 +-- ...t-guild.root-module-conventions.gradle.kts | 34 -- gradle/libs.versions.toml | 17 +- settings.gradle.kts | 10 +- .../build.gradle.kts | 192 ++------ .../forte/simbot/qguild/OptAnnotations.kt | 8 +- .../love/forte/simbot/qguild/QQGuild.kt | 28 +- .../simbot/qguild/QQGuildApiException.kt | 11 +- .../forte/simbot/qguild/api/ApiDescription.kt | 21 +- .../forte/simbot/qguild/api/ApiRequestor.kt | 71 --- .../forte/simbot/qguild/api/ApiRequests.kt | 236 +++++++++ .../forte/simbot/qguild/api/GatewayApi.kt | 20 +- .../qguild/api/MessageAuditedException.kt | 6 +- .../forte/simbot/qguild/api/QQGuildApi.kt | 447 +++--------------- .../forte/simbot/qguild/api/Serializers.kt | 18 +- .../api/announces/CreateAnnouncesApi.kt | 19 +- .../api/announces/DeleteAnnouncesApi.kt | 24 +- .../apipermission/DemandApiPermissionApi.kt | 12 +- .../apipermission/GetApiPermissionListApi.kt | 11 +- .../qguild/api/channel/CreateChannelApi.kt | 135 +++++- .../qguild/api/channel/DeleteChannelApi.kt | 33 +- .../qguild/api/channel/GetChannelApi.kt | 11 +- .../api/channel/GetChannelOnlineNumsApi.kt | 36 +- .../api/channel/GetGuildChannelListApi.kt | 12 +- .../qguild/api/channel/ModifyChannelApi.kt | 108 ++++- .../GetChannelMemberPermissionsApi.kt | 11 +- .../GetChannelRolePermissionsApi.kt | 12 +- .../ModifyChannelMemberPermissionsApi.kt | 25 +- .../ModifyChannelRolePermissionsApi.kt | 25 +- .../api/channel/pins/AddPinsMessageApi.kt | 25 +- .../api/channel/pins/DeletePinsMessageApi.kt | 21 +- .../api/channel/pins/GetPinsMessageApi.kt | 21 +- .../channel/schedules/CreateScheduleApi.kt | 13 +- .../channel/schedules/DeleteScheduleApi.kt | 22 +- .../api/channel/schedules/GetScheduleApi.kt | 10 +- .../channel/schedules/GetScheduleListApi.kt | 13 +- .../channel/schedules/ModifyScheduleApi.kt | 23 +- .../qguild/api/forum/DeleteThreadApi.kt | 23 +- .../simbot/qguild/api/forum/GetThreadApi.kt | 11 +- .../qguild/api/forum/GetThreadListApi.kt | 12 +- .../qguild/api/forum/PublishThreadApi.kt | 32 +- .../simbot/qguild/api/guild/GetGuildApi.kt | 22 +- .../qguild/api/guild/mute/MuteAllApi.kt | 49 +- .../qguild/api/guild/mute/MuteMemberApi.kt | 28 +- .../api/guild/mute/MuteMultiMemberApi.kt | 30 +- .../qguild/api/member/DeleteMemberApi.kt | 33 +- .../api/member/GetGuildMemberListApi.kt | 20 +- .../api/member/GetGuildRoleMemberListApi.kt | 15 +- .../simbot/qguild/api/member/GetMemberApi.kt | 11 +- .../qguild/api/message/DeleteMessageApi.kt | 25 +- .../qguild/api/message/GetMessageApi.kt | 11 +- .../qguild/api/message/MessageSendApi.kt | 111 ++--- .../qguild/api/message/QGMessageForSending.kt | 19 +- .../qguild/api/message/direct/CreateDmsApi.kt | 17 +- .../qguild/api/message/direct/DeleteDmsApi.kt | 34 +- .../qguild/api/message/direct/DmsSendApi.kt | 79 +--- .../message/setting/GetMessageSettingApi.kt | 11 +- .../qguild/api/role/AddMemberRoleApi.kt | 31 +- .../qguild/api/role/CreateGuildRoleApi.kt | 15 +- .../qguild/api/role/DeleteGuildRoleApi.kt | 30 +- .../qguild/api/role/GetGuildRoleListApi.kt | 23 +- .../qguild/api/role/ModifyGuildRoleApi.kt | 26 +- .../qguild/api/role/RemoveMemberRoleApi.kt | 39 +- .../qguild/api/user/GetBotGuildListApi.kt | 38 +- .../simbot/qguild/api/user/GetBotInfoApi.kt | 11 +- .../love/forte/simbot/qguild/event/Signal.kt | 6 +- .../forte/simbot/qguild/model/Announces.kt | 6 +- .../love/forte/simbot/qguild/time/Instants.kt | 3 +- .../love/forte/simbot/qguild/time/TimeUnit.kt | 70 --- .../src/commonTest/kotlin/TimeUnitTest.kt | 15 - .../forte/simbot/qguild/api/QQGuildApi.js.kt | 152 +++--- .../qguild/api/message/resolveOther.js.kt | 8 +- .../forte/simbot/qguild/time/TimeUnit.js.kt | 172 ------- .../simbot/qguild/api/ApiRequestor.jvm.kt | 104 ---- .../simbot/qguild/api/ApiRequests.jvm.kt | 69 +++ .../forte/simbot/qguild/api/QQGuildApi.jvm.kt | 156 +----- .../qguild/api/message/resolveOther.jvm.kt | 6 +- .../simbot/qguild/api/QQGuildApi.native.kt | 48 +- .../qguild/api/message/resolveOther.native.kt | 6 +- .../forte/simbot/qguild/initCause.native.kt | 4 +- .../simbot/qguild/time/TimeUnit.native.kt | 172 ------- 94 files changed, 1590 insertions(+), 2685 deletions(-) rename simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/time/TimeUnit.jvm.kt => buildSrc/src/main/kotlin/JVMConstants.kt (76%) create mode 100644 buildSrc/src/main/kotlin/JsConfig.kt create mode 100644 buildSrc/src/main/kotlin/JvmConfig.kt delete mode 100644 buildSrc/src/main/kotlin/simbot-tencent-guild.root-module-conventions.gradle.kts delete mode 100644 simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequestor.kt create mode 100644 simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt delete mode 100644 simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/time/TimeUnit.kt delete mode 100644 simbot-component-qq-guild-api/src/commonTest/kotlin/TimeUnitTest.kt delete mode 100644 simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/time/TimeUnit.js.kt delete mode 100644 simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequestor.jvm.kt create mode 100644 simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt delete mode 100644 simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/time/TimeUnit.native.kt diff --git a/build.gradle.kts b/build.gradle.kts index ea5966c2..3f40c225 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,3 +1,22 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +import love.forte.gradle.common.core.repository.Repositories + /* * Copyright (c) 2022-2023. ForteScarlet. * @@ -16,9 +35,9 @@ */ plugins { + idea id("simbot-tencent-guild.changelog-generator") id("simbot-tencent-guild.dokka-multi-module") - id("simbot-tencent-guild.root-module-conventions") id("simbot-tencent-guild.nexus-publish") } @@ -26,10 +45,9 @@ buildscript { repositories { mavenCentral() } - - dependencies { - classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:${libs.versions.atomicfu.get()}") - } +// dependencies { +// classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:${libs.versions.atomicfu.get()}") +// } } //group = P.ComponentTencentGuild.GROUP @@ -37,15 +55,35 @@ buildscript { logger.info("=== Current version: {} ===", version) -subprojects { +allprojects { repositories { mavenCentral() - love.forte.gradle.common.core.repository.Repositories.Snapshot.Default.apply { - configMaven { - mavenContent { - snapshotsOnly() - } + maven { + url = uri(Repositories.Snapshot.URL) + mavenContent { + snapshotsOnly() } } + mavenLocal() } } + +idea { + module.apply { + isDownloadSources = true + isDownloadJavadoc = true + } + project { + modules.forEach { module -> + module.apply { + isDownloadSources = true + isDownloadJavadoc = true + } + } + } +} + +configurations.all { + // check for updates every build + resolutionStrategy.cacheChangingModulesFor(0, "seconds") +} diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 209d4681..706be426 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023. ForteScarlet. + * Copyright (c) 2021-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -26,8 +26,8 @@ repositories { val kotlinVersion = "1.9.21" val dokkaPluginVersion = "1.9.10" -val suspendTransformVersion = "0.5.1" -val gradleCommon = "0.1.1" +val suspendTransformVersion = "0.6.0-beta3" +val gradleCommon = "0.2.0" //val atomicfuVersion = "0.20.0" dependencies { diff --git a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/time/TimeUnit.jvm.kt b/buildSrc/src/main/kotlin/JVMConstants.kt similarity index 76% rename from simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/time/TimeUnit.jvm.kt rename to buildSrc/src/main/kotlin/JVMConstants.kt index 13374685..a1daba86 100644 --- a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/time/TimeUnit.jvm.kt +++ b/buildSrc/src/main/kotlin/JVMConstants.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,11 +15,7 @@ * If not, see . */ -package love.forte.simbot.qguild.time - -/** - * 时间单位,用于时间转化。 - * - * @see java.util.concurrent.TimeUnit - */ -public actual typealias TimeUnit = java.util.concurrent.TimeUnit +object JVMConstants { + const val KT_JVM_TARGET_VALUE = 11 + const val KT_JVM_TARGET = "11" +} diff --git a/buildSrc/src/main/kotlin/JsConfig.kt b/buildSrc/src/main/kotlin/JsConfig.kt new file mode 100644 index 00000000..0c258f4a --- /dev/null +++ b/buildSrc/src/main/kotlin/JsConfig.kt @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.targets.js.dsl.KotlinJsTargetDsl + + +inline fun KotlinJsTargetDsl.configJs( + nodeJs: Boolean = true, + browser: Boolean = true, + block: () -> Unit = {} +) { + if (nodeJs) { + nodejs { + testTask { + useMocha { + timeout = "10000" + } + } + } + } + + if (browser) { + browser { + testTask{ + useKarma { + useChromeHeadless() + // useConfigDirectory(File(project.rootProject.projectDir, "karma")) + } + } + } + } + + binaries.library() + block() +} + + +fun Project.configJsTestTasks() { + // val shouldRunJsBrowserTest = !hasProperty("teamcity") || hasProperty("enable-js-tests") + // if (shouldRunJsBrowserTest) return + tasks.findByName("cleanJsBrowserTest")?.apply { + onlyIf { false } + } + tasks.findByName("jsBrowserTest")?.apply { + onlyIf { false } + } +} diff --git a/buildSrc/src/main/kotlin/JvmConfig.kt b/buildSrc/src/main/kotlin/JvmConfig.kt new file mode 100644 index 00000000..434c9f88 --- /dev/null +++ b/buildSrc/src/main/kotlin/JvmConfig.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +import org.gradle.api.Project +import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.compile.JavaCompile +import org.gradle.kotlin.dsl.assign +import org.gradle.kotlin.dsl.get +import org.gradle.kotlin.dsl.getByName +import org.gradle.kotlin.dsl.withType +import org.gradle.process.CommandLineArgumentProvider +import org.jetbrains.kotlin.gradle.dsl.JvmTarget +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension +import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget + + +inline fun KotlinJvmTarget.configJava(crossinline block: KotlinJvmTarget.() -> Unit = {}) { + withJava() + compilations.all { + kotlinOptions { + javaParameters = true + freeCompilerArgs = freeCompilerArgs + listOf("-Xjvm-default=all") + } + } + + testRuns["test"].executionTask.configure { + useJUnitPlatform() + } + block() +} + + +fun KotlinTopLevelExtension.configJavaToolchain(jdkVersion: Int) { + jvmToolchain(jdkVersion) +} + +inline fun KotlinMultiplatformExtension.configKotlinJvm( + jdkVersion: Int = JVMConstants.KT_JVM_TARGET_VALUE, + crossinline block: KotlinJvmTarget.() -> Unit = {} +) { + configJavaToolchain(jdkVersion) + jvm { + configJava(block) + } +} + +inline fun KotlinJvmProjectExtension.configKotlinJvm( + jdkVersion: Int = JVMConstants.KT_JVM_TARGET_VALUE, + crossinline block: KotlinJvmProjectExtension.() -> Unit = {} +) { + configJavaToolchain(jdkVersion) + compilerOptions { + javaParameters = true + jvmTarget.set(JvmTarget.fromTarget(jdkVersion.toString())) + // freeCompilerArgs.addAll("-Xjvm-default=all", "-Xjsr305=strict") + freeCompilerArgs.set(freeCompilerArgs.getOrElse(emptyList()) + listOf("-Xjvm-default=all", "-Xjsr305=strict")) + } + block() +} + +inline fun Project.configJavaCompileWithModule( + moduleName: String? = null, + jvmVersion: String = JVMConstants.KT_JVM_TARGET, + crossinline block: JavaCompile.() -> Unit = {} +) { + tasks.withType { + options.encoding = "UTF-8" + sourceCompatibility = jvmVersion + targetCompatibility = jvmVersion + + if (moduleName != null) { + options.compilerArgumentProviders.add(CommandLineArgumentProvider { + // Provide compiled Kotlin classes to javac – needed for Java/Kotlin mixed sources to work + listOf("--patch-module", "$moduleName=${sourceSets["main"].output.asPath}") + }) + } + + block() + } +} + +@PublishedApi +internal val Project.sourceSets: SourceSetContainer + get() = extensions.getByName("sourceSets") diff --git a/buildSrc/src/main/kotlin/P.kt b/buildSrc/src/main/kotlin/P.kt index 9e59f977..e1dc21ed 100644 --- a/buildSrc/src/main/kotlin/P.kt +++ b/buildSrc/src/main/kotlin/P.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -18,6 +18,7 @@ import love.forte.gradle.common.core.project.ProjectDetail import love.forte.gradle.common.core.project.Version import love.forte.gradle.common.core.project.minus +import love.forte.gradle.common.core.property.systemProp import love.forte.gradle.common.core.project.version as v val simbotVersion = v(3, 2, 0) @@ -58,15 +59,12 @@ object P { override val homepage: String get() = HOMEPAGE - private val baseVersion = v( - "${simbotVersion.major}.${simbotVersion.minor}", - 0, 0 - ) + private val baseVersion = v(4, 0, 0) - v("dev1") //private val alphaSuffix = v("beta", 2) - override val version = baseVersion // - alphaSuffix val snapshotVersion = baseVersion - Version.SNAPSHOT + override val version = if (isSnapshot()) snapshotVersion else baseVersion val versionIfSnap get() = (if (isSnapshot()) snapshotVersion else version).toString() @@ -118,3 +116,5 @@ private fun initIsSnapshot(): Boolean { } fun isSnapshot(): Boolean = _isSnapshot + +fun isSimbotLocal(): Boolean = systemProp("SIMBOT_LOCAL").toBoolean() diff --git a/buildSrc/src/main/kotlin/SuspendTransforms.kt b/buildSrc/src/main/kotlin/SuspendTransforms.kt index 09051ee4..5d70e035 100644 --- a/buildSrc/src/main/kotlin/SuspendTransforms.kt +++ b/buildSrc/src/main/kotlin/SuspendTransforms.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,60 +19,116 @@ import love.forte.plugin.suspendtrans.* object SuspendTransforms { - private val includeAnnotationApi4JClassInfo = ClassInfo("love.forte.simbot", "Api4J") - private val includeAnnotationApi4J = IncludeAnnotation(includeAnnotationApi4JClassInfo) - private val includeAnnotations = listOf(includeAnnotationApi4J) - + private val javaIncludeAnnotationApi4JClassInfo = ClassInfo("love.forte.simbot.annotations", "Api4J") + private val javaIncludeAnnotationApi4J = IncludeAnnotation(javaIncludeAnnotationApi4JClassInfo) + private val javaIncludeAnnotations = listOf(javaIncludeAnnotationApi4J) + + private val jsIncludeAnnotationApi4JsClassInfo = ClassInfo("love.forte.simbot.annotations", "Api4Js") + private val jsIncludeAnnotationApi4Js = IncludeAnnotation(jsIncludeAnnotationApi4JsClassInfo) + private val jsIncludeAnnotations = listOf(jsIncludeAnnotationApi4Js) + + + private val SuspendReserveClassInfo = ClassInfo( + packageName = "love.forte.simbot.suspendrunner.reserve", + className = "SuspendReserve", + + ) + /** * JvmBlocking */ val jvmBlockingTransformer = SuspendTransformConfiguration.jvmBlockingTransformer.copy( - syntheticFunctionIncludeAnnotations = includeAnnotations, - transformFunctionInfo = FunctionInfo("love.forte.simbot.utils", null, "$\$runInBlocking"), + syntheticFunctionIncludeAnnotations = javaIncludeAnnotations, + transformFunctionInfo = FunctionInfo("love.forte.simbot.suspendrunner", null, "$\$runInBlocking"), copyAnnotationExcludes = SuspendTransformConfiguration.jvmBlockingTransformer.copyAnnotationExcludes + SuspendTransformConfiguration.jvmBlockingTransformer.markAnnotation.classInfo ) - + /** * JvmAsync */ val jvmAsyncTransformer = SuspendTransformConfiguration.jvmAsyncTransformer.copy( - syntheticFunctionIncludeAnnotations = includeAnnotations, - transformFunctionInfo = FunctionInfo("love.forte.simbot.utils", null, "$\$runInAsync1"), + syntheticFunctionIncludeAnnotations = javaIncludeAnnotations, + transformFunctionInfo = FunctionInfo("love.forte.simbot.suspendrunner", null, "$\$runInAsyncNullable"), copyAnnotationExcludes = SuspendTransformConfiguration.jvmAsyncTransformer.copyAnnotationExcludes + SuspendTransformConfiguration.jvmAsyncTransformer.markAnnotation.classInfo ) - + + /** + * JvmReserve + */ + val jvmReserveTransformer = SuspendTransformConfiguration.jvmAsyncTransformer.copy( + syntheticFunctionIncludeAnnotations = javaIncludeAnnotations, + transformFunctionInfo = FunctionInfo("love.forte.simbot.suspendrunner", null, "$\$asReserve"), + copyAnnotationExcludes = SuspendTransformConfiguration.jvmAsyncTransformer.copyAnnotationExcludes + SuspendTransformConfiguration.jvmAsyncTransformer.markAnnotation.classInfo, + transformReturnType = SuspendReserveClassInfo, + transformReturnTypeGeneric = true, + ) + + /** + * JsPromise + */ + val jsPromiseTransformer = SuspendTransformConfiguration.jsPromiseTransformer.copy( + syntheticFunctionIncludeAnnotations = javaIncludeAnnotations, + transformFunctionInfo = FunctionInfo("love.forte.simbot.suspendrunner", null, "$\$runInPromise"), + copyAnnotationExcludes = SuspendTransformConfiguration.jsPromiseTransformer.copyAnnotationExcludes + SuspendTransformConfiguration.jsPromiseTransformer.markAnnotation.classInfo, + ) + //region @JvmSuspendTrans - private val jvmSuspendTransMarkAnnotationClassInfo = ClassInfo("love.forte.simbot", "JvmSuspendTrans") - + private val suspendTransMarkAnnotationClassInfo = ClassInfo("love.forte.simbot.suspendrunner", "SuspendTrans") + private val jvmSuspendTransMarkAnnotationForBlocking = MarkAnnotation( - jvmSuspendTransMarkAnnotationClassInfo, + suspendTransMarkAnnotationClassInfo, baseNameProperty = "blockingBaseName", suffixProperty = "blockingSuffix", asPropertyProperty = "blockingAsProperty", defaultSuffix = SuspendTransformConfiguration.jvmBlockingAnnotationInfo.defaultSuffix, ) private val jvmSuspendTransMarkAnnotationForAsync = MarkAnnotation( - jvmSuspendTransMarkAnnotationClassInfo, + suspendTransMarkAnnotationClassInfo, baseNameProperty = "asyncBaseName", suffixProperty = "asyncSuffix", asPropertyProperty = "asyncAsProperty", defaultSuffix = SuspendTransformConfiguration.jvmAsyncAnnotationInfo.defaultSuffix, ) - - val jvmSuspendTransTransformerForBlocking = jvmBlockingTransformer.copy( + private val jvmSuspendTransMarkAnnotationForReserve = MarkAnnotation( + suspendTransMarkAnnotationClassInfo, + baseNameProperty = "reserveBaseName", + suffixProperty = "reserveSuffix", + asPropertyProperty = "reserveAsProperty", + defaultSuffix = "Reserve", + ) + private val jsSuspendTransMarkAnnotationForPromise = MarkAnnotation( + suspendTransMarkAnnotationClassInfo, + baseNameProperty = "jsPromiseBaseName", + suffixProperty = "jsPromiseSuffix", + asPropertyProperty = "jsPromiseAsProperty", + defaultSuffix = "Async", + ) + + val suspendTransTransformerForJvmBlocking = jvmBlockingTransformer.copy( markAnnotation = jvmSuspendTransMarkAnnotationForBlocking, copyAnnotationExcludes = SuspendTransformConfiguration.jvmBlockingTransformer.copyAnnotationExcludes + jvmSuspendTransMarkAnnotationForBlocking.classInfo ) - - val jvmSuspendTransTransformerForAsync = jvmAsyncTransformer.copy( + + val suspendTransTransformerForJvmAsync = jvmAsyncTransformer.copy( markAnnotation = jvmSuspendTransMarkAnnotationForAsync, copyAnnotationExcludes = SuspendTransformConfiguration.jvmAsyncTransformer.copyAnnotationExcludes + jvmSuspendTransMarkAnnotationForAsync.classInfo ) + + val suspendTransTransformerForJvmReserve = jvmReserveTransformer.copy( + markAnnotation = jvmSuspendTransMarkAnnotationForReserve, + copyAnnotationExcludes = jvmReserveTransformer.copyAnnotationExcludes + jvmSuspendTransMarkAnnotationForReserve.classInfo, + ) + + val suspendTransTransformerForJsPromise = jsPromiseTransformer.copy( + markAnnotation = jvmSuspendTransMarkAnnotationForReserve, + copyAnnotationExcludes = jsPromiseTransformer.copyAnnotationExcludes + jsSuspendTransMarkAnnotationForPromise.classInfo, + ) //endregion - + //region @JvmSuspendTransProperty - private val jvmSuspendTransPropMarkAnnotationClassInfo = ClassInfo("love.forte.simbot", "JvmSuspendTransProperty") - + private val jvmSuspendTransPropMarkAnnotationClassInfo = + ClassInfo("love.forte.simbot.suspendrunner", "SuspendTransProperty") + private val jvmSuspendTransPropMarkAnnotationForBlocking = MarkAnnotation( jvmSuspendTransPropMarkAnnotationClassInfo, baseNameProperty = "blockingBaseName", @@ -89,20 +145,29 @@ object SuspendTransforms { defaultSuffix = SuspendTransformConfiguration.jvmAsyncAnnotationInfo.defaultSuffix, defaultAsProperty = true ) - + private val jvmSuspendTransPropMarkAnnotationForReserve = MarkAnnotation( + jvmSuspendTransPropMarkAnnotationClassInfo, + baseNameProperty = "reserveBaseName", + suffixProperty = "reserveSuffix", + asPropertyProperty = "reserveAsProperty", + defaultSuffix = "Reserve", + defaultAsProperty = true + ) + val jvmSuspendTransPropTransformerForBlocking = jvmBlockingTransformer.copy( markAnnotation = jvmSuspendTransPropMarkAnnotationForBlocking, copyAnnotationExcludes = SuspendTransformConfiguration.jvmBlockingTransformer.copyAnnotationExcludes + jvmSuspendTransPropMarkAnnotationForBlocking.classInfo ) - + val jvmSuspendTransPropTransformerForAsync = jvmAsyncTransformer.copy( markAnnotation = jvmSuspendTransPropMarkAnnotationForAsync, copyAnnotationExcludes = SuspendTransformConfiguration.jvmAsyncTransformer.copyAnnotationExcludes + jvmSuspendTransPropMarkAnnotationForAsync.classInfo ) + + val jvmSuspendTransPropTransformerForReserve = jvmReserveTransformer.copy( + markAnnotation = jvmSuspendTransPropMarkAnnotationForReserve, + copyAnnotationExcludes = jvmReserveTransformer.copyAnnotationExcludes + jvmSuspendTransPropMarkAnnotationForReserve.classInfo + ) //endregion - } - - - diff --git a/buildSrc/src/main/kotlin/qq-guild-dokka-partial-configure.gradle.kts b/buildSrc/src/main/kotlin/qq-guild-dokka-partial-configure.gradle.kts index dfbcfe46..6670c383 100644 --- a/buildSrc/src/main/kotlin/qq-guild-dokka-partial-configure.gradle.kts +++ b/buildSrc/src/main/kotlin/qq-guild-dokka-partial-configure.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,7 +15,6 @@ * If not, see . */ -import org.gradle.kotlin.dsl.withType import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.gradle.DokkaTaskPartial import java.net.URL @@ -50,10 +49,10 @@ tasks.withType().configureEach { } // samples - samples.from( - project.files(), - project.files("src/samples"), - ) +// samples.from( +// project.files(), +// project.files("src/samples"), +// ) sourceLink { localDirectory.set(projectDir.resolve("src")) diff --git a/buildSrc/src/main/kotlin/qq-guild-multiplatform-maven-publish.gradle.kts b/buildSrc/src/main/kotlin/qq-guild-multiplatform-maven-publish.gradle.kts index c4217d50..ab499aa3 100644 --- a/buildSrc/src/main/kotlin/qq-guild-multiplatform-maven-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/qq-guild-multiplatform-maven-publish.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,53 +19,46 @@ import love.forte.gradle.common.core.Gpg import love.forte.gradle.common.core.project.setup import love.forte.gradle.common.core.property.systemProp import love.forte.gradle.common.publication.configure.multiplatformConfigPublishing -import org.jetbrains.kotlin.konan.target.HostManager -import org.jetbrains.kotlin.konan.target.KonanTarget plugins { - kotlin("multiplatform") signing `maven-publish` } -tasks.withType { - sourceCompatibility = "1.8" - targetCompatibility = "1.8" - options.encoding = "UTF-8" -} setup(P.ComponentQQGuild) -if (isSnapshot()) { - version = P.ComponentQQGuild.snapshotVersion.toString() -} val p = project multiplatformConfigPublishing { project = P.ComponentQQGuild + isSnapshot = project.version.toString().contains("SNAPSHOT", true) val jarJavadoc by tasks.registering(Jar::class) { group = "documentation" archiveClassifier.set("javadoc") - from(tasks.findByName("dokkaHtml")) + if (!(isSnapshot || isSnapshot() || isSimbotLocal())) { + archiveClassifier.set("javadoc") + from(tasks.findByName("dokkaHtml")) + } } + artifact(jarJavadoc) - isSnapshot = project.version.toString().contains("SNAPSHOT", true) releasesRepository = ReleaseRepository snapshotRepository = SnapshotRepository gpg = Gpg.ofSystemPropOrNull() - if (systemProp("SIMBOT_LOCAL").toBoolean()) { + if (isSimbotLocal()) { mainHost = null } -// else { -// -// mainHostSupportedTargets = mainHost?.supports(hostManager) ?: emptySet() -// } + publicationsFromMainHost += listOf("wasm", "wasm32", "wasm_js") + mainHostSupportedTargets += listOf("wasm", "wasm32", "wasm_js") } -fun KonanTarget.supports(hostManager: HostManager): Set { - return hostManager.enabledByHost[this]?.mapTo(mutableSetOf()) { target -> target.name } ?: emptySet() +// TODO see https://github.com/gradle-nexus/publish-plugin/issues/208#issuecomment-1465029831 +val signingTasks: TaskCollection = tasks.withType() +tasks.withType().configureEach { + mustRunAfter(signingTasks) } show() @@ -89,3 +82,6 @@ fun show() { inline val Project.sourceSets: SourceSetContainer get() = extensions.getByName("sourceSets") as SourceSetContainer + +internal val TaskContainer.dokkaHtml: TaskProvider + get() = named("dokkaHtml") diff --git a/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts b/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts index a996744a..d8b405cc 100644 --- a/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts +++ b/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,43 +15,31 @@ * If not, see . */ -import love.forte.plugin.suspendtrans.SuspendTransformConfiguration - -/* - * Copyright (c) 2022 ForteScarlet - * - * 本文件是 simply-robot (或称 simple-robot 3.x 、simbot 3.x ) 的一部分。 - * - * simply-robot 是自由软件:你可以再分发之和/或依照由自由软件基金会发布的 GNU 通用公共许可证修改之,无论是版本 3 许可证,还是(按你的决定)任何以后版都可以。 - * - * 发布 simply-robot 是希望它能有用,但是并无保障;甚至连可销售和符合某个特定的目的都不保证。请参看 GNU 通用公共许可证,了解详情。 - * - * 你应该随程序获得一份 GNU 通用公共许可证的复本。如果没有,请看: - * https://www.gnu.org/licenses - * https://www.gnu.org/licenses/gpl-3.0-standalone.html - * https://www.gnu.org/licenses/lgpl-3.0-standalone.html - * - */ - plugins { id("love.forte.plugin.suspend-transform") } suspendTransform { includeRuntime = false - + addJvmTransformers( // @JvmBlocking SuspendTransforms.jvmBlockingTransformer, // @JvmAsync SuspendTransforms.jvmAsyncTransformer, - + // @JvmSuspendTrans - SuspendTransforms.jvmSuspendTransTransformerForBlocking, - SuspendTransforms.jvmSuspendTransTransformerForAsync, - + SuspendTransforms.suspendTransTransformerForJvmBlocking, + SuspendTransforms.suspendTransTransformerForJvmAsync, + SuspendTransforms.suspendTransTransformerForJvmReserve, + // @JvmSuspendTransProperty SuspendTransforms.jvmSuspendTransPropTransformerForBlocking, - SuspendTransforms.jvmSuspendTransPropTransformerForAsync + SuspendTransforms.jvmSuspendTransPropTransformerForAsync, + SuspendTransforms.jvmSuspendTransPropTransformerForReserve, ) + + // addJsTransformers( + // SuspendTransforms.suspendTransTransformerForJsPromise, + // ) } diff --git a/buildSrc/src/main/kotlin/simbot-tencent-guild.dokka-multi-module.gradle.kts b/buildSrc/src/main/kotlin/simbot-tencent-guild.dokka-multi-module.gradle.kts index bbd3983f..676781c7 100644 --- a/buildSrc/src/main/kotlin/simbot-tencent-guild.dokka-multi-module.gradle.kts +++ b/buildSrc/src/main/kotlin/simbot-tencent-guild.dokka-multi-module.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,8 +17,6 @@ import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.DokkaBaseConfiguration -import org.gradle.api.plugins.JavaBasePlugin -import java.io.File import java.time.Year plugins { @@ -30,7 +28,7 @@ repositories { } fun org.jetbrains.dokka.gradle.AbstractDokkaTask.configOutput(format: String) { - moduleName.set("Simple Robot Component Tencent Guild") + moduleName.set("Simple Robot Component | QQ Guild") outputDirectory.set(rootProject.file("build/dokka/$format")) } diff --git a/buildSrc/src/main/kotlin/simbot-tencent-guild.maven-publish.gradle.kts b/buildSrc/src/main/kotlin/simbot-tencent-guild.maven-publish.gradle.kts index 1724bd01..feed9fc9 100644 --- a/buildSrc/src/main/kotlin/simbot-tencent-guild.maven-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/simbot-tencent-guild.maven-publish.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -30,145 +30,50 @@ plugins { val (isSnapshotOnly, isReleaseOnly, isPublishConfigurable) = checkPublishConfigurable() -logger.info("isSnapshotOnly: $isSnapshotOnly") -logger.info("isReleaseOnly: $isReleaseOnly") -logger.info("isPublishConfigurable: $isPublishConfigurable") - - +logger.info("isSnapshotOnly: {}", isSnapshotOnly) +logger.info("isReleaseOnly: {}", isReleaseOnly) +logger.info("isPublishConfigurable: {}", isPublishConfigurable) if (!isCi || isLinux) { checkPublishConfigurable { jvmConfigPublishing { project = P.ComponentQQGuild - publicationName = "tencentGuildDist" + publicationName = "QQGuildDist" + isSnapshot = isSnapshot().also { + logger.info("jvmConfigPublishing.isSnapshot: {}", it) + } + val jarSources by tasks.registering(Jar::class) { archiveClassifier.set("sources") from(sourceSets["main"].allSource) } val jarJavadoc by tasks.registering(Jar::class) { + if (!(isSnapshot || isSnapshot())) { + dependsOn(tasks.dokkaHtml) + from(tasks.dokkaHtml.flatMap { it.outputDirectory }) + } archiveClassifier.set("javadoc") } artifact(jarSources) artifact(jarJavadoc) - isSnapshot = isSnapshot().also { - logger.info("jvmConfigPublishing.isSnapshot: {}", it) - } releasesRepository = ReleaseRepository snapshotRepository = SnapshotRepository - gpg = if (isSnapshot()) null else Gpg.ofSystemPropOrNull() + gpg = Gpg.ofSystemPropOrNull() } - - if (isSnapshot()) { - publishing { - publications.withType { - version = P.ComponentQQGuild.snapshotVersion.toString() - } - } - } - - publishing { - publications.withType { - show() - } - } - - } -} - - -// -//if (isPublishConfigurable) { -// val sonatypeUsername: String? = systemProp("OSSRH_USER") -// val sonatypePassword: String? = systemProp("OSSRH_PASSWORD") -// -// if (sonatypeUsername == null || sonatypePassword == null) { -// println("[WARN] - sonatype.username or sonatype.password is null, cannot config nexus publishing.") -// } -// -// val jarSources by tasks.registering(Jar::class) { -// archiveClassifier.set("sources") -// from(sourceSets["main"].allSource) -// } -// -// val jarJavadoc by tasks.registering(Jar::class) { -// archiveClassifier.set("javadoc") -// } -// -// publishing { -// publications { -// create("tencentGuildDist") { -// from(components["java"]) -// artifact(jarSources) -// artifact(jarJavadoc) -// -// groupId = project.group.toString() -// artifactId = project.name -// version = project.version.toString() -// description = project.description?.toString() ?: P.ComponentTencentGuild.DESCRIPTION -// -// pom { -// show() -// -// name.set("${project.group}:${project.name}") -// description.set(project.description?.toString() ?: P.ComponentTencentGuild.DESCRIPTION) -// url.set("https://github.com/simple-robot/simbot-component-qq-guild") -// licenses { -// license { -// name.set("GNU GENERAL PUBLIC LICENSE, Version 3") -// url.set("https://www.gnu.org/licenses/gpl-3.0-standalone.html") -// } -// license { -// name.set("GNU LESSER GENERAL PUBLIC LICENSE, Version 3") -// url.set("https://www.gnu.org/licenses/lgpl-3.0-standalone.html") -// } -// } -// scm { -// url.set("https://github.com/simple-robot/simbot-component-qq-guild") -// connection.set("scm:git:https://github.com/simple-robot/simbot-component-qq-guild.git") -// developerConnection.set("scm:git:ssh://git@github.com/simple-robot/simbot-component-tencent-guild.git") -// } -// -// setupDevelopers() -// } -// } -// -// -// -// repositories { -// configMaven(Sonatype.Central, sonatypeUsername, sonatypePassword) -// configMaven(Sonatype.Snapshot, sonatypeUsername, sonatypePassword) -// } -// } -// } -// -// -// signing { -// val keyId = System.getenv("GPG_KEY_ID") -// val secretKey = System.getenv("GPG_SECRET_KEY") -// val password = System.getenv("GPG_PASSWORD") -// -// setRequired { -// !project.version.toString().endsWith("SNAPSHOT") -// } -// -// useInMemoryPgpKeys(keyId, secretKey, password) -// -// sign(publishing.publications["tencentGuildDist"]) -// } -// -// -// println("[publishing-configure] - [$name] configured.") -//} + show() +} inline val Project.sourceSets: SourceSetContainer get() = extensions.getByName("sourceSets") as SourceSetContainer +internal val TaskContainer.dokkaHtml: TaskProvider + get() = named("dokkaHtml") fun Project.show() { //// show project info diff --git a/buildSrc/src/main/kotlin/simbot-tencent-guild.module-conventions.gradle.kts b/buildSrc/src/main/kotlin/simbot-tencent-guild.module-conventions.gradle.kts index 5123f125..a725312d 100644 --- a/buildSrc/src/main/kotlin/simbot-tencent-guild.module-conventions.gradle.kts +++ b/buildSrc/src/main/kotlin/simbot-tencent-guild.module-conventions.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,69 +15,27 @@ * If not, see . */ import love.forte.gradle.common.core.project.setup -import love.forte.gradle.common.core.repository.Repositories -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.gradle.DokkaTaskPartial -import java.net.URL plugins { - `java-library` - kotlin("jvm") - kotlin("plugin.serialization") idea } setup(P.ComponentQQGuild) -if (isSnapshot()) { - version = P.ComponentQQGuild.snapshotVersion.toString() -} - -repositories { - mavenCentral() - maven { - url = uri(Repositories.Snapshot.URL) - mavenContent { - snapshotsOnly() - } - } -} - -dependencies { - testImplementation(kotlin("test-junit5")) -} - - -tasks.getByName("test") { - useJUnitPlatform() -} - -tasks.withType().configureEach { - kotlinOptions { - javaParameters = true - jvmTarget = "1.8" - freeCompilerArgs = freeCompilerArgs + listOf("-Xjvm-default=all") - } -} - -tasks.withType { - sourceCompatibility = "1.8" - targetCompatibility = "1.8" - options.encoding = "UTF-8" -} - -kotlin { - explicitApi() - sourceSets.configureEach { - languageSettings { - optIn("kotlin.RequiresOptIn") - } - } - sourceSets.getByName("test").kotlin { - srcDir("src/samples") - } -} +// +//kotlin { +// explicitApi() +// sourceSets.configureEach { +// languageSettings { +// optIn("kotlin.RequiresOptIn") +// } +// } +// +// sourceSets.getByName("test").kotlin { +// srcDir("src/samples") +// } +//} configurations.all { // check for updates every build @@ -91,11 +49,3 @@ idea { isDownloadJavadoc = true } } - - -//// show project info -logger.info("========================================================") -logger.info("== project.group: ${group}") -logger.info("== project.name: ${name}") -logger.info("== project.version: ${version}") -logger.info("========================================================") diff --git a/buildSrc/src/main/kotlin/simbot-tencent-guild.nexus-publish.gradle.kts b/buildSrc/src/main/kotlin/simbot-tencent-guild.nexus-publish.gradle.kts index 109405d5..f93b89cb 100644 --- a/buildSrc/src/main/kotlin/simbot-tencent-guild.nexus-publish.gradle.kts +++ b/buildSrc/src/main/kotlin/simbot-tencent-guild.nexus-publish.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -18,8 +18,6 @@ import love.forte.gradle.common.core.project.setup import love.forte.gradle.common.core.repository.Repositories import love.forte.gradle.common.publication.configure.nexusPublishConfig -import util.checkPublishConfigurable -import java.time.Duration plugins { id("io.github.gradle-nexus.publish-plugin") @@ -27,16 +25,6 @@ plugins { setup(P.ComponentQQGuild) -if (isSnapshot()) { - version = P.ComponentQQGuild.snapshotVersion.toString() -} - -//val (isSnapshotOnly, isReleaseOnly, isPublishConfigurable) = checkPublishConfigurable() -// -//logger.info("isSnapshotOnly: {}", isSnapshotOnly) -//logger.info("isReleaseOnly: {}", isReleaseOnly) -//logger.info("isPublishConfigurable: {}", isPublishConfigurable) - val userInfo = love.forte.gradle.common.publication.sonatypeUserInfoOrNull if (userInfo == null) { @@ -54,43 +42,3 @@ nexusPublishConfig { } } } - -// -//if (isPublishConfigurable) { -// val sonatypeUsername: String? = sonatypeUsername -// val sonatypePassword: String? = sonatypePassword -// -// if (sonatypeUsername == null || sonatypePassword == null) { -// logger.warn("sonatype.username or sonatype.password is null, cannot config nexus publishing.") -// } -// -// nexusPublishing { -// packageGroup.set(project.group.toString()) -// -// useStaging.set( -// project.provider { !project.version.toString().endsWith("SNAPSHOT", ignoreCase = true).also { -// logger.info("UseStaging: {} (project version: {})", it, project.version.toString()) -// } } -// ) -// -// transitionCheckOptions { -// maxRetries.set(100) -// delayBetween.set(Duration.ofSeconds(30)) -// } -// -// repositories { -// sonatype { -// snapshotRepositoryUrl.set(uri(Repositories.Snapshot.URL)) -// username.set(sonatypeUsername) -// password.set(sonatypePassword) -// } -// } -// } -// -// -// logger.info("[publishing-configure] - [{}] configured.", name) -//} - - - - diff --git a/buildSrc/src/main/kotlin/simbot-tencent-guild.root-module-conventions.gradle.kts b/buildSrc/src/main/kotlin/simbot-tencent-guild.root-module-conventions.gradle.kts deleted file mode 100644 index 201a127e..00000000 --- a/buildSrc/src/main/kotlin/simbot-tencent-guild.root-module-conventions.gradle.kts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2022-2023. ForteScarlet. - * - * This file is part of simbot-component-tencent-guild. - * - * simbot-component-tencent-guild is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - * - * simbot-component-tencent-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-tencent-guild. If not, see . - */ -plugins { - idea -} - -idea { - module.apply { - isDownloadSources = true - isDownloadJavadoc = true - } - project { - modules.forEach { module -> - module.apply { - isDownloadSources = true - isDownloadJavadoc = true - } - } - } -} - -configurations.all { - // check for updates every build - resolutionStrategy.cacheChangingModulesFor(0, "seconds") -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 44732334..dc4a8e77 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,14 +1,25 @@ [versions] -kotlinx-coroutines = "1.7.0" -kotlinx-serialization = "1.5.0" +kotlinx-coroutines = "1.7.3" +kotlinx-serialization = "1.6.2" kotlinx-datetime = "0.4.0" okio = "3.3.0" -ktor = "2.3.0" +ktor = "2.3.7" openjdk-jmh = "1.35" log4j = "2.20.0" atomicfu = "0.20.1" +simbot = "4.0.0-dev1-SNAPSHOT" [libraries] +# simbot +simbot-api = { group = "love.forte.simbot", name = "simbot-api", version.ref = "simbot" } +simbot-core = { group = "love.forte.simbot", name = "simbot-core", version.ref = "simbot" } +simbot-logger = { group = "love.forte.simbot.logger", name = "simbot-logger", version.ref = "simbot" } +simbot-common-apidefinition = { group = "love.forte.simbot.common", name = "simbot-common-apidefinition", version.ref = "simbot" } +simbot-common-atomic = { group = "love.forte.simbot.common", name = "simbot-common-atomic", version.ref = "simbot" } +simbot-common-core = { group = "love.forte.simbot.common", name = "simbot-common-core", version.ref = "simbot" } +simbot-common-suspend = { group = "love.forte.simbot.common", name = "simbot-common-suspend-runner", version.ref = "simbot" } +simbot-common-annotations = { group = "love.forte.simbot.common", name = "simbot-common-annotations", version.ref = "simbot" } + # jetbrains-annotation jetbrains-annotations = "org.jetbrains:annotations:24.0.1" diff --git a/settings.gradle.kts b/settings.gradle.kts index 18b57b61..b780eef3 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -20,9 +20,9 @@ rootProject.name = "qq-guild" //include(":builder-generator") include(":simbot-component-qq-guild-api") -include(":simbot-component-qq-guild-stdlib") -include(":simbot-component-qq-guild-core-common") -include(":simbot-component-qq-guild-core") -include(":simbot-component-qq-guild-benchmark") +//include(":simbot-component-qq-guild-stdlib") +//include(":simbot-component-qq-guild-core-common") +//include(":simbot-component-qq-guild-core") +//include(":simbot-component-qq-guild-benchmark") diff --git a/simbot-component-qq-guild-api/build.gradle.kts b/simbot-component-qq-guild-api/build.gradle.kts index f0e3a5c3..a2454f39 100644 --- a/simbot-component-qq-guild-api/build.gradle.kts +++ b/simbot-component-qq-guild-api/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,23 +15,23 @@ * If not, see . */ -import love.forte.gradle.common.kotlin.multiplatform.NativeTargets +import love.forte.gradle.common.kotlin.multiplatform.applyTier1 +import love.forte.gradle.common.kotlin.multiplatform.applyTier2 +import love.forte.gradle.common.kotlin.multiplatform.applyTier3 plugins { kotlin("multiplatform") - `qq-guild-multiplatform-maven-publish` -// id("simbot-tencent-guild.module-conventions") -// id("simbot-tencent-guild.maven-publish") kotlin("plugin.serialization") `qq-guild-dokka-partial-configure` } -repositories { - mavenCentral() -} +configJavaCompileWithModule("simbot.component.qqguild.api") +//apply(plugin = "qq-guild-dokka-partial-configure") +apply(plugin = "qq-guild-multiplatform-maven-publish") kotlin { explicitApi() + applyDefaultHierarchyTemplate() sourceSets.configureEach { languageSettings { @@ -39,167 +39,57 @@ kotlin { } } - jvm { - withJava() - compilations.all { - kotlinOptions { - jvmTarget = "1.8" - javaParameters = true - freeCompilerArgs = freeCompilerArgs + listOf("-Xjvm-default=all") - } - } - testRuns["test"].executionTask.configure { - useJUnitPlatform() - } - } + configKotlinJvm() js(IR) { - nodejs() - binaries.library() + configJs() } - - val mainPresets = mutableSetOf() - val testPresets = mutableSetOf() - - // https://kotlinlang.org/docs/native-target-support.html - // 针对 kotlin target support 中的列表结合 ktor-client 平台的支持提供的平台能力。 - // 注释掉的内容是Ktor尚不支持的平台 -// val supportTargets = setOf( -// // Tier 1 -// "linuxX64", -// "macosX64", -// "macosArm64", -// "iosSimulatorArm64", -// "iosX64", -// -// // Tier 2 -//// "linuxArm64", -// "watchosSimulatorArm64", -// "watchosX64", -// "watchosArm32", -// "watchosArm64", -// "tvosSimulatorArm64", -// "tvosX64", -// "tvosArm64", -// "iosArm64", -// -// // Tier 3 -//// "androidNativeArm32", -//// "androidNativeArm64", -//// "androidNativeX86", -//// "androidNativeX64", -// "mingwX64", -//// "watchosDeviceArm64", -// ) - - val targets = NativeTargets.Official.all.intersect(NativeTargets.KtorClient.all) + setOf("mingwX64") - - targets { - presets.filterIsInstance>() - .filter { it.name in targets } - .forEach { presets -> - val target = fromPreset(presets, presets.name) - val mainSourceSet = target.compilations["main"].kotlinSourceSets.first() - val testSourceSet = target.compilations["test"].kotlinSourceSets.first() - - val tn = target.name - when { - // just for test - // main中只使用HttpClient但用不到引擎,没必要指定 - - // win - tn.startsWith("mingw") -> { - testSourceSet.dependencies { - implementation(libs.ktor.client.winhttp) - } - } - // linux: CIO..? - tn.startsWith("linux") -> { - testSourceSet.dependencies { - implementation(libs.ktor.client.cio) - } - } - - // darwin based - tn.startsWith("macos") - || tn.startsWith("ios") - || tn.startsWith("watchos") - || tn.startsWith("tvos") -> { - testSourceSet.dependencies { - implementation(libs.ktor.client.darwin) - } - } - } - - mainPresets.add(mainSourceSet) - testPresets.add(testSourceSet) - } - } + applyTier1() + applyTier2() + applyTier3(supportKtorClient = true) sourceSets { - val commonMain by getting { - dependencies { - api(libs.ktor.client.core) - api(libs.ktor.client.contentNegotiation) - api(libs.ktor.serialization.kotlinxJson) - api(libs.kotlinx.serialization.json) - api(simbotLogger) - } + commonMain.dependencies { + api(libs.kotlinx.coroutines.core) + + api(libs.simbot.logger) + api(libs.simbot.common.apidefinition) + api(libs.simbot.common.suspend) + api(libs.simbot.common.core) + compileOnly(libs.simbot.common.annotations) + + api(libs.ktor.client.core) + api(libs.ktor.client.contentNegotiation) + api(libs.ktor.serialization.kotlinxJson) + api(libs.kotlinx.serialization.json) } - val commonTest by getting { - dependencies { - implementation(kotlin("test")) - implementation(libs.kotlinx.coroutines.test) - } + commonTest.dependencies { + implementation(kotlin("test")) + implementation(libs.kotlinx.coroutines.test) } - getByName("jvmMain") { - dependencies { - compileOnly(simbotApi) // use @Api4J annotation - } + jvmMain.dependencies { +// compileOnly(libs.simbot.api) // use @Api4J annotation } - getByName("jvmTest") { - dependencies { - implementation(libs.ktor.client.cio) - implementation(simbotApi) // use @Api4J annotation - implementation(libs.log4j.api) - implementation(libs.log4j.core) - implementation(libs.log4j.slf4j2) - } + jvmTest.dependencies { + implementation(libs.ktor.client.cio) +// implementation(simbotApi) // use @Api4J annotation + implementation(libs.log4j.api) + implementation(libs.log4j.core) + implementation(libs.log4j.slf4j2) } - getByName("jsMain") { - dependencies { - api(libs.ktor.client.js) - } + jsMain.dependencies { + api(libs.ktor.client.js) } - val nativeMain by creating { - dependsOn(commonMain) + mingwTest.dependencies { + implementation(libs.ktor.client.winhttp) } - - val nativeTest by creating { - dependsOn(commonTest) - } - - configure(mainPresets) { dependsOn(nativeMain) } - configure(testPresets) { dependsOn(nativeTest) } - } } -// suppress all? -//tasks.withType().configureEach { -// dokkaSourceSets.configureEach { -// suppress.set(true) -// perPackageOption { -// suppress.set(true) -// } -// } -//} - - diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/OptAnnotations.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/OptAnnotations.kt index f8853770..05492542 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/OptAnnotations.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/OptAnnotations.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -25,14 +25,14 @@ package love.forte.simbot.qguild @Retention(AnnotationRetention.BINARY) @MustBeDocumented @RequiresOptIn("API marked for Java use, not recommended for Kotlin.", level = RequiresOptIn.Level.WARNING) -public annotation class Api4J +public annotation class QGApi4J /** * 一个仅服务于JS的API。对于Kotlin来讲通常有更优选择。 */ @Retention(AnnotationRetention.BINARY) @MustBeDocumented @RequiresOptIn("API marked for JS use, not recommended for Kotlin.", level = RequiresOptIn.Level.WARNING) -public annotation class Api4JS +public annotation class QGApi4JS // 好吧可能 OptIn 的 annotation 不能用 actual @@ -42,4 +42,4 @@ public annotation class Api4JS @Retention(AnnotationRetention.BINARY) @MustBeDocumented @RequiresOptIn("Internal API", level = RequiresOptIn.Level.WARNING) -public annotation class InternalApi +public annotation class QGInternalApi diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuild.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuild.kt index c1ad1058..7ae37ed3 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuild.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuild.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -18,6 +18,7 @@ package love.forte.simbot.qguild import io.ktor.http.* +import kotlinx.serialization.json.Json import kotlin.js.ExperimentalJsExport import kotlin.js.JsExport import kotlin.jvm.JvmField @@ -66,6 +67,31 @@ public object QQGuild { */ @JvmField public val SANDBOX_URL: Url = Url(SANDBOX_URL_STRING) + + /** + * 部分API中默认使用的Json序列化器。 + * + * ```kotlin + * Json { + * isLenient = true + * ignoreUnknownKeys = true + * allowSpecialFloatingPointValues = true + * allowStructuredMapKeys = true + * prettyPrint = false + * useArrayPolymorphism = false + * } + * ``` + * + */ + @JvmField + public val DefaultJson: Json = Json { + isLenient = true + ignoreUnknownKeys = true + allowSpecialFloatingPointValues = true + allowStructuredMapKeys = true + prettyPrint = false + useArrayPolymorphism = false + } } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuildApiException.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuildApiException.kt index 048b3c2a..ac6122af 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuildApiException.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuildApiException.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -26,6 +26,7 @@ import kotlinx.serialization.json.JsonNull /** * 初始化 cause, 并得到自身(或结果) * 在 JVM 平台上生效。 + * 在其他平台会使用 [addSuppressed] 添加 [cause]。 */ @PublishedApi internal expect inline fun T.initCause0(cause: Throwable): T @@ -61,6 +62,7 @@ public open class QQGuildApiException : RuntimeException { /** * @suppress */ +@OptIn(QGInternalApi::class) @Deprecated("use 'addStackTrace'", ReplaceWith("addStackTrace()")) public fun QQGuildApiException.copyCurrent(): QQGuildApiException = addStackTrace() @@ -68,7 +70,7 @@ public fun QQGuildApiException.copyCurrent(): QQGuildApiException = addStackTrac * * @suppress internal API to add suppressed for api exception */ -@InternalApi +@QGInternalApi public inline fun E.addStackTrace(block: () -> String? = { null }): E { addSuppressed(APIStackException(block())) return this @@ -78,7 +80,7 @@ public inline fun E.addStackTrace(block: () -> String? * * @see addStackTrace */ -@InternalApi +@QGInternalApi @PublishedApi internal class APIStackException @PublishedApi internal constructor(message: String? = null) : Exception(message) @@ -95,12 +97,14 @@ public inline val QQGuildApiException.isUnauthorized: Boolean get() = value == 4 /** * 如果 [QQGuildApiException.isNotFound] 为 `true`, 得到null,否则抛出此异常 */ +@OptIn(QGInternalApi::class) public inline fun QQGuildApiException.ifNotFoundThenNull(throwCopy: Boolean = true): T? = if (isNotFound) null else if (throwCopy) throw this.addStackTrace() else throw this /** * 如果 [QQGuildApiException.isNotFound] 为 `true`, 得到null,否则抛出此异常 */ +@OptIn(QGInternalApi::class) public inline fun QQGuildApiException.ifNotFoundThen(throwCopy: Boolean = true, value: () -> T): T = if (isNotFound) value() else if (throwCopy) throw this.addStackTrace() else throw this @@ -108,6 +112,7 @@ public inline fun QQGuildApiException.ifNotFoundThen(throwCopy: Bool /** * 如果 [QQGuildApiException.isNotFound] 为 `true`, 得到null,否则抛出此异常 */ +@OptIn(QGInternalApi::class) public inline fun QQGuildApiException.ifNotFoundThenNoSuch(throwCopy: Boolean = true, value: () -> String): Nothing = if (isNotFound) { throw NoSuchElementException(value()).apply { initCause0(this) } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiDescription.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiDescription.kt index d4d3fd34..c80620d4 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiDescription.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiDescription.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -31,7 +31,6 @@ import io.ktor.http.* * @author ForteScarlet */ public interface ApiDescription { - /** * API 接口名,例如 `/guilds/{guild_id}/members/{user_id}` */ @@ -77,3 +76,21 @@ public abstract class SimpleGetApiDescription(path: String) : public abstract class SimplePostApiDescription(path: String) : SimpleApiDescription(HttpMethod.Post, path) +/** + * [SimpleApiDescription] 的基本抽象实现,[method] 提供为 [HttpMethod.Put] + */ +public abstract class SimplePutApiDescription(path: String) : + SimpleApiDescription(HttpMethod.Put, path) + +/** + * [SimpleApiDescription] 的基本抽象实现,[method] 提供为 [HttpMethod.Delete] + */ +public abstract class SimpleDeleteApiDescription(path: String) : + SimpleApiDescription(HttpMethod.Delete, path) + +/** + * [SimpleApiDescription] 的基本抽象实现,[method] 提供为 [HttpMethod.Patch] + */ +public abstract class SimplePatchApiDescription(path: String) : + SimpleApiDescription(HttpMethod.Patch, path) + diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequestor.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequestor.kt deleted file mode 100644 index e74700ee..00000000 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequestor.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2022-2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - - -package love.forte.simbot.qguild.api - -import io.ktor.client.* -import io.ktor.http.* -import kotlinx.serialization.StringFormat -import love.forte.simbot.logger.LoggerFactory -import love.forte.simbot.qguild.QQGuild -import kotlin.jvm.JvmSynthetic - -internal val apiRequestorLogger = LoggerFactory.getLogger("love.forte.simbot.qguild.api.ApiRequestor") - -/** - * - * 通过提供的参数,对此api进行请求并得到最终结果。 - * - * @param client 提供一个 http client,默认使用 [QQGuildApi.DefaultHttpClient]。 - * @param server 提供一个目标服务器路径。See also: [QQGuild.URL]、[QQGuild.SANDBOX_URL] - * @param token 提供一个 token. - * @param decoder 如果有,提供一个decoder用于通过 [QQGuildApi.resultDeserializer] 进行反序列化,而不是通过 [client] 进行。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果响应码不在 200..300 范围内。 - * - */ -@JvmSynthetic -public suspend fun QQGuildApi.request( - client: HttpClient = QQGuildApi.DefaultHttpClient, - server: Url, - token: String, - decoder: StringFormat = QQGuildApi.DefaultJsonDecoder, -): R { - return doRequest(client, server, token, decoder) -} -/** - * - * 通过提供的参数,对此api进行请求并得到最终结果。 - * - * @param client 提供一个 http client,默认使用 [QQGuildApi.DefaultHttpClient]。 - * @param server 提供一个目标服务器路径。See also: [QQGuild.URL]、[QQGuild.SANDBOX_URL] - * @param token 提供一个 token. - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果响应码不在 200..300 范围内。 - * - */ -@JvmSynthetic -public suspend fun QQGuildApi<*>.requestRaw( - client: HttpClient = QQGuildApi.DefaultHttpClient, - server: Url, - token: String, -): String { - return doRequestRaw(client, server, token) -} - - diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt new file mode 100644 index 00000000..2265318b --- /dev/null +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +@file:JvmName("ApiRequests") +@file:JvmMultifileClass + +package love.forte.simbot.qguild.api + +import io.ktor.client.* +import io.ktor.client.plugins.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.client.utils.* +import io.ktor.http.* +import io.ktor.http.content.* +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.StringFormat +import kotlinx.serialization.builtins.serializer +import kotlinx.serialization.descriptors.StructureKind +import kotlinx.serialization.json.Json +import love.forte.simbot.common.serialization.guessSerializer +import love.forte.simbot.logger.isDebugEnabled +import love.forte.simbot.qguild.ErrInfo +import love.forte.simbot.qguild.QGInternalApi +import love.forte.simbot.qguild.QQGuild +import love.forte.simbot.qguild.QQGuildApiException +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.InvocationKind +import kotlin.contracts.contract +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic + +/** + * 使用 [client] 向当前目标 API [QQGuildApi] 发起请求。 + * + * @param server 如果不为 null 则会取 [server] 中的 [Url.protocol]、[Url.hostWithPort] + * 替换 [QQGuildApi.url] 中提供的值。 + * + */ +@JvmSynthetic +public suspend fun QQGuildApi.request( + client: HttpClient, + token: String, + server: Url? = null, + json: Json = QQGuild.DefaultJson +): HttpResponse { + val api = this + + return client.request { + method = api.method + + headers { + this[HttpHeaders.Authorization] = token + with(api.headers) { + if (!isEmpty()) { + appendAll(api.headers) + } + } + } + + url { + takeFrom(api.url) + + if (server != null) { + protocol = server.protocol + host = server.host + port = server.port + } + } + + when (val body = api.body) { + null -> { + setBody(EmptyContent) + } + + is OutgoingContent -> { + setBody(body) + } + + else -> { + if (client.pluginOrNull(ContentNegotiation) != null) { + setBody(body) + } else { + try { + val ser = guessSerializer(body, json.serializersModule) + val bodyJson = json.encodeToString(ser, body) + setBody(bodyJson) + } catch (e: Throwable) { + try { + setBody(body) + } catch (e0: Throwable) { + e0.addSuppressed(e) + throw e0 + } + } + } + } + } + + apiLogger.debug("[{} /{}] =====> {}, body: {}", method.logName, url.encodedPath, url.host, api.body) + } +} + + +/** + * 通过 [request] 得到响应,读取为文本并输出debug日志后返回。 + * 不会进行校验。 + */ +@OptIn(ExperimentalContracts::class) +public suspend inline fun QQGuildApi.requestText( + client: HttpClient, + token: String, + server: Url = QQGuild.URL, + useResp: (HttpResponse) -> Unit = {} +): String { + contract { + callsInPlace(useResp, InvocationKind.EXACTLY_ONCE) + } + + val resp = request(client, token, server) + useResp(resp) + + val text = resp.bodyAsText() + + if (apiLogger.isDebugEnabled) { + val traceId = resp.headers[TRACE_ID_HEAD] + apiLogger.debug( + "[{} {}] <===== status: {}, body: {}, traceID: {}", + method.logName, + resp.request.url.encodedPath, + resp.status, + text, + traceId + ) + } + + return text +} + +/** + * 使用此api发起一次请求,并得到预期中的结果。 + * + * ## Body序列化 + * + * 参数 [client] 不强制要求必须安装 [ContentNegotiation] 插件, + * 如果未安装此插件且 API 的请求过程中存在 body,则内部会使用一个默认的序列化器 (不是 [decoder]) + * 进行一个与此插件 _类似的_ 逻辑去寻找 body 的序列化信息。此时要求 API 的 body 必须支持 Kotlinx 的序列化。 + * + * @param client 用于本次http请求的client。 + * @param server 请求目标服务器。See also: [QQGuild.URL]、[QQGuild.SANDBOX_URL]。 + * @param token 用于本次请求鉴权的token。 + * @param decoder 用于本次请求结果的反序列化器。不出意外的话应该是 [Json] 序列化器,默认使用 [QQGuild.DefaultJson]。 + * + * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 + * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误。 + * + * @see ErrInfo + */ +@JvmSynthetic +public suspend fun QQGuildApi.requestData( + client: HttpClient, + token: String, + server: Url = QQGuild.URL, + decoder: Json = QQGuild.DefaultJson, +): R { + val resp: HttpResponse + val text = requestText(client, token, server) { resp = it } + + checkStatus(text, QQGuild.DefaultJson, resp.status, resp) + + return decodeResponse(decoder, text) +} + + +@Suppress("UNCHECKED_CAST") +@OptIn(ExperimentalSerializationApi::class) +internal fun QQGuildApi.decodeResponse( + decoder: Json, remainingText: String +): R { + if (resultDeserializationStrategy === Unit.serializer()) { + return Unit as R + } + + if (remainingText.isEmpty() && resultDeserializationStrategy.descriptor.kind is StructureKind.OBJECT) { + return decoder.decodeFromString(resultDeserializationStrategy, "{}") + } + + return decoder.decodeFromString(resultDeserializationStrategy, remainingText) +} + +@OptIn(QGInternalApi::class) +internal fun checkStatus( + remainingText: String, + decoder: StringFormat, + status: HttpStatusCode, + resp: HttpResponse, +) { + // TODO 201,202 异步操作成功,虽然说成功,但是会返回一个 error body,需要特殊处理 + if (!status.isSuccess()) { + val info = decoder.decodeFromString(ErrInfo.serializer(), remainingText) + // throw err + throw QQGuildApiException(info, status.value, status.description) + } + + // 202 消息审核异常 + if (status == HttpStatusCode.Accepted) { + val info = decoder.decodeFromString(ErrInfo.serializer(), remainingText) + // maybe audited + if (MessageAuditedException.isAuditResultCode(info.code)) { + throw MessageAuditedException( + QQGuild.DefaultJson.decodeFromJsonElement(MessageAudit.serializer(), info.data).messageAudit, + info, + resp.status.value, + resp.status.description + ) + } + + throw QQGuildApiException(info, resp.status.value, resp.status.description) + } +} diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/GatewayApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/GatewayApi.kt index fe627c77..10191d13 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/GatewayApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/GatewayApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,7 +17,9 @@ package love.forte.simbot.qguild.api -import kotlinx.serialization.* +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable import love.forte.simbot.qguild.api.GatewayApis.Normal import love.forte.simbot.qguild.api.GatewayApis.Shared @@ -35,22 +37,18 @@ import love.forte.simbot.qguild.api.GatewayApis.Shared * @author ForteScarlet */ public sealed class GatewayApis( - protected val path: Array, - override val resultDeserializer: DeserializationStrategy + override val path: Array, + override val resultDeserializationStrategy: DeserializationStrategy ) : GetQQGuildApi() { - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - + /** * 获取通用 WSS 接入点 * * > [参考文档](https://bot.q.qq.com/wiki/develop/api/openapi/wss/url_get.html) */ public object Normal : GatewayApis(arrayOf("gateway"), Gateway.serializer()) - - + + /** * 获取带分片 WSS 接入点 * diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/MessageAuditedException.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/MessageAuditedException.kt index b6f9e36a..fce811d4 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/MessageAuditedException.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/MessageAuditedException.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -21,7 +21,7 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import love.forte.simbot.qguild.ApiModel import love.forte.simbot.qguild.ErrInfo -import love.forte.simbot.qguild.InternalApi +import love.forte.simbot.qguild.QGInternalApi import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.api.message.MessageSendApi import love.forte.simbot.qguild.api.message.direct.DmsSendApi @@ -83,7 +83,7 @@ public class MessageAuditedException : QQGuildApiException { public companion object { private val AUDIT_ERROR_CODES = setOf(304023, 304024) - @InternalApi + @QGInternalApi internal fun isAuditResultCode(code: Int): Boolean = code in AUDIT_ERROR_CODES } } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.kt index f9d62e12..f8c7894a 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,31 +17,13 @@ package love.forte.simbot.qguild.api -import io.ktor.client.* -import io.ktor.client.plugins.* -import io.ktor.client.plugins.contentnegotiation.* -import io.ktor.client.request.* -import io.ktor.client.statement.* -import io.ktor.client.utils.* import io.ktor.http.* -import io.ktor.http.content.* -import io.ktor.serialization.kotlinx.json.* -import kotlinx.serialization.* -import kotlinx.serialization.builtins.* -import kotlinx.serialization.descriptors.StructureKind -import kotlinx.serialization.json.Json -import kotlinx.serialization.modules.SerializersModule +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.builtins.serializer +import love.forte.simbot.common.apidefinition.* import love.forte.simbot.logger.Logger import love.forte.simbot.logger.LoggerFactory -import love.forte.simbot.logger.isDebugEnabled -import love.forte.simbot.qguild.ErrInfo -import love.forte.simbot.qguild.InternalApi import love.forte.simbot.qguild.QQGuild -import love.forte.simbot.qguild.QQGuildApiException -import kotlin.contracts.ExperimentalContracts -import kotlin.contracts.InvocationKind -import kotlin.contracts.contract -import kotlin.jvm.JvmSynthetic @PublishedApi internal val apiLogger: Logger = LoggerFactory.getLogger("love.forte.simbot.qguild.api") @@ -58,68 +40,10 @@ internal val apiLogger: Logger = LoggerFactory.getLogger("love.forte.simbot.qgui @PublishedApi internal const val TRACE_ID_HEAD: String = "X-Tps-trace-ID" - -/** - * 用于多平台实现的最小目标。 - * - * 在JVM平台和JS平台中分别提供对应的 blocking/async 兼容函数。 - * 但是不应追加新的抽象函数。 - * - * _仅内部平台实现用_ - * - * @see QQGuildApi - */ -@InternalApi -public expect abstract class PlatformQQGuildApi() { - - /** - * 使用此api发起一次请求,并得到预期中的结果。 - * - * @param client 用于本次http请求的client。默认使用 [QQGuildApi.DefaultHttpClient]。 - * @param server 请求目标服务器。See also: [QQGuild.URL]、[QQGuild.SANDBOX_URL]。 - * @param token 用于本次请求鉴权的token。 - * @param decoder 用于本次请求结果的反序列化器。不出意外的话应该是 [Json] 序列化器,默认使用 [QQGuildApi.DefaultJsonDecoder]。 - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误(http状态码 !in 200 .. 300) - * - * @see ErrInfo - */ - @JvmSynthetic - public abstract suspend fun doRequest( - client: HttpClient = QQGuildApi.DefaultHttpClient, - server: Url, - token: String, - decoder: StringFormat = QQGuildApi.DefaultJsonDecoder, - ): R - - /** - * 使用此api发起一次请求,并得到响应结果的字符串。 - * - * @param client 用于本次http请求的client。默认使用 [QQGuildApi.DefaultHttpClient]。 - * @param server 请求目标服务器。See also: [QQGuild.URL]、[QQGuild.SANDBOX_URL]。 - * @param token 用于本次请求鉴权的token。 - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误(http状态码 !in 200 .. 300) - * - * @see ErrInfo - */ - @JvmSynthetic - public abstract suspend fun doRequestRaw( - client: HttpClient = QQGuildApi.DefaultHttpClient, - server: Url, - token: String, - ): String - - public companion object -} - - /** * 表示为一个QQ频道的API。 * - * 通过 [doRequest] 发起一次请求。 + * 通过 [requestData] 发起一次请求。 * * ### 日志 * @@ -139,346 +63,101 @@ public expect abstract class PlatformQQGuildApi() { * -Dsimbot.qguild.api.logger.color.enable=false * ``` */ -public abstract class QQGuildApi : PlatformQQGuildApi() { +public interface QQGuildApi : ApiDefinition { /** * 得到响应值的反序列化器. */ - public abstract val resultDeserializer: DeserializationStrategy - + override val resultDeserializationStrategy: DeserializationStrategy /** - * 此api请求方式 + * 最终用于请求的目标地址。 + * 默认会使用 [QQGuild.URL] + * 作为其域名地址。 */ - public abstract val method: HttpMethod - + override val url: Url /** - * 此请求对应的api路由路径以及路径参数。 - * 例如:`/guild/list` + * 此api请求方式 */ - public abstract fun route(builder: RouteInfoBuilder) - + override val method: HttpMethod /** * 此次请求所发送的数据。为null则代表没有参数。 */ - public abstract val body: Any? - - - /** - * 当通过 [doRequest] 得到成功结果后进行的操作。 - */ - public open fun post(resp: @UnsafeVariance R) {} + override val body: Any? - /** - * 使用此api发起一次请求,并得到预期中的结果。 - * - * ## Body序列化 - * - * 参数 [client] 不强制要求必须安装 [ContentNegotiation] 插件, - * 如果未安装此插件且 API 的请求过程中存在 body,则内部会使用一个默认的序列化器 (不是 [decoder]) - * 进行一个与此插件 _类似的_ 逻辑去寻找 body 的序列化信息。此时要求 API 的 body 必须支持 Kotlinx 的序列化。 - * - * @param client 用于本次http请求的client。默认使用 [QQGuildApi.DefaultHttpClient]。 - * @param server 请求目标服务器。See also: [QQGuild.URL]、[QQGuild.SANDBOX_URL]。 - * @param token 用于本次请求鉴权的token。 - * @param decoder 用于本次请求结果的反序列化器。不出意外的话应该是 [Json] 序列化器,默认使用 [QQGuildApi.DefaultJsonDecoder]。 - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误。 - * - * @see ErrInfo - */ - @JvmSynthetic - public override suspend fun doRequest( - client: HttpClient, - server: Url, - token: String, - decoder: StringFormat, - ): R { - val text = doRequestRaw(client, server, token) + public companion object +} - return decodeResponse(decoder, text) - } +public interface QQGuildApiWithoutResult : QQGuildApi { + override val resultDeserializationStrategy: DeserializationStrategy + get() = Unit.serializer() //EmptyUnitSerializer +} +public abstract class GetQQGuildApi : GetApiDefinition(), QQGuildApi { + protected abstract val path: Array /** - * 使用此api发起一次请求,并得到响应结果的字符串。 - * - * ## Body序列化 - * - * 参数 [client] 不强制要求必须安装 [ContentNegotiation] 插件, - * 如果未安装此插件且 API 的请求过程中存在 body,则内部会使用一个默认的序列化器进行一个与此插件 - * _类似的_ 逻辑去寻找 body 的序列化信息。此时要求 API 的 body 必须支持 Kotlinx 的序列化。 - * - * @see ErrInfo - * - * @param client 用于本次http请求的client。默认使用 [QQGuildApi.DefaultHttpClient]。 - * @param server 请求目标服务器。See also: [QQGuild.URL]、[QQGuild.SANDBOX_URL]。 - * @param token 用于本次请求鉴权的token。 - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误(http状态码 !in 200 .. 300) + * 已经完成 [path] 拼接后、追加其他额外内容(例如parameter)时使用 */ - @JvmSynthetic - override suspend fun doRequestRaw(client: HttpClient, server: Url, token: String): String { - val resp: HttpResponse - val text = requestForText(client, server, token) { resp = it } - - checkStatus(text, DefaultErrInfoDecoder, resp.status) - - if (text.isEmpty() && resp.status.isSuccess()) { - return "{}" - } - - return text - } - - public companion object { - - /** - * 可以使用的默认 [HttpClient]。 - * - * 通过 [`HttpClient()`][HttpClient] 构建懒加载并没有任何额外配置。 - * - * JVM平台下需要添加可用的引擎依赖到classpath中以支持自动加载。 - * - */ - public val DefaultHttpClient: HttpClient by lazy { - HttpClient { - install(ContentNegotiation) { - json(DefaultJsonDecoder) - } - } - } - - internal val DefaultErrInfoDecoder = Json { - isLenient = true - ignoreUnknownKeys = true - allowSpecialFloatingPointValues = true - allowStructuredMapKeys = true - prettyPrint = false - useArrayPolymorphism = false - } - - /** - * 部分API中默认使用的Json序列化器。 - * - * ```kotlin - * Json { - * isLenient = true - * ignoreUnknownKeys = true - * allowSpecialFloatingPointValues = true - * allowStructuredMapKeys = true - * prettyPrint = false - * useArrayPolymorphism = false - * } - * ``` - * - */ - @InternalApi - public val DefaultJsonDecoder: Json = Json(DefaultErrInfoDecoder) {} - } - - - protected suspend fun requestForResponse(client: HttpClient, server: Url, token: String, json: Json = DefaultJsonDecoder): HttpResponse { - val api = this + protected open fun URLBuilder.buildUrl() {} - return client.request { - method = api.method - - headers { - this[HttpHeaders.Authorization] = token - } - - - url { - takeFrom(server) - - // route builder - val routeBuilder = RouteInfoBuilder { name, value -> - parameters.append(name, value.toString()) - } - - api.route(routeBuilder) - - when (val body = api.body) { - null -> { - setBody(EmptyContent) - } - is OutgoingContent -> { - setBody(body) - } - - else -> { - if (client.pluginOrNull(ContentNegotiation) != null) { - setBody(body) - } else { - try { - val ser = guessSerializer(body, json.serializersModule) - val bodyJson = json.encodeToString(ser, body) - setBody(bodyJson) - } catch (e: Throwable) { - try { - setBody(body) - } catch (e0: Throwable) { - e0.addSuppressed(e) - throw e0 - } - } - - } - } - } - - - - routeBuilder.apiPath?.let { apiPath -> appendPathSegments(components = apiPath) } - routeBuilder.contentType?.let { - headers { - this[HttpHeaders.ContentType] = it.toString() - } - } - } - - apiLogger.debug("[{} /{}] =====> {}, body: {}", method.logName, url.encodedPath, url.host, api.body) - } - } + override val url: Url + get() = URLBuilder(QQGuild.URL).apply { + appendEncodedPathSegments(components = path) + buildUrl() + }.build() +} +public abstract class PostQQGuildApi : PostApiDefinition(), QQGuildApi { + protected abstract val path: Array /** - * 通过 [requestForResponse] 得到响应,读取为文本并输出debug日志后返回。 - * 不会进行校验。 + * 已经完成 [path] 拼接后、追加其他额外内容(例如parameter)时使用 */ - @OptIn(ExperimentalContracts::class) - protected suspend inline fun requestForText(client: HttpClient, server: Url, token: String, useResp: (HttpResponse) -> Unit = {}): String { - contract { - callsInPlace(useResp, InvocationKind.EXACTLY_ONCE) - } - - val resp = requestForResponse(client, server, token) - useResp(resp) - - val text = resp.bodyAsText() - - if (apiLogger.isDebugEnabled) { - val traceId = resp.headers[TRACE_ID_HEAD] - apiLogger.debug( - "[{} {}] <===== status: {}, body: {}, traceID: {}", - method.logName, - resp.request.url.encodedPath, - resp.status, - text, - traceId - ) - } + protected open fun URLBuilder.buildUrl() {} - return text - } + override val url: Url + get() = URLBuilder(QQGuild.URL).apply { + appendEncodedPathSegments(components = path) + buildUrl() + }.build() } +public abstract class PutQQGuildApi : PutApiDefinition(), QQGuildApi { + protected abstract val path: Array + /** + * 已经完成 [path] 拼接后、追加其他额外内容(例如parameter)时使用 + */ + protected open fun URLBuilder.buildUrl() {} - -//region Ktor ContentNegotiation guessSerializer -// see KotlinxSerializationJsonExtensions.kt -// see SerializerLookup.kt - -@Suppress("UNCHECKED_CAST") -private fun guessSerializer(value: Any?, module: SerializersModule): KSerializer = when (value) { - null -> String.serializer().nullable - is List<*> -> ListSerializer(value.elementSerializer(module)) - is Array<*> -> value.firstOrNull()?.let { guessSerializer(it, module) } ?: ListSerializer(String.serializer()) - is Set<*> -> SetSerializer(value.elementSerializer(module)) - is Map<*, *> -> { - val keySerializer = value.keys.elementSerializer(module) - val valueSerializer = value.values.elementSerializer(module) - MapSerializer(keySerializer, valueSerializer) - } - - else -> { - @OptIn(InternalSerializationApi::class, ExperimentalSerializationApi::class) - module.getContextual(value::class) ?: value::class.serializer() - } -} as KSerializer - - -@OptIn(ExperimentalSerializationApi::class) -private fun Collection<*>.elementSerializer(module: SerializersModule): KSerializer<*> { - val serializers: List> = - filterNotNull().map { guessSerializer(it, module) }.distinctBy { it.descriptor.serialName } - - if (serializers.size > 1) { - error( - "Serializing collections of different element types is not yet supported. " + - "Selected serializers: ${serializers.map { it.descriptor.serialName }}", - ) - } - - val selected = serializers.singleOrNull() ?: String.serializer() - - if (selected.descriptor.isNullable) { - return selected - } - - @Suppress("UNCHECKED_CAST") - selected as KSerializer - - if (any { it == null }) { - return selected.nullable - } - - return selected -} -//endregion - -@Suppress("UNCHECKED_CAST") -@OptIn(ExperimentalSerializationApi::class) -internal fun QQGuildApi.decodeResponse( - decoder: StringFormat, remainingText: String -): R { - if (resultDeserializer === Unit.serializer()) { - return Unit as R - } - - if (remainingText.isEmpty() && resultDeserializer.descriptor.kind is StructureKind.OBJECT) { - return decoder.decodeFromString(resultDeserializer, "{}") - } - - return decoder.decodeFromString(resultDeserializer, remainingText) -} - -internal fun checkStatus( - remainingText: String, - decoder: StringFormat, - status: HttpStatusCode -) { - // TODO 201,202 异步操作成功,虽然说成功,但是会返回一个 error body,需要特殊处理 - if (!status.isSuccess()) { - val info = decoder.decodeFromString(ErrInfo.serializer(), remainingText) - // throw err - throw QQGuildApiException(info, status.value, status.description) - } + override val url: Url + get() = URLBuilder(QQGuild.URL).apply { + appendEncodedPathSegments(components = path) + buildUrl() + }.build() } -public abstract class QQGuildApiWithoutResult : QQGuildApi() { - override val resultDeserializer: DeserializationStrategy - get() = Unit.serializer() -} +public abstract class DeleteQQGuildApi : DeleteApiDefinition(), QQGuildApi { + protected abstract val path: Array -public abstract class GetQQGuildApi : QQGuildApi() { - override val method: HttpMethod - get() = HttpMethod.Get + /** + * 已经完成 [path] 拼接后、追加其他额外内容(例如parameter)时使用 + */ + protected open fun URLBuilder.buildUrl() {} - override val body: Any? - get() = null + override val url: Url + get() = URLBuilder(QQGuild.URL).apply { + appendEncodedPathSegments(components = path) + buildUrl() + }.build() } -public abstract class PostQQGuildApi : QQGuildApi() { +public abstract class PatchQQGuildApi : PutQQGuildApi(), QQGuildApi { override val method: HttpMethod - get() = HttpMethod.Post - + get() = HttpMethod.Patch } internal fun HttpMethod.defaultForLogName(): String = when (this) { diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/Serializers.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/Serializers.kt index 897452d8..334ac01a 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/Serializers.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/Serializers.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -18,6 +18,7 @@ package love.forte.simbot.qguild.api import kotlinx.serialization.KSerializer +import kotlinx.serialization.builtins.serializer import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor import kotlinx.serialization.descriptors.SerialDescriptor @@ -44,3 +45,18 @@ public object NumberAsBooleanSerializer : KSerializer { */ private fun Boolean.toNumber(): Int = if (this) 1 else 0 } + + +internal object EmptyUnitSerializer : KSerializer { + private val ser = Unit.serializer() + override fun deserialize(decoder: Decoder) { + // Just do nothing? + } + + override val descriptor: SerialDescriptor + get() = ser.descriptor + + override fun serialize(encoder: Encoder, value: Unit) { + ser.serialize(encoder, value) + } +} diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/announces/CreateAnnouncesApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/announces/CreateAnnouncesApi.kt index eb3096f3..b9c5b1a4 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/announces/CreateAnnouncesApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/announces/CreateAnnouncesApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,12 +17,10 @@ package love.forte.simbot.qguild.api.announces -import io.ktor.http.* import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import love.forte.simbot.qguild.api.PostQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimplePostApiDescription import love.forte.simbot.qguild.model.Announces import kotlin.jvm.JvmStatic @@ -39,7 +37,7 @@ import kotlin.jvm.JvmStatic */ public class CreateAnnouncesApi private constructor( channelId: String, - messageId: String, + private val messageId: String, ) : PostQQGuildApi() { public companion object Factory : SimplePostApiDescription( @@ -57,19 +55,12 @@ public class CreateAnnouncesApi private constructor( } + override val path: Array = arrayOf("channels", channelId, "announces") - // POST /channels/{channel_id}/announces - private val path = arrayOf("channels", channelId, "announces") - - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = Announces.serializer() - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val body: Any = Body(messageId) - + override fun createBody(): Any = Body(messageId) @Serializable private data class Body( diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/announces/DeleteAnnouncesApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/announces/DeleteAnnouncesApi.kt index dcb742ee..31853ab9 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/announces/DeleteAnnouncesApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/announces/DeleteAnnouncesApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,10 +17,9 @@ package love.forte.simbot.qguild.api.announces -import io.ktor.http.* +import love.forte.simbot.qguild.api.DeleteQQGuildApi import love.forte.simbot.qguild.api.QQGuildApiWithoutResult -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.SimpleDeleteApiDescription import kotlin.jvm.JvmStatic @@ -35,10 +34,10 @@ import kotlin.jvm.JvmStatic public class DeleteAnnouncesApi private constructor( channelId: String, messageId: String, -) : QQGuildApiWithoutResult() { +) : QQGuildApiWithoutResult, DeleteQQGuildApi() { - public companion object Factory : SimpleApiDescription( - HttpMethod.Delete, "/channels/{channel_id}/announces/{message_id}" + public companion object Factory : SimpleDeleteApiDescription( + "/channels/{channel_id}/announces/{message_id}" ) { /** @@ -49,14 +48,5 @@ public class DeleteAnnouncesApi private constructor( DeleteAnnouncesApi(channelId, messageId) } - private val path = arrayOf("channels", channelId, "announces", messageId) - - override val method: HttpMethod - get() = HttpMethod.Delete - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val body: Any? = null + override val path: Array = arrayOf("channels", channelId, "announces", messageId) } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/apipermission/DemandApiPermissionApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/apipermission/DemandApiPermissionApi.kt index ac87f67e..73cfc7a1 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/apipermission/DemandApiPermissionApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/apipermission/DemandApiPermissionApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -22,7 +22,6 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import love.forte.simbot.qguild.api.ApiDescription import love.forte.simbot.qguild.api.PostQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimplePostApiDescription import love.forte.simbot.qguild.model.ApiPermissionDemand import love.forte.simbot.qguild.model.ApiPermissionDemandIdentify @@ -81,14 +80,13 @@ public class DemandApiPermissionApi private constructor( } - private val path = arrayOf("guilds", guildId, "api_permission", "demand") + override val path: Array = arrayOf("guilds", guildId, "api_permission", "demand") - override val resultDeserializer: DeserializationStrategy + override fun createBody(): Any? = null + + override val resultDeserializationStrategy: DeserializationStrategy get() = ApiPermissionDemand.serializer() - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } /** * 用于在 [DemandApiPermissionApi] 中进行请求的 diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/apipermission/GetApiPermissionListApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/apipermission/GetApiPermissionListApi.kt index 0d451782..7c1540ae 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/apipermission/GetApiPermissionListApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/apipermission/GetApiPermissionListApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -22,7 +22,6 @@ import kotlinx.serialization.Serializable import love.forte.simbot.qguild.ApiModel import love.forte.simbot.qguild.api.ApiDescription import love.forte.simbot.qguild.api.GetQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.ApiPermission import love.forte.simbot.qguild.model.isAuthorized @@ -53,14 +52,10 @@ public class GetApiPermissionListApi private constructor( public fun create(guildId: String): GetApiPermissionListApi = GetApiPermissionListApi(guildId) } - private val path = arrayOf("guilds", guildId, "api_permission") + override val path: Array = arrayOf("guilds", guildId, "api_permission") - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = ApiPermissions.serializer() - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } } /** diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/CreateChannelApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/CreateChannelApi.kt index 322d0578..fbde7ca2 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/CreateChannelApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/CreateChannelApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -22,10 +22,10 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import love.forte.simbot.qguild.PrivateDomainOnly import love.forte.simbot.qguild.api.PostQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimplePostApiDescription import love.forte.simbot.qguild.model.* import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic /** @@ -52,16 +52,22 @@ public class CreateChannelApi private constructor( */ @JvmStatic public fun create(guildId: String, body: Body): CreateChannelApi = CreateChannelApi(guildId, body) + + /** + * 构造 [CreateChannelApi] + * + */ + @JvmSynthetic + public inline fun create(guildId: String, block: Body.Builder.() -> Unit): CreateChannelApi = + create(guildId, Body.builder().also(block).build()) } - private val path = arrayOf("guilds", guildId, "channels") + override val path: Array = arrayOf("guilds", guildId, "channels") - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = SimpleChannel.serializer() - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } + override fun createBody(): Any? = null /** * [CreateChannelApi] 的请求体。 @@ -115,5 +121,118 @@ public class CreateChannelApi private constructor( */ @SerialName("application_id") val applicationId: String? = null - ) + ) { + /** + * Builder for [Body]. + * + * 其中除了 [applicationId] 以外的属性都是必选的。 + */ + public class Builder { + /** + * @see Body.name + */ + public var name: String? = null + + /** + * @see Body.type + */ + public var type: ChannelType? = null + + /** + * @see Body.subType + */ + public var subType: ChannelSubType? = null + + /** + * @see Body.position + */ + public var position: Int? = null + + /** + * @see Body.parentId + */ + public var parentId: String? = null + + /** + * @see Body.privateType + */ + public var privateType: PrivateType? = null + + /** + * @see Body.privateUserIds + */ + public var privateUserIds: List? = null + + /** + * @see Body.speakPermission + */ + public var speakPermission: SpeakPermission? = null + + /** + * @see Body.applicationId + */ + public var applicationId: String? = null + + public fun name(value: String?): Builder = apply { + this.name = value + } + + public fun type(value: ChannelType?): Builder = apply { + this.type = value + } + + public fun subType(value: ChannelSubType?): Builder = apply { + this.subType = value + } + + public fun position(value: Int?): Builder = apply { + this.position = value + } + + public fun parentId(value: String?): Builder = apply { + this.parentId = value + } + + public fun privateType(value: PrivateType?): Builder = apply { + this.privateType = value + } + + public fun privateUserIds(value: List?): Builder = apply { + this.privateUserIds = value + } + + public fun speakPermission(value: SpeakPermission?): Builder = apply { + this.speakPermission = value + } + + public fun applicationId(value: String?): Builder = apply { + this.applicationId = value + } + + public fun build(): Body = Body( + name = name ?: mismatchProp("name"), + type = type ?: mismatchProp("type"), + subType = subType ?: mismatchProp("subType"), + position = position ?: mismatchProp("position"), + parentId = parentId ?: mismatchProp("parentId"), + privateType = privateType ?: mismatchProp("privateType"), + privateUserIds = privateUserIds ?: mismatchProp("privateUserIds"), + speakPermission = speakPermission ?: mismatchProp("speakPermission"), + applicationId = applicationId, + ) + } + + public companion object { + /** + * 创建 [Builder] + */ + @JvmStatic + public fun builder(): Builder = Builder() + } + } +} + + +private fun mismatchProp(name: String): Nothing { + throw IllegalArgumentException("Required '$name' was null") } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/DeleteChannelApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/DeleteChannelApi.kt index fc2e7b8c..506d4445 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/DeleteChannelApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/DeleteChannelApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,13 +17,10 @@ package love.forte.simbot.qguild.api.channel -import io.ktor.http.* -import kotlinx.serialization.DeserializationStrategy -import kotlinx.serialization.builtins.serializer import love.forte.simbot.qguild.PrivateDomainOnly -import love.forte.simbot.qguild.api.QQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.DeleteQQGuildApi +import love.forte.simbot.qguild.api.QQGuildApiWithoutResult +import love.forte.simbot.qguild.api.SimpleDeleteApiDescription import kotlin.jvm.JvmStatic @@ -41,9 +38,10 @@ import kotlin.jvm.JvmStatic * @author ForteScarlet */ @PrivateDomainOnly -public class DeleteChannelApi private constructor(channelId: String) : QQGuildApi() { - public companion object Factory : SimpleApiDescription( - HttpMethod.Delete, "/channels/{channel_id}" +public class DeleteChannelApi private constructor(channelId: String) : QQGuildApiWithoutResult, + DeleteQQGuildApi() { + public companion object Factory : SimpleDeleteApiDescription( + "/channels/{channel_id}" ) { /** @@ -55,18 +53,5 @@ public class DeleteChannelApi private constructor(channelId: String) : QQGuildAp } - private val path = arrayOf("channels", channelId) - - override val resultDeserializer: DeserializationStrategy - get() = Unit.serializer() - - override val method: HttpMethod - get() = HttpMethod.Delete - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val body: Any? - get() = null + override val path: Array = arrayOf("channels", channelId) } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/GetChannelApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/GetChannelApi.kt index 2f72ca17..1edde513 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/GetChannelApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/GetChannelApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,7 +19,6 @@ package love.forte.simbot.qguild.api.channel import kotlinx.serialization.DeserializationStrategy import love.forte.simbot.qguild.api.GetQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.SimpleChannel import kotlin.jvm.JvmStatic @@ -43,12 +42,8 @@ public class GetChannelApi private constructor(channelId: String) : GetQQGuildAp public fun create(channelId: String): GetChannelApi = GetChannelApi(channelId) } - private val path = arrayOf("channels", channelId) + override val path: Array = arrayOf("channels", channelId) - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = SimpleChannel.serializer() - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/GetChannelOnlineNumsApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/GetChannelOnlineNumsApi.kt index 910996c0..71fd75a4 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/GetChannelOnlineNumsApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/GetChannelOnlineNumsApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -20,12 +20,7 @@ package love.forte.simbot.qguild.api.channel import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.descriptors.PrimitiveKind -import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor -import kotlinx.serialization.descriptors.SerialDescriptor -import kotlinx.serialization.encoding.Decoder import love.forte.simbot.qguild.api.GetQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import kotlin.jvm.JvmStatic @@ -37,11 +32,10 @@ import kotlin.jvm.JvmStatic * * @author ForteScarlet */ -public class GetChannelOnlineNumsApi(channelId: String) : GetQQGuildApi() { +public class GetChannelOnlineNumsApi(channelId: String) : GetQQGuildApi() { public companion object Factory : SimpleGetApiDescription( "/channels/{channel_id}/online_nums" ) { - /** * 构造 [GetChannelOnlineNumsApi] */ @@ -49,25 +43,15 @@ public class GetChannelOnlineNumsApi(channelId: String) : GetQQGuildApi() { public fun create(channelId: String): GetChannelOnlineNumsApi = GetChannelOnlineNumsApi(channelId) } - private val path = arrayOf("channels", channelId, "online_nums") - - override val resultDeserializer: DeserializationStrategy - get() = OnlineNumsToIntDeserializationStrategy + override val path: Array = arrayOf("channels", channelId, "online_nums") - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } + override val resultDeserializationStrategy: DeserializationStrategy + get() = OnlineNumsResult.serializer() } -private object OnlineNumsToIntDeserializationStrategy : DeserializationStrategy { - @Serializable - private data class OnlineNumsResult(@SerialName("online_nums") val onlineNums: Int) - - override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("OnlineNums", PrimitiveKind.INT) - - override fun deserialize(decoder: Decoder): Int { - val decoded = OnlineNumsResult.serializer().deserialize(decoder) - return decoded.onlineNums - } -} +/** + * Result of [GetChannelOnlineNumsApi] + */ +@Serializable +public data class OnlineNumsResult(@SerialName("online_nums") val onlineNums: Int) diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/GetGuildChannelListApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/GetGuildChannelListApi.kt index dfe0a9c1..085ca709 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/GetGuildChannelListApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/GetGuildChannelListApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -20,7 +20,6 @@ package love.forte.simbot.qguild.api.channel import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.builtins.ListSerializer import love.forte.simbot.qguild.api.GetQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.SimpleChannel import kotlin.jvm.JvmStatic @@ -47,13 +46,8 @@ public class GetGuildChannelListApi private constructor(guildId: String) : GetQQ public fun create(guildId: String): GetGuildChannelListApi = GetGuildChannelListApi(guildId) } - private val path = arrayOf("guilds", guildId, "channels") + override val path: Array = arrayOf("guilds", guildId, "channels") - override val resultDeserializer: DeserializationStrategy> + override val resultDeserializationStrategy: DeserializationStrategy> get() = serializer - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/ModifyChannelApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/ModifyChannelApi.kt index 8a800bba..4721723c 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/ModifyChannelApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/ModifyChannelApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,18 +17,17 @@ package love.forte.simbot.qguild.api.channel -import io.ktor.http.* import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import love.forte.simbot.qguild.PrivateDomainOnly -import love.forte.simbot.qguild.api.QQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.PatchQQGuildApi +import love.forte.simbot.qguild.api.SimplePatchApiDescription import love.forte.simbot.qguild.model.PrivateType import love.forte.simbot.qguild.model.SimpleChannel import love.forte.simbot.qguild.model.SpeakPermission import kotlin.jvm.JvmStatic +import kotlin.jvm.JvmSynthetic /** @@ -44,9 +43,9 @@ import kotlin.jvm.JvmStatic @PrivateDomainOnly public class ModifyChannelApi private constructor( channelId: String, override val body: Body -) : QQGuildApi() { - public companion object Factory : SimpleApiDescription( - HttpMethod.Patch, "/channels/{channel_id}" +) : PatchQQGuildApi() { + public companion object Factory : SimplePatchApiDescription( + "/channels/{channel_id}" ) { /** @@ -54,21 +53,23 @@ public class ModifyChannelApi private constructor( * */ @JvmStatic - public fun create(channelId: String, body: Body): ModifyChannelApi = ModifyChannelApi(channelId, body) + public fun create(channelId: String, body: Body): ModifyChannelApi = + ModifyChannelApi(channelId, body) + /** + * 使用 [Body.Builder] 构造 [Body] 并将其作为参数构造 [ModifyChannelApi]. + */ + @JvmSynthetic + public inline fun create(channelId: String, block: Body.Builder.() -> Unit): ModifyChannelApi = + create(channelId, Body.builder().also(block).build()) } - private val path = arrayOf("channels", channelId) - - override val resultDeserializer: DeserializationStrategy - get() = SimpleChannel.serializer() + override val path: Array = arrayOf("channels", channelId) - override val method: HttpMethod - get() = HttpMethod.Patch + override fun createBody(): Any? = null - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } + override val resultDeserializationStrategy: DeserializationStrategy + get() = SimpleChannel.serializer() /** * 用于 [ModifyChannelApi] 的请求体。 @@ -98,5 +99,74 @@ public class ModifyChannelApi private constructor( * 子频道发言权限 [SpeakPermission] */ @SerialName("speak_permission") val speakPermission: SpeakPermission? = null - ) + ) { + /** + * [Builder] for [Body] + */ + @Suppress("MemberVisibilityCanBePrivate") + public class Builder { + /** + * @see Body.name + */ + public var name: String? = null + + /** + * @see Body.position + */ + public var position: Int? = null + + /** + * @see Body.parentId + */ + public var parentId: String? = null + + /** + * @see Body.privateType + */ + public var privateType: PrivateType? = null + + /** + * @see Body.speakPermission + */ + public var speakPermission: SpeakPermission? = null + + public fun name(value: String?): Builder = apply { + this.name = value + } + + public fun position(value: Int?): Builder = apply { + this.position = value + } + + public fun parentId(value: String?): Builder = apply { + this.parentId = value + } + + public fun privateType(value: PrivateType?): Builder = apply { + this.privateType = value + } + + public fun speakPermission(value: SpeakPermission?): Builder = apply { + this.speakPermission = value + } + + public fun build(): Body = Body( + name = name, + position = position, + parentId = parentId, + privateType = privateType, + speakPermission = speakPermission, + ) + + } + + public companion object { + /** + * 创建一个 [Builder]. + */ + @JvmStatic + public fun builder(): Builder = Builder() + } + + } } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/permissions/GetChannelMemberPermissionsApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/permissions/GetChannelMemberPermissionsApi.kt index 4e609159..937aa4fb 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/permissions/GetChannelMemberPermissionsApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/permissions/GetChannelMemberPermissionsApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,7 +19,6 @@ package love.forte.simbot.qguild.api.channel.permissions import kotlinx.serialization.DeserializationStrategy import love.forte.simbot.qguild.api.GetQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.ChannelPermissions import kotlin.jvm.JvmStatic @@ -49,12 +48,8 @@ public class GetChannelMemberPermissionsApi private constructor( GetChannelMemberPermissionsApi(channelId, memberId) } - private val path = arrayOf("channels", channelId, "members", memberId, "permissions") + override val path: Array = arrayOf("channels", channelId, "members", memberId, "permissions") - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = ChannelPermissions.serializer() - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/permissions/GetChannelRolePermissionsApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/permissions/GetChannelRolePermissionsApi.kt index de880744..3af64dd5 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/permissions/GetChannelRolePermissionsApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/permissions/GetChannelRolePermissionsApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,7 +19,6 @@ package love.forte.simbot.qguild.api.channel.permissions import kotlinx.serialization.DeserializationStrategy import love.forte.simbot.qguild.api.GetQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.ChannelPermissions import kotlin.jvm.JvmStatic @@ -47,15 +46,10 @@ public class GetChannelRolePermissionsApi( @JvmStatic public fun create(channelId: String, roleId: String): GetChannelRolePermissionsApi = GetChannelRolePermissionsApi(channelId, roleId) - } - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = ChannelPermissions.serializer() - private val path = arrayOf("channels", channelId, "roles", roleId, "permissions") - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } + override val path: Array = arrayOf("channels", channelId, "roles", roleId, "permissions") } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/permissions/ModifyChannelMemberPermissionsApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/permissions/ModifyChannelMemberPermissionsApi.kt index fa9a85bf..b87bc25d 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/permissions/ModifyChannelMemberPermissionsApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/permissions/ModifyChannelMemberPermissionsApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,11 +17,10 @@ package love.forte.simbot.qguild.api.channel.permissions -import io.ktor.http.* import kotlinx.serialization.Serializable +import love.forte.simbot.qguild.api.PutQQGuildApi import love.forte.simbot.qguild.api.QQGuildApiWithoutResult -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.SimplePutApiDescription import love.forte.simbot.qguild.model.Permissions import kotlin.jvm.JvmName import kotlin.jvm.JvmStatic @@ -41,9 +40,9 @@ import kotlin.jvm.JvmStatic public class ModifyChannelMemberPermissionsApi private constructor( channelId: String, memberId: String, private val _body: Body -) : QQGuildApiWithoutResult() { - public companion object Factory : SimpleApiDescription( - HttpMethod.Put, "/channels/{channel_id}/members/{user_id}/permissions" +) : QQGuildApiWithoutResult, PutQQGuildApi() { + public companion object Factory : SimplePutApiDescription( + "/channels/{channel_id}/members/{user_id}/permissions" ) { /** * 构造 [ModifyChannelMemberPermissionsApi]. @@ -81,17 +80,9 @@ public class ModifyChannelMemberPermissionsApi private constructor( } - private val path = arrayOf("channels", channelId, "members", memberId, "permissions") + override val path: Array = arrayOf("channels", channelId, "members", memberId, "permissions") - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val method: HttpMethod - get() = HttpMethod.Put - - override val body: Any - get() = _body + override fun createBody(): Any = _body @Serializable private data class Body( diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/permissions/ModifyChannelRolePermissionsApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/permissions/ModifyChannelRolePermissionsApi.kt index 00ca64a9..28b66952 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/permissions/ModifyChannelRolePermissionsApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/permissions/ModifyChannelRolePermissionsApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,11 +17,10 @@ package love.forte.simbot.qguild.api.channel.permissions -import io.ktor.http.* import kotlinx.serialization.Serializable +import love.forte.simbot.qguild.api.PutQQGuildApi import love.forte.simbot.qguild.api.QQGuildApiWithoutResult -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.SimplePutApiDescription import love.forte.simbot.qguild.model.Permissions import kotlin.jvm.JvmName import kotlin.jvm.JvmStatic @@ -42,9 +41,9 @@ import kotlin.jvm.JvmStatic public class ModifyChannelRolePermissionsApi private constructor( channelId: String, roleId: String, private val _body: Body -) : QQGuildApiWithoutResult() { - public companion object Factory : SimpleApiDescription( - HttpMethod.Put, "/channels/{channel_id}/roles/{role_id}/permissions" +) : QQGuildApiWithoutResult, PutQQGuildApi() { + public companion object Factory : SimplePutApiDescription( + "/channels/{channel_id}/roles/{role_id}/permissions" ) { /** @@ -82,17 +81,9 @@ public class ModifyChannelRolePermissionsApi private constructor( ): ModifyChannelRolePermissionsApi = create(channelId, memberId, remove = remove) } - private val path = arrayOf("channels", channelId, "roles", roleId, "permissions") + override val path: Array = arrayOf("channels", channelId, "roles", roleId, "permissions") - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val method: HttpMethod - get() = HttpMethod.Put - - override val body: Any - get() = _body + override fun createBody(): Any = _body @Serializable private data class Body(val add: Permissions?, val remove: Permissions?) { diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/pins/AddPinsMessageApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/pins/AddPinsMessageApi.kt index cf669def..fc11a787 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/pins/AddPinsMessageApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/pins/AddPinsMessageApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,11 +17,9 @@ package love.forte.simbot.qguild.api.channel.pins -import io.ktor.http.* import kotlinx.serialization.DeserializationStrategy -import love.forte.simbot.qguild.api.QQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.PutQQGuildApi +import love.forte.simbot.qguild.api.SimplePutApiDescription import love.forte.simbot.qguild.model.PinsMessage import kotlin.jvm.JvmStatic @@ -39,8 +37,8 @@ import kotlin.jvm.JvmStatic */ public class AddPinsMessageApi private constructor( channelId: String, messageId: String -) : QQGuildApi() { - public companion object Factory : SimpleApiDescription(HttpMethod.Put, "/channels/{channel_id}/pins/{message_id}") { +) : PutQQGuildApi() { + public companion object Factory : SimplePutApiDescription("/channels/{channel_id}/pins/{message_id}") { /** * 构造一个 [AddPinsMessageApi] @@ -53,16 +51,9 @@ public class AddPinsMessageApi private constructor( AddPinsMessageApi(channelId, messageId) } - private val path = arrayOf("channels", channelId, "pins", messageId) + override val path: Array = arrayOf("channels", channelId, "pins", messageId) + override val resultDeserializationStrategy: DeserializationStrategy get() = PinsMessage.serializer() - override val resultDeserializer: DeserializationStrategy get() = PinsMessage.serializer() - - override val method: HttpMethod get() = HttpMethod.Put - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val body: Any? get() = null + override fun createBody(): Any? = null } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/pins/DeletePinsMessageApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/pins/DeletePinsMessageApi.kt index aea885d1..753b4492 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/pins/DeletePinsMessageApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/pins/DeletePinsMessageApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,10 +17,9 @@ package love.forte.simbot.qguild.api.channel.pins -import io.ktor.http.* +import love.forte.simbot.qguild.api.DeleteQQGuildApi import love.forte.simbot.qguild.api.QQGuildApiWithoutResult -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.SimpleDeleteApiDescription import love.forte.simbot.qguild.api.channel.pins.DeletePinsMessageApi.Factory.DELETE_ALL_MESSAGE_ID import kotlin.jvm.JvmStatic @@ -36,8 +35,8 @@ import kotlin.jvm.JvmStatic */ public class DeletePinsMessageApi private constructor( channelId: String, messageId: String -) : QQGuildApiWithoutResult() { - public companion object Factory : SimpleApiDescription(HttpMethod.Delete, "/channels/{channel_id}/pins/{message_id}") { +) : QQGuildApiWithoutResult, DeleteQQGuildApi() { + public companion object Factory : SimpleDeleteApiDescription("/channels/{channel_id}/pins/{message_id}") { /** * 当要删除的目标为全部时使用的 `message_id` 。 @@ -68,13 +67,5 @@ public class DeletePinsMessageApi private constructor( DeletePinsMessageApi(channelId, DELETE_ALL_MESSAGE_ID) } - private val path = arrayOf("channels", channelId, "pins", messageId) - - override val method: HttpMethod get() = HttpMethod.Delete - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val body: Any? get() = null + override val path: Array = arrayOf("channels", channelId, "pins", messageId) } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/pins/GetPinsMessageApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/pins/GetPinsMessageApi.kt index 0fb7c640..917390a2 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/pins/GetPinsMessageApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/pins/GetPinsMessageApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,10 +17,8 @@ package love.forte.simbot.qguild.api.channel.pins -import io.ktor.http.* import kotlinx.serialization.DeserializationStrategy -import love.forte.simbot.qguild.api.QQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder +import love.forte.simbot.qguild.api.GetQQGuildApi import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.PinsMessage import kotlin.jvm.JvmStatic @@ -35,7 +33,7 @@ import kotlin.jvm.JvmStatic */ public class GetPinsMessageApi private constructor( channelId: String -) : QQGuildApi() { +) : GetQQGuildApi() { public companion object Factory : SimpleGetApiDescription("/channels/{channel_id}/pins") { /** @@ -47,16 +45,7 @@ public class GetPinsMessageApi private constructor( public fun create(channelId: String): GetPinsMessageApi = GetPinsMessageApi(channelId) } - private val path = arrayOf("channels", channelId, "pins") + override val path: Array = arrayOf("channels", channelId, "pins") - - override val resultDeserializer: DeserializationStrategy get() = PinsMessage.serializer() - - override val method: HttpMethod get() = HttpMethod.Get - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val body: Any? get() = null + override val resultDeserializationStrategy: DeserializationStrategy get() = PinsMessage.serializer() } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/CreateScheduleApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/CreateScheduleApi.kt index 368fdeee..a62ee5cc 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/CreateScheduleApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/CreateScheduleApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,7 +19,6 @@ package love.forte.simbot.qguild.api.channel.schedules import kotlinx.serialization.DeserializationStrategy import love.forte.simbot.qguild.api.PostQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimplePostApiDescription import love.forte.simbot.qguild.api.channel.schedules.ScheduleRequestBody.Companion.toCreateBody import love.forte.simbot.qguild.model.Schedule @@ -65,16 +64,12 @@ public class CreateScheduleApi private constructor( @JvmStatic public fun create(channelId: String, schedule: Schedule): CreateScheduleApi = create(channelId, schedule.toCreateBody()) - } - override val resultDeserializer: DeserializationStrategy get() = Schedule.serializer() - - private val path = arrayOf("channels", channelId, "schedules") + override val resultDeserializationStrategy: DeserializationStrategy get() = Schedule.serializer() - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } + override val path: Array = arrayOf("channels", channelId, "schedules") + override fun createBody(): Any? = null } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/DeleteScheduleApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/DeleteScheduleApi.kt index b76c5d44..8f6bc59c 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/DeleteScheduleApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/DeleteScheduleApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,10 +17,9 @@ package love.forte.simbot.qguild.api.channel.schedules -import io.ktor.http.* +import love.forte.simbot.qguild.api.DeleteQQGuildApi import love.forte.simbot.qguild.api.QQGuildApiWithoutResult -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.SimpleDeleteApiDescription import kotlin.jvm.JvmStatic @@ -35,9 +34,9 @@ import kotlin.jvm.JvmStatic */ public class DeleteScheduleApi private constructor( channelId: String, scheduleId: String -) : QQGuildApiWithoutResult() { +) : QQGuildApiWithoutResult, DeleteQQGuildApi() { public companion object Factory : - SimpleApiDescription(HttpMethod.Delete, "/channels/{channel_id}/schedules/{schedule_id}") { + SimpleDeleteApiDescription("/channels/{channel_id}/schedules/{schedule_id}") { /** * 构造 [DeleteScheduleApi] @@ -49,17 +48,8 @@ public class DeleteScheduleApi private constructor( @JvmStatic public fun create(channelId: String, scheduleId: String): DeleteScheduleApi = DeleteScheduleApi(channelId, scheduleId) - } - override val method: HttpMethod get() = HttpMethod.Delete - - private val path = arrayOf("channels", channelId, "schedules", scheduleId) - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } + override val path: Array = arrayOf("channels", channelId, "schedules", scheduleId) - override val body: Any? - get() = null } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/GetScheduleApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/GetScheduleApi.kt index 76a4a8ea..1a1e7d3e 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/GetScheduleApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/GetScheduleApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,7 +19,6 @@ package love.forte.simbot.qguild.api.channel.schedules import kotlinx.serialization.DeserializationStrategy import love.forte.simbot.qguild.api.GetQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.Schedule import kotlin.jvm.JvmStatic @@ -48,11 +47,8 @@ public class GetScheduleApi private constructor( public fun create(channelId: String, scheduleId: String): GetScheduleApi = GetScheduleApi(channelId, scheduleId) } - override val resultDeserializer: DeserializationStrategy get() = Schedule.serializer() + override val resultDeserializationStrategy: DeserializationStrategy get() = Schedule.serializer() - private val path = arrayOf("channels", channelId, "schedules", scheduleId) + override val path: Array = arrayOf("channels", channelId, "schedules", scheduleId) - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/GetScheduleListApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/GetScheduleListApi.kt index 1edf3065..65c5236d 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/GetScheduleListApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/GetScheduleListApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,10 +17,10 @@ package love.forte.simbot.qguild.api.channel.schedules +import io.ktor.http.* import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.builtins.ListSerializer import love.forte.simbot.qguild.api.GetQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.Schedule import kotlin.jvm.JvmStatic @@ -82,14 +82,13 @@ public class GetScheduleListApi private constructor( } } - override val resultDeserializer: DeserializationStrategy> get() = serializer + override val resultDeserializationStrategy: DeserializationStrategy> get() = serializer - private val path = arrayOf("channels", channelId, "schedules") + override val path: Array = arrayOf("channels", channelId, "schedules") - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path + override fun URLBuilder.buildUrl() { if (sinceMark) { - builder.parametersAppender.append("since", since) + parameters.append("since", since.toString()) } } } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/ModifyScheduleApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/ModifyScheduleApi.kt index 6cb081fe..b26f921f 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/ModifyScheduleApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/channel/schedules/ModifyScheduleApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,9 +19,8 @@ package love.forte.simbot.qguild.api.channel.schedules import io.ktor.http.* import kotlinx.serialization.DeserializationStrategy -import love.forte.simbot.qguild.api.QQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.PatchQQGuildApi +import love.forte.simbot.qguild.api.SimplePatchApiDescription import love.forte.simbot.qguild.api.channel.schedules.ScheduleRequestBody.Companion.toCreateBody import love.forte.simbot.qguild.model.Schedule import kotlin.jvm.JvmStatic @@ -37,9 +36,11 @@ import kotlin.jvm.JvmStatic * @author ForteScarlet */ public class ModifyScheduleApi private constructor( - channelId: String, scheduleId: String, override val body: ScheduleRequestBody -) : QQGuildApi() { - public companion object Factory : SimpleApiDescription(HttpMethod.Patch, "/channels/{channel_id}/schedules/{schedule_id}") { + channelId: String, scheduleId: String, + override val body: ScheduleRequestBody +) : PatchQQGuildApi() { + public companion object Factory : + SimplePatchApiDescription("/channels/{channel_id}/schedules/{schedule_id}") { /** * 构造 [ModifyScheduleApi] @@ -78,12 +79,10 @@ public class ModifyScheduleApi private constructor( } - override val resultDeserializer: DeserializationStrategy get() = Schedule.serializer() + override val resultDeserializationStrategy: DeserializationStrategy get() = Schedule.serializer() override val method: HttpMethod get() = HttpMethod.Patch - private val path = arrayOf("channels", channelId, "schedules", scheduleId) + override val path: Array = arrayOf("channels", channelId, "schedules", scheduleId) - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } + override fun createBody(): Any? = null } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/forum/DeleteThreadApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/forum/DeleteThreadApi.kt index 47c78760..0fe42ba2 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/forum/DeleteThreadApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/forum/DeleteThreadApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,11 +17,10 @@ package love.forte.simbot.qguild.api.forum -import io.ktor.http.* import love.forte.simbot.qguild.PrivateDomainOnly +import love.forte.simbot.qguild.api.DeleteQQGuildApi import love.forte.simbot.qguild.api.QQGuildApiWithoutResult -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.SimpleDeleteApiDescription import kotlin.jvm.JvmStatic /** @@ -32,9 +31,9 @@ import kotlin.jvm.JvmStatic * @author ForteScarlet */ @PrivateDomainOnly -public class DeleteThreadApi(channelId: String, threadId: String) : QQGuildApiWithoutResult() { +public class DeleteThreadApi(channelId: String, threadId: String) : QQGuildApiWithoutResult, DeleteQQGuildApi() { public companion object Factory : - SimpleApiDescription(HttpMethod.Delete, "/channels/{channel_id}/threads/{thread_id}") { + SimpleDeleteApiDescription("/channels/{channel_id}/threads/{thread_id}") { /** * 构造 [DeleteThreadApi]. @@ -46,17 +45,7 @@ public class DeleteThreadApi(channelId: String, threadId: String) : QQGuildApiWi @JvmStatic public fun create(channelId: String, threadId: String): DeleteThreadApi = DeleteThreadApi(channelId, threadId) - - } - - override val method: HttpMethod - get() = HttpMethod.Delete - - private val path = arrayOf("channels", channelId, "threads", threadId) - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path } - override val body: Any? get() = null + override val path: Array = arrayOf("channels", channelId, "threads", threadId) } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/forum/GetThreadApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/forum/GetThreadApi.kt index 1f69065b..1544bb80 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/forum/GetThreadApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/forum/GetThreadApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -21,7 +21,6 @@ import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.Serializable import love.forte.simbot.qguild.PrivateDomainOnly import love.forte.simbot.qguild.api.GetQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.forum.Thread import kotlin.jvm.JvmStatic @@ -50,14 +49,10 @@ public class GetThreadApi(channelId: String, threadId: String) : GetQQGuildApi = arrayOf("channels", channelId, "threads", threadId) - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = ThreadInfoResult.serializer() - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } } /** diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/forum/GetThreadListApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/forum/GetThreadListApi.kt index 9ea5cf9a..061e1ef7 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/forum/GetThreadListApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/forum/GetThreadListApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -24,7 +24,6 @@ import love.forte.simbot.qguild.ApiModel import love.forte.simbot.qguild.PrivateDomainOnly import love.forte.simbot.qguild.api.GetQQGuildApi import love.forte.simbot.qguild.api.NumberAsBooleanSerializer -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.forum.Thread import kotlin.jvm.JvmStatic @@ -51,15 +50,10 @@ public class GetThreadListApi private constructor(channelId: String) : GetQQGuil GetThreadListApi(channelId) } - private val path = arrayOf("channels", channelId, "threads") + override val path: Array = arrayOf("channels", channelId, "threads") - - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = ThreadListResult.serializer() - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } } /** diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/forum/PublishThreadApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/forum/PublishThreadApi.kt index 4528ab94..692dd04f 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/forum/PublishThreadApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/forum/PublishThreadApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,14 +17,12 @@ package love.forte.simbot.qguild.api.forum -import io.ktor.http.* import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import love.forte.simbot.qguild.ApiModel -import love.forte.simbot.qguild.api.QQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.PutQQGuildApi +import love.forte.simbot.qguild.api.SimplePutApiDescription import kotlin.jvm.JvmStatic /** @@ -35,8 +33,8 @@ import kotlin.jvm.JvmStatic */ public class PublishThreadApi private constructor( channelId: String, private val _body: Body -) : QQGuildApi() { - public companion object Factory : SimpleApiDescription(HttpMethod.Put, "/channels/{channel_id}/threads") { +) : PutQQGuildApi() { + public companion object Factory : SimplePutApiDescription("/channels/{channel_id}/threads") { /** * 构造 [PublishThreadApi] @@ -59,7 +57,12 @@ public class PublishThreadApi private constructor( * @param format 帖子文本格式。see [ThreadPublishFormat] */ @JvmStatic - public fun create(channelId: String, title: String, content: String, format: ThreadPublishFormat): PublishThreadApi = + public fun create( + channelId: String, + title: String, + content: String, + format: ThreadPublishFormat + ): PublishThreadApi = create(channelId, title, content, format.value) /** @@ -116,19 +119,12 @@ public class PublishThreadApi private constructor( } - override val method: HttpMethod - get() = HttpMethod.Put + override val path: Array = arrayOf("channels", channelId, "threads") - private val path = arrayOf("channels", channelId, "threads") - - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = ThreadPublishResult.serializer() - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val body: Any get() = _body + override fun createBody(): Any = _body @Serializable private data class Body(val title: String, val content: String, val format: Int) diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/guild/GetGuildApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/guild/GetGuildApi.kt index 0f6f088d..4600aff5 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/guild/GetGuildApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/guild/GetGuildApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,10 +17,8 @@ package love.forte.simbot.qguild.api.guild -import io.ktor.http.* import kotlinx.serialization.DeserializationStrategy -import love.forte.simbot.qguild.api.QQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder +import love.forte.simbot.qguild.api.GetQQGuildApi import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.SimpleGuild import kotlin.jvm.JvmStatic @@ -31,7 +29,7 @@ import kotlin.jvm.JvmStatic * 用于获取 `guildId` 指定的频道的详情。 * */ -public class GetGuildApi private constructor(guildId: String) : QQGuildApi() { +public class GetGuildApi private constructor(guildId: String) : GetQQGuildApi() { public companion object Factory : SimpleGetApiDescription( "/guilds/{guild_id}" ) { @@ -44,18 +42,8 @@ public class GetGuildApi private constructor(guildId: String) : QQGuildApi = arrayOf("guilds", guildId) - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = SimpleGuild.serializer() - - override val method: HttpMethod - get() = HttpMethod.Get - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val body: Any? get() = null - } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/guild/mute/MuteAllApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/guild/mute/MuteAllApi.kt index 0dfc5caf..320b8c8c 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/guild/mute/MuteAllApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/guild/mute/MuteAllApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,13 +17,10 @@ package love.forte.simbot.qguild.api.guild.mute -import io.ktor.http.* -import kotlinx.serialization.DeserializationStrategy -import kotlinx.serialization.builtins.serializer -import love.forte.simbot.qguild.api.QQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription -import love.forte.simbot.qguild.time.TimeUnit +import love.forte.simbot.common.time.TimeUnit +import love.forte.simbot.qguild.api.PatchQQGuildApi +import love.forte.simbot.qguild.api.QQGuildApiWithoutResult +import love.forte.simbot.qguild.api.SimplePatchApiDescription import kotlin.jvm.JvmStatic import kotlin.jvm.JvmSynthetic import kotlin.time.Duration @@ -32,15 +29,11 @@ import kotlin.time.Duration /** * @suppress */ -public abstract class BaseMuteAllApi internal constructor(guildId: String, body: MuteBody) : QQGuildApi() { - override val method: HttpMethod get() = HttpMethod.Patch - private val path = arrayOf("guilds", guildId, "mute") - override val resultDeserializer: DeserializationStrategy get() = Unit.serializer() - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val body: Any = body +public abstract class BaseMuteAllApi internal constructor(guildId: String, private val _body: MuteBody) : + QQGuildApiWithoutResult, + PatchQQGuildApi() { + override val path: Array = arrayOf("guilds", guildId, "mute") + override fun createBody(): Any = _body } /** @@ -56,10 +49,9 @@ public abstract class BaseMuteAllApi internal constructor(guildId: String, body: * @author ForteScarlet */ public class MuteAllApi private constructor(guildId: String, body: MuteBody) : BaseMuteAllApi(guildId, body) { - public companion object Factory : SimpleApiDescription( - HttpMethod.Patch, "/guilds/{guild_id}/mute" + public companion object Factory : SimplePatchApiDescription( + "/guilds/{guild_id}/mute" ) { - /** * 使用 `mute_seconds` 的方式构建 [MuteAllApi] * @param muteSeconds 禁言多少秒 @@ -70,7 +62,6 @@ public class MuteAllApi private constructor(guildId: String, body: MuteBody) : B public fun create(guildId: String, muteSeconds: Duration): MuteAllApi = createBySeconds(guildId, muteSeconds.inWholeSeconds) - /** * 使用 `mute_seconds` 的方式构建 [MuteAllApi] * @param muteTime 禁言时长 @@ -88,7 +79,6 @@ public class MuteAllApi private constructor(guildId: String, body: MuteBody) : B @JvmStatic public fun createUnmute(guildId: String): MuteAllApi = MuteAllApi(guildId, MuteBody.Unmute) - private fun createBySeconds(guildId: String, seconds: Long): MuteAllApi { return when { seconds == 0L -> MuteAllApi(guildId, MuteBody.Unmute) @@ -98,19 +88,4 @@ public class MuteAllApi private constructor(guildId: String, body: MuteBody) : B } } - - override val method: HttpMethod - get() = HttpMethod.Patch - - private val path = arrayOf("guilds", guildId, "mute") - - override val resultDeserializer: DeserializationStrategy - get() = Unit.serializer() - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val body: Any = body - } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/guild/mute/MuteMemberApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/guild/mute/MuteMemberApi.kt index 61738c54..8a9eda75 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/guild/mute/MuteMemberApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/guild/mute/MuteMemberApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,11 +17,10 @@ package love.forte.simbot.qguild.api.guild.mute -import io.ktor.http.* +import love.forte.simbot.common.time.TimeUnit +import love.forte.simbot.qguild.api.PatchQQGuildApi import love.forte.simbot.qguild.api.QQGuildApiWithoutResult -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription -import love.forte.simbot.qguild.time.TimeUnit +import love.forte.simbot.qguild.api.SimplePatchApiDescription import kotlin.jvm.JvmStatic import kotlin.jvm.JvmSynthetic import kotlin.time.Duration @@ -37,10 +36,10 @@ import kotlin.time.Duration * * @author ForteScarlet */ -public class MuteMemberApi private constructor(guildId: String, userId: String, body: MuteBody) : - QQGuildApiWithoutResult() { - public companion object Factory : SimpleApiDescription( - HttpMethod.Patch, "/guilds/{guild_id}/members/{user_id}/mute" +public class MuteMemberApi private constructor(guildId: String, userId: String, private val _body: MuteBody) : + QQGuildApiWithoutResult, PatchQQGuildApi() { + public companion object Factory : SimplePatchApiDescription( + "/guilds/{guild_id}/members/{user_id}/mute" ) { /** @@ -82,15 +81,8 @@ public class MuteMemberApi private constructor(guildId: String, userId: String, } } - private val path = arrayOf("guilds", guildId, "members", userId, "mute") + override val path: Array = arrayOf("guilds", guildId, "members", userId, "mute") - override val method: HttpMethod - get() = HttpMethod.Patch - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val body: Any = body + override fun createBody(): Any = _body } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/guild/mute/MuteMultiMemberApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/guild/mute/MuteMultiMemberApi.kt index 77ba938b..40a5af7e 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/guild/mute/MuteMultiMemberApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/guild/mute/MuteMultiMemberApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,15 +17,13 @@ package love.forte.simbot.qguild.api.guild.mute -import io.ktor.http.* import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable +import love.forte.simbot.common.time.TimeUnit import love.forte.simbot.qguild.ApiModel -import love.forte.simbot.qguild.api.QQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription -import love.forte.simbot.qguild.time.TimeUnit +import love.forte.simbot.qguild.api.PatchQQGuildApi +import love.forte.simbot.qguild.api.SimplePatchApiDescription import kotlin.jvm.JvmStatic import kotlin.jvm.JvmSynthetic import kotlin.time.Duration @@ -41,9 +39,10 @@ import kotlin.time.Duration * * @author ForteScarlet */ -public class MuteMultiMemberApi private constructor(guildId: String, body: MuteBody) : QQGuildApi() { - public companion object Factory : SimpleApiDescription( - HttpMethod.Patch, "/guilds/{guild_id}/mute" +public class MuteMultiMemberApi private constructor(guildId: String, private val _body: MuteBody) : + PatchQQGuildApi() { + public companion object Factory : SimplePatchApiDescription( + "/guilds/{guild_id}/mute" ) { /** @@ -84,19 +83,12 @@ public class MuteMultiMemberApi private constructor(guildId: String, body: MuteB } } - private val path = arrayOf("guilds", guildId, "mute") + override val path: Array = arrayOf("guilds", guildId, "mute") - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = MultiMuteResult.serializer() - override val method: HttpMethod - get() = HttpMethod.Patch - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val body: Any = body + override fun createBody(): Any = _body } /** diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/member/DeleteMemberApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/member/DeleteMemberApi.kt index 577d6eef..0aa9ac74 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/member/DeleteMemberApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/member/DeleteMemberApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,15 +17,12 @@ package love.forte.simbot.qguild.api.member -import io.ktor.http.* -import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.builtins.serializer import love.forte.simbot.qguild.PrivateDomainOnly -import love.forte.simbot.qguild.api.QQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.DeleteQQGuildApi +import love.forte.simbot.qguild.api.QQGuildApiWithoutResult +import love.forte.simbot.qguild.api.SimpleDeleteApiDescription import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmStatic @@ -47,9 +44,9 @@ public class DeleteMemberApi private constructor( guildId: String, userId: String, private val _body: Body -) : QQGuildApi() { - public companion object Factory : SimpleApiDescription( - HttpMethod.Delete, "/guilds/{guild_id}/members/{user_id}" +) : QQGuildApiWithoutResult, DeleteQQGuildApi() { + public companion object Factory : SimpleDeleteApiDescription( + "/guilds/{guild_id}/members/{user_id}" ) { /** @@ -71,17 +68,7 @@ public class DeleteMemberApi private constructor( } - private val path = arrayOf("guilds", guildId, "members", userId) - - override val resultDeserializer: DeserializationStrategy - get() = Unit.serializer() - - override val method: HttpMethod - get() = HttpMethod.Delete - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } + override val path: Array = arrayOf("guilds", guildId, "members", userId) override val body: Any get() = _body @@ -104,11 +91,11 @@ public class DeleteMemberApi private constructor( } companion object { - private const val deleteHistoryMsgDaysRange = + private const val DELETE_HISTORY_MSG_DAYS_RANGE = 0 or (1 shl 3) or (1 shl 7) or (1 shl 15) or (1 shl 30) or (1 shl 0) private fun deleteHistoryMsgDaysRangeContains(value: Int): Boolean { - return value <= 30 && (deleteHistoryMsgDaysRange and (1 shl value)) != 0 + return value <= 30 && (DELETE_HISTORY_MSG_DAYS_RANGE and (1 shl value)) != 0 } } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/member/GetGuildMemberListApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/member/GetGuildMemberListApi.kt index 1d571fe3..3aa805eb 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/member/GetGuildMemberListApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/member/GetGuildMemberListApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,6 +17,7 @@ package love.forte.simbot.qguild.api.member +import io.ktor.http.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.serialization.DeserializationStrategy @@ -25,7 +26,6 @@ import love.forte.simbot.logger.LoggerFactory import love.forte.simbot.logger.logger import love.forte.simbot.qguild.PrivateDomainOnly import love.forte.simbot.qguild.api.GetQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.SimpleMember import kotlin.jvm.JvmOverloads @@ -61,9 +61,9 @@ public class GetGuildMemberListApi private constructor( ) : GetQQGuildApi>() { init { require(limit > 0) { "limit must > 0, but $limit" } - if (limit > 400) { + if (limit > MAX_LIMIT) { // or throw error? or ignore? - logger.warn("The maximum value of the limit is 400, but {}", limit) + logger.warn("The maximum value of the limit is $MAX_LIMIT, but {}", limit) } } @@ -96,16 +96,14 @@ public class GetGuildMemberListApi private constructor( GetGuildMemberListApi(guildId, null, limit) } - private val path = arrayOf("guilds", guildId, "members") + override val path: Array = arrayOf("guilds", guildId, "members") - override val resultDeserializer: DeserializationStrategy> + override val resultDeserializationStrategy: DeserializationStrategy> get() = deserializer - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - - after?.also { builder.parametersAppender.append("after", it) } - builder.parametersAppender.append("limit", limit) + override fun URLBuilder.buildUrl() { + after?.also { parameters.append("after", it) } + parameters.append("limit", limit.toString()) } } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/member/GetGuildRoleMemberListApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/member/GetGuildRoleMemberListApi.kt index 36dd1dbc..ecaddec7 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/member/GetGuildRoleMemberListApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/member/GetGuildRoleMemberListApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,6 +17,7 @@ package love.forte.simbot.qguild.api.member +import io.ktor.http.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.serialization.DeserializationStrategy @@ -26,7 +27,6 @@ import love.forte.simbot.logger.logger import love.forte.simbot.qguild.ApiModel import love.forte.simbot.qguild.PrivateDomainOnly import love.forte.simbot.qguild.api.GetQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.SimpleMember import kotlin.jvm.JvmOverloads @@ -92,15 +92,14 @@ public class GetGuildRoleMemberListApi private constructor( ): GetGuildRoleMemberListApi = GetGuildRoleMemberListApi(guildId, roleId, null, limit) } - private val path = arrayOf("guilds", guildId, "roles", roleId, "members") + override val path: Array = arrayOf("guilds", guildId, "roles", roleId, "members") - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = GuildRoleMemberList.serializer() - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - startIndex?.also { builder.parametersAppender.append("start_index", startIndex) } - builder.parametersAppender.append("limit", limit) + override fun URLBuilder.buildUrl() { + startIndex?.also { parameters.append("start_index", startIndex) } + parameters.append("limit", limit.toString()) } } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/member/GetMemberApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/member/GetMemberApi.kt index 72d752b8..48450838 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/member/GetMemberApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/member/GetMemberApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,7 +19,6 @@ package love.forte.simbot.qguild.api.member import kotlinx.serialization.DeserializationStrategy import love.forte.simbot.qguild.api.GetQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.SimpleMember import kotlin.jvm.JvmStatic @@ -46,12 +45,8 @@ public class GetMemberApi private constructor( public fun create(guildId: String, userId: String): GetMemberApi = GetMemberApi(guildId, userId) } - private val path = arrayOf("guilds", guildId, "members", userId) + override val path: Array = arrayOf("guilds", guildId, "members", userId) - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = SimpleMember.serializer() - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/DeleteMessageApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/DeleteMessageApi.kt index adf06a34..fc3b24ea 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/DeleteMessageApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/DeleteMessageApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,9 +19,9 @@ package love.forte.simbot.qguild.api.message import io.ktor.http.* import love.forte.simbot.qguild.PrivateDomainOnly +import love.forte.simbot.qguild.api.DeleteQQGuildApi import love.forte.simbot.qguild.api.QQGuildApiWithoutResult -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.SimpleDeleteApiDescription import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmStatic @@ -38,9 +38,9 @@ import kotlin.jvm.JvmStatic @PrivateDomainOnly public class DeleteMessageApi private constructor( channelId: String, messageId: String, private val hidetip: Boolean? = null -) : QQGuildApiWithoutResult() { - public companion object Factory : SimpleApiDescription( - HttpMethod.Delete, "/channels/{channel_id}/messages/{message_id}" +) : QQGuildApiWithoutResult, DeleteQQGuildApi() { + public companion object Factory : SimpleDeleteApiDescription( + "/channels/{channel_id}/messages/{message_id}" ) { /** @@ -54,18 +54,11 @@ public class DeleteMessageApi private constructor( DeleteMessageApi(channelId, messageId, hidetip) } - override val method: HttpMethod - get() = HttpMethod.Delete - // /channels/{channel_id}/messages/{message_id}?hidetip=false - private val path = arrayOf("channels", channelId, "messages", messageId) + override val path: Array = arrayOf("channels", channelId, "messages", messageId) - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - hidetip?.also { builder.parametersAppender.append("hidetip", it) } + override fun URLBuilder.buildUrl() { + hidetip?.also { parameters.append("hidetip", it.toString()) } } - - override val body: Any? - get() = null } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/GetMessageApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/GetMessageApi.kt index 02c6d7fc..18592b40 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/GetMessageApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/GetMessageApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,7 +19,6 @@ package love.forte.simbot.qguild.api.message import kotlinx.serialization.DeserializationStrategy import love.forte.simbot.qguild.api.GetQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.Message import kotlin.jvm.JvmStatic @@ -43,12 +42,8 @@ public class GetMessageApi private constructor(channelId: String, messageId: Str public fun create(channelId: String, messageId: String): GetMessageApi = GetMessageApi(channelId, messageId) } - private val path = arrayOf("channels", channelId, "messages", messageId) + override val path: Array = arrayOf("channels", channelId, "messages", messageId) - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = Message.serializer() - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.kt index cfe26473..47476280 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -18,10 +18,7 @@ package love.forte.simbot.qguild.api.message -import io.ktor.client.* -import io.ktor.client.request.* import io.ktor.client.request.forms.* -import io.ktor.client.statement.* import io.ktor.http.* import io.ktor.utils.io.core.* import kotlinx.serialization.* @@ -30,10 +27,11 @@ import kotlinx.serialization.encoding.CompositeEncoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule -import love.forte.simbot.qguild.ErrInfo -import love.forte.simbot.qguild.InternalApi -import love.forte.simbot.qguild.QQGuildApiException -import love.forte.simbot.qguild.api.* +import love.forte.simbot.qguild.QGInternalApi +import love.forte.simbot.qguild.QQGuild +import love.forte.simbot.qguild.api.MessageAuditedException +import love.forte.simbot.qguild.api.PostQQGuildApi +import love.forte.simbot.qguild.api.SimplePostApiDescription import love.forte.simbot.qguild.api.message.MessageSendApi.Body.Builder import love.forte.simbot.qguild.message.ContentTextDecoder import love.forte.simbot.qguild.message.ContentTextEncoder @@ -134,93 +132,36 @@ import kotlin.jvm.JvmSynthetic */ public class MessageSendApi private constructor( channelId: String, - body: Body, // TencentMessageForSending || MultiPartFormDataContent -) : QQGuildApi() { + private val _body: Body, // TencentMessageForSending || MultiPartFormDataContent +) : PostQQGuildApi() { public companion object Factory : SimplePostApiDescription( "/channels/{channel_id}/messages" ) { - /** 类似于 [io.ktor.serialization.kotlinx.json.DefaultJson] */ + internal val FormDataHeader = headers { + append(HttpHeaders.ContentType, ContentType.MultiPart.FormData) + } + internal val defaultJson: Json - get() = Json { - encodeDefaults = true - isLenient = true - allowSpecialFloatingPointValues = true - allowStructuredMapKeys = true - prettyPrint = false - useArrayPolymorphism = false - } + get() = QQGuild.DefaultJson /** - * 构造 [MessageSendApi] + * 提供 [Body] 构造 [MessageSendApi] */ @JvmStatic public fun create(channelId: String, body: Body): MessageSendApi = MessageSendApi(channelId, body) } - override val body: Any = body.toRealBody(defaultJson) + override fun createBody(): Any = _body.toRealBody(defaultJson) - private val path = arrayOf("channels", channelId, "messages") + override val path: Array = arrayOf("channels", channelId, "messages") - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = Message.serializer() - override val method: HttpMethod - get() = HttpMethod.Post - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - if (body is MultiPartFormDataContent) { - builder.contentType = ContentType.MultiPart.FormData - } - } - - /** - * 使用当前API发送消息 - * - * @throws MessageAuditedException 当响应状态为表示消息审核的 `304023`、`304024` 时 - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误(http状态码 !in 200 .. 300) - */ - override suspend fun doRequest(client: HttpClient, server: Url, token: String, decoder: StringFormat): Message { - return super.doRequest(client, server, token, decoder) - } - - /** - * 使用当前API发送消息 - * - * - * @throws MessageAuditedException 当响应状态为表示消息审核的 `304023`、`304024` 时 - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误(http状态码 !in 200 .. 300) - */ - override suspend fun doRequestRaw(client: HttpClient, server: Url, token: String): String { - val resp: HttpResponse - val text = requestForText(client, server, token) { resp = it } - - checkStatus(text, DefaultErrInfoDecoder, resp.status) + override val headers: Headers + get() = if (body is MultiPartFormDataContent) FormDataHeader else Headers.Empty - if (text.isEmpty() && resp.status.isSuccess()) { - return "{}" - } - if (resp.status == HttpStatusCode.Accepted) { - // decode as error data - val errorInfo = DefaultErrInfoDecoder.decodeFromString(ErrInfo.serializer(), text) - // maybe audited - if (MessageAuditedException.isAuditResultCode(errorInfo.code)) { - throw MessageAuditedException( - DefaultErrInfoDecoder.decodeFromJsonElement(MessageAudit.serializer(), errorInfo.data).messageAudit, - errorInfo, - resp.status.value, - resp.status.description - ) - } - - throw QQGuildApiException(errorInfo, resp.status.value, resp.status.description) - } - - return text - } /** * [MessageSendApi] 所需参数。详情参考 [文档](https://bot.q.qq.com/wiki/develop/api/openapi/message/post_messages.html#%E9%80%9A%E7%94%A8%E5%8F%82%E6%95%B0) @@ -300,7 +241,7 @@ public class MessageSendApi private constructor( * 各属性参考 [Body] 内同名属性。 * */ - @OptIn(InternalApi::class) + @OptIn(QGInternalApi::class) @Suppress("MemberVisibilityCanBePrivate") public class Builder : BaseMessageSendBodyBuilder() { public var content: String? = null @@ -366,6 +307,12 @@ public class MessageSendApi private constructor( public companion object { + /** + * 得到一个 [Builder]。 + */ + @JvmStatic + public fun builder(): Builder = Builder() + /** * 构造一个 [Body]. * @@ -406,7 +353,7 @@ public inline fun MessageSendApi.Factory.create(channelId: String, builder: Buil * 主要针对 `fileImage`。 */ @Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") -@InternalApi +@QGInternalApi public expect abstract class BaseMessageSendBodyBuilder() { public open var fileImage: Any? protected set @@ -448,7 +395,7 @@ private const val FILE_IMAGE_PROPERTY_NAME = "file_image" * - [resolveOther] support other platform type. */ -@OptIn(InternalApi::class) +@OptIn(QGInternalApi::class) private fun FormBuilder.appendFileImage(fileImage: Any?) { when (fileImage) { is ByteArray -> { @@ -500,7 +447,7 @@ private fun FormBuilder.appendFileImage(fileImage: Any?) { * 类型以外可能支持的类型。 * */ -@InternalApi +@QGInternalApi public expect fun FormBuilder.resolveOther(fileImage: Any?) diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/QGMessageForSending.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/QGMessageForSending.kt index 3102e111..dae61e19 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/QGMessageForSending.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/QGMessageForSending.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -23,48 +23,49 @@ import love.forte.simbot.qguild.model.Message import kotlin.jvm.JvmOverloads +// TODO? /** * 用于发送的普通消息. * * _content, embed, ark, image/file_image, markdown 至少需要有一个字段,否则无法下发消息。_ */ @Serializable -public data class QGMessageForSending @JvmOverloads constructor( +internal data class QGMessageForSending @JvmOverloads constructor( /** * 消息内容,文本内容,支持内嵌格式 */ - public var content: String? = null, + var content: String? = null, /** * MessageEmbed embed 消息,一种特殊的 ark */ - public var embed: Message.Embed? = null, + var embed: Message.Embed? = null, /** * ark消息对象 ark 消息 */ - public var ark: Message.Ark? = null, + var ark: Message.Ark? = null, /** * 图片url地址 */ - public var image: String? = null, + var image: String? = null, /** * 要回复的消息id(Message.id), 在 CREATE_MESSAGE 事件中获取。带了 msg_id 视为被动回复消息,否则视为主动推送消息 */ @SerialName("msg_id") - public var msgId: String? = null, + var msgId: String? = null, /** * 选填,要回复的事件id, 在各事件对象中获取。 */ @SerialName("event_id") - public var eventId: String? = null, + var eventId: String? = null, /** * 选填,markdown 消息 */ - public var markdown: Message.Markdown? = null, + var markdown: Message.Markdown? = null, ) diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/direct/CreateDmsApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/direct/CreateDmsApi.kt index af232498..244f4a09 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/direct/CreateDmsApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/direct/CreateDmsApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,12 +17,10 @@ package love.forte.simbot.qguild.api.message.direct -import io.ktor.http.* import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import love.forte.simbot.qguild.api.PostQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimplePostApiDescription import love.forte.simbot.qguild.model.DirectMessageSession import kotlin.jvm.JvmStatic @@ -45,8 +43,8 @@ import kotlin.jvm.JvmStatic * @author ForteScarlet */ public class CreateDmsApi private constructor( - recipientId: String, - sourceGuildId: String, + private val recipientId: String, + private val sourceGuildId: String, ) : PostQQGuildApi() { public companion object Factory : SimplePostApiDescription( "/users/@me/dms" @@ -66,14 +64,13 @@ public class CreateDmsApi private constructor( CreateDmsApi(recipientId, sourceGuildId) } - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = DirectMessageSession.serializer() - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = pathSegments - } + override val path: Array + get() = pathSegments - override val body: Any = Body(recipientId, sourceGuildId) + override fun createBody(): Any = Body(recipientId, sourceGuildId) @Serializable private data class Body( diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/direct/DeleteDmsApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/direct/DeleteDmsApi.kt index 32575e8f..4a1d6cd7 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/direct/DeleteDmsApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/direct/DeleteDmsApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -18,12 +18,10 @@ package love.forte.simbot.qguild.api.message.direct import io.ktor.http.* -import kotlinx.serialization.DeserializationStrategy -import kotlinx.serialization.builtins.serializer import love.forte.simbot.qguild.PrivateDomainOnly -import love.forte.simbot.qguild.api.QQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.DeleteQQGuildApi +import love.forte.simbot.qguild.api.QQGuildApiWithoutResult +import love.forte.simbot.qguild.api.SimpleDeleteApiDescription import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmStatic @@ -41,9 +39,9 @@ public class DeleteDmsApi private constructor( guildId: String, messageId: String, private val hidetip: Boolean? = null, -) : QQGuildApi() { - public companion object Factory : SimpleApiDescription( - HttpMethod.Delete, "/dms/{guild_id}/messages/{message_id}" +) : QQGuildApiWithoutResult, DeleteQQGuildApi() { + public companion object Factory : SimpleDeleteApiDescription( + "/dms/{guild_id}/messages/{message_id}" ) { /** @@ -56,23 +54,11 @@ public class DeleteDmsApi private constructor( @JvmOverloads public fun create(guildId: String, messageId: String, hidetip: Boolean? = null): DeleteDmsApi = DeleteDmsApi(guildId, messageId, hidetip) - } - override val resultDeserializer: DeserializationStrategy - get() = Unit.serializer() - - override val method: HttpMethod - get() = HttpMethod.Delete - - private val path = arrayOf("dms", guildId, "messages", messageId) - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - hidetip?.also { builder.parametersAppender.append("hidetip", it) } + override val path: Array = arrayOf("dms", guildId, "messages", messageId) + override fun URLBuilder.buildUrl() { + hidetip?.also { parameters.append("hidetip", it.toString()) } } - - override val body: Any? - get() = null } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/direct/DmsSendApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/direct/DmsSendApi.kt index b883db1a..4f0a66dd 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/direct/DmsSendApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/direct/DmsSendApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,16 +17,11 @@ package love.forte.simbot.qguild.api.message.direct -import io.ktor.client.* -import io.ktor.client.request.* import io.ktor.client.request.forms.* -import io.ktor.client.statement.* import io.ktor.http.* import kotlinx.serialization.DeserializationStrategy -import kotlinx.serialization.StringFormat -import love.forte.simbot.qguild.ErrInfo -import love.forte.simbot.qguild.QQGuildApiException -import love.forte.simbot.qguild.api.* +import love.forte.simbot.qguild.api.PostQQGuildApi +import love.forte.simbot.qguild.api.SimplePostApiDescription import love.forte.simbot.qguild.api.message.MessageSendApi import love.forte.simbot.qguild.api.message.toRealBody import love.forte.simbot.qguild.model.Message @@ -59,7 +54,7 @@ import kotlin.jvm.JvmSynthetic */ public class DmsSendApi private constructor( guildId: String, - body: MessageSendApi.Body, // TencentMessageForSending || MultiPartFormDataContent + private val _body: MessageSendApi.Body, // TencentMessageForSending || MultiPartFormDataContent ) : PostQQGuildApi() { public companion object Factory : SimplePostApiDescription( "/channels/{channel_id}/messages" @@ -72,71 +67,15 @@ public class DmsSendApi private constructor( public fun create(guildId: String, body: MessageSendApi.Body): DmsSendApi = DmsSendApi(guildId, body) } - override val body: Any = body.toRealBody(MessageSendApi.defaultJson) + override fun createBody(): Any = _body.toRealBody(MessageSendApi.defaultJson) - private val path = arrayOf("dms", guildId, "messages") + override val path: Array = arrayOf("dms", guildId, "messages") - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = Message.serializer() - override val method: HttpMethod - get() = HttpMethod.Post - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - if (body is MultiPartFormDataContent) { - builder.contentType = ContentType.MultiPart.FormData - } - } - - /** - * 使用当前API发送消息 - * - * @throws MessageAuditedException 当响应状态为表示消息审核的 `304023`、`304024` 时 - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误(http状态码 !in 200 .. 300) - */ - override suspend fun doRequest(client: HttpClient, server: Url, token: String, decoder: StringFormat): Message { - return super.doRequest(client, server, token, decoder) - } - - /** - * 使用当前API发送消息 - * - * - * @throws MessageAuditedException 当响应状态为表示消息审核的 `304023`、`304024` 时 - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误(http状态码 !in 200 .. 300) - */ - override suspend fun doRequestRaw(client: HttpClient, server: Url, token: String): String { - val resp: HttpResponse - val text = requestForText(client, server, token) { resp = it } - - checkStatus(text, DefaultErrInfoDecoder, resp.status) - - if (text.isEmpty() && resp.status.isSuccess()) { - return "{}" - } - - if (resp.status == HttpStatusCode.Accepted) { - // decode as error data - val errorInfo = DefaultErrInfoDecoder.decodeFromString(ErrInfo.serializer(), text) - // maybe audited - if (MessageAuditedException.isAuditResultCode(errorInfo.code)) { - throw MessageAuditedException( - DefaultErrInfoDecoder.decodeFromJsonElement(MessageAudit.serializer(), errorInfo.data).messageAudit, - errorInfo, - resp.status.value, - resp.status.description - ) - } - - throw QQGuildApiException(errorInfo, resp.status.value, resp.status.description) - } - - return text - } - + override val headers: Headers + get() = if (body is MultiPartFormDataContent) MessageSendApi.FormDataHeader else Headers.Empty } /** diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/setting/GetMessageSettingApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/setting/GetMessageSettingApi.kt index 15c0fd93..71f5f035 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/setting/GetMessageSettingApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/setting/GetMessageSettingApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,7 +19,6 @@ package love.forte.simbot.qguild.api.message.setting import kotlinx.serialization.DeserializationStrategy import love.forte.simbot.qguild.api.GetQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.MessageSetting import kotlin.jvm.JvmStatic @@ -43,12 +42,8 @@ public class GetMessageSettingApi private constructor(guildId: String) : GetQQGu public fun create(guildId: String): GetMessageSettingApi = GetMessageSettingApi(guildId) } - private val path = arrayOf("guilds", guildId, "message", "setting") + override val path: Array = arrayOf("guilds", guildId, "message", "setting") - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = MessageSetting.serializer() - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/AddMemberRoleApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/AddMemberRoleApi.kt index 4f289c0d..61323297 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/AddMemberRoleApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/AddMemberRoleApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,11 +17,10 @@ package love.forte.simbot.qguild.api.role -import io.ktor.http.* import kotlinx.serialization.Serializable +import love.forte.simbot.qguild.api.PutQQGuildApi import love.forte.simbot.qguild.api.QQGuildApiWithoutResult -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.SimplePutApiDescription import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmStatic @@ -41,10 +40,10 @@ public class AddMemberRoleApi private constructor( guildId: String, userId: String, roleId: String, - channelId: String?, -) : QQGuildApiWithoutResult() { - public companion object Factory : SimpleApiDescription( - HttpMethod.Put, "/guilds/{guild_id}/members/{user_id}/roles/{role_id}" + private val channelId: String?, +) : QQGuildApiWithoutResult, PutQQGuildApi() { + public companion object Factory : SimplePutApiDescription( + "/guilds/{guild_id}/members/{user_id}/roles/{role_id}" ) { /** @@ -56,28 +55,20 @@ public class AddMemberRoleApi private constructor( @JvmStatic @JvmOverloads public fun create( - guildId: String, - userId: String, - roleId: String, + guildId: String, userId: String, roleId: String, channelId: String? = null ): AddMemberRoleApi = AddMemberRoleApi(guildId, userId, roleId, channelId) } - private val path = arrayOf( + override val path: Array = arrayOf( "guilds", guildId, "members", userId, "roles", roleId, ) - override val method: HttpMethod - get() = HttpMethod.Put - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val body: Any? = channelId?.let { cid -> Body(ChannelId(cid)) } + override fun createBody(): Any? = + channelId?.let { cid -> Body(ChannelId(cid)) } @Serializable private data class Body(val channel: ChannelId) diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/CreateGuildRoleApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/CreateGuildRoleApi.kt index 52282abc..c6a39521 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/CreateGuildRoleApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/CreateGuildRoleApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -22,7 +22,6 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import love.forte.simbot.qguild.ApiModel import love.forte.simbot.qguild.api.PostQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimplePostApiDescription import love.forte.simbot.qguild.model.ColorIntSerializer import love.forte.simbot.qguild.model.Role @@ -68,16 +67,12 @@ public class CreateGuildRoleApi private constructor( CreateGuildRoleApi(guildId, NewBody(name, color, hoist)) } - private val path = arrayOf("guilds", guildId, "roles") + override val path: Array = arrayOf("guilds", guildId, "roles") - override val resultDeserializer: DeserializationStrategy get() = GuildRoleCreated.serializer() - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val body: Any get() = _body + override val resultDeserializationStrategy: DeserializationStrategy + get() = GuildRoleCreated.serializer() + override fun createBody(): Any? = _body @Serializable private data class NewBody( diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/DeleteGuildRoleApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/DeleteGuildRoleApi.kt index 925fbd57..3ec57960 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/DeleteGuildRoleApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/DeleteGuildRoleApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,10 +17,9 @@ package love.forte.simbot.qguild.api.role -import io.ktor.http.* +import love.forte.simbot.qguild.api.DeleteQQGuildApi import love.forte.simbot.qguild.api.QQGuildApiWithoutResult -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.SimpleDeleteApiDescription import kotlin.jvm.JvmStatic /** @@ -32,26 +31,19 @@ import kotlin.jvm.JvmStatic * 需要使用的 `token` 对应的用户具备删除身份组权限。如果是机器人,要求被添加为管理员。 * @author ForteScarlet */ -public class DeleteGuildRoleApi(guildId: String, roleId: String) : QQGuildApiWithoutResult() { - public companion object Factory : SimpleApiDescription( - HttpMethod.Delete, "/guilds/{guild_id}/roles/{role_id}" +public class DeleteGuildRoleApi(guildId: String, roleId: String) : + QQGuildApiWithoutResult, DeleteQQGuildApi() { + public companion object Factory : SimpleDeleteApiDescription( + "/guilds/{guild_id}/roles/{role_id}" ) { - + /** * 构造 [DeleteGuildRoleApi] */ @JvmStatic - public fun create(guildId: String, roleId: String): DeleteGuildRoleApi = DeleteGuildRoleApi(guildId, roleId) + public fun create(guildId: String, roleId: String): DeleteGuildRoleApi = + DeleteGuildRoleApi(guildId, roleId) } - private val path = arrayOf("guilds", guildId, "roles", roleId) - - override val method: HttpMethod - get() = HttpMethod.Delete - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val body: Any? get() = null + override val path: Array = arrayOf("guilds", guildId, "roles", roleId) } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/GetGuildRoleListApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/GetGuildRoleListApi.kt index 368d7d63..ed382b4e 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/GetGuildRoleListApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/GetGuildRoleListApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,10 +17,12 @@ package love.forte.simbot.qguild.api.role -import kotlinx.serialization.* -import love.forte.simbot.* -import love.forte.simbot.qguild.* -import love.forte.simbot.qguild.api.* +import kotlinx.serialization.DeserializationStrategy +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +import love.forte.simbot.qguild.ApiModel +import love.forte.simbot.qguild.api.GetQQGuildApi +import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.Role import kotlin.jvm.JvmStatic @@ -44,14 +46,9 @@ public class GetGuildRoleListApi private constructor(guildId: String) : GetQQGui public fun create(guildId: String): GetGuildRoleListApi = GetGuildRoleListApi(guildId) } - private val path = arrayOf("guilds", guildId, "roles") - override val resultDeserializer: DeserializationStrategy get() = GuildRoleList.serializer() - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - + override val path: Array = arrayOf("guilds", guildId, "roles") + override val resultDeserializationStrategy: DeserializationStrategy + get() = GuildRoleList.serializer() } /** diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/ModifyGuildRoleApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/ModifyGuildRoleApi.kt index fb997ae3..a1a3ed32 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/ModifyGuildRoleApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/ModifyGuildRoleApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -21,8 +21,7 @@ import io.ktor.http.* import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import love.forte.simbot.qguild.api.QQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder +import love.forte.simbot.qguild.api.PatchQQGuildApi import love.forte.simbot.qguild.api.SimpleApiDescription import love.forte.simbot.qguild.model.ColorIntSerializer import love.forte.simbot.qguild.model.Role @@ -42,7 +41,7 @@ import kotlin.jvm.JvmStatic public class ModifyGuildRoleApi private constructor( guildId: String, roleId: String, private val _body: Body, -) : QQGuildApi() { +) : PatchQQGuildApi() { public companion object Factory : SimpleApiDescription( HttpMethod.Patch, "/guilds/{guild_id}/roles/{role_id}" ) { @@ -64,22 +63,19 @@ public class ModifyGuildRoleApi private constructor( } - private val path = arrayOf("guilds", guildId, "roles", roleId) + override val path: Array = arrayOf("guilds", guildId, "roles", roleId) - override val resultDeserializer: DeserializationStrategy + override val resultDeserializationStrategy: DeserializationStrategy get() = GuildRoleModified.serializer() - override val method: HttpMethod - get() = HttpMethod.Patch - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - - override val body: Any get() = _body + override fun createBody(): Any = _body @Serializable - private data class Body(val name: String?, @Serializable(ColorIntSerializer::class) val color: Int?, val hoist: Int?) { + private data class Body( + val name: String?, + @Serializable(ColorIntSerializer::class) val color: Int?, + val hoist: Int? + ) { init { require(name != null || color != null || hoist != null) { "At least one of the parameters should not be null" diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/RemoveMemberRoleApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/RemoveMemberRoleApi.kt index 6f82c81c..d777c335 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/RemoveMemberRoleApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/role/RemoveMemberRoleApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,11 +17,10 @@ package love.forte.simbot.qguild.api.role -import io.ktor.http.* import kotlinx.serialization.Serializable +import love.forte.simbot.qguild.api.DeleteQQGuildApi import love.forte.simbot.qguild.api.QQGuildApiWithoutResult -import love.forte.simbot.qguild.api.RouteInfoBuilder -import love.forte.simbot.qguild.api.SimpleApiDescription +import love.forte.simbot.qguild.api.SimpleDeleteApiDescription import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmStatic @@ -41,11 +40,11 @@ public class RemoveMemberRoleApi private constructor( userId: String, roleId: String, channelId: String?, -) : QQGuildApiWithoutResult() { - public companion object Factory : SimpleApiDescription( - HttpMethod.Delete, "/guilds/{guild_id}/members/{user_id}/roles/{role_id}" +) : QQGuildApiWithoutResult, DeleteQQGuildApi() { + public companion object Factory : SimpleDeleteApiDescription( + "/guilds/{guild_id}/members/{user_id}/roles/{role_id}" ) { - + /** * 构造 [RemoveMemberRoleApi] * @@ -61,29 +60,19 @@ public class RemoveMemberRoleApi private constructor( channelId: String? = null, ): RemoveMemberRoleApi = RemoveMemberRoleApi(guildId, userId, roleId, channelId) } - - private val path = arrayOf( - "guilds", - guildId, - "members", - userId, - "roles", - roleId, + + override val path: Array = arrayOf( + "guilds", guildId, + "members", userId, + "roles", roleId, ) - - override val method: HttpMethod - get() = HttpMethod.Delete - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = path - } - override val body: Any? = channelId?.let { cid -> Body(ChannelId(cid)) } + override val body: Any? = + channelId?.let { cid -> Body(ChannelId(cid)) } @Serializable private data class Body(val channel: ChannelId) @Serializable private data class ChannelId(val id: String) - } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/user/GetBotGuildListApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/user/GetBotGuildListApi.kt index a354eb3f..fc64edfc 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/user/GetBotGuildListApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/user/GetBotGuildListApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,12 +17,12 @@ package love.forte.simbot.qguild.api.user +import io.ktor.http.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.builtins.ListSerializer import love.forte.simbot.qguild.api.GetQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.model.SimpleGuild import kotlin.jvm.JvmOverloads @@ -106,30 +106,28 @@ public class GetBotGuildListApi private constructor( public fun createByAfter(after: String, limit: Int = DEFAULT_LIMIT): GetBotGuildListApi = create(before = null, after = after, limit) - } - - override val resultDeserializer: DeserializationStrategy> + override val resultDeserializationStrategy: DeserializationStrategy> get() = serializer - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = route - if (before != null) { - builder.parametersAppender.append("before", before.toString()) - } - if (after != null) { - builder.parametersAppender.append("after", after.toString()) - } - val limit = limit - if (limit > 0) { - builder.parametersAppender.append("limit", limit) + override val path: Array + get() = route + + override fun URLBuilder.buildUrl() { + parameters.apply { + if (before != null) { + append("before", before.toString()) + } + if (after != null) { + append("after", after.toString()) + } + val limit = limit + if (limit > 0) { + append("limit", limit.toString()) + } } } - - override val body: Any? - get() = null - } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/user/GetBotInfoApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/user/GetBotInfoApi.kt index 87196fbf..2e322020 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/user/GetBotInfoApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/user/GetBotInfoApi.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -22,7 +22,6 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import love.forte.simbot.qguild.api.ApiDescription import love.forte.simbot.qguild.api.GetQQGuildApi -import love.forte.simbot.qguild.api.RouteInfoBuilder import love.forte.simbot.qguild.api.SimpleGetApiDescription import love.forte.simbot.qguild.api.user.GetBotInfoApi.Description import love.forte.simbot.qguild.model.User @@ -46,12 +45,8 @@ public object GetBotInfoApi : GetQQGuildApi() { */ public object Description : SimpleGetApiDescription("/users/@me") - private val pathSec = arrayOf("users", "@me") - override val resultDeserializer: DeserializationStrategy = BotInfoDeserializationStrategy - - override fun route(builder: RouteInfoBuilder) { - builder.apiPath = pathSec - } + override val path: Array = arrayOf("users", "@me") + override val resultDeserializationStrategy: DeserializationStrategy = BotInfoDeserializationStrategy } private object BotInfoDeserializationStrategy : DeserializationStrategy { diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/Signal.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/Signal.kt index 37570929..08cca7f8 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/Signal.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/Signal.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -26,7 +26,7 @@ import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.* -import love.forte.simbot.qguild.InternalApi +import love.forte.simbot.qguild.QGInternalApi import love.forte.simbot.qguild.event.Signal.Dispatch.Unknown import love.forte.simbot.qguild.event.Signal.Resume.Data import kotlin.jvm.JvmField @@ -212,7 +212,7 @@ public sealed class Signal(@Serializable(Opcode.SerializerByCode::class) publ * [Unknown] 的构建仅由内部完成。 * */ - public data class Unknown @InternalApi constructor(override val s: Long, override val data: JsonElement, val raw: String) : Dispatch() + public data class Unknown @QGInternalApi constructor(override val s: Long, override val data: JsonElement, val raw: String) : Dispatch() } } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/model/Announces.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/model/Announces.kt index 96677b5f..eae08f9f 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/model/Announces.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/model/Announces.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,8 +19,8 @@ package love.forte.simbot.qguild.model import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import love.forte.simbot.qguild.Api4J import love.forte.simbot.qguild.ApiModel +import love.forte.simbot.qguild.QGApi4J import kotlin.jvm.JvmName @@ -47,7 +47,7 @@ public data class Announces( * 获取 [announcesType] 的结果并从 [Unsigned integer type](https://kotlinlang.org/docs/unsigned-integer-types.html) 转为Java可用的 [Int] * */ - @Api4J + @QGApi4J public val announcesTypeIntValue: Int get() = announcesType.toInt() } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/time/Instants.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/time/Instants.kt index b3ea6fc6..a90e9f6b 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/time/Instants.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/time/Instants.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,6 +17,7 @@ package love.forte.simbot.qguild.time + /** * 用在一些不支持获取时间的地方使用的默认值 `"0000-01-01T08:00:00.000+08:00"`。 * diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/time/TimeUnit.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/time/TimeUnit.kt deleted file mode 100644 index 0ae6e653..00000000 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/time/TimeUnit.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.qguild.time - -/** - * 时间单位,用于时间转化。 - * 在 JVM 中会使用 `java.util.concurrent.TimeUnit`, - * 其他平台则会参照进行简单实现。 - */ -public expect enum class TimeUnit { - NANOSECONDS, - MICROSECONDS, - MILLISECONDS, - SECONDS, - MINUTES, - HOURS, - DAYS; - - - /** - * 转为 Nanos - */ - public open fun toNanos(duration: Long): Long - - /** - * 转为 Micros - */ - public open fun toMicros(duration: Long): Long - - /** - * 转为 Millis - */ - public open fun toMillis(duration: Long): Long - - /** - * 转为 Seconds - */ - public open fun toSeconds(duration: Long): Long - - /** - * 转为 Minutes - */ - public open fun toMinutes(duration: Long): Long - - /** - * 转为 Hours - */ - public open fun toHours(duration: Long): Long - - /** - * 转为 Days - */ - public open fun toDays(duration: Long): Long - -} diff --git a/simbot-component-qq-guild-api/src/commonTest/kotlin/TimeUnitTest.kt b/simbot-component-qq-guild-api/src/commonTest/kotlin/TimeUnitTest.kt deleted file mode 100644 index bd7e61ba..00000000 --- a/simbot-component-qq-guild-api/src/commonTest/kotlin/TimeUnitTest.kt +++ /dev/null @@ -1,15 +0,0 @@ -package test - -import love.forte.simbot.qguild.time.TimeUnit -import kotlin.test.Test -import kotlin.test.assertEquals - - -class TimeUnitTest { - - @Test - fun timeUnitTest() { - assertEquals(TimeUnit.DAYS.toSeconds(30), TimeUnit.HOURS.toSeconds(30 * 24)) - } - -} diff --git a/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.js.kt b/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.js.kt index a55690fb..18416d0d 100644 --- a/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.js.kt +++ b/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.js.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,87 +17,79 @@ package love.forte.simbot.qguild.api -import io.ktor.client.* -import io.ktor.client.request.* import io.ktor.http.* -import kotlinx.coroutines.CoroutineName -import kotlinx.coroutines.promise -import kotlinx.serialization.StringFormat -import love.forte.simbot.qguild.ErrInfo -import love.forte.simbot.qguild.InternalApi -import kotlin.js.Promise - -/** - * 用于多平台实现的最小目标。 - * - * 在JVM平台和JS平台中分别提供对应的 blocking/async 兼容函数。 - * 但是不应追加新的抽象函数。 - */ -@InternalApi -public actual abstract class PlatformQQGuildApi actual constructor() { - - /** - * 使用此api发起一次请求,并得到预期中的结果。 - * - * @see love.forte.simbot.qguild.ErrInfo - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误。 - */ - public actual abstract suspend fun doRequest( - client: HttpClient, - server: Url, - token: String, - decoder: StringFormat - ): R - - - /** - * 使用此api发起一次请求,并得到预期中的结果。 - * - * @see ErrInfo - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误。 - */ - public actual abstract suspend fun doRequestRaw( - client: HttpClient, - server: Url, - token: String, - ): String - - /** - * 使用此api发起一次请求,并得到预期中的结果。 - * - * @see doRequestRaw - */ - public fun doRequestAsync( - client: HttpClient, - server: Url, - token: String, - decoder: StringFormat - ): Promise { - return client.promise(COROUTINE_NAME) { doRequest(client, server, token, decoder) } - } - - /** - * 使用此api发起一次请求,并得到预期中的结果。 - * - * @see doRequestRaw - */ - public fun doRequestRawAsync( - client: HttpClient, - server: Url, - token: String, - ): Promise { - return client.promise(COROUTINE_NAME) { doRequestRaw(client, server, token) } - } - - public actual companion object { - private val COROUTINE_NAME = CoroutineName("Api-Async-Scope") - } -} +// +///** +// * 用于多平台实现的最小目标。 +// * +// * 在JVM平台和JS平台中分别提供对应的 blocking/async 兼容函数。 +// * 但是不应追加新的抽象函数。 +// */ +//@QGInternalApi +//public actual abstract class PlatformQQGuildApi actual constructor() { +// +// /** +// * 使用此api发起一次请求,并得到预期中的结果。 +// * +// * @see love.forte.simbot.qguild.ErrInfo +// * +// * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 +// * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误。 +// */ +// public actual abstract suspend fun doRequest( +// client: HttpClient, +// server: Url, +// token: String, +// decoder: StringFormat +// ): R +// +// +// /** +// * 使用此api发起一次请求,并得到预期中的结果。 +// * +// * @see ErrInfo +// * +// * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 +// * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误。 +// */ +// public actual abstract suspend fun doRequestRaw( +// client: HttpClient, +// server: Url, +// token: String, +// ): String +// +// /** +// * 使用此api发起一次请求,并得到预期中的结果。 +// * +// * @see doRequestRaw +// */ +// public fun doRequestAsync( +// client: HttpClient, +// server: Url, +// token: String, +// decoder: StringFormat +// ): Promise { +// return client.promise(COROUTINE_NAME) { doRequest(client, server, token, decoder) } +// } +// +// /** +// * 使用此api发起一次请求,并得到预期中的结果。 +// * +// * @see doRequestRaw +// */ +// public fun doRequestRawAsync( +// client: HttpClient, +// server: Url, +// token: String, +// ): Promise { +// return client.promise(COROUTINE_NAME) { doRequestRaw(client, server, token) } +// } +// +// public actual companion object { +// private val COROUTINE_NAME = CoroutineName("Api-Async-Scope") +// } +//} /** * 日志对齐 diff --git a/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.js.kt b/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.js.kt index cc8faa28..ab69e34e 100644 --- a/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.js.kt +++ b/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.js.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -18,7 +18,7 @@ package love.forte.simbot.qguild.api.message import io.ktor.client.request.forms.* -import love.forte.simbot.qguild.InternalApi +import love.forte.simbot.qguild.QGInternalApi /** * @@ -28,7 +28,7 @@ import love.forte.simbot.qguild.InternalApi * * */ -@InternalApi +@QGInternalApi public actual fun FormBuilder.resolveOther(fileImage: Any?) { } @@ -37,7 +37,7 @@ public actual fun FormBuilder.resolveOther(fileImage: Any?) { * 提供一些需要由不同平台额外实现的基类。 * 主要针对 `fileImage`。 */ -@InternalApi +@QGInternalApi public actual abstract class BaseMessageSendBodyBuilder actual constructor() { public actual open var fileImage: Any? = null protected set diff --git a/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/time/TimeUnit.js.kt b/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/time/TimeUnit.js.kt deleted file mode 100644 index 6a8ff3bc..00000000 --- a/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/time/TimeUnit.js.kt +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.qguild.time - -private const val NANO_SCALE = 1L -private const val MICRO_SCALE = 1000L * NANO_SCALE -private const val MILLI_SCALE = 1000L * MICRO_SCALE -private const val SECOND_SCALE = 1000L * MILLI_SCALE -private const val MINUTE_SCALE = 60L * SECOND_SCALE -private const val HOUR_SCALE = 60L * MINUTE_SCALE -private const val DAY_SCALE = 24L * HOUR_SCALE - -/** - * 时间单位,用于时间转化。 - */ -public actual enum class TimeUnit(private val scale: Long) { - NANOSECONDS(NANO_SCALE), - MICROSECONDS(MICRO_SCALE), - MILLISECONDS(MILLI_SCALE), - SECONDS(SECOND_SCALE), - MINUTES(MINUTE_SCALE), - HOURS(HOUR_SCALE), - DAYS(DAY_SCALE); - - private val maxNanos: Long = Long.MAX_VALUE / scale - private val maxMicros: Long - private val maxMillis: Long - private val maxSecs: Long - private val microRatio: Long - private val milliRatio: Int - private val secRatio: Int - - init { - val ur: Long = - if (scale >= MICRO_SCALE) scale / MICRO_SCALE else MICRO_SCALE / scale - microRatio = ur - maxMicros = Long.MAX_VALUE / ur - val mr: Long = - if (scale >= MILLI_SCALE) scale / MILLI_SCALE else MILLI_SCALE / scale - milliRatio = mr.toInt() - maxMillis = Long.MAX_VALUE / mr - val sr: Long = - if (scale >= SECOND_SCALE) scale / SECOND_SCALE else SECOND_SCALE / scale - secRatio = sr.toInt() - maxSecs = Long.MAX_VALUE / sr - } - - /** - * 转为 Nanos - */ - public actual open fun toNanos(duration: Long): Long { - return when { - scale == NANO_SCALE -> duration - duration > maxNanos -> Long.MAX_VALUE - duration < -maxNanos -> Long.MIN_VALUE - else -> duration * scale - } - } - - /** - * 转为 Micros - */ - public actual open fun toMicros(duration: Long): Long { - return when { - scale <= MICRO_SCALE -> if (scale == MICRO_SCALE) { - duration - } else { - duration / microRatio - } - duration > maxMicros -> Long.MAX_VALUE - duration < -maxMicros -> Long.MIN_VALUE - else -> duration * microRatio - } - } - - /** - * 转为 Millis - */ - public actual open fun toMillis(duration: Long): Long { - return when { - scale <= MILLI_SCALE -> if (scale == MILLI_SCALE) { - duration - } else { - duration / milliRatio - } - duration > maxMillis -> Long.MAX_VALUE - duration < -maxMillis -> Long.MIN_VALUE - else -> duration * milliRatio - } - } - - /** - * 转为 Seconds - */ - public actual open fun toSeconds(duration: Long): Long { - return when { - scale <= SECOND_SCALE -> if (scale == SECOND_SCALE) { - duration - } else { - duration / secRatio - } - duration > maxSecs -> Long.MAX_VALUE - duration < -maxSecs -> Long.MIN_VALUE - else -> duration * secRatio - } - } - - /** - * 转为 Minutes - */ - public actual open fun toMinutes(duration: Long): Long { - return when { - scale <= SECOND_SCALE -> if (scale == SECOND_SCALE) { - duration - } else { - duration / secRatio - } - duration > maxSecs -> Long.MAX_VALUE - duration < -maxSecs -> Long.MIN_VALUE - else -> duration * secRatio - } - } - - /** - * 转为 Hours - */ - public actual open fun toHours(duration: Long): Long { - return cvt(duration, HOUR_SCALE, scale) - } - - /** - * 转为 Days - */ - public actual open fun toDays(duration: Long): Long { - return cvt(duration, DAY_SCALE, scale) - } - - -} - -/** - * General conversion utility. - * - * @param d duration - * @param dst result unit scale - * @param src source unit scale - */ -private fun cvt(d: Long, dst: Long, src: Long): Long { - var r: Long - return when { - src == dst -> d - src < dst -> d / (dst / src) - d > (Long.MAX_VALUE / (src / dst).also { r = it }) -> Long.MAX_VALUE - d < -Long.MAX_VALUE -> Long.MIN_VALUE - else -> d * r - } -} diff --git a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequestor.jvm.kt b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequestor.jvm.kt deleted file mode 100644 index 018e339e..00000000 --- a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequestor.jvm.kt +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -@file:JvmName("ApiRequestUtil") - -package love.forte.simbot.qguild.api - -import io.ktor.client.* -import io.ktor.http.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking -import kotlinx.serialization.StringFormat -import kotlinx.serialization.json.Json -import kotlinx.serialization.json.JsonBuilder -import love.forte.simbot.qguild.Api4J -import love.forte.simbot.qguild.QQGuild -import love.forte.simbot.utils.runInNoScopeBlocking -import java.util.function.Consumer - - -private val simbotInClasspath: Boolean by lazy { - runCatching { - val runnerClass = Class.forName("love.forte.simbot.utils.BlockingRunnerKt") - runnerClass.methods.any { it.name == "runInNoScopeBlocking" }.also { - if (it) { - apiRequestorLogger.debug("simbot-api is contains in current classpath. Blocking request will use simbot runInNoScopeBlocking.") - } else { - apiRequestorLogger.debug("simbot-api is contains in current classpath, but 'runInNoScopeBlocking' method not found. Blocking request will use runBlocking with Dispatchers.IO") - } - } - }.getOrElse { - if (it is ClassNotFoundException) { - apiRequestorLogger.debug("simbot-api is not contains in current classpath. Blocking request will use runBlocking with Dispatchers.IO") - } else { - apiRequestorLogger.debug("simbot-api classpath check failure: {}. Blocking request will use runBlocking with Dispatchers.IO", it.localizedMessage, it) - } - false - } -} - -/** - * [request] for Java - */ -@Api4J -@JvmOverloads -public fun doRequest( - api: QQGuildApi, - client: HttpClient, - server: String, - token: String, - decoder: StringFormat = QQGuildApi.DefaultJsonDecoder, -): R { - val url = when (server) { - QQGuild.URL_STRING -> QQGuild.URL - QQGuild.SANDBOX_URL_STRING -> QQGuild.SANDBOX_URL - else -> Url(server) - } - - return if (simbotInClasspath) { - runInNoScopeBlocking { - api.request(client, url, token, decoder) - } - } else { - runBlocking(Dispatchers.IO) { - api.request(client, url, token, decoder) - } - } -} - -/** - * 用于在Java中构建一个默认的 [HttpClient]。 - */ -@Api4J -public fun newHttpClient(): HttpClient = HttpClient() - -/** - * 用于在Java中构建一个 [Json]。 - */ -@Api4J -@JvmOverloads -public fun newJson( - build: Consumer = Consumer { - it.apply { - isLenient = true - ignoreUnknownKeys = true - } - }, -): Json = Json { - build.accept(this) -} diff --git a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt new file mode 100644 index 00000000..8c0099ca --- /dev/null +++ b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +@file:JvmName("ApiRequests") +@file:JvmMultifileClass + +package love.forte.simbot.qguild.api + +import io.ktor.client.* +import io.ktor.client.statement.* +import io.ktor.http.* +import kotlinx.serialization.json.Json +import kotlinx.serialization.json.JsonBuilder +import love.forte.simbot.qguild.QGApi4J +import love.forte.simbot.qguild.QQGuild +import love.forte.simbot.suspendrunner.runInNoScopeBlocking +import java.util.function.Consumer + + +/** + * 用于在Java中构建一个默认的 [HttpClient]。 + */ +@QGApi4J +public fun newHttpClient(): HttpClient = HttpClient() + +/** + * 用于在Java中构建一个 [Json]。 + */ +@QGApi4J +@JvmOverloads +public fun newJson( + build: Consumer = Consumer { + it.apply { + isLenient = true + ignoreUnknownKeys = true + } + }, +): Json = Json { + build.accept(this) +} + + +/** + * [QQGuildApi.request] for Java + */ +@QGApi4J +@JvmOverloads +public fun QQGuildApi.requestBlocking( + client: HttpClient, + token: String, + server: Url = QQGuild.URL, + decoder: Json = QQGuild.DefaultJson, +): HttpResponse = runInNoScopeBlocking { + request(client, token, server, decoder) +} diff --git a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.jvm.kt b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.jvm.kt index 99215b44..2130aa62 100644 --- a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.jvm.kt +++ b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.jvm.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,151 +17,7 @@ package love.forte.simbot.qguild.api -import io.ktor.client.* -import io.ktor.client.request.* import io.ktor.http.* -import kotlinx.coroutines.* -import kotlinx.coroutines.future.asCompletableFuture -import kotlinx.serialization.StringFormat -import kotlinx.serialization.json.Json -import love.forte.simbot.qguild.Api4J -import love.forte.simbot.qguild.ErrInfo -import love.forte.simbot.qguild.InternalApi -import java.util.concurrent.CompletableFuture - -/** - * 用于多平台实现的最小目标。 - * - * 提供兼容Java的blocking/async函数 - */ -@InternalApi -public actual abstract class PlatformQQGuildApi actual constructor() { - - /** - * 使用此api发起一次请求,并得到预期中的结果。 * - * @see ErrInfo - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误。 - */ - @JvmSynthetic - public actual abstract suspend fun doRequest( - client: HttpClient, - server: Url, - token: String, - decoder: StringFormat - ): R - - - /** - * 使用此api发起一次请求,并得到预期中的结果。 * - * @see ErrInfo - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误。 - */ - @JvmSynthetic - public actual abstract suspend fun doRequestRaw( - client: HttpClient, - server: Url, - token: String, - ): String - - - /** - * 使用此api发起一次请求,并得到预期中的结果。 - * - * 为Java服务的阻塞API,内部使用 [runBlocking] 阻塞并等待 [doRequest] 的结果。 - * 其中,阻塞代码块上下文的调度器为 [Dispatchers.IO]。 - * - * @see ErrInfo - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误。 - */ - @Api4J - @JvmOverloads - public fun doRequestBlocking( - client: HttpClient = QQGuildApi.DefaultHttpClient, - server: Url, - token: String, - decoder: StringFormat = Json, - ): R = runBlocking(Dispatchers.IO) { - doRequest(client, server, token, decoder) - } - - - /** - * 使用此api发起一次请求,并得到预期中的结果。 - * - * 为Java服务的异步API,内部使用 [CoroutineScope.async] 异步执行 [doRequest] 并返回 - * [CompletableFuture] 结果。 - * - * @see ErrInfo - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误。 - */ - @Api4J - @JvmOverloads - public fun doRequestAsync( - client: HttpClient = QQGuildApi.DefaultHttpClient, - server: Url, - token: String, - decoder: StringFormat = Json, - ): CompletableFuture { - return client.async(COROUTINE_NAME) { doRequest(client, server, token, decoder) }.asCompletableFuture() - } - - /** - * 使用此api发起一次请求,并得到预期中的结果。 - * - * 为Java服务的阻塞API,内部使用 [runBlocking] 阻塞并等待 [doRequest] 的结果。 - * 其中,阻塞代码块上下文的调度器为 [Dispatchers.IO]。 - * - * @see ErrInfo - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误。 - */ - @Api4J - @JvmOverloads - public fun doRequestRawBlocking( - client: HttpClient = QQGuildApi.DefaultHttpClient, - server: Url, - token: String, - ): String = runBlocking(Dispatchers.IO) { - doRequestRaw(client, server, token) - } - - - /** - * 使用此api发起一次请求,并得到预期中的结果。 - * - * 为Java服务的异步API,内部使用 [CoroutineScope.async] 异步执行 [doRequest] 并返回 - * [CompletableFuture] 结果。 - * - * @see ErrInfo - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误。 - */ - @Api4J - @JvmOverloads - public fun doRequestRawAsync( - client: HttpClient = QQGuildApi.DefaultHttpClient, - server: Url, - token: String, - ): CompletableFuture { - return client.async(COROUTINE_NAME) { doRequestRaw(client, server, token) }.asCompletableFuture() - } - - - public actual companion object { - private val COROUTINE_NAME = CoroutineName("Api-Async-Scope") - } -} - // 急切初始化 private val logNameProcessor: HttpMethod.() -> String = run { @@ -180,11 +36,11 @@ private val logNameProcessor: HttpMethod.() -> String = run { PUT, PATCH 紫色 DELETE 红色 */ - HttpMethod.Get -> "\u001B[32m GET\u001B[0m" - HttpMethod.Post -> "\u001B[34m POST\u001B[0m" - HttpMethod.Put -> "\u001B[35m PUT\u001B[0m" - HttpMethod.Patch -> "\u001B[35m PATCH\u001B[0m" - HttpMethod.Delete -> "\u001B[31mDELETE\u001B[0m" + HttpMethod.Get -> "\u001B[32m GET\u001B[0m" + HttpMethod.Post -> "\u001B[34m POST\u001B[0m" + HttpMethod.Put -> "\u001B[35m PUT\u001B[0m" + HttpMethod.Patch -> "\u001B[35m PATCH\u001B[0m" + HttpMethod.Delete -> "\u001B[31mDELETE\u001B[0m" else -> value } } diff --git a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.jvm.kt b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.jvm.kt index ffcae1cf..09a3914d 100644 --- a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.jvm.kt +++ b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.jvm.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -22,7 +22,7 @@ import io.ktor.http.* import io.ktor.util.cio.* import io.ktor.utils.io.nio.* import io.ktor.utils.io.streams.* -import love.forte.simbot.qguild.InternalApi +import love.forte.simbot.qguild.QGInternalApi import java.io.File import java.net.URI import java.net.URL @@ -95,7 +95,7 @@ public actual fun FormBuilder.resolveOther(fileImage: Any?) { * 与 [resolveOther] 中的支持类型相对应。 * */ -@InternalApi +@QGInternalApi public actual abstract class BaseMessageSendBodyBuilder actual constructor() { /* 追加额外的平台功能,但是不能有抽象方法 diff --git a/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.native.kt b/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.native.kt index 0d64e017..31b956e2 100644 --- a/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.native.kt +++ b/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.native.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,53 +17,7 @@ package love.forte.simbot.qguild.api -import io.ktor.client.* -import io.ktor.client.request.* import io.ktor.http.* -import kotlinx.serialization.StringFormat -import love.forte.simbot.qguild.ErrInfo -import love.forte.simbot.qguild.InternalApi - - -/** - * 用于多平台实现的最小目标。 - */ -@InternalApi -public actual abstract class PlatformQQGuildApi actual constructor() { - - /** - * 使用此api发起一次请求,并得到预期中的结果。如果返回了代表错误的响应值 - * - * @see love.forte.simbot.qguild.ErrInfo - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误。 - */ - public actual abstract suspend fun doRequest( - client: HttpClient, - server: Url, - token: String, - decoder: StringFormat - ): R - - - /** - * 使用此api发起一次请求,并得到响应结果的字符串。 - * - * @see ErrInfo - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws love.forte.simbot.qguild.QQGuildApiException 请求过程中出现了错误(http状态码 !in 200 .. 300) - */ - public actual abstract suspend fun doRequestRaw( - client: HttpClient, - server: Url, - token: String, - ): String - - public actual companion object -} - /** * 日志对齐 diff --git a/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.native.kt b/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.native.kt index c7e7f348..fc1d7b29 100644 --- a/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.native.kt +++ b/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.native.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -18,7 +18,7 @@ package love.forte.simbot.qguild.api.message import io.ktor.client.request.forms.* -import love.forte.simbot.qguild.InternalApi +import love.forte.simbot.qguild.QGInternalApi /** * native平台没有额外的类型支持 @@ -31,7 +31,7 @@ public actual fun FormBuilder.resolveOther(fileImage: Any?) { * 提供一些需要由不同平台额外实现的基类。 * 主要针对 `fileImage`。 */ -@InternalApi +@QGInternalApi public actual abstract class BaseMessageSendBodyBuilder actual constructor() { public actual open var fileImage: Any? = null protected set diff --git a/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/initCause.native.kt b/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/initCause.native.kt index 3a2301c6..4ad4c953 100644 --- a/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/initCause.native.kt +++ b/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/initCause.native.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -21,4 +21,4 @@ package love.forte.simbot.qguild * @suppress */ @PublishedApi -internal actual inline fun T.initCause0(cause: Throwable): T = this +internal actual inline fun T.initCause0(cause: Throwable): T = this.also { addSuppressed(cause) } diff --git a/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/time/TimeUnit.native.kt b/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/time/TimeUnit.native.kt deleted file mode 100644 index 6a8ff3bc..00000000 --- a/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/time/TimeUnit.native.kt +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.qguild.time - -private const val NANO_SCALE = 1L -private const val MICRO_SCALE = 1000L * NANO_SCALE -private const val MILLI_SCALE = 1000L * MICRO_SCALE -private const val SECOND_SCALE = 1000L * MILLI_SCALE -private const val MINUTE_SCALE = 60L * SECOND_SCALE -private const val HOUR_SCALE = 60L * MINUTE_SCALE -private const val DAY_SCALE = 24L * HOUR_SCALE - -/** - * 时间单位,用于时间转化。 - */ -public actual enum class TimeUnit(private val scale: Long) { - NANOSECONDS(NANO_SCALE), - MICROSECONDS(MICRO_SCALE), - MILLISECONDS(MILLI_SCALE), - SECONDS(SECOND_SCALE), - MINUTES(MINUTE_SCALE), - HOURS(HOUR_SCALE), - DAYS(DAY_SCALE); - - private val maxNanos: Long = Long.MAX_VALUE / scale - private val maxMicros: Long - private val maxMillis: Long - private val maxSecs: Long - private val microRatio: Long - private val milliRatio: Int - private val secRatio: Int - - init { - val ur: Long = - if (scale >= MICRO_SCALE) scale / MICRO_SCALE else MICRO_SCALE / scale - microRatio = ur - maxMicros = Long.MAX_VALUE / ur - val mr: Long = - if (scale >= MILLI_SCALE) scale / MILLI_SCALE else MILLI_SCALE / scale - milliRatio = mr.toInt() - maxMillis = Long.MAX_VALUE / mr - val sr: Long = - if (scale >= SECOND_SCALE) scale / SECOND_SCALE else SECOND_SCALE / scale - secRatio = sr.toInt() - maxSecs = Long.MAX_VALUE / sr - } - - /** - * 转为 Nanos - */ - public actual open fun toNanos(duration: Long): Long { - return when { - scale == NANO_SCALE -> duration - duration > maxNanos -> Long.MAX_VALUE - duration < -maxNanos -> Long.MIN_VALUE - else -> duration * scale - } - } - - /** - * 转为 Micros - */ - public actual open fun toMicros(duration: Long): Long { - return when { - scale <= MICRO_SCALE -> if (scale == MICRO_SCALE) { - duration - } else { - duration / microRatio - } - duration > maxMicros -> Long.MAX_VALUE - duration < -maxMicros -> Long.MIN_VALUE - else -> duration * microRatio - } - } - - /** - * 转为 Millis - */ - public actual open fun toMillis(duration: Long): Long { - return when { - scale <= MILLI_SCALE -> if (scale == MILLI_SCALE) { - duration - } else { - duration / milliRatio - } - duration > maxMillis -> Long.MAX_VALUE - duration < -maxMillis -> Long.MIN_VALUE - else -> duration * milliRatio - } - } - - /** - * 转为 Seconds - */ - public actual open fun toSeconds(duration: Long): Long { - return when { - scale <= SECOND_SCALE -> if (scale == SECOND_SCALE) { - duration - } else { - duration / secRatio - } - duration > maxSecs -> Long.MAX_VALUE - duration < -maxSecs -> Long.MIN_VALUE - else -> duration * secRatio - } - } - - /** - * 转为 Minutes - */ - public actual open fun toMinutes(duration: Long): Long { - return when { - scale <= SECOND_SCALE -> if (scale == SECOND_SCALE) { - duration - } else { - duration / secRatio - } - duration > maxSecs -> Long.MAX_VALUE - duration < -maxSecs -> Long.MIN_VALUE - else -> duration * secRatio - } - } - - /** - * 转为 Hours - */ - public actual open fun toHours(duration: Long): Long { - return cvt(duration, HOUR_SCALE, scale) - } - - /** - * 转为 Days - */ - public actual open fun toDays(duration: Long): Long { - return cvt(duration, DAY_SCALE, scale) - } - - -} - -/** - * General conversion utility. - * - * @param d duration - * @param dst result unit scale - * @param src source unit scale - */ -private fun cvt(d: Long, dst: Long, src: Long): Long { - var r: Long - return when { - src == dst -> d - src < dst -> d / (dst / src) - d > (Long.MAX_VALUE / (src / dst).also { r = it }) -> Long.MAX_VALUE - d < -Long.MAX_VALUE -> Long.MIN_VALUE - else -> d * r - } -} From d9a6b2613103e62740d18a0a2d530c4023cccd1e Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Sat, 13 Jan 2024 01:37:49 +0800 Subject: [PATCH 07/71] stdlib --- .github/workflows/publish-v4-snapshot.yml | 106 ++++++++++ gradle/libs.versions.toml | 1 + settings.gradle.kts | 2 +- .../build.gradle.kts | 1 - .../forte/simbot/qguild/api/ApiRequests.kt | 4 +- .../src/jvmMain/java/module-info.java | 38 ++++ .../simbot/qguild/api/ApiRequests.jvm.kt | 2 +- .../build.gradle.kts | 200 ++++-------------- .../kotlin/love/forte/simbot/qguild/Bot.kt | 106 +++------- .../forte/simbot/qguild/BotConfiguration.kt | 65 ++---- .../love/forte/simbot/qguild/BotFactory.kt | 65 ++++++ .../love/forte/simbot/qguild/BotProcessor.kt | 4 +- .../{BotRequestor.kt => BotRequests.kt} | 61 ++++-- .../qguild/ConfigurableBotConfiguration.kt | 37 ++-- ...SuspendAnnotation.kt => EventProcessor.kt} | 31 ++- .../forte/simbot/qguild/TransAnnotations.kt | 108 ---------- .../love/forte/simbot/qguild/exceptions.kt} | 18 +- .../forte/simbot/qguild/internal/BotImpl.kt | 151 +++++++------ .../forte/simbot/qguild/internal/BotStates.kt | 133 +++++------- .../qguild/internal/SimpleConcurrentQueue.kt | 42 ---- .../forte/simbot/qguild/internal/WeakRef.kt | 35 --- .../forte/simbot/qguild/BotRequests.js.kt | 55 +++++ .../forte/simbot/qguild/PlatformBaseBotJs.kt | 110 ---------- .../internal/SimpleConcurrentQueueJs.kt | 37 ---- .../forte/simbot/qguild/internal/WeakRefJs.kt | 43 ---- .../src/jvmMain/java/module-info.java | 14 ++ .../forte/simbot/qguild/BotRequestorJvm.kt | 75 ------- .../forte/simbot/qguild/BotRequests.jvm.kt | 96 +++++++++ .../forte/simbot/qguild/EventProcessor.jvm.kt | 81 +++++++ .../forte/simbot/qguild/PlatformBaseBotJvm.kt | 113 ---------- .../internal/SimpleConcurrentQueueJvm.kt | 34 --- .../simbot/qguild/internal/WeakRefJvm.kt | 28 --- .../simbot/qguild/PlatformBaseBotNative.kt | 49 ----- .../internal/SimpleConcurrentQueueNative.kt | 63 ------ .../simbot/qguild/internal/WeakRefNative.kt | 29 --- 35 files changed, 782 insertions(+), 1255 deletions(-) create mode 100644 .github/workflows/publish-v4-snapshot.yml create mode 100644 simbot-component-qq-guild-api/src/jvmMain/java/module-info.java create mode 100644 simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotFactory.kt rename simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/{BotRequestor.kt => BotRequests.kt} (54%) rename simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/{SuspendAnnotation.kt => EventProcessor.kt} (50%) delete mode 100644 simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/TransAnnotations.kt rename simbot-component-qq-guild-stdlib/src/{jsMain/kotlin/love/forte/simbot/qguild/BotRequestorJs.kt => commonMain/kotlin/love/forte/simbot/qguild/exceptions.kt} (64%) delete mode 100644 simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/SimpleConcurrentQueue.kt delete mode 100644 simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/WeakRef.kt create mode 100644 simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/BotRequests.js.kt delete mode 100644 simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/PlatformBaseBotJs.kt delete mode 100644 simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/internal/SimpleConcurrentQueueJs.kt delete mode 100644 simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/internal/WeakRefJs.kt create mode 100644 simbot-component-qq-guild-stdlib/src/jvmMain/java/module-info.java delete mode 100644 simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/BotRequestorJvm.kt create mode 100644 simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/BotRequests.jvm.kt create mode 100644 simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/EventProcessor.jvm.kt delete mode 100644 simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/PlatformBaseBotJvm.kt delete mode 100644 simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/internal/SimpleConcurrentQueueJvm.kt delete mode 100644 simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/internal/WeakRefJvm.kt delete mode 100644 simbot-component-qq-guild-stdlib/src/nativeMain/kotlin/love/forte/simbot/qguild/PlatformBaseBotNative.kt delete mode 100644 simbot-component-qq-guild-stdlib/src/nativeMain/kotlin/love/forte/simbot/qguild/internal/SimpleConcurrentQueueNative.kt delete mode 100644 simbot-component-qq-guild-stdlib/src/nativeMain/kotlin/love/forte/simbot/qguild/internal/WeakRefNative.kt diff --git a/.github/workflows/publish-v4-snapshot.yml b/.github/workflows/publish-v4-snapshot.yml new file mode 100644 index 00000000..7c03c73e --- /dev/null +++ b/.github/workflows/publish-v4-snapshot.yml @@ -0,0 +1,106 @@ +name: Publish V4 Snapshot +on: + push: + branches: + - v4-dev/ver/** + - v4-dev/main + + paths: + - '**src/main/kotlin/**.kt' + - '**src/main/java/**.java' + - 'buildSrc/**' + - '**gradle.kts' + - 'gradle.properties' + + # 手动触发工作流 + workflow_dispatch: + +env: + IS_CI: true + GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} + GPG_SECRET_KEY: ${{ secrets.GPG_SECRET_KEY }} + GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }} + OSSRH_USER: ${{ secrets.SONATYPE_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SIMBOT_IS_SNAPSHOT: true + SIMBOT_SNAPSHOT_ONLY: true + GRADLE_OPTS: "-Dfile.encoding=UTF-8" + + +jobs: + publish-snapshot: + name: Publish snapshot + strategy: + matrix: + os: [ macos-latest, windows-latest, ubuntu-latest ] + runs-on: ${{ matrix.os }} + steps: + # 检出仓库代码 + - name: Check out repo + uses: actions/checkout@v3 + + # setup Java + - name: Setup java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: 21 + + # setup Gradle + - name: Gradle test and publish snapshot + uses: gradle/gradle-build-action@v2 + with: + gradle-version: 8.5 + arguments: | + test + publishToSonatype + closeAndReleaseStagingRepository + --info + --warning-mode all + -Porg.gradle.jvmargs="-Xmx8g -Xms2g -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8" + -Porg.gradle.daemon=false + + deploy-doc: + name: Deploy-doc + runs-on: ubuntu-latest + needs: publish-snapshot + steps: + # 检出仓库代码 + - name: Check out repo + uses: actions/checkout@v3 + with: + persist-credentials: false + fetch-depth: 0 + # setup Java + - name: Setup java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: 21 + + # setup Gradle + - name: Gradle generate documentation + uses: gradle/gradle-build-action@v2 + with: + gradle-version: 8.5 + arguments: | + -Porg.gradle.jvmargs="-Xmx4g -Xms4g -XX:MaxMetaspaceSize=2g -Dfile.encoding=UTF-8" + -Porg.gradle.daemon=false + -DisSnapshot=false + --info + --warning-mode all + -x test + --build-cache + dokkaHtmlMultiModule + + - name: Push to doc repository + uses: peaceiris/actions-gh-pages@v3 + with: + personal_token: ${{ secrets.PUSH_TOKEN }} + external_repository: simple-robot-library/simbot3-api-docs + publish_branch: kdoc-deploy/snapshots/component-qq-guild-v4 + publish_dir: ./build/dokka/html + # deploy to sub dir + destination_dir: snapshots/components/qq-guild-v4 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index dc4a8e77..031330e1 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -19,6 +19,7 @@ simbot-common-atomic = { group = "love.forte.simbot.common", name = "simbot-comm simbot-common-core = { group = "love.forte.simbot.common", name = "simbot-common-core", version.ref = "simbot" } simbot-common-suspend = { group = "love.forte.simbot.common", name = "simbot-common-suspend-runner", version.ref = "simbot" } simbot-common-annotations = { group = "love.forte.simbot.common", name = "simbot-common-annotations", version.ref = "simbot" } +simbot-common-loop = { group = "love.forte.simbot.common", name = "simbot-common-stage-loop", version.ref = "simbot" } # jetbrains-annotation jetbrains-annotations = "org.jetbrains:annotations:24.0.1" diff --git a/settings.gradle.kts b/settings.gradle.kts index b780eef3..f3254cdb 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -20,7 +20,7 @@ rootProject.name = "qq-guild" //include(":builder-generator") include(":simbot-component-qq-guild-api") -//include(":simbot-component-qq-guild-stdlib") +include(":simbot-component-qq-guild-stdlib") //include(":simbot-component-qq-guild-core-common") //include(":simbot-component-qq-guild-core") //include(":simbot-component-qq-guild-benchmark") diff --git a/simbot-component-qq-guild-api/build.gradle.kts b/simbot-component-qq-guild-api/build.gradle.kts index a2454f39..0cd44063 100644 --- a/simbot-component-qq-guild-api/build.gradle.kts +++ b/simbot-component-qq-guild-api/build.gradle.kts @@ -61,7 +61,6 @@ kotlin { api(libs.ktor.client.core) api(libs.ktor.client.contentNegotiation) - api(libs.ktor.serialization.kotlinxJson) api(libs.kotlinx.serialization.json) } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt index 2265318b..96c7675c 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt @@ -57,8 +57,7 @@ import kotlin.jvm.JvmSynthetic public suspend fun QQGuildApi.request( client: HttpClient, token: String, - server: Url? = null, - json: Json = QQGuild.DefaultJson + server: Url? = null ): HttpResponse { val api = this @@ -98,6 +97,7 @@ public suspend fun QQGuildApi.request( setBody(body) } else { try { + val json = QQGuild.DefaultJson val ser = guessSerializer(body, json.serializersModule) val bodyJson = json.encodeToString(ser, body) setBody(bodyJson) diff --git a/simbot-component-qq-guild-api/src/jvmMain/java/module-info.java b/simbot-component-qq-guild-api/src/jvmMain/java/module-info.java new file mode 100644 index 00000000..e51323f1 --- /dev/null +++ b/simbot-component-qq-guild-api/src/jvmMain/java/module-info.java @@ -0,0 +1,38 @@ +module simbot.component.qqguild.api { + requires kotlin.stdlib; + requires transitive kotlinx.coroutines.core; + requires transitive kotlinx.serialization.core; + requires transitive kotlinx.serialization.json; + // simbot + requires static simbot.common.annotations; + requires transitive simbot.common.apidefinition; + requires transitive simbot.common.suspendrunner; + requires transitive simbot.common.core; + // ktor + requires io.ktor.client.core; + requires io.ktor.client.content.negotiation; + + + exports love.forte.simbot.qguild; + exports love.forte.simbot.qguild.event; + exports love.forte.simbot.qguild.message; + exports love.forte.simbot.qguild.model; + exports love.forte.simbot.qguild.model.forum; + exports love.forte.simbot.qguild.time; + exports love.forte.simbot.qguild.api; + exports love.forte.simbot.qguild.api.announces; + exports love.forte.simbot.qguild.api.apipermission; + exports love.forte.simbot.qguild.api.channel; + exports love.forte.simbot.qguild.api.channel.permissions; + exports love.forte.simbot.qguild.api.channel.pins; + exports love.forte.simbot.qguild.api.channel.schedules; + exports love.forte.simbot.qguild.api.forum; + exports love.forte.simbot.qguild.api.guild; + exports love.forte.simbot.qguild.api.guild.mute; + exports love.forte.simbot.qguild.api.member; + exports love.forte.simbot.qguild.api.message; + exports love.forte.simbot.qguild.api.message.direct; + exports love.forte.simbot.qguild.api.message.setting; + exports love.forte.simbot.qguild.api.role; + exports love.forte.simbot.qguild.api.user; +} diff --git a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt index 8c0099ca..f06532f0 100644 --- a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt +++ b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt @@ -65,5 +65,5 @@ public fun QQGuildApi.requestBlocking( server: Url = QQGuild.URL, decoder: Json = QQGuild.DefaultJson, ): HttpResponse = runInNoScopeBlocking { - request(client, token, server, decoder) + request(client, token, server) } diff --git a/simbot-component-qq-guild-stdlib/build.gradle.kts b/simbot-component-qq-guild-stdlib/build.gradle.kts index e91a576e..e26e3d88 100644 --- a/simbot-component-qq-guild-stdlib/build.gradle.kts +++ b/simbot-component-qq-guild-stdlib/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,43 +15,23 @@ * If not, see . */ -import love.forte.gradle.common.kotlin.multiplatform.NativeTargets - -/* - * Copyright (c) 2022-2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - - +import love.forte.gradle.common.kotlin.multiplatform.applyTier1 +import love.forte.gradle.common.kotlin.multiplatform.applyTier2 +import love.forte.gradle.common.kotlin.multiplatform.applyTier3 plugins { kotlin("multiplatform") - `qq-guild-multiplatform-maven-publish` kotlin("plugin.serialization") `qq-guild-dokka-partial-configure` `simbot-tcg-suspend-transform-configure` - id("kotlinx-atomicfu") -} - -repositories { - mavenCentral() } +configJavaCompileWithModule("simbot.component.qqguild.stdlib") +apply(plugin = "qq-guild-multiplatform-maven-publish") kotlin { explicitApi() + applyDefaultHierarchyTemplate() sourceSets.configureEach { languageSettings { @@ -59,160 +39,52 @@ kotlin { } } - jvm { - withJava() - compilations.all { - kotlinOptions { - jvmTarget = "1.8" - javaParameters = true - freeCompilerArgs = freeCompilerArgs + listOf("-Xjvm-default=all") - } - } - testRuns["test"].executionTask.configure { - useJUnitPlatform() - } - } + configKotlinJvm() js(IR) { - nodejs { - testTask { - useMocha { - timeout = "30m" - } - } - } + configJs() } - - val mainPresets = mutableSetOf() - val testPresets = mutableSetOf() - - // https://kotlinlang.org/docs/native-target-support.html - // 针对 kotlin target support 中的列表结合 ktor-client 平台的支持提供的平台能力。 - // 注释掉的内容是Ktor尚不支持的平台 -// val supportTargets = setOf( -// // Tier 1 -// "linuxX64", -// "macosX64", -// "macosArm64", -// "iosSimulatorArm64", -// "iosX64", -// // Tier 2 -//// "linuxArm64", -// "watchosSimulatorArm64", -// "watchosX64", -// "watchosArm32", -// "watchosArm64", -// "tvosSimulatorArm64", -// "tvosX64", -// "tvosArm64", -// "iosArm64", -// // Tier 3 -//// "androidNativeArm32", -//// "androidNativeArm64", -//// "androidNativeX86", -//// "androidNativeX64", -// "mingwX64", -//// "watchosDeviceArm64", -// ) - - val targets = NativeTargets.Official.all.intersect(NativeTargets.KtorClient.all) - - - targets { - presets.filterIsInstance>() - .filter { it.name in targets } - .forEach { presets -> - val target = fromPreset(presets, presets.name) - val mainSourceSet = target.compilations["main"].kotlinSourceSets.first() - val testSourceSet = target.compilations["test"].kotlinSourceSets.first() - - val tn = target.name - when { - // win - tn.startsWith("mingw") -> { - testSourceSet.dependencies { - // TODO ws connect timeout? - implementation(libs.ktor.client.winhttp) - } - } - // linux: nothing - - // darwin based - tn.startsWith("macos") - || tn.startsWith("ios") - || tn.startsWith("watchos") - || tn.startsWith("tvos") -> { - testSourceSet.dependencies { - implementation(libs.ktor.client.darwin) - } - } - } - - mainPresets.add(mainSourceSet) - testPresets.add(testSourceSet) - } - } + applyTier1() + applyTier2() + applyTier3(supportKtorClient = true) sourceSets { - val commonMain by getting { - dependencies { - api(project(":simbot-component-qq-guild-api")) - api(simbotLogger) - api(simbotUtilLoop) - api(simbotUtilSuspendTransformer) - compileOnly(simbotUtilAnnotations) - api(libs.ktor.client.ws) - api("org.jetbrains.kotlinx:atomicfu:${libs.versions.atomicfu.get()}") - } - } - - val commonTest by getting { - dependencies { - implementation(kotlin("test")) - implementation(libs.kotlinx.coroutines.test) - } + commonMain.dependencies { + api(project(":simbot-component-qq-guild-api")) + api(libs.simbot.common.loop) + api(libs.simbot.common.atomic) + compileOnly(libs.simbot.common.annotations) + // ktor + api(libs.ktor.client.contentNegotiation) + api(libs.ktor.serialization.kotlinxJson) + api(libs.ktor.client.ws) } - getByName("jvmMain") { - dependencies { - compileOnly(simbotUtilAnnotations) // use @Api4J annotation - } + commonTest.dependencies { + implementation(kotlin("test")) + implementation(libs.kotlinx.coroutines.test) } - getByName("jvmTest") { - dependencies { - runtimeOnly(libs.ktor.client.cio) - implementation(simbotApi) // use @Api4J annotation - implementation(libs.log4j.api) - implementation(libs.log4j.core) - implementation(libs.log4j.slf4j2) - } + jvmTest.dependencies { + runtimeOnly(libs.ktor.client.cio) + implementation(libs.log4j.api) + implementation(libs.log4j.core) + implementation(libs.log4j.slf4j2) } - getByName("jsMain") { - dependencies { - implementation(libs.ktor.client.js) - } + jsMain.dependencies { + api(libs.simbot.common.annotations) + api(libs.ktor.client.js) } - val nativeMain by creating { - dependsOn(commonMain) + nativeMain.dependencies { + api(libs.simbot.common.annotations) } - val nativeTest by creating { - dependsOn(commonTest) + mingwTest.dependencies { + implementation(libs.ktor.client.winhttp) } - - configure(mainPresets) { dependsOn(nativeMain) } - configure(testPresets) { dependsOn(nativeTest) } - } - } -atomicfu { - transformJvm = true - transformJs = true - jvmVariant = "FU" -} diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/Bot.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/Bot.kt index 9bd6a003..e566f322 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/Bot.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/Bot.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,49 +19,17 @@ package love.forte.simbot.qguild import io.ktor.client.* import io.ktor.http.* +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import kotlinx.serialization.json.Json -import love.forte.plugin.suspendtrans.annotation.JvmAsync -import love.forte.plugin.suspendtrans.annotation.JvmBlocking import love.forte.simbot.qguild.api.GatewayInfo import love.forte.simbot.qguild.api.user.GetBotInfoApi import love.forte.simbot.qguild.event.Shard -import love.forte.simbot.qguild.event.Signal import love.forte.simbot.qguild.model.User +import love.forte.simbot.suspendrunner.ST +import love.forte.simbot.suspendrunner.STP import kotlin.coroutines.CoroutineContext -import kotlin.jvm.JvmSynthetic - -/** - * 由平台为 [Bot] 实现并提供更多额外能力的接口类型。 - * - * _仅由内部使用_ - * - */ -@InternalApi -public expect interface BasePlatformBot { - /** - * 添加一个事件**预处理器**。 - * - * [registerPreProcessor] 与 [processor] 不同的是, - * [registerPreProcessor] 所注册的所有事件处理器会在接收到事件的时候以**同步顺序**的方式依次执行, - * 而后再交由下游的 [processor] 处理。 - * - * 也同样因此,尽可能避免在 [registerPreProcessor] 中执行任何耗时逻辑,这可能会对事件处理流程造成严重阻塞。 - * - * @param processor 用于处理事件的函数类型 - */ - @JvmSynthetic - public fun registerPreProcessor(processor: EventProcessor): DisposableHandle - - /** - * 添加一个事件处理器。 - * - */ - @JvmSynthetic - public fun registerProcessor(processor: EventProcessor): DisposableHandle -} - /** * 一个 QQ频道Bot。 @@ -70,7 +38,7 @@ public expect interface BasePlatformBot { * * @author ForteScarlet */ -public interface Bot : BasePlatformBot, CoroutineScope { +public interface Bot : CoroutineScope { override val coroutineContext: CoroutineContext /** @@ -107,18 +75,18 @@ public interface Bot : BasePlatformBot, CoroutineScope { * * 也同样因此,尽可能避免在 [registerPreProcessor] 中执行任何耗时逻辑,这可能会对事件处理流程造成严重阻塞。 * + * @see EventProcessor + * * @param processor 用于处理事件的函数类型 */ - @JvmSynthetic - public override fun registerPreProcessor(processor: EventProcessor): DisposableHandle + public fun registerPreProcessor(processor: EventProcessor): DisposableHandle /** * 添加一个事件处理器。 * + * @see EventProcessor */ - @JvmSynthetic - public override fun registerProcessor(processor: EventProcessor): DisposableHandle - + public fun registerProcessor(processor: EventProcessor): DisposableHandle /** * [票据](https://bot.q.qq.com/wiki/develop/api/#%E7%A5%A8%E6%8D%AE) @@ -145,65 +113,60 @@ public interface Bot : BasePlatformBot, CoroutineScope { /** * 启动当前BOT。如果已经存在 [client], 则会关闭已存连接并重新连接。 + * + * @throws CancellationException 如果当前 bot 已经被关闭 */ - @JvmBlocking - @JvmAsync - public suspend fun start(): Boolean - + @ST + public suspend fun start() /** * 启动当前BOT。如果已经存在 [client], 则会关闭已存连接并重新连接。 * + * @throws CancellationException 如果当前 bot 已经被关闭 + * * @param gateway 提供准备好的路由信息。 */ - @JvmBlocking - @JvmAsync - public suspend fun start(gateway: GatewayInfo): Boolean + @ST + public suspend fun start(gateway: GatewayInfo) /** * 终止当前BOT。 - * - * @return 当且仅当此BOT未关闭且关闭成功才会得到true。 */ - @JvmBlocking - @JvmAsync - public suspend fun cancel(reason: Throwable? = null): Boolean + public fun cancel(reason: Throwable?) + /** + * 终止当前BOT。 + */ + public fun cancel() { + cancel(null) + } /** - * 挂起直到此bot被 [cancel]. 如果已经 [cancel], 则不会挂起。 + * 挂起直到此 bot 被 [cancel] 或因其他原因终止(例如收到了昭示着无法重连的事件) */ - @JvmBlocking - @JvmAsync + @ST(asyncBaseName = "asFuture", asyncSuffix = "") public suspend fun join() - /** * 此bot使用的 [shard] 信息。 * */ public val shard: Shard - /** * 此Bot中的连接信息。 * - * 如果当前处于重连、重启的状态,得到的 [client] 中 [client.isActive][Client.isActive] 可能为 `false`,也可能不存在。 + * 如果当前处于重连、重启的状态,得到的 [client] 中 [client.isActive][Client.isActive] 可能为 `false`, + * [client] 本身也可能不存在。 * * @return 当前bot持有的连接。如果当前正处于连接中、重连中或尚未启动,则可能得到null */ public val client: Client? - /** * Bot的连接信息。一般来讲代表一个ws连接。 */ public interface Client { - /** - * 此连接所属的 [Bot] - */ - public val bot: Bot - /** * 事件推送所携带的 `s`. */ @@ -215,21 +178,12 @@ public interface Bot : BasePlatformBot, CoroutineScope { public val isActive: Boolean } - //// some self api /** * 通过 api [GetBotInfoApi] 查询bot自身信息。 */ - @JvmBlocking(asProperty = true) - @JvmAsync(asProperty = true, suffix = "") + @STP public suspend fun me(): User } -/** - * 用于处理事件的函数。 - */ -public typealias EventProcessor = suspend Signal.Dispatch.(raw: String) -> Unit - -// fun interface got: 'suspend' modifier is not allowed on a single abstract member - diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotConfiguration.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotConfiguration.kt index d434e530..56e206f0 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotConfiguration.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -26,28 +26,9 @@ import love.forte.simbot.qguild.event.EventIntents import love.forte.simbot.qguild.event.Intents import love.forte.simbot.qguild.event.Shard import love.forte.simbot.qguild.event.Signal -import love.forte.simbot.qguild.internal.BotImpl import kotlin.coroutines.CoroutineContext -import kotlin.jvm.JvmStatic import kotlin.jvm.JvmSynthetic -/** - * 用于构建 [Bot] 的工厂类型,提供一些工厂函数。 - */ -public object BotFactory { - - /** - * 构造一个 [Bot]. - */ - @JvmStatic - public fun create(appId: String, secret: String, token: String, config: ConfigurableBotConfiguration.() -> Unit = {}): Bot { - val ticket = Bot.Ticket(appId, secret, token) - val configuration = ConfigurableBotConfiguration().also(config) - return BotImpl(ticket, configuration.release()) - } - -} - /** * [Bot] 所需的配置信息。 */ @@ -172,28 +153,28 @@ public interface BotConfiguration { * */ public val apiDecoder: Json - - /** - * BOT内事件冲区的容量。 - * - * 缓冲区中堆积的事件如果已满则后续推送的事件会挂起等待缓冲区内元素的消费。 - * - * @see DEFAULT_EVENT_BUFFER_CAPACITY - * - */ - public val eventBufferCapacity: Int - - - public companion object { - - /** - * 事件缓冲区的默认容量: `64` - * - * _此默认值没什么特殊含义,一拍脑袋想的。_ - * - */ - public const val DEFAULT_EVENT_BUFFER_CAPACITY: Int = 64 - } +// +// /** +// * BOT内事件冲区的容量。 +// * +// * 缓冲区中堆积的事件如果已满则后续推送的事件会挂起等待缓冲区内元素的消费。 +// * +// * @see DEFAULT_EVENT_BUFFER_CAPACITY +// * +// */ +// public val eventBufferCapacity: Int +// +// +// public companion object { +// +// /** +// * 事件缓冲区的默认容量: `64` +// * +// * _此默认值没什么特殊含义,一拍脑袋想的。_ +// * +// */ +// public const val DEFAULT_EVENT_BUFFER_CAPACITY: Int = 64 +// } } diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotFactory.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotFactory.kt new file mode 100644 index 00000000..9f81bc21 --- /dev/null +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotFactory.kt @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.qguild + +import love.forte.simbot.common.function.ConfigurerFunction +import love.forte.simbot.common.function.invokeBy +import love.forte.simbot.qguild.internal.BotImpl +import kotlin.jvm.JvmOverloads +import kotlin.jvm.JvmStatic + +/** + * 用于构建 [Bot] 的工厂类型,提供一些工厂函数。 + */ +public object BotFactory { + /** + * 构造一个 [Bot]. + */ + @JvmStatic + @JvmOverloads + public fun create(appId: String, secret: String, token: String, config: ConfigurableBotConfiguration? = null): Bot = + create(Bot.Ticket(appId, secret, token), config) + + /** + * 构造一个 [Bot]. + */ + @JvmStatic + @JvmOverloads + public fun create(ticket: Bot.Ticket, config: ConfigurableBotConfiguration? = null): Bot { + return BotImpl(ticket, (config ?: ConfigurableBotConfiguration()).release()) + } + + /** + * 构造一个 [Bot]. + */ + @JvmStatic + public fun create( + appId: String, + secret: String, + token: String, + config: ConfigurerFunction + ): Bot = + create(Bot.Ticket(appId, secret, token), ConfigurableBotConfiguration().invokeBy(config)) + + /** + * 构造一个 [Bot]. + */ + @JvmStatic + public fun create(ticket: Bot.Ticket, config: ConfigurerFunction): Bot = + create(ticket, ConfigurableBotConfiguration().invokeBy(config)) +} diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotProcessor.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotProcessor.kt index 09aed408..e18ef7b7 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotProcessor.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotProcessor.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -29,7 +29,7 @@ import love.forte.simbot.qguild.event.Signal * ``` * */ -public inline fun Bot.registerProcessor( +public inline fun Bot.process( crossinline block: suspend E.(raw: String) -> Unit ): DisposableHandle { return registerProcessor { raw -> diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotRequestor.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotRequests.kt similarity index 54% rename from simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotRequestor.kt rename to simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotRequests.kt index 12a48d85..e0f3fd5c 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotRequestor.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotRequests.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,44 +15,50 @@ * If not, see . */ +@file:JvmName("BotRequests") +@file:JvmMultifileClass + package love.forte.simbot.qguild +import io.ktor.client.statement.* import love.forte.simbot.qguild.api.QQGuildApi import love.forte.simbot.qguild.api.request -import love.forte.simbot.qguild.api.requestRaw +import love.forte.simbot.qguild.api.requestData +import love.forte.simbot.qguild.api.requestText import love.forte.simbot.qguild.internal.BotImpl +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName import kotlin.jvm.JvmSynthetic /** * 直接通过bot进行请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 */ @JvmSynthetic -public suspend fun QQGuildApi.requestBy(bot: Bot): R { - val botToken = if (bot is BotImpl) bot.botToken else "Bot ${bot.ticket.appId}.${bot.ticket.token}" +public suspend fun QQGuildApi<*>.requestBy(bot: Bot): HttpResponse { + val botToken = bot.botToken() return request( client = bot.apiClient, - server = bot.apiServer, token = botToken, - decoder = bot.apiDecoder, + server = bot.apiServer, ) } /** * 直接通过bot进行请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 */ @JvmSynthetic -public suspend fun QQGuildApi.requestRawBy(bot: Bot): String { - val botToken = if (bot is BotImpl) bot.botToken else "Bot ${bot.ticket.appId}.${bot.ticket.token}" - return requestRaw( +public suspend inline fun QQGuildApi<*>.requestTextBy( + bot: Bot, + useResp: (HttpResponse) -> Unit = {} +): String { + val botToken = bot.botToken() + return requestText( client = bot.apiClient, - server = bot.apiServer, token = botToken, + server = bot.apiServer, + useResp = useResp ) } @@ -62,8 +68,27 @@ public suspend fun QQGuildApi.requestRawBy(bot: Bot): String { * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 */ @JvmSynthetic -public suspend fun Bot.request(api: QQGuildApi): R = api.requestBy(this) +public suspend inline fun QQGuildApi.requestDataBy(bot: Bot): R { + val botToken = bot.botToken() + return requestData( + client = bot.apiClient, + token = botToken, + server = bot.apiServer, + decoder = bot.apiDecoder + ) +} + +/** + * 直接通过bot进行请求。 + */ +@JvmSynthetic +public suspend fun Bot.request(api: QQGuildApi<*>): HttpResponse = api.requestBy(this) +/** + * 直接通过bot进行请求。 + */ +@JvmSynthetic +public suspend fun Bot.requestText(api: QQGuildApi<*>): String = api.requestTextBy(this) /** * 直接通过bot进行请求。 @@ -71,7 +96,9 @@ public suspend fun Bot.request(api: QQGuildApi): R = api.requestBy(this) * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 */ @JvmSynthetic -public suspend fun Bot.requestRaw(api: QQGuildApi): String = api.requestRawBy(this) - +public suspend fun Bot.requestData(api: QQGuildApi): R = api.requestDataBy(this) +@PublishedApi +internal fun Bot.botToken(): String = + if (this is BotImpl) botToken else "Bot ${ticket.appId}.${ticket.token}" diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/ConfigurableBotConfiguration.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/ConfigurableBotConfiguration.kt index 04c53a91..3f480f6e 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/ConfigurableBotConfiguration.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/ConfigurableBotConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -22,7 +22,6 @@ import io.ktor.client.engine.* import io.ktor.client.plugins.* import io.ktor.http.* import kotlinx.serialization.json.Json -import love.forte.simbot.qguild.BotConfiguration.Companion.DEFAULT_EVENT_BUFFER_CAPACITY import love.forte.simbot.qguild.event.EventIntents import love.forte.simbot.qguild.event.Intents import love.forte.simbot.qguild.event.Shard @@ -165,28 +164,22 @@ public class ConfigurableBotConfiguration : BotConfiguration { /** * 用于API请求结果反序列化的 [Json]. * - * 如果为null则会使用一个默认的 Json: - * ```kotlin - * Json { - * isLenient = true - * ignoreUnknownKeys = true - * } - * ``` + * 如果为null则会使用默认 Json [QQGuild.DefaultJson] * */ - override var apiDecoder: Json = defaultJson + override var apiDecoder: Json = QQGuild.DefaultJson - /** - * BOT内接收到事件后推送到的缓冲区的容量。 - * - * 缓冲区中堆积的事件如果已满则后续推送的事件会挂起等待缓冲区内元素的消费。 - * - * 默认为 [`64`][DEFAULT_EVENT_BUFFER_CAPACITY]。 - * - * @see DEFAULT_EVENT_BUFFER_CAPACITY - * - */ - override var eventBufferCapacity: Int = DEFAULT_EVENT_BUFFER_CAPACITY +// /** +// * BOT内接收到事件后推送到的缓冲区的容量。 +// * +// * 缓冲区中堆积的事件如果已满则后续推送的事件会挂起等待缓冲区内元素的消费。 +// * +// * 默认为 [`64`][DEFAULT_EVENT_BUFFER_CAPACITY]。 +// * +// * @see DEFAULT_EVENT_BUFFER_CAPACITY +// * +// */ +// override var eventBufferCapacity: Int = DEFAULT_EVENT_BUFFER_CAPACITY public companion object { private val defaultJson = Json { @@ -211,7 +204,6 @@ public class ConfigurableBotConfiguration : BotConfiguration { wsClientEngine = wsClientEngine, wsClientEngineFactory = wsClientEngineFactory, apiDecoder = apiDecoder, - eventBufferCapacity = eventBufferCapacity, ) } @@ -230,5 +222,4 @@ private class BotConfigurationImpl( override val wsClientEngine: HttpClientEngine?, override val wsClientEngineFactory: HttpClientEngineFactory<*>?, override val apiDecoder: Json, - override val eventBufferCapacity: Int, ) : BotConfiguration diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/SuspendAnnotation.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/EventProcessor.kt similarity index 50% rename from simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/SuspendAnnotation.kt rename to simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/EventProcessor.kt index deb4de03..312c05d5 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/SuspendAnnotation.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/EventProcessor.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,7 +15,36 @@ * If not, see . */ +@file:JvmName("EventProcessors") +@file:JvmMultifileClass + package love.forte.simbot.qguild +import love.forte.simbot.qguild.event.Signal +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic + +/** + * 用于处理事件的函数接口。 + * + * 在Java中可以使用 `JBlockEventProcessor`、`JAsyncEventProcessor` + * 或者 `EventProcessors` 中提供的静态工厂函数, + * 例如 + * ```java + * bot.registerProcessor(EventProcessors.block((event, raw) -> { ... })) + * ``` + * + */ +public fun interface EventProcessor { + /** + * 事件处理函数 + */ + @JvmSynthetic + public suspend operator fun Signal.Dispatch.invoke(raw: String) +} +internal suspend fun EventProcessor.doInvoke(d: Signal.Dispatch, r: String) { + d.apply { invoke(r) } +} diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/TransAnnotations.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/TransAnnotations.kt deleted file mode 100644 index 072edd48..00000000 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/TransAnnotations.kt +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.qguild -// -// -///** -// * -// * ```kotlin -// * @JvmBlocking -// * suspend fun foo(): T = ... -// * ``` -// * transform to: -// * -// * ```kotlin -// * @JvmSynthetic -// * suspend fun foo(): T = ... -// * -// * @Api4J -// * fun fooBlocking(): T = runInBlocking { foo() } -// * ``` -// * -// */ -//@OptIn(ExperimentalMultiplatform::class) -//@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) -//@Retention(AnnotationRetention.BINARY) -//@OptionalExpectation -//public expect annotation class JvmBlocking( -// /** -// * 生成函数的基础名称,如果为空则为当前函数名。 -// * 最终生成的函数名为 [baseName] + [suffix]。 -// */ -// val baseName: String = "", -// -// /** -// * [baseName] 名称基础上追加的名称后缀。 -// */ -// val suffix: String = "Blocking", -// -// /** -// * 是否转化为 property 的形式: -// * -// * ```kotlin -// * suspend fun foo(): T = ... -// * -// * // Generated -// * val fooBlocking: T get() = runInBlocking { foo() } -// * ``` -// * -// * 只有函数没有参数时有效。 -// * -// */ -// val asProperty: Boolean = false -//) -// -///** -// * ```kotlin -// * suspend fun run(): Int -// * ``` -// * -// * to -// * -// * ```kotlin -// * @JvmSynthetic -// * suspend fun run(): Int -// * -// * @Api4J -// * fun runAsync(): Future = jvmAsyncScope.future { run() } -// * -// * ``` -// * -// */ -//@OptIn(ExperimentalMultiplatform::class) -//@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS) -//@Retention(AnnotationRetention.BINARY) -//@OptionalExpectation -//public expect annotation class JvmAsync( -// val baseName: String = "", -// val suffix: String = "Async", -// /** -// * 是否转化为 property 的形式: -// * -// * ```kotlin -// * suspend fun foo(): T = ... -// * -// * // Generated -// * val fooAsync: Future get() = runInAsync { foo() } -// * ``` -// * -// * 只有函数没有参数时有效。 -// * -// */ -// actual val asProperty: Boolean = false -//) diff --git a/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/BotRequestorJs.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/exceptions.kt similarity index 64% rename from simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/BotRequestorJs.kt rename to simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/exceptions.kt index f6d22f17..8cce7629 100644 --- a/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/BotRequestorJs.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/exceptions.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,16 +17,12 @@ package love.forte.simbot.qguild -import kotlinx.coroutines.promise -import love.forte.simbot.qguild.api.QQGuildApi -import kotlin.js.Promise - - /** - * 直接通过bot进行请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 + * 当尝试使用已经被关闭的 [Bot] 进行某些操作时(例如尝试启动它) */ -public fun doRequestAsync(bot: Bot, api: QQGuildApi): Promise = bot.promise { - api.requestBy(bot) +public open class BotAlreadyCancelledException : IllegalStateException { + public constructor() : super() + public constructor(message: String?) : super(message) + public constructor(message: String?, cause: Throwable?) : super(message, cause) + public constructor(cause: Throwable?) : super(cause) } diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/BotImpl.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/BotImpl.kt index ff6f8f94..00d2f23a 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/BotImpl.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/BotImpl.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -23,14 +23,18 @@ import io.ktor.client.plugins.contentnegotiation.* import io.ktor.client.plugins.websocket.* import io.ktor.http.* import io.ktor.serialization.kotlinx.json.* -import kotlinx.atomicfu.AtomicLong -import kotlinx.atomicfu.atomic -import kotlinx.atomicfu.getAndUpdate -import kotlinx.atomicfu.updateAndGet import kotlinx.coroutines.* import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.serialization.json.Json +import love.forte.simbot.common.atomic.AtomicLong +import love.forte.simbot.common.atomic.atomic +import love.forte.simbot.common.collection.ConcurrentQueue +import love.forte.simbot.common.collection.ExperimentalSimbotCollectionApi +import love.forte.simbot.common.collection.createConcurrentQueue +import love.forte.simbot.common.stageloop.loop +import love.forte.simbot.common.weak.WeakRef +import love.forte.simbot.common.weak.weakRef import love.forte.simbot.logger.Logger import love.forte.simbot.logger.LoggerFactory import love.forte.simbot.qguild.* @@ -43,15 +47,15 @@ import love.forte.simbot.qguild.event.Ready import love.forte.simbot.qguild.event.Shard import love.forte.simbot.qguild.event.Signal import love.forte.simbot.qguild.model.User -import love.forte.simbot.util.stageloop.loop +import kotlin.concurrent.Volatile import kotlin.coroutines.CoroutineContext -import kotlin.jvm.Volatile /** * implementation for [Bot]. * @author ForteScarlet */ +@OptIn(ExperimentalSimbotCollectionApi::class) internal class BotImpl( override val ticket: Bot.Ticket, override val configuration: BotConfiguration, @@ -60,14 +64,15 @@ internal class BotImpl( // private val parentJob: Job override val coroutineContext: CoroutineContext - private val supervisorJob: Job + private val job: Job init { - val configContext = configuration.coroutineContext - val configJob = configContext[Job] - supervisorJob = SupervisorJob(configJob) - coroutineContext = configContext + supervisorJob + CoroutineName("QGBot.${ticket.appId}") configCheck() + + val configContext = configuration.coroutineContext + val parentJob = configContext[Job] + job = SupervisorJob(parentJob) + coroutineContext = configContext.minusKey(Job) + job + CoroutineName("QGBot.${ticket.appId}") } private fun configCheck() { @@ -102,12 +107,7 @@ internal class BotImpl( private fun HttpClientConfig<*>.configWsHttpClient() { - install(ContentNegotiation) { - json(wsDecoder) - } - - install(WebSockets) { - // ...? + WebSockets { } } @@ -159,8 +159,8 @@ internal class BotImpl( internal val intents: Intents = configuration.intents - internal val processorQueue: SimpleConcurrentQueue = createSimpleConcurrentQueue() - internal val preProcessorQueue: SimpleConcurrentQueue = createSimpleConcurrentQueue() + internal val processorQueue: ConcurrentQueue = createConcurrentQueue() + internal val preProcessorQueue: ConcurrentQueue = createConcurrentQueue() override fun registerPreProcessor(processor: EventProcessor): DisposableHandle { @@ -173,31 +173,31 @@ internal class BotImpl( return DisposableHandleImpl(processorQueue, processor) } - private class DisposableHandleImpl(queue: SimpleConcurrentQueue, subject: EventProcessor) : + private class DisposableHandleImpl(queue: ConcurrentQueue, subject: EventProcessor) : DisposableHandle { @Suppress("unused") private val disposed = atomic(false) // 0 @Volatile - private var queueRef: WeakRef>? = WeakRef(queue) + private var queueRef: WeakRef>? = weakRef(queue) @Volatile - private var subjectRef: WeakRef? = WeakRef(subject) + private var subjectRef: WeakRef? = weakRef(subject) override fun dispose() { - if (!disposed.compareAndSet(expect = false, update = true)) { + if (!disposed.compareAndSet(expect = false, value = true)) { return } val queue = queueRef?.let { ref -> - ref.get().also { + ref.value.also { ref.clear() queueRef = null } } val subject = subjectRef?.let { ref -> - ref.get().also { + ref.value.also { ref.clear() subjectRef = null } @@ -210,71 +210,77 @@ internal class BotImpl( queue.remove(subject) } - private companion object { - } + private companion object } /** * 用于 [start] 或内部重新连接的锁。 */ - internal val connectLock = Mutex() + internal val startLock = Mutex() - private val stageLoopJob = atomic(null) - private val _client = atomic(null) + // TODO atomic? lock update? +// private val stageLoopJob = atomicRef(null) - override val client: Bot.Client? get() = _client.value + @Volatile + private var stageLoopJob: Job? = null - internal fun getAndUpdateClient(function: (ClientImpl?) -> ClientImpl?): ClientImpl? { - return _client.getAndUpdate { function(it) } - } + private val clientLock = Mutex() + + // TODO With Lock + @Volatile + private var _client: ClientImpl? = null + override val client: Bot.Client? get() = _client - override suspend fun start(): Boolean = start(GatewayApis.Normal.requestBy(this)) + internal suspend inline fun updateClient(new: ClientImpl?) { + clientLock.withLock { + _client?.cancel() + _client = new + } + } - override suspend fun start(gateway: GatewayInfo): Boolean = connectLock.withLock { - val gatewayInfo: GatewayInfo = GatewayApis.Normal.requestBy(this) + override suspend fun start() { + start(requestData(GatewayApis.Normal)) + } - logger.debug("Request gateway {} by shard {}", gatewayInfo, shard) + override suspend fun start(gateway: GatewayInfo) { + startLock.withLock { + stageLoopJob?.cancel() + stageLoopJob = null - val state = Connect(this, shard, gatewayInfo) + logger.debug("Request gateway {} by shard {}", gateway, shard) -// val loop = StageLoop() + val state = Connect(this, shard, gateway) - logger.debug("Create state loop {}", state) + logger.debug("Create state loop {}", state) - var st: State? = state - do { - logger.debug("Current state: {}", st) - st = st?.invoke() - } while (st != null && st !is ReceiveEvent) + var st: State? = state + do { + logger.debug("Current state: {}", st) + st = st?.invoke() + } while (st != null && st !is ReceiveEvent) - if (st == null) { - // 当前状态为空且尚未进入事件接收状态 - throw IllegalStateException("The current state is null and not yet in the event receiving state") - } + if (st == null) { + // 当前状态为空且尚未进入事件接收状态 + throw IllegalStateException("The current state is null and not yet in the event receiving state") + } - val stageLoopJob: Job = launch { - st.loop() - } + val stageLoopJob: Job = launch { + st.loop() + } - this.stageLoopJob.getAndUpdate { old -> - old?.cancel() - stageLoopJob + this.stageLoopJob = stageLoopJob } - - return true } - override suspend fun cancel(reason: Throwable?): Boolean { - if (supervisorJob.isCancelled) return false + override fun cancel(reason: Throwable?) { + if (!job.isActive) return - supervisorJob.cancel(reason?.let { CancellationException(it.message, it) }) - supervisorJob.join() - return true + job.cancel(reason?.let { CancellationException(it.message, it) }) } override suspend fun join() { - supervisorJob.join() + job.join() } @@ -283,7 +289,6 @@ internal class BotImpl( val sessionInfo: SessionInfo, val wsSession: DefaultClientWebSocketSession, ) : Bot.Client { - override val bot: Bot get() = this@BotImpl override val seq: Long get() = sessionInfo.seq.value override val isActive: Boolean get() = wsSession.isActive @@ -305,12 +310,10 @@ internal class BotImpl( } } - - //// self api override suspend fun me(): User { - return GetBotInfoApi.requestBy(this) + return GetBotInfoApi.requestDataBy(this) } } @@ -324,7 +327,15 @@ internal class AtomicLongRef(initValue: Long = 0) { atomicValue.value = value } - fun updateAndGet(function: (Long) -> Long): Long = atomicValue.updateAndGet(function) + fun updateAndGet(function: (Long) -> Long): Long { + while (true) { + val current = value + val new = function(current) + if (atomicValue.compareAndSet(current, new)) { + return new + } + } + } } internal data class SessionInfo( diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/BotStates.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/BotStates.kt index 0b94d335..6f4d7caf 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/BotStates.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/BotStates.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -23,22 +23,24 @@ import io.ktor.client.request.* import io.ktor.websocket.* import kotlinx.coroutines.* import kotlinx.coroutines.channels.ClosedReceiveChannelException -import kotlinx.coroutines.flow.* import kotlinx.coroutines.sync.withLock import kotlinx.serialization.SerializationException import kotlinx.serialization.json.int import kotlinx.serialization.json.jsonObject import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.longOrNull +import love.forte.simbot.common.collection.ExperimentalSimbotCollectionApi import love.forte.simbot.logger.Logger import love.forte.simbot.logger.isDebugEnabled import love.forte.simbot.logger.isTraceEnabled +import love.forte.simbot.qguild.QGInternalApi import love.forte.simbot.qguild.api.GatewayApis import love.forte.simbot.qguild.api.GatewayInfo +import love.forte.simbot.qguild.doInvoke import love.forte.simbot.qguild.err import love.forte.simbot.qguild.event.* import love.forte.simbot.qguild.internal.BotImpl.ClientImpl -import love.forte.simbot.qguild.requestBy +import love.forte.simbot.qguild.requestDataBy import kotlin.math.max import kotlin.random.Random import kotlin.time.Duration.Companion.milliseconds @@ -74,7 +76,7 @@ private fun canBeIdentified(code: Short): Boolean { /** * 流转的状态 */ -internal abstract class State : love.forte.simbot.util.stageloop.State() { +internal abstract class State : love.forte.simbot.common.stageloop.State() { internal abstract val bot: BotImpl internal val logger: Logger get() = bot.logger } @@ -82,7 +84,7 @@ internal abstract class State : love.forte.simbot.util.stageloop.State() internal class Connect( override val bot: BotImpl, private val shard: Shard, - private val gateway: GatewayInfo + private val gateway: GatewayInfo, ) : State() { override suspend fun invoke(): State { val intents = bot.configuration.intents @@ -235,7 +237,7 @@ internal class HeartbeatJob( /** - * 创建 [ClientImpl], 并在异步中处理事件。 + * 创建 [ClientImpl] */ internal class CreateClient( override val bot: BotImpl, @@ -243,11 +245,6 @@ internal class CreateClient( private val session: DefaultClientWebSocketSession ) : State() { override suspend fun invoke(): State { - val eventBufferCapacity = bot.configuration.eventBufferCapacity - logger.debug("Bot event buffer capacity: {}", eventBufferCapacity) - val sharedFlow = MutableSharedFlow(extraBufferCapacity = eventBufferCapacity) - launchEventProcessJob(sharedFlow) - val client = bot.ClientImpl( botClientSession.readyData, botClientSession, @@ -256,64 +253,11 @@ internal class CreateClient( logger.debug("Current client: {}", client) - // store client - bot.getAndUpdateClient { old -> - old?.cancel() - client - } + // update client + bot.updateClient(client) // next: receive events - return ReceiveEvent(bot, client, sharedFlow) - } - - private fun launchEventProcessJob(flow: SharedFlow): Job { - return flow - .onEach { (raw, event) -> - // 先顺序地使用 preProcessor 处理 - bot.preProcessorQueue.forEach { processor -> - runCatching { - processor.invoke(event, raw) - }.onFailure { e -> - if (logger.isDebugEnabled) { - logger.debug( - "Event pre-precess failure. raw: {}, event: {}", raw, event - ) - } - logger.error("Event pre-precess failure.", e) - } - } - - // 然后异步地正常处理 - // TODO 同步异步 configurable? - // bot launch or session launch? - bot.launch { - bot.processorQueue.forEach { processor -> - runCatching { - processor.invoke(event, raw) - }.onFailure { e -> - if (logger.isDebugEnabled) { - logger.debug( - "Event precess failure. raw: {}, event: {}", raw, event - ) - } - logger.error("Event precess failure.", e) - } - } - } - - }.onCompletion { cause -> - if (cause == null) { - logger.debug("Event process flow is completed. No cause.") - } else { - logger.debug( - "Event process flow is completed. Cause: {}", cause.message, cause - ) - } - }.catch { cause -> - logger.error( - "Event process flow on error: {}", cause.message, cause - ) - }.launchIn(session) + return ReceiveEvent(bot, client) } } @@ -323,10 +267,8 @@ internal class CreateClient( internal class ReceiveEvent( override val bot: BotImpl, private val client: ClientImpl, - private val eventSharedFlow: MutableSharedFlow, ) : State() { - // TODO session.incoming buffer - + @OptIn(QGInternalApi::class) override suspend fun invoke(): State? { val session = client.wsSession val seq = client.sessionInfo.seq @@ -345,7 +287,7 @@ internal class ReceiveEvent( } suspend fun doIdentify(): State { - val gatewayInfo: GatewayInfo = GatewayApis.Normal.requestBy(bot) + val gatewayInfo: GatewayInfo = GatewayApis.Normal.requestDataBy(bot) logger.debug("Reconnect gateway {} by shard {}", gatewayInfo, bot.shard) return Connect(bot, bot.shard, gatewayInfo) } @@ -386,6 +328,8 @@ internal class ReceiveEvent( } bot.cancel(e) + session.cancel(e.message ?: e.toString(), e) + bot.updateClient(null) } } @@ -407,8 +351,6 @@ internal class ReceiveEvent( val raw = (frame as? Frame.Text)?.readText() ?: run { logger.debug("Not Text frame {}, skip.", frame) return this -// loop.appendStage(this) // next: this -// return } logger.debug("Received text frame raw: {}", raw) val json = bot.wsDecoder.parseToJsonElement(raw) @@ -438,7 +380,8 @@ internal class ReceiveEvent( val dispatchSeq = dispatch.seq // 推送事件 - eventSharedFlow.emit(EventData(raw, dispatch)) + emitEvent(bot, dispatch, raw) +// eventSharedFlow.emit(EventData(raw, dispatch)) // seq留下最大值 val currentSeq = seq.updateAndGet { pref -> max(pref, dispatchSeq) } @@ -463,7 +406,42 @@ internal class ReceiveEvent( // next: self return this -// loop.appendStage(this) + } +} + +@OptIn(ExperimentalSimbotCollectionApi::class) +private suspend fun emitEvent(bot: BotImpl, dispatch: Signal.Dispatch, raw: String) { + val logger = bot.logger + // 先顺序地使用 preProcessor 处理 + bot.preProcessorQueue.forEach { processor -> + runCatching { + processor.doInvoke(dispatch, raw) + }.onFailure { e -> + if (logger.isDebugEnabled) { + logger.debug( + "Event pre-precess failure. raw: {}, event: {}", raw, dispatch + ) + } + logger.error("Event pre-precess failure.", e) + } + } + + // 然后异步地正常处理 + // TODO 同步异步 configurable? + // bot launch or session launch? + bot.launch { + bot.processorQueue.forEach { processor -> + runCatching { + processor.doInvoke(dispatch, raw) + }.onFailure { e -> + if (logger.isDebugEnabled) { + logger.debug( + "Event precess failure. raw: {}, event: {}", raw, dispatch + ) + } + logger.error("Event precess failure.", e) + } + } } } @@ -480,10 +458,10 @@ internal class Resume( private val client: ClientImpl, ) : State() { override suspend fun invoke(): State { - val newSession = bot.connectLock.withLock { + val newSession = bot.startLock.withLock { // 关闭当前连接 client.cancelAndJoin() - val gateway = GatewayApis.Normal.requestBy(bot) + val gateway = GatewayApis.Normal.requestDataBy(bot) bot.wsClient.ws { gateway }.apply { // 发送 Opcode6 val resumeSignal = @@ -492,7 +470,6 @@ internal class Resume( } } - return WaitingHello(bot, newSession) { hello -> // 跳过 wait ready, 直接HeartbeatJob HeartbeatJob(bot, hello.data, client.readyData, session) diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/SimpleConcurrentQueue.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/SimpleConcurrentQueue.kt deleted file mode 100644 index 0c8c6852..00000000 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/SimpleConcurrentQueue.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.qguild.internal - - -/** - * - * @author ForteScarlet - */ -public expect class SimpleConcurrentQueue { - - /** - * 向此队列添加一个元素。 - */ - public fun add(element: T): Boolean - - /** - * 移除指定目标。 - */ - public fun remove(element: T): Boolean -} - - -public expect fun createSimpleConcurrentQueue(): SimpleConcurrentQueue - - -public expect inline fun SimpleConcurrentQueue.forEach(block: (T) -> Unit) diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/WeakRef.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/WeakRef.kt deleted file mode 100644 index ed9dc06b..00000000 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/WeakRef.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.qguild.internal - -/** - * 预期为弱引用的平台类型。 - */ -public expect class WeakRef(referred: T) { - - /** - * 获取引用或当引用已被清除的时候得到null。 - */ - public fun get(): T? - - /** - * 清除内部引用。 - */ - public fun clear() - -} diff --git a/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/BotRequests.js.kt b/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/BotRequests.js.kt new file mode 100644 index 00000000..44ba4efe --- /dev/null +++ b/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/BotRequests.js.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.qguild + +import io.ktor.client.statement.* +import kotlinx.coroutines.promise +import love.forte.simbot.annotations.Api4Js +import love.forte.simbot.qguild.api.QQGuildApi +import kotlin.js.Promise + + +/** + * 直接通过bot进行请求。 + * + * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 + */ +@Api4Js +public fun Bot.requestByAsync(api: QQGuildApi<*>): Promise = promise { + request(api) +} + +/** + * 直接通过bot进行请求。 + * + * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 + */ +@Api4Js +public fun Bot.requestTextByAsync(api: QQGuildApi<*>): Promise = promise { + requestText(api) +} + +/** + * 直接通过bot进行请求。 + * + * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 + */ +@Api4Js +public fun Bot.requestDataByAsync(api: QQGuildApi): Promise = promise { + requestData(api) +} diff --git a/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/PlatformBaseBotJs.kt b/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/PlatformBaseBotJs.kt deleted file mode 100644 index c7268ba3..00000000 --- a/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/PlatformBaseBotJs.kt +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.qguild - -import kotlinx.coroutines.await -import love.forte.simbot.qguild.event.Signal -import kotlin.js.Promise -import kotlin.reflect.KClass -import kotlin.reflect.cast - -/** - * JS平台下实现的 [Bot] 基础抽象类,对 [Bot] 提供额外的能力。 - * - * _仅由内部使用_ - */ -@InternalApi -public actual interface BasePlatformBot { - /** - * 添加一个事件**预处理器**。 - * - * [registerPreProcessor] 与 [processor] 不同的是, - * [registerPreProcessor] 所注册的所有事件处理器会在接收到事件的时候以**同步顺序**的方式依次执行, - * 而后再交由下游的 [processor] 处理。 - * - * 也同样因此,尽可能避免在 [registerPreProcessor] 中执行任何耗时逻辑,这可能会对事件处理流程造成严重阻塞。 - * - * @param processor 用于处理事件的函数类型 - */ - public actual fun registerPreProcessor(processor: EventProcessor): DisposableHandle - - /** - * 添加一个事件处理器。 - * - */ - public actual fun registerProcessor(processor: EventProcessor): DisposableHandle - - - /** - * 注册一个使用非挂起函数 [processor] 的事件处理器。 - */ - @Api4JS - public fun registerBlockingProcessor(processor: Signal.Dispatch.(raw: String) -> Unit): DisposableHandle { - return registerProcessor { raw -> processor.invoke(this, raw) } - } - - /** - * 注册一个使用非挂起函数 [processor] 的事件处理器, - * 并通过 [KClass] 对类型进行筛选。 - */ - @Api4JS - @JsName("registerBlockingProcessorWithType") - public fun registerBlockingProcessor( - eventType: KClass, - processor: E.(raw: String) -> Unit - ): DisposableHandle { - return registerProcessor { raw -> - if (eventType.isInstance(this)) { - val event = eventType.cast(this) - processor.invoke(event, raw) - } - } - } - - /** - * 注册一个使用异步函数(返回值为 [Promise])类型 [processor] 的事件处理器。 - * - * [processor] 返回的异步结果会在当前监听函数流程内被[挂起并获取][Promise.await]。 - */ - @Api4JS - public fun registerAsyncProcessor(processor: Signal.Dispatch.(raw: String) -> Promise): DisposableHandle { - return registerProcessor { raw -> processor.invoke(this, raw).await() } - } - - /** - * 注册一个使用异步函数(返回值为 [Promise])类型 [processor] 的事件处理器, - * 并通过 [KClass] 对类型进行筛选。 - * - * [processor] 返回的异步结果会在当前监听函数流程内被[挂起并获取][Promise.await]。 - */ - @Api4JS - @JsName("registerAsyncProcessorWithType") - public fun registerAsyncProcessor( - eventType: KClass, - processor: E.(raw: String) -> Promise - ): DisposableHandle { - return registerProcessor { raw -> - if (eventType.isInstance(this)) { - val event = eventType.cast(this) - processor.invoke(event, raw).await() - } - } - } -} - - diff --git a/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/internal/SimpleConcurrentQueueJs.kt b/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/internal/SimpleConcurrentQueueJs.kt deleted file mode 100644 index d22d48ea..00000000 --- a/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/internal/SimpleConcurrentQueueJs.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.qguild.internal - - -/** - * typealias [SimpleConcurrentQueue] with [ArrayList] - */ -public actual typealias SimpleConcurrentQueue = ArrayList - -/** - * - */ -public actual fun createSimpleConcurrentQueue(): SimpleConcurrentQueue = SimpleConcurrentQueue(16) - - -/** - * foreach [SimpleConcurrentQueue] values. - */ -public actual inline fun SimpleConcurrentQueue.forEach(block: (T) -> Unit) { - this.forEach(block) -} diff --git a/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/internal/WeakRefJs.kt b/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/internal/WeakRefJs.kt deleted file mode 100644 index f0721e4d..00000000 --- a/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/internal/WeakRefJs.kt +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.qguild.internal - -/** - * 弱引用类型,在JS平台下并非真正的弱引用。 - * 内部将会持有其引用并直到使用 [clear] 将其清除。 - * - */ -public actual class WeakRef actual constructor(referred: T) { - private var actualValue: T? = referred - - /** - * 获取内部持有的引用对象。 - * - * 直到调用 [clear] 才会得到null。 - */ - public actual fun get(): T? = actualValue - - /** - * 清除内部对象。 - */ - public actual fun clear() { - actualValue = null - } - -} - diff --git a/simbot-component-qq-guild-stdlib/src/jvmMain/java/module-info.java b/simbot-component-qq-guild-stdlib/src/jvmMain/java/module-info.java new file mode 100644 index 00000000..7f16a4ee --- /dev/null +++ b/simbot-component-qq-guild-stdlib/src/jvmMain/java/module-info.java @@ -0,0 +1,14 @@ +module simbot.component.qqguild.stdlib { + requires kotlin.stdlib; + requires transitive simbot.component.qqguild.api; + // simbot + requires transitive simbot.common.stageloop; + requires transitive simbot.common.atomic; + requires static simbot.common.annotations; + // ktor + requires io.ktor.client.content.negotiation; + requires io.ktor.serialization.kotlinx.json; + requires io.ktor.client.websockets; + + exports love.forte.simbot.qguild; +} diff --git a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/BotRequestorJvm.kt b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/BotRequestorJvm.kt deleted file mode 100644 index b8ff49ab..00000000 --- a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/BotRequestorJvm.kt +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -@file:JvmName("BotRequestUtil") - -package love.forte.simbot.qguild - -import kotlinx.coroutines.future.future -import love.forte.simbot.Api4J -import love.forte.simbot.qguild.api.QQGuildApi -import love.forte.simbot.utils.runInNoScopeBlocking -import java.util.concurrent.CompletableFuture - -/** - * @see requestBlocking - * @suppress use [requestBlocking] or [requestAsync] - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@Api4J -@Deprecated("Use 'requestBlocking'", ReplaceWith("requestBlocking(bot, api)")) -public fun doRequest(bot: Bot, api: QQGuildApi): R = requestBlocking(bot, api) - -/** - * 直接通过bot进行阻塞地请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@Api4J -public fun requestBlocking(bot: Bot, api: QQGuildApi): R = runInNoScopeBlocking { - api.requestBy(bot) -} - -/** - * 直接通过bot进行异步地请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@Api4J -public fun requestAsync(bot: Bot, api: QQGuildApi): CompletableFuture = bot.future { - api.requestBy(bot) -} - -/** - * 直接通过bot进行阻塞地请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@Api4J -public fun requestRawBlocking(bot: Bot, api: QQGuildApi<*>): String = runInNoScopeBlocking { - api.requestRawBy(bot) -} - -/** - * 直接通过bot进行异步地请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@Api4J -public fun requestRawAsync(bot: Bot, api: QQGuildApi<*>): CompletableFuture = bot.future { - api.requestRawBy(bot) -} diff --git a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/BotRequests.jvm.kt b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/BotRequests.jvm.kt new file mode 100644 index 00000000..65df161a --- /dev/null +++ b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/BotRequests.jvm.kt @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +@file:JvmName("BotRequests") +@file:JvmMultifileClass + +package love.forte.simbot.qguild + +import io.ktor.client.statement.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.future.future +import love.forte.simbot.annotations.Api4J +import love.forte.simbot.qguild.api.QQGuildApi +import love.forte.simbot.suspendrunner.runInNoScopeBlocking +import java.util.concurrent.CompletableFuture + + +/** + * 直接通过bot进行请求。 + * + * 异步请求使用的作用域 [scope] 默认为 [Bot.apiClient]。 + */ +@Api4J +@JvmOverloads +public fun Bot.requestAsync(api: QQGuildApi<*>, scope: CoroutineScope? = null): CompletableFuture = + (scope ?: apiClient).future { + request(api) + } + +/** + * 直接通过bot进行请求。 + * + * 异步请求使用的作用域 [scope] 默认为 [Bot.apiClient]。 + */ +@Api4J +@JvmOverloads +public fun Bot.requestTextAsync(api: QQGuildApi<*>, scope: CoroutineScope? = null): CompletableFuture = + (scope ?: apiClient).future { + requestText(api) + } + +/** + * 直接通过bot进行请求。 + * + * 异步请求使用的作用域 [scope] 默认为 [Bot.apiClient]。 + * + * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 + */ +@Api4J +@JvmOverloads +public fun Bot.requestDataAsync( + api: QQGuildApi, + scope: CoroutineScope? = null +): CompletableFuture = (scope ?: apiClient).future { + requestData(api) +} + +/** + * 直接通过bot进行请求。 + */ +@Api4J +public fun Bot.requestByBlocking(api: QQGuildApi<*>): HttpResponse = runInNoScopeBlocking { + request(api) +} + +/** + * 直接通过bot进行请求。 + */ +@Api4J +public fun Bot.requestTextByBlocking(api: QQGuildApi<*>): String = runInNoScopeBlocking { + requestText(api) +} + +/** + * 直接通过bot进行请求。 + * + * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 + */ +@Api4J +public fun Bot.requestDataByBlocking(api: QQGuildApi): R = runInNoScopeBlocking { + requestData(api) +} diff --git a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/EventProcessor.jvm.kt b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/EventProcessor.jvm.kt new file mode 100644 index 00000000..62f2c045 --- /dev/null +++ b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/EventProcessor.jvm.kt @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +@file:JvmName("EventProcessors") +@file:JvmMultifileClass + +package love.forte.simbot.qguild + + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.future.await +import kotlinx.coroutines.runInterruptible +import love.forte.simbot.qguild.event.Signal +import love.forte.simbot.suspendrunner.runInBlocking +import org.jetbrains.annotations.Blocking +import org.jetbrains.annotations.NonBlocking +import java.util.concurrent.CompletableFuture + +/** + * 以阻塞的形式实现 [EventProcessor],是提供给 Java 的友好类型。 + * 可直接使用 `EventProcessors.block((event, raw) -> { ... })` 构建。 + * + * [block] 最终会在 [Dispatchers.IO] 的调度器下使用 [runInterruptible] 执行。 + */ +public fun interface JBlockEventProcessor : EventProcessor { + /** + * 处理事件。 + */ + @Blocking + @Throws(Exception::class) + public fun block(event: Signal.Dispatch, raw: String) + + @JvmSynthetic + override suspend fun Signal.Dispatch.invoke(raw: String) { + runInBlocking { } + + runInterruptible(Dispatchers.IO) { block(this, raw) } + } +} + +/** + * 以异步的形式实现 [EventProcessor],是提供给 Java 的友好类型。 + * 可直接使用 `EventProcessors.async((event, raw) -> { ... })` 构建。 + */ +public fun interface JAsyncEventProcessor : EventProcessor { + /** + * 处理事件。 + */ + @Throws(Exception::class) + @NonBlocking + public fun async(event: Signal.Dispatch, raw: String): CompletableFuture + + @JvmSynthetic + override suspend fun Signal.Dispatch.invoke(raw: String) { + async(this, raw).await() + } +} + +/** + * 构建一个 [JBlockEventProcessor]. + */ +public fun block(function: JBlockEventProcessor): JBlockEventProcessor = function + +/** + * 构建一个 [JAsyncEventProcessor]. + */ +public fun async(function: JAsyncEventProcessor): JAsyncEventProcessor = function diff --git a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/PlatformBaseBotJvm.kt b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/PlatformBaseBotJvm.kt deleted file mode 100644 index 86a7da19..00000000 --- a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/PlatformBaseBotJvm.kt +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.qguild - -import kotlinx.coroutines.future.await -import love.forte.simbot.Api4J -import love.forte.simbot.qguild.event.Signal -import java.util.concurrent.CompletionStage -import java.util.function.BiConsumer -import java.util.function.BiFunction - -/** - * JVM平台下实现的 [Bot] 基础抽象类,对 [Bot] 提供额外的能力。 - * - * _仅由内部使用_ - */ -@InternalApi -public actual interface BasePlatformBot { - /** - * 添加一个事件**预处理器**。 - * - * [registerPreProcessor] 与 [processor] 不同的是, - * [registerPreProcessor] 所注册的所有事件处理器会在接收到事件的时候以**同步顺序**的方式依次执行, - * 而后再交由下游的 [processor] 处理。 - * - * 也同样因此,尽可能避免在 [registerPreProcessor] 中执行任何耗时逻辑,这可能会对事件处理流程造成严重阻塞。 - * - * @param processor 用于处理事件的函数类型 - */ - @JvmSynthetic - public actual fun registerPreProcessor(processor: EventProcessor): DisposableHandle - - /** - * 添加一个事件处理器。 - * - */ - @JvmSynthetic - public actual fun registerProcessor(processor: EventProcessor): DisposableHandle - - - /** - * 注册一个使用非挂起的阻塞函数 [processor] 的事件处理器。 - * - */ - @Api4J - public fun registerBlockingProcessor(processor: BiConsumer): DisposableHandle { - return registerProcessor { raw -> processor.accept(this, raw) } - } - - /** - * 注册一个使用非挂起的阻塞函数 [processor] 的事件处理器,并通过 [Class] 对类型进行筛选。 - */ - @Api4J - public fun registerBlockingProcessor( - eventType: Class, - processor: BiConsumer - ): DisposableHandle { - return registerProcessor { raw -> - if (eventType.isInstance(this)) { - val event = eventType.cast(this) - processor.accept(event, raw) - } - } - } - - /** - * 注册一个使用结果类型为 [CompletionStage] 的异步函数 [processor] 的事件处理器。 - * - * [processor] 返回的异步结果会在当前监听函数流程内被[挂起并获取][CompletionStage.await]。 - * - */ - @Api4J - public fun registerAsyncProcessor(processor: BiFunction>): DisposableHandle { - return registerProcessor { raw -> processor.apply(this, raw).await() } - } - - /** - * 注册一个使用结果类型为 [CompletionStage] 的异步函数 [processor] 的事件处理器, - * 并通过 [Class] 对类型进行筛选。 - * - * [processor] 返回的异步结果会在当前监听函数流程内被[挂起并获取][CompletionStage.await]。 - * - */ - @Api4J - public fun registerAsyncProcessor( - eventType: Class, - processor: BiFunction> - ): DisposableHandle { - return registerProcessor { raw -> - if (eventType.isInstance(this)) { - val event = eventType.cast(this) - processor.apply(event, raw).await() - } - } - } -} - - diff --git a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/internal/SimpleConcurrentQueueJvm.kt b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/internal/SimpleConcurrentQueueJvm.kt deleted file mode 100644 index deaee6c5..00000000 --- a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/internal/SimpleConcurrentQueueJvm.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.qguild.internal - -import java.util.concurrent.ConcurrentLinkedQueue - -/** - * typealias [SimpleConcurrentQueue] with [ConcurrentLinkedQueue] - */ -public actual typealias SimpleConcurrentQueue = ConcurrentLinkedQueue - -public actual fun createSimpleConcurrentQueue(): SimpleConcurrentQueue = SimpleConcurrentQueue() - -/** - * foreach [SimpleConcurrentQueue] values. - */ -public actual inline fun SimpleConcurrentQueue.forEach(block: (T) -> Unit) { - (this as Iterable).forEach(block) -} diff --git a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/internal/WeakRefJvm.kt b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/internal/WeakRefJvm.kt deleted file mode 100644 index 3fd77cc5..00000000 --- a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/internal/WeakRefJvm.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.qguild.internal - -import java.lang.ref.WeakReference - -/** - * 弱引用。 - * - * @see WeakReference - */ -public actual typealias WeakRef = WeakReference - diff --git a/simbot-component-qq-guild-stdlib/src/nativeMain/kotlin/love/forte/simbot/qguild/PlatformBaseBotNative.kt b/simbot-component-qq-guild-stdlib/src/nativeMain/kotlin/love/forte/simbot/qguild/PlatformBaseBotNative.kt deleted file mode 100644 index 7ab82510..00000000 --- a/simbot-component-qq-guild-stdlib/src/nativeMain/kotlin/love/forte/simbot/qguild/PlatformBaseBotNative.kt +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.qguild - - -/** - * Native平台下的 [BasePlatformBot] 实现。 - * _没有什么额外的能力提供。_ - * - * _仅由内部使用_ - */ -@InternalApi -public actual interface BasePlatformBot { - /** - * 添加一个事件**预处理器**。 - * - * [registerPreProcessor] 与 [processor] 不同的是, - * [registerPreProcessor] 所注册的所有事件处理器会在接收到事件的时候以**同步顺序**的方式依次执行, - * 而后再交由下游的 [processor] 处理。 - * - * 也同样因此,尽可能避免在 [registerPreProcessor] 中执行任何耗时逻辑,这可能会对事件处理流程造成严重阻塞。 - * - * @param processor 用于处理事件的函数类型 - */ - public actual fun registerPreProcessor(processor: EventProcessor): DisposableHandle - - /** - * 添加一个事件处理器。 - * - */ - public actual fun registerProcessor(processor: EventProcessor): DisposableHandle -} - - diff --git a/simbot-component-qq-guild-stdlib/src/nativeMain/kotlin/love/forte/simbot/qguild/internal/SimpleConcurrentQueueNative.kt b/simbot-component-qq-guild-stdlib/src/nativeMain/kotlin/love/forte/simbot/qguild/internal/SimpleConcurrentQueueNative.kt deleted file mode 100644 index 5a3939ce..00000000 --- a/simbot-component-qq-guild-stdlib/src/nativeMain/kotlin/love/forte/simbot/qguild/internal/SimpleConcurrentQueueNative.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.qguild.internal - -import kotlinx.atomicfu.locks.SynchronizedObject -import kotlinx.atomicfu.locks.synchronized - -/** - * native平台下的 [SimpleConcurrentQueue] 实现。 - * - * _注意:此实现尚处于试验阶段。_ - * - */ -public actual class SimpleConcurrentQueue(initialCapacity: Int) : SynchronizedObject() { - - private val list = ArrayList(initialCapacity) - - /** - * 向此队列添加一个元素。 - */ - public actual fun add(element: T): Boolean { - return synchronized(this) { list.add(element) } - } - - /** - * 移除指定目标。 - */ - public actual fun remove(element: T): Boolean { - return synchronized(this) { list.remove(element) } - } - - /** - * 得到当前队列的迭代器。 - */ - public val iterator: MutableIterator get() = list.iterator() -} - -/** - * 创建一个 [SimpleConcurrentQueue]。 - */ -public actual fun createSimpleConcurrentQueue(): SimpleConcurrentQueue = SimpleConcurrentQueue(10) - -/** - * foreach [SimpleConcurrentQueue] values. - */ -public actual inline fun SimpleConcurrentQueue.forEach(block: (T) -> Unit) { - iterator.forEach(block) -} diff --git a/simbot-component-qq-guild-stdlib/src/nativeMain/kotlin/love/forte/simbot/qguild/internal/WeakRefNative.kt b/simbot-component-qq-guild-stdlib/src/nativeMain/kotlin/love/forte/simbot/qguild/internal/WeakRefNative.kt deleted file mode 100644 index 74988210..00000000 --- a/simbot-component-qq-guild-stdlib/src/nativeMain/kotlin/love/forte/simbot/qguild/internal/WeakRefNative.kt +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.qguild.internal - -import kotlin.native.ref.WeakReference - -/** - * - * 弱引用。 - * - * @see WeakReference - */ -public actual typealias WeakRef = WeakReference - From f1c6e5667605402ca91803440c5053b048225330 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Sat, 13 Jan 2024 02:52:05 +0800 Subject: [PATCH 08/71] Module: stdlib --- buildSrc/src/main/kotlin/JsConfig.kt | 32 +- ...tcg-suspend-transform-configure.gradle.kts | 1 + gradle.properties | 30 +- kotlin-js-store/yarn.lock | 1617 +++++++++++++---- .../build.gradle.kts | 16 +- .../love/forte/simbot/qguild/QQGuild.kt | 9 +- .../simbot/qguild/QQGuildApiException.kt | 4 - .../forte/simbot/qguild/api/ApiRequests.kt | 2 - .../qguild/api/message/MessageSendApi.kt | 3 - .../love/forte/simbot/qguild/model/Emoji.kt | 9 +- .../src/jvmMain/java/module-info.java | 2 + .../simbot/qguild/api/ApiRequests.jvm.kt | 1 - .../build.gradle.kts | 4 +- .../build.gradle.kts | 4 +- .../build.gradle.kts | 5 +- .../forte/simbot/qguild/{ => stdlib}/Bot.kt | 2 +- .../qguild/{ => stdlib}/BotConfiguration.kt | 4 +- .../simbot/qguild/{ => stdlib}/BotFactory.kt | 4 +- .../qguild/{ => stdlib}/BotProcessor.kt | 2 +- .../simbot/qguild/{ => stdlib}/BotRequests.kt | 4 +- .../ConfigurableBotConfiguration.kt | 25 +- .../qguild/{ => stdlib}/DisposableHandle.kt | 11 +- .../qguild/{ => stdlib}/EventProcessor.kt | 2 +- .../simbot/qguild/{ => stdlib}/exceptions.kt | 2 +- .../qguild/{ => stdlib}/internal/BotImpl.kt | 10 +- .../qguild/{ => stdlib}/internal/BotStates.kt | 10 +- .../qguild/{ => stdlib}/BotRequests.js.kt | 2 +- .../src/jvmMain/java/module-info.java | 5 +- .../qguild/{ => stdlib}/BotRequests.jvm.kt | 2 +- .../qguild/{ => stdlib}/EventProcessor.jvm.kt | 2 +- 30 files changed, 1415 insertions(+), 411 deletions(-) rename simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/{ => stdlib}/Bot.kt (99%) rename simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/{ => stdlib}/BotConfiguration.kt (97%) rename simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/{ => stdlib}/BotFactory.kt (95%) rename simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/{ => stdlib}/BotProcessor.kt (96%) rename simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/{ => stdlib}/BotRequests.kt (96%) rename simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/{ => stdlib}/ConfigurableBotConfiguration.kt (92%) rename simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/{ => stdlib}/DisposableHandle.kt (85%) rename simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/{ => stdlib}/EventProcessor.kt (97%) rename simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/{ => stdlib}/exceptions.kt (96%) rename simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/{ => stdlib}/internal/BotImpl.kt (97%) rename simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/{ => stdlib}/internal/BotStates.kt (98%) rename simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/{ => stdlib}/BotRequests.js.kt (97%) rename simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/{ => stdlib}/BotRequests.jvm.kt (98%) rename simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/{ => stdlib}/EventProcessor.jvm.kt (98%) diff --git a/buildSrc/src/main/kotlin/JsConfig.kt b/buildSrc/src/main/kotlin/JsConfig.kt index 0c258f4a..7bc685b7 100644 --- a/buildSrc/src/main/kotlin/JsConfig.kt +++ b/buildSrc/src/main/kotlin/JsConfig.kt @@ -25,24 +25,26 @@ inline fun KotlinJsTargetDsl.configJs( block: () -> Unit = {} ) { if (nodeJs) { - nodejs { - testTask { - useMocha { - timeout = "10000" - } - } - } + nodejs() +// { +//// testTask { +//// useMocha { +//// timeout = "10000" +//// } +//// } +// } } if (browser) { - browser { - testTask{ - useKarma { - useChromeHeadless() - // useConfigDirectory(File(project.rootProject.projectDir, "karma")) - } - } - } + browser() +// { +// testTask{ +// useKarma { +// useChromeHeadless() +// // useConfigDirectory(File(project.rootProject.projectDir, "karma")) +// } +// } +// } } binaries.library() diff --git a/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts b/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts index d8b405cc..3034d846 100644 --- a/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts +++ b/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts @@ -21,6 +21,7 @@ plugins { suspendTransform { includeRuntime = false + includeAnnotation = false addJvmTransformers( // @JvmBlocking diff --git a/gradle.properties b/gradle.properties index 43390c50..ad34dc55 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,17 +1,29 @@ + kotlin.code.style=official + kotlin.js.generate.executable.default=false kotlin.mpp.stability.nowarn=true +# w: The following Kotlin/Native targets cannot be built on this machine and are disabled +# To hide this message, add 'kotlin.native.ignoreDisabledTargets=true' to the Gradle properties. kotlin.native.ignoreDisabledTargets=true -org.gradle.jvmargs=-Xmx4g -Dfile.encoding=UTF-8 -# -Xmx8g -Xms2g -XX:MaxMetaspaceSize=1g -# https://kotlinlang.org/docs/native-improving-compilation-time.html +# Such dependencies are not applicable for Kotlin/Native, consider changing the dependency type to 'implementation' or 'api'. +# To disable this warning, set the kotlin.native.ignoreIncorrectDependencies=true project property +kotlin.native.ignoreIncorrectDependencies=true + +# 'expect'/'actual' classes (including interfaces, objects, annotations, enums, and 'actual' typealiases) are in Beta. You can use -Xexpect-actual-classes flag to suppress this warning. Also see: https://youtrack.jetbrains.com/issue/KT-61573 +# "-Xexpect-actual-classes" +org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=1G -Dfile.encoding=UTF-8 +# https://kotlinlang.org/docs/native-improving-compilation-time.html#gradle-configuration +#org.gradle.workers.max=8 +org.gradle.parallel=true org.gradle.caching=true -org.gradle.workers.max=4 -#org.gradle.daemon=false -#org.gradle.parallel=false -# Hide: A compileOnly dependency is used in the Kotlin/Native target 'xxx' -# Such dependencies are not applicable for Kotlin/Native, consider changing the dependency type to 'implementation' or 'api'. -kotlin.native.ignoreIncorrectDependencies=true + +# https://kotlinlang.org/docs/gradle-compilation-and-caches.html#the-new-kotlin-compiler +#kotlin.experimental.tryK2=true + + +#http.proxyHost=localhost +#http.proxyPort=7790 diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index df1559fa..adc3867c 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -2,26 +2,15 @@ # yarn lockfile v1 -"@babel/code-frame@^7.10.4": - version "7.21.4" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" - integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== - dependencies: - "@babel/highlight" "^7.18.6" - -"@babel/helper-validator-identifier@^7.18.6": - version "7.19.1" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" - integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== -"@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" - js-tokens "^4.0.0" +"@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== "@jridgewell/gen-mapping@^0.3.0": version "0.3.3" @@ -37,15 +26,20 @@ resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + "@jridgewell/set-array@^1.0.1": version "1.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== -"@jridgewell/source-map@^0.3.2": - version "0.3.3" - resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.3.tgz#8108265659d4c33e72ffe14e33d6cc5eb59f2fda" - integrity sha512-b+fsZXeLYi9fEULmfBrhxn4IrPlINf8fiNarzTof004v3lFdntdwa9PF7vFJqm3mg7s+ScJMxXaE3Acp1irZcg== +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== dependencies: "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" @@ -55,11 +49,19 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== -"@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== +"@jridgewell/trace-mapping@^0.3.20": + version "0.3.21" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz#5dc1df7b3dc4a6209e503a924e1ca56097a2bb15" + integrity sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@jridgewell/trace-mapping@^0.3.9": version "0.3.18" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" @@ -68,79 +70,216 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" -"@rollup/plugin-commonjs@^21.0.1": - version "21.1.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-commonjs/-/plugin-commonjs-21.1.0.tgz#45576d7b47609af2db87f55a6d4b46e44fc3a553" - integrity sha512-6ZtHx3VHIp2ReNNDxHjuUml6ur+WcQ28N1yHgCQwsbNkQg2suhxGMDQGJOn/KuDxKtd1xuZP5xSTwBA4GQ8hbA== - dependencies: - "@rollup/pluginutils" "^3.1.0" - commondir "^1.0.1" - estree-walker "^2.0.1" - glob "^7.1.6" - is-reference "^1.2.1" - magic-string "^0.25.7" - resolve "^1.17.0" - -"@rollup/plugin-node-resolve@^13.1.3": - version "13.3.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz#da1c5c5ce8316cef96a2f823d111c1e4e498801c" - integrity sha512-Lus8rbUo1eEcnS4yTFKLZrVumLPY+YayBdWXgFSHYhTT2iJbMhoaaBL3xl5NCdeRytErGr8tZ0L71BMRmnlwSw== - dependencies: - "@rollup/pluginutils" "^3.1.0" - "@types/resolve" "1.17.1" - deepmerge "^4.2.2" - is-builtin-module "^3.1.0" - is-module "^1.0.0" - resolve "^1.19.0" - -"@rollup/plugin-typescript@^8.3.0": - version "8.5.0" - resolved "https://registry.yarnpkg.com/@rollup/plugin-typescript/-/plugin-typescript-8.5.0.tgz#7ea11599a15b0a30fa7ea69ce3b791d41b862515" - integrity sha512-wMv1/scv0m/rXx21wD2IsBbJFba8wGF3ErJIr6IKRfRj49S85Lszbxb4DCo8iILpluTjk2GAAu9CoZt4G3ppgQ== +"@socket.io/component-emitter@~3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" + integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== + +"@types/cookie@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" + integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== + +"@types/cors@^2.8.12": + version "2.8.17" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.17.tgz#5d718a5e494a8166f569d986794e49c48b216b2b" + integrity sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA== dependencies: - "@rollup/pluginutils" "^3.1.0" - resolve "^1.17.0" + "@types/node" "*" -"@rollup/pluginutils@^3.0.9", "@rollup/pluginutils@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" - integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== +"@types/eslint-scope@^3.7.3": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.56.2" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.2.tgz#1c72a9b794aa26a8b94ad26d5b9aa51c8a6384bb" + integrity sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw== dependencies: - "@types/estree" "0.0.39" - estree-walker "^1.0.1" - picomatch "^2.2.2" + "@types/estree" "*" + "@types/json-schema" "*" "@types/estree@*": version "1.0.1" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.1.tgz#aa22750962f3bf0e79d753d3cc067f010c95f194" integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== -"@types/estree@0.0.39": - version "0.0.39" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" - integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== +"@types/estree@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/json-schema@*", "@types/json-schema@^7.0.8": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== "@types/node@*": version "20.1.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.1.2.tgz#8fd63447e3f99aba6c3168fd2ec4580d5b97886f" integrity sha512-CTO/wa8x+rZU626cL2BlbCDzydgnFNgc19h4YvizpTO88MFQxab8wqisxaofQJ/9bLGugRdWIuX/TbIs6VVF6g== -"@types/node@^12.12.14": - version "12.20.55" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" - integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== +"@types/node@>=10.0.0": + version "20.11.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.0.tgz#8e0b99e70c0c1ade1a86c4a282f7b7ef87c9552f" + integrity sha512-o9bjXmDNcF7GbM4CNQpmi+TutCgap/K3w1JyKgxAjqx41zp9qlIAVFi0IhCNsJcXolEqLWhbFbEeL0PvYm4pcQ== + dependencies: + undici-types "~5.26.4" + +"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" + integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + +"@webassemblyjs/helper-buffer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" + integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== + +"@webassemblyjs/helper-wasm-section@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" + integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" + integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-opt" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/wast-printer" "1.11.6" + +"@webassemblyjs/wasm-gen@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" + integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" + integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + +"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" + integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" + integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^2.1.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" + integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== -"@types/resolve@1.17.1": - version "1.17.1" - resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6" - integrity sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw== - dependencies: - "@types/node" "*" +"@webpack-cli/info@^2.0.1": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" + integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== -"@ungap/promise-all-settled@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz#aa58042711d6e3275dd37dc597e5d31e8c290a44" - integrity sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q== +"@webpack-cli/serve@^2.0.3": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" + integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +abab@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== abort-controller@3.0.0: version "3.0.0" @@ -149,10 +288,38 @@ abort-controller@3.0.0: dependencies: event-target-shim "^5.0.0" -acorn@^8.5.0: - version "8.8.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== +accepts@~1.3.4: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-import-assertions@^1.7.6: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn@^8.7.1, acorn@^8.8.2: + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" ansi-colors@4.1.1: version "4.1.1" @@ -164,13 +331,6 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" @@ -191,21 +351,39 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64id@2.0.0, base64id@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" + integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +body-parser@^1.19.0: + version "1.20.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" + integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -221,7 +399,7 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -braces@~3.0.2: +braces@^3.0.2, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -233,29 +411,44 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== +browserslist@^4.14.5: + version "4.22.2" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.2.tgz#704c4943072bd81ea18997f3bd2180e89c77874b" + integrity sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A== + dependencies: + caniuse-lite "^1.0.30001565" + electron-to-chromium "^1.4.601" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -builtin-modules@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" - integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== + dependencies: + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" camelcase@^6.0.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -chalk@^2.0.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" +caniuse-lite@^1.0.30001565: + version "1.0.30001576" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz#893be772cf8ee6056d6c1e2d07df365b9ec0a5c4" + integrity sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg== chalk@^4.1.0: version "4.1.2" @@ -265,7 +458,7 @@ chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chokidar@3.5.3: +chokidar@3.5.3, chokidar@^3.5.1: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -280,6 +473,11 @@ chokidar@3.5.3: optionalDependencies: fsevents "~2.3.2" +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -289,12 +487,14 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== dependencies: - color-name "1.1.3" + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" color-convert@^2.0.1: version "2.0.1" @@ -303,32 +503,86 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - color-name@~1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +colorette@^2.0.14: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg== - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -debug@4.3.4: +connect@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + dependencies: + debug "2.6.9" + finalhandler "1.1.2" + parseurl "~1.3.3" + utils-merge "1.0.1" + +content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie@~0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +cors@~2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +custom-event@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" + integrity sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg== + +date-format@^4.0.14: + version "4.0.14" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400" + integrity sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg== + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4.3.4, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -340,64 +594,184 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== -decode-uri-component@^0.2.0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9" - integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== +define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -deepmerge@^4.2.2: - version "4.3.1" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" - integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +di@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" + integrity sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA== diff@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== -dukat@0.5.8-rc.4: - version "0.5.8-rc.4" - resolved "https://registry.yarnpkg.com/dukat/-/dukat-0.5.8-rc.4.tgz#90384dcb50b14c26f0e99dae92b2dea44f5fce21" - integrity sha512-ZnMt6DGBjlVgK2uQamXfd7uP/AxH7RqI0BL9GLrrJb2gKdDxvJChWy+M9AQEaL+7/6TmxzJxFOsRiInY9oGWTA== +dom-serialize@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" + integrity sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ== dependencies: - google-protobuf "3.12.2" - typescript "3.9.5" + custom-event "~1.0.0" + ent "~2.2.0" + extend "^3.0.0" + void-elements "^2.0.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.601: + version "1.4.629" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.629.tgz#9cbffe1b08a5627b6a25118360f7fd3965416caf" + integrity sha512-5UUkr3k3CZ/k+9Sw7vaaIMyOzMC0XbPyprKI3n0tbKDqkzTDOjK4izm7DxlkueRMim6ZZQ1ja9F7hoFVplHihA== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +engine.io-parser@~5.2.1: + version "5.2.1" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.1.tgz#9f213c77512ff1a6cc0c7a86108a7ffceb16fcfb" + integrity sha512-9JktcM3u18nU9N2Lz3bWeBgxVgOKpw7yhRaoxQA3FUDZzzw+9WlA6p4G4u0RixNkg14fH7EfEc/RhpurtiROTQ== + +engine.io@~6.5.2: + version "6.5.4" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.5.4.tgz#6822debf324e781add2254e912f8568508850cdc" + integrity sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg== + dependencies: + "@types/cookie" "^0.4.1" + "@types/cors" "^2.8.12" + "@types/node" ">=10.0.0" + accepts "~1.3.4" + base64id "2.0.0" + cookie "~0.4.1" + cors "~2.8.5" + debug "~4.3.1" + engine.io-parser "~5.2.1" + ws "~8.11.0" + +enhanced-resolve@^5.13.0: + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +ent@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" + integrity sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA== + +envinfo@^7.7.3: + version "7.11.0" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.0.tgz#c3793f44284a55ff8c82faf1ffd91bc6478ea01f" + integrity sha512-G9/6xF1FPbIw0TtalAMaVPpiq2aDEuKLXM314jPVAO9r2fo2a4BLqMNkmRS7O/xPPZ+COAhGIz3ETvHEV3eUcg== + +es-module-lexer@^1.2.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.4.1.tgz#41ea21b43908fe6a287ffcbe4300f790555331f5" + integrity sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w== + escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + escape-string-regexp@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" -estree-walker@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-1.0.1.tgz#31bc5d612c96b704106b477e6dd5d8aa138cb700" - integrity sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg== +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" -estree-walker@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" - integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== event-target-shim@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -405,6 +779,19 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" +finalhandler@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + find-up@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" @@ -413,16 +800,43 @@ find-up@5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + flat@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== -format-util@1.0.5, format-util@^1.0.5: +flatted@^3.2.7: + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== + +follow-redirects@^1.0.0: + version "1.15.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== + +format-util@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -433,16 +847,26 @@ fsevents@~2.3.2: resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== +get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -450,6 +874,11 @@ glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + glob@7.2.0: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" @@ -462,7 +891,7 @@ glob@7.2.0: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.1.3, glob@^7.1.6: +glob@^7.1.3, glob@^7.1.7: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -474,33 +903,94 @@ glob@^7.1.3, glob@^7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" -google-protobuf@3.12.2: - version "3.12.2" - resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.12.2.tgz#50ce9f9b6281235724eb243d6a83e969a2176e53" - integrity sha512-4CZhpuRr1d6HjlyrxoXoocoGFnRYgKULgMtikMddA9ztRyYR59Aondv2FioyxWVamRo0rF2XpYawkTCBEQOSkA== +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has@^1.0.3: +has-property-descriptors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== + dependencies: + get-intrinsic "^1.2.2" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.3: version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== dependencies: - function-bind "^1.1.1" + function-bind "^1.1.2" he@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -509,11 +999,16 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2: +inherits@2, inherits@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -521,24 +1016,12 @@ is-binary-path@~2.1.0: dependencies: binary-extensions "^2.0.0" -is-builtin-module@^3.1.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.2.1.tgz#f03271717d8654cfcaf07ab0463faa3571581169" - integrity sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A== - dependencies: - builtin-modules "^3.3.0" - -is-core-module@^2.11.0: - version "2.12.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.0.tgz#36ad62f6f73c8253fd6472517a12483cf03e7ec4" - integrity sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ== +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== dependencies: - has "^1.0.3" - -is-docker@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + hasown "^2.0.0" is-extglob@^2.1.1: version "2.1.1" @@ -557,11 +1040,6 @@ is-glob@^4.0.1, is-glob@~4.0.1: dependencies: is-extglob "^2.1.1" -is-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" - integrity sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g== - is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -572,38 +1050,41 @@ is-plain-obj@^2.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== -is-reference@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.2.1.tgz#8b2dac0b371f4bc994fdeaba9eb542d03002d0b7" - integrity sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ== +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: - "@types/estree" "*" + isobject "^3.0.1" is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" +isbinaryfile@^4.0.8: + version "4.0.10" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" + integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== -jest-worker@^26.2.1: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" - integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== dependencies: "@types/node" "*" merge-stream "^2.0.0" - supports-color "^7.0.0" - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + supports-color "^8.0.0" js-yaml@4.1.0: version "4.1.0" @@ -612,6 +1093,100 @@ js-yaml@4.1.0: dependencies: argparse "^2.0.1" +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + +karma-chrome-launcher@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz#eb9c95024f2d6dfbb3748d3415ac9b381906b9a9" + integrity sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q== + dependencies: + which "^1.2.1" + +karma-mocha@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/karma-mocha/-/karma-mocha-2.0.1.tgz#4b0254a18dfee71bdbe6188d9a6861bf86b0cd7d" + integrity sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ== + dependencies: + minimist "^1.2.3" + +karma-sourcemap-loader@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.4.0.tgz#b01d73f8f688f533bcc8f5d273d43458e13b5488" + integrity sha512-xCRL3/pmhAYF3I6qOrcn0uhbQevitc2DERMPH82FMnG+4WReoGcGFZb1pURf2a5apyrOHRdvD+O6K7NljqKHyA== + dependencies: + graceful-fs "^4.2.10" + +karma-webpack@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.0.tgz#2a2c7b80163fe7ffd1010f83f5507f95ef39f840" + integrity sha512-+54i/cd3/piZuP3dr54+NcFeKOPnys5QeM1IY+0SPASwrtHsliXUiCL50iW+K9WWA7RvamC4macvvQ86l3KtaA== + dependencies: + glob "^7.1.3" + minimatch "^3.0.4" + webpack-merge "^4.1.5" + +karma@6.4.2: + version "6.4.2" + resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.2.tgz#a983f874cee6f35990c4b2dcc3d274653714de8e" + integrity sha512-C6SU/53LB31BEgRg+omznBEMY4SjHU3ricV6zBcAe1EeILKkeScr+fZXtaI5WyDbkVowJxxAI6h73NcFPmXolQ== + dependencies: + "@colors/colors" "1.5.0" + body-parser "^1.19.0" + braces "^3.0.2" + chokidar "^3.5.1" + connect "^3.7.0" + di "^0.0.1" + dom-serialize "^2.2.1" + glob "^7.1.7" + graceful-fs "^4.2.6" + http-proxy "^1.18.1" + isbinaryfile "^4.0.8" + lodash "^4.17.21" + log4js "^6.4.1" + mime "^2.5.2" + minimatch "^3.0.4" + mkdirp "^0.5.5" + qjobs "^1.2.0" + range-parser "^1.2.1" + rimraf "^3.0.2" + socket.io "^4.4.1" + source-map "^0.6.1" + tmp "^0.2.1" + ua-parser-js "^0.7.30" + yargs "^16.1.1" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + locate-path@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" @@ -619,6 +1194,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash@^4.17.15, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + log-symbols@4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" @@ -627,18 +1207,44 @@ log-symbols@4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" -magic-string@^0.25.7: - version "0.25.9" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" - integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== +log4js@^6.4.1: + version "6.9.1" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.9.1.tgz#aba5a3ff4e7872ae34f8b4c533706753709e38b6" + integrity sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g== dependencies: - sourcemap-codec "^1.4.8" + date-format "^4.0.14" + debug "^4.3.4" + flatted "^3.2.7" + rfdc "^1.3.0" + streamroller "^3.1.5" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@^2.5.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + minimatch@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" @@ -653,12 +1259,23 @@ minimatch@^3.0.4, minimatch@^3.1.1: dependencies: brace-expansion "^1.1.7" -mocha@10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.0.0.tgz#205447d8993ec755335c4b13deba3d3a13c4def9" - integrity sha512-0Wl+elVUD43Y0BqPZBzZt8Tnkw9CMUdNYnUsTfOM1vuhJVZL+kiesFYsqwBkEEuEixaiPe5ZQdqDgX2jddhmoA== +minimist@^1.2.3, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mkdirp@^0.5.5: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mocha@10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" + integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== dependencies: - "@ungap/promise-all-settled" "1.1.2" ansi-colors "4.1.1" browser-stdout "1.3.1" chokidar "3.5.3" @@ -681,6 +1298,11 @@ mocha@10.0.0: yargs-parser "20.2.4" yargs-unparser "2.0.0" +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" @@ -696,6 +1318,16 @@ nanoid@3.3.3: resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + node-fetch@2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" @@ -703,11 +1335,40 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +object-assign@^4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -715,6 +1376,13 @@ once@^1.3.0: dependencies: wrappy "1" +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + p-limit@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" @@ -722,6 +1390,13 @@ p-limit@^3.0.2: dependencies: yocto-queue "^0.1.0" +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + p-locate@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" @@ -729,6 +1404,16 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -739,16 +1424,50 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2: +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +qjobs@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" + integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -756,6 +1475,21 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" +range-parser@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -763,57 +1497,75 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.0" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -resolve@^1.17.0, resolve@^1.19.0: - version "1.22.2" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" - integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.20.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== dependencies: - is-core-module "^2.11.0" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -rimraf@^3.0.0: +rfdc@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + +rimraf@^3.0.0, rimraf@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== dependencies: glob "^7.1.3" -rollup-plugin-sourcemaps@^0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/rollup-plugin-sourcemaps/-/rollup-plugin-sourcemaps-0.6.3.tgz#bf93913ffe056e414419607f1d02780d7ece84ed" - integrity sha512-paFu+nT1xvuO1tPFYXGe+XnQvg4Hjqv/eIhG8i5EspfYYPBKL57X7iVbfv55aNVASg3dzWvES9dmWsL2KhfByw== - dependencies: - "@rollup/pluginutils" "^3.0.9" - source-map-resolve "^0.6.0" - -rollup-plugin-terser@^7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz#e8fbba4869981b2dc35ae7e8a502d5c6c04d324d" - integrity sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ== - dependencies: - "@babel/code-frame" "^7.10.4" - jest-worker "^26.2.1" - serialize-javascript "^4.0.0" - terser "^5.0.0" - -rollup@^2.68.0: - version "2.79.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7" - integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== - optionalDependencies: - fsevents "~2.3.2" - safe-buffer@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +schema-utils@^3.1.1, schema-utils@^3.1.2: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + serialize-javascript@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" @@ -821,20 +1573,97 @@ serialize-javascript@6.0.0: dependencies: randombytes "^2.1.0" -serialize-javascript@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" - integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== +serialize-javascript@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" -source-map-resolve@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.6.0.tgz#3d9df87e236b53f16d01e58150fc7711138e5ed2" - integrity sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w== +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +socket.io-adapter@~2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz#5de9477c9182fdc171cd8c8364b9a8894ec75d12" + integrity sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA== + dependencies: + ws "~8.11.0" + +socket.io-parser@~4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" + integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + +socket.io@^4.4.1: + version "4.7.4" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.7.4.tgz#2401a2d7101e4bdc64da80b140d5d8b6a8c7738b" + integrity sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw== + dependencies: + accepts "~1.3.4" + base64id "~2.0.0" + cors "~2.8.5" + debug "~4.3.2" + engine.io "~6.5.2" + socket.io-adapter "~2.5.2" + socket.io-parser "~4.2.4" + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-loader@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-4.0.1.tgz#72f00d05f5d1f90f80974eda781cbd7107c125f2" + integrity sha512-oqXpzDIByKONVY8g1NUPOTQhe0UTU5bWUl32GSkqK2LjJj0HmwTMVKxcUip0RgAYhY1mqgOxjbQM48a0mmeNfA== + dependencies: + abab "^2.0.6" + iconv-lite "^0.6.3" + source-map-js "^1.0.2" source-map-support@0.5.21, source-map-support@~0.5.20: version "0.5.21" @@ -844,15 +1673,29 @@ source-map-support@0.5.21, source-map-support@~0.5.20: buffer-from "^1.0.0" source-map "^0.6.0" -source-map@^0.6.0: +source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -sourcemap-codec@^1.4.8: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +streamroller@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.5.tgz#1263182329a45def1ffaef58d31b15d13d2ee7ff" + integrity sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + fs-extra "^8.1.0" string-width@^4.1.0, string-width@^4.2.0: version "4.2.3" @@ -875,21 +1718,14 @@ strip-json-comments@3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -supports-color@8.1.1: +supports-color@8.1.1, supports-color@^8.0.0: version "8.1.1" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.0.0, supports-color@^7.1.0: +supports-color@^7.1.0: version "7.2.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== @@ -901,16 +1737,39 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -terser@^5.0.0: - version "5.17.3" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.17.3.tgz#7f908f16b3cdf3f6c0f8338e6c1c674837f90d25" - integrity sha512-AudpAZKmZHkG9jueayypz4duuCFJMMNGRMwaPvQKWfxKedh8Z2x3OCoDqIIi1xx5+iwx1u6Au8XQcc9Lke65Yg== - dependencies: - "@jridgewell/source-map" "^0.3.2" - acorn "^8.5.0" +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.3.7: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.20" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.26.0" + +terser@^5.26.0: + version "5.26.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.26.0.tgz#ee9f05d929f4189a9c28a0feb889d96d50126fe1" + integrity sha512-dytTGoE2oHgbNV9nTzgBEPaqAWvcJNl66VZ0BkJqlvp71IjO8CxdBx/ykCNb47cLnCmCvRZ6ZR0tLkqvZCdVBQ== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" commander "^2.20.0" source-map-support "~0.5.20" +tmp@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -918,36 +1777,162 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== -tslib@^2.3.1: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" - integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" -typescript@3.9.5: - version "3.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.5.tgz#586f0dba300cde8be52dd1ac4f7e1009c1b13f36" - integrity sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ== +typescript@5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" + integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== -typescript@4.7.4: - version "4.7.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" - integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== +ua-parser-js@^0.7.30: + version "0.7.37" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.37.tgz#e464e66dac2d33a7a1251d7d7a99d6157ec27832" + integrity sha512-xV8kqRKM+jhMvcHWUKthV9fNebIzrNy//2O9ZwWcfiBFR5f25XVZPLlEajk/sf3Ra15V92isyQqnIEXRDaZWEA== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" -typescript@^3.7.2: - version "3.9.10" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" - integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +vary@^1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +void-elements@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung== + +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== +webpack-cli@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.0.tgz#abc4b1f44b50250f2632d8b8b536cfe2f6257891" + integrity sha512-a7KRJnCxejFoDpYTOwzm5o21ZXMaNqtRlvS183XzGDUPRdVEzJNImcQokqYZ8BNTnk9DkKiuWxw75+DCCoZ26w== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^2.1.0" + "@webpack-cli/info" "^2.0.1" + "@webpack-cli/serve" "^2.0.3" + colorette "^2.0.14" + commander "^10.0.1" + cross-spawn "^7.0.3" + envinfo "^7.7.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^3.1.1" + rechoir "^0.8.0" + webpack-merge "^5.7.3" + +webpack-merge@^4.1.5: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + +webpack-merge@^5.7.3: + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.0" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@5.82.0: + version "5.82.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.82.0.tgz#3c0d074dec79401db026b4ba0fb23d6333f88e7d" + integrity sha512-iGNA2fHhnDcV1bONdUu554eZx+XeldsaeQ8T67H6KKHl2nUSwX8Zm7cmzOA46ox/X1ARxf7Bjv8wQ/HsB5fxBg== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.0" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" + acorn "^8.7.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.13.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.2" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.7" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -956,6 +1941,25 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" +which@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + workerpool@6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" @@ -980,6 +1984,11 @@ ws@8.5.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== +ws@~8.11.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" + integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" @@ -1005,7 +2014,7 @@ yargs-unparser@2.0.0: flat "^5.0.2" is-plain-obj "^2.1.0" -yargs@16.2.0: +yargs@16.2.0, yargs@^16.1.1: version "16.2.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== diff --git a/simbot-component-qq-guild-api/build.gradle.kts b/simbot-component-qq-guild-api/build.gradle.kts index 0cd44063..491b1a99 100644 --- a/simbot-component-qq-guild-api/build.gradle.kts +++ b/simbot-component-qq-guild-api/build.gradle.kts @@ -18,6 +18,7 @@ import love.forte.gradle.common.kotlin.multiplatform.applyTier1 import love.forte.gradle.common.kotlin.multiplatform.applyTier2 import love.forte.gradle.common.kotlin.multiplatform.applyTier3 +import love.forte.plugin.suspendtrans.gradle.withKotlinTargets plugins { kotlin("multiplatform") @@ -29,13 +30,15 @@ configJavaCompileWithModule("simbot.component.qqguild.api") //apply(plugin = "qq-guild-dokka-partial-configure") apply(plugin = "qq-guild-multiplatform-maven-publish") +//configJsTestTasks() + kotlin { explicitApi() applyDefaultHierarchyTemplate() sourceSets.configureEach { languageSettings { - optIn("love.forte.simbot.qguild.InternalApi") + optIn("love.forte.simbot.qguild.QGInternalApi") } } @@ -49,6 +52,13 @@ kotlin { applyTier2() applyTier3(supportKtorClient = true) + withKotlinTargets { target -> + targets.findByName(target.name)?.compilations?.all { + // 'expect'/'actual' classes (including interfaces, objects, annotations, enums, and 'actual' typealiases) are in Beta. You can use -Xexpect-actual-classes flag to suppress this warning. Also see: https://youtrack.jetbrains.com/issue/KT-61573 + kotlinOptions.freeCompilerArgs += "-Xexpect-actual-classes" + } + } + sourceSets { commonMain.dependencies { api(libs.kotlinx.coroutines.core) @@ -85,6 +95,10 @@ kotlin { api(libs.ktor.client.js) } + jsTest.dependencies { + api(libs.ktor.client.js) + } + mingwTest.dependencies { implementation(libs.ktor.client.winhttp) } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuild.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuild.kt index 7ae37ed3..9cf7e309 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuild.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuild.kt @@ -17,10 +17,10 @@ package love.forte.simbot.qguild +//import kotlin.js.ExperimentalJsExport +//import kotlin.js.JsExport import io.ktor.http.* import kotlinx.serialization.json.Json -import kotlin.js.ExperimentalJsExport -import kotlin.js.JsExport import kotlin.jvm.JvmField /** @@ -30,9 +30,8 @@ import kotlin.jvm.JvmField * * */ -@OptIn(ExperimentalJsExport::class) -@JsExport -@Suppress("NON_EXPORTABLE_TYPE") +//@OptIn(ExperimentalJsExport::class) +//@JsExport public object QQGuild { /** * 正式环境接口域名 `https://api.sgroup.qq.com` diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuildApiException.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuildApiException.kt index ac6122af..930a2687 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuildApiException.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuildApiException.kt @@ -62,7 +62,6 @@ public open class QQGuildApiException : RuntimeException { /** * @suppress */ -@OptIn(QGInternalApi::class) @Deprecated("use 'addStackTrace'", ReplaceWith("addStackTrace()")) public fun QQGuildApiException.copyCurrent(): QQGuildApiException = addStackTrace() @@ -97,14 +96,12 @@ public inline val QQGuildApiException.isUnauthorized: Boolean get() = value == 4 /** * 如果 [QQGuildApiException.isNotFound] 为 `true`, 得到null,否则抛出此异常 */ -@OptIn(QGInternalApi::class) public inline fun QQGuildApiException.ifNotFoundThenNull(throwCopy: Boolean = true): T? = if (isNotFound) null else if (throwCopy) throw this.addStackTrace() else throw this /** * 如果 [QQGuildApiException.isNotFound] 为 `true`, 得到null,否则抛出此异常 */ -@OptIn(QGInternalApi::class) public inline fun QQGuildApiException.ifNotFoundThen(throwCopy: Boolean = true, value: () -> T): T = if (isNotFound) value() else if (throwCopy) throw this.addStackTrace() else throw this @@ -112,7 +109,6 @@ public inline fun QQGuildApiException.ifNotFoundThen(throwCopy: Bool /** * 如果 [QQGuildApiException.isNotFound] 为 `true`, 得到null,否则抛出此异常 */ -@OptIn(QGInternalApi::class) public inline fun QQGuildApiException.ifNotFoundThenNoSuch(throwCopy: Boolean = true, value: () -> String): Nothing = if (isNotFound) { throw NoSuchElementException(value()).apply { initCause0(this) } diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt index 96c7675c..771a1658 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt @@ -36,7 +36,6 @@ import kotlinx.serialization.json.Json import love.forte.simbot.common.serialization.guessSerializer import love.forte.simbot.logger.isDebugEnabled import love.forte.simbot.qguild.ErrInfo -import love.forte.simbot.qguild.QGInternalApi import love.forte.simbot.qguild.QQGuild import love.forte.simbot.qguild.QQGuildApiException import kotlin.contracts.ExperimentalContracts @@ -204,7 +203,6 @@ internal fun QQGuildApi.decodeResponse( return decoder.decodeFromString(resultDeserializationStrategy, remainingText) } -@OptIn(QGInternalApi::class) internal fun checkStatus( remainingText: String, decoder: StringFormat, diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.kt index 47476280..073887af 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.kt @@ -241,7 +241,6 @@ public class MessageSendApi private constructor( * 各属性参考 [Body] 内同名属性。 * */ - @OptIn(QGInternalApi::class) @Suppress("MemberVisibilityCanBePrivate") public class Builder : BaseMessageSendBodyBuilder() { public var content: String? = null @@ -352,7 +351,6 @@ public inline fun MessageSendApi.Factory.create(channelId: String, builder: Buil * 提供一些需要由不同平台额外实现的基类。 * 主要针对 `fileImage`。 */ -@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING") @QGInternalApi public expect abstract class BaseMessageSendBodyBuilder() { public open var fileImage: Any? @@ -395,7 +393,6 @@ private const val FILE_IMAGE_PROPERTY_NAME = "file_image" * - [resolveOther] support other platform type. */ -@OptIn(QGInternalApi::class) private fun FormBuilder.appendFileImage(fileImage: Any?) { when (fileImage) { is ByteArray -> { diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/model/Emoji.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/model/Emoji.kt index 24c96831..d54628ae 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/model/Emoji.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/model/Emoji.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,14 +15,11 @@ * If not, see . */ -@file:OptIn(ExperimentalJsExport::class) -@file:JsExport +//@file:OptIn(ExperimentalJsExport::class) +//@file:JsExport package love.forte.simbot.qguild.model -import kotlin.js.ExperimentalJsExport -import kotlin.js.JsExport - /** * diff --git a/simbot-component-qq-guild-api/src/jvmMain/java/module-info.java b/simbot-component-qq-guild-api/src/jvmMain/java/module-info.java index e51323f1..6029eb69 100644 --- a/simbot-component-qq-guild-api/src/jvmMain/java/module-info.java +++ b/simbot-component-qq-guild-api/src/jvmMain/java/module-info.java @@ -1,5 +1,6 @@ module simbot.component.qqguild.api { requires kotlin.stdlib; + requires transitive kotlinx.coroutines.core; requires transitive kotlinx.serialization.core; requires transitive kotlinx.serialization.json; @@ -9,6 +10,7 @@ requires transitive simbot.common.suspendrunner; requires transitive simbot.common.core; // ktor + requires io.ktor.http; requires io.ktor.client.core; requires io.ktor.client.content.negotiation; diff --git a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt index f06532f0..8bb47a61 100644 --- a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt +++ b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt @@ -63,7 +63,6 @@ public fun QQGuildApi.requestBlocking( client: HttpClient, token: String, server: Url = QQGuild.URL, - decoder: Json = QQGuild.DefaultJson, ): HttpResponse = runInNoScopeBlocking { request(client, token, server) } diff --git a/simbot-component-qq-guild-core-common/build.gradle.kts b/simbot-component-qq-guild-core-common/build.gradle.kts index 7595551e..8cc015d4 100644 --- a/simbot-component-qq-guild-core-common/build.gradle.kts +++ b/simbot-component-qq-guild-core-common/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -27,7 +27,7 @@ kotlin { sourceSets.configureEach { languageSettings { optIn("love.forte.simbot.InternalSimbotApi") - optIn("love.forte.simbot.qguild.InternalApi") + optIn("love.forte.simbot.qguild.QGInternalApi") } } } diff --git a/simbot-component-qq-guild-core/build.gradle.kts b/simbot-component-qq-guild-core/build.gradle.kts index 2331817d..a34d1c75 100644 --- a/simbot-component-qq-guild-core/build.gradle.kts +++ b/simbot-component-qq-guild-core/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -27,7 +27,7 @@ kotlin { sourceSets.configureEach { languageSettings { optIn("love.forte.simbot.InternalSimbotApi") - optIn("love.forte.simbot.qguild.InternalApi") + optIn("love.forte.simbot.qguild.QGInternalApi") } } } diff --git a/simbot-component-qq-guild-stdlib/build.gradle.kts b/simbot-component-qq-guild-stdlib/build.gradle.kts index e26e3d88..3c54a073 100644 --- a/simbot-component-qq-guild-stdlib/build.gradle.kts +++ b/simbot-component-qq-guild-stdlib/build.gradle.kts @@ -29,13 +29,15 @@ plugins { configJavaCompileWithModule("simbot.component.qqguild.stdlib") apply(plugin = "qq-guild-multiplatform-maven-publish") +configJsTestTasks() + kotlin { explicitApi() applyDefaultHierarchyTemplate() sourceSets.configureEach { languageSettings { - optIn("love.forte.simbot.qguild.InternalApi") + optIn("love.forte.simbot.qguild.QGInternalApi") } } @@ -87,4 +89,3 @@ kotlin { } } } - diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/Bot.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/Bot.kt similarity index 99% rename from simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/Bot.kt rename to simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/Bot.kt index e566f322..e8659b05 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/Bot.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/Bot.kt @@ -15,7 +15,7 @@ * If not, see . */ -package love.forte.simbot.qguild +package love.forte.simbot.qguild.stdlib import io.ktor.client.* import io.ktor.http.* diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotConfiguration.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/BotConfiguration.kt similarity index 97% rename from simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotConfiguration.kt rename to simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/BotConfiguration.kt index 56e206f0..c41dc0ae 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotConfiguration.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/BotConfiguration.kt @@ -15,7 +15,7 @@ * If not, see . */ -package love.forte.simbot.qguild +package love.forte.simbot.qguild.stdlib import io.ktor.client.* import io.ktor.client.engine.* @@ -76,7 +76,7 @@ public interface BotConfiguration { public val clientProperties: Map /** - * 请求的服务器地址。默认为 [QQGuild.URL]. 即正式地址。 + * 请求的服务器地址。默认为 [love.forte.simbot.qguild.QQGuild.URL]. 即正式地址。 */ public val serverUrl: Url diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotFactory.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/BotFactory.kt similarity index 95% rename from simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotFactory.kt rename to simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/BotFactory.kt index 9f81bc21..c5f4c869 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotFactory.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/BotFactory.kt @@ -15,11 +15,11 @@ * If not, see . */ -package love.forte.simbot.qguild +package love.forte.simbot.qguild.stdlib import love.forte.simbot.common.function.ConfigurerFunction import love.forte.simbot.common.function.invokeBy -import love.forte.simbot.qguild.internal.BotImpl +import love.forte.simbot.qguild.stdlib.internal.BotImpl import kotlin.jvm.JvmOverloads import kotlin.jvm.JvmStatic diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotProcessor.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/BotProcessor.kt similarity index 96% rename from simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotProcessor.kt rename to simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/BotProcessor.kt index e18ef7b7..ff4e0fba 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotProcessor.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/BotProcessor.kt @@ -15,7 +15,7 @@ * If not, see . */ -package love.forte.simbot.qguild +package love.forte.simbot.qguild.stdlib import love.forte.simbot.qguild.event.Signal diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotRequests.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.kt similarity index 96% rename from simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotRequests.kt rename to simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.kt index e0f3fd5c..604cae09 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/BotRequests.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.kt @@ -18,14 +18,14 @@ @file:JvmName("BotRequests") @file:JvmMultifileClass -package love.forte.simbot.qguild +package love.forte.simbot.qguild.stdlib import io.ktor.client.statement.* import love.forte.simbot.qguild.api.QQGuildApi import love.forte.simbot.qguild.api.request import love.forte.simbot.qguild.api.requestData import love.forte.simbot.qguild.api.requestText -import love.forte.simbot.qguild.internal.BotImpl +import love.forte.simbot.qguild.stdlib.internal.BotImpl import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName import kotlin.jvm.JvmSynthetic diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/ConfigurableBotConfiguration.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/ConfigurableBotConfiguration.kt similarity index 92% rename from simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/ConfigurableBotConfiguration.kt rename to simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/ConfigurableBotConfiguration.kt index 3f480f6e..2efc448b 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/ConfigurableBotConfiguration.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/ConfigurableBotConfiguration.kt @@ -15,13 +15,14 @@ * If not, see . */ -package love.forte.simbot.qguild +package love.forte.simbot.qguild.stdlib import io.ktor.client.* import io.ktor.client.engine.* import io.ktor.client.plugins.* import io.ktor.http.* import kotlinx.serialization.json.Json +import love.forte.simbot.qguild.QQGuild import love.forte.simbot.qguild.event.EventIntents import love.forte.simbot.qguild.event.Intents import love.forte.simbot.qguild.event.Shard @@ -169,26 +170,6 @@ public class ConfigurableBotConfiguration : BotConfiguration { */ override var apiDecoder: Json = QQGuild.DefaultJson -// /** -// * BOT内接收到事件后推送到的缓冲区的容量。 -// * -// * 缓冲区中堆积的事件如果已满则后续推送的事件会挂起等待缓冲区内元素的消费。 -// * -// * 默认为 [`64`][DEFAULT_EVENT_BUFFER_CAPACITY]。 -// * -// * @see DEFAULT_EVENT_BUFFER_CAPACITY -// * -// */ -// override var eventBufferCapacity: Int = DEFAULT_EVENT_BUFFER_CAPACITY - - public companion object { - private val defaultJson = Json { - isLenient = true - ignoreUnknownKeys = true - } - } - - internal fun release(): BotConfiguration = BotConfigurationImpl( coroutineContext = coroutineContext, shard = shard, @@ -205,6 +186,8 @@ public class ConfigurableBotConfiguration : BotConfiguration { wsClientEngineFactory = wsClientEngineFactory, apiDecoder = apiDecoder, ) + + public companion object } private class BotConfigurationImpl( diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/DisposableHandle.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/DisposableHandle.kt similarity index 85% rename from simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/DisposableHandle.kt rename to simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/DisposableHandle.kt index 387f9c42..4bc022ef 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/DisposableHandle.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/DisposableHandle.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,10 +15,7 @@ * If not, see . */ -package love.forte.simbot.qguild - -import kotlin.js.ExperimentalJsExport -import kotlin.js.JsExport +package love.forte.simbot.qguild.stdlib /** @@ -26,8 +23,8 @@ import kotlin.js.JsExport * * @author ForteScarlet */ -@OptIn(ExperimentalJsExport::class) -@JsExport +//@OptIn(ExperimentalJsExport::class) +//@JsExport public interface DisposableHandle { /** diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/EventProcessor.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.kt similarity index 97% rename from simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/EventProcessor.kt rename to simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.kt index 312c05d5..f62da52c 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/EventProcessor.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.kt @@ -18,7 +18,7 @@ @file:JvmName("EventProcessors") @file:JvmMultifileClass -package love.forte.simbot.qguild +package love.forte.simbot.qguild.stdlib import love.forte.simbot.qguild.event.Signal import kotlin.jvm.JvmMultifileClass diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/exceptions.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/exceptions.kt similarity index 96% rename from simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/exceptions.kt rename to simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/exceptions.kt index 8cce7629..1c875620 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/exceptions.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/exceptions.kt @@ -15,7 +15,7 @@ * If not, see . */ -package love.forte.simbot.qguild +package love.forte.simbot.qguild.stdlib /** * 当尝试使用已经被关闭的 [Bot] 进行某些操作时(例如尝试启动它) diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/BotImpl.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/internal/BotImpl.kt similarity index 97% rename from simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/BotImpl.kt rename to simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/internal/BotImpl.kt index 00d2f23a..3f3740cc 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/BotImpl.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/internal/BotImpl.kt @@ -15,7 +15,7 @@ * If not, see . */ -package love.forte.simbot.qguild.internal +package love.forte.simbot.qguild.stdlib.internal import io.ktor.client.* import io.ktor.client.plugins.* @@ -37,16 +37,15 @@ import love.forte.simbot.common.weak.WeakRef import love.forte.simbot.common.weak.weakRef import love.forte.simbot.logger.Logger import love.forte.simbot.logger.LoggerFactory -import love.forte.simbot.qguild.* -import love.forte.simbot.qguild.DisposableHandle import love.forte.simbot.qguild.api.GatewayApis import love.forte.simbot.qguild.api.GatewayInfo import love.forte.simbot.qguild.api.user.GetBotInfoApi -import love.forte.simbot.qguild.event.Intents import love.forte.simbot.qguild.event.Ready import love.forte.simbot.qguild.event.Shard import love.forte.simbot.qguild.event.Signal import love.forte.simbot.qguild.model.User +import love.forte.simbot.qguild.stdlib.* +import love.forte.simbot.qguild.stdlib.DisposableHandle import kotlin.concurrent.Volatile import kotlin.coroutines.CoroutineContext @@ -156,7 +155,6 @@ internal class BotImpl( internal val botToken = "Bot ${ticket.appId}.${ticket.token}" override val shard: Shard = configuration.shard override val apiDecoder: Json = configuration.apiDecoder - internal val intents: Intents = configuration.intents internal val processorQueue: ConcurrentQueue = createConcurrentQueue() @@ -346,5 +344,3 @@ internal data class SessionInfo( val readyData: Ready.Data, ) -internal data class EventData(val raw: String, val event: Signal.Dispatch) - diff --git a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/BotStates.kt b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/internal/BotStates.kt similarity index 98% rename from simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/BotStates.kt rename to simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/internal/BotStates.kt index 6f4d7caf..c01edebd 100644 --- a/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/internal/BotStates.kt +++ b/simbot-component-qq-guild-stdlib/src/commonMain/kotlin/love/forte/simbot/qguild/stdlib/internal/BotStates.kt @@ -15,7 +15,7 @@ * If not, see . */ -package love.forte.simbot.qguild.internal +package love.forte.simbot.qguild.stdlib.internal import io.ktor.client.* import io.ktor.client.plugins.websocket.* @@ -33,14 +33,13 @@ import love.forte.simbot.common.collection.ExperimentalSimbotCollectionApi import love.forte.simbot.logger.Logger import love.forte.simbot.logger.isDebugEnabled import love.forte.simbot.logger.isTraceEnabled -import love.forte.simbot.qguild.QGInternalApi import love.forte.simbot.qguild.api.GatewayApis import love.forte.simbot.qguild.api.GatewayInfo -import love.forte.simbot.qguild.doInvoke import love.forte.simbot.qguild.err import love.forte.simbot.qguild.event.* -import love.forte.simbot.qguild.internal.BotImpl.ClientImpl -import love.forte.simbot.qguild.requestDataBy +import love.forte.simbot.qguild.stdlib.doInvoke +import love.forte.simbot.qguild.stdlib.internal.BotImpl.ClientImpl +import love.forte.simbot.qguild.stdlib.requestDataBy import kotlin.math.max import kotlin.random.Random import kotlin.time.Duration.Companion.milliseconds @@ -268,7 +267,6 @@ internal class ReceiveEvent( override val bot: BotImpl, private val client: ClientImpl, ) : State() { - @OptIn(QGInternalApi::class) override suspend fun invoke(): State? { val session = client.wsSession val seq = client.sessionInfo.seq diff --git a/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/BotRequests.js.kt b/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.js.kt similarity index 97% rename from simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/BotRequests.js.kt rename to simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.js.kt index 44ba4efe..ac5212b8 100644 --- a/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/BotRequests.js.kt +++ b/simbot-component-qq-guild-stdlib/src/jsMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.js.kt @@ -15,7 +15,7 @@ * If not, see . */ -package love.forte.simbot.qguild +package love.forte.simbot.qguild.stdlib import io.ktor.client.statement.* import kotlinx.coroutines.promise diff --git a/simbot-component-qq-guild-stdlib/src/jvmMain/java/module-info.java b/simbot-component-qq-guild-stdlib/src/jvmMain/java/module-info.java index 7f16a4ee..0fc19475 100644 --- a/simbot-component-qq-guild-stdlib/src/jvmMain/java/module-info.java +++ b/simbot-component-qq-guild-stdlib/src/jvmMain/java/module-info.java @@ -9,6 +9,9 @@ requires io.ktor.client.content.negotiation; requires io.ktor.serialization.kotlinx.json; requires io.ktor.client.websockets; + requires io.ktor.client.core; - exports love.forte.simbot.qguild; + requires static org.jetbrains.annotations; + + exports love.forte.simbot.qguild.stdlib; } diff --git a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/BotRequests.jvm.kt b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.jvm.kt similarity index 98% rename from simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/BotRequests.jvm.kt rename to simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.jvm.kt index 65df161a..e1160c80 100644 --- a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/BotRequests.jvm.kt +++ b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.jvm.kt @@ -18,7 +18,7 @@ @file:JvmName("BotRequests") @file:JvmMultifileClass -package love.forte.simbot.qguild +package love.forte.simbot.qguild.stdlib import io.ktor.client.statement.* import kotlinx.coroutines.CoroutineScope diff --git a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/EventProcessor.jvm.kt b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.jvm.kt similarity index 98% rename from simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/EventProcessor.jvm.kt rename to simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.jvm.kt index 62f2c045..a232627f 100644 --- a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/EventProcessor.jvm.kt +++ b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.jvm.kt @@ -18,7 +18,7 @@ @file:JvmName("EventProcessors") @file:JvmMultifileClass -package love.forte.simbot.qguild +package love.forte.simbot.qguild.stdlib import kotlinx.coroutines.Dispatchers From 6ff5650e73cc2488cdbf65e11873ab93781562bd Mon Sep 17 00:00:00 2001 From: ForteScarlet <1149159218@qq.com> Date: Sat, 13 Jan 2024 02:55:44 +0800 Subject: [PATCH 09/71] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20publish-v4-snapshot.?= =?UTF-8?q?yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/publish-v4-snapshot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish-v4-snapshot.yml b/.github/workflows/publish-v4-snapshot.yml index 7c03c73e..14e892d7 100644 --- a/.github/workflows/publish-v4-snapshot.yml +++ b/.github/workflows/publish-v4-snapshot.yml @@ -2,7 +2,7 @@ name: Publish V4 Snapshot on: push: branches: - - v4-dev/ver/** + - v4-dev/* - v4-dev/main paths: From 1127baf275fba5e12d0a6c68e76351ac9a30df57 Mon Sep 17 00:00:00 2001 From: ForteScarlet <1149159218@qq.com> Date: Sat, 13 Jan 2024 02:56:57 +0800 Subject: [PATCH 10/71] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20ArkBuilderTest.kt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/commonTest/kotlin/ArkBuilderTest.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/simbot-component-qq-guild-api/src/commonTest/kotlin/ArkBuilderTest.kt b/simbot-component-qq-guild-api/src/commonTest/kotlin/ArkBuilderTest.kt index 0a36f6b3..9d545f86 100644 --- a/simbot-component-qq-guild-api/src/commonTest/kotlin/ArkBuilderTest.kt +++ b/simbot-component-qq-guild-api/src/commonTest/kotlin/ArkBuilderTest.kt @@ -26,7 +26,6 @@ class ArkBuilderTest { @Test fun test() { - val ark = buildArk("0") { // 增加kvs kvs { From 12bddc188fde5c548af7e4691d1a10c67331d4c3 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Sat, 13 Jan 2024 15:23:44 +0800 Subject: [PATCH 11/71] Module: stdlib --- .github/workflows/publish-v4-snapshot.yml | 13 +++- .../forte/simbot/qguild/api/ApiRequests.kt | 2 +- .../simbot/qguild/api/ApiRequests.jvm.kt | 77 ++++++++++++++++++- .../simbot/qguild/stdlib/BotRequests.jvm.kt | 6 +- 4 files changed, 89 insertions(+), 9 deletions(-) diff --git a/.github/workflows/publish-v4-snapshot.yml b/.github/workflows/publish-v4-snapshot.yml index 14e892d7..b7c29b9c 100644 --- a/.github/workflows/publish-v4-snapshot.yml +++ b/.github/workflows/publish-v4-snapshot.yml @@ -3,13 +3,18 @@ on: push: branches: - v4-dev/* + - v4-dev/v4-upgrade - v4-dev/main paths: - - '**src/main/kotlin/**.kt' - - '**src/main/java/**.java' - - 'buildSrc/**' - - '**gradle.kts' + - 'buildSrc' + - '**src/**/kotlin/**.kt' + - '**src/**/java/**.java' + - '**/src/**/kotlin/**.kt' + - '**/src/**/java/**.java' + - '**/build.gradle.kts' + - 'build.gradle.kts' + - 'settings.gradle.kts' - 'gradle.properties' # 手动触发工作流 diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt index 771a1658..885b0b1c 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt @@ -216,7 +216,7 @@ internal fun checkStatus( throw QQGuildApiException(info, status.value, status.description) } - // 202 消息审核异常 + // 202 消息审核 if (status == HttpStatusCode.Accepted) { val info = decoder.decodeFromString(ErrInfo.serializer(), remainingText) // maybe audited diff --git a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt index 8bb47a61..0f83d5c6 100644 --- a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt +++ b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt @@ -23,11 +23,14 @@ package love.forte.simbot.qguild.api import io.ktor.client.* import io.ktor.client.statement.* import io.ktor.http.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.future.future import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonBuilder import love.forte.simbot.qguild.QGApi4J import love.forte.simbot.qguild.QQGuild import love.forte.simbot.suspendrunner.runInNoScopeBlocking +import java.util.concurrent.CompletableFuture import java.util.function.Consumer @@ -56,13 +59,85 @@ public fun newJson( /** * [QQGuildApi.request] for Java + * */ @QGApi4J @JvmOverloads -public fun QQGuildApi.requestBlocking( +public fun QQGuildApi<*>.requestBlocking( client: HttpClient, token: String, server: Url = QQGuild.URL, ): HttpResponse = runInNoScopeBlocking { request(client, token, server) } + +/** + * [QQGuildApi.requestText] for Java + * + */ +@QGApi4J +@JvmOverloads +public fun QQGuildApi<*>.requestTextBlocking( + client: HttpClient, + token: String, + server: Url = QQGuild.URL, +): String = runInNoScopeBlocking { + requestText(client, token, server) +} + +/** + * [QQGuildApi.requestData] for Java + */ +@QGApi4J +@JvmOverloads +public fun QQGuildApi.requestDataBlocking( + client: HttpClient, + token: String, + server: Url = QQGuild.URL, +): R = runInNoScopeBlocking { + requestData(client, token, server) +} + +/** + * [QQGuildApi.request] for Java + * + */ +@QGApi4J +@JvmOverloads +public fun QQGuildApi<*>.requestAsync( + client: HttpClient, + token: String, + server: Url = QQGuild.URL, + scope: CoroutineScope? = null, +): CompletableFuture = (scope ?: client).future { + request(client, token, server) +} + +/** + * [QQGuildApi.requestText] for Java + * + */ +@QGApi4J +@JvmOverloads +public fun QQGuildApi<*>.requestTextAsync( + client: HttpClient, + token: String, + server: Url = QQGuild.URL, + scope: CoroutineScope? = null, +): CompletableFuture = (scope ?: client).future { + requestText(client, token, server) +} + +/** + * [QQGuildApi.requestData] for Java + */ +@QGApi4J +@JvmOverloads +public fun QQGuildApi.requestDataAsync( + client: HttpClient, + token: String, + server: Url = QQGuild.URL, + scope: CoroutineScope? = null, +): CompletableFuture = (scope ?: client).future { + requestData(client, token, server) +} diff --git a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.jvm.kt b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.jvm.kt index e1160c80..b9576e96 100644 --- a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.jvm.kt +++ b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.jvm.kt @@ -73,7 +73,7 @@ public fun Bot.requestDataAsync( * 直接通过bot进行请求。 */ @Api4J -public fun Bot.requestByBlocking(api: QQGuildApi<*>): HttpResponse = runInNoScopeBlocking { +public fun Bot.requestBlocking(api: QQGuildApi<*>): HttpResponse = runInNoScopeBlocking { request(api) } @@ -81,7 +81,7 @@ public fun Bot.requestByBlocking(api: QQGuildApi<*>): HttpResponse = runInNoScop * 直接通过bot进行请求。 */ @Api4J -public fun Bot.requestTextByBlocking(api: QQGuildApi<*>): String = runInNoScopeBlocking { +public fun Bot.requestTextBlocking(api: QQGuildApi<*>): String = runInNoScopeBlocking { requestText(api) } @@ -91,6 +91,6 @@ public fun Bot.requestTextByBlocking(api: QQGuildApi<*>): String = runInNoScopeB * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 */ @Api4J -public fun Bot.requestDataByBlocking(api: QQGuildApi): R = runInNoScopeBlocking { +public fun Bot.requestDataBlocking(api: QQGuildApi): R = runInNoScopeBlocking { requestData(api) } From 6e82edd4b489ffcac98137c760b6963b494278d7 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Sun, 14 Jan 2024 18:41:43 +0800 Subject: [PATCH 12/71] Module: core --- build.gradle.kts | 29 +- gradle.properties | 4 +- gradle/libs.versions.toml | 2 +- kotlin-js-store/yarn.lock | 5 + settings.gradle.kts | 2 +- .../qguild/api/message/MessageSendApi.kt | 63 ++- .../forte/simbot/qguild/event/EventIntents.kt | 53 ++- .../simbot/qguild/message/MessageBuilders.kt | 11 +- .../qguild/api/message/MessageSendApi.js.kt | 21 + .../qguild/api/message/resolveOther.js.kt | 10 - .../src/jvmMain/java/module-info.java | 4 + .../simbot/qguild/api/ApiRequests.jvm.kt | 55 +++ .../qguild/api/message/resolveOther.jvm.kt | 55 +-- .../api/message/MessageSendApi.native.kt | 21 + .../qguild/api/message/resolveOther.native.kt | 11 +- .../build.gradle.kts | 79 +++- .../component/qguild/QGObjectiveContainer.kt | 2 +- .../qguild/QQGuildBotManagerUsage.kt | 82 ++++ .../component/qguild/QQGuildComponent.kt | 98 ++-- .../component/qguild/QQGuildComponentUsage.kt | 186 ++++++++ .../component/qguild/QQGuildInitException.kt | 18 +- .../simbot/component/qguild/annotations.kt | 33 ++ .../simbot/component/qguild/bot}/QGBot.kt | 222 ++-------- .../component/qguild/bot/QQGuildBotManager.kt | 298 +++++++++++++ .../qguild/bot}/config/CacheStrategyConfig.kt | 8 +- .../qguild/bot}/config/IntentsConfig.kt | 14 +- .../config/QGBotComponentConfiguration.kt | 12 +- .../bot}/config/QGBotFileConfiguration.kt | 43 +- .../qguild/bot}/config/ShardConfig.kt | 15 +- .../qguild/bot}/config/TicketConfiguration.kt | 22 +- .../component/qguild/channel/QGCategory.kt | 86 ++++ .../component/qguild/channel/QGChannel.kt | 73 +++ .../qguild/channel}/QGForumChannel.kt | 43 +- .../component/qguild/channel/QGTextChannel.kt | 100 +++++ .../component/qguild/event/QGChannelEvents.kt | 122 +++++ .../simbot/component/qguild/event/QGEvent.kt | 62 +++ .../component/qguild/event/QGForumEvents.kt | 112 +---- .../component/qguild/event/QGGuildEvents.kt | 118 +++++ .../qguild/event/QGGuildMemberEvents.kt | 150 +++++++ .../qguild/event/QGInternalBotEvent.kt | 55 +++ .../component/qguild/event/QGMessageEvents.kt | 75 ++-- .../qguild/event/QGOpenForumEvents.kt | 111 +---- .../qguild/event/QGUnsupportedEvent.kt | 29 +- .../qguild/forum/QGForumInfoContainer.kt | 9 +- .../simbot/component/qguild/forum/QGPost.kt | 68 ++- .../simbot/component/qguild/forum/QGReply.kt | 81 ++-- .../simbot/component/qguild/forum/QGThread.kt | 72 ++- .../component/qguild/forum/QGThreadCreator.kt | 6 +- .../simbot/component/qguild/guild}/QGGuild.kt | 174 +++----- .../component/qguild/guild/QGGuildRelation.kt | 91 ++++ .../component/qguild/guild}/QGMember.kt | 69 ++- .../qguild/internal/bot/QGBotImpl.kt | 417 ++++++++++++++++++ .../internal/bot/QQGuildBotManagerImpl.kt | 169 +++++++ .../internal/channel/QGChannelCategoryImpl.kt | 80 ++++ .../internal/channel/QGForumChannelImpl.kt | 106 +++++ .../internal/channel/QGNonTextChannelImpl.kt | 64 +++ .../internal/channel/QGTextChannelImpl.kt | 111 +++++ .../qguild/internal/forum/QGForumsImpl.kt | 72 +++ .../qguild/internal/forum/QGPostImpl.kt | 77 ++++ .../qguild/internal/forum/QGReplyImpl.kt | 77 ++++ .../internal/forum/QGThreadCreatorImpl.kt | 55 +++ .../qguild/internal/forum/QGThreadImpl.kt | 88 ++++ .../qguild/internal/guild/QGGuildImpl.kt | 171 +++++++ .../qguild/internal/guild/QGMemberImpl.kt | 166 +++++++ .../message/QGReceiveMessageContentImpl.kt | 56 +++ .../message/QGSingleMessageReceiptImpl.kt | 45 ++ .../qguild/internal/role/BaseQGRole.kt | 41 ++ .../qguild/internal/role/QGGuildRoleImpl.kt | 134 ++++++ .../qguild/internal/role/QGMemberRoleImpl.kt | 81 ++++ .../qguild/internal/role/QGRoleCreatorImpl.kt | 64 +++ .../qguild/internal/role/QGRoleUpdaterImpl.kt | 52 +++ .../component/qguild/message/ImageParser.kt | 80 ++++ .../component/qguild/message/MessageSender.kt | 169 +++++++ .../simbot/component/qguild/message/QGArk.kt | 23 +- .../component/qguild/message/QGAtMessages.kt | 25 +- .../qguild/message/QGAttachmentMessage.kt | 26 +- .../component/qguild/message/QGContentText.kt | 14 +- .../component/qguild/message/QGEmbed.kt | 14 +- .../component/qguild/message/QGImages.kt | 5 +- .../qguild/message/QGMessageContent.kt} | 24 +- .../qguild/message/QGMessageElement.kt | 15 +- .../qguild/message/QGMessageReceipt.kt | 55 +-- .../component/qguild/message/QGReference.kt | 23 +- .../component/qguild/message/QGReplyTo.kt | 23 +- .../qguild/message/SendingMessageParser.kt | 84 ++-- .../qguild/message/StandardMessageParsers.kt | 34 +- .../component/qguild/message/annotations.kt | 4 +- .../component/qguild/role/QGGuildRole.kt | 87 ++-- .../component/qguild/role/QGMemberRole.kt | 34 +- .../simbot/component/qguild/role}/QGRole.kt | 48 +- .../component/qguild/role/QGRoleCreator.kt | 14 +- .../component/qguild/role/QGRoleUpdater.kt | 7 +- .../component/qguild/utils/TimestampUtil.kt | 43 ++ .../component/qgguild/test/FileConfigTest.kt | 35 ++ .../component/qguild/QQGuildComponent.js.kt} | 13 +- .../qguild/bot/QQGuildBotManager.js.kt | 20 + .../bot/config/TicketConfiguration.js.kt | 21 + .../qguild/message/ImageParser.js.kt | 28 ++ .../src/jvmMain/java/module-info.java | 51 +++ .../component/qguild/QQGuildComponent.jvm.kt | 26 ++ .../qguild/bot/QQGuildBotManager.jvm.kt | 26 ++ .../bot/config/TicketConfiguration.jvm.kt | 21 + .../qguild/message/ImageParser.jvm.kt | 53 +++ ....simbot.component.ComponentFactoryProvider | 1 + ....forte.simbot.plugin.PluginFactoryProvider | 1 + .../component/qguild/BaseQQGuildBotManager.kt | 172 -------- .../simbot/component/qguild/QGChannel.kt | 233 ---------- .../component/qguild/QGChannelCategory.kt | 165 ------- .../simbot/component/qguild/QGTextChannel.kt | 94 ---- .../component/qguild/event/QGChannelEvents.kt | 195 -------- .../simbot/component/qguild/event/QGEvent.kt | 61 --- .../component/qguild/event/QGGuildEvents.kt | 221 ---------- .../qguild/event/QGGuildMemberEvents.kt | 254 ----------- .../qguild/event/QGInternalBotEvent.kt | 89 ---- .../simbot/component/qguild/forum/QGForums.kt | 71 --- .../component/qguild/message/ImageParser.kt | 72 --- .../qguild/util/ComponentBotRequestUtil.kt | 67 --- .../component/qguild/util/QGBotRequestUtil.kt | 112 ----- .../component/qguild/util/RootJobUtil.kt | 42 -- ...forte.simbot.ComponentAutoRegistrarFactory | 1 - .../qguild/QQGuildComponent.native.kt | 21 + .../qguild/bot/QQGuildBotManager.native.kt | 20 + .../bot/config/TicketConfiguration.native.kt | 21 + .../qguild/message/ImageParser.native.kt | 28 ++ .../component/qgguild/test/FileConfigTest.kt | 23 - .../simbot/qguild/stdlib/BotRequests.jvm.kt | 68 ++- 126 files changed, 5256 insertions(+), 3231 deletions(-) create mode 100644 simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.js.kt create mode 100644 simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.native.kt rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/QGObjectiveContainer.kt (96%) create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildBotManagerUsage.kt rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt (64%) create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/QQGuildInitException.kt (51%) create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/annotations.kt rename simbot-component-qq-guild-core-common/src/{main/kotlin/love/forte/simbot/component/qguild => commonMain/kotlin/love/forte/simbot/component/qguild/bot}/QGBot.kt (51%) create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt rename simbot-component-qq-guild-core-common/src/{main/kotlin/love/forte/simbot/component/qguild => commonMain/kotlin/love/forte/simbot/component/qguild/bot}/config/CacheStrategyConfig.kt (92%) rename simbot-component-qq-guild-core-common/src/{main/kotlin/love/forte/simbot/component/qguild => commonMain/kotlin/love/forte/simbot/component/qguild/bot}/config/IntentsConfig.kt (89%) rename simbot-component-qq-guild-core-common/src/{main/kotlin/love/forte/simbot/component/qguild => commonMain/kotlin/love/forte/simbot/component/qguild/bot}/config/QGBotComponentConfiguration.kt (85%) rename simbot-component-qq-guild-core-common/src/{main/kotlin/love/forte/simbot/component/qguild => commonMain/kotlin/love/forte/simbot/component/qguild/bot}/config/QGBotFileConfiguration.kt (90%) rename simbot-component-qq-guild-core-common/src/{main/kotlin/love/forte/simbot/component/qguild => commonMain/kotlin/love/forte/simbot/component/qguild/bot}/config/ShardConfig.kt (70%) rename simbot-component-qq-guild-core-common/src/{main/kotlin/love/forte/simbot/component/qguild => commonMain/kotlin/love/forte/simbot/component/qguild/bot}/config/TicketConfiguration.kt (91%) create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGCategory.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGChannel.kt rename simbot-component-qq-guild-core-common/src/{main/kotlin/love/forte/simbot/component/qguild/forum => commonMain/kotlin/love/forte/simbot/component/qguild/channel}/QGForumChannel.kt (69%) create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGTextChannel.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGEvent.kt rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt (62%) create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildEvents.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildMemberEvents.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGInternalBotEvent.kt rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt (63%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt (60%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt (71%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/forum/QGForumInfoContainer.kt (86%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/forum/QGPost.kt (56%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/forum/QGReply.kt (52%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/forum/QGThread.kt (58%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/forum/QGThreadCreator.kt (96%) rename simbot-component-qq-guild-core-common/src/{main/kotlin/love/forte/simbot/component/qguild => commonMain/kotlin/love/forte/simbot/component/qguild/guild}/QGGuild.kt (54%) create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuildRelation.kt rename simbot-component-qq-guild-core-common/src/{main/kotlin/love/forte/simbot/component/qguild => commonMain/kotlin/love/forte/simbot/component/qguild/guild}/QGMember.kt (73%) create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QGBotImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGChannelCategoryImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGForumChannelImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGNonTextChannelImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGTextChannelImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGPostImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGReplyImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadCreatorImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGGuildImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGMemberImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGReceiveMessageContentImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGSingleMessageReceiptImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/BaseQGRole.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleCreatorImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleUpdaterImpl.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/MessageSender.kt rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/message/QGArk.kt (82%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/message/QGAtMessages.kt (83%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/message/QGAttachmentMessage.kt (80%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/message/QGContentText.kt (88%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/message/QGEmbed.kt (86%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/message/QGImages.kt (93%) rename simbot-component-qq-guild-core-common/src/{main/kotlin/love/forte/simbot/component/qguild/message/QGReceiveMessageContent.kt => commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageContent.kt} (89%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/message/QGMessageElement.kt (50%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/message/QGMessageReceipt.kt (68%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/message/QGReference.kt (87%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/message/QGReplyTo.kt (78%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/message/SendingMessageParser.kt (80%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/message/StandardMessageParsers.kt (86%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/message/annotations.kt (89%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/role/QGGuildRole.kt (62%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/role/QGMemberRole.kt (71%) rename simbot-component-qq-guild-core-common/src/{main/kotlin/love/forte/simbot/component/qguild => commonMain/kotlin/love/forte/simbot/component/qguild/role}/QGRole.kt (79%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/role/QGRoleCreator.kt (88%) rename simbot-component-qq-guild-core-common/src/{main => commonMain}/kotlin/love/forte/simbot/component/qguild/role/QGRoleUpdater.kt (93%) create mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/utils/TimestampUtil.kt create mode 100644 simbot-component-qq-guild-core-common/src/commonTest/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt rename simbot-component-qq-guild-core-common/src/{main/kotlin/love/forte/simbot/component/qguild/JvmSuspendTransAnnotations.kt => jsMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.js.kt} (76%) create mode 100644 simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.js.kt create mode 100644 simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.js.kt create mode 100644 simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.js.kt create mode 100644 simbot-component-qq-guild-core-common/src/jvmMain/java/module-info.java create mode 100644 simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.jvm.kt create mode 100644 simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.jvm.kt create mode 100644 simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.jvm.kt create mode 100644 simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.jvm.kt create mode 100644 simbot-component-qq-guild-core-common/src/jvmMain/resources/META-INF/services/love.forte.simbot.component.ComponentFactoryProvider create mode 100644 simbot-component-qq-guild-core-common/src/jvmMain/resources/META-INF/services/love.forte.simbot.plugin.PluginFactoryProvider delete mode 100644 simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/BaseQQGuildBotManager.kt delete mode 100644 simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGChannel.kt delete mode 100644 simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGChannelCategory.kt delete mode 100644 simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGTextChannel.kt delete mode 100644 simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt delete mode 100644 simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGEvent.kt delete mode 100644 simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGGuildEvents.kt delete mode 100644 simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGGuildMemberEvents.kt delete mode 100644 simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGInternalBotEvent.kt delete mode 100644 simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGForums.kt delete mode 100644 simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/ImageParser.kt delete mode 100644 simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/util/ComponentBotRequestUtil.kt delete mode 100644 simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/util/QGBotRequestUtil.kt delete mode 100644 simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/util/RootJobUtil.kt delete mode 100644 simbot-component-qq-guild-core-common/src/main/resources/META-INF/services/love.forte.simbot.ComponentAutoRegistrarFactory create mode 100644 simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.native.kt create mode 100644 simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.native.kt create mode 100644 simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.native.kt create mode 100644 simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.native.kt delete mode 100644 simbot-component-qq-guild-core-common/src/test/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt diff --git a/build.gradle.kts b/build.gradle.kts index 3f40c225..a7724346 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,22 +17,6 @@ import love.forte.gradle.common.core.repository.Repositories -/* - * Copyright (c) 2022-2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ plugins { idea @@ -66,24 +50,23 @@ allprojects { } mavenLocal() } +// +// configurations.all { +// resolutionStrategy.cacheChangingModulesFor(60, "seconds") +// } } idea { module.apply { isDownloadSources = true - isDownloadJavadoc = true } project { modules.forEach { module -> module.apply { isDownloadSources = true - isDownloadJavadoc = true } } } } -configurations.all { - // check for updates every build - resolutionStrategy.cacheChangingModulesFor(0, "seconds") -} + diff --git a/gradle.properties b/gradle.properties index ad34dc55..62459fef 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,5 +25,5 @@ org.gradle.caching=true #kotlin.experimental.tryK2=true -#http.proxyHost=localhost -#http.proxyPort=7790 +http.proxyHost=localhost +http.proxyPort=7790 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 031330e1..ef3cf930 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] kotlinx-coroutines = "1.7.3" kotlinx-serialization = "1.6.2" -kotlinx-datetime = "0.4.0" +kotlinx-datetime = "0.5.0" okio = "3.3.0" ktor = "2.3.7" openjdk-jmh = "1.35" diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index adc3867c..fd7abab4 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -70,6 +70,11 @@ "@jridgewell/resolve-uri" "3.1.0" "@jridgewell/sourcemap-codec" "1.4.14" +"@js-joda/core@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" + integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== + "@socket.io/component-emitter@~3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" diff --git a/settings.gradle.kts b/settings.gradle.kts index f3254cdb..ea776ffe 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,7 +21,7 @@ rootProject.name = "qq-guild" //include(":builder-generator") include(":simbot-component-qq-guild-api") include(":simbot-component-qq-guild-stdlib") -//include(":simbot-component-qq-guild-core-common") +include(":simbot-component-qq-guild-core-common") //include(":simbot-component-qq-guild-core") //include(":simbot-component-qq-guild-benchmark") diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.kt index 073887af..1fa1b967 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.kt @@ -162,7 +162,6 @@ public class MessageSendApi private constructor( get() = if (body is MultiPartFormDataContent) FormDataHeader else Headers.Empty - /** * [MessageSendApi] 所需参数。详情参考 [文档](https://bot.q.qq.com/wiki/develop/api/openapi/message/post_messages.html#%E9%80%9A%E7%94%A8%E5%8F%82%E6%95%B0) * @@ -242,7 +241,53 @@ public class MessageSendApi private constructor( * */ @Suppress("MemberVisibilityCanBePrivate") - public class Builder : BaseMessageSendBodyBuilder() { + public class Builder { + /** + * [fileImage] 直接set时支持的类型: + * + * 所有平台: + * - [ByteArray] + * - [InputProvider] + * - [ByteReadPacket] + * - [ChannelProvider] + * + * JVM 平台: + * - `java.nio.Path` + * - `java.io.File` + * - `java.net.URL` + * - `java.net.URI` + */ + public var fileImage: Any? = null + set(value) { + when (value) { + null -> { + field = value + } + + is ByteArray -> { + field = value + } + + is InputProvider -> { + field = value + } + + is ByteReadPacket -> { + field = value + } + + is ChannelProvider -> { + field = value + } + + else -> { + checkFileImage(value) + field = value + } + } + + } + public var content: String? = null public var embed: Message.Embed? = null public var ark: Message.Ark? = null @@ -347,19 +392,7 @@ public inline fun MessageSendApi.Factory.create(channelId: String, builder: Buil create(channelId, MessageSendApi.Body.invoke(builder)) -/** - * 提供一些需要由不同平台额外实现的基类。 - * 主要针对 `fileImage`。 - */ -@QGInternalApi -public expect abstract class BaseMessageSendBodyBuilder() { - public open var fileImage: Any? - protected set - /* - * 追加额外的平台功能,但是不能有抽象方法 - */ -} - +internal expect fun checkFileImage(fileImage: Any) // // TencentMessageForSending || MultiPartFormDataContent /** diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/EventIntents.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/EventIntents.kt index 96f0b114..8d8e3288 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/EventIntents.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/EventIntents.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -115,7 +115,7 @@ public sealed class EventIntents { * ``` * */ - public object Guilds : EventIntents() { + public data object Guilds : EventIntents() { /** 频道事件 `intents` */ @get:JvmStatic @get:JvmName("getIntents") @@ -151,7 +151,7 @@ public sealed class EventIntents { * - GUILD_MEMBER_REMOVE // 当成员被移除时 * ``` */ - public object GuildMembers : EventIntents() { + public data object GuildMembers : EventIntents() { /** 频道成员事件 `intents` */ @get:JvmStatic @get:JvmName("getIntents") @@ -179,7 +179,7 @@ public sealed class EventIntents { * ``` */ @PrivateDomainOnly - public object GuildMessages : EventIntents() { + public data object GuildMessages : EventIntents() { /** 频道消息事件 `intents` */ @get:JvmStatic @get:JvmName("getIntents") @@ -202,7 +202,7 @@ public sealed class EventIntents { * - MESSAGE_REACTION_REMOVE // 为消息删除表情表态 * ``` */ - public object GuildMessageReactions : EventIntents() { + public data object GuildMessageReactions : EventIntents() { /** 表情表态事件 `intents` */ @get:JvmStatic @get:JvmName("getIntents") @@ -226,7 +226,7 @@ public sealed class EventIntents { * - DIRECT_MESSAGE_DELETE // 删除(撤回)消息事件 * ``` */ - public object DirectMessage : EventIntents() { + public data object DirectMessage : EventIntents() { /** 表情表态事件 `intents` */ @get:JvmStatic @get:JvmName("getIntents") @@ -255,7 +255,7 @@ public sealed class EventIntents { * - OPEN_FORUM_REPLY_DELETE // 当用户删除评论时 * ``` */ - public object OpenForumsEvent : EventIntents() { + public data object OpenForumsEvent : EventIntents() { /** 论坛事件 `intents` */ @get:JvmStatic @get:JvmName("getIntents") @@ -294,7 +294,7 @@ public sealed class EventIntents { * - AUDIO_OR_LIVE_CHANNEL_MEMBER_EXIT // 当用户离开音视频/直播子频道 * ``` */ - public object AudioOrLiveChannelMember : EventIntents() { + public data object AudioOrLiveChannelMember : EventIntents() { /** 音视频/直播子频道成员进出事件 `intents` */ @get:JvmStatic @get:JvmName("getIntents") @@ -317,7 +317,7 @@ public sealed class EventIntents { * - INTERACTION_CREATE // 互动事件创建时 * ``` */ - public object Interaction : EventIntents() { + public data object Interaction : EventIntents() { /** 互动事件 `intents` */ @get:JvmStatic @get:JvmName("getIntents") @@ -338,7 +338,7 @@ public sealed class EventIntents { * - MESSAGE_AUDIT_REJECT // 消息审核不通过 * ``` */ - public object MessageAudit : EventIntents() { + public data object MessageAudit : EventIntents() { /** 互动事件 `intents` */ @get:JvmStatic @get:JvmName("getIntents") @@ -369,7 +369,7 @@ public sealed class EventIntents { * ``` */ @PrivateDomainOnly - public object ForumsEvent : EventIntents() { + public data object ForumsEvent : EventIntents() { /** 论坛事件 `intents` */ @get:JvmStatic @get:JvmName("getIntents") @@ -414,7 +414,7 @@ public sealed class EventIntents { * ``` */ @PrivateDomainOnly - public object AudioAction : EventIntents() { + public data object AudioAction : EventIntents() { /** 论坛事件 `intents` */ @get:JvmStatic @get:JvmName("getIntents") @@ -445,7 +445,7 @@ public sealed class EventIntents { * ``` */ @PrivateDomainOnly - public object PublicGuildMessages : EventIntents() { + public data object PublicGuildMessages : EventIntents() { /** 论坛事件 `intents` */ @get:JvmStatic @get:JvmName("getIntents") @@ -481,15 +481,24 @@ public sealed class EventIntents { } } -@Deprecated("Unused") -public enum class Events { - -} - - - - - +/** + * [EventIntents] 的所有实现类型的数组。 + * **注意:获取时不会拷贝,也因此不要修改其中任何元素的值。** + */ +public val EventIntentsInstances: Array = arrayOf( + Guilds, + GuildMembers, + GuildMessages, + GuildMessageReactions, + DirectMessage, + OpenForumsEvent, + AudioOrLiveChannelMember, + Interaction, + MessageAudit, + ForumsEvent, + AudioAction, + PublicGuildMessages, +) /** * 鉴权成功之后,后台会下发的 Ready Event. diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/message/MessageBuilders.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/message/MessageBuilders.kt index 69684fa4..28cfdb3b 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/message/MessageBuilders.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/message/MessageBuilders.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -202,15 +202,6 @@ public class EmbedBuilder { */ public var fields: MutableList = mutableListOf() - /** - * 向 [fields] 中添加一个元素。 - * - */ - @Deprecated("'value' is deprecated.", ReplaceWith("addField(name)")) - public fun addField(name: String, value: String) { - addField(name) - } - /** * 向 [fields] 中添加一个元素。 * diff --git a/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.js.kt b/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.js.kt new file mode 100644 index 00000000..640fb7cb --- /dev/null +++ b/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.js.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.qguild.api.message + +internal actual fun checkFileImage(fileImage: Any) { +} diff --git a/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.js.kt b/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.js.kt index ab69e34e..5ea909af 100644 --- a/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.js.kt +++ b/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.js.kt @@ -32,13 +32,3 @@ import love.forte.simbot.qguild.QGInternalApi public actual fun FormBuilder.resolveOther(fileImage: Any?) { } - -/** - * 提供一些需要由不同平台额外实现的基类。 - * 主要针对 `fileImage`。 - */ -@QGInternalApi -public actual abstract class BaseMessageSendBodyBuilder actual constructor() { - public actual open var fileImage: Any? = null - protected set -} diff --git a/simbot-component-qq-guild-api/src/jvmMain/java/module-info.java b/simbot-component-qq-guild-api/src/jvmMain/java/module-info.java index 6029eb69..25f67d3b 100644 --- a/simbot-component-qq-guild-api/src/jvmMain/java/module-info.java +++ b/simbot-component-qq-guild-api/src/jvmMain/java/module-info.java @@ -6,6 +6,8 @@ requires transitive kotlinx.serialization.json; // simbot requires static simbot.common.annotations; + requires transitive simbot.logger; + requires transitive org.slf4j; requires transitive simbot.common.apidefinition; requires transitive simbot.common.suspendrunner; requires transitive simbot.common.core; @@ -13,6 +15,8 @@ requires io.ktor.http; requires io.ktor.client.core; requires io.ktor.client.content.negotiation; + requires io.ktor.utils; + requires io.ktor.io; exports love.forte.simbot.qguild; diff --git a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt index 0f83d5c6..679797ae 100644 --- a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt +++ b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.jvm.kt @@ -27,11 +27,15 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.future.future import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonBuilder +import love.forte.simbot.annotations.InternalSimbotAPI import love.forte.simbot.qguild.QGApi4J import love.forte.simbot.qguild.QQGuild +import love.forte.simbot.suspendrunner.reserve.SuspendReserve +import love.forte.simbot.suspendrunner.reserve.suspendReserve import love.forte.simbot.suspendrunner.runInNoScopeBlocking import java.util.concurrent.CompletableFuture import java.util.function.Consumer +import kotlin.coroutines.EmptyCoroutineContext /** @@ -141,3 +145,54 @@ public fun QQGuildApi.requestDataAsync( ): CompletableFuture = (scope ?: client).future { requestData(client, token, server) } + +/** + * [QQGuildApi.request] for Java + * + * @see SuspendReserve + */ +@QGApi4J +@JvmOverloads +@OptIn(InternalSimbotAPI::class) +public fun QQGuildApi<*>.requestReserve( + client: HttpClient, + token: String, + server: Url = QQGuild.URL, + scope: CoroutineScope? = null, +): SuspendReserve = suspendReserve(scope = (scope ?: client), context = EmptyCoroutineContext) { + request(client, token, server) +} + +/** + * [QQGuildApi.requestText] for Java + * + * @see SuspendReserve + */ +@QGApi4J +@JvmOverloads +@OptIn(InternalSimbotAPI::class) +public fun QQGuildApi<*>.requestTextReserve( + client: HttpClient, + token: String, + server: Url = QQGuild.URL, + scope: CoroutineScope? = null, +): SuspendReserve = suspendReserve(scope = (scope ?: client), context = EmptyCoroutineContext) { + requestText(client, token, server) +} + +/** + * [QQGuildApi.requestData] for Java + * + * @see SuspendReserve + */ +@QGApi4J +@JvmOverloads +@OptIn(InternalSimbotAPI::class) +public fun QQGuildApi.requestDataReserve( + client: HttpClient, + token: String, + server: Url = QQGuild.URL, + scope: CoroutineScope? = null, +): SuspendReserve = suspendReserve(scope = (scope ?: client), context = EmptyCoroutineContext) { + requestData(client, token, server) +} diff --git a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.jvm.kt b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.jvm.kt index 09a3914d..6a4ede7b 100644 --- a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.jvm.kt +++ b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.jvm.kt @@ -22,7 +22,6 @@ import io.ktor.http.* import io.ktor.util.cio.* import io.ktor.utils.io.nio.* import io.ktor.utils.io.streams.* -import love.forte.simbot.qguild.QGInternalApi import java.io.File import java.net.URI import java.net.URL @@ -34,11 +33,13 @@ import kotlin.io.path.name /** * JVM平台下所能支持的针对 [MessageSendApi.Body.fileImage] 的解析。 * - * 处理当类型为 + * 处理当类型为: + * * - [File] * - [Path] * - [URL] * - [URI] + * * 时的表单提交方式。 * */ @@ -78,46 +79,14 @@ public actual fun FormBuilder.resolveOther(fileImage: Any?) { } } - -/** - * 提供一些需要由不同平台额外实现的基类。 - * 主要针对 `fileImage`。 - * - * 提供额外对于 - * - * - [File] - * - [Path] - * - [URL] - * - [URI] - * - * 的类型支持。 - * - * 与 [resolveOther] 中的支持类型相对应。 - * - */ -@QGInternalApi -public actual abstract class BaseMessageSendBodyBuilder actual constructor() { - /* - 追加额外的平台功能,但是不能有抽象方法 - */ - public actual open var fileImage: Any? = null - protected set - - - public fun setFileImage(file: File) { - fileImage = file - } - - public fun setFileImage(path: Path) { - fileImage = path - } - - public fun setFileImage(url: URL) { - fileImage = url - } - - public fun setFileImage(uri: URI) { - fileImage = uri +internal actual fun checkFileImage(fileImage: Any) { + when (fileImage) { + is File -> {} + is Path -> {} + is URL -> {} + is URI -> {} + else -> { + throw IllegalArgumentException("Unsupported fileImage type: $fileImage (${fileImage::class.java})") + } } - } diff --git a/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.native.kt b/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.native.kt new file mode 100644 index 00000000..640fb7cb --- /dev/null +++ b/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/api/message/MessageSendApi.native.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.qguild.api.message + +internal actual fun checkFileImage(fileImage: Any) { +} diff --git a/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.native.kt b/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.native.kt index fc1d7b29..6f62c346 100644 --- a/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.native.kt +++ b/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/api/message/resolveOther.native.kt @@ -24,15 +24,6 @@ import love.forte.simbot.qguild.QGInternalApi * native平台没有额外的类型支持 * */ -public actual fun FormBuilder.resolveOther(fileImage: Any?) { -} - -/** - * 提供一些需要由不同平台额外实现的基类。 - * 主要针对 `fileImage`。 - */ @QGInternalApi -public actual abstract class BaseMessageSendBodyBuilder actual constructor() { - public actual open var fileImage: Any? = null - protected set +public actual fun FormBuilder.resolveOther(fileImage: Any?) { } diff --git a/simbot-component-qq-guild-core-common/build.gradle.kts b/simbot-component-qq-guild-core-common/build.gradle.kts index 8cc015d4..c173301e 100644 --- a/simbot-component-qq-guild-core-common/build.gradle.kts +++ b/simbot-component-qq-guild-core-common/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,42 +15,77 @@ * If not, see . */ +import love.forte.gradle.common.kotlin.multiplatform.applyTier1 +import love.forte.gradle.common.kotlin.multiplatform.applyTier2 +import love.forte.gradle.common.kotlin.multiplatform.applyTier3 + plugins { - `simbot-tcg-suspend-transform-configure` - id("simbot-tencent-guild.module-conventions") - id("simbot-tencent-guild.maven-publish") + kotlin("multiplatform") kotlin("plugin.serialization") `qq-guild-dokka-partial-configure` + `simbot-tcg-suspend-transform-configure` } +configJavaCompileWithModule("simbot.component.qqguild.core") +apply(plugin = "qq-guild-multiplatform-maven-publish") + +configJsTestTasks() + kotlin { + explicitApi() + applyDefaultHierarchyTemplate() + sourceSets.configureEach { languageSettings { - optIn("love.forte.simbot.InternalSimbotApi") optIn("love.forte.simbot.qguild.QGInternalApi") } } -} -dependencies { - api(project(":simbot-component-qq-guild-stdlib")) { - exclude(SIMBOT_GROUP, "simbot-logger-slf4j-impl") + configKotlinJvm() + + js(IR) { + configJs() } - compileOnly(simbotCore) - api(libs.ktor.client.core) - api(libs.ktor.client.cio) - api(libs.ktor.client.ws) - api(libs.ktor.client.contentNegotiation) - api(libs.ktor.serialization.kotlinxJson) - api(libs.kotlinx.serialization.json) + applyTier1() + applyTier2() + applyTier3(supportKtorClient = true) - compileOnly(libs.kotlinx.serialization.properties) - compileOnly(libs.charleskorn.kaml) + sourceSets { + commonMain.dependencies { + api(libs.simbot.api) + api(project(":simbot-component-qq-guild-stdlib")) + compileOnly(libs.simbot.common.annotations) + // ktor + api(libs.ktor.client.contentNegotiation) + api(libs.ktor.serialization.kotlinxJson) + api(libs.ktor.client.ws) + // datetime + api(libs.kotlinx.datetime) + } - testImplementation(simbotCore) - testImplementation(libs.charleskorn.kaml) - testImplementation(simbotLoggerSlf4jImpl) + commonTest.dependencies { + implementation(kotlin("test")) + implementation(libs.kotlinx.coroutines.test) + } -} + jvmTest.dependencies { + runtimeOnly(libs.ktor.client.cio) + implementation(libs.log4j.api) + implementation(libs.log4j.core) + implementation(libs.log4j.slf4j2) + } + jsMain.dependencies { + api(libs.simbot.common.annotations) + } + + nativeMain.dependencies { + api(libs.simbot.common.annotations) + } + + mingwTest.dependencies { + implementation(libs.ktor.client.winhttp) + } + } +} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGObjectiveContainer.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QGObjectiveContainer.kt similarity index 96% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGObjectiveContainer.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QGObjectiveContainer.kt index b779a6df..7115a4c7 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGObjectiveContainer.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QGObjectiveContainer.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildBotManagerUsage.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildBotManagerUsage.kt new file mode 100644 index 00000000..76bb3941 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildBotManagerUsage.kt @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2021-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild + +import love.forte.simbot.application.Application +import love.forte.simbot.component.qguild.bot.QQGuildBotManager +import love.forte.simbot.plugin.Plugin + + +/** + * 获取第一个 [QQGuildBotManager] 并使用 + * @throws [NoSuchElementException] if no such element is found. + */ +public inline fun Application.qgGuildBots(block: QQGuildBotManager.() -> Unit) { + val manager = botManagers.first { it is QQGuildBotManager } as QQGuildBotManager + manager.block() +} + +/** + * 尝试寻找并使用 [QQGuildBotManager] + */ +public inline fun Application.qgGuildBotsIfSupport(block: QQGuildBotManager.() -> Unit) { + val manager = (botManagers.firstOrNull { it is QQGuildBotManager } ?: return) as QQGuildBotManager + manager.block() +} + +/** + * 从中过滤取出所有 [QQGuildBotManager] 实例. + */ +public fun Iterable.filterIsQQGuildBotManagers(): List = + filterIsInstance() + +/** + * 从序列中过滤出 [QQGuildBotManager] 实例. + */ +public fun Sequence.filterIsQQGuildBotManagers(): Sequence = + filterIsInstance() + +/** + * 从中过滤取出所有 [QQGuildBotManager] 实例。 + * + * @throws NoSuchElementException 如果不存在 + */ +public fun Iterable.firstQQGuildBotManager(): QQGuildBotManager = + first { it is QQGuildBotManager } as QQGuildBotManager + +/** + * 从序列中过滤出 [QQGuildBotManager] 实例。 + * + * @throws NoSuchElementException 如果不存在 + */ +public fun Sequence.firstQQGuildBotManager(): QQGuildBotManager = + first { it is QQGuildBotManager } as QQGuildBotManager + +/** + * 从中过滤取出所有 [QQGuildBotManager] 实例。 + * 如果不存在则得到null。 + */ +public fun Iterable.firstQQGuildBotManagerOrNull(): QQGuildBotManager? = + firstOrNull { it is QQGuildBotManager } as QQGuildBotManager? + +/** + * 从序列中过滤出 [QQGuildBotManager] 实例。 + * 如果不存在则得到null。 + */ +public fun Sequence.firstQQGuildBotManagerOrNull(): QQGuildBotManager? = + firstOrNull { it is QQGuildBotManager } as QQGuildBotManager? diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt similarity index 64% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt index 981c57b5..ea7b9028 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023. ForteScarlet. + * Copyright (c) 2021-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -21,11 +21,15 @@ import kotlinx.serialization.modules.PolymorphicModuleBuilder import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.polymorphic import kotlinx.serialization.modules.subclass -import love.forte.simbot.* -import love.forte.simbot.component.qguild.config.QGBotFileConfiguration +import love.forte.simbot.bot.serializableBotConfigurationPolymorphic +import love.forte.simbot.common.function.ConfigurerFunction +import love.forte.simbot.common.function.invokeBy +import love.forte.simbot.component.* +import love.forte.simbot.component.qguild.bot.config.QGBotFileConfiguration import love.forte.simbot.component.qguild.message.* import love.forte.simbot.message.At import love.forte.simbot.message.Message +import kotlin.jvm.JvmStatic /** @@ -36,13 +40,12 @@ import love.forte.simbot.message.Message public class QQGuildComponent : Component { override val id: String get() = ID_VALUE - - override val componentSerializersModule: SerializersModule + + override val serializersModule: SerializersModule get() = messageSerializersModule - - + override fun toString(): String = TO_STRING_VALUE - + override fun equals(other: Any?): Boolean { return when { other === this -> true @@ -50,11 +53,9 @@ public class QQGuildComponent : Component { else -> other.id == id } } - - override fun hashCode(): Int { - return id.hashCode() - } - + + override fun hashCode(): Int = ID_VALUE_HASH + /** * 组件 [QQGuildComponent] 的注册器。 */ @@ -64,17 +65,13 @@ public class QQGuildComponent : Component { */ @Suppress("MemberVisibilityCanBePrivate") public const val ID_VALUE: String = "simbot.qqguild" - - internal const val TO_STRING_VALUE: String = "QQGuildComponent(id=$ID_VALUE)" - - /** - * [ID_VALUE] 的 [ID] 类型。 - */ - @Deprecated("Unused") - public val componentID: CharSequenceID = ID_VALUE.ID + + private val ID_VALUE_HASH = ID_VALUE.hashCode() + private const val TO_STRING_VALUE: String = "QQGuildComponent(id=$ID_VALUE)" + /** - * 在 [QGReceiveMessageContent] 中提及 `channel` 的时候,被转化的 [At] 所使用的 [At.type]。 + * 在 [QGMessageContent] 中提及 `channel` 的时候,被转化的 [At] 所使用的 [At.type]。 * * ```kotlin * // 在QQ频道相关事件中 @@ -89,27 +86,29 @@ public class QQGuildComponent : Component { */ public const val AT_CHANNEL_TYPE: String = "channel" - /** * 作为注册器时的标识。 */ - override val key: Attribute = attribute(ID_VALUE) - + override val key: ComponentFactory.Key = object : ComponentFactory.Key {} + /** - * 注册配置函数。 + * 创建 [QQGuildComponent]. */ - override suspend fun create(configurator: QQGuildComponentConfiguration.() -> Unit): QQGuildComponent { - QQGuildComponentConfiguration.configurator() - + override fun create( + context: ComponentConfigureContext, + configurer: ConfigurerFunction + ): QQGuildComponent { + QQGuildComponentConfiguration().invokeBy(configurer) + return QQGuildComponent() } - + /** * QQ频道组件所使用到的特殊消息类型序列化信息。 */ @JvmStatic public val messageSerializersModule: SerializersModule = SerializersModule { - fun PolymorphicModuleBuilder>.subclass0() { + fun PolymorphicModuleBuilder.subclass0() { subclass(QGArk.serializer()) subclass(QGAttachmentMessage.serializer()) subclass(QGReplyTo.serializer()) @@ -120,21 +119,23 @@ public class QQGuildComponent : Component { @Suppress("DEPRECATION") subclass(QGAtChannel.serializer()) } - + polymorphic(QGMessageElement::class) { subclass0() } - + polymorphic(Message.Element::class) { subclass0() } - include(QGBotFileConfiguration.serializersModule) + serializableBotConfigurationPolymorphic { + subclass(QGBotFileConfiguration.serializer()) + } } - - + + } - + } @@ -144,20 +145,27 @@ public class QQGuildComponent : Component { * _Note: 目前 [QQGuildComponent] 暂无可配置内容。_ * */ -public object QQGuildComponentConfiguration +public class QQGuildComponentConfiguration /** * [QQGuildComponent] 的注册器工厂,用于支持组件的自动加载。 * */ -public class QQGuildComponentRegistrarFactory : - ComponentAutoRegistrarFactory { - override val registrar: QQGuildComponent.Factory - get() = QQGuildComponent -} - - +public class QQGuildComponentFactoryProvider : + ComponentFactoryProvider { + override fun loadConfigurers(): Sequence? = + loadQGComponentConfigurers() + override fun provide(): ComponentFactory<*, QQGuildComponentConfiguration> = QQGuildComponent +} +// TODO add to package-info +/** + * 用于 [QQGuildComponentFactoryProvider.loadConfigurers] 的可加载的额外配置器。 + * + */ +public fun interface QQGuildComponentFactoryConfigurerProvider : + ComponentFactoryConfigurerProvider +internal expect fun loadQGComponentConfigurers(): Sequence? diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt new file mode 100644 index 00000000..2aa541cd --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild + +import love.forte.simbot.application.Application +import love.forte.simbot.application.ApplicationBuilder +import love.forte.simbot.application.ApplicationFactoryConfigurer +import love.forte.simbot.common.function.ConfigurerFunction +import love.forte.simbot.common.function.invokeWith +import love.forte.simbot.component.qguild.bot.QQGuildBotManager +import love.forte.simbot.component.qguild.bot.QQGuildBotManagerConfiguration + + +/** + * 在 [ApplicationBuilder] 中安装使用 [QQGuildComponent]。 + * + * usage: + * ```kotlin + * simbotApplication(Foo) { + * useQQGuildComponent() + * // 或 + * useQQGuildComponent { ... } + * } + * ``` + * + * 相当于: + * ```kotlin + * simbotApplication(Foo) { + * install(QQGuildComponent) { ... } + * } + * ``` + * + * @see QQGuildComponent + * + */ +public fun ApplicationFactoryConfigurer<*, *, *>.useQQGuildComponent(configurator: ConfigurerFunction? = null) { + if (configurator != null) { + install(QQGuildComponent, configurator) + } else { + install(QQGuildComponent) + } +} + +/** + * 在 [ApplicationBuilder] 中 **尝试** 安装使用 [QQGuildBotManager]。 + * + * usage: + * ```kotlin + * simbotApplication(Foo) { + * useQQGuildBotManager() + * // 或 + * useQQGuildBotManager { ... } + * } + * ``` + * + * 相当于: + * ```kotlin + * simbotApplication(Foo) { + * install(QQGuildBotManager) { ... } + * } + * ``` + * @see QQGuildBotManager + * + */ +public fun ApplicationFactoryConfigurer<*, *, *>.useQQGuildBotManager(configurator: ConfigurerFunction? = null) { + if (configurator != null) { + install(QQGuildBotManager, configurator) + } else { + install(QQGuildBotManager) + } +} + +/** + * 同时安装使用 [QQGuildComponent] 和 [QQGuildBotManager]. + * + * usage: + * ```kotlin + * simbotApplication(Foo) { + * useQQGuild() + * // 或 + * useQQGuild { + * component { ... } + * botManager { ... } + * } + * } + * ``` + * + * 相当于: + * ```kotlin + * simbotApplication(Foo) { + * install(QQGuildComponent) { ... } + * install(QGBotManager) { ... } + * } + * ``` + * + * + */ +public fun ApplicationFactoryConfigurer<*, *, *>.useQQGuild(builder: QQGuildUsageBuilder.() -> Unit = {}) { + QQGuildUsageBuilderImpl().also(builder).build(this) +} + +/** + * 使用 [QQGuildBotManager] + * + * @throws NoSuchElementException 如果不存在 + */ +public inline fun A.qqGuildBots(block: QQGuildBotManager.() -> Unit) { + botManagers.firstQQGuildBotManager().also(block) +} + + +/** + * 为 [QQGuildUsageBuilder] 中的函数染色。 + */ +@DslMarker +@Target(AnnotationTarget.FUNCTION) +internal annotation class QQGuildUsageBuilderDsl + + +/** + * 使用在 [useQQGuild] 函数中,用于同时针对 [QQGuildComponent] 和 [QQGuildBotManager] + * 进行配置。 + * + * @see useQQGuild + */ +public interface QQGuildUsageBuilder { + + /** + * 追加一个安装 [QQGuildComponent] 时候使用的配置。 + */ + @QQGuildUsageBuilderDsl + public fun component(configurator: ConfigurerFunction) + + + /** + * 追加一个安装 [QQGuildBotManager] 时候使用的配置。 + */ + @QQGuildUsageBuilderDsl + public fun botManager(configurator: ConfigurerFunction) + +} + + +private class QQGuildUsageBuilderImpl : QQGuildUsageBuilder { + private var componentConfigs = mutableListOf>() + private var botManagerConfigs = mutableListOf>() + + override fun component(configurator: ConfigurerFunction) { + componentConfigs.add(configurator) + } + + override fun botManager(configurator: ConfigurerFunction) { + botManagerConfigs.add(configurator) + + } + + fun build(configurer: ApplicationFactoryConfigurer<*, *, *>) { + configurer.useQQGuildComponent { + componentConfigs.forEach { + it.invokeWith(this) + } + } + configurer.useQQGuildBotManager { + botManagerConfigs.forEach { + it.invokeWith(this) + } + } + } + + +} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildInitException.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildInitException.kt similarity index 51% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildInitException.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildInitException.kt index 54e87546..02cb8fa2 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildInitException.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildInitException.kt @@ -1,25 +1,27 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. If not, see . + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . */ package love.forte.simbot.component.qguild -import love.forte.simbot.SimbotRuntimeException - - /** * 用于当 [QGGuild] 或 [QGBot] 等类型实现类内部初始化信息过程中出现的异常。 * @author ForteScarlet */ -public open class QQGuildInitException : SimbotRuntimeException { +public open class QQGuildInitException : RuntimeException { public constructor() : super() public constructor(message: String?) : super(message) public constructor(message: String?, cause: Throwable?) : super(message, cause) diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/annotations.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/annotations.kt new file mode 100644 index 00000000..2840fb4f --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/annotations.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild + +/** + * 一个尚在试验阶段的QQ频道组件API。 + * 试验阶段的API可能存在漏洞、缺陷,或实现不稳定,且有可能在未来被修改、删除,且没有兼容性保证。 + */ +@Retention(AnnotationRetention.BINARY) +@RequiresOptIn( + message = "一个尚在试验阶段的QQ频道组件API。" + + "试验阶段的API可能存在漏洞、缺陷,或实现不稳定,且有可能在未来被修改、删除,且没有兼容性保证。", + level = RequiresOptIn.Level.WARNING +) +@MustBeDocumented +public annotation class ExperimentalQGApi + + diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGBot.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QGBot.kt similarity index 51% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGBot.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QGBot.kt index 4c7810f5..996870ea 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGBot.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QGBot.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2023. ForteScarlet. + * Copyright (c) 2021-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,29 +15,28 @@ * If not, see . */ -package love.forte.simbot.component.qguild +package love.forte.simbot.component.qguild.bot import io.ktor.client.* import io.ktor.client.request.* -import kotlinx.coroutines.isActive -import love.forte.simbot.ID -import love.forte.simbot.action.UnsupportedActionException import love.forte.simbot.bot.Bot +import love.forte.simbot.bot.ContactRelation +import love.forte.simbot.bot.GroupRelation +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.component.qguild.QQGuildComponent +import love.forte.simbot.component.qguild.channel.QGTextChannel +import love.forte.simbot.component.qguild.guild.QGGuildRelation import love.forte.simbot.component.qguild.message.QGMessageReceipt -import love.forte.simbot.definition.Contact -import love.forte.simbot.definition.Group -import love.forte.simbot.definition.GuildBot -import love.forte.simbot.event.EventProcessor -import love.forte.simbot.message.Image import love.forte.simbot.message.Message import love.forte.simbot.message.MessageContent import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.api.MessageAuditedException -import love.forte.simbot.qguild.model.ChannelType -import love.forte.simbot.utils.item.Items -import love.forte.simbot.utils.item.Items.Companion.emptyItems -import love.forte.simbot.qguild.Bot as QGSourceBot +import love.forte.simbot.suspendrunner.ST +import love.forte.simbot.suspendrunner.STP +import kotlin.jvm.JvmSynthetic import love.forte.simbot.qguild.model.User as QGSourceUser +import love.forte.simbot.qguild.stdlib.Bot as QGSourceBot /** * 一个 [QQ频道Bot][QGSourceBot] 的 simbot组件实现接口, @@ -58,16 +57,10 @@ public interface QGBot : Bot { */ override val component: QQGuildComponent - /** - * 得到自己。 - */ - override val bot: QGBot - get() = this - /** * 在QQ频道标准库中的原始Bot类型。 */ - public val source: love.forte.simbot.qguild.Bot + public val source: QGSourceBot /** * 当前bot的 **appId** 。 @@ -83,9 +76,9 @@ public interface QGBot : Bot { * 得到当前bot的用户ID。 * * [userId] 是当前bot的用户id,需要至少执行一次 [start] 来初始化, - * 否则会导致 [UninitializedPropertyAccessException] 异常。 + * 否则会导致 [IllegalStateException] 异常。 * - * @throws UninitializedPropertyAccessException bot没有通过 [start] 初始化用户信息. + * @throws IllegalStateException bot没有通过 [start] 初始化用户信息. */ public val userId: ID @@ -93,11 +86,11 @@ public interface QGBot : Bot { * bot的用户名 * * 需要至少执行一次 [start] 来初始化, - * 否则会导致 [UninitializedPropertyAccessException] 异常。 + * 否则会导致 [IllegalStateException] 异常。 * - * @throws UninitializedPropertyAccessException bot没有通过 [start] 初始化用户信息. + * @throws IllegalStateException bot没有通过 [start] 初始化用户信息. */ - override val username: String + override val name: String /** * QQ机器人有两个可能的唯一标识:作为bot的 [app id][id] 以及在系统中作为用户的 [user id][userId]. @@ -110,114 +103,36 @@ public interface QGBot : Bot { /** * 启动当前bot, 并且初始化此bot的信息。 * - * 启动时会通过 [Bot.me()][QGSourceBot.me] 查询当前bot的信息,并为 [isMe] 提供额外id的匹配支持。 + * 启动时会通过 [me] 查询当前bot的信息,并为 [isMe] 提供额外id的匹配支持。 * */ @JvmSynthetic - override suspend fun start(): Boolean + override suspend fun start() /** * bot的头像 */ - override val avatar: String - - /** - * bot所属的bot管理器 - */ - override val manager: BaseQQGuildBotManager - - /** - * bot所属的事件处理器。 - */ - override val eventProcessor: EventProcessor - + public val avatar: String //// Impl /** - * 暂时无法直接解析“id”图片 - * - * @throws UnsupportedActionException - */ - @JST - override suspend fun resolveImage(id: ID): Image<*> { - - // TODO fake remote image? - throw UnsupportedActionException("resolveImage(ID) not support.") - } - - override val isActive: Boolean - get() = source.isActive - - /** - * Deprecated: QQ频道BOT没有'群'概念 - */ - @Deprecated( - "Group related APIs are not supported", - ReplaceWith("emptyItems()", "love.forte.simbot.utils.item.Items.Companion.emptyItems") - ) - override val groups: Items - get() = emptyItems() - - /** - * Deprecated: QQ频道BOT没有'群'概念 - */ - @Deprecated("Group related APIs are not supported", ReplaceWith("false")) - override val isGroupsSupported: Boolean - get() = false - - /** - * Deprecated: QQ频道BOT没有'群'概念 - */ - @Deprecated("Group related APIs are not supported", ReplaceWith("0")) - @JvmSynthetic - override suspend fun groupCount(): Int = 0 - - /** - * 是否支持QQ频道相关API,如 [guilds]。始终得到 `true`。 - */ - override val isGuildsSupported: Boolean - get() = true - - /** - * 获取当前bot所在的频道服务器列表。 - */ - override val guilds: Items - - /** - * QQ频道数量。QQ频道不支持获取频道服务器数量,始终得到 `-1`。 - */ - @Deprecated("The guild count API is not supported.", ReplaceWith("-1")) - @JvmSynthetic - override suspend fun guildCount(): Int = -1 - - /** - * 根据ID尝试获取一个指定的guild。 + * QQ频道BOT没有'群'概念,始终得到 `null`。 */ - @JST(blockingBaseName = "getGuild", blockingSuffix = "", asyncBaseName = "getGuild") - override suspend fun guild(id: ID): QGGuild? + override val groupRelation: GroupRelation? + get() = null /** - * 直接获取指定ID的子频道。 - * - * @see category - * - * @throws QQGuildApiException 请求失败,例如没有权限 + * QQ频道BOT不存在'联系人'相关操作,始终得到 `null`。 */ - @JST(blockingBaseName = "getChannel", blockingSuffix = "", asyncBaseName = "getChannel") - public suspend fun channel(channelId: ID): QGChannel? - + override val contactRelation: ContactRelation? + get() = null /** - * 直接获取指定ID的子频道分类。 - * - * @throws QQGuildApiException 请求失败,例如没有权限 - * @throws IllegalStateException 当目标子频道的类型不属于 [分组类型][ChannelType.CATEGORY] 时 + * 对频道服务器、以及频道、分组等相关内容的操作。 * */ - @JST(blockingBaseName = "getCategory", blockingSuffix = "", asyncBaseName = "getCategory") - public suspend fun category(channelId: ID): QGChannelCategory? - + override val guildRelation: QGGuildRelation /** * 直接向目标子频道发送消息。 @@ -225,7 +140,7 @@ public interface QGBot : Bot { * 此频道需要为文字子频道,否则会产生异常,但是此异常不会由程序检测, * 而是通过API的错误响应 [QQGuildApiException] 体现。 * - * [sendTo] 相对于 [QGChannel.send] 而言更加“不可靠” + * [sendTo] 相对于 [QGTextChannel.send] 而言更加“不可靠” * —— 因为它跳过了对频道服务器和对子频道类型的校验,失去了在消息中自动填充 `msgId` 等透明行为,并且直接使用ID也会存在一些细微的隐患。 * 但是这可以有效规避当没有获取频道服务器或子频道信息权限时候可能导致的问题。 * @@ -235,7 +150,7 @@ public interface QGBot : Bot { * * @return 消息发送回执 */ - @JST + @ST public suspend fun sendTo(channelId: ID, text: String): QGMessageReceipt /** @@ -244,7 +159,7 @@ public interface QGBot : Bot { * 此频道需要为文字子频道,否则会产生异常,但是此异常不会由程序检测, * 而是通过API的错误响应 [QQGuildApiException] 体现。 * - * [sendTo] 相对于 [QGChannel.send] 而言更加“不可靠” + * [sendTo] 相对于 [QGTextChannel.send] 而言更加“不可靠” * —— 因为它跳过了对频道服务器和对子频道类型的校验,失去了在消息中自动填充 `msgId` 等透明行为,并且直接使用ID也会存在一些细微的隐患。 * 但是这可以有效规避当没有获取频道服务器或子频道信息权限时候可能导致的问题。 * @@ -254,7 +169,7 @@ public interface QGBot : Bot { * * @return 消息发送回执 */ - @JST + @ST public suspend fun sendTo(channelId: ID, message: Message): QGMessageReceipt /** @@ -263,8 +178,9 @@ public interface QGBot : Bot { * 此频道需要为文字子频道,否则会产生异常,但是此异常不会由程序检测, * 而是通过API的错误响应 [QQGuildApiException] 体现。 * - * [sendTo] 相对于 [QGChannel.send] 而言更加“不可靠” - * —— 因为它跳过了对频道服务器和对子频道类型的校验,失去了在消息中自动填充 `msgId` 等透明行为,并且直接使用ID也会存在一些细微的隐患。 + * [sendTo] 相对于 [QGTextChannel.send] 而言更加“不可靠” + * —— 因为它跳过了对频道服务器和对子频道类型的校验,失去了在消息中自动填充 `msgId` 等透明行为, + * 并且直接使用ID也会存在一些细微的隐患。 * 但是这可以有效规避当没有获取频道服务器或子频道信息权限时候可能导致的问题。 * * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 @@ -273,48 +189,10 @@ public interface QGBot : Bot { * * @return 消息发送回执 */ - @JST + @ST public suspend fun sendTo(channelId: ID, message: MessageContent): QGMessageReceipt - /** - * Deprecated: QQ频道BOT不存在'联系人'列表,始终得到 [emptyItems]。 - */ - @Deprecated( - "Contact related APIs are not supported", - ReplaceWith("emptyItems()", "love.forte.simbot.utils.item.Items.Companion.emptyItems") - ) - override val contacts: Items - get() = emptyItems() - - - /** - * Deprecated: QQ频道BOT不存在'联系人'列表,始终得到 `false`。 - */ - @Deprecated("Contact related APIs are not supported", ReplaceWith("null")) - override val isContactsSupported: Boolean - get() = false - - /** - * Deprecated: QQ频道BOT不存在'联系人'列表,始终得到 `0`。 - */ - @Deprecated("Contact related APIs are not supported", ReplaceWith("0")) - override suspend fun contactCount(): Int = 0 - - /** - * Deprecated: QQ频道BOT不存在'联系人'列表,始终得到 `null`。 - */ - @Deprecated("Contact related APIs are not supported", ReplaceWith("null")) - @JvmSynthetic - override suspend fun contact(id: ID): Contact? = null - - /** - * QQ频道BOT不存在'联系人'列表 - */ - @Deprecated("Group related APIs are not supported", ReplaceWith("null")) - @JvmSynthetic - override suspend fun group(id: ID): Group? = null - /** * 获取当前bot对应的用户信息。 * @@ -325,7 +203,7 @@ public interface QGBot : Bot { * * @return API得到的用户信息结果 */ - @JST(blockingBaseName = "getMe", blockingSuffix = "", asyncBaseName = "getMe") + @ST(blockingBaseName = "getMe", blockingSuffix = "", asyncBaseName = "getMe") public suspend fun me(withCache: Boolean): QGSourceUser /** @@ -335,26 +213,6 @@ public interface QGBot : Bot { * * @return API得到的用户信息结果 */ - @JSTP + @STP public suspend fun me(): QGSourceUser = me(false) } - - -/** - * QQ频道组件中 [QGSourceBot] 对 [GuildBot] 的实现。 - * - */ -public interface QGGuildBot : QGBot, GuildBot { - override suspend fun asMember(): QGMember - - /** - * 获取频道服务器序列 - */ - override val guilds: Items - - /** - * 根据ID尝试获取一个指定的guild。 - */ - @JST(blockingBaseName = "getGuild", blockingSuffix = "", asyncBaseName = "getGuild") - override suspend fun guild(id: ID): QGGuild? -} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt new file mode 100644 index 00000000..c9ad1c79 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2021-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.bot + +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import love.forte.simbot.application.ApplicationConfiguration +import love.forte.simbot.bot.BotManager +import love.forte.simbot.bot.JobBasedBotManager +import love.forte.simbot.bot.SerializableBotConfiguration +import love.forte.simbot.bot.UnsupportedBotConfigurationException +import love.forte.simbot.common.coroutines.linkTo +import love.forte.simbot.common.function.ConfigurerFunction +import love.forte.simbot.common.function.invokeBy +import love.forte.simbot.component.NoSuchComponentException +import love.forte.simbot.component.find +import love.forte.simbot.component.qguild.QQGuildComponent +import love.forte.simbot.component.qguild.bot.config.QGBotComponentConfiguration +import love.forte.simbot.component.qguild.bot.config.QGBotFileConfiguration +import love.forte.simbot.component.qguild.internal.bot.QQGuildBotManagerImpl +import love.forte.simbot.event.EventDispatcher +import love.forte.simbot.logger.Logger +import love.forte.simbot.plugin.PluginConfigureContext +import love.forte.simbot.plugin.PluginFactory +import love.forte.simbot.plugin.PluginFactoryConfigurerProvider +import love.forte.simbot.plugin.PluginFactoryProvider +import love.forte.simbot.qguild.stdlib.Bot +import love.forte.simbot.qguild.stdlib.BotConfiguration +import love.forte.simbot.qguild.stdlib.ConfigurableBotConfiguration +import kotlin.contracts.ExperimentalContracts +import kotlin.contracts.contract +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext + +/** + * QQ频道BOT的bot管理器。 + * + * [QQGuildBotManager] 不允许注册相同 `appId` 的bot。 + * + * _Note: [QQGuildBotManager] 仅由内部继承实现使用,对外不稳定_ + * + * @author ForteScarlet + */ +public abstract class QQGuildBotManager : BotManager, JobBasedBotManager() { + public abstract val eventDispatcher: EventDispatcher + protected abstract val logger: Logger + protected abstract val component: QQGuildComponent + public abstract val configuration: QQGuildBotManagerConfiguration + + @OptIn(ExperimentalContracts::class) + private fun checkConfig(configuration: SerializableBotConfiguration): Boolean { + contract { + returns(true) implies (configuration is QGBotFileConfiguration) + } + return configuration is QGBotFileConfiguration + } + + override fun configurable(configuration: SerializableBotConfiguration): Boolean = checkConfig(configuration) + + /** + * 注册一个Bot的信息 + */ + override fun register(configuration: SerializableBotConfiguration): QGBot { + if (!checkConfig(configuration)) { + throw UnsupportedBotConfigurationException("Required configuration type: ${QGBotFileConfiguration::class}, but $configuration (${configuration::class})") + } + + val c: QGBotFileConfiguration = configuration + + // no config + val (appId, secret, token) = c.ticket.toTicket() + return register(appId, secret, token, c::includeConfig) + } + + /** + * 通过所需信息注册一个bot。 + * + * 注意,在配置 [ConfigurableBotConfiguration] 时, + * 如果 [coroutineContext][ConfigurableBotConfiguration.coroutineContext] 中 + * 存在自定义的 [Job][kotlinx.coroutines.Job],那么 `Application` 中的Job + * 则会作为一个 `Root Job` 而不是 `Parent Job` 使用。 + * + * `Root Job` 仅会在其自身完成或关闭的时候**通知**相关联的子类使它们关闭, + * 但不会有硬性关联,这种通知是通过 [Job.invokeOnCompletion][kotlinx.coroutines.Job.invokeOnCompletion] 实现的, + * 参考 [Job.linkTo]。 + * + * 如果 [coroutineContext][ConfigurableBotConfiguration.coroutineContext] + * 中不存在自定义的Job,则会直接使用 [QQGuildBotManager][love.forte.simbot.component.qguild.QQGuildBotManager] + * 内的 [Job] 作为 parent Job 。 + * + */ + public abstract fun register( + appId: String, + secret: String, + token: String, + block: QGBotComponentConfiguration.() -> Unit = {}, + ): QGBot + + /** + * 通过所需信息注册一个bot。 + * + * 注意,在配置 [ConfigurableBotConfiguration] 时, + * 如果 [coroutineContext][ConfigurableBotConfiguration.coroutineContext] 中 + * 存在自定义的 [Job][kotlinx.coroutines.Job],那么 `Application` 中的Job + * 则会作为一个 `Root Job` 而不是 `Parent Job` 使用。 + * + * `Root Job` 仅会在其自身完成或关闭的时候**通知**相关联的子类使它们关闭, + * 但不会有硬性关联,这种通知是通过 [Job.invokeOnCompletion][kotlinx.coroutines.Job.invokeOnCompletion] 实现的, + * 参考 [Job.linkTo]。 + * + * 如果 [coroutineContext][ConfigurableBotConfiguration.coroutineContext] + * 中不存在自定义的Job,则会直接使用 + * [QQGuildBotManager][love.forte.simbot.component.qguild.QQGuildBotManager] + * 内的 [Job] 作为 parent Job 。 + * + */ + public fun register( + appId: String, + secret: String, + token: String + ): QGBot = register(appId, secret, token) {} + + /** + * 通过所需信息注册一个bot。 + * + * 注意,在配置 [ConfigurableBotConfiguration] 时, + * 如果 [coroutineContext][ConfigurableBotConfiguration.coroutineContext] 中 + * 存在自定义的 [Job][kotlinx.coroutines.Job],那么 `Application` 中的Job + * 则会作为一个 `Root Job` 而不是 `Parent Job` 使用。 + * + * `Root Job` 仅会在其自身完成或关闭的时候**通知**相关联的子类使它们关闭, + * 但不会有硬性关联,这种通知是通过 [Job.invokeOnCompletion][kotlinx.coroutines.Job.invokeOnCompletion] 实现的, + * 参考 [Job.linkTo]。 + * + * 如果 [coroutineContext][ConfigurableBotConfiguration.coroutineContext] + * 中不存在自定义的Job,则会直接使用 + * [QQGuildBotManager][love.forte.simbot.component.qguild.QQGuildBotManager] + * 内的 [Job] 作为 parent Job 。 + * + */ + public abstract fun register( + ticket: Bot.Ticket, + block: QGBotComponentConfiguration.() -> Unit = {}, + ): QGBot + + /** + * 通过所需信息注册一个bot。 + * + * 注意,在配置 [ConfigurableBotConfiguration] 时, + * 如果 [coroutineContext][ConfigurableBotConfiguration.coroutineContext] 中 + * 存在自定义的 [Job][kotlinx.coroutines.Job],那么 `Application` 中的Job + * 则会作为一个 `Root Job` 而不是 `Parent Job` 使用。 + * + * `Root Job` 仅会在其自身完成或关闭的时候**通知**相关联的子类使它们关闭, + * 但不会有硬性关联,这种通知是通过 [Job.invokeOnCompletion][kotlinx.coroutines.Job.invokeOnCompletion] 实现的, + * 参考 [Job.linkTo]。 + * + * 如果 [coroutineContext][ConfigurableBotConfiguration.coroutineContext] + * 中不存在自定义的Job,则会直接使用 + * [QQGuildBotManager][love.forte.simbot.component.qguild.QQGuildBotManager] + * 内的 [Job] 作为 parent Job 。 + * + */ + public fun register(ticket: Bot.Ticket): QGBot = register(ticket) {} + + /** + * [QQGuildBotManager] 的注册工厂。 + */ + public companion object Factory : PluginFactory { + override val key: PluginFactory.Key = object : PluginFactory.Key {} + + override fun create( + context: PluginConfigureContext, + configurer: ConfigurerFunction + ): QQGuildBotManager { + val component = context.components.find() + ?: throw NoSuchComponentException("typeof ${QQGuildComponent::class}") + + val appContext = context.applicationConfiguration.coroutineContext + val configuration = QQGuildBotManagerConfiguration() + // init context + configuration.coroutineContext = appContext.minusKey(Job) + configuration.invokeBy(configurer) + // config Job + val appJob = appContext[Job] + var job = configuration.coroutineContext[Job] + if (job == null) { + job = SupervisorJob(appJob) + configuration.coroutineContext += job + } else if (appJob != null) { + job.linkTo(appJob) + } + + return QQGuildBotManagerImpl( + eventDispatcher = context.eventDispatcher, + configuration = configuration, + component = component, + job = job, + coroutineContext = configuration.coroutineContext.minusKey(Job) + ) + } + } +} + +/** + * @suppress + */ +@DslMarker +@Retention(AnnotationRetention.BINARY) +public annotation class QGBotManagerConfigurationDsl + + +/** + * [QQGuildBotManager] 使用的配置类描述。 + */ +public class QQGuildBotManagerConfiguration { + /** + * 当前 botManager 使用的协程上下文。 + * 初始值为 [ApplicationConfiguration] 所提供的上下文。 + * + * 此上下文会用作每一个构建出来的 [QGBot] 的基础上下文。 + * 如果其中包括 [Job],则会使用此 [Job] 作为 parent Job. + * 如果 [ApplicationConfiguration] 也提供了 Job, + * 则会将产生的 Job [链接][Job.linkTo] 到 application 的 Job 上。 + * + */ + public var coroutineContext: CoroutineContext = EmptyCoroutineContext + + /** + * 对所有bot的配置信息进行统一处理的函数。 + * 如果直接对 [botConfigure] 进行赋值,则会覆盖之前的配置。 + * + * 如果想要以 _追加_ 的形式进行配置,考虑使用同名的函数 [botConfigure]. + * + * e.g. + * ```kotlin + * botConfigure = { appId, secret, token -> /* ... */ } + * ``` + */ + public var botConfigure: BotConfiguration.(appId: String, secret: String, token: String) -> Unit = + { _, _, _ -> } + + /** + * 对所有bot的配置信息进行统一处理的函数。 + * 使用当前函数会保留之前的配置。 + * + * 如果想直接覆盖配置并抛弃之前的配置,考虑使用同名的属性 [botConfigure]. + * + * e.g. + * ```kotlin + * botConfigure { appId, secret, token -> /* ... */ } + * ``` + */ + @QGBotManagerConfigurationDsl + public fun botConfigure(configure: BotConfiguration.(appId: String, secret: String, token: String) -> Unit) { + botConfigure.also { old -> + botConfigure = { appId, secret, token -> + old(appId, secret, token) + configure(appId, secret, token) + } + } + } +} + +/** + * Provide [QQGuildBotManager.Factory] to SPI + */ +public class QQGuildBotManagerProvider : PluginFactoryProvider { + override fun configurersLoader(): Sequence? = + loadQQGuildBotManagerConfigurers() + + override fun provide(): PluginFactory<*, QQGuildBotManagerConfiguration> = QQGuildBotManager +} + +/** + * 应用于 [QQGuildBotManagerProvider.configurersLoader] 的额外配置器。 + * + */ +public fun interface QQGuildBotManagerFactoryConfigurerProvider : + PluginFactoryConfigurerProvider + + +internal expect fun loadQQGuildBotManagerConfigurers(): Sequence? diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/CacheStrategyConfig.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/CacheStrategyConfig.kt similarity index 92% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/CacheStrategyConfig.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/CacheStrategyConfig.kt index 3b9fe57f..12fc07de 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/CacheStrategyConfig.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/CacheStrategyConfig.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,11 +15,11 @@ * If not, see . */ -package love.forte.simbot.component.qguild.config +package love.forte.simbot.component.qguild.bot.config import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import love.forte.simbot.utils.item.Items +import love.forte.simbot.common.collectable.Collectable /** @@ -104,7 +104,7 @@ public data class CacheConfig( * * 传递性缓存即用于处理此种情况,当一个存在从属关系的子信息可以使用其所属的来源信息时,则直接将其传递而非再次查询。 * - * 传递性缓存不会应用于任何序列/列表本身。因此无论何时而获取到的 [Items] 类型始终都会是全新的类型。(不过其中的元素可能会携带可传递缓存) + * 传递性缓存不会应用于任何序列/列表本身。因此无论何时而获取到的 [Collectable] 类型始终都会是全新的类型。(不过其中的元素可能会携带可传递缓存) * * 传递性缓存涉及到 `channel` 、`channel category` 、 `member` 、 `role`, * 一般来说传递的是 `guild`,少部分API会传递其他内容,例如 `memberRole` 可能会传递 `channel` 。 diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/IntentsConfig.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/IntentsConfig.kt similarity index 89% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/IntentsConfig.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/IntentsConfig.kt index faba1791..a0de1a7e 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/IntentsConfig.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/IntentsConfig.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,11 +15,12 @@ * If not, see . */ -package love.forte.simbot.component.qguild.config +package love.forte.simbot.component.qguild.bot.config import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import love.forte.simbot.qguild.event.EventIntents +import love.forte.simbot.qguild.event.EventIntentsInstances import love.forte.simbot.qguild.event.Intents /** @@ -76,12 +77,11 @@ public sealed class IntentsConfig { } val intentsMap = mutableMapOf() - EventIntents::class.sealedSubclasses + EventIntentsInstances .asSequence() - .filter { it.simpleName != null && it.objectInstance != null } - .forEach { - val intentsValue = it.objectInstance!!.intentsValue - val simpleName = it.simpleName!! + .forEach { instance -> + val simpleName = instance::class.simpleName ?: return@forEach + val intentsValue = instance.intentsValue intentsMap[simpleName.toSnakeCase()] = intentsValue intentsMap[simpleName] = intentsValue diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/QGBotComponentConfiguration.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotComponentConfiguration.kt similarity index 85% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/QGBotComponentConfiguration.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotComponentConfiguration.kt index dec66a2d..f2c10e44 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/QGBotComponentConfiguration.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotComponentConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,12 +15,11 @@ * If not, see . */ -package love.forte.simbot.component.qguild.config - -import love.forte.simbot.qguild.Bot -import love.forte.simbot.qguild.BotConfiguration -import love.forte.simbot.qguild.ConfigurableBotConfiguration +package love.forte.simbot.component.qguild.bot.config +import love.forte.simbot.qguild.stdlib.Bot +import love.forte.simbot.qguild.stdlib.BotConfiguration +import love.forte.simbot.qguild.stdlib.ConfigurableBotConfiguration /** * @@ -28,6 +27,7 @@ import love.forte.simbot.qguild.ConfigurableBotConfiguration * * @author ForteScarlet */ +@Suppress("MemberVisibilityCanBePrivate") public class QGBotComponentConfiguration { /** * 得到 [QQGuild Bot][Bot] 使用的配置。 diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/QGBotFileConfiguration.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotFileConfiguration.kt similarity index 90% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/QGBotFileConfiguration.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotFileConfiguration.kt index b55d4124..01f38608 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/QGBotFileConfiguration.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotFileConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,19 +15,20 @@ * If not, see . */ -package love.forte.simbot.component.qguild.config +package love.forte.simbot.component.qguild.bot.config import io.ktor.http.* import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.modules.SerializersModule -import love.forte.simbot.qguild.Bot -import love.forte.simbot.qguild.BotConfiguration +import love.forte.simbot.bot.SerializableBotConfiguration +import love.forte.simbot.bot.configuration.DispatcherConfiguration +import love.forte.simbot.component.qguild.QQGuildComponent import love.forte.simbot.qguild.QQGuild import love.forte.simbot.qguild.event.EventIntents import love.forte.simbot.qguild.event.Signal +import love.forte.simbot.qguild.stdlib.Bot +import love.forte.simbot.qguild.stdlib.BotConfiguration -// 仅用于文件序列化 /** * 标记一个类型为**仅用于配置序列化**的类型。 * @@ -58,6 +59,7 @@ public annotation class UsedOnlyForConfigSerialization */ @Suppress("MemberVisibilityCanBePrivate") @Serializable +@SerialName(QQGuildComponent.ID_VALUE) @UsedOnlyForConfigSerialization public data class QGBotFileConfiguration( /** @@ -69,7 +71,7 @@ public data class QGBotFileConfiguration( * 其他配置信息。 */ val config: Config? = null -) { +) : SerializableBotConfiguration() { /** * bot票据信息。是与 [Bot.Ticket] 相对应的映射类 @@ -231,20 +233,11 @@ public data class QGBotFileConfiguration( @SerialName("cache") public val cacheConfig: CacheConfig? = DEFAULT.cacheConfig, /** - * 事件缓冲区容量。 + * 调度器相关配置。 * - * ```json - * { - * "config": { - * "eventBufferCapacity": 64 - * } - * } - * ``` - * - * @see BotConfiguration.eventBufferCapacity + * @see DispatcherConfiguration */ - public val eventBufferCapacity: Int? = null - + @SerialName("dispatcher") public val dispatcherConfiguration: DispatcherConfiguration? = null, ) { public companion object { internal const val SERVER_URL_SANDBOX_VALUE: String = "SANDBOX" @@ -302,19 +295,13 @@ public data class QGBotFileConfiguration( configuration.apiHttpSocketTimeoutMillis = timeout.apiHttpSocketTimeoutMillis } - eventBufferCapacity?.also { - configuration.eventBufferCapacity = it + dispatcherConfiguration?.dispatcher?.also { dispatcher -> + configuration.coroutineContext += dispatcher } - } } } - public companion object { - internal val serializersModule = SerializersModule { - include(TicketConfiguration.serializersModule) - } - - } + public companion object } diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/ShardConfig.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/ShardConfig.kt similarity index 70% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/ShardConfig.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/ShardConfig.kt index 281ad543..d0e72a9d 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/ShardConfig.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/ShardConfig.kt @@ -1,16 +1,21 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. If not, see . + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . */ -package love.forte.simbot.component.qguild.config +package love.forte.simbot.component.qguild.bot.config import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/TicketConfiguration.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.kt similarity index 91% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/TicketConfiguration.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.kt index 8fd16ff7..8413fadc 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/config/TicketConfiguration.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,11 +15,10 @@ * If not, see . */ -package love.forte.simbot.component.qguild.config +package love.forte.simbot.component.qguild.bot.config import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.modules.SerializersModule /** @@ -120,8 +119,8 @@ public sealed class TicketConfiguration { * } * ``` * - * [Env] 会首先尝试获取 JVM 参数,即运行时的 `-Dxxx=xxx` (也就是 [System.getProperty]), - * 当不存在时会尝试通过环境变量获取(即 [System.getenv])。 + * [Env] 会首先尝试获取 JVM 参数,即运行时的 `-Dxxx=xxx` (也就是 `System.getProperty`), + * 当不存在时会尝试通过环境变量获取(即 `System.getenv`)。 * * ## 原始输入 * @@ -203,8 +202,8 @@ public sealed class TicketConfiguration { return prop.substringAfter(PLAIN_PREFIX, missingDelimiterValue = "") } - return System.getProperty(prop) - ?: System.getenv(prop) + return systemProperty(prop) + ?: systemEnv(prop) ?: if (plain) return prop else throw IllegalStateException("Cannot find property '$prop' for ticket '$key' in JVM or environment variables.") } @@ -216,9 +215,8 @@ public sealed class TicketConfiguration { } - public companion object { - internal val serializersModule = SerializersModule { - polymorphicDefaultDeserializer(TicketConfiguration::class) { Plain.serializer() } - } - } + public companion object } + +internal expect fun systemProperty(name: String): String? +internal expect fun systemEnv(name: String): String? diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGCategory.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGCategory.kt new file mode 100644 index 00000000..5389a3d2 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGCategory.kt @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.channel + +import love.forte.simbot.common.id.ID +import love.forte.simbot.definition.Category +import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.qguild.model.Channel +import love.forte.simbot.qguild.model.ChannelType +import love.forte.simbot.suspendrunner.ST +import love.forte.simbot.suspendrunner.STP +import love.forte.simbot.qguild.model.Channel as QGSourceChannel + + +/** + * 当一个频道的 [QGSourceChannel.type] 的值等于 [ChannelType.CATEGORY] 时, + * 此频道代表为一个分组。 + * + * [QGCategory] 是一个**仅存在ID**的 QQ频道子频道分组实现。QQ频道对于子频道分组类型的变更不会推送事件, + * 因此无法内建缓存,而如果每次事件都要实时查询channel的分组则可能有些多余 —— 毕竟分组可能并不是一个高频使用的对象。 + * + * 因此在 [QGTextChannel.category] 中我们仅提供 [QGCategory] 类型来直接提供分组ID, + * 并在有需要的时候通过 [resolveToChannel] 查询并获取真正的对象实例。 + * + * [QGCategory] 中 [id] 为子频道分组的ID,[name] 由于需要通过API查询, + * 此处将直接返回 [id] 的字符串值。 + * + * 通过 [QGGuild.categories] 或 [QGGuild.category] 获取的结果均为实时查询结果, + * 它们是具体的 [QGCategory] 类型,其中已经包含了具体信息。 + * + * @see QGCategory + */ +public interface QGCategory : Category { + + /** + * 当前子频道分组ID + */ + override val id: ID + + /** + * 当前子频道分组ID。分组信息未初始化时,值同 [id]。 + * 如果需要获取真正的名称,判断当前类型是否为 [QGCategory] + * 或直接通过 [resolveToChannel] 实时查询新的结果。 + */ + @STP + override suspend fun name(): String? + + /** + * 根据当前ID**初始化**并得到一个具体的分组频道 [QGNonTextChannel] 对象实例。 + * + * @throws NoSuchElementException 当未查询到结果(例如查询时分组已被删除) + * @throws QQGuildApiException api查询期间得到的任何异常 + */ + @ST + public suspend fun resolveToChannel(): QGCategoryChannel + +} + +/** + * 一个用来表示“分组”的频道类型。 + * 通过 [QGCategory.resolveToChannel] 得到。 + * + */ +public interface QGCategoryChannel : QGNonTextChannel { + override val source: Channel + + /** + * 分组类型的子频道的分组就是自己所代表的分组。 + */ + override val category: QGCategory +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGChannel.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGChannel.kt new file mode 100644 index 00000000..d1481e18 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGChannel.kt @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.channel + +import kotlinx.coroutines.CoroutineScope +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.component.qguild.QGObjectiveContainer +import love.forte.simbot.definition.* +import love.forte.simbot.qguild.model.ChannelType +import love.forte.simbot.qguild.model.Channel as QGSourceChannel + +/** + * 一个QQ频道中的子频道 [Channel] 类型, + * 提供部分来自 [source channel][QGSourceChannel] 的属性获取。 + * + * [QGChannel] 的主要类型有两个: + * - [QGTextChannel] + * - [QGNonTextChannel] + * + * 这两个类型分别代表了它们的类型是否属于 [ChannelType.TEXT]。 + * + * 而 [QGNonTextChannel] 下可能会有更多细分类型,详情参阅其文档注释的说明。 + * + * @author ForteScarlet + */ +public interface QGChannel : CoroutineScope, QGObjectiveContainer, Channel { + /** + * 原始的子频道信息 + */ + override val source: QGSourceChannel + + /** + * 子频道ID + */ + override val id: ID + get() = source.id.ID + + /** + * 子频道名称 + */ + override val name: String + get() = source.name + + /** + * 子频道分组**的ID**。 + * + * 子频道分组ID实例 [QGCategory] 是一个仅包含 `id` 信息的未初始化实例, + * 其 [id][QGCategory.id] 和 [name][QGCategory.name] + * 的值都是 [source.parentId][love.forte.simbot.qguild.model.Channel.parentId] 的值, + * 即分组ID的字符串值。 + * + * 如果希望获取完整信息,使用 [QGCategory.resolveToChannel]. + * + * @see QGCategory + */ + override val category: QGCategory +} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGForumChannel.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGForumChannel.kt similarity index 69% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGForumChannel.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGForumChannel.kt index bbd19308..d7fd0efc 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGForumChannel.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGForumChannel.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,22 +15,16 @@ * If not, see . */ -package love.forte.simbot.component.qguild.forum +package love.forte.simbot.component.qguild.channel -import love.forte.simbot.Api4J -import love.forte.simbot.ID -import love.forte.simbot.InternalSimbotApi -import love.forte.simbot.component.qguild.JST -import love.forte.simbot.component.qguild.QGChannel -import love.forte.simbot.component.qguild.QGGuild -import love.forte.simbot.component.qguild.QGNonTextChannel +import love.forte.simbot.common.collectable.Collectable +import love.forte.simbot.common.id.ID +import love.forte.simbot.component.qguild.forum.QGThread +import love.forte.simbot.component.qguild.forum.QGThreadCreator import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.api.forum.ThreadPublishResult import love.forte.simbot.qguild.model.ChannelType -import love.forte.simbot.utils.item.Items -import love.forte.simbot.utils.runInAsync -import love.forte.simbot.utils.runInNoScopeBlocking -import java.util.concurrent.CompletableFuture +import love.forte.simbot.suspendrunner.ST import love.forte.simbot.qguild.model.Channel as QGSourceChannel /** @@ -48,43 +42,24 @@ import love.forte.simbot.qguild.model.Channel as QGSourceChannel * @author ForteScarlet */ public interface QGForumChannel : QGNonTextChannel { - /** * 表示此帖子频道的源频道。 */ override val source: QGSourceChannel - @JvmSynthetic - override suspend fun guild(): QGGuild - - /** - * @suppress for hidden warning - */ - @Api4J - override val guild: QGGuild - get() = runInNoScopeBlocking { guild() } - - /** - * @suppress for hidden warning - */ - @OptIn(InternalSimbotApi::class) - @Api4J - override val guildAsync: CompletableFuture - get() = runInAsync(this) { guild() } - /** * 查询当前子频道中的所有 [主题帖][QGThread] * * @throws QQGuildApiException api请求异常 */ - public val threads: Items + public val threads: Collectable /** * 根据ID查询 [主题帖][QGThread] * * @throws QQGuildApiException api请求异常 */ - @JST(blockingBaseName = "getThread", blockingSuffix = "", asyncBaseName = "getThread") + @ST(blockingBaseName = "getThread", blockingSuffix = "", asyncBaseName = "getThread") public suspend fun thread(id: ID): QGThread? /** diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGTextChannel.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGTextChannel.kt new file mode 100644 index 00000000..6d73cf25 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGTextChannel.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.channel + +import io.ktor.client.* +import io.ktor.client.request.* +import love.forte.simbot.component.qguild.message.QGMessageReceipt +import love.forte.simbot.definition.ChatChannel +import love.forte.simbot.message.Message +import love.forte.simbot.message.MessageContent +import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.qguild.api.MessageAuditedException +import love.forte.simbot.qguild.model.ChannelType +import love.forte.simbot.suspendrunner.ST + +/** + * QQ频道中的 **文字子频道**。 + * + * [QGTextChannel] 是当 [source.type][love.forte.simbot.qguild.model.Channel.type] 为 [ChannelType.TEXT] 类型时的表现。 + * + * + * @see QGChannel + * @see QGGuild + * @author ForteScarlet + */ +public interface QGTextChannel : QGChannel, ChatChannel { + override val name: String + get() = super.name + + override val category: QGCategory + + /** + * 向子频道发送消息。此频道需要为文字子频道,否则会产生异常。 + * + * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 + * @throws QQGuildApiException 请求异常,例如无权限 + * @throws UnsupportedOperationException 如果当前子频道类型不是文字子频道 + * @throws MessageAuditedException 当响应状态为表示消息审核的 `304023`、`304024` 时 + */ + @ST + override suspend fun send(message: Message): QGMessageReceipt + + /** + * 向子频道发送消息。此频道需要为文字子频道,否则会产生异常。 + * + * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 + * @throws QQGuildApiException 请求异常,例如无权限 + * @throws UnsupportedOperationException 如果当前子频道类型不是文字子频道 + * @throws MessageAuditedException 当响应状态为表示消息审核的 `304023`、`304024` 时 + */ + @ST + override suspend fun send(text: String): QGMessageReceipt + + /** + * 向子频道发送消息。此频道需要为文字子频道,否则会产生异常。 + * + * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 + * @throws QQGuildApiException 请求异常,例如无权限 + * @throws UnsupportedOperationException 如果当前子频道类型不是文字子频道 + * @throws MessageAuditedException 当响应状态为表示消息审核的 `304023`、`304024` 时 + * + */ + @ST + override suspend fun send(messageContent: MessageContent): QGMessageReceipt +} + + +/** + * QQ频道中的 **非文字子频道**。 + * + * 与 [QGTextChannel] 类型相对,[QGNonTextChannel] + * 用于统一表示那些不支持消息发送的子频道类型。 + * + * [QGNonTextChannel] 可能会有进一步的子类型实现用来提供更多功能或用以描述它们的类型, + * 例如: + * - [QGCategoryChannel] + * - [QGForumChannel] + * + * @see QGTextChannel + * @see QGChannel + * @see QGCategoryChannel + * @see QGForumChannel + * + */ +public interface QGNonTextChannel : QGChannel diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt new file mode 100644 index 00000000..820076f6 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.event + +import love.forte.simbot.annotations.ExperimentalSimbotAPI +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.common.time.Timestamp +import love.forte.simbot.component.qguild.channel.QGChannel +import love.forte.simbot.component.qguild.channel.QGTextChannel +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.event.* +import love.forte.simbot.qguild.event.EventChannel +import love.forte.simbot.suspendrunner.STP +import love.forte.simbot.qguild.event.EventChannel as QGSourceEventChannel + +/** + * + * 子频道相关的事件。 + * + * [QGChannelEvent] 主要用于为子类型提供一个基础类型,其本身没有过多的事件类型约束。 + * + * 子频道的增减视为频道服务器变更事件 [OrganizationChangeEvent], + * 而子频道本身的修改事件则视为子频道变更事件 [ChannelEvent] + [ChangeEvent]。 + * + * @see QGChannelCreateEvent + * @see QGChannelUpdateEvent + * @see QGChannelDeleteEvent + * @see QGChannelCategoryCreateEvent + * @see QGChannelCategoryUpdateEvent + * @see QGChannelCategoryDeleteEvent + * + * @author ForteScarlet + */ +@STP +public sealed class QGChannelEvent : QGBotEvent() { + /** + * 标准库中子频道相关事件得到的事件本体。 + */ + abstract override val sourceEventEntity: EventChannel + + /** + * 操作者ID + */ + public val operatorId: ID get() = sourceEventEntity.opUserId.ID +} + + +/** + * 子频道被创建事件。是一个频道服务器变更事件。 + */ +@STP +public abstract class QGChannelCreateEvent : QGChannelEvent(), OrganizationChangeEvent { + @OptIn(ExperimentalSimbotAPI::class) + override val time: Timestamp = Timestamp.now() + + /** + * 发生变更的频道服务器。 + */ + abstract override suspend fun content(): QGGuild + + /** + * 新增的频道。 + */ + public abstract suspend fun channel(): QGChannel +} + +/** + * 子频道信息变更事件。 + * 事件通知即已完成变更。 + */ +@STP +public abstract class QGChannelUpdateEvent : QGChannelEvent(), ChangeEvent, ChannelEvent { + @OptIn(ExperimentalSimbotAPI::class) + override val time: Timestamp = Timestamp.now() + + /** + * 发生变更的频道。 + */ + abstract override suspend fun content(): QGTextChannel + + /** + * 发生的所在频道服务器 + */ + abstract override suspend fun guild(): QGGuild +} + +/** + * 子频道被删除 + * + * 当收到此事件时,[content] 已经被删除。 + */ +@STP +public abstract class QGChannelDeleteEvent : QGChannelEvent(), OrganizationChangeEvent { + @OptIn(ExperimentalSimbotAPI::class) + override val time: Timestamp = Timestamp.now() + + /** + * 发生变更的频道服务器。 + */ + abstract override suspend fun content(): QGGuild + + /** + * 被删除的频道。 + */ + public abstract suspend fun channel(): QGChannel +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGEvent.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGEvent.kt new file mode 100644 index 00000000..9fb45e8e --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGEvent.kt @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.event + +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.time.Timestamp +import love.forte.simbot.component.qguild.bot.QGBot +import love.forte.simbot.event.BotEvent +import love.forte.simbot.event.Event + +/** + * + * QQ频道bot的事件总类。 + * + * @param T 此类型代表其真正事件所得到的结果。 + * + * @author ForteScarlet + */ +public abstract class QGEvent : Event { + /** + * 事件ID。 + */ + abstract override val id: ID + + /** + * 接收到事件的时间。 + */ + abstract override val time: Timestamp + + /** + * 真正的原始事件所得到的事件实体。 + */ + public abstract val sourceEventEntity: T + + /** + * 接收到的事件的原始JSON字符串 + */ + public abstract val sourceEventRaw: String +} + +/** + * 与 [QGBot] 相关的 QQ频道 [BotEvent] 子类型。 + * + */ +public abstract class QGBotEvent : QGEvent(), BotEvent { + abstract override val bot: QGBot +} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt similarity index 62% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt index 50bad99c..8537bb79 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,24 +17,20 @@ package love.forte.simbot.component.qguild.event -import love.forte.simbot.ID -import love.forte.simbot.JSTP -import love.forte.simbot.component.qguild.QGGuild -import love.forte.simbot.component.qguild.QGMember -import love.forte.simbot.component.qguild.forum.QGForumChannel +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.component.qguild.channel.QGForumChannel import love.forte.simbot.component.qguild.forum.QGPost import love.forte.simbot.component.qguild.forum.QGReply import love.forte.simbot.component.qguild.forum.QGThread -import love.forte.simbot.definition.ChannelInfoContainer -import love.forte.simbot.definition.GuildInfoContainer -import love.forte.simbot.event.BaseEvent -import love.forte.simbot.event.BaseEventKey -import love.forte.simbot.event.Event -import love.forte.simbot.message.doSafeCast +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.guild.QGMember +import love.forte.simbot.event.ChannelEvent import love.forte.simbot.qguild.PrivateDomainOnly import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.event.* import love.forte.simbot.qguild.model.forum.* +import love.forte.simbot.suspendrunner.STP /** @@ -46,30 +42,32 @@ import love.forte.simbot.qguild.model.forum.* * * @see ForumDispatch */ -@BaseEvent -@JSTP +@STP @PrivateDomainOnly -public abstract class QGForumEvent : QGEvent(), GuildInfoContainer, ChannelInfoContainer { +public abstract class QGForumEvent : QGBotEvent(), ChannelEvent { /** * 频道ID * * @see OpenForumEventData.guildId */ - public open val guildId: ID get() = sourceEventEntity.guildId.ID + public open val guildId: ID + get() = sourceEventEntity.guildId.ID /** * 子频道ID * * @see OpenForumEventData.channelId */ - public open val channelId: ID get() = sourceEventEntity.channelId.ID + public open val channelId: ID + get() = sourceEventEntity.channelId.ID /** * 发布人ID * * @see OpenForumEventData.authorId */ - public open val authorId: ID get() = sourceEventEntity.authorId.ID + public open val authorId: ID + get() = sourceEventEntity.authorId.ID /** * 得到本次事件 [guildId] 对应的频道。 @@ -78,7 +76,7 @@ public abstract class QGForumEvent : QGEvent(), GuildInfoContai * @throws NoSuchElementException 对应子频道已不存在 */ override suspend fun guild(): QGGuild = - bot.guild(guildId) ?: throw NoSuchElementException("guild(id=$guildId)") + bot.guildRelation.guild(guildId) ?: throw NoSuchElementException("guild(id=$guildId)") /** * 得到本次事件 [channelId] 对应的论坛子频道。 @@ -87,7 +85,7 @@ public abstract class QGForumEvent : QGEvent(), GuildInfoContai * @throws IllegalStateException 对应子频道已经不再是论坛类型 * @throws NoSuchElementException 对应子频道已不存在 */ - abstract override suspend fun channel(): QGForumChannel + abstract override suspend fun content(): QGForumChannel /** * 得到本次事件 [authorId] 对应的频道中成员。 @@ -96,12 +94,6 @@ public abstract class QGForumEvent : QGEvent(), GuildInfoContai * @throws NoSuchElementException 对应成员已不存在 */ public abstract suspend fun author(): QGMember - - abstract override val key: Event.Key - - public companion object Key : BaseEventKey("qg.forum", QGEvent) { - override fun safeCast(value: Any): QGForumEvent? = doSafeCast(value) - } } /** @@ -119,12 +111,6 @@ public abstract class QGForumThreadEvent : QGForumEvent() { * 得到此事件中基于 [sourceEventEntity] 的 [QGThread]。 */ public abstract val thread: QGThread - - abstract override val key: Event.Key - - public companion object Key : BaseEventKey("qg.forum_thread", QGForumEvent) { - override fun safeCast(value: Any): QGForumThreadEvent? = doSafeCast(value) - } } /** @@ -138,12 +124,6 @@ public abstract class QGForumThreadEvent : QGForumEvent() { public abstract class QGForumThreadCreateEvent : QGForumThreadEvent() { override fun toString(): String = "QGForumThreadCreateEvent(sourceEventEntity=$sourceEventEntity)" - - override val key: Event.Key get() = Key - - public companion object Key : BaseEventKey("qg.forum_thread_create", QGForumThreadEvent) { - override fun safeCast(value: Any): QGForumThreadCreateEvent? = doSafeCast(value) - } } /** @@ -157,12 +137,6 @@ public abstract class QGForumThreadCreateEvent : QGForumThreadEvent() { public abstract class QGForumThreadUpdateEvent : QGForumThreadEvent() { override fun toString(): String = "QGForumThreadUpdateEvent(sourceEventEntity=$sourceEventEntity)" - - override val key: Event.Key get() = Key - - public companion object Key : BaseEventKey("qg.forum_thread_update", QGForumThreadEvent) { - override fun safeCast(value: Any): QGForumThreadUpdateEvent? = doSafeCast(value) - } } /** @@ -176,12 +150,6 @@ public abstract class QGForumThreadUpdateEvent : QGForumThreadEvent() { public abstract class QGForumThreadDeleteEvent : QGForumThreadEvent() { override fun toString(): String = "QGForumThreadDeleteEvent(sourceEventEntity=$sourceEventEntity)" - - override val key: Event.Key get() = Key - - public companion object Key : BaseEventKey("qg.forum_thread_delete", QGForumThreadEvent) { - override fun safeCast(value: Any): QGForumThreadDeleteEvent? = doSafeCast(value) - } } /** @@ -199,12 +167,6 @@ public abstract class QGForumPostEvent : QGForumEvent() { * 得到此事件中基于 [sourceEventEntity] 的 [QGPost]。 */ public abstract val post: QGPost - - abstract override val key: Event.Key - - public companion object Key : BaseEventKey("qg.forum_post", QGForumEvent) { - override fun safeCast(value: Any): QGForumPostEvent? = doSafeCast(value) - } } /** @@ -218,12 +180,6 @@ public abstract class QGForumPostEvent : QGForumEvent() { public abstract class QGForumPostCreateEvent : QGForumPostEvent() { override fun toString(): String = "QGForumPostCreateEvent(sourceEventEntity=$sourceEventEntity)" - - override val key: Event.Key get() = Key - - public companion object Key : BaseEventKey("qg.forum_post_create", QGForumPostEvent) { - override fun safeCast(value: Any): QGForumPostCreateEvent? = doSafeCast(value) - } } /** @@ -237,12 +193,6 @@ public abstract class QGForumPostCreateEvent : QGForumPostEvent() { public abstract class QGForumPostDeleteEvent : QGForumPostEvent() { override fun toString(): String = "QGForumPostDeleteEvent(sourceEventEntity=$sourceEventEntity)" - - override val key: Event.Key get() = Key - - public companion object Key : BaseEventKey("qg.forum_post_delete", QGForumPostEvent) { - override fun safeCast(value: Any): QGForumPostDeleteEvent? = doSafeCast(value) - } } @@ -261,12 +211,6 @@ public abstract class QGForumReplyEvent : QGForumEvent() { * 得到此事件中基于 [sourceEventEntity] 的 [QGReply]。 */ public abstract val post: QGReply - - abstract override val key: Event.Key - - public companion object Key : BaseEventKey("qg.forum_reply", QGForumEvent) { - override fun safeCast(value: Any): QGForumReplyEvent? = doSafeCast(value) - } } /** @@ -280,12 +224,6 @@ public abstract class QGForumReplyEvent : QGForumEvent() { public abstract class QGForumReplyCreateEvent : QGForumReplyEvent() { override fun toString(): String = "QGForumReplyCreateEvent(sourceEventEntity=$sourceEventEntity)" - - override val key: Event.Key get() = Key - - public companion object Key : BaseEventKey("qg.forum_reply_create", QGForumReplyEvent) { - override fun safeCast(value: Any): QGForumReplyCreateEvent? = doSafeCast(value) - } } /** @@ -299,12 +237,6 @@ public abstract class QGForumReplyCreateEvent : QGForumReplyEvent() { public abstract class QGForumReplyDeleteEvent : QGForumReplyEvent() { override fun toString(): String = "QGForumReplyDeleteEvent(sourceEventEntity=$sourceEventEntity)" - - override val key: Event.Key get() = Key - - public companion object Key : BaseEventKey("qg.forum_reply_delete", QGForumReplyEvent) { - override fun safeCast(value: Any): QGForumReplyDeleteEvent? = doSafeCast(value) - } } /** @@ -324,12 +256,4 @@ public abstract class QGForumPublishAuditResultEvent : QGForumEvent() { override fun toString(): String = "QGForumPublishAuditResultEvent(sourceEventEntity=$sourceEventEntity)" - - override val key: Event.Key get() = Key - - public companion object Key : - BaseEventKey("qg.forum_publish_audit_result", QGForumEvent) { - override fun safeCast(value: Any): QGForumPublishAuditResultEvent? = doSafeCast(value) - } - } diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildEvents.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildEvents.kt new file mode 100644 index 00000000..99b72794 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildEvents.kt @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.event + +import love.forte.simbot.annotations.ExperimentalSimbotAPI +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.common.time.Timestamp +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.event.ChangeEvent +import love.forte.simbot.event.GuildEvent +import love.forte.simbot.event.OrganizationChangeEvent +import love.forte.simbot.qguild.event.EventGuild +import love.forte.simbot.suspendrunner.STP + +/** + * + * 频道变更事件相关。 + * + * [QGGuildEvent] 主要为子类型提供基本类型,但 [QGGuildEvent] 本身没有过多事件类型约束。 + * + * [频道新增事件][QGGuildCreateEvent] 表示bot进入到了一个新的频道中。 + * + * [频道更新事件][QGGuildUpdateEvent] 表示某个频道的信息发生了更新。 + * + * [频道移除事件][QGGuildDeleteEvent] 表示bot离开了某个频道。 + * 此事件与前两者有较大差别:它不实现 [GuildEvent] —— 毕竟频道已经离开(不存在)了。 + * + * @see QGGuildCreateEvent + * @see QGGuildUpdateEvent + * @see QGGuildDeleteEvent + * + * @author ForteScarlet + */ +public sealed class QGGuildEvent : QGBotEvent() { + /** + * 事件操作者的ID + */ + public val operatorId: ID get() = sourceEventEntity.opUserId.ID +} + +/** + * 频道创建事件。 + * + * 触发时机: + * - 机器人被加入到某个频道服务器的时候 + */ +@STP +public abstract class QGGuildCreateEvent : QGGuildEvent(), GuildEvent, ChangeEvent { + @OptIn(ExperimentalSimbotAPI::class) + override val time: Timestamp = Timestamp.now() + + /** + * 创建的guild。 + */ + abstract override suspend fun content(): QGGuild +} + +/** + * 频道更新事件。 + * + * 触发时机: + * - 频道信息变更 + */ +@STP +public abstract class QGGuildUpdateEvent : QGGuildEvent(), GuildEvent, OrganizationChangeEvent { + @OptIn(ExperimentalSimbotAPI::class) + override val time: Timestamp = Timestamp.now() + + /** + * 被更新的 [QGGuild]。 + */ + abstract override suspend fun content(): QGGuild +} + +/** + * 频道删除事件。 + * + * [QGGuildDeleteEvent] 不实现 [GuildEvent],因为事件触发时已经离开频道,不存在可稳定获取的 [QGGuild] 实例。 + * 但是此事件额外提供属性 [guild], 代表获取一个**可能存在**的不稳定 [QGGuild]. 详细内容参考 [guild] 说明。 + * + * + * 触发时机: + * - 频道被解散 + * - 机器人被移除 + */ +@STP +public abstract class QGGuildDeleteEvent : QGGuildEvent(), ChangeEvent { + @OptIn(ExperimentalSimbotAPI::class) + override val time: Timestamp = Timestamp.now() + + /** + * 被删除的guild。 + * + * 获取一个**可能存在**的不稳定 [QGGuild]. + * 之所以称其为**不稳定**,因为这个对象**可能**来自于内部缓存中已经被移除的对象。 + * 当此对象能够被获取时,也已经无法使用大部分API(发送消息等) + * + * 如果希望得到较为可靠的**频道信息**,使用 [sourceEventEntity]。 + */ +// @FragileSimbotApi + public abstract val guild: QGGuild? +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildMemberEvents.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildMemberEvents.kt new file mode 100644 index 00000000..6734233d --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildMemberEvents.kt @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.event + +import love.forte.simbot.annotations.ExperimentalSimbotAPI +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.common.time.Timestamp +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.guild.QGMember +import love.forte.simbot.event.GuildMemberDecreaseEvent +import love.forte.simbot.event.GuildMemberIncreaseEvent +import love.forte.simbot.event.MemberChangeEvent +import love.forte.simbot.event.OrganizationAwareEvent +import love.forte.simbot.qguild.event.EventIntents +import love.forte.simbot.qguild.event.EventMember +import love.forte.simbot.suspendrunner.STP + +/** + * QQ频道[成员相关的事件][EventIntents.GuildMembers]。 + * + * [QGMemberEvent] 是一种父事件类型,本身不存在过多类型约束。 + * + * - [新增频道成员][QGMemberAddEvent] + * - [频道成员信息更新][QGMemberUpdateEvent] + * - [频道成员离开/移除][QGMemberRemoveEvent] + * + * @see QGMemberAddEvent + * @see QGMemberUpdateEvent + * @see QGMemberRemoveEvent + */ +public sealed class QGMemberEvent : QGEvent() { + /** + * 事件接收到的原始的用户信息。 + */ + abstract override val sourceEventEntity: EventMember +} + +/** + * 频道成员增加事件。 + * + */ +@STP +public abstract class QGMemberAddEvent : QGMemberEvent(), GuildMemberIncreaseEvent { + @OptIn(ExperimentalSimbotAPI::class) + override val time: Timestamp = Timestamp.now() + + /** + * 操作者ID。 + */ + public open val operatorId: ID + get() = sourceEventEntity.opUserId.ID + + /** + * 操作者。无权限获取、找不到(例如获取时已经离群)等情况会得到null。 + */ + public abstract suspend fun operator(): QGMember? + + /** + * 新增的频道成员。 + */ + abstract override suspend fun member(): QGMember + + /** + * 增加了频道成员的频道 + */ + abstract override suspend fun content(): QGGuild +} + +/** + * 频道成员信息更新事件。 + */ +@STP +public abstract class QGMemberUpdateEvent : QGMemberEvent(), MemberChangeEvent, OrganizationAwareEvent { + @OptIn(ExperimentalSimbotAPI::class) + override val time: Timestamp = Timestamp.now() + + /** + * 操作者ID。 + */ + public open val operatorId: ID + get() = sourceEventEntity.opUserId.ID + + /** + * 操作者。无权限获取、找不到(例如获取时已经离群)等情况会得到null。 + */ + public abstract suspend fun operator(): QGMember? + + /** + * 发生变更的成员。 + */ + abstract override suspend fun content(): QGMember + + /** + * 发生变更的成员的所属频道。 + */ + abstract override suspend fun organization(): QGGuild + + /** + * 同 [organization] + */ + public suspend fun guild(): QGGuild = organization() +} + +/** + * 频道成员离开/被移除事件。事件触发时已经被移除。 + */ +@STP +public abstract class QGMemberRemoveEvent : QGMemberEvent(), GuildMemberDecreaseEvent { + @OptIn(ExperimentalSimbotAPI::class) + override val time: Timestamp = Timestamp.now() + + /** + * 操作者ID。 + */ + public open val operatorId: ID + get() = sourceEventEntity.opUserId.ID + + /** + * 操作者。无权限获取、找不到(例如获取时已经离群)等情况会得到null。 + */ + public abstract suspend fun operator(): QGMember? + + /** + * 离去的成员。 + */ + abstract override suspend fun member(): QGMember + + /** + * 成员所处的频道 + */ + abstract override suspend fun content(): QGGuild +} + + diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGInternalBotEvent.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGInternalBotEvent.kt new file mode 100644 index 00000000..5ef9f259 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGInternalBotEvent.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.event + +import love.forte.simbot.component.qguild.bot.QGBot +import love.forte.simbot.component.qguild.bot.QQGuildBotManager +import love.forte.simbot.event.BotEvent +import love.forte.simbot.event.Event + +/** + * + * 频道组件中对于部分内部事件的统一实现接口。 + * + * @author ForteScarlet + */ +public sealed interface QGInternalBotEvent : Event + +/** + * qq频道组件中,每当 [QQGuildBotManager] 通过任意 [QQGuildBotManager.register] 注册并得到Bot实例后触发的事件。 + * + * 此事件会在注册完成后**异步**触发. + * + * @see QQGuildBotManager + * @see QQGuildBotManager.register + */ +public abstract class QGBotRegisteredEvent : QGInternalBotEvent, BotEvent { + abstract override val bot: QGBot +} + + +/** + * qq频道组件中,每当 [QGBot.start] 被执行的时候会被推送的事件。 + * 当事件被推送的时候代表此bot实际上已经完成的 `start` 的逻辑,但是[QGBot.start]会直到事件处理流程完成后才会最终返回。 + * + * @see QGBot + * @see QGBot.start + */ +public abstract class QGBotStartedEvent : QGInternalBotEvent, BotEvent { + abstract override val bot: QGBot +} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt similarity index 63% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt index 1ed335b9..af4800c9 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,17 +17,22 @@ package love.forte.simbot.component.qguild.event -import love.forte.simbot.Timestamp -import love.forte.simbot.component.qguild.JSTP -import love.forte.simbot.component.qguild.QGMember -import love.forte.simbot.component.qguild.QGTextChannel +import love.forte.simbot.common.time.Timestamp +import love.forte.simbot.component.qguild.ExperimentalQGApi +import love.forte.simbot.component.qguild.channel.QGTextChannel +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.guild.QGMember +import love.forte.simbot.component.qguild.message.QGMessageContent import love.forte.simbot.component.qguild.message.QGMessageReceipt -import love.forte.simbot.component.qguild.message.QGReceiveMessageContent -import love.forte.simbot.definition.Objective -import love.forte.simbot.event.* -import love.forte.simbot.message.doSafeCast +import love.forte.simbot.component.qguild.utils.toTimestamp +import love.forte.simbot.definition.Actor +import love.forte.simbot.event.ActorEvent +import love.forte.simbot.event.ChatChannelMessageEvent +import love.forte.simbot.event.ContentEvent +import love.forte.simbot.event.MessageEvent import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.model.Message +import love.forte.simbot.suspendrunner.STP import love.forte.simbot.qguild.model.Message as QGSourceMessage @@ -43,8 +48,7 @@ import love.forte.simbot.qguild.model.Message as QGSourceMessage * * @author ForteScarlet */ -@BaseEvent -public sealed class QGMessageEvent : QGEvent(), MessageEvent { +public sealed class QGMessageEvent : QGBotEvent(), MessageEvent, ContentEvent, ActorEvent { /** * 标准库接收到的原始事件内容。 */ @@ -53,12 +57,12 @@ public sealed class QGMessageEvent : QGEvent(), MessageEvent { /** * 消息发生(收到)的时间 */ - abstract override val timestamp: Timestamp + abstract override val time: Timestamp /** * 接收到的消息内容。 */ - abstract override val messageContent: QGReceiveMessageContent + abstract override val messageContent: QGMessageContent /** * 基于当前事件进行消息回复。 @@ -72,20 +76,12 @@ public sealed class QGMessageEvent : QGEvent(), MessageEvent { abstract override suspend fun reply(message: love.forte.simbot.message.Message): QGMessageReceipt /** - * 接收到消息的本体。实现子类型来决定具体类型。 + * 接收到消息的事件主体。实现子类型来决定具体类型。 * * @throws QQGuildApiException 请求失败,例如无权限 * @throws NoSuchElementException 没有找到结果 */ - abstract override suspend fun source(): Objective - - abstract override val key: Event.Key - - public companion object Key : BaseEventKey( - "qg.message", QGEvent, MessageEvent - ) { - override fun safeCast(value: Any): QGMessageEvent? = doSafeCast(value) - } + abstract override suspend fun content(): Actor } @@ -99,8 +95,11 @@ public sealed class QGMessageEvent : QGEvent(), MessageEvent { * * @author ForteScarlet */ -@JSTP -public abstract class QGAtMessageCreateEvent : QGMessageEvent(), ChannelMessageEvent { +@STP +public abstract class QGAtMessageCreateEvent : QGMessageEvent(), ChatChannelMessageEvent { + @OptIn(ExperimentalQGApi::class) + override val time: Timestamp + get() = sourceEventEntity.timestamp.toTimestamp() /** * 发送消息的用户 @@ -110,36 +109,16 @@ public abstract class QGAtMessageCreateEvent : QGMessageEvent(), ChannelMessageE */ abstract override suspend fun author(): QGMember - /** * 接收到消息的子频道。 * * @throws QQGuildApiException 请求失败,例如无权限 * @throws NoSuchElementException 没有找到结果 */ - abstract override suspend fun channel(): QGTextChannel - - /** - * 接收到消息的子频道。同 [channel] - * - * @throws QQGuildApiException 请求失败,例如无权限 - * @throws NoSuchElementException 没有找到结果 - */ - override suspend fun source(): QGTextChannel = channel() + abstract override suspend fun content(): QGTextChannel /** - * 接收到消息的子频道。同 [channel] - * - * @throws QQGuildApiException 请求失败,例如无权限 - * @throws NoSuchElementException 没有找到结果 + * 接收到消息的子频道的所属频道服务器。 */ - override suspend fun organization(): QGTextChannel = channel() - - override val key: Event.Key get() = Key - - public companion object Key : BaseEventKey( - "qg.at_message_create", QGMessageEvent, ChannelMessageEvent - ) { - override fun safeCast(value: Any): QGAtMessageCreateEvent? = doSafeCast(value) - } + abstract override suspend fun guild(): QGGuild } diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt similarity index 60% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt index d3c86d35..64977c85 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,19 +17,15 @@ package love.forte.simbot.component.qguild.event -import love.forte.simbot.ID -import love.forte.simbot.JSTP -import love.forte.simbot.component.qguild.QGGuild -import love.forte.simbot.component.qguild.QGMember -import love.forte.simbot.component.qguild.forum.QGForumChannel -import love.forte.simbot.definition.ChannelInfoContainer -import love.forte.simbot.definition.GuildInfoContainer -import love.forte.simbot.event.BaseEvent -import love.forte.simbot.event.BaseEventKey -import love.forte.simbot.event.Event -import love.forte.simbot.message.doSafeCast +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.component.qguild.channel.QGForumChannel +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.guild.QGMember +import love.forte.simbot.event.ChannelEvent import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.event.* +import love.forte.simbot.suspendrunner.STP /** @@ -41,29 +37,31 @@ import love.forte.simbot.qguild.event.* * * @see OpenForumDispatch */ -@BaseEvent -@JSTP -public abstract class QGOpenForumEvent : QGEvent(), GuildInfoContainer, ChannelInfoContainer { +@STP +public abstract class QGOpenForumEvent : QGBotEvent(), ChannelEvent { /** * 频道ID * * @see OpenForumEventData.guildId */ - public val guildId: ID get() = sourceEventEntity.guildId.ID + public val guildId: ID + get() = sourceEventEntity.guildId.ID /** * 子频道ID * * @see OpenForumEventData.channelId */ - public val channelId: ID get() = sourceEventEntity.channelId.ID + public val channelId: ID + get() = sourceEventEntity.channelId.ID /** * 发布人ID * * @see OpenForumEventData.authorId */ - public val authorId: ID get() = sourceEventEntity.authorId.ID + public val authorId: ID + get() = sourceEventEntity.authorId.ID /** * 得到本次事件 [guildId] 对应的频道。 @@ -72,7 +70,7 @@ public abstract class QGOpenForumEvent : QGEvent(), GuildInf * @throws NoSuchElementException 对应子频道已不存在 */ override suspend fun guild(): QGGuild = - bot.guild(guildId) ?: throw NoSuchElementException("guild(id=$guildId)") + bot.guildRelation.guild(guildId) ?: throw NoSuchElementException("guild(id=$guildId)") /** * 得到本次事件 [channelId] 对应的论坛子频道。 @@ -81,7 +79,7 @@ public abstract class QGOpenForumEvent : QGEvent(), GuildInf * @throws IllegalStateException 对应子频道已经不再是论坛类型 * @throws NoSuchElementException 对应子频道已不存在 */ - abstract override suspend fun channel(): QGForumChannel + abstract override suspend fun content(): QGForumChannel /** * 得到本次事件 [authorId] 对应的频道中成员。 @@ -90,12 +88,6 @@ public abstract class QGOpenForumEvent : QGEvent(), GuildInf * @throws NoSuchElementException 对应成员已不存在 */ public abstract suspend fun author(): QGMember - - abstract override val key: Event.Key - - public companion object Key : BaseEventKey("qg.open_forum", QGEvent) { - override fun safeCast(value: Any): QGOpenForumEvent? = doSafeCast(value) - } } /** @@ -111,12 +103,6 @@ public abstract class QGOpenForumThreadEvent : QGOpenForumEvent() { * API模块的原事件内容。 */ abstract override val sourceEventEntity: OpenForumThreadData - - abstract override val key: Event.Key - - public companion object Key : BaseEventKey("qg.open_forum_thread", QGOpenForumEvent) { - override fun safeCast(value: Any): QGOpenForumThreadEvent? = doSafeCast(value) - } } /** @@ -128,16 +114,9 @@ public abstract class QGOpenForumThreadEvent : QGOpenForumEvent() { * @see QGOpenForumThreadEvent */ public abstract class QGOpenForumThreadCreateEvent : QGOpenForumThreadEvent() { - override val key: Event.Key get() = Key - override fun toString(): String { return "QGOpenForumThreadCreateEvent(sourceEventEntity=$sourceEventEntity)" } - - public companion object Key : - BaseEventKey("qg.open_forum_thread_create", QGOpenForumThreadEvent) { - override fun safeCast(value: Any): QGOpenForumThreadCreateEvent? = doSafeCast(value) - } } /** @@ -149,16 +128,9 @@ public abstract class QGOpenForumThreadCreateEvent : QGOpenForumThreadEvent() { * @see QGOpenForumThreadEvent */ public abstract class QGOpenForumThreadUpdateEvent : QGOpenForumThreadEvent() { - override val key: Event.Key get() = Key - override fun toString(): String { return "QGOpenForumThreadUpdateEvent(sourceEventEntity=$sourceEventEntity)" } - - public companion object Key : - BaseEventKey("qg.open_forum_thread_update", QGOpenForumThreadEvent) { - override fun safeCast(value: Any): QGOpenForumThreadUpdateEvent? = doSafeCast(value) - } } /** @@ -170,16 +142,9 @@ public abstract class QGOpenForumThreadUpdateEvent : QGOpenForumThreadEvent() { * @see QGOpenForumThreadEvent */ public abstract class QGOpenForumThreadDeleteEvent : QGOpenForumThreadEvent() { - override val key: Event.Key get() = Key - override fun toString(): String { return "QGOpenForumThreadDeleteEvent(sourceEventEntity=$sourceEventEntity)" } - - public companion object Key : - BaseEventKey("qg.open_forum_thread_delete", QGOpenForumThreadEvent) { - override fun safeCast(value: Any): QGOpenForumThreadDeleteEvent? = doSafeCast(value) - } } @@ -196,12 +161,6 @@ public abstract class QGOpenForumPostEvent : QGOpenForumEvent() { * API模块的原事件内容。 */ abstract override val sourceEventEntity: OpenForumPostData - - abstract override val key: Event.Key - - public companion object Key : BaseEventKey("qg.open_forum_post", QGOpenForumEvent) { - override fun safeCast(value: Any): QGOpenForumPostEvent? = doSafeCast(value) - } } /** @@ -213,16 +172,9 @@ public abstract class QGOpenForumPostEvent : QGOpenForumEvent() { * @see QGOpenForumEvent */ public abstract class QGOpenForumPostCreateEvent : QGOpenForumPostEvent() { - override val key: Event.Key get() = Key - override fun toString(): String { return "QGOpenForumPostCreateEvent(sourceEventEntity=$sourceEventEntity)" } - - public companion object Key : - BaseEventKey("qg.open_forum_post_create", QGOpenForumPostEvent) { - override fun safeCast(value: Any): QGOpenForumPostCreateEvent? = doSafeCast(value) - } } /** @@ -234,16 +186,9 @@ public abstract class QGOpenForumPostCreateEvent : QGOpenForumPostEvent() { * @see QGOpenForumEvent */ public abstract class QGOpenForumPostDeleteEvent : QGOpenForumPostEvent() { - override val key: Event.Key get() = Key - override fun toString(): String { return "QGOpenForumPostDeleteEvent(sourceEventEntity=$sourceEventEntity)" } - - public companion object Key : - BaseEventKey("qg.open_forum_post_delete", QGOpenForumPostEvent) { - override fun safeCast(value: Any): QGOpenForumPostDeleteEvent? = doSafeCast(value) - } } @@ -260,12 +205,6 @@ public abstract class QGOpenForumReplyEvent : QGOpenForumEvent() { * API模块的原事件内容。 */ abstract override val sourceEventEntity: OpenForumReplyData - - abstract override val key: Event.Key - - public companion object Key : BaseEventKey("qg.open_forum_reply", QGOpenForumEvent) { - override fun safeCast(value: Any): QGOpenForumReplyEvent? = doSafeCast(value) - } } /** @@ -277,16 +216,9 @@ public abstract class QGOpenForumReplyEvent : QGOpenForumEvent() { * @see QGOpenForumEvent */ public abstract class QGOpenForumReplyCreateEvent : QGOpenForumReplyEvent() { - override val key: Event.Key get() = Key - override fun toString(): String { return "QGOpenForumReplyCreateEvent(sourceEventEntity=$sourceEventEntity)" } - - public companion object Key : - BaseEventKey("qg.open_forum_reply_create", QGOpenForumReplyEvent) { - override fun safeCast(value: Any): QGOpenForumReplyCreateEvent? = doSafeCast(value) - } } /** @@ -298,14 +230,7 @@ public abstract class QGOpenForumReplyCreateEvent : QGOpenForumReplyEvent() { * @see QGOpenForumEvent */ public abstract class QGOpenForumReplyDeleteEvent : QGOpenForumReplyEvent() { - override val key: Event.Key get() = Key - override fun toString(): String { return "QGOpenForumReplyDeleteEvent(sourceEventEntity=$sourceEventEntity)" } - - public companion object Key : - BaseEventKey("qg.open_forum_reply_delete", QGOpenForumReplyEvent) { - override fun safeCast(value: Any): QGOpenForumReplyDeleteEvent? = doSafeCast(value) - } } diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt similarity index 71% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt index b077606d..c1e0a7bd 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,15 +17,10 @@ package love.forte.simbot.component.qguild.event -import love.forte.simbot.FragileSimbotApi -import love.forte.simbot.ID -import love.forte.simbot.Timestamp -import love.forte.simbot.delegate.getValue -import love.forte.simbot.delegate.stringID -import love.forte.simbot.delegate.timestamp -import love.forte.simbot.event.BaseEventKey -import love.forte.simbot.event.Event -import love.forte.simbot.message.doSafeCast +import love.forte.simbot.annotations.ExperimentalSimbotAPI +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.UUID +import love.forte.simbot.common.time.Timestamp import love.forte.simbot.qguild.event.Signal @@ -47,26 +42,20 @@ import love.forte.simbot.qguild.event.Signal * * @author ForteScarlet */ -@FragileSimbotApi +@ExperimentalSimbotAPI public abstract class QGUnsupportedEvent : QGEvent() { /** - * 事件ID。一个随机字符串。 + * 事件ID。一个随机ID。 */ - override val id: ID by stringID { random } + override val id: ID = UUID.random() /** * 事件构建时间,即此对象被构建的时间 */ - override val timestamp: Timestamp by timestamp { now } + override val time: Timestamp = Timestamp.now() /** * 原始的标准库事件对象。 */ abstract override val sourceEventEntity: Signal.Dispatch - - override val key: Event.Key> get() = Key - - public companion object Key : BaseEventKey("qg.unsupported", Event) { - override fun safeCast(value: Any): QGUnsupportedEvent? = doSafeCast(value) - } } diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGForumInfoContainer.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGForumInfoContainer.kt similarity index 86% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGForumInfoContainer.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGForumInfoContainer.kt index 9aacea10..54ae21d5 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGForumInfoContainer.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGForumInfoContainer.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,8 +17,7 @@ package love.forte.simbot.component.qguild.forum -import love.forte.simbot.ID -import love.forte.simbot.definition.Container +import love.forte.simbot.common.id.ID /** * @@ -26,8 +25,7 @@ import love.forte.simbot.definition.Container * * @author ForteScarlet */ -public interface QGForumInfoContainer : Container { - +public interface QGForumInfoContainer { /** * 频道ID */ @@ -42,5 +40,4 @@ public interface QGForumInfoContainer : Container { * 作者ID */ public val authorId: ID - } diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGPost.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGPost.kt similarity index 56% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGPost.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGPost.kt index bff4ab3e..8f98c74c 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGPost.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGPost.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,20 +17,18 @@ package love.forte.simbot.component.qguild.forum -import love.forte.simbot.ID -import love.forte.simbot.JSTP -import love.forte.simbot.Timestamp -import love.forte.simbot.component.qguild.QGBot -import love.forte.simbot.component.qguild.QGGuild -import love.forte.simbot.component.qguild.QGMember +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.IDContainer +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.common.time.Timestamp +import love.forte.simbot.component.qguild.ExperimentalQGApi import love.forte.simbot.component.qguild.QGObjectiveContainer import love.forte.simbot.component.qguild.event.QGForumPostEvent -import love.forte.simbot.definition.BotContainer -import love.forte.simbot.definition.ChannelInfoContainer -import love.forte.simbot.definition.GuildInfoContainer -import love.forte.simbot.definition.IDContainer +import love.forte.simbot.component.qguild.guild.QGMember +import love.forte.simbot.component.qguild.utils.toTimestamp import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.model.forum.Post +import love.forte.simbot.suspendrunner.STP /** @@ -38,16 +36,9 @@ import love.forte.simbot.qguild.model.forum.Post * * @author ForteScarlet */ -@JSTP +@STP public interface QGPost : QGObjectiveContainer, - QGForumInfoContainer, - IDContainer, BotContainer, GuildInfoContainer, - ChannelInfoContainer { - /** - * 当前bot - */ - override val bot: QGBot - + QGForumInfoContainer, IDContainer { /** * 评论信息的源信息 */ @@ -56,54 +47,45 @@ public interface QGPost : QGObjectiveContainer, /** * 此回复的ID */ - override val id: ID get() = source.postInfo.postId.ID + override val id: ID + get() = source.postInfo.postId.ID /** * 频道ID */ - override val guildId: ID get() = source.guildId.ID + override val guildId: ID + get() = source.guildId.ID /** * 子频道ID */ - override val channelId: ID get() = source.channelId.ID + override val channelId: ID + get() = source.channelId.ID /** * 作者ID */ - override val authorId: ID get() = source.authorId.ID + override val authorId: ID + get() = source.authorId.ID /** * 此评论对应的主题帖ID */ - public val threadId: ID get() = source.postInfo.threadId.ID + public val threadId: ID + get() = source.postInfo.threadId.ID /** * 内容 */ - public val content: String get() = source.postInfo.content + public val content: String + get() = source.postInfo.content /** * 评论时间 */ + @OptIn(ExperimentalQGApi::class) public val datetime: Timestamp - - /** - * 此评论所属的频道服务器 - * - * @throws QQGuildApiException API请求产生异常 - * @throws NoSuchElementException 此频道已不存在 - */ - override suspend fun guild(): QGGuild - - /** - * 此评论所属的论坛子频道 - * - * @throws QQGuildApiException API请求产生异常 - * @throws IllegalStateException 此子频道已不再是论坛类型 - * @throws NoSuchElementException 此子频道已不存在 - */ - override suspend fun channel(): QGForumChannel + get() = source.postInfo.dateTime.toTimestamp() /** * 在频道中的评论者 diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGReply.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGReply.kt similarity index 52% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGReply.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGReply.kt index b88bf3aa..1617b4e5 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGReply.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGReply.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,20 +17,18 @@ package love.forte.simbot.component.qguild.forum -import love.forte.simbot.ID -import love.forte.simbot.JSTP -import love.forte.simbot.Timestamp -import love.forte.simbot.component.qguild.QGBot -import love.forte.simbot.component.qguild.QGGuild -import love.forte.simbot.component.qguild.QGMember +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.IDContainer +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.common.time.Timestamp +import love.forte.simbot.component.qguild.ExperimentalQGApi import love.forte.simbot.component.qguild.QGObjectiveContainer import love.forte.simbot.component.qguild.event.QGForumReplyEvent -import love.forte.simbot.definition.BotContainer -import love.forte.simbot.definition.ChannelInfoContainer -import love.forte.simbot.definition.GuildInfoContainer -import love.forte.simbot.definition.IDContainer +import love.forte.simbot.component.qguild.guild.QGMember +import love.forte.simbot.component.qguild.utils.toTimestamp import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.model.forum.Reply +import love.forte.simbot.suspendrunner.STP /** @@ -38,17 +36,9 @@ import love.forte.simbot.qguild.model.forum.Reply * * @author ForteScarlet */ -@JSTP +@STP public interface QGReply : QGObjectiveContainer, - QGForumInfoContainer, - IDContainer, BotContainer, GuildInfoContainer, - ChannelInfoContainer { - - /** - * 当前bot - */ - override val bot: QGBot - + QGForumInfoContainer, IDContainer { /** * 回复信息的源信息 */ @@ -57,59 +47,51 @@ public interface QGReply : QGObjectiveContainer, /** * 此回复的ID */ - override val id: ID get() = source.replyInfo.postId.ID + override val id: ID + get() = source.replyInfo.postId.ID /** * 频道ID */ - override val guildId: ID get() = source.guildId.ID + override val guildId: ID + get() = source.guildId.ID /** * 子频道ID */ - override val channelId: ID get() = source.channelId.ID + override val channelId: ID + get() = source.channelId.ID /** * 作者ID */ - override val authorId: ID get() = source.authorId.ID + override val authorId: ID + get() = source.authorId.ID /** * 此回复对应的主题帖ID */ - public val threadId: ID get() = source.replyInfo.threadId.ID + public val threadId: ID + get() = source.replyInfo.threadId.ID /** * 此回复对应的评论ID */ - public val postId: ID get() = source.replyInfo.postId.ID + public val postId: ID + get() = source.replyInfo.postId.ID /** * 内容 */ - public val content: String get() = source.replyInfo.content + public val content: String + get() = source.replyInfo.content /** * 评论时间 */ + @OptIn(ExperimentalQGApi::class) public val datetime: Timestamp - - /** - * 此评论所属的频道服务器 - * - * @throws QQGuildApiException API请求产生异常 - * @throws NoSuchElementException 此频道已不存在 - */ - override suspend fun guild(): QGGuild - - /** - * 此评论所属的论坛子频道 - * - * @throws QQGuildApiException API请求产生异常 - * @throws IllegalStateException 此子频道已不再是论坛类型 - * @throws NoSuchElementException 此子频道已不存在 - */ - override suspend fun channel(): QGForumChannel + get() = source.replyInfo.dateTime.toTimestamp() /** * 频道中的回复者。 @@ -126,13 +108,4 @@ public interface QGReply : QGObjectiveContainer, * @throws NoSuchElementException 所属主题已不存在 */ public suspend fun thread(): QGThread - - // No API supported -// /** -// * 此回复所属的评论 -// * -// * @throws QQGuildApiException API请求产生异常 -// * @throws NoSuchElementException 所属评论已不存在 -// */ -// public suspend fun post(): QGPost } diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGThread.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGThread.kt similarity index 58% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGThread.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGThread.kt index 91795aec..df6f3f36 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGThread.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGThread.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -18,16 +18,20 @@ package love.forte.simbot.component.qguild.forum import kotlinx.coroutines.CoroutineScope -import love.forte.simbot.ID -import love.forte.simbot.Timestamp -import love.forte.simbot.action.DeleteSupport -import love.forte.simbot.component.qguild.* -import love.forte.simbot.definition.BotContainer -import love.forte.simbot.definition.ChannelInfoContainer -import love.forte.simbot.definition.GuildInfoContainer -import love.forte.simbot.definition.IDContainer +import love.forte.simbot.ability.DeleteOption +import love.forte.simbot.ability.DeleteSupport +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.IDContainer +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.common.time.Timestamp +import love.forte.simbot.component.qguild.ExperimentalQGApi +import love.forte.simbot.component.qguild.QGObjectiveContainer +import love.forte.simbot.component.qguild.guild.QGMember +import love.forte.simbot.component.qguild.utils.toTimestamp import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.model.forum.Thread +import love.forte.simbot.suspendrunner.ST +import love.forte.simbot.suspendrunner.STP /** @@ -36,69 +40,55 @@ import love.forte.simbot.qguild.model.forum.Thread * * @author ForteScarlet */ -public interface QGThread : CoroutineScope, IDContainer, BotContainer, GuildInfoContainer, ChannelInfoContainer, - QGForumInfoContainer, +public interface QGThread : CoroutineScope, IDContainer, QGForumInfoContainer, QGObjectiveContainer, DeleteSupport { /** * 主题帖信息的源类型。 */ override val source: Thread - /** - * 所属BOT - */ - override val bot: QGBot - /** * 帖子ID */ - override val id: ID get() = source.threadInfo.threadId.ID + override val id: ID + get() = source.threadInfo.threadId.ID /** * 频道ID */ - override val guildId: ID get() = source.guildId.ID + override val guildId: ID + get() = source.guildId.ID /** * 子频道ID */ - override val channelId: ID get() = source.channelId.ID + override val channelId: ID + get() = source.channelId.ID /** * 作者ID */ - override val authorId: ID get() = source.authorId.ID - + override val authorId: ID + get() = source.authorId.ID /** * 帖子标题 */ - public val title: String get() = source.threadInfo.title + public val title: String + get() = source.threadInfo.title /** * 帖子内容 */ - public val content: String get() = source.threadInfo.content + public val content: String + get() = source.threadInfo.content /** * 帖子发表时间 */ + @OptIn(ExperimentalQGApi::class) public val dateTime: Timestamp - - /** - * 依据 [guildId] 寻找所属频道 - * - * @throws QQGuildApiException api请求异常 - * @throws NoSuchElementException 获取时目标已不存在 - */ - @JSTP - override suspend fun guild(): QGGuild - - /** - * 这个帖子所属的子频道。 - */ - @JSTP - override suspend fun channel(): QGForumChannel + get() = source.threadInfo.dateTime.toTimestamp() /** * 依据 [authorId] 寻找作者信息 @@ -106,7 +96,7 @@ public interface QGThread : CoroutineScope, IDContainer, BotContainer, GuildInfo * @throws QQGuildApiException api请求异常 * @throws NoSuchElementException 获取时目标已不存在 */ - @JSTP + @STP public suspend fun author(): QGMember /** @@ -115,6 +105,6 @@ public interface QGThread : CoroutineScope, IDContainer, BotContainer, GuildInfo * @return 当API请求成功得到 `true`,当API请求响应 404 得到 `false`,其他情况抛出原异常。 * @throws QQGuildApiException API请求产生的异常 */ - @JST - override suspend fun delete(): Boolean + @ST + override suspend fun delete(vararg options: DeleteOption) } diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGThreadCreator.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGThreadCreator.kt similarity index 96% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGThreadCreator.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGThreadCreator.kt index 02372921..4b8ffaa9 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGThreadCreator.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGThreadCreator.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,10 +17,10 @@ package love.forte.simbot.component.qguild.forum -import love.forte.simbot.component.qguild.JST import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.api.forum.ThreadPublishFormat import love.forte.simbot.qguild.api.forum.ThreadPublishResult +import love.forte.simbot.suspendrunner.ST /** @@ -105,7 +105,7 @@ public interface QGThreadCreator { * * @return 帖子发布后的回执 */ - @JST + @ST public suspend fun publish(): ThreadPublishResult } diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGGuild.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuild.kt similarity index 54% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGGuild.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuild.kt index 78a1194d..ee64ec65 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGGuild.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuild.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,24 +15,26 @@ * If not, see . */ -package love.forte.simbot.component.qguild +package love.forte.simbot.component.qguild.guild import kotlinx.coroutines.CoroutineScope -import love.forte.simbot.ExperimentalSimbotApi -import love.forte.simbot.ID -import love.forte.simbot.Timestamp -import love.forte.simbot.component.qguild.forum.QGForums +import love.forte.simbot.common.collectable.Collectable +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.common.time.Timestamp +import love.forte.simbot.component.qguild.ExperimentalQGApi +import love.forte.simbot.component.qguild.QGObjectiveContainer +import love.forte.simbot.component.qguild.channel.* import love.forte.simbot.component.qguild.role.QGGuildRole import love.forte.simbot.component.qguild.role.QGRoleCreator -import love.forte.simbot.definition.Guild -import love.forte.simbot.definition.Organization import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.api.apipermission.ApiPermissions import love.forte.simbot.qguild.api.channel.GetGuildChannelListApi import love.forte.simbot.qguild.api.member.GetGuildMemberListApi import love.forte.simbot.qguild.model.ChannelType -import love.forte.simbot.utils.item.Items -import kotlin.time.Duration +import love.forte.simbot.suspendrunner.ST +import love.forte.simbot.suspendrunner.STP +import love.forte.simbot.definition.Guild as SimbotGuild import love.forte.simbot.qguild.model.Guild as QGSourceGuild /** @@ -40,88 +42,76 @@ import love.forte.simbot.qguild.model.Guild as QGSourceGuild * * @author ForteScarlet */ -public interface QGGuild : Guild, CoroutineScope, QGObjectiveContainer { +public interface QGGuild : SimbotGuild, CoroutineScope, QGObjectiveContainer { /** - * 当前bot在此频道服务器中的频道bot实例。 + * 原始的频道信息实例 */ - override val bot: QGGuildBot + override val source: QGSourceGuild /** * 频道ID */ override val id: ID + get() = source.id.ID /** * 频道名称 */ override val name: String + get() = source.name /** * 频道主ID */ override val ownerId: ID - - /** - * 无法获取频道创建时间,始终得到 [Timestamp.notSupport] - * - * 如果希望获取bot加入时间,使用 [joinTime] - */ - override val createTime: Timestamp get() = Timestamp.notSupport() + get() = source.ownerId.ID /** * bot加入此频道的时间。 */ public val joinTime: Timestamp + get() = TODO("source.joinedAt") /** * 频道描述 */ - override val description: String + public val description: String + get() = source.description /** * 频道图标。 */ - override val icon: String - - /** - * 最大成员数量 - */ - override val maximumMember: Int + public val icon: String + get() = source.icon /** * 当前成员数量 */ - override val currentMember: Int - - /** - * 无法无副作用的得到当前频道数量,始终得到 `-1`。 - * - * 如果希望计算频道数量,使用 [channels] 和 [categories]。 - */ - override val currentChannel: Int get() = -1 + public val memberCount: Int + get() = source.memberCount /** - * 无法得到子频道上限,可能也没有上限。始终得到 `-1` - */ - override val maximumChannel: Int get() = -1 - - /** - * 原始的频道信息实例 + * 最大成员数量 */ - override val source: QGSourceGuild + public val maxMembers: Int + get() = source.maxMembers /** * 查询并获取当前bot在频道服务器中拥有的API权限集。 */ - @JSTP + @STP public suspend fun permissions(): ApiPermissions + //region channels /** * 得到此频道服务器下的所有子频道。 * * _可以通过 [permissions] 手动检查是否存在 [GetGuildChannelListApi] 的权限。_ * - * 注意: [channels] 的结果集中 **不会出现** 类型为分组类型 [ChannelType.CATEGORY] 的子频道。 + * 注意: [channels] 的结果集中 **会出现** 包括类型为分组类型 [ChannelType.CATEGORY] + * 在内的所有类型的的子频道。 + * + * 如果希望只获取可用于发送消息的文字频道类型,参考使用 [chatChannels] * * @see QGTextChannel * @see channel @@ -130,37 +120,22 @@ public interface QGGuild : Guild, CoroutineScope, QGObjectiveContainer + override val channels: Collectable /** * 获取指定ID的子频道。 * * @throws QQGuildApiException 请求失败,例如没有权限 */ - @JST(blockingBaseName = "getChannel", blockingSuffix = "", asyncBaseName = "getChannel") + @ST(blockingBaseName = "getChannel", blockingSuffix = "", asyncBaseName = "getChannel") override suspend fun channel(id: ID): QGChannel? - /** - * 得到此频道服务器下的所有子频道。同 [channels]。 - * - * @see channels - */ - override val children: Items get() = channels - - /** - * 获取指定ID的子频道,同 [channel] - * - * @see channel - */ - @JST(blockingBaseName = "getChild", blockingSuffix = "", asyncBaseName = "getChild") - override suspend fun child(id: ID): QGChannel? = channel(id) - /** * 得到当前频道服务器下的所有频道分类。 * * @throws QQGuildApiException 请求失败,例如没有权限 */ - public val categories: Items + public val categories: Collectable /** * 获取指定ID的子频道分类。 @@ -169,28 +144,47 @@ public interface QGGuild : Guild, CoroutineScope, QGObjectiveContainer + + /** + * 获取指定ID的文字频道。 + */ + @ST(blockingBaseName = "getChatChannel", blockingSuffix = "", asyncBaseName = "getChatChannel") + override suspend fun chatChannel(id: ID): QGTextChannel? + + /** + * 得到当前 guild 中的所有 **帖子类型** [ChannelType.FORUM] 的子频道实例。 * - * 也可理解为频道主、创建者等。 + * 得到的数据集是 [QGGuild.channels] 的子集。 * - * @throws QQGuildApiException 当没有权限获取时,通常 [QQGuildApiException.value] == `401` - * @throws NoSuchElementException 当没有找到对应成员时 + * @see QGForumChannel * */ - @JSTP - override suspend fun owner(): QGMember + public val forumChannels: Collectable + /** + * 根据ID寻找匹配的 **帖子类型** [ChannelType.FORUM] 的子频道 + * + * @throws QQGuildApiException API请求过程中出现的异常 + * @throws IllegalStateException 当目标子频道类型不是 [ChannelType.FORUM] 时 + */ + @ST(blockingBaseName = "getForumChannel", blockingSuffix = "", asyncBaseName = "getForumChannel") + public suspend fun forumChannel(id: ID): QGForumChannel? + //endregion + //region members /** * 获取指定成员的信息。 * * @throws QQGuildApiException 请求失败,例如没有权限 */ - @JST(blockingBaseName = "getMember", blockingSuffix = "", asyncBaseName = "getMember") + @ST(blockingBaseName = "getMember", blockingSuffix = "", asyncBaseName = "getMember") override suspend fun member(id: ID): QGMember? /** @@ -201,48 +195,22 @@ public interface QGGuild : Guild, CoroutineScope, QGObjectiveContainer + override val members: Collectable + //endregion /** * 当前频道中的角色。 * * @throws QQGuildApiException 请求失败,例如没有权限 */ - @ExperimentalSimbotApi - override val roles: Items + @ExperimentalQGApi + override val roles: Collectable /** * 得到一个角色构造器。 */ - @ExperimentalSimbotApi + @ExperimentalQGApi public fun roleCreator(): QGRoleCreator - - /** - * 得到针对 **帖子子频道** 的操作器。 - * - * @see QGForums - */ - public val forums: QGForums - - //// Impls - - /** - * 频道服务器没有全频道禁言/取消禁言 - */ - @JvmSynthetic - override suspend fun unmute(): Boolean = false - - /** - * 频道服务器没有全频道禁言/取消禁言 - */ - @JvmSynthetic - override suspend fun mute(duration: Duration): Boolean = false - - /** - * 频道服务器没有上级概念。 - */ - @JvmSynthetic - override suspend fun previous(): Organization? = null } /** @@ -250,6 +218,6 @@ public interface QGGuild : Guild, CoroutineScope, QGObjectiveContainer Unit): QGGuildRole = roleCreator().also(block).create() diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuildRelation.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuildRelation.kt new file mode 100644 index 00000000..eda20393 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuildRelation.kt @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.guild + +import love.forte.simbot.bot.GuildRelation +import love.forte.simbot.common.collectable.Collectable +import love.forte.simbot.common.id.ID +import love.forte.simbot.component.qguild.channel.* +import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.qguild.model.ChannelType +import love.forte.simbot.suspendrunner.ST +import kotlin.jvm.JvmSynthetic + + +/** + * QQ频道组件中一个 [QGBot] 下针对频道服务器、子频道、子频道分组等相关内容的关系操作。 + * + * @author ForteScarlet + */ +public interface QGGuildRelation : GuildRelation { + + override val guilds: Collectable + + @ST(blockingBaseName = "getGuild", blockingSuffix = "", asyncBaseName = "getGuild") + override suspend fun guild(id: ID): QGGuild? + + /** + * 获取所有频道服务器的数量。 + * 注意:QQ频道没有直接获取数量的 API, + * 因此会查询所有的频道列表并计数。 + */ + @JvmSynthetic + override suspend fun guildCount(): Int + + /** + * 直接获取指定ID的子频道。 + * + * @see category + * + * @throws QQGuildApiException 请求失败,例如没有权限 + */ + @ST(blockingBaseName = "getChannel", blockingSuffix = "", asyncBaseName = "getChannel") + public suspend fun channel(channelId: ID): QGChannel? + + /** + * 直接获取指定ID的文字类型子频道。 + * + * @see category + * + * @throws QQGuildApiException 请求失败,例如没有权限 + * @throws IllegalStateException 当目标子频道的类型不属于 [文字类型][ChannelType.TEXT] 时 + */ + @ST(blockingBaseName = "getChatChannel", blockingSuffix = "", asyncBaseName = "getChatChannel") + public suspend fun chatChannel(channelId: ID): QGTextChannel? + + + /** + * 直接获取指定ID的子频道分类。 + * + * @throws QQGuildApiException 请求失败,例如没有权限 + * @throws IllegalStateException 当目标子频道的类型不属于 [分组类型][ChannelType.CATEGORY] 时 + * + */ + @ST(blockingBaseName = "getCategory", blockingSuffix = "", asyncBaseName = "getCategory") + public suspend fun category(channelId: ID): QGCategoryChannel? + + /** + * 根据ID寻找匹配的 **帖子类型** [ChannelType.FORUM] 的子频道 + * + * @throws QQGuildApiException API请求过程中出现的异常 + * @throws IllegalStateException 当目标子频道类型不是 [ChannelType.FORUM] 时 + */ + @ST(blockingBaseName = "getForumChannel", blockingSuffix = "", asyncBaseName = "getForumChannel") + public suspend fun forumChannel(id: ID): QGForumChannel? +} + diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGMember.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGMember.kt similarity index 73% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGMember.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGMember.kt index a3e618dd..eaf25278 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGMember.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGMember.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,33 +15,34 @@ * If not, see . */ -package love.forte.simbot.component.qguild +package love.forte.simbot.component.qguild.guild import io.ktor.client.* import io.ktor.client.request.* import kotlinx.coroutines.CoroutineScope -import love.forte.simbot.ExperimentalSimbotApi -import love.forte.simbot.ID +import love.forte.simbot.common.collectable.Collectable +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.time.Timestamp +import love.forte.simbot.component.qguild.ExperimentalQGApi +import love.forte.simbot.component.qguild.QGObjectiveContainer import love.forte.simbot.component.qguild.message.QGContentText import love.forte.simbot.component.qguild.message.QGMessageReceipt import love.forte.simbot.component.qguild.role.QGMemberRole -import love.forte.simbot.definition.GuildMember +import love.forte.simbot.component.qguild.utils.toTimestamp +import love.forte.simbot.definition.Member import love.forte.simbot.message.Message import love.forte.simbot.message.MessageContent import love.forte.simbot.message.Text import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.api.MessageAuditedException -import love.forte.simbot.utils.item.Items -import kotlin.time.Duration +import love.forte.simbot.suspendrunner.ST import love.forte.simbot.qguild.model.Member as QGSourceMember /** * * @author ForteScarlet */ -public interface QGMember : GuildMember, CoroutineScope, QGObjectiveContainer { - override val bot: QGBot - +public interface QGMember : Member, CoroutineScope, QGObjectiveContainer { /** * 成员的用户ID */ @@ -50,23 +51,33 @@ public interface QGMember : GuildMember, CoroutineScope, QGObjectiveContainer + @ExperimentalQGApi + public val roles: Collectable /** * 向目标成员发送私聊消息。 @@ -76,7 +87,7 @@ public interface QGMember : GuildMember, CoroutineScope, QGObjectiveContainer. + */ + +package love.forte.simbot.component.qguild.internal.bot + +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import love.forte.simbot.bot.JobBasedBot +import love.forte.simbot.common.collectable.Collectable +import love.forte.simbot.common.collectable.asCollectable +import love.forte.simbot.common.collection.computeValueIfAbsent +import love.forte.simbot.common.collection.concurrentMutableMap +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.common.id.literal +import love.forte.simbot.component.qguild.QQGuildComponent +import love.forte.simbot.component.qguild.bot.QGBot +import love.forte.simbot.component.qguild.bot.config.QGBotComponentConfiguration +import love.forte.simbot.component.qguild.channel.* +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.guild.QGGuildRelation +import love.forte.simbot.component.qguild.internal.channel.* +import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl +import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl.Companion.qgGuild +import love.forte.simbot.component.qguild.internal.guild.QGMemberImpl +import love.forte.simbot.component.qguild.message.QGMessageReceipt +import love.forte.simbot.component.qguild.message.sendMessage +import love.forte.simbot.event.EventDispatcher +import love.forte.simbot.logger.LoggerFactory +import love.forte.simbot.message.Message +import love.forte.simbot.message.MessageContent +import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.qguild.addStackTrace +import love.forte.simbot.qguild.api.channel.GetChannelApi +import love.forte.simbot.qguild.api.channel.GetGuildChannelListApi +import love.forte.simbot.qguild.api.guild.GetGuildApi +import love.forte.simbot.qguild.api.member.GetMemberApi +import love.forte.simbot.qguild.api.user.GetBotGuildListApi +import love.forte.simbot.qguild.api.user.createFlow +import love.forte.simbot.qguild.ifNotFoundThenNull +import love.forte.simbot.qguild.model.ChannelType +import love.forte.simbot.qguild.model.SimpleChannel +import love.forte.simbot.qguild.model.SimpleGuild +import love.forte.simbot.qguild.model.isCategory +import love.forte.simbot.qguild.stdlib.requestDataBy +import kotlin.concurrent.Volatile +import kotlin.coroutines.CoroutineContext +import love.forte.simbot.qguild.model.User as QGUser +import love.forte.simbot.qguild.stdlib.Bot as StdlibBot + +/** + * + * @author ForteScarlet + */ +internal class QGBotImpl( + override val source: StdlibBot, + override val component: QQGuildComponent, + private val eventDispatcher: EventDispatcher, + configuration: QGBotComponentConfiguration +) : QGBot, JobBasedBot() { + private val logger = + LoggerFactory.getLogger("love.forte.simbot.component.qguild.bot.${source.ticket.secret}") + + override val job: Job + get() = source.coroutineContext[Job]!! + + override val coroutineContext: CoroutineContext + get() = source.coroutineContext + + private val cacheConfig = configuration.cacheConfig + private val cacheable = cacheConfig?.enable == true + + init { + // check config with warning log + if (configuration.cacheConfig?.dynamicCacheConfig?.enable == true) { + logger.warn("DynamicCacheConfig is not supported yet, but dynamicCacheConfig.enable == `true`. This will have no real effect.") + } + } + + @Volatile + private lateinit var botSelf: QGUser + + override val userId: ID + get() { + if (!::botSelf.isInitialized) { + throw IllegalStateException("Information of bot has not been initialized. Please execute the `start()` method at least once first") + } + + return botSelf.id.ID + } + + override val name: String + get() { + if (!::botSelf.isInitialized) { + throw IllegalStateException("Information of bot has not been initialized. Please execute the `start()` method at least once first") + } + + return botSelf.username + } + + override val avatar: String + get() = if (!::botSelf.isInitialized) "" else botSelf.avatar + + + override fun isMe(id: ID): Boolean { + if (id == this.id) return true + return ::botSelf.isInitialized && botSelf.id == id.literal + } + + override val guildRelation: QGGuildRelation = GuildRelationImpl() + + + private inner class GuildRelationImpl : QGGuildRelation { + @OptIn(ExperimentalCoroutinesApi::class) + override val guilds: Collectable + get() = queryGuildList().flatMapConcat { it.asFlow() }.map { + qgGuild(this@QGBotImpl, it) + }.asCollectable() + // TODO Spec Collectable type? + + + override suspend fun guild(id: ID): QGGuild? = queryGuild(id.literal) + + override suspend fun guildCount(): Int = + GetBotGuildListApi.createFlow { requestDataBy(source) }.count() + + override suspend fun channel(channelId: ID): QGChannel? = channel(channelId.literal, null) + + override suspend fun chatChannel(channelId: ID): QGTextChannel? { + val channel = channel(channelId) ?: return null + + return channel as? QGTextChannel + ?: throw IllegalStateException("The type of channel(id=${channel.source.id}, name=${channel.source.name}) is not category (${ChannelType.TEXT}), it is ${channel.source.type}") + } + + override suspend fun category(channelId: ID): QGCategoryChannel? { + val channel = channel(channelId) ?: return null + + return channel as? QGCategoryChannel + ?: throw IllegalStateException("The type of channel(id=${channel.source.id}, name=${channel.source.name}) is not category (${ChannelType.CATEGORY}), it is ${channel.source.type}") + } + + override suspend fun forumChannel(id: ID): QGForumChannel? { + val channel = channel(id) ?: return null + + return channel as? QGForumChannel + ?: throw IllegalStateException("The type of channel(id=${channel.source.id}, name=${channel.source.name}) is not category (${ChannelType.FORUM}), it is ${channel.source.type}") + } + } + + + internal suspend fun queryGuild(id: String): QGGuildImpl? { + return try { + GetGuildApi.create(id).requestDataBy(source) + } catch (apiEx: QQGuildApiException) { + apiEx.ifNotFoundThenNull() + }?.let { guild -> qgGuild(this, guild) } + } + + private fun queryGuildList(batch: Int = 0): Flow> = flow { + val limit = batch.takeIf { it > 0 } ?: GetBotGuildListApi.DEFAULT_LIMIT + var lastId: String? = null + while (true) { + val list = GetBotGuildListApi.create(after = lastId, limit = limit).requestDataBy(source) + if (list.isEmpty()) break + lastId = list.last().id + emit(list) + } + } + + internal suspend fun querySimpleChannel(id: String): SimpleChannel? { + return try { + GetChannelApi.create(id).requestDataBy(source) + } catch (apiEx: QQGuildApiException) { + apiEx.ifNotFoundThenNull() + } + } + + internal fun channelFlow(guildId: String): Flow { + return flow { + GetGuildChannelListApi.create(guildId) + .requestDataBy(source) + .forEach { + emit(it) + } + } + } + + + internal suspend fun channel(id: String, sourceGuild: QGGuildImpl?): QGChannel? { + val channelInfo = querySimpleChannel(id) ?: return null + + return when (channelInfo.type) { + ChannelType.CATEGORY -> QGCategoryChannelImpl( + bot = this, + source = channelInfo, + sourceGuild = checkIfTransmitCacheable(sourceGuild), + ) + + ChannelType.TEXT -> QGTextChannelImpl( + bot = this, + source = channelInfo, + sourceGuild = checkIfTransmitCacheable(sourceGuild), + ) + + ChannelType.FORUM -> QGForumChannelImpl( + bot = this, + source = channelInfo, + sourceGuild = checkIfTransmitCacheable(sourceGuild), + ) + + else -> QGNonTextChannelImpl( + bot = this, + source = channelInfo, + sourceGuild = checkIfTransmitCacheable(sourceGuild), + ) + } + } + + internal data class ChannelInfoWithCategory(val info: SimpleChannel, val category: QGCategory) + + internal fun channelFlowWithCategoryId(guildId: String, sourceGuild: QGGuildImpl?): Flow { + val categoryMap = concurrentMutableMap() + + return channelFlow(guildId).filter { info -> + val gid = info.guildId.ID + if (info.type.isCategory) { + categoryMap.computeValueIfAbsent(info.id) { + QGCategoryImpl( + bot = this, + guildId = gid, + id = info.id.ID, + sourceGuild = checkIfTransmitCacheable(sourceGuild), + source = QGCategoryChannelImpl( + bot = this, + source = info, + sourceGuild = sourceGuild, + ), + ) + } + + false + } else { + true + } + }.map { info -> + val gid = info.guildId.ID + val category = categoryMap.computeValueIfAbsent(info.parentId) { cid -> + QGCategoryImpl( + bot = this, + guildId = gid, + id = cid.ID, + sourceGuild = checkIfTransmitCacheable(sourceGuild), + ) + } + + ChannelInfoWithCategory(info, category) + } + } + + /** + * 通过API实时查询channels列表 + */ + internal fun queryChannels(guildId: String, sourceGuild: QGGuildImpl?): Flow = flow { + channelFlowWithCategoryId(guildId, sourceGuild).map { (info, category) -> + when (info.type) { + ChannelType.TEXT -> QGTextChannelImpl( + bot = this@QGBotImpl, + source = info, + sourceGuild = checkIfTransmitCacheable(sourceGuild), + category = category, + ) + + ChannelType.FORUM -> QGForumChannelImpl( + bot = this@QGBotImpl, + source = info, + sourceGuild = checkIfTransmitCacheable(sourceGuild), + category = category, + ) + + else -> QGNonTextChannelImpl( + bot = this@QGBotImpl, + source = info, + sourceGuild = checkIfTransmitCacheable(sourceGuild), + category = category + ) + } + } + } + + override suspend fun sendTo(channelId: ID, text: String): QGMessageReceipt { + return try { + sendMessage(channelId.literal, text) + } catch (e: QQGuildApiException) { + throw e.addStackTrace { "Bot.sendTo" } + } + } + + override suspend fun sendTo(channelId: ID, message: Message): QGMessageReceipt { + return try { + sendMessage(channelId.literal, message) + } catch (e: QQGuildApiException) { + throw e.addStackTrace { "Bot.sendTo" } + } + } + + override suspend fun sendTo(channelId: ID, message: MessageContent): QGMessageReceipt { + return try { + sendMessage(channelId.literal, message) + } catch (e: QQGuildApiException) { + throw e.addStackTrace { "Bot.sendTo" } + } + } + + internal suspend fun member(guildId: String, userId: String, sourceGuild: QGGuildImpl?): QGMemberImpl? { + val member = try { + GetMemberApi.create(guildId, userId).requestDataBy(source) + } catch (apiEx: QQGuildApiException) { + apiEx.ifNotFoundThenNull() + } + + return member?.let { info -> + QGMemberImpl( + bot = this, + source = info, + guildId = guildId.ID, + sourceGuild = checkIfTransmitCacheable(sourceGuild) + ) + } + } + + private val startLock = Mutex() + + override suspend fun me(withCache: Boolean): QGUser { + if (withCache && ::botSelf.isInitialized) { + return botSelf + } + + return source.me().also { botSelf = it } + } + + + @Volatile + private var sourceListenerDisposableHandle: DisposableHandle? = null + + /** + * 启动当前bot。 + */ + override suspend fun start() { + startLock.withLock { + source.start().also { + // just set everytime. + botSelf = me().also { me -> + logger.debug("bot own information: {}", me) + } + + suspend fun pushStartedEvent() { + // TODO +// if (eventDispatcher.isProcessable(QGBotStartedEvent)) { +// launch { +// eventProcessor.push(QGBotStartedEventImpl(this@QGBotImpl)) +// } +// } + } + + if (!isStarted) { + pushStartedEvent() + return@also + } + + sourceListenerDisposableHandle?.also { handle -> + handle.dispose() + sourceListenerDisposableHandle = null + } + // TODO +// sourceListenerDisposableHandle = registerEventProcessor() + pushStartedEvent() + } + + isStarted = true + } + } + + + private val isTransmitCacheable = cacheable && cacheConfig?.transmitCacheConfig?.enable == true + + internal fun checkIfTransmitCacheable(target: T): T? = target.takeIf { isTransmitCacheable } + + override fun toString(): String { + // 还未初始化 + val uid = if (::botSelf.isInitialized) botSelf.id else "(Not initialized yet)" + return "QGBotImpl(appId=$id, userId=$uid, isActive=$isActive)" + } +} + + +/** + * 从 [QGBot] 中分配一个拥有新的 [SupervisorJob] 的 [CoroutineContext]. + */ +internal fun CoroutineScope.newSupervisorCoroutineContext(): CoroutineContext = + coroutineContext + SupervisorJob(coroutineContext[Job]) diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt new file mode 100644 index 00000000..225e45b3 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2021-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.bot + +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancel +import love.forte.simbot.bot.ConflictBotException +import love.forte.simbot.bot.NoSuchBotException +import love.forte.simbot.common.atomic.atomic +import love.forte.simbot.common.collection.ExperimentalSimbotCollectionApi +import love.forte.simbot.common.collection.createConcurrentQueue +import love.forte.simbot.common.coroutines.linkTo +import love.forte.simbot.common.id.ID +import love.forte.simbot.component.qguild.QQGuildComponent +import love.forte.simbot.component.qguild.bot.QGBot +import love.forte.simbot.component.qguild.bot.QQGuildBotManager +import love.forte.simbot.component.qguild.bot.QQGuildBotManagerConfiguration +import love.forte.simbot.component.qguild.bot.config.QGBotComponentConfiguration +import love.forte.simbot.event.EventDispatcher +import love.forte.simbot.logger.Logger +import love.forte.simbot.logger.LoggerFactory +import love.forte.simbot.logger.logger +import love.forte.simbot.qguild.stdlib.Bot +import love.forte.simbot.qguild.stdlib.BotFactory +import kotlin.coroutines.CoroutineContext +import kotlin.coroutines.EmptyCoroutineContext + +/** + * + * @author ForteScarlet + */ +@OptIn(ExperimentalSimbotCollectionApi::class) +internal class QQGuildBotManagerImpl( + override val eventDispatcher: EventDispatcher, + override val configuration: QQGuildBotManagerConfiguration, + override val component: QQGuildComponent, + override val job: Job, + private val coroutineContext: CoroutineContext // 不应包含 Job +) : QQGuildBotManager() { + companion object { + private val LOGGER = LoggerFactory.logger() + } + + override val logger: Logger + get() = LOGGER + + /** + * 允许重复的 id 的bot。 + * + */ + private var bots = createConcurrentQueue() + + init { + job.invokeOnCompletion { + // TODO to clear + bots.removeIf { + it.cancel("BotManager was on completion") + true + } + } + } + + override fun get(id: ID): QGBot { + if (!isActive) throw IllegalStateException("This manager has been completed") + + val all = all(id).take(2).toList() + when { + all.size > 1 -> throw ConflictBotException("id $id with $all") + all.isEmpty() -> throw NoSuchBotException("id=$id") + else -> return all.first() + } + } + + override fun all(): Sequence { + if (!isActive) return emptySequence() + return bots.asSequence() + } + + override fun all(id: ID): Sequence = + all().filter { it.isMe(id) } + + private val onRegister = atomic(false) + + override fun register(ticket: Bot.Ticket, block: QGBotComponentConfiguration.() -> Unit): QGBot { + while (true) { + if (!isActive) throw IllegalStateException("This manager has been completed") + if (onRegister.compareAndSet(expect = false, value = true)) { + break + } + } + + try { + return doRegister(ticket, block) + } finally { + onRegister.value = false + } + } + + override fun register( + appId: String, + secret: String, + token: String, + block: QGBotComponentConfiguration.() -> Unit, + ): QGBot = register(Bot.Ticket(appId, secret, token), block) + + private fun doRegister( + ticket: Bot.Ticket, + block: QGBotComponentConfiguration.() -> Unit, + ): QGBot { + val configure = configuration.botConfigure + val config = QGBotComponentConfiguration().also(block) + val sourceBot = BotFactory.create(ticket) { + // init context + coroutineContext = if (coroutineContext == EmptyCoroutineContext) { + this@QQGuildBotManagerImpl.coroutineContext + } else { + this@QQGuildBotManagerImpl.coroutineContext + coroutineContext + } + + configure(ticket.appId, ticket.secret, ticket.token) + config.botConfigure(this) + + val customJob = coroutineContext[Job] + if (customJob == null) { + coroutineContext += SupervisorJob(job) + } else { + customJob.linkTo(job) + } + } + + // check botInfo + logger.info("Registered bot appId: {}", sourceBot.ticket.appId) + val newBot = QGBotImpl( + source = sourceBot, + component = component, + eventDispatcher = eventDispatcher, + configuration = config + ) + + bots.add(newBot) + + onCompletion { + // remove self on completion + bots.remove(newBot) + } + + // TODO +// eventProcessor.pushAndLaunch(newBot, QGBotRegisteredEventImpl(bot)) + + return newBot + } + +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGChannelCategoryImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGChannelCategoryImpl.kt new file mode 100644 index 00000000..5600a32c --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGChannelCategoryImpl.kt @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.channel + +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.component.qguild.channel.QGCategory +import love.forte.simbot.component.qguild.channel.QGCategoryChannel +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.bot.newSupervisorCoroutineContext +import love.forte.simbot.qguild.model.Channel +import kotlin.concurrent.Volatile +import kotlin.coroutines.CoroutineContext + + +internal class QGCategoryImpl( + private val bot: QGBotImpl, + private val guildId: ID, + override val id: ID, + private val sourceGuild: QGGuild? = null, + source: QGCategoryChannel? = null +) : QGCategory { + @Volatile + private lateinit var _channel: QGCategoryChannel + + init { + source?.also { _channel = it } + } + + private val initLock = Mutex() + + override suspend fun resolveToChannel(): QGCategoryChannel { + return if (::_channel.isInitialized) _channel else initLock.withLock { + if (::_channel.isInitialized) _channel else { + guild().category(id)?.also { + _channel = it + } ?: throw NoSuchElementException("category(id=$id)") + } + } + } + + override suspend fun name(): String = resolveToChannel().name + + private suspend fun guild(): QGGuild = + sourceGuild + ?: bot.guildRelation.guild(guildId) + ?: throw NoSuchElementException("guild(id=$guildId)") + + override fun toString(): String { + return "QGCategory(id=$id, guildId=$guildId)" + } +} + +internal class QGCategoryChannelImpl( + private val bot: QGBotImpl, + override val source: Channel, + private val sourceGuild: QGGuild? = null, +) : QGCategoryChannel { + override val coroutineContext: CoroutineContext = bot.newSupervisorCoroutineContext() + override val category: QGCategory + get() = QGCategoryImpl(bot, source.guildId.ID, id, sourceGuild, this) +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGForumChannelImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGForumChannelImpl.kt new file mode 100644 index 00000000..da1376cf --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGForumChannelImpl.kt @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.channel + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map +import love.forte.simbot.common.collectable.Collectable +import love.forte.simbot.common.collectable.asCollectable +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.common.id.literal +import love.forte.simbot.component.qguild.channel.QGCategory +import love.forte.simbot.component.qguild.channel.QGChannel +import love.forte.simbot.component.qguild.channel.QGForumChannel +import love.forte.simbot.component.qguild.forum.QGThread +import love.forte.simbot.component.qguild.forum.QGThreadCreator +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.bot.newSupervisorCoroutineContext +import love.forte.simbot.component.qguild.internal.forum.QGThreadCreatorImpl +import love.forte.simbot.component.qguild.internal.forum.QGThreadImpl +import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.qguild.api.forum.GetThreadApi +import love.forte.simbot.qguild.api.forum.GetThreadListApi +import love.forte.simbot.qguild.ifNotFoundThenNull +import love.forte.simbot.qguild.model.Channel +import love.forte.simbot.qguild.model.ChannelType +import love.forte.simbot.qguild.model.forum.Thread +import love.forte.simbot.qguild.stdlib.requestDataBy +import kotlin.coroutines.CoroutineContext + + +/** + * + * @author ForteScarlet + */ +internal class QGForumChannelImpl( + private val bot: QGBotImpl, + override val source: Channel, + internal val sourceGuild: QGGuild?, + override val category: QGCategory = QGCategoryImpl( + bot = bot, + guildId = source.guildId.ID, + id = source.parentId.ID, + sourceGuild = sourceGuild, + ), +) : QGForumChannel { + override val coroutineContext: CoroutineContext = bot.newSupervisorCoroutineContext() +// override val guildId: ID = source.guildId.ID +// override val ownerId: ID = source.ownerId.ID +// +// override suspend fun owner(): QGMember = +// guild().member(ownerId) ?: throw NoSuchElementException("owner(id=$ownerId)") +// +// override suspend fun guild(): QGGuild = +// sourceGuild ?: bot.guild(guildId) ?: throw NoSuchElementException("guild(id=$guildId)") + + private fun threadFlow(): Flow = flow { + GetThreadListApi.create(source.id).requestDataBy(bot.source).threads.forEach { + emit(it) + } + } + + override val threads: Collectable + get() = threadFlow().map { it.toQGThread() }.asCollectable() + + override suspend fun thread(id: ID): QGThread? { + return try { + GetThreadApi.create(source.id, id.literal).requestDataBy(bot.source).thread.toQGThread() + } catch (apiEx: QQGuildApiException) { + apiEx.ifNotFoundThenNull() + } + } + + private fun Thread.toQGThread(): QGThread = QGThreadImpl(bot, this, this@QGForumChannelImpl) + + override fun threadCreator(): QGThreadCreator = QGThreadCreatorImpl(this) + + override fun toString(): String { + return "QGForumChannel(id=$id, name=$name, category=$category)" + } +} + +/** + * @throws IllegalStateException Can't as [QGForumChannel] + */ +internal fun QGChannel.asForumChannel(source: Channel = this.source): QGForumChannel = + this as? QGForumChannel + ?: throw IllegalStateException("The type of channel(id=${source.id}, name=${source.name}) in guild(id=${source.guildId}) is not category (${ChannelType.CATEGORY}), but ${source.type}") + diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGNonTextChannelImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGNonTextChannelImpl.kt new file mode 100644 index 00000000..8c3b4be6 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGNonTextChannelImpl.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.channel + +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.component.qguild.channel.QGCategory +import love.forte.simbot.component.qguild.channel.QGNonTextChannel +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.bot.newSupervisorCoroutineContext +import love.forte.simbot.qguild.model.Channel +import kotlin.coroutines.CoroutineContext + + +/** + * [QGNonTextChannel] 基本实现,用于包装那些非特殊实现的非文字子频道类型。 + * + * [QGNonTextChannelImpl] 类型作为 [QGNonTextChannel] 实现的默认替补,且不对外公开。 + * + * @author ForteScarlet + */ +internal class QGNonTextChannelImpl( + private val bot: QGBotImpl, + override val source: Channel, + private val sourceGuild: QGGuild? = null, + override val category: QGCategory = QGCategoryImpl( + bot = bot, + guildId = source.guildId.ID, + id = source.parentId.ID, + sourceGuild = sourceGuild, + ), +) : QGNonTextChannel { + override val coroutineContext: CoroutineContext = bot.newSupervisorCoroutineContext() + override val id: ID = source.id.ID +// private val guildId: ID get() = source.guildId.ID +// private val ownerId: ID get() = source.ownerId.ID + +// private suspend fun owner(): QGMember = guild().member(ownerId) ?: throw NoSuchElementException("owner(id=$ownerId)") +// +// private suspend fun guild(): QGGuild = +// sourceGuild +// ?: bot.guildRelation.guild(guildId) +// ?: throw NoSuchElementException("guild(id=$guildId)") + + override fun toString(): String { + return "QGNonTextChannel(id=$id, name=$name, category=$category)" + } +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGTextChannelImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGTextChannelImpl.kt new file mode 100644 index 00000000..3508f7d4 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGTextChannelImpl.kt @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.channel + +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.component.qguild.channel.QGCategory +import love.forte.simbot.component.qguild.channel.QGTextChannel +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.bot.newSupervisorCoroutineContext +import love.forte.simbot.component.qguild.message.QGMessageReceipt +import love.forte.simbot.component.qguild.message.sendMessage +import love.forte.simbot.message.Message +import love.forte.simbot.message.MessageContent +import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.qguild.addStackTrace +import kotlin.coroutines.CoroutineContext +import love.forte.simbot.qguild.model.Channel as QGSourceChannel + +/** + * + * @author ForteScarlet + */ +internal class QGTextChannelImpl internal constructor( + private val bot: QGBotImpl, + override val source: QGSourceChannel, + private val sourceGuild: QGGuild? = null, + override val category: QGCategory = QGCategoryImpl( + bot = bot, + guildId = source.guildId.ID, + id = source.parentId.ID, + sourceGuild = sourceGuild, + ), + /** + * 如果是从一个事件而来,提供可用于消息回复的 msgId 来避免 event.channel().send(...) 出现问题 + */ + private val currentMsgId: String? = null, +) : QGTextChannel { + override val coroutineContext: CoroutineContext = bot.newSupervisorCoroutineContext() + override val id: ID = source.id.ID +// private val guildId: ID get() = source.guildId.ID +// private val ownerId: ID get() = source.ownerId.ID + +// private suspend fun guild(): QGGuild = +// sourceGuild +// ?: bot.guildRelation.guild(guildId) +// ?: throw NoSuchElementException("guild(id=$guildId)") +// +// private suspend fun owner(): QGMember = +// guild().member(ownerId) ?: throw NoSuchElementException("owner(id=$ownerId)") + + override suspend fun send(message: Message): QGMessageReceipt { + return try { + bot.sendMessage(source.id, message) { + if (msgId == null) { + msgId = currentMsgId + } + } + } catch (e: QQGuildApiException) { + throw e.addStackTrace { "channel.send" } + } + } + + override suspend fun send(messageContent: MessageContent): QGMessageReceipt { + return try { + bot.sendMessage(source.id, messageContent) { + if (msgId == null) { + msgId = currentMsgId + } + } + } catch (e: QQGuildApiException) { + throw e.addStackTrace { "channel.send" } + } + } + + override suspend fun send(text: String): QGMessageReceipt { + return try { + bot.sendMessage(source.id, text) { + if (msgId == null) { + msgId = currentMsgId + } + } + } catch (e: QQGuildApiException) { + throw e.addStackTrace { "channel.send" } + } + } + + override fun toString(): String { + return "QGTextChannel(id=$id, name=$name, category=$category)" + } +} + + + + diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt new file mode 100644 index 00000000..759b80fc --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.forum + +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.map +import love.forte.simbot.common.id.ID +import love.forte.simbot.component.qguild.QGBot +import love.forte.simbot.component.qguild.QGGuild +import love.forte.simbot.component.qguild.bot.QGBot +import love.forte.simbot.component.qguild.channel.QGForumChannel +import love.forte.simbot.component.qguild.forum.QGForums +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.internal.bot.newSupervisorCoroutineContext +import love.forte.simbot.component.qguild.internal.channel.QGForumChannelImpl +import love.forte.simbot.component.qguild.internal.channel.asForumChannel +import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl +import love.forte.simbot.component.qguild.internal.newSupervisorCoroutineContext +import love.forte.simbot.qguild.model.ChannelType +import love.forte.simbot.utils.item.Items +import love.forte.simbot.utils.item.effectedItemsByFlow +import kotlin.coroutines.CoroutineContext + + +/** + * + * @author ForteScarlet + */ +internal class QGForumsImpl( + private val sourceGuild: QGGuildImpl, +) : QGForums { + override val coroutineContext: CoroutineContext = sourceGuild.newSupervisorCoroutineContext() + override val bot: QGBot get() = sourceGuild.baseBot + + override suspend fun guild(): QGGuild = sourceGuild + + override val forumChannels: Items + get() = effectedItemsByFlow { + sourceGuild.baseBot.channelFlowWithCategoryId(sourceGuild.source.id, sourceGuild) + .filter { (info, _) -> + info.type == ChannelType.FORUM + }.map { (info, category) -> + QGForumChannelImpl( + bot = sourceGuild.bot, + source = info, + sourceGuild = sourceGuild.baseBot.checkIfTransmitCacheable(sourceGuild), + category = category + ) + } + } + + override suspend fun forumChannel(id: ID): QGForumChannel? { + val channel = sourceGuild.channel(id) ?: return null + + return channel.asForumChannel(channel.source) + } +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGPostImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGPostImpl.kt new file mode 100644 index 00000000..5c603576 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGPostImpl.kt @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.forum + +import love.forte.simbot.ExperimentalSimbotApi +import love.forte.simbot.Timestamp +import love.forte.simbot.common.time.Timestamp +import love.forte.simbot.component.qguild.QGGuild +import love.forte.simbot.component.qguild.QGMember +import love.forte.simbot.component.qguild.channel.QGForumChannel +import love.forte.simbot.component.qguild.forum.QGPost +import love.forte.simbot.component.qguild.forum.QGThread +import love.forte.simbot.component.qguild.internal.QGGuildImpl +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.channel.QGForumChannelImpl +import love.forte.simbot.component.qguild.internal.channel.asForumChannel +import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl +import love.forte.simbot.component.qguild.internal.toTimestamp +import love.forte.simbot.component.qguild.util.requestBy +import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.qguild.api.forum.GetThreadApi +import love.forte.simbot.qguild.ifNotFoundThenNoSuch +import love.forte.simbot.qguild.model.forum.Post +import love.forte.simbot.qguild.stdlib.requestBy + +/** + * + * @author ForteScarlet + */ +internal class QGPostImpl( + override val bot: QGBotImpl, + override val source: Post, + private val sourceChannel: QGForumChannelImpl? +) : QGPost { + + @OptIn(ExperimentalSimbotApi::class) + override val datetime: Timestamp = source.postInfo.dateTime.toTimestamp() + + override suspend fun guild(): QGGuild = + sourceChannel?.guild() + ?: bot.queryGuild(source.guildId) + ?: throw NoSuchElementException("guild(id=${source.guildId})") + + + override suspend fun channel(): QGForumChannel = + sourceChannel + ?: (bot.channel(source.channelId, null) + ?: throw NoSuchElementException("channel(id=${source.channelId})")).asForumChannel() + + override suspend fun author(): QGMember = + bot.member(source.guildId, source.authorId, sourceChannel?.sourceGuild as? QGGuildImpl) ?: throw NoSuchElementException("author(id=$authorId)") + + override suspend fun thread(): QGThread { + val (thread) = try { + GetThreadApi.create(source.channelId, source.postInfo.threadId).requestBy(bot) + } catch (apiEx: QQGuildApiException) { + apiEx.ifNotFoundThenNoSuch { "thread(id=${source.postInfo.threadId}) by post(id=${source.postInfo.postId})" } + } + + return QGThreadImpl(bot, thread, sourceChannel) + } +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGReplyImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGReplyImpl.kt new file mode 100644 index 00000000..35d0d8b2 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGReplyImpl.kt @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.forum + +import love.forte.simbot.ExperimentalSimbotApi +import love.forte.simbot.Timestamp +import love.forte.simbot.common.time.Timestamp +import love.forte.simbot.component.qguild.QGGuild +import love.forte.simbot.component.qguild.QGMember +import love.forte.simbot.component.qguild.channel.QGForumChannel +import love.forte.simbot.component.qguild.forum.QGReply +import love.forte.simbot.component.qguild.forum.QGThread +import love.forte.simbot.component.qguild.internal.QGGuildImpl +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.channel.QGForumChannelImpl +import love.forte.simbot.component.qguild.internal.channel.asForumChannel +import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl +import love.forte.simbot.component.qguild.internal.toTimestamp +import love.forte.simbot.component.qguild.util.requestBy +import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.qguild.api.forum.GetThreadApi +import love.forte.simbot.qguild.ifNotFoundThenNoSuch +import love.forte.simbot.qguild.model.forum.Reply +import love.forte.simbot.qguild.stdlib.requestBy + +/** + * + * @author ForteScarlet + */ +internal class QGReplyImpl( + override val bot: QGBotImpl, + override val source: Reply, + private val sourceChannel: QGForumChannelImpl? +) : QGReply { + @OptIn(ExperimentalSimbotApi::class) + override val datetime: Timestamp = source.replyInfo.dateTime.toTimestamp() + + override suspend fun guild(): QGGuild = + sourceChannel?.guild() + ?: bot.queryGuild(source.guildId) + ?: throw NoSuchElementException("guild(id=${source.guildId})") + + + override suspend fun channel(): QGForumChannel = + sourceChannel + ?: (bot.channel(source.channelId, null) + ?: throw NoSuchElementException("channel(id=${source.channelId})")).asForumChannel() + + + override suspend fun author(): QGMember = + bot.member(source.guildId, source.authorId, sourceChannel?.sourceGuild as? QGGuildImpl) ?: throw NoSuchElementException("author(id=$authorId)") + + override suspend fun thread(): QGThread { + val (thread) = try { + GetThreadApi.create(source.channelId, source.replyInfo.threadId).requestBy(bot) + } catch (apiEx: QQGuildApiException) { + apiEx.ifNotFoundThenNoSuch { "thread(id=${source.replyInfo.threadId}) by reply(id=${source.replyInfo.replyId})" } + } + + return QGThreadImpl(bot, thread, sourceChannel) + } +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadCreatorImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadCreatorImpl.kt new file mode 100644 index 00000000..9e34c01a --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadCreatorImpl.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.forum + +import love.forte.simbot.component.qguild.forum.QGThreadCreator +import love.forte.simbot.component.qguild.internal.channel.QGForumChannelImpl +import love.forte.simbot.component.qguild.util.requestBy +import love.forte.simbot.qguild.api.forum.PublishThreadApi +import love.forte.simbot.qguild.api.forum.ThreadPublishFormat +import love.forte.simbot.qguild.api.forum.ThreadPublishResult +import love.forte.simbot.qguild.stdlib.requestBy + + +/** + * + * @author ForteScarlet + */ +internal class QGThreadCreatorImpl(private val channel: QGForumChannelImpl) : QGThreadCreator { + override var title: String? = null + override var content: String? = null + override var format: Int? = null + override var formatType: ThreadPublishFormat? + get() = format?.let { ThreadPublishFormat.values().find { f -> f.value == it } } + set(value) { + format = value?.value + } + + override suspend fun publish(): ThreadPublishResult { + return PublishThreadApi.create( + channelId = channel.source.id, + title = title ?: throw IllegalArgumentException("Required property 'title' is null"), + content = content ?: throw IllegalArgumentException("Required property 'content' is null"), + format = format ?: throw IllegalArgumentException("Required property 'format' is null"), + ).requestBy(channel.bot) + } + + override fun toString(): String { + return "QGThreadCreatorImpl(title=$title, content=$content, format=$format, formatType=$formatType)" + } +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt new file mode 100644 index 00000000..a2d6d1db --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.forum + +import love.forte.simbot.ExperimentalSimbotApi +import love.forte.simbot.Timestamp +import love.forte.simbot.component.qguild.QGGuild +import love.forte.simbot.component.qguild.QGMember +import love.forte.simbot.component.qguild.channel.QGForumChannel +import love.forte.simbot.component.qguild.forum.QGForumChannel +import love.forte.simbot.component.qguild.forum.QGThread +import love.forte.simbot.component.qguild.internal.QGBotImpl +import love.forte.simbot.component.qguild.internal.QGGuildImpl +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.channel.QGForumChannelImpl +import love.forte.simbot.component.qguild.internal.newSupervisorCoroutineContext +import love.forte.simbot.component.qguild.internal.toTimestamp +import love.forte.simbot.component.qguild.util.requestBy +import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.qguild.api.forum.DeleteThreadApi +import love.forte.simbot.qguild.ifNotFoundThen +import love.forte.simbot.qguild.model.forum.Thread +import kotlin.coroutines.CoroutineContext + + +/** + * + * @author ForteScarlet + */ +internal class QGThreadImpl( + override val bot: QGBotImpl, + override val source: Thread, + private val sourceChannel: QGForumChannelImpl?, +) : QGThread { + + override val coroutineContext: CoroutineContext = bot.newSupervisorCoroutineContext() + + override suspend fun guild(): QGGuild = + sourceChannel?.guild() + ?: bot.queryGuild(source.guildId) + ?: throw NoSuchElementException("guild(id=${source.guildId})") + + override suspend fun author(): QGMember = + bot.member(source.guildId, source.authorId, sourceChannel?.sourceGuild as? QGGuildImpl) ?: throw NoSuchElementException("author(id=$authorId)") + + @OptIn(ExperimentalSimbotApi::class) + override val dateTime: Timestamp + get() = source.threadInfo.dateTime.toTimestamp() + + override suspend fun channel(): QGForumChannel { + if (sourceChannel != null) { + return sourceChannel + } + + val channel = bot.channel(source.channelId, null) + ?: throw NoSuchElementException("channel(id=${source.channelId})") + + return channel.asForumChannel() + } + + override suspend fun delete(): Boolean { + return try { + DeleteThreadApi.create(source.channelId, source.threadInfo.threadId).requestBy(bot) + true + } catch (apiEx: QQGuildApiException) { + apiEx.ifNotFoundThen { false } + } + } + + override fun toString(): String { + return "QGThreadImpl(id=${source.threadInfo.threadId}, title=$title, channelId=${source.channelId})" + } +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGGuildImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGGuildImpl.kt new file mode 100644 index 00000000..1800f532 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGGuildImpl.kt @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.guild + +import kotlinx.coroutines.CompletableJob +import kotlinx.coroutines.Job +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map +import love.forte.simbot.common.collectable.Collectable +import love.forte.simbot.common.collectable.asCollectable +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.literal +import love.forte.simbot.component.qguild.ExperimentalQGApi +import love.forte.simbot.component.qguild.channel.QGCategoryChannel +import love.forte.simbot.component.qguild.channel.QGChannel +import love.forte.simbot.component.qguild.channel.QGForumChannel +import love.forte.simbot.component.qguild.channel.QGTextChannel +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.channel.QGCategoryChannelImpl +import love.forte.simbot.component.qguild.role.QGGuildRole +import love.forte.simbot.component.qguild.role.QGRoleCreator +import love.forte.simbot.definition.Member +import love.forte.simbot.qguild.api.apipermission.ApiPermissions +import love.forte.simbot.qguild.api.apipermission.GetApiPermissionListApi +import love.forte.simbot.qguild.api.member.GetGuildMemberListApi +import love.forte.simbot.qguild.api.member.createFlow +import love.forte.simbot.qguild.model.ChannelType +import love.forte.simbot.qguild.model.Guild +import love.forte.simbot.qguild.model.isCategory +import love.forte.simbot.qguild.stdlib.requestDataBy +import kotlin.coroutines.CoroutineContext + + +/** + * + * @author ForteScarlet + */ +internal class QGGuildImpl private constructor( + internal val bot: QGBotImpl, + override val source: Guild, + override val coroutineContext: CoroutineContext +) : QGGuild { + override suspend fun permissions(): ApiPermissions = + GetApiPermissionListApi.create(source.id).requestDataBy(bot.source) + + override suspend fun botAsMember(): Member { + TODO("Not yet implemented") + } + + override val members: Collectable + get() = queryMembers() + + // TODO Spec Collectable type? + private fun queryMembers(): Collectable = flow { // prop -> + // 批次 +// val batchLimit = prop.batch.takeIf { it > 0 } ?: GetGuildMemberListApi.MAX_LIMIT + val batchLimit = GetGuildMemberListApi.MAX_LIMIT + val flow = + GetGuildMemberListApi.createFlow(guildId = source.id, batch = batchLimit) { requestDataBy(bot.source) } +// .let(prop::effectOn) + + emitAll(flow.map { m -> + QGMemberImpl( + bot = bot, + source = m, + guildId = this@QGGuildImpl.id, + sourceGuild = bot.checkIfTransmitCacheable(this@QGGuildImpl) + ) + }) + }.asCollectable() + + override suspend fun member(id: ID): QGMemberImpl? = member(id.literal) + + internal suspend fun member(id: String): QGMemberImpl? = bot.member(source.id, id, this) + + // TODO + @ExperimentalQGApi + override val roles: Collectable + get() = TODO() +// get() = bot.effectedFlowItems { +// GetGuildRoleListApi.create(source.id).requestBy(bot).roles.forEach { info -> +// val roleImpl = QGGuildRoleImpl( +// bot = bot, +// guildId = id, +// source = info, +// sourceGuild = bot.checkIfTransmitCacheable(this@QGGuildImpl) +// ) +// +// emit(roleImpl) +// } +// } + + + @ExperimentalQGApi + override fun roleCreator(): QGRoleCreator = TODO() //QGRoleCreatorImpl(this) + + // + override val channels: Collectable + get() = bot.queryChannels(source.id, bot.checkIfTransmitCacheable(this)) + .asCollectable() + + override suspend fun channel(id: ID): QGChannel? = + bot.channel(id.literal, this) + + override val chatChannels: Collectable + get() = TODO("Not yet implemented") + + override suspend fun chatChannel(id: ID): QGTextChannel? { + TODO("Not yet implemented") + } + + override val forumChannels: Collectable + get() = TODO("Not yet implemented") + + override suspend fun forumChannel(id: ID): QGForumChannel? { + TODO("Not yet implemented") + } + + override val categories: Collectable + get() = queryCategories() + + private fun queryCategories(): Collectable = bot.channelFlow(source.id) + .filter { it.type.isCategory } + .map { info -> + QGCategoryChannelImpl( + bot = bot, + source = info, + sourceGuild = this, + ) + }.asCollectable() + + override suspend fun category(id: ID): QGCategoryChannel? { + val channel = channel(id) ?: return null +// + return channel as? QGCategoryChannel + ?: throw IllegalStateException("The type of channel(id=${channel.source.id}, name=${channel.source.name}) in guild(id=${source.id}, name=$name) is not category (${ChannelType.CATEGORY}), but ${channel.source.type}") + } + + + override fun toString(): String { + return "QGGuild(id=$id, name=$name)" + } + + companion object { + internal fun qgGuild(bot: QGBotImpl, guild: Guild): QGGuildImpl { + val job: CompletableJob = SupervisorJob(bot.coroutineContext[Job]) + val coroutineContext: CoroutineContext = bot.coroutineContext + job + + return QGGuildImpl(bot, guild, coroutineContext) + } + } +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGMemberImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGMemberImpl.kt new file mode 100644 index 00000000..5b68ee08 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGMemberImpl.kt @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.guild + +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import love.forte.simbot.common.collectable.Collectable +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.common.id.literal +import love.forte.simbot.component.qguild.ExperimentalQGApi +import love.forte.simbot.component.qguild.guild.QGMember +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.bot.newSupervisorCoroutineContext +import love.forte.simbot.component.qguild.internal.message.asReceipt +import love.forte.simbot.component.qguild.message.MessageParsers +import love.forte.simbot.component.qguild.message.QGMessageContent +import love.forte.simbot.component.qguild.message.QGMessageReceipt +import love.forte.simbot.component.qguild.message.sendMessage +import love.forte.simbot.component.qguild.role.QGMemberRole +import love.forte.simbot.message.Message +import love.forte.simbot.message.MessageContent +import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.qguild.addStackTrace +import love.forte.simbot.qguild.api.message.MessageSendApi +import love.forte.simbot.qguild.api.message.direct.CreateDmsApi +import love.forte.simbot.qguild.api.message.direct.DmsSendApi +import love.forte.simbot.qguild.model.DirectMessageSession +import love.forte.simbot.qguild.stdlib.requestDataBy +import kotlin.concurrent.Volatile +import kotlin.coroutines.CoroutineContext +import kotlin.jvm.JvmSynthetic +import love.forte.simbot.qguild.model.Member as QGSourceMember + +/** + * + * @author ForteScarlet + */ +internal class QGMemberImpl( + private val bot: QGBotImpl, + override val source: QGSourceMember, + private val guildId: ID, + private val sourceGuild: QGGuildImpl? = null +) : QGMember { + override val coroutineContext: CoroutineContext = bot.newSupervisorCoroutineContext() + + private val user get() = source.user + + override val id: ID = user.id.ID + +// override suspend fun guild(): QGGuildImpl = +// sourceGuild ?: bot.guild(guildId) ?: throw NoSuchElementException("guild(id=$guildId)") + + // TODO + private val roleIdSet = source.roles.toSet() + + private val dmsInitLock = Mutex() + + @Volatile + private lateinit var dms: DirectMessageSession + + private suspend fun getDms(): DirectMessageSession { + if (::dms.isInitialized) { + return dms + } else { + dmsInitLock.withLock { + if (::dms.isInitialized) { + return dms + } + return CreateDmsApi.create(user.id, guildId.literal).requestDataBy(bot.source).also { + dms = it + } + } + } + } + + // TODO + + @ExperimentalQGApi + override val roles: Collectable + get() = TODO("Not yet implemented") + + // TODO +// @ExperimentalSimbotApi +// override val roles: Collectable +// get() { +// return effectedFlowItems { +// guild().roles.collect { +// if (it.id.literal in roleIdSet) { +// emit( +// QGMemberRoleImpl( +// guildRole = it, +// memberId = this@QGMemberImpl.id, +// sourceMember = bot.checkIfTransmitCacheable(this@QGMemberImpl) +// ) +// ) +// } +// } +// } +// } + + + @JvmSynthetic + override suspend fun send(message: Message): QGMessageReceipt { + val builder = MessageParsers.parse(message) + return send0(builder) + } + + override suspend fun send(text: String): QGMessageReceipt { + return bot.sendMessage(dms.guildId, text) + } + + + override suspend fun send(messageContent: MessageContent): QGMessageReceipt { + if (messageContent is QGMessageContent) { + val body = MessageSendApi.Body { + fromMessage(messageContent.sourceMessage) + } + return send0(body) + } + + return send(messageContent.messages) + } + + private suspend fun send0(body: MessageSendApi.Body): QGMessageReceipt { + val dms = getDms() + return DmsSendApi.create(dms.guildId, body).requestDataBy(bot.source).asReceipt() + } + + private suspend fun send0(bodyList: List): QGMessageReceipt { + val dms = getDms() + + if (bodyList.size == 1) { + val body = bodyList[0].build() + return DmsSendApi.create(dms.guildId, body).requestDataBy(bot.source).asReceipt() + } + + return try { + bodyList.map { + DmsSendApi.create(dms.guildId, it.build()).requestDataBy(bot.source) + }.asReceipt() + } catch (e: QQGuildApiException) { + throw e.addStackTrace { "member.send" } + } + } + + override fun toString(): String { + return "QGMember(id=$id, name=$name, nick=$nick, guildId=$guildId)" + } +} + diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGReceiveMessageContentImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGReceiveMessageContentImpl.kt new file mode 100644 index 00000000..42129160 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGReceiveMessageContentImpl.kt @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.message + +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.component.qguild.message.MessageParsers +import love.forte.simbot.component.qguild.message.QGMessageContent +import love.forte.simbot.message.Messages +import love.forte.simbot.qguild.model.Message + +/** + * + * @author ForteScarlet + */ +internal class QGMessageContentImpl(override val sourceMessage: Message) : QGMessageContent() { + + override val id: ID get() = sourceMessage.id.ID + + private val parseContext by lazy(LazyThreadSafetyMode.PUBLICATION) { MessageParsers.parse(sourceMessage) } + + override val messages: Messages get() = parseContext.messages + + override val plainText: String by lazy(LazyThreadSafetyMode.PUBLICATION) { + parseContext.plainTextBuilder.toString() + } + + override fun toString(): String { + return "QGReceiveMessageContentImpl(messageId=${sourceMessage.id}, sourceMessage=$sourceMessage)" + } + + + override fun equals(other: Any?): Boolean { + if (other !is QGMessageContent) return false + if (other === this) return true + return sourceMessage.id == other.sourceMessage.id + } + + override fun hashCode(): Int = sourceMessage.id.hashCode() +} + diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGSingleMessageReceiptImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGSingleMessageReceiptImpl.kt new file mode 100644 index 00000000..d77b0360 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGSingleMessageReceiptImpl.kt @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.message + +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.component.qguild.message.QGAggregatedMessageReceipt +import love.forte.simbot.component.qguild.message.QGSingleMessageReceipt +import love.forte.simbot.qguild.model.Message + + +private class QGSingleMessageReceiptImpl(override val messageResult: Message) : QGSingleMessageReceipt() { + override val id: ID = messageResult.id.ID +} + +@PublishedApi +internal fun Message.asReceipt(): QGSingleMessageReceipt = QGSingleMessageReceiptImpl(this) + + +private class QGAggregatedMessageReceiptImpl(private val messages: List) : + QGAggregatedMessageReceipt() { + override val size: Int get() = messages.size + + override fun get(index: Int): QGSingleMessageReceipt = messages[index] + + override fun iterator(): Iterator = messages.iterator() +} + +internal fun Iterable.asReceipt(): QGAggregatedMessageReceipt = + QGAggregatedMessageReceiptImpl(this.map { QGSingleMessageReceiptImpl(it) }) diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/BaseQGRole.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/BaseQGRole.kt new file mode 100644 index 00000000..31d4cff7 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/BaseQGRole.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.role + +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.component.qguild.ExperimentalQGApi +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.bot.newSupervisorCoroutineContext +import love.forte.simbot.component.qguild.role.QGRole +import love.forte.simbot.qguild.model.Role +import kotlin.coroutines.CoroutineContext + + +/** + * + * @author ForteScarlet + */ +@ExperimentalQGApi +internal abstract class BaseQGRole(protected val bot: QGBotImpl) : QGRole { + override val coroutineContext: CoroutineContext = bot.newSupervisorCoroutineContext() + abstract override var source: Role + + override val id: ID + get() = source.id.ID +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt new file mode 100644 index 00000000..69988eed --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt @@ -0,0 +1,134 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.role + +import io.ktor.util.internal.* +import kotlinx.coroutines.flow.map +import love.forte.simbot.ability.DeleteOption +import love.forte.simbot.ability.StandardDeleteOption +import love.forte.simbot.ability.StandardDeleteOption.Companion.standardAnalysis +import love.forte.simbot.common.collectable.Collectable +import love.forte.simbot.common.collectable.asCollectable +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.literal +import love.forte.simbot.component.qguild.ExperimentalQGApi +import love.forte.simbot.component.qguild.guild.QGMember +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl +import love.forte.simbot.component.qguild.internal.guild.QGMemberImpl +import love.forte.simbot.component.qguild.role.QGGuildRole +import love.forte.simbot.component.qguild.role.QGRoleUpdater +import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.qguild.api.member.GetGuildRoleMemberListApi +import love.forte.simbot.qguild.api.member.createFlow +import love.forte.simbot.qguild.api.role.AddMemberRoleApi +import love.forte.simbot.qguild.api.role.DeleteGuildRoleApi +import love.forte.simbot.qguild.model.Role +import love.forte.simbot.qguild.stdlib.requestDataBy +import kotlin.concurrent.Volatile + + +/** + * + * @author ForteScarlet + */ +@OptIn(ExperimentalQGApi::class) +internal class QGGuildRoleImpl( + bot: QGBotImpl, + override val guildId: ID, + @Volatile override var source: Role, + private val sourceGuild: QGGuildImpl? = null +) : BaseQGRole(bot), QGGuildRole { + override fun updater(): QGRoleUpdater = QGRoleUpdaterImpl(this) + + override suspend fun grantTo(memberId: ID): QGMemberRoleImpl = grantTo0(memberId, null) + + override suspend fun grantTo(member: QGMember): QGMemberRoleImpl = grantTo0(member, null) + + override suspend fun grantTo(memberId: ID, channelId: ID): QGMemberRoleImpl = grantTo0(memberId, channelId.literal) + + override suspend fun grantTo(member: QGMember, channelId: ID): QGMemberRoleImpl = + grantTo0(member, channelId.literal) + + private suspend fun grantTo0(memberId: ID, channelId: String?): QGMemberRoleImpl { + AddMemberRoleApi.create(guildId.literal, memberId.literal, id.literal, channelId).requestDataBy(bot.source) + return QGMemberRoleImpl(this, memberId, null) + } + + private suspend fun grantTo0(member: QGMember, channelId: String?): QGMemberRoleImpl { + AddMemberRoleApi.create(guildId.literal, member.id.literal, id.literal, channelId).requestDataBy(bot.source) + return QGMemberRoleImpl(this, member.id, member) + } + + override suspend fun delete(vararg options: DeleteOption) { + val stdOpts = options.standardAnalysis() + kotlin.runCatching { + DeleteGuildRoleApi.create(guildId.literal, source.id).requestDataBy(bot.source) + }.onFailure { e -> + if (e is QQGuildApiException) { + if (e.value == 404 && StandardDeleteOption.IGNORE_ON_NO_SUCH_TARGET !in stdOpts) { + // TODO + throw NoSuchElementException().also { it.initCauseBridge(e) } + } + } + // TODO + } + } + + override fun members(batch: Int): Collectable { + require(batch > 0) { "'batch' must > 0, but: $batch" } + val guildId = guildId + val guildIdLiteral = guildId.literal + val roleId = source.id + + return GetGuildRoleMemberListApi.createFlow( + guildId = guildIdLiteral, roleId = roleId, batch = batch + ) { requestDataBy(bot.source) } + .map { + QGMemberImpl( + bot = bot, source = it, guildId = guildId, sourceGuild = this.sourceGuild + ) + }.asCollectable() + + + } + +// override val members: Collectable +// get() { +// val guildId = guildId +// val guildIdLiteral = guildId.literal +// val roleId = source.id +// +// return flow {// prop -> +// GetGuildRoleMemberListApi.createFlow( +// guildIdLiteral, +// roleId, +// prop.batch.takeIf { it < 1 } ?: GetGuildRoleMemberListApi.MAX_LIMIT +// ) { requestBy(bot) } +// .let(prop::effectOn) +// .map { +// QGMemberImpl( +// bot = bot, +// source = it, +// guildId = guildId, +// sourceGuild = this.sourceGuild +// ) +// } +// } +// } +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt new file mode 100644 index 00000000..82e7d079 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.role + +import love.forte.simbot.ExperimentalSimbotApi +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.literal +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.guild.QGMember +import love.forte.simbot.component.qguild.internal.QGBotImpl +import love.forte.simbot.component.qguild.internal.QGMemberImpl +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.bot.newSupervisorCoroutineContext +import love.forte.simbot.component.qguild.internal.guild.QGMemberImpl +import love.forte.simbot.component.qguild.internal.newSupervisorCoroutineContext +import love.forte.simbot.component.qguild.role.QGMemberRole +import love.forte.simbot.component.qguild.role.QGRoleUpdater +import love.forte.simbot.component.qguild.util.requestBy +import love.forte.simbot.literal +import love.forte.simbot.qguild.api.member.GetMemberApi +import love.forte.simbot.qguild.api.role.RemoveMemberRoleApi +import love.forte.simbot.qguild.model.Role +import kotlin.coroutines.CoroutineContext + + +/** + * + * @author ForteScarlet + */ +@OptIn(ExperimentalSimbotApi::class) +internal class QGMemberRoleImpl( + override val guildRole: QGGuildRoleImpl, + override val memberId: ID, + private val sourceMember: QGMember?, +) : BaseQGRole(), QGMemberRole { + override val coroutineContext: CoroutineContext = guildRole.bot.newSupervisorCoroutineContext() + + override val id: ID get() = guildRole.id + + override val guildId: ID + get() = guildRole.guildId + + override val bot: QGBotImpl + get() = guildRole.bot + + override var source: Role + get() = guildRole.source + set(value) { + guildRole.source = value + } + + override suspend fun guild(): QGGuild = guildRole.guild() + override fun updater(): QGRoleUpdater = guildRole.updater() + override suspend fun member(): QGMember { + return sourceMember ?: GetMemberApi.create(guildId.literal, memberId.literal).requestBy(bot) + .let { QGMemberImpl(bot, it, guildId) } + } + + override suspend fun delete(): Boolean = delete0(null) + override suspend fun delete(channelId: ID): Boolean = delete0(channelId.literal) + + private suspend fun delete0(channelId: String?): Boolean { + RemoveMemberRoleApi.create(guildId.literal, memberId.literal, id.literal, channelId).requestBy(bot) + return true + } +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleCreatorImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleCreatorImpl.kt new file mode 100644 index 00000000..d583f4f3 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleCreatorImpl.kt @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.role + +import love.forte.simbot.ExperimentalSimbotApi +import love.forte.simbot.common.id.literal +import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl +import love.forte.simbot.component.qguild.role.QGGuildRole +import love.forte.simbot.component.qguild.role.QGRoleCreator +import love.forte.simbot.component.qguild.util.requestBy +import love.forte.simbot.literal +import love.forte.simbot.qguild.api.role.CreateGuildRoleApi +import love.forte.simbot.qguild.api.role.ModifyGuildRoleApi +import love.forte.simbot.qguild.stdlib.requestBy + + +/** + * + * @author ForteScarlet + */ +@OptIn(ExperimentalSimbotApi::class) +internal class QGRoleCreatorImpl(private val guild: QGGuildImpl) : QGRoleCreator { + override var name: String? = null + override var color: Int? = null + override var isHoist: Boolean? = null + + override suspend fun create(): QGGuildRole { + if (name == null && color == null && isHoist == null) { + throw IllegalArgumentException("No Parameters are set") + } + + val hoist = isHoist?.let { if (it) 1 else 0 } + val created = CreateGuildRoleApi.create(guild.id.literal, name, color, hoist) + .requestBy(guild.baseBot) + + val role = created.role ?: ModifyGuildRoleApi.create(guild.id.literal, created.roleId, name, color, hoist) + .requestBy(guild.baseBot).role +// ?: GetGuildRoleListApi.create(guild.id.literal).requestBy(guild.baseBot) +// .roles.find { it.id == created.roleId }!! + + + return QGGuildRoleImpl( + bot = guild.baseBot, + guildId = guild.id, + source = role, + sourceGuild = guild.baseBot.checkIfTransmitCacheable(guild) + ) + } +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleUpdaterImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleUpdaterImpl.kt new file mode 100644 index 00000000..5fbba561 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleUpdaterImpl.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.role + +import love.forte.simbot.ExperimentalSimbotApi +import love.forte.simbot.common.id.literal +import love.forte.simbot.component.qguild.role.QGRoleUpdater +import love.forte.simbot.component.qguild.util.requestBy +import love.forte.simbot.literal +import love.forte.simbot.qguild.api.role.ModifyGuildRoleApi +import love.forte.simbot.qguild.stdlib.requestBy + + +/** + * + * @author ForteScarlet + */ +@OptIn(ExperimentalSimbotApi::class) +internal class QGRoleUpdaterImpl(private val role: BaseQGRole) : QGRoleUpdater { + override var name: String? = null + override var color: Int? = null + + // 0-否, 1-是 + override var isHoist: Boolean? = null + + override suspend fun update() { + if (name == null && color == null && isHoist == null) { + throw IllegalArgumentException("No parameters are set") + } + val modified = ModifyGuildRoleApi.create(role.guildId.literal, role.source.id, name, color, isHoist?.let { if (it) 1 else 0 }) + .requestBy(role.bot) + + // update value + role.source = modified.role + } + +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.kt new file mode 100644 index 00000000..e067e11f --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.kt @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.message + +import io.ktor.utils.io.core.* +import love.forte.simbot.message.* +import love.forte.simbot.resource.ByteArrayResource + + +/** + * + * @author ForteScarlet + */ +public object ImageParser : SendingMessageParser { + override suspend fun invoke( + index: Int, + element: Message.Element, + messages: Messages?, + builderContext: SendingMessageParser.BuilderContext + ) { + // TODO attachment? + + when (element) { + is Image -> { + if (element is OfflineImage) { + processOfflineImage(index, element, messages, builderContext) + } + + } + + // TODO more image type support for file_image + } + } +} + +internal fun processOfflineImage( + index: Int, + element: OfflineImage, + messages: Messages?, + builderContext: SendingMessageParser.BuilderContext +) { + if (processOfflineImage0(index, element, messages, builderContext)) { + return + } + if (element is OfflineResourceImage) { + when (val resource = element.resource) { + is ByteArrayResource -> { + builderContext.builderOrNew { it.fileImage == null }.setFileImage(resource.data()) + } + else -> { + builderContext.builderOrNew { it.fileImage == null }.setFileImage(ByteReadPacket(resource.data())) + } + } + } else { + val dataBytes = element.data() + builderContext.builderOrNew { it.fileImage == null }.setFileImage(ByteReadPacket(dataBytes)) + } +} + +internal expect fun processOfflineImage0( + index: Int, + element: OfflineImage, + messages: Messages?, + builderContext: SendingMessageParser.BuilderContext +): Boolean diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/MessageSender.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/MessageSender.kt new file mode 100644 index 00000000..94597919 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/MessageSender.kt @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.message + +import love.forte.simbot.component.qguild.bot.QGBot +import love.forte.simbot.component.qguild.internal.message.asReceipt +import love.forte.simbot.message.Message +import love.forte.simbot.message.MessageContent +import love.forte.simbot.qguild.QGInternalApi +import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.qguild.api.MessageAuditedException +import love.forte.simbot.qguild.api.message.MessageSendApi +import love.forte.simbot.qguild.api.message.direct.DmsSendApi +import love.forte.simbot.qguild.message.ContentTextEncoder +import love.forte.simbot.qguild.stdlib.requestDataBy + +/** + * 使用当前 [QGBot] 向 [channelId] 通过 [MessageSendApi] 发送一个消息, + * 消息内容为 [message] 的解析结果。 + * + * [message] 会通过 [MessageParsers.parse] 解析为一个或多个 [MessageSendApi.Body.Builder]。 + * 当解析结果数量为1时,[sendMessage] 的结果为 [QGSingleMessageReceipt] 类型的回执。 + * 相反如果结果大于1,则 [sendMessage] 会得到实际为 [QGAggregatedMessageReceipt] 类型的结果。 + * + * 需要注意如果 [message] 的解析结果最终没有任何可用元素(例如元素为空)则可能会引发API请求异常。 + * + * @param channelId 目标频道ID + * @param message 解析消息 + * @param onEachPre 对于每个 [MessageSendApi.Body.Builder] 被构造后的初始化行为 + * @param onEachPost 对于每个 [MessageSendApi.Body.Builder] 最终执行的收尾行为 + * + * @see MessageParsers.parse + * + * @throws QQGuildApiException 请求得到了异常的结果 + * @throws MessageAuditedException 当响应状态为表示消息审核的 `304023`、`304024` 时 + * + * @return 发送后的回执 + */ +@QGInternalApi +public suspend inline fun QGBot.sendMessage( + channelId: String, + message: Message, + crossinline onEachPre: MessageSendApi.Body.Builder.() -> Unit = {}, + onEachPost: MessageSendApi.Body.Builder.() -> Unit = {}, +): QGMessageReceipt { + val parsed = MessageParsers.parse(message = message, onEachPre = onEachPre, onEachPost = onEachPost) + + if (parsed.size == 1) { + val body = parsed[0].build() + return sendMessage(channelId, body) + } + + return sendMessage(channelId, parsed) +} + +/** + * 使用当前 [QGBot] 向 [channelId] 通过 [MessageSendApi] 发送一个消息, + * 消息内容为 [messageContent] 的解析结果。 + * + * 如果 [messageContent] 是其他QQ频道事件收到的消息(即为 [QGReceiveMessageContent] 类型), + * 则会直接通过 [MessageSendApi.Body.Builder.fromMessage] 转义内部信息而不会尝试解析 [MessageContent.messages]。 + * + * **注意:** 在转化的过程中会丢失不支持发送的消息(例如附件、图片等)。 + * + * 如果类型不是上述情况,则行为与使用 [MessageContent.messages] 一致。 + * + * @param channelId 目标频道ID + * @param messageContent 解析消息本体 + * @param onEachPre 对于每个 [MessageSendApi.Body.Builder] 被构造后的初始化行为 + * @param onEachPost 对于每个 [MessageSendApi.Body.Builder] 最终执行的收尾行为 + * + * @see MessageParsers.parse + * + * @throws QQGuildApiException 请求得到了异常的结果 + * @throws MessageAuditedException 当响应状态为表示消息审核的 `304023`、`304024` 时 + * + * @return 发送后的回执 + */ +@QGInternalApi +public suspend inline fun QGBot.sendMessage( + channelId: String, + messageContent: MessageContent, + crossinline onEachPre: MessageSendApi.Body.Builder.() -> Unit = {}, + onEachPost: MessageSendApi.Body.Builder.() -> Unit = {}, +): QGMessageReceipt { + if (messageContent is QGMessageContent) { + val body = MessageSendApi.Body { + onEachPre() + fromMessage(messageContent.sourceMessage) + onEachPost() + } + + return sendMessage(channelId, body) + } + + return sendMessage(channelId, messageContent.messages, onEachPre, onEachPost) +} + +/** + * 使用当前 [QGBot] 向 [channelId] 通过 [MessageSendApi] 发送一个消息, + * 消息内容为通过 [ContentTextEncoder.encode] 转义后的无特殊含义 [text] 文本。 + * + * [text] 会通过 [ContentTextEncoder.encode] 进行转义来消除其中可能存在的[内嵌格式](https://bot.q.qq.com/wiki/develop/api/openapi/message/message_format.html)文本。 + * 如果你希望保留内嵌格式,参考使用 [QGContentText] 而不是纯文本内容。 + * + * + * @param channelId 目标频道ID + * @param text 纯文本消息 + * @param onEachPre 对于每个 [MessageSendApi.Body.Builder] 被构造后的初始化行为 + * @param onEachPost 对于每个 [MessageSendApi.Body.Builder] 最终执行的收尾行为 + * + * @see MessageParsers.parse + * @see QGContentText + * + * @throws QQGuildApiException 请求得到了异常的结果 + * @throws MessageAuditedException 当响应状态为表示消息审核的 `304023`、`304024` 时 + * + * @return 发送后的回执 + */ +@QGInternalApi +public suspend inline fun QGBot.sendMessage( + channelId: String, + text: String, + onEachPre: MessageSendApi.Body.Builder.() -> Unit = {}, + onEachPost: MessageSendApi.Body.Builder.() -> Unit = {}, +): QGMessageReceipt { + val body = MessageSendApi.Body { + onEachPre() + // 转移后的纯文本字符串 + content = ContentTextEncoder.encode(text) + onEachPost() + } + + return sendMessage(channelId, body) +} + +@PublishedApi +@QGInternalApi +internal suspend fun QGBot.sendMessage(channelId: String, body: MessageSendApi.Body): QGMessageReceipt = + MessageSendApi.create(channelId, body).requestDataBy(source).asReceipt() + +@PublishedApi +@QGInternalApi +internal suspend fun QGBot.sendMessage( + channelId: String, bodyBuilderList: List +): QGMessageReceipt = bodyBuilderList.mapTo(ArrayList(bodyBuilderList.size)) { + MessageSendApi.create(channelId, it.build()).requestDataBy(source) +}.asReceipt() + + +@PublishedApi +@QGInternalApi +internal suspend fun QGBot.sendDmsMessage(guildId: String, body: MessageSendApi.Body): QGMessageReceipt = + DmsSendApi.create(guildId, body).requestDataBy(source).asReceipt() diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGArk.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGArk.kt similarity index 82% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGArk.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGArk.kt index d7296b95..31c9bed5 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGArk.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGArk.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,13 +19,14 @@ package love.forte.simbot.component.qguild.message import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import love.forte.simbot.ID -import love.forte.simbot.component.qguild.message.QGArk.Key.byArk -import love.forte.simbot.literal +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.common.id.literal +import love.forte.simbot.component.qguild.message.QGArk.Companion.byArk import love.forte.simbot.message.Messages -import love.forte.simbot.message.doSafeCast import love.forte.simbot.qguild.message.buildArk import love.forte.simbot.qguild.model.Message +import kotlin.jvm.JvmStatic import love.forte.simbot.message.Message as SimbotMessage /** @@ -37,22 +38,16 @@ import love.forte.simbot.message.Message as SimbotMessage @Serializable public data class QGArk internal constructor( @SerialName("template_id") - @Serializable(ID.AsCharSequenceIDSerializer::class) public val templateId: ID, public val kvs: List = emptyList() -) : QGMessageElement { - override val key: SimbotMessage.Key - get() = Key - +) : QGMessageElement { /** * 转化为一个真正的 [Message.Ark]. */ public fun toRealArk(): Message.Ark = Message.Ark(templateId.literal, kvs.toList()) - public companion object Key : SimbotMessage.Key { - override fun safeCast(value: Any): QGArk? = doSafeCast(value) - + public companion object { @JvmStatic public fun byArk(ark: Message.Ark): QGArk = QGArk(ark.templateId.ID, ark.kv.toList()) @@ -77,7 +72,7 @@ public fun QGArk.toArk(): Message.Ark = buildArk(templateId.literal) { internal object ArkParser : SendingMessageParser { override suspend fun invoke( index: Int, - element: SimbotMessage.Element<*>, + element: SimbotMessage.Element, messages: Messages?, builderContext: SendingMessageParser.BuilderContext ) { diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGAtMessages.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGAtMessages.kt similarity index 83% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGAtMessages.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGAtMessages.kt index e30bcb6e..4aedeeea 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGAtMessages.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGAtMessages.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,8 +19,11 @@ package love.forte.simbot.component.qguild.message import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import love.forte.simbot.ID -import love.forte.simbot.message.* +import love.forte.simbot.common.id.ID +import love.forte.simbot.message.At +import love.forte.simbot.message.AtAll +import love.forte.simbot.message.Message +import love.forte.simbot.message.Messages /** * 在QQ频道中AT(提及)一个子频道 @@ -28,7 +31,6 @@ import love.forte.simbot.message.* * Deprecated: 直接使用 [At] 并使其 [type][At.type] 为 [`"channel"`][love.forte.simbot.component.qguild.QQGuildComponent.AT_CHANNEL_TYPE] 即可。 * */ -@Suppress("DEPRECATION") @SerialName("qg.atChannel") @Serializable @Deprecated( @@ -39,25 +41,14 @@ import love.forte.simbot.message.* ) ) @QGSendOnly -public data class QGAtChannel( - @Serializable(ID.AsCharSequenceIDSerializer::class) - public val target: ID -) : QGMessageElement { - override val key: Message.Key - get() = Key - - public companion object Key : Message.Key { - override fun safeCast(value: Any): QGAtChannel? = doSafeCast(value) - } -} - +public data class QGAtChannel(public val target: ID) : QGMessageElement internal object MentionParser : SendingMessageParser { @Suppress("DEPRECATION") override suspend fun invoke( index: Int, - element: Message.Element<*>, + element: Message.Element, messages: Messages?, builderContext: SendingMessageParser.BuilderContext ) { diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGAttachmentMessage.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGAttachmentMessage.kt similarity index 80% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGAttachmentMessage.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGAttachmentMessage.kt index edafd1a4..6105b37d 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGAttachmentMessage.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGAttachmentMessage.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,11 +19,15 @@ package love.forte.simbot.component.qguild.message import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import love.forte.simbot.ID +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID import love.forte.simbot.logger.LoggerFactory +import love.forte.simbot.logger.logger import love.forte.simbot.message.Messages -import love.forte.simbot.message.doSafeCast import love.forte.simbot.qguild.model.Message +import kotlin.jvm.JvmName +import kotlin.jvm.JvmOverloads +import kotlin.jvm.JvmStatic import love.forte.simbot.message.Message as SimbotMessage @@ -38,7 +42,9 @@ import love.forte.simbot.message.Message as SimbotMessage */ @SerialName("qg.attachment") @Serializable -public data class QGAttachmentMessage @JvmOverloads constructor(public val url: String, public val properties: Map = emptyMap()) : QGMessageElement { +public data class QGAttachmentMessage +@JvmOverloads constructor(public val url: String, public val properties: Map = emptyMap()) : + QGMessageElement { private lateinit var _source: Message.Attachment @@ -54,11 +60,7 @@ public data class QGAttachmentMessage @JvmOverloads constructor(public val url: @Deprecated("Just get url", ReplaceWith("url.ID", "love.forte.simbot.ID")) public val id: ID get() = url.ID - override val key: SimbotMessage.Key - get() = Key - - public companion object Key : SimbotMessage.Key { - override fun safeCast(value: Any): QGAttachmentMessage? = doSafeCast(value) + public companion object { /** * 将 [Message.Attachment] 转化为 [QGAttachmentMessage]. @@ -79,14 +81,14 @@ public data class QGAttachmentMessage @JvmOverloads constructor(public val url: * @suppress */ @Deprecated("Use QGAttachmentMessage.source", ReplaceWith("source")) -public fun QGAttachmentMessage.toAttachment(): Message.Attachment = source +public fun QGAttachmentMessage.toAttachment(): Message.Attachment = source internal object AttachmentParser : SendingMessageParser { - private val logger = LoggerFactory.getLogger(AttachmentParser::class) + private val logger = LoggerFactory.logger() override suspend fun invoke( index: Int, - element: SimbotMessage.Element<*>, + element: SimbotMessage.Element, messages: Messages?, builderContext: SendingMessageParser.BuilderContext ) { diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGContentText.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGContentText.kt similarity index 88% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGContentText.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGContentText.kt index adbbbd25..44d3515f 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGContentText.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGContentText.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,10 +19,8 @@ package love.forte.simbot.component.qguild.message import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import love.forte.simbot.message.Message import love.forte.simbot.message.PlainText import love.forte.simbot.message.Text -import love.forte.simbot.message.doSafeCast import love.forte.simbot.qguild.message.ContentTextDecoder import love.forte.simbot.qguild.message.ContentTextEncoder @@ -55,8 +53,7 @@ import love.forte.simbot.qguild.message.ContentTextEncoder */ @SerialName("qg.contentText") @Serializable -public data class QGContentText(public val content: String) : PlainText, QGMessageElement { - +public data class QGContentText(public val content: String) : PlainText, QGMessageElement { /** * 同 [content]。 */ @@ -80,11 +77,4 @@ public data class QGContentText(public val content: String) : PlainText - get() = Key - - public companion object Key : Message.Key { - override fun safeCast(value: Any): QGContentText? = doSafeCast(value) - } } diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGEmbed.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGEmbed.kt similarity index 86% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGEmbed.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGEmbed.kt index 7189abd4..f1d6b504 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGEmbed.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGEmbed.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -21,10 +21,10 @@ import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import love.forte.simbot.event.MessageEvent import love.forte.simbot.message.Messages -import love.forte.simbot.message.doSafeCast import love.forte.simbot.qguild.api.message.MessageSendApi import love.forte.simbot.qguild.message.EmbedBuilder import love.forte.simbot.qguild.model.Message +import kotlin.jvm.JvmStatic /** @@ -47,13 +47,9 @@ import love.forte.simbot.qguild.model.Message */ @SerialName("qg.embed") @Serializable -public data class QGEmbed internal constructor(public val embed: Message.Embed) : QGMessageElement { - override val key: love.forte.simbot.message.Message.Key - get() = Key - - public companion object Key : love.forte.simbot.message.Message.Key { - override fun safeCast(value: Any): QGEmbed? = doSafeCast(value) +public data class QGEmbed internal constructor(public val embed: Message.Embed) : QGMessageElement { + public companion object { /** * 将提供的 [Message.Embed] 包装为 [QGEmbed]。 */ @@ -74,7 +70,7 @@ public inline fun buildQGEmbed(block: EmbedBuilder.() -> Unit): QGEmbed = internal object EmbedParser : SendingMessageParser { override suspend fun invoke( index: Int, - element: love.forte.simbot.message.Message.Element<*>, + element: love.forte.simbot.message.Message.Element, messages: Messages?, builderContext: SendingMessageParser.BuilderContext ) { diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGImages.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGImages.kt similarity index 93% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGImages.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGImages.kt index 7588059e..8c1a3dd2 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGImages.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGImages.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -23,9 +23,8 @@ package love.forte.simbot.component.qguild.message */ public class QGOfflineImage // TODO - /** - * 图片类型 + * 收到的图片类型 * */ public class QGImage // TODO diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGReceiveMessageContent.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageContent.kt similarity index 89% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGReceiveMessageContent.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageContent.kt index e3495113..8d126006 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGReceiveMessageContent.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageContent.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,10 +17,13 @@ package love.forte.simbot.component.qguild.message -import love.forte.simbot.ID +import love.forte.simbot.ability.DeleteOption +import love.forte.simbot.ability.StandardDeleteOption +import love.forte.simbot.common.id.ID import love.forte.simbot.component.qguild.QQGuildComponent import love.forte.simbot.message.* import love.forte.simbot.qguild.message.ContentTextDecoder +import kotlin.jvm.JvmSynthetic import love.forte.simbot.qguild.model.Message as QGSourceMessage /** @@ -28,12 +31,11 @@ import love.forte.simbot.qguild.model.Message as QGSourceMessage * * @author ForteScarlet */ -public abstract class QGReceiveMessageContent : ReceivedMessageContent() { - +public abstract class QGMessageContent : MessageContent { /** * 此消息的ID */ - abstract override val messageId: ID + abstract override val id: ID /** * 通过 [sourceMessage] 转化后的消息元素链。 @@ -151,8 +153,16 @@ public abstract class QGReceiveMessageContent : ReceivedMessageContent() { abstract override val plainText: String /** - * 暂时不支持消息撤回,将会始终返回false. + * 暂时不支持消息撤回。 + * 如果 [options] 中不包含 + * [StandardDeleteOption.IGNORE_ON_UNSUPPORTED] + * 则抛出 [UnsupportedOperationException] */ - override suspend fun delete(): Boolean = false + @JvmSynthetic + override suspend fun delete(vararg options: DeleteOption) { + if (options.none { it == StandardDeleteOption.IGNORE_ON_UNSUPPORTED }) { + throw UnsupportedOperationException("QGReceiveMessageContent.delete") + } + } } diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGMessageElement.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageElement.kt similarity index 50% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGMessageElement.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageElement.kt index ca7b14c9..e9ad9c99 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGMessageElement.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageElement.kt @@ -1,13 +1,18 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. If not, see . + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . */ package love.forte.simbot.component.qguild.message @@ -23,4 +28,4 @@ import love.forte.simbot.message.Message * * @author ForteScarlet */ -public interface QGMessageElement> : Message.Element +public interface QGMessageElement : Message.Element diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGMessageReceipt.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageReceipt.kt similarity index 68% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGMessageReceipt.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageReceipt.kt index bb544a9c..8062fe9c 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGMessageReceipt.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageReceipt.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,17 +17,22 @@ package love.forte.simbot.component.qguild.message +import love.forte.simbot.ability.DeleteOption +import love.forte.simbot.ability.StandardDeleteOption import love.forte.simbot.message.AggregatedMessageReceipt import love.forte.simbot.message.MessageReceipt import love.forte.simbot.message.SingleMessageReceipt import love.forte.simbot.qguild.api.message.MessageSendApi import love.forte.simbot.qguild.model.Message +import kotlin.jvm.JvmSynthetic /** - * QQ频道中消息发送后的回执。可能代表一个 [单回执][QGSingleMessageReceipt] 或一个 [聚合回执][QGAggregatedMessageReceipt]。 + * QQ频道中消息发送后的回执。可能代表一个 [单回执][QGSingleMessageReceipt] + * 或一个 [聚合回执][QGAggregatedMessageReceipt]。 * - * 在发送消息时,如果消息链中出现了无法在同一次 [MessageSendApi] 中就请求的内容,则会将请求拆分为多个请求,并将多个消息合并为一个 [QGAggregatedMessageReceipt]。 + * 在发送消息时,如果消息链中出现了无法在同一次 [MessageSendApi] 中就请求的内容, + * 则会将请求拆分为多个请求,并将多个消息合并为一个 [QGAggregatedMessageReceipt]。 * * 更多说明参考 [sendMessage][love.forte.simbot.component.qguild.message.sendMessage] 的文档说明。 * @@ -37,33 +42,23 @@ import love.forte.simbot.qguild.model.Message * @author ForteScarlet */ public interface QGMessageReceipt : MessageReceipt { - - /** - * 是否发送成功。 - * 能得到此类型即说明消息已发送成功,始终为 `true`。 - */ - override val isSuccess: Boolean - - /** - * 消息暂时不支持撤回。始终得到 `false`。 + * 消息暂时不支持撤回。 + * 如果 [options] 不包含 [StandardDeleteOption.IGNORE_ON_UNSUPPORTED] + * 则会抛出 [UnsupportedOperationException] 异常。 */ - override suspend fun delete(): Boolean = false + @JvmSynthetic + public override suspend fun delete(vararg options: DeleteOption) { + if (options.none { it == StandardDeleteOption.IGNORE_ON_UNSUPPORTED }) { + throw UnsupportedOperationException("QGMessageReceipt.delete") + } + } } - /** * 代表为一次消息发送请求后的回执结果,是 [QGAggregatedMessageReceipt] 的元素类型。 */ public abstract class QGSingleMessageReceipt : SingleMessageReceipt(), QGMessageReceipt { - - /** - * 是否发送成功。 - * 能得到此类型即说明消息已发送成功,始终为 `true`。 - */ - override val isSuccess: Boolean - get() = true - /** * QQ频道消息发送api发送消息后得到的回执,也就是消息对象。 */ @@ -75,14 +70,6 @@ public abstract class QGSingleMessageReceipt : SingleMessageReceipt(), QGMessage * */ public abstract class QGAggregatedMessageReceipt : AggregatedMessageReceipt(), QGMessageReceipt { - - /** - * 是否发送成功。 - * 能得到此类型即说明消息已发送成功,始终为 `true`。 - */ - override val isSuccess: Boolean - get() = true - /** * 聚合内容的数量,通常来讲会 `> 1` 。 */ @@ -100,11 +87,7 @@ public abstract class QGAggregatedMessageReceipt : AggregatedMessageReceipt(), Q */ abstract override fun iterator(): Iterator - - /** - * 遍历内容的所有 [QGSingleMessageReceipt] 并依次使用它们的 [`delete`][QGSingleMessageReceipt.delete] - */ - override suspend fun delete(): Boolean { - return super.delete() + override suspend fun delete(vararg options: DeleteOption) { + super.delete(*options) } } diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGReference.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGReference.kt similarity index 87% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGReference.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGReference.kt index fce0ccba..03b71440 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGReference.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGReference.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,13 +19,15 @@ package love.forte.simbot.component.qguild.message import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import love.forte.simbot.ID -import love.forte.simbot.literal +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.common.id.literal import love.forte.simbot.message.Messages -import love.forte.simbot.message.doSafeCast import love.forte.simbot.qguild.api.message.MessageSendApi import love.forte.simbot.qguild.model.Message -import love.forte.simbot.message.Message as SimbotMessage +import kotlin.jvm.JvmName +import kotlin.jvm.JvmOverloads +import kotlin.jvm.JvmStatic /** @@ -43,7 +45,7 @@ import love.forte.simbot.message.Message as SimbotMessage public class QGReference private constructor( public val messageId: ID, public val ignoreGetMessageError: Boolean -) : QGMessageElement { +) : QGMessageElement { private lateinit var _source: Message.Reference @@ -57,13 +59,8 @@ public class QGReference private constructor( } } - override val key: SimbotMessage.Key - get() = Key - - - public companion object Key : SimbotMessage.Key { - override fun safeCast(value: Any): QGReference? = doSafeCast(value) + public companion object { /** * 直接通过 [Message.Reference] 构造一个 [QGReference]。 */ @@ -114,7 +111,7 @@ public class QGReference private constructor( internal object ReferenceParser : SendingMessageParser { override suspend fun invoke( index: Int, - element: love.forte.simbot.message.Message.Element<*>, + element: love.forte.simbot.message.Message.Element, messages: Messages?, builderContext: SendingMessageParser.BuilderContext ) { diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGReplyTo.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGReplyTo.kt similarity index 78% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGReplyTo.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGReplyTo.kt index ceff5268..32535268 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/QGReplyTo.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGReplyTo.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,18 +19,17 @@ package love.forte.simbot.component.qguild.message import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import love.forte.simbot.ID -import love.forte.simbot.literal +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.literal import love.forte.simbot.message.Message import love.forte.simbot.message.Messages -import love.forte.simbot.message.doSafeCast import love.forte.simbot.qguild.api.message.MessageSendApi /** * * QQ频道机器人为公域时可能需要指定一个回复消息的目标,通过拼接 [QGReplyTo] 到当前消息列表中来提供一个回复消息的目标信息。 * - * [QGReplyTo] 是一个仅用于发送的消息,不会在 [QGReceiveMessageContent.messages] 中得到。 + * [QGReplyTo] 是一个仅用于发送的消息,不会在 [QGMessageContent.messages] 中得到。 * * [QGReplyTo] 在消息链中被解析时,如果存在,会**覆盖**当前所有已经被解析存在的消息体的 [msgId][MessageSendApi.Body.msgId], * 但是无法影响到尚未被构建的未知 builder。 @@ -42,22 +41,12 @@ import love.forte.simbot.qguild.api.message.MessageSendApi @SerialName("qg.replyTo") @Serializable @QGSendOnly -public data class QGReplyTo(@Serializable(ID.AsCharSequenceIDSerializer::class) val id: ID) : - QGMessageElement { - override val key: Message.Key - get() = Key - - public companion object Key : Message.Key { - override fun safeCast(value: Any): QGReplyTo? = doSafeCast(value) - } - -} - +public data class QGReplyTo(val id: ID) : QGMessageElement internal object ReplyToParser : SendingMessageParser { override suspend fun invoke( index: Int, - element: Message.Element<*>, + element: Message.Element, messages: Messages?, builderContext: SendingMessageParser.BuilderContext ) { diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/SendingMessageParser.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/SendingMessageParser.kt similarity index 80% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/SendingMessageParser.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/SendingMessageParser.kt index ccbb29e1..bc9a9803 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/SendingMessageParser.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/SendingMessageParser.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,18 +17,17 @@ package love.forte.simbot.component.qguild.message -import love.forte.simbot.ExperimentalSimbotApi +import love.forte.simbot.annotations.ExperimentalSimbotAPI +import love.forte.simbot.component.qguild.ExperimentalQGApi import love.forte.simbot.message.Messages import love.forte.simbot.message.emptyMessages import love.forte.simbot.qguild.api.message.MessageSendApi import love.forte.simbot.qguild.model.Message -import love.forte.simbot.resources.Resource -import java.util.* -import java.util.concurrent.ConcurrentLinkedDeque -import java.util.concurrent.ConcurrentLinkedQueue import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract +import kotlin.jvm.JvmName +import kotlin.jvm.JvmOverloads import love.forte.simbot.message.Message as SimbotMessage @@ -36,7 +35,7 @@ import love.forte.simbot.message.Message as SimbotMessage * 通过消息体和message builder, 以责任链的形式构建消息体。 */ public fun interface SendingMessageParser : - suspend (Int, SimbotMessage.Element<*>, Messages?, SendingMessageParser.BuilderContext) -> Unit { + suspend (Int, SimbotMessage.Element, Messages?, SendingMessageParser.BuilderContext) -> Unit { /** * 将 [SimbotMessage.Element] 拼接到 [MessageSendApi.Body.Builder] 中。 * @@ -44,7 +43,7 @@ public fun interface SendingMessageParser : */ override suspend fun invoke( index: Int, - element: SimbotMessage.Element<*>, + element: SimbotMessage.Element, messages: Messages?, builderContext: BuilderContext, ) @@ -52,13 +51,13 @@ public fun interface SendingMessageParser : public data class BuilderContext( val builderFactory: () -> MessageSendApi.Body.Builder ) { - public val builders: Deque + public val builders: ArrayDeque /** * 标记下一次再获取 builder 时必须新建。 */ public var nextIsNew: Boolean = false - private set + private set @PublishedApi internal fun nextMustBeNew(value: Boolean = true) { @@ -66,19 +65,21 @@ public fun interface SendingMessageParser : } init { - builders = ConcurrentLinkedDeque() + builders = ArrayDeque() builders.add(builderFactory()) } - val builder: MessageSendApi.Body.Builder get() { - if (nextIsNew) { - return newBuilder().also { - nextMustBeNew(false) + val builder: MessageSendApi.Body.Builder + get() { + if (nextIsNew) { + return newBuilder().also { + nextMustBeNew(false) + } } + + return builders.last() } - return builders.peekLast() - } public fun newBuilder(): MessageSendApi.Body.Builder { return builderFactory().also { builders.addLast(it) } } @@ -123,8 +124,8 @@ public fun interface ReceivingMessageParser { * 将一个 [Message.Element][SimbotMessage.Element] 拼接到 [MessageSendApi.Body.Builder] 中。 */ public object MessageParsers { - @ExperimentalSimbotApi - public val sendingParsers: Queue = ConcurrentLinkedQueue().apply { + @ExperimentalQGApi + public val sendingParsers: List = buildList { add(ContentParser) add(FaceParser) add(MentionParser) @@ -136,21 +137,20 @@ public object MessageParsers { add(ReferenceParser) } - @ExperimentalSimbotApi - public val receivingParsers: Queue = ConcurrentLinkedQueue().apply { + @ExperimentalQGApi + public val receivingParsers: List = buildList { add(QGMessageParser) } - - @ExperimentalSimbotApi - public fun addParser(parser: SendingMessageParser) { - sendingParsers.add(parser) - } - - @ExperimentalSimbotApi - public fun addParser(parser: ReceivingMessageParser) { - receivingParsers.add(parser) - } +// @ExperimentalSimbotAPI +// public fun addParser(parser: SendingMessageParser) { +// sendingParsers.add(parser) +// } +// +// @ExperimentalSimbotAPI +// public fun addParser(parser: ReceivingMessageParser) { +// receivingParsers.add(parser) +// } /** * 将 [message] 解析为一个或多个 [MessageSendApi.Body.Builder]。 @@ -184,7 +184,7 @@ public object MessageParsers { * * @return 解析结果的 [MessageSendApi.Body.Builder] 序列。 */ - @OptIn(ExperimentalSimbotApi::class, ExperimentalContracts::class) + @OptIn(ExperimentalSimbotAPI::class, ExperimentalContracts::class, ExperimentalQGApi::class) @JvmOverloads @JvmName("parse") public suspend inline fun parse( @@ -202,7 +202,7 @@ public object MessageParsers { val context = SendingMessageParser.BuilderContext { MessageSendApi.Body.Builder().also(onEachPre) } when (message) { - is SimbotMessage.Element<*> -> { + is SimbotMessage.Element -> { for (parser in sendingParsers) { parser(0, message, null, context) } @@ -219,14 +219,14 @@ public object MessageParsers { val builders = context.builders if (builders.size <= 1) { - return listOf(builders.first.also(onEachPost)) + return listOf(builders.first().also(onEachPost)) } return builders.mapTo(ArrayList(builders.size)) { it.also(onEachPost) } } - @OptIn(ExperimentalSimbotApi::class) + @OptIn(ExperimentalQGApi::class) @JvmOverloads public fun parse( message: Message, @@ -247,20 +247,12 @@ public object MessageParsers { public class QGMessageForSendingForParse internal constructor() { public var sendBodyBuilder: MessageSendApi.Body.Builder = MessageSendApi.Body.Builder() - /** - * 通过 form-data上传图片的 `file_image` 参数。 - * 目前支持使用的类型: - * - [Resource] - */ -// public var fileImage: Resource? = null - - @TmfsDsl public inline fun forSending(block: MessageSendApi.Body.Builder.() -> Unit) { sendBodyBuilder.block() } - + @TmfsDsl public fun contentAppend(contentText: String) { forSending { if (content == null) { @@ -273,10 +265,6 @@ public class QGMessageForSendingForParse internal constructor() { } -//internal operator fun TencentMessageForSendingForParse.component1(): MessageSendApi.Body.Builder = sendBodyBuilder -//internal operator fun TencentMessageForSendingForParse.component2(): Resource? = fileImage - - @Retention(AnnotationRetention.BINARY) @DslMarker internal annotation class TmfsDsl // TencentMessageForSendingBuilderDsl diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/StandardMessageParsers.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/StandardMessageParsers.kt similarity index 86% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/StandardMessageParsers.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/StandardMessageParsers.kt index 3e4e13f7..d4627d88 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/StandardMessageParsers.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/StandardMessageParsers.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,11 +17,11 @@ package love.forte.simbot.component.qguild.message -import love.forte.simbot.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.common.id.literal import love.forte.simbot.component.qguild.QQGuildComponent -import love.forte.simbot.component.qguild.message.QGAttachmentMessage.Key.toMessage -import love.forte.simbot.component.qguild.message.QGReference.Key.toMessage -import love.forte.simbot.literal +import love.forte.simbot.component.qguild.message.QGAttachmentMessage.Companion.toMessage +import love.forte.simbot.component.qguild.message.QGReference.Companion.toMessage import love.forte.simbot.message.* import love.forte.simbot.qguild.message.ContentTextDecoder import love.forte.simbot.qguild.message.ContentTextEncoder @@ -32,7 +32,7 @@ import love.forte.simbot.message.Message as SimbotMessage internal object ContentParser : SendingMessageParser { override suspend fun invoke( index: Int, - element: SimbotMessage.Element<*>, + element: SimbotMessage.Element, messages: Messages?, builderContext: SendingMessageParser.BuilderContext ) { @@ -64,7 +64,7 @@ internal object FaceParser : SendingMessageParser { override suspend fun invoke( index: Int, - element: love.forte.simbot.message.Message.Element<*>, + element: love.forte.simbot.message.Message.Element, messages: Messages?, builderContext: SendingMessageParser.BuilderContext ) { @@ -109,26 +109,26 @@ internal object QGMessageParser : ReceivingMessageParser { return context } - val messageList = mutableListOf>() + val messageList = mutableListOf() // plainText builder val textBuilder = StringBuilder() fun appendText(value: String, startIndex: Int, endIndex: Int) { ContentTextDecoder.decodeTo(value, startIndex, endIndex, object : Appendable { - override fun append(csq: CharSequence?): Appendable = also { - textBuilder.append(csq) - context.plainTextBuilder.append(csq) + override fun append(value: CharSequence?): Appendable = also { + textBuilder.append(value) + context.plainTextBuilder.append(value) } - override fun append(csq: CharSequence?, start: Int, end: Int): Appendable = also { - textBuilder.append(csq, start, end) - context.plainTextBuilder.append(csq, start, end) + override fun append(value: CharSequence?, startIndex: Int, endIndex: Int): Appendable = also { + textBuilder.append(value, startIndex, endIndex) + context.plainTextBuilder.append(value, startIndex, endIndex) } - override fun append(c: Char): Appendable = also { - textBuilder.append(c) - context.plainTextBuilder.append(c) + override fun append(value: Char): Appendable = also { + textBuilder.append(value) + context.plainTextBuilder.append(value) } }) } diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/annotations.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/annotations.kt similarity index 89% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/annotations.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/annotations.kt index 1a5a47cb..8ab8d48e 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/annotations.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/annotations.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -19,7 +19,7 @@ package love.forte.simbot.component.qguild.message /** * 标记一个QQ频道中的消息元素实现类为一个**仅用于发送**的消息类型。 - * 被标记的类型不会在 [QGReceiveMessageContent.messages] 被解析出现。 + * 被标记的类型不会在 [QGMessageContent.messages] 被解析出现。 * * _此注解仅用于标记_ */ diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/role/QGGuildRole.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGGuildRole.kt similarity index 62% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/role/QGGuildRole.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGGuildRole.kt index 1882caef..5c69aaa7 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/role/QGGuildRole.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGGuildRole.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,50 +17,54 @@ package love.forte.simbot.component.qguild.role -import love.forte.simbot.ExperimentalSimbotApi -import love.forte.simbot.ID -import love.forte.simbot.action.DeleteSupport -import love.forte.simbot.component.qguild.JST -import love.forte.simbot.component.qguild.QGMember -import love.forte.simbot.component.qguild.QGRole -import love.forte.simbot.definition.GuildMember +import love.forte.simbot.ability.DeleteOption +import love.forte.simbot.ability.DeleteSupport +import love.forte.simbot.common.collectable.Collectable +import love.forte.simbot.common.id.ID +import love.forte.simbot.component.qguild.ExperimentalQGApi +import love.forte.simbot.component.qguild.guild.QGMember +import love.forte.simbot.definition.Member import love.forte.simbot.qguild.QQGuildApiException -import love.forte.simbot.utils.item.Items +import love.forte.simbot.qguild.api.member.GetGuildRoleMemberListApi +import love.forte.simbot.suspendrunner.ST +import kotlin.js.JsName /** * 一个[角色][QGRole]在某个频道服务器中的表现类型。 * * @see QGRole */ -@ExperimentalSimbotApi +@ExperimentalQGApi public interface QGGuildRole : QGRole, DeleteSupport { - /** * 将当前角色赋予指定用户ID的用户。 * * @throws QQGuildApiException API异常,例如没有权限或用户不存在等 */ - @JST + @ST public suspend fun grantTo(memberId: ID): QGMemberRole /** - * 将当前角色赋予指定用户。[member] 的实际类型必须为 [QGMember] + * 将当前角色赋予指定用户。 * - * @throws ClassCastException 如果 [member] 的类型不是 [QGMember] * @throws QQGuildApiException API异常,例如没有权限或用户不存在等 */ - @JST - public suspend fun grantTo(member: GuildMember): QGMemberRole = - grantTo(member as? QGMember ?: throw ClassCastException("QGGuildRole.grantTo only support member of type QGMember, but ${member::class}")) + @ST + public suspend fun grantTo(member: QGMember): QGMemberRole /** * 将当前角色赋予指定用户。 * + * @throws ClassCastException 如果 [member] 的类型不是 [QGMember] * @throws QQGuildApiException API异常,例如没有权限或用户不存在等 */ - @JST - public suspend fun grantTo(member: QGMember): QGMemberRole + @ST + public suspend fun grantTo(member: Member): QGMemberRole = + grantTo( + member as? QGMember + ?: throw ClassCastException("QGGuildRole.grantTo only support member of type QGMember, but ${member::class}"), + ) /** @@ -70,45 +74,62 @@ public interface QGGuildRole : QGRole, DeleteSupport { * * @throws QQGuildApiException API异常,例如没有权限或用户不存在等 */ - @JST + @ST public suspend fun grantTo(memberId: ID, channelId: ID): QGMemberRole /** - * 将当前角色赋予指定用户。[member] 的实际类型必须为 [QGMember] + * 将当前角色赋予指定用户。 * * @param channelId 如果身份组 `ID` 是 `5-子频道管理员`,需要增加 `channel.id` 来指定具体是哪个子频道 * - * @throws ClassCastException 如果 [member] 的类型不是 [QGMember] * @throws QQGuildApiException API异常,例如没有权限或用户不存在等 */ - @JST - public suspend fun grantTo(member: GuildMember, channelId: ID): QGMemberRole = - grantTo(member as? QGMember ?: throw ClassCastException("QGGuildRole.grantTo only support member of type QGMember, but ${member::class}"), channelId) + @ST + public suspend fun grantTo(member: QGMember, channelId: ID): QGMemberRole /** - * 将当前角色赋予指定用户。 + * 将当前角色赋予指定用户。[member] 的实际类型必须为 [QGMember] * * @param channelId 如果身份组 `ID` 是 `5-子频道管理员`,需要增加 `channel.id` 来指定具体是哪个子频道 * + * @throws ClassCastException 如果 [member] 的类型不是 [QGMember] * @throws QQGuildApiException API异常,例如没有权限或用户不存在等 */ - @JST - public suspend fun grantTo(member: QGMember, channelId: ID): QGMemberRole + @ST + public suspend fun grantTo(member: Member, channelId: ID): QGMemberRole = + grantTo( + member as? QGMember + ?: throw ClassCastException("QGGuildRole.grantTo only support member of type QGMember, but ${member::class}"), + channelId + ) /** * 删除频道下对应的角色(身份组)。 * - * @return 如果没出现异常则始终为 `true` - * * @throws QQGuildApiException API异常,例如没有权限或对象不存在 */ - @JST - override suspend fun delete(): Boolean + @ST + override suspend fun delete(vararg options: DeleteOption) /** * 得到当前频道身份组下所有的频道成员。 + * + * @see members */ - public val members: Items + public val members: Collectable + get() = members(GetGuildRoleMemberListApi.MAX_LIMIT) + /** + * 得到当前频道身份组下所有的频道成员。 + * + * @param batch 内部自动分页查询时每一次查询的批次数量。 + * 默认为 [GetGuildRoleMemberListApi.MAX_LIMIT] + * (通过 [members] 属性获取即为此默认值。) + * + * @throws IllegalArgumentException [batch] 小于等于 `0` + * @see members + */ + @JsName("getMembers") + public fun members(batch: Int): Collectable } diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/role/QGMemberRole.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGMemberRole.kt similarity index 71% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/role/QGMemberRole.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGMemberRole.kt index cfd4e633..84ae42fb 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/role/QGMemberRole.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGMemberRole.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,23 +17,20 @@ package love.forte.simbot.component.qguild.role -import love.forte.simbot.ExperimentalSimbotApi -import love.forte.simbot.ID -import love.forte.simbot.action.DeleteSupport -import love.forte.simbot.component.qguild.JST -import love.forte.simbot.component.qguild.JSTP -import love.forte.simbot.component.qguild.QGMember -import love.forte.simbot.component.qguild.QGRole -import love.forte.simbot.definition.MemberInfoContainer +import love.forte.simbot.ability.DeleteOption +import love.forte.simbot.ability.DeleteSupport +import love.forte.simbot.common.id.ID +import love.forte.simbot.component.qguild.ExperimentalQGApi import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.suspendrunner.ST /** * 一个被赋予在某个成员身上的[角色][QGRole]。 * * @see QGRole */ -@ExperimentalSimbotApi -public interface QGMemberRole : QGRole, MemberInfoContainer, DeleteSupport { +@ExperimentalQGApi +public interface QGMemberRole : QGRole, DeleteSupport { /** * 得到当前角色所属的成员ID */ @@ -44,19 +41,13 @@ public interface QGMemberRole : QGRole, MemberInfoContainer, DeleteSupport { */ public val guildRole: QGGuildRole - /** - * 得到当前角色所属的成员 - */ - @JSTP - override suspend fun member(): QGMember - /** * 为当前成员移除此角色。即撤除对应成员的当前角色权限。 * * @throws QQGuildApiException API请求错误,例如无权限 */ - @JST - override suspend fun delete(): Boolean + @ST + override suspend fun delete(vararg options: DeleteOption) /** * 为当前成员移除此角色。即撤除对应成员的当前角色权限。 @@ -67,7 +58,6 @@ public interface QGMemberRole : QGRole, MemberInfoContainer, DeleteSupport { * * @throws QQGuildApiException API请求错误,例如无权限 */ - @JST - public suspend fun delete(channelId: ID): Boolean - + @ST + public suspend fun delete(channelId: ID, vararg options: DeleteOption): Boolean } diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGRole.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRole.kt similarity index 79% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGRole.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRole.kt index f699c08f..fe96e6c8 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGRole.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRole.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,15 +15,12 @@ * If not, see . */ -package love.forte.simbot.component.qguild +package love.forte.simbot.component.qguild.role import kotlinx.coroutines.CoroutineScope -import love.forte.simbot.ExperimentalSimbotApi -import love.forte.simbot.ID -import love.forte.simbot.component.qguild.role.QGGuildRole -import love.forte.simbot.component.qguild.role.QGMemberRole -import love.forte.simbot.component.qguild.role.QGRoleUpdater -import love.forte.simbot.definition.GuildInfoContainer +import love.forte.simbot.common.id.ID +import love.forte.simbot.component.qguild.ExperimentalQGApi +import love.forte.simbot.component.qguild.QGObjectiveContainer import love.forte.simbot.definition.Role import love.forte.simbot.qguild.model.Role.Companion.isDefault import love.forte.simbot.qguild.model.Role as QGSourceRole @@ -40,33 +37,36 @@ import love.forte.simbot.qguild.model.Role as QGSourceRole * * @author ForteScarlet */ -@ExperimentalSimbotApi -public interface QGRole : Role, CoroutineScope, GuildInfoContainer, QGObjectiveContainer { +@ExperimentalQGApi +public interface QGRole : Role, CoroutineScope, QGObjectiveContainer { /** * 身份组ID */ override val id: ID /** - * 原始的身份组对象。 + * 角色名称 */ - override val source: QGSourceRole + override val name: String get() = source.name /** - * 当前角色所属频道服务器ID。 + * 判断是拥有管理员权限。 + * + * 判断标准为是某个 [默认角色][QGSourceRole.isDefault] + * 且不是 [全体成员][QGSourceRole.DEFAULT_ID_ALL_MEMBER]。 + * */ - public val guildId: ID + override val isAdmin: Boolean get() = source.isDefault && source.id != QGSourceRole.DEFAULT_ID_ALL_MEMBER /** - * 得到当前角色所属频道服务器。 + * 原始的身份组对象。 */ - @JSTP - override suspend fun guild(): QGGuild + override val source: QGSourceRole /** - * 角色名称 + * 当前角色所属频道服务器ID。 */ - override val name: String get() = source.name + public val guildId: ID /** * ARGB的HEX十六进制颜色值转换后的十进制数值 @@ -88,14 +88,6 @@ public interface QGRole : Role, CoroutineScope, GuildInfoContainer, QGObjectiveC */ public val memberLimit: Int get() = source.memberLimit - /** - * 判断是拥有管理员权限。 - * - * 判断标准为是某个 [默认角色][QGSourceRole.isDefault] 且不是 [全体成员][QGSourceRole.DEFAULT_ID_ALL_MEMBER]。 - * - */ - override val isAdmin: Boolean get() = source.isDefault && source.id != QGSourceRole.DEFAULT_ID_ALL_MEMBER - /** * 基于当前 [QGRole] 得到一个 [QGRoleUpdater]。 * @@ -111,7 +103,7 @@ public interface QGRole : Role, CoroutineScope, GuildInfoContainer, QGObjectiveC * 更新指定的 [QGRole] 信息。 * */ -@ExperimentalSimbotApi +@ExperimentalQGApi public suspend inline fun QGRole.update(block: QGRoleUpdater.() -> Unit) { updater().also(block).update() } diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/role/QGRoleCreator.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRoleCreator.kt similarity index 88% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/role/QGRoleCreator.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRoleCreator.kt index 595d4dc7..713508e7 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/role/QGRoleCreator.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRoleCreator.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,12 +17,10 @@ package love.forte.simbot.component.qguild.role -import love.forte.simbot.ExperimentalSimbotApi -import love.forte.simbot.component.qguild.JST -import love.forte.simbot.component.qguild.QGGuild -import love.forte.simbot.component.qguild.QGRole +import love.forte.simbot.component.qguild.ExperimentalQGApi +import love.forte.simbot.component.qguild.guild.QGGuild import love.forte.simbot.qguild.QQGuildApiException - +import love.forte.simbot.suspendrunner.ST /** @@ -32,7 +30,7 @@ import love.forte.simbot.qguild.QQGuildApiException * * @author ForteScarlet */ -@ExperimentalSimbotApi +@ExperimentalQGApi public interface QGRoleCreator { // DSL API @@ -75,6 +73,6 @@ public interface QGRoleCreator { * @throws IllegalArgumentException 没有设置任何参数 * @throws QQGuildApiException API异常 */ - @JST + @ST public suspend fun create(): QGGuildRole } diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/role/QGRoleUpdater.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRoleUpdater.kt similarity index 93% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/role/QGRoleUpdater.kt rename to simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRoleUpdater.kt index 63fe3c96..94e7ad75 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/role/QGRoleUpdater.kt +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRoleUpdater.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,9 +17,8 @@ package love.forte.simbot.component.qguild.role -import love.forte.simbot.component.qguild.JST -import love.forte.simbot.component.qguild.QGRole import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.suspendrunner.ST /** @@ -72,6 +71,6 @@ public interface QGRoleUpdater { * @throws IllegalArgumentException 没有设置任何修改参数 * @throws QQGuildApiException API异常 */ - @JST + @ST public suspend fun update() } diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/utils/TimestampUtil.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/utils/TimestampUtil.kt new file mode 100644 index 00000000..78cd0b4b --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/utils/TimestampUtil.kt @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.utils + +import kotlinx.datetime.Instant +import kotlinx.datetime.toInstant +import love.forte.simbot.common.time.TimeUnit +import love.forte.simbot.common.time.Timestamp +import love.forte.simbot.component.qguild.ExperimentalQGApi + +/** + * Parse iso 8601 datetime string to [Timestamp] + * + * @see String.toInstant + */ +@ExperimentalQGApi +public fun String.toTimestamp(): Timestamp = KxInstantTimestamp(toInstant()) + +private class KxInstantTimestamp(private val instant: Instant) : Timestamp { + override val milliseconds: Long = instant.toEpochMilliseconds() + override fun timeAs(unit: TimeUnit): Long { + return when (unit) { + TimeUnit.SECONDS -> instant.epochSeconds + TimeUnit.MILLISECONDS -> milliseconds + else -> return unit.convert(milliseconds, TimeUnit.MILLISECONDS) + } + } +} diff --git a/simbot-component-qq-guild-core-common/src/commonTest/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt b/simbot-component-qq-guild-core-common/src/commonTest/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt new file mode 100644 index 00000000..b95917c5 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/commonTest/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt @@ -0,0 +1,35 @@ +package love.forte.simbot.component.qgguild.test + +import love.forte.simbot.component.qguild.bot.config.IntentsConfig +import love.forte.simbot.qguild.event.EventIntents +import kotlin.test.Test +import kotlin.test.assertEquals + + +/** + * + * @author ForteScarlet + */ +class FileConfigTest { + + @Test + fun intentsConfigTest() { + assertEquals( + IntentsConfig.Names(setOf("PublicGuildMessages")).intents, + EventIntents.PublicGuildMessages.intents + ) + assertEquals( + IntentsConfig.Names(setOf("public_guild_messages")).intents, + EventIntents.PublicGuildMessages.intents + ) + assertEquals( + IntentsConfig.Names(setOf("PublicGuildMessages", "Guilds")).intents, + EventIntents.Guilds.intents + EventIntents.PublicGuildMessages.intents + ) + assertEquals( + IntentsConfig.Names(setOf("public_guild_messages", "guilds")).intents, + EventIntents.Guilds.intents + EventIntents.PublicGuildMessages.intents + ) + } + +} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/JvmSuspendTransAnnotations.kt b/simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.js.kt similarity index 76% rename from simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/JvmSuspendTransAnnotations.kt rename to simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.js.kt index 75fdb218..38bf6501 100644 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/JvmSuspendTransAnnotations.kt +++ b/simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.js.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -17,12 +17,5 @@ package love.forte.simbot.component.qguild -import love.forte.simbot.JvmSuspendTrans -import love.forte.simbot.JvmSuspendTransProperty - - -/** @suppress */ -public typealias JST = JvmSuspendTrans - -/** @suppress */ -public typealias JSTP = JvmSuspendTransProperty +internal actual fun loadQGComponentConfigurers(): Sequence? = + null diff --git a/simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.js.kt b/simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.js.kt new file mode 100644 index 00000000..75ebde16 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.js.kt @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.bot + +internal actual fun loadQQGuildBotManagerConfigurers(): Sequence? = null diff --git a/simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.js.kt b/simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.js.kt new file mode 100644 index 00000000..e6bbf9a6 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.js.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.bot.config + +internal actual fun systemProperty(name: String): String? = null +internal actual fun systemEnv(name: String): String? = null diff --git a/simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.js.kt b/simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.js.kt new file mode 100644 index 00000000..edec521d --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.js.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.message + +import love.forte.simbot.message.Messages +import love.forte.simbot.message.OfflineImage + +internal actual fun processOfflineImage0( + index: Int, + element: OfflineImage, + messages: Messages?, + builderContext: SendingMessageParser.BuilderContext +): Boolean = false diff --git a/simbot-component-qq-guild-core-common/src/jvmMain/java/module-info.java b/simbot-component-qq-guild-core-common/src/jvmMain/java/module-info.java new file mode 100644 index 00000000..b653c2a5 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/jvmMain/java/module-info.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +import love.forte.simbot.component.ComponentFactoryProvider; +import love.forte.simbot.component.qguild.QQGuildComponentFactoryConfigurerProvider; +import love.forte.simbot.component.qguild.QQGuildComponentFactoryProvider; +import love.forte.simbot.component.qguild.bot.QQGuildBotManagerFactoryConfigurerProvider; +import love.forte.simbot.component.qguild.bot.QQGuildBotManagerProvider; +import love.forte.simbot.plugin.PluginFactoryProvider; + +module simbot.component.qqguild.core { + requires kotlin.stdlib; + requires transitive simbot.api; + requires transitive simbot.component.qqguild.stdlib; + requires transitive simbot.component.qqguild.api; + + requires kotlinx.datetime; + + // exports + + exports love.forte.simbot.component.qguild; + exports love.forte.simbot.component.qguild.bot; + exports love.forte.simbot.component.qguild.bot.config; + exports love.forte.simbot.component.qguild.channel; + exports love.forte.simbot.component.qguild.event; + exports love.forte.simbot.component.qguild.forum; + exports love.forte.simbot.component.qguild.guild; + exports love.forte.simbot.component.qguild.message; + exports love.forte.simbot.component.qguild.role; + exports love.forte.simbot.component.qguild.utils; + + // SPI + provides ComponentFactoryProvider with QQGuildComponentFactoryProvider; + uses QQGuildComponentFactoryConfigurerProvider; + provides PluginFactoryProvider with QQGuildBotManagerProvider; + uses QQGuildBotManagerFactoryConfigurerProvider; +} diff --git a/simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.jvm.kt b/simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.jvm.kt new file mode 100644 index 00000000..0c9fc60c --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.jvm.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild + +import java.util.* +import kotlin.streams.asSequence + +internal actual fun loadQGComponentConfigurers(): Sequence? { + return ServiceLoader.load(QQGuildComponentFactoryConfigurerProvider::class.java).stream().map { it.get() } + .asSequence() +} diff --git a/simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.jvm.kt b/simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.jvm.kt new file mode 100644 index 00000000..1b00d14a --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.jvm.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.bot + +import java.util.* +import kotlin.streams.asSequence + +internal actual fun loadQQGuildBotManagerConfigurers(): Sequence? { + return ServiceLoader.load(QQGuildBotManagerFactoryConfigurerProvider::class.java) + .stream().map { it.get() }.asSequence() +} diff --git a/simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.jvm.kt b/simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.jvm.kt new file mode 100644 index 00000000..8942c6b1 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.jvm.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.bot.config + +internal actual fun systemProperty(name: String): String? = System.getProperty(name) +internal actual fun systemEnv(name: String): String? = System.getenv(name) diff --git a/simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.jvm.kt b/simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.jvm.kt new file mode 100644 index 00000000..f7427c61 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.jvm.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.message + +import love.forte.simbot.message.Messages +import love.forte.simbot.message.OfflineImage +import love.forte.simbot.message.OfflineResourceImage +import love.forte.simbot.resource.FileResource +import love.forte.simbot.resource.PathResource +import love.forte.simbot.resource.URIResource + +internal actual fun processOfflineImage0( + index: Int, + element: OfflineImage, + messages: Messages?, + builderContext: SendingMessageParser.BuilderContext +): Boolean { + if (element is OfflineResourceImage) { + when (val resource = element.resource) { + is FileResource -> { + builderContext.builderOrNew { it.fileImage == null }.fileImage = resource.file + return true + } + + is PathResource -> { + builderContext.builderOrNew { it.fileImage == null }.fileImage = resource.path + return true + } + + is URIResource -> { + builderContext.builderOrNew { it.fileImage == null }.fileImage = resource.uri + return true + } + } + } + + return false +} diff --git a/simbot-component-qq-guild-core-common/src/jvmMain/resources/META-INF/services/love.forte.simbot.component.ComponentFactoryProvider b/simbot-component-qq-guild-core-common/src/jvmMain/resources/META-INF/services/love.forte.simbot.component.ComponentFactoryProvider new file mode 100644 index 00000000..c4c596f5 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/jvmMain/resources/META-INF/services/love.forte.simbot.component.ComponentFactoryProvider @@ -0,0 +1 @@ +love.forte.simbot.component.qguild.QQGuildComponentFactoryProvider diff --git a/simbot-component-qq-guild-core-common/src/jvmMain/resources/META-INF/services/love.forte.simbot.plugin.PluginFactoryProvider b/simbot-component-qq-guild-core-common/src/jvmMain/resources/META-INF/services/love.forte.simbot.plugin.PluginFactoryProvider new file mode 100644 index 00000000..f5ae69cc --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/jvmMain/resources/META-INF/services/love.forte.simbot.plugin.PluginFactoryProvider @@ -0,0 +1 @@ +love.forte.simbot.component.qguild.bot.QQGuildBotManagerProvider diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/BaseQQGuildBotManager.kt b/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/BaseQQGuildBotManager.kt deleted file mode 100644 index 0433a147..00000000 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/BaseQQGuildBotManager.kt +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright (c) 2021-2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.component.qguild - -import kotlinx.coroutines.Job -import love.forte.simbot.Attribute -import love.forte.simbot.application.ApplicationConfiguration -import love.forte.simbot.application.EventProviderFactory -import love.forte.simbot.attribute -import love.forte.simbot.bot.BotManager -import love.forte.simbot.bot.BotVerifyInfo -import love.forte.simbot.bot.ComponentMismatchException -import love.forte.simbot.component.qguild.config.QGBotComponentConfiguration -import love.forte.simbot.component.qguild.config.QGBotFileConfiguration -import love.forte.simbot.component.qguild.util.registerRootJob -import love.forte.simbot.event.EventProcessor -import love.forte.simbot.qguild.BotConfiguration -import love.forte.simbot.qguild.ConfigurableBotConfiguration -import org.slf4j.Logger -import kotlin.coroutines.CoroutineContext - -/** - * QQ频道BOT的bot管理器。 - * - * [BaseQQGuildBotManager] 不允许注册相同 `appId` 的bot。 - * - * _Note: [BaseQQGuildBotManager] 仅由内部继承实现使用,对外不稳定_ - * - * @author ForteScarlet - */ -public abstract class BaseQQGuildBotManager : BotManager() { - public abstract val eventProcessor: EventProcessor - protected abstract val logger: Logger - abstract override val component: QQGuildComponent - public abstract val configuration: QQGuildBotManagerConfiguration - - - /** - * 注册一个Bot的信息,并使用默认配置。 - */ - override fun register(verifyInfo: BotVerifyInfo): QGBot { - val serializer = QGBotFileConfiguration.serializer() - - val component = verifyInfo.componentId - - val currentComponent = this.component.id - if (component != currentComponent) { - logger.debug( - "[{}] mismatch: [{}] != [{}]", - verifyInfo.name, - component, - currentComponent - ) - throw ComponentMismatchException("[$component] != [$currentComponent]") - } - - val configuration = verifyInfo.decode(serializer) - - // no config - val (appId, secret, token) = configuration.ticket.toTicket() - return register(appId, secret, token, configuration::includeConfig) - } - - /** - * 通过所需信息注册一个bot。 - * - * 注意,在配置 [ConfigurableBotConfiguration] 时, - * 如果 [coroutineContext][ConfigurableBotConfiguration.coroutineContext] 中 - * 存在自定义的 [Job][kotlinx.coroutines.Job],那么 `Application` 中的Job - * 则会作为一个 `Root Job` 而不是 `Parent Job` 使用。 - * - * `Root Job` 仅会在其自身完成或关闭的时候**通知**相关联的子类使它们关闭, - * 但不会有硬性关联,这种通知是通过 [Job.invokeOnCompletion][kotlinx.coroutines.Job.invokeOnCompletion] 实现的, - * 参考 [Job.registerRootJob]。 - * - * 如果 [coroutineContext] 中不存在自定义的Job,则会直接使用 [QQGuildBotManager][love.forte.simbot.component.qguild.QQGuildBotManager] 内的 [Job] 作为 parent Job 。 - * - */ - public abstract fun register( - appId: String, - appKey: String, - token: String, - block: QGBotComponentConfiguration.() -> Unit = {}, - ): QGBot - - /** - * 由 [BaseQQGuildBotManager] 的实现者提供的工厂继承使用 - */ - public abstract class BaseFactory : - EventProviderFactory { - final override val key: Attribute = attribute("SIMBOT.QQGUILD") - } -} - -/** - * @suppress - */ -@DslMarker -@Retention(AnnotationRetention.BINARY) -public annotation class QGBotManagerConfigurationDsl - - -/** - * [QQGuildBotManager][love.forte.simbot.component.qguild.QQGuildBotManager] 使用的配置类描述。 - */ -public interface QQGuildBotManagerConfiguration { - - /** - * 对所有bot的配置信息进行统一处理的函数。 - * 如果直接对 [botConfigure] 进行赋值,则会覆盖之前的配置。 - * - * 如果想要以_追加_的形式进行配置,考虑使用同名的函数 [botConfigure]. - * - * e.g. - * ```kotlin - * botConfigure = { appId, appKey, token -> /* ... */ } - * ``` - */ - public var botConfigure: BotConfiguration.(appId: String, appKey: String, token: String) -> Unit - - /** - * 对所有bot的配置信息进行统一处理的函数。 - * 使用当前函数会保留之前的配置。 - * - * 如果想直接覆盖配置并抛弃之前的配置,考虑使用同名的属性 [botConfigure]. - * - * e.g. - * ```kotlin - * botConfigure { appId, appKey, token -> /* ... */ } - * ``` - */ - @QGBotManagerConfigurationDsl - public fun botConfigure(configure: BotConfiguration.(appId: String, appKey: String, token: String) -> Unit) - - /** - * 当前botManager使用的协程上下文。初始值为 [ApplicationConfiguration] 所提供的上下文。 - * - */ - public var parentCoroutineContext: CoroutineContext - - - /** - * 提供 [appId]、[appKey]、[token] 和 [配置信息][botConfiguration] 来预注册一个bot。 - * - * @param onBot 当bot被注册后的回调函数。 - */ - @QGBotManagerConfigurationDsl - public fun register( - appId: String, appKey: String, token: String, - botConfiguration: QGBotComponentConfiguration.() -> Unit = {}, - onBot: suspend (QGBot) -> Unit, - ) - - -} - - diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGChannel.kt b/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGChannel.kt deleted file mode 100644 index ce4c6b07..00000000 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGChannel.kt +++ /dev/null @@ -1,233 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.component.qguild - -import io.ktor.client.* -import io.ktor.client.request.* -import kotlinx.coroutines.CoroutineScope -import love.forte.simbot.ExperimentalSimbotApi -import love.forte.simbot.ID -import love.forte.simbot.Timestamp -import love.forte.simbot.component.qguild.message.QGMessageReceipt -import love.forte.simbot.definition.* -import love.forte.simbot.message.Message -import love.forte.simbot.message.MessageContent -import love.forte.simbot.qguild.QQGuildApiException -import love.forte.simbot.qguild.api.MessageAuditedException -import love.forte.simbot.qguild.model.ChannelType -import love.forte.simbot.utils.item.Items -import love.forte.simbot.utils.item.flowItems -import kotlin.time.Duration -import love.forte.simbot.qguild.model.Channel as QGSourceChannel - -/** - * 一个QQ频道中的子频道 [Channel] 类型, - * 提供部分来自 [source channel][QGSourceChannel] 的属性获取。 - * - * [QGChannel] 的主要类型有两个: - * - [QGTextChannel] - * - [QGNonTextChannel] - * - * 这两个类型分别代表了它们的类型是否属于 [ChannelType.TEXT]。 - * 在 [QGTextChannel] 中,实现类型允许进行消息发送的能力,而在 [QGNonTextChannel] 中使用 [send] - * 会直接得到 [UnsupportedOperationException]。 - * - * 而 [QGNonTextChannel] 下可能会有更多细分类型,详情参阅其文档注释的说明。 - * - * - * @author ForteScarlet - */ -@Suppress("DeprecatedCallableAddReplaceWith") -public interface QGChannel : BotContainer, CoroutineScope, QGObjectiveContainer, Channel { - /** - * 原始的子频道信息 - */ - override val source: QGSourceChannel - - /** - * 所属bot - */ - override val bot: QGGuildBot - - /** - * 子频道ID - */ - override val id: ID - - /** - * 所属频道ID - */ - override val guildId: ID - - /** - * 子频道名称 - */ - override val name: String get() = source.name - - /** - * 创建人ID。 - */ - override val ownerId: ID - - - /** - * 得到当前子频道所属频道服务器 - */ - @JSTP - override suspend fun guild(): QGGuild - - /** - * 得到当前子频道所属用户 - */ - @JSTP - override suspend fun owner(): QGMember - - /** - * 同 [guild] - */ - @JSTP - override suspend fun previous(): QGGuild = guild() - - - /** - * 无效的属性,始终得到 [Timestamp.notSupport]。 - */ - @Deprecated("Invalid property") - override val createTime: Timestamp get() = Timestamp.notSupport() - - /** - * 无效的属性,始终得到 `-1`。 - */ - @Deprecated("Invalid property") - override val currentMember: Int get() = -1 - - /** - * 无效的属性,始终得到 `""`。 - */ - @Deprecated("Invalid property") - override val description: String get() = "" - - /** - * 无效的属性,始终得到 `""`。 - */ - @Deprecated("Invalid property") - override val icon: String get() = "" - - /** - * 无效的属性,始终得到 `""`。 - */ - @Deprecated("Invalid property") - override val maximumMember: Int get() = -1 - - /** - * 子频道分组**的ID**。 - * - * 子频道分组ID实例 [QGChannelCategoryId] 是一个仅包含 `id` 信息的未初始化实例, - * 其 [id][QGChannelCategoryId.id] 和 [name][QGChannelCategoryId.name] - * 的值都是 [source.parentId][love.forte.simbot.qguild.model.Channel.parentId] 的值, - * 即分组ID的字符串值。 - * - * 如果希望获取完整信息,使用 [QGChannelCategoryId.resolve]. - * - * @see QGChannelCategoryId - */ - override val category: QGChannelCategoryId - - - /** - * 向子频道发送消息。此频道需要为文字子频道,否则会产生异常。 - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws QQGuildApiException 请求异常,例如无权限 - * @throws UnsupportedOperationException 如果当前子频道类型不是文字子频道 - * @throws MessageAuditedException 当响应状态为表示消息审核的 `304023`、`304024` 时 - */ - @JST - override suspend fun send(message: Message): QGMessageReceipt - - /** - * 向子频道发送消息。此频道需要为文字子频道,否则会产生异常。 - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws QQGuildApiException 请求异常,例如无权限 - * @throws UnsupportedOperationException 如果当前子频道类型不是文字子频道 - * @throws MessageAuditedException 当响应状态为表示消息审核的 `304023`、`304024` 时 - */ - @JST - override suspend fun send(text: String): QGMessageReceipt - - /** - * 向子频道发送消息。此频道需要为文字子频道,否则会产生异常。 - * - * @throws Exception see [HttpClient.request], 可能会抛出任何ktor请求过程中的异常。 - * @throws QQGuildApiException 请求异常,例如无权限 - * @throws UnsupportedOperationException 如果当前子频道类型不是文字子频道 - * @throws MessageAuditedException 当响应状态为表示消息审核的 `304023`、`304024` 时 - * - */ - @JST - override suspend fun send(message: MessageContent): QGMessageReceipt - - /** - * 子频道不能获取成员列表,考虑使用 [guild] 获取。 - */ - @Deprecated( - "Channels cannot get a list of members, use `guild` to get it.", - ReplaceWith("guild().members") - ) - override val members: Items - get() = flowItems { prop -> - guild().members.batch(prop.batch).offset(prop.offset).limit(prop.limit).collect { emit(it) } - } - - /** - * 尚不支持子频道角色(权限)获取。 - * - * 如果希望获取频道角色,使用 [guild] 后获取。 - */ - @OptIn(ExperimentalSimbotApi::class) - @Deprecated("NotSupported yet") - override val roles: Items - get() = flowItems { prop -> - guild().roles.batch(prop.batch).offset(prop.offset).limit(prop.limit).collect { emit(it) } - } - - /** - * 子频道不能获取成员,考虑使用 [guild] 获取。 - */ - @Deprecated( - "Channels cannot get a member, use `guild` to get it.", - ReplaceWith("guild().member(id)") - ) - @JST(blockingBaseName = "getMember", blockingSuffix = "", asyncBaseName = "getMember") - override suspend fun member(id: ID): QGMember? = guild().member(id) - - /** - * Note: 尚不支持对子频道的禁言相关操作 - */ - @Deprecated("Mute channel is not supported", ReplaceWith("false")) - @JvmSynthetic - override suspend fun mute(duration: Duration): Boolean = false - - /** - * Note: 尚不支持对子频道的禁言相关操作 - */ - @Deprecated("Mute channel is not supported", ReplaceWith("false")) - @JST - override suspend fun unmute(): Boolean = false -} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGChannelCategory.kt b/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGChannelCategory.kt deleted file mode 100644 index d60477a4..00000000 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGChannelCategory.kt +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (c) 2022-2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.component.qguild - -import kotlinx.coroutines.sync.Mutex -import love.forte.simbot.Api4J -import love.forte.simbot.ID -import love.forte.simbot.InternalSimbotApi -import love.forte.simbot.definition.BotContainer -import love.forte.simbot.definition.Category -import love.forte.simbot.definition.GuildInfoContainer -import love.forte.simbot.literal -import love.forte.simbot.qguild.QQGuildApiException -import love.forte.simbot.qguild.model.ChannelType -import love.forte.simbot.utils.runInAsync -import love.forte.simbot.utils.runInNoScopeBlocking -import java.util.concurrent.CompletableFuture -import love.forte.simbot.qguild.model.Channel as QGSourceChannel - - -/** - * 当一个频道的 [QGSourceChannel.type] 的值等于 [ChannelType.CATEGORY] 时, - * 此频道代表为一个分组。 - * - * [QGChannelCategoryId] 是一个**仅存在ID**的 QQ频道子频道分组实现。QQ频道对于子频道分组类型的变更不会推送事件, - * 因此无法内建缓存,而如果每次事件都要实时查询channel的分组则可能有些多余 —— 毕竟分组可能并不是一个高频使用的对象。 - * - * 因此在 [QGTextChannel.category] 中我们仅提供 [QGChannelCategoryId] 类型来直接提供分组ID, - * 并在有需要的时候通过 [resolve] 查询并获取真正的对象实例。 - * - * [QGChannelCategoryId] 中 [id] 为子频道分组的ID,[name] 由于需要通过API查询, - * 此处将直接返回 [id] 的字符串值。 - * - * 通过 [QGGuild.categories] 或 [QGGuild.category] 获取的结果均为实时查询结果, - * 它们是具体的 [QGChannelCategory] 类型,其中已经包含了具体信息。 - * - * @see QGChannelCategory - */ -public interface QGChannelCategoryId : Category, BotContainer, GuildInfoContainer { - /** - * 所属BOT - */ - override val bot: QGGuildBot - - /** - * 当前子频道分组ID - */ - override val id: ID - - /** - * 当前子频道分组ID。分组信息未初始化时,值同 [id]。 - * 如果需要获取真正的名称,判断当前类型是否为 [QGChannelCategory] - * 或直接通过 [resolve] 实时查询新的结果。 - */ - override val name: String get() = id.literal - - /** - * 获取此分类所属的频道服务器。 - */ - @JSTP - override suspend fun guild(): QGGuild - - /** - * 根据当前ID**初始化**并得到一个具体的分组对象实例 [QGChannelCategory]. - * - * [resolve] 只会最多生效一次并记录其结果,内部通过 [锁][Mutex] 控制并发。 - * - * 如下: - * ```kotlin - * category.resolve().resolve() - * ``` - * - * **至多** 只会请求一次API。参考 [QGChannelCategory.resolve]。 - * - * 如果在初始化阶段出现异常(例如无权限或未找到),则下次仍然会发起请求(且仍然会得到可能的任何异常)。 - * - * @throws NoSuchElementException 当未查询到结果(例如查询时分组已被删除) - * @throws QQGuildApiException api查询期间得到的任何异常 - */ - @JST - public suspend fun resolve(): QGChannelCategory - -} - - -/** - * 频道分组。[QGChannel] 的实现类型之一。 - * - * 当一个频道的 [QGSourceChannel.type] 的值等于 [ChannelType.CATEGORY] 时, - * 此频道代表为一个分组。 - * - * 可以通过 [QGGuild.categories] 或 [QGGuild.category] 查询获取。 - * - * @see QGChannel - * - * @author ForteScarlet - */ -public interface QGChannelCategory : Category, QGNonTextChannel, QGChannelCategoryId { - /** - * 所属BOT - */ - override val bot: QGGuildBot - - /** - * 当前子频道分组ID - */ - override val id: ID - - /** - * 获取此分类所属的频道服务器。 - */ - @JvmSynthetic - override suspend fun guild(): QGGuild - - /** - * @suppress for hidden warning - */ - @Api4J - override val guild: QGGuild get() = runInNoScopeBlocking { guild() } - - /** - * @suppress for hidden warning - */ - @OptIn(InternalSimbotApi::class) - @Api4J - override val guildAsync: CompletableFuture - get() = runInAsync(this) { guild() } - - /** - * 分组名称 - */ - override val name: String get() = source.name - - /** - * 所属频道ID - */ - override val guildId: ID - - /** - * 排序值。无法获取时得到 -1 - */ - public val position: Int get() = source.position - - - /** - * 直接得到自身 ( `this` )。 - */ - @JvmSynthetic - override suspend fun resolve(): QGChannelCategory -} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGTextChannel.kt b/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGTextChannel.kt deleted file mode 100644 index 63e07d28..00000000 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/QGTextChannel.kt +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (c) 2022-2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.component.qguild - -import love.forte.simbot.component.qguild.forum.QGForumChannel -import love.forte.simbot.message.Message -import love.forte.simbot.message.MessageContent -import love.forte.simbot.qguild.model.ChannelType - -/** - * QQ频道中的 **文字子频道**。 - * - * [QGTextChannel] 是当 [source.type][love.forte.simbot.qguild.model.Channel.type] 为 [ChannelType.TEXT] 类型时的表现。 - * - * - * @see QGChannel - * @see QGGuild - * @author ForteScarlet - */ -public interface QGTextChannel : QGChannel - - -/** - * QQ频道中的 **非文字子频道**。 - * - * 与 [QGTextChannel] 类型相对,[QGNonTextChannel] - * 用于统一表示那些不支持消息发送的子频道类型。 - * - * [QGNonTextChannel] 在默认情况下使用消息发送的API [send] - * 会直接抛出 [UnsupportedOperationException] 异常。 - * - * [QGNonTextChannel] 可能会有进一步的子类型实现用来提供更多功能或用以描述它们的类型, - * 例如: - * - [QGChannelCategory] - * - [QGForumChannel] - * - * @see QGTextChannel - * @see QGChannel - * @see QGChannelCategory - * @see QGForumChannel - * - */ -public interface QGNonTextChannel : QGChannel { - /** - * 得到当前子频道所属频道服务器 - */ - @JSTP - override suspend fun guild(): QGGuild - - /** - * 非文字子频道将会抛出 [UnsupportedOperationException] - * - * @throws UnsupportedOperationException 不支持发送消息 - */ - @JST - override suspend fun send(message: Message): Nothing { - throw UnsupportedOperationException("The type of channel(id=$id, name=$name) does not support sending messages") - } - - /** - * 非文字子频道将会抛出 [UnsupportedOperationException] - * - * @throws UnsupportedOperationException 不支持发送消息 - */ - @JST - override suspend fun send(text: String): Nothing { - throw UnsupportedOperationException("The type of channel(id=$id, name=$name) does not support sending messages") - } - - /** - * 非文字子频道将会抛出 [UnsupportedOperationException] - * - * @throws UnsupportedOperationException 不支持发送消息 - */ - @JST - override suspend fun send(message: MessageContent): Nothing { - throw UnsupportedOperationException("The type of channel(id=$id, name=$name) does not support sending messages") - } -} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt b/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt deleted file mode 100644 index 77b75d8f..00000000 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (c) 2022-2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.component.qguild.event - -import love.forte.simbot.ID -import love.forte.simbot.Timestamp -import love.forte.simbot.component.qguild.JSTP -import love.forte.simbot.component.qguild.QGGuild -import love.forte.simbot.component.qguild.QGTextChannel -import love.forte.simbot.event.* -import love.forte.simbot.message.doSafeCast -import love.forte.simbot.qguild.event.EventChannel -import love.forte.simbot.qguild.event.EventChannel as QGSourceEventChannel - -/** - * - * 子频道相关的事件。 - * - * [QGChannelEvent] 主要用于为子类型提供一个基础类型,其本身没有过多的事件类型约束。 - * - * @see QGChannelCreateEvent - * @see QGChannelUpdateEvent - * @see QGChannelDeleteEvent - * @see QGChannelCategoryCreateEvent - * @see QGChannelCategoryUpdateEvent - * @see QGChannelCategoryDeleteEvent - * - * @author ForteScarlet - */ -@BaseEvent -@JSTP -public sealed class QGChannelEvent : QGEvent() { - /** - * 标准库中子频道相关事件得到的事件本体。 - */ - abstract override val sourceEventEntity: EventChannel - - /** - * 操作者ID - */ - public val operatorId: ID get() = sourceEventEntity.opUserId.ID - - abstract override val key: Event.Key - - public companion object Key : BaseEventKey( - "qg.channel_modify", QGEvent - ) { - override fun safeCast(value: Any): QGChannelEvent? = doSafeCast(value) - } -} - - -/** - * 子频道被创建 - */ -@JSTP -public abstract class QGChannelCreateEvent : QGChannelEvent(), StartPointEvent, ChangedEvent, ChannelEvent { - abstract override val changedTime: Timestamp - override val timestamp: Timestamp get() = changedTime - - /** - * 变更源。即发生变更的频道。 - */ - abstract override suspend fun channel(): QGTextChannel - - /** - * 变更源。即发生的所在频道服务器 - */ - abstract override suspend fun source(): QGGuild - - /** - * 事件发生的频道。同 [channel]. - */ - override suspend fun organization(): QGTextChannel = channel() - - /** - * 被创建的频道。同 [source]。 - */ - override suspend fun after(): QGTextChannel = channel() - - /** - * 始终为null。 - */ - override suspend fun before(): Any? = null - - override val key: Event.Key get() = Key - - - public companion object Key : BaseEventKey( - "qg.channel_create", setOf(QGChannelEvent, StartPointEvent, ChangedEvent, ChannelEvent) - ) { - override fun safeCast(value: Any): QGChannelCreateEvent? = doSafeCast(value) - } -} - -/** - * 子频道信息变更 - * - * 无法得知变更前 ([before]) 的信息。 - */ -@JSTP -public abstract class QGChannelUpdateEvent : QGChannelEvent(), ChangedEvent, ChannelEvent { - abstract override val changedTime: Timestamp - override val timestamp: Timestamp get() = changedTime - - /** - * 变更源。即发生变更的频道。 - */ - abstract override suspend fun channel(): QGTextChannel - - /** - * 变更源。即发生的所在频道服务器 - */ - abstract override suspend fun source(): QGGuild - - /** - * 事件发生的频道。同 [channel]. - */ - override suspend fun organization(): QGTextChannel = channel() - - /** - * 发生变更的频道。同 [source]. - */ - override suspend fun after(): QGTextChannel = channel() - - /** - * 始终为null。 - */ - override suspend fun before(): Any? = null - - //// - - override val key: Event.Key get() = Key - - public companion object Key : BaseEventKey( - "qg.channel_update", QGChannelEvent, ChangedEvent, ChannelEvent - ) { - override fun safeCast(value: Any): QGChannelUpdateEvent? = doSafeCast(value) - } -} - -/** - * 子频道被删除 - * - * 当收到此事件时,channel 已经被删除。 - */ -@JSTP -public abstract class QGChannelDeleteEvent : QGChannelEvent(), EndPointEvent, ChannelEvent { - abstract override val changedTime: Timestamp - override val timestamp: Timestamp get() = changedTime - - - /** - * 被删除的频道 - */ - abstract override suspend fun channel(): QGTextChannel - - /** - * 被删除的频道,同 [channel] - */ - override suspend fun before(): QGTextChannel = channel() - - /** - * 被删除的频道,同 [channel] - */ - override suspend fun organization(): QGTextChannel = channel() - - /** - * 变更源。即发生的所在频道服务器 - */ - abstract override suspend fun source(): QGGuild - - override val key: Event.Key get() = Key - - public companion object Key : BaseEventKey( - "qg.channel_delete", setOf(QGChannelEvent, EndPointEvent, ChannelEvent) - ) { - override fun safeCast(value: Any): QGChannelDeleteEvent? = doSafeCast(value) - } -} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGEvent.kt b/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGEvent.kt deleted file mode 100644 index 1fc78bcb..00000000 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGEvent.kt +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2022-2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. If not, see . - */ - -package love.forte.simbot.component.qguild.event - -import love.forte.simbot.ID -import love.forte.simbot.Timestamp -import love.forte.simbot.component.qguild.QGBot -import love.forte.simbot.definition.BotContainer -import love.forte.simbot.event.BaseEvent -import love.forte.simbot.event.BaseEventKey -import love.forte.simbot.event.Event -import love.forte.simbot.message.doSafeCast - -/** - * - * QQ频道bot的事件总类。 - * - * @param T 此类型代表其真正事件所得到的结果。 - * - * @author ForteScarlet - */ -@BaseEvent -public abstract class QGEvent : Event, BotContainer { - abstract override val bot: QGBot - - /** - * 事件ID。 - */ - abstract override val id: ID - - /** - * 接收到事件的时间。 - */ - abstract override val timestamp: Timestamp - - /** - * 真正的原始事件所得到的事件实体。 - */ - public abstract val sourceEventEntity: T - - /** - * 接收到的事件的原始JSON字符串 - */ - public abstract val eventRaw: String - - abstract override val key: Event.Key> - - public companion object : BaseEventKey>("qg.event", Event) { - override fun safeCast(value: Any): QGEvent<*>? = doSafeCast(value) - } -} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGGuildEvents.kt b/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGGuildEvents.kt deleted file mode 100644 index 0d625ee4..00000000 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGGuildEvents.kt +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (c) 2022-2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.component.qguild.event - -import kotlinx.coroutines.Job -import kotlinx.coroutines.cancel -import love.forte.simbot.FragileSimbotApi -import love.forte.simbot.ID -import love.forte.simbot.Timestamp -import love.forte.simbot.component.qguild.JSTP -import love.forte.simbot.component.qguild.QGBot -import love.forte.simbot.component.qguild.QGGuild -import love.forte.simbot.event.* -import love.forte.simbot.message.doSafeCast -import love.forte.simbot.qguild.event.EventGuild -import love.forte.simbot.qguild.model.Guild as QGSourceGuild - -/** - * - * 频道变更事件相关。 - * - * [QGGuildEvent] 主要为子类型提供基本类型,但 [QGGuildEvent] 本身没有过多事件类型约束。 - * - * [频道新增事件][QGGuildCreateEvent] 表示bot进入到了一个新的频道中。 - * - * [频道更新事件][QGGuildUpdateEvent] 表示某个频道的信息发生了更新。 - * - * [频道移除事件][QGGuildDeleteEvent] 表示bot离开了某个频道。 - * 此事件与前两者有较大差别:它不实现 [GuildEvent] —— 毕竟频道已经离开(不存在)了。 - * - * @see QGGuildCreateEvent - * @see QGGuildUpdateEvent - * @see QGGuildDeleteEvent - * - * @author ForteScarlet - */ -public sealed class QGGuildEvent : QGEvent() { - /** - * 事件涉及的bot - */ - abstract override val bot: QGBot - - /** - * 事件操作者的ID - */ - public val operatorId: ID get() = sourceEventEntity.opUserId.ID - - abstract override val key: Event.Key - - public companion object Key : - BaseEventKey( - "qg.guild_modify", QGEvent - ) { - override fun safeCast(value: Any): QGGuildEvent? = doSafeCast(value) - } -} - -/** - * 频道创建事件。 - * - * 触发时机: - * - 机器人被加入到某个频道服务器的时候 - * - * [before] 恒为null。 - */ -@JSTP -public abstract class QGGuildCreateEvent : QGGuildEvent(), StartPointEvent, GuildEvent, ChangedEvent { - abstract override val changedTime: Timestamp - override val timestamp: Timestamp get() = changedTime - - /** - * 变更源。同 [bot]. - */ - override suspend fun source(): QGBot = bot - - /** - * 创建前。始终为null。 - */ - override suspend fun before(): QGGuild? = null - - /** - * 创建的guild。 - */ - abstract override suspend fun guild(): QGGuild - - /** - * 创建的guild。同 [guild]. - */ - override suspend fun after(): QGGuild = guild() - - /** - * 创建的guild。同 [guild]. - */ - override suspend fun organization(): QGGuild = guild() - - override val key: Event.Key get() = Key - - public companion object Key : BaseEventKey( - "qg.guild_create", setOf(QGGuildEvent, StartPointEvent, GuildEvent, ChangedEvent) - ) { - override fun safeCast(value: Any): QGGuildCreateEvent? = doSafeCast(value) - } -} - -/** - * 频道更新事件。 - * - * 触发时机: - * - 频道信息变更 - * - * [before] 恒为null。 - * - * [after] 字段内容为变更后的字段内容 - */ -@JSTP -public abstract class QGGuildUpdateEvent : QGGuildEvent(), GuildEvent, ChangedEvent { - abstract override val changedTime: Timestamp - override val timestamp: Timestamp get() = changedTime - - /** - * 变更源。同 [bot]. - */ - override suspend fun source(): QGBot = bot - - /** - * 频道更新前。无法得知,始终为null。 - */ - override suspend fun before(): QGGuild? = null - - /** - * 创建的guild。 - */ - abstract override suspend fun guild(): QGGuild - - /** - * 创建的guild。同 [guild]. - */ - override suspend fun organization(): QGGuild = guild() - - /** - * 变更的guild。同 [guild]. - */ - override suspend fun after(): QGGuild = guild() - - override val key: Event.Key get() = Key - - public companion object Key : BaseEventKey("qg.guild_update", setOf(QGGuildEvent, GuildEvent, ChangedEvent)) { - override fun safeCast(value: Any): QGGuildUpdateEvent? = doSafeCast(value) - } -} - -/** - * 频道删除事件。 - * - * [QGGuildDeleteEvent] 不实现 [GuildEvent],因为事件触发时已经离开频道,不存在可稳定获取的 [QGGuild] 实例。 - * 但是此事件额外提供属性 [guild], 代表获取一个**可能存在**的不稳定 [QGGuild]. 详细内容参考 [guild] 说明。 - * - * - * 触发时机: - * - 频道被解散 - * - 机器人被移除 - * - * [before] 字段内容为变更前的字段内容 - * - */ -@JSTP -public abstract class QGGuildDeleteEvent : QGGuildEvent(), ChangedEvent { - abstract override val changedTime: Timestamp - override val timestamp: Timestamp get() = changedTime - - /** - * 变更源。同 [bot]. - */ - override suspend fun source(): QGBot = bot - - /** - * 被删除的guild。 - * - * 获取一个**可能存在**的不稳定 [QGGuild]. - * 之所以称其为**不稳定**,因为这个对象**可能**来自于内部缓存中已经被移除的对象。当此对象能够被获取时, - * 它已经被 [关闭][QGGuild.cancel] 了,不再存在可用的 [Job] ,且已经无法使用大部分API(发送消息等) - * - * 如果希望得到较为可靠的**频道信息**,使用 [sourceEventEntity] 或 [before]. - */ - @FragileSimbotApi - public abstract val guild: QGGuild? - - - /** - * 被删除的guild信息。 - * - * 同 [sourceEventEntity] - */ - override suspend fun before(): QGSourceGuild = sourceEventEntity - - /** - * 删除后。始终为null。 - */ - override suspend fun after(): QGGuild? = null - - override val key: Event.Key get() = Key - - public companion object Key : BaseEventKey("qg.guild_delete", setOf(QGGuildEvent, ChangedEvent)) { - override fun safeCast(value: Any): QGGuildDeleteEvent? = doSafeCast(value) - } -} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGGuildMemberEvents.kt b/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGGuildMemberEvents.kt deleted file mode 100644 index 57326999..00000000 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGGuildMemberEvents.kt +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (c) 2022-2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.component.qguild.event - -import love.forte.simbot.Timestamp -import love.forte.simbot.action.ActionType -import love.forte.simbot.component.qguild.JSTP -import love.forte.simbot.component.qguild.QGGuild -import love.forte.simbot.component.qguild.QGMember -import love.forte.simbot.event.* -import love.forte.simbot.message.doSafeCast -import love.forte.simbot.qguild.event.EventIntents -import love.forte.simbot.qguild.event.EventMember - -/** - * QQ频道[成员相关的事件][EventIntents.GuildMembers]。 - * - * [QGMemberEvent] 是一种父事件类型,本身不存在过多类型约束。 - * - * - [新增频道成员][QGMemberAddEvent] - * - [频道成员信息更新][QGMemberUpdateEvent] - * - [频道成员离开/移除][QGMemberRemoveEvent] - * - * @see QGMemberAddEvent - * @see QGMemberUpdateEvent - * @see QGMemberRemoveEvent - */ -@BaseEvent -public sealed class QGMemberEvent : QGEvent() { - /** - * 事件接收到的原始的用户信息。 - */ - abstract override val sourceEventEntity: EventMember - - abstract override val key: Event.Key - - public companion object Key : BaseEventKey( - "qg.member", QGEvent - ) { - override fun safeCast(value: Any): QGMemberEvent? = doSafeCast(value) - } -} - -/** - * 频道成员增加事件。 - * - */ -@JSTP -public abstract class QGMemberAddEvent : QGMemberEvent(), GuildMemberIncreaseEvent { - /** - * 事件对象构建时间。 - */ - abstract override val changedTime: Timestamp - - /** - * 同 [changedTime] - */ - override val timestamp: Timestamp get() = changedTime - - /** - * 操作者。无权限获取、找不到(例如获取时已经离群)等情况会得到null。 - */ - abstract override suspend fun operator(): QGMember? - - /** - * 如果 [operator] 与当前成员相同则视为 [主动][ActionType.PROACTIVE], - * 其他情况均视为 [被动][ActionType.PASSIVE] - */ - abstract override val actionType: ActionType - - /** - * 新增的频道成员。 - */ - abstract override suspend fun member(): QGMember - - /** - * 新增的频道成员,同 [member] - */ - override suspend fun after(): QGMember = member() - - /** - * 新增的频道成员,同 [member] - */ - override suspend fun user(): QGMember = member() - - /** - * 增加了频道成员的频道 - */ - abstract override suspend fun guild(): QGGuild - - /** - * 增加了频道成员的频道,同 [guild] - */ - override suspend fun source(): QGGuild = guild() - - /** - * 增加了频道成员的频道,同 [guild] - */ - override suspend fun organization(): QGGuild = guild() - - override val key: Event.Key get() = Key - - public companion object Key : BaseEventKey( - "qg.member_add", QGMemberEvent, GuildMemberIncreaseEvent - ) { - override fun safeCast(value: Any): QGMemberAddEvent? = doSafeCast(value) - } -} - -/** - * 频道成员信息更新事件。 - */ -@JSTP -public abstract class QGMemberUpdateEvent : QGMemberEvent(), MemberChangedEvent, GuildEvent { - /** - * 事件对象构建时间。 - */ - abstract override val changedTime: Timestamp - - /** - * 同 [changedTime] - */ - override val timestamp: Timestamp get() = changedTime - - /** - * 操作者。无权限获取、找不到(例如获取时已经离群)等情况会得到null。 - */ - abstract override suspend fun operator(): QGMember? - - /** - * 发生变更的成员。 - */ - abstract override suspend fun member(): QGMember - - /** - * 发生变更的成员,同 [member] - */ - override suspend fun user(): QGMember = member() - /** - * 发生变更的成员,同 [member] - */ - override suspend fun after(): QGMember = member() - - /** - * 变更前成员的属性,无法获取,始终为null。 - */ - - override suspend fun before(): Any? = null - - /** - * 频道成员所处的频道 - */ - abstract override suspend fun guild(): QGGuild - - /** - * 增加了频道成员的频道,同 [guild] - */ - override suspend fun source(): QGGuild = guild() - - /** - * 频道成员所处的频道,同 [guild] - */ - override suspend fun organization(): QGGuild = guild() - - override val key: Event.Key get() = Key - - public companion object Key : BaseEventKey( - "qg.member_update", QGMemberEvent, MemberChangedEvent, GuildEvent - ) { - override fun safeCast(value: Any): QGMemberUpdateEvent? = doSafeCast(value) - } -} - -/** - * 频道成员离开/被移除事件。事件触发时已经被移除。 - */ -@JSTP -public abstract class QGMemberRemoveEvent : QGMemberEvent(), GuildMemberDecreaseEvent { - /** - * 事件对象构建时间。 - */ - abstract override val changedTime: Timestamp - - /** - * 同 [changedTime] - */ - override val timestamp: Timestamp get() = changedTime - - /** - * 操作者。无权限获取、找不到(例如获取时已经离群)等情况会得到null。 - */ - abstract override suspend fun operator(): QGMember? - - /** - * 如果 [operator] 与当前成员相同则视为 [主动][ActionType.PROACTIVE], - * 其他情况均视为 [被动][ActionType.PASSIVE] - */ - abstract override val actionType: ActionType - - /** - * 离去的成员。 - */ - abstract override suspend fun member(): QGMember - - /** - * 离去的成员,同 [member] - */ - override suspend fun before(): QGMember = member() - - /** - * 离去的成员,同 [member] - */ - override suspend fun user(): QGMember = member() - - /** - * 频道成员所处的频道 - */ - abstract override suspend fun guild(): QGGuild - - /** - * 频道成员所处的频道,同 [guild] - */ - override suspend fun source(): QGGuild = guild() - - /** - * 频道成员所处的频道,同 [guild] - */ - override suspend fun organization(): QGGuild = guild() - - override val key: Event.Key get() = Key - - public companion object Key : BaseEventKey( - "qg.member_remove", QGMemberEvent, GuildMemberDecreaseEvent - ) { - override fun safeCast(value: Any): QGMemberRemoveEvent? = doSafeCast(value) - } -} - - diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGInternalBotEvent.kt b/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGInternalBotEvent.kt deleted file mode 100644 index 3d7c0074..00000000 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/event/QGInternalBotEvent.kt +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2022-2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.component.qguild.event - -import love.forte.simbot.component.qguild.BaseQQGuildBotManager -import love.forte.simbot.component.qguild.QGBot -import love.forte.simbot.event.BaseEventKey -import love.forte.simbot.event.Event -import love.forte.simbot.event.internal.* -import love.forte.simbot.message.doSafeCast - -/** - * - * 频道组件中对于 [InternalBotEvent] 事件的统一实现接口. - * - * 虽然 [QGInternalBotEvent] 实现的是 [Event], 但是它作为 [InternalBotEvent] 的标准实现使用。 - * - * @author ForteScarlet - */ -public sealed interface QGInternalBotEvent : Event { - - - public companion object Key : BaseEventKey( - "qg.internal.bot", InternalBotEvent - ) { - override fun safeCast(value: Any): QGInternalBotEvent? = doSafeCast(value) - } -} - -/** - * qq频道组件中,每当 [BaseQQGuildBotManager] 通过任意 [BaseQQGuildBotManager.register] 注册并得到Bot实例后触发的事件。 - * - * 遵循 [BotRegisteredEvent] 约定特性,此事件会在注册完成后**异步**触发. - * - * @see BotRegisteredEvent - * @see BaseQQGuildBotManager - * @see BaseQQGuildBotManager.register - */ -public abstract class QGBotRegisteredEvent : BotRegisteredEvent(), QGInternalBotEvent { - - abstract override val bot: QGBot - - //// - override val key: InternalEvent.Key get() = Key - - public companion object Key : BaseInternalKey( - "qg.internal.bot_registered", InternalBotEvent, QGInternalBotEvent - ) { - override fun safeCast(value: Any): QGBotRegisteredEvent? = doSafeCast(value) - } -} - - -/** - * tcg组件中,每当 [QGBot.start] 被执行的时候会被推送的事件。 - * 当事件被推送的时候代表此bot实际上已经完成的 `start` 的逻辑,但是[QGBot.start]会直到事件处理流程完成后才会最终返回。 - * - * @see BotStartedEvent - * @see QGBot - * @see QGBot.start - */ -public abstract class QGBotStartedEvent : BotStartedEvent(), QGInternalBotEvent { - abstract override val bot: QGBot - - //// - override val key: InternalEvent.Key get() = Key - - public companion object Key : BaseInternalKey( - "qg.internal.bot_started", BotStartedEvent, QGInternalBotEvent - ) { - override fun safeCast(value: Any): QGBotStartedEvent? = doSafeCast(value) - } - -} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGForums.kt b/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGForums.kt deleted file mode 100644 index c10284e8..00000000 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/forum/QGForums.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.component.qguild.forum - -import kotlinx.coroutines.CoroutineScope -import love.forte.simbot.ID -import love.forte.simbot.JST -import love.forte.simbot.component.qguild.JSTP -import love.forte.simbot.component.qguild.QGBot -import love.forte.simbot.component.qguild.QGGuild -import love.forte.simbot.definition.BotContainer -import love.forte.simbot.definition.GuildInfoContainer -import love.forte.simbot.qguild.QQGuildApiException -import love.forte.simbot.qguild.model.ChannelType -import love.forte.simbot.utils.item.Items - - -/** - * - * QQ频道中对帖子子频道的操作器。 - * - * @author ForteScarlet - */ -public interface QGForums : CoroutineScope, BotContainer, GuildInfoContainer { - - /** - * 所属 [QGBot]. - */ - override val bot: QGBot - - /** - * 所属 [QGGuild]. - */ - @JSTP - override suspend fun guild(): QGGuild - - /** - * 得到当前 guild 中的所有 **帖子类型** [ChannelType.FORUM] 的子频道实例。 - * - * 得到的数据集是 [QGGuild.channels] 的子集。 - * - * @see QGForumChannel - * - */ - public val forumChannels: Items - - /** - * 根据ID寻找匹配的 - * - * @throws QQGuildApiException API请求过程中出现的异常 - * @throws IllegalStateException 当目标子频道类型不是 [ChannelType.FORUM] 时 - */ - @JST(blockingBaseName = "getForumChannel", blockingSuffix = "", asyncBaseName = "getForumChannel") - public suspend fun forumChannel(id: ID): QGForumChannel? - -} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/ImageParser.kt b/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/ImageParser.kt deleted file mode 100644 index a50d108f..00000000 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/message/ImageParser.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.component.qguild.message - -import io.ktor.client.request.forms.* -import io.ktor.utils.io.streams.* -import love.forte.simbot.message.Image -import love.forte.simbot.message.Message -import love.forte.simbot.message.Messages -import love.forte.simbot.resources.ByteArrayResource -import love.forte.simbot.resources.FileResource -import love.forte.simbot.resources.PathResource -import love.forte.simbot.resources.URLResource - - -/** - * - * @author ForteScarlet - */ -public object ImageParser : SendingMessageParser { - override suspend fun invoke( - index: Int, - element: Message.Element<*>, - messages: Messages?, - builderContext: SendingMessageParser.BuilderContext - ) { - // TODO attachment? - - when (element) { - is Image -> { - when (val resource = element.resource()) { - is FileResource -> { - builderContext.builderOrNew { it.fileImage == null }.setFileImage(resource.file) - } - - is PathResource -> { - builderContext.builderOrNew { it.fileImage == null }.setFileImage(resource.path) - } - - is ByteArrayResource -> { - builderContext.builderOrNew { it.fileImage == null }.setFileImage(resource.bytes) - } - - is URLResource -> { - builderContext.builderOrNew { it.image == null }.image = resource.url.toString() - } - - else -> { - builderContext.builderOrNew { it.fileImage == null }.setFileImage(InputProvider { resource.openStream().asInput() }) - } - } - } - - // TODO more image type support for file_image - } - } -} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/util/ComponentBotRequestUtil.kt b/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/util/ComponentBotRequestUtil.kt deleted file mode 100644 index 6b480b2f..00000000 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/util/ComponentBotRequestUtil.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2022-2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -@file:JvmName("BotRequestUtil") - -package love.forte.simbot.component.qguild.util - -import love.forte.simbot.component.qguild.QGBot -import love.forte.simbot.qguild.api.QQGuildApi -import love.forte.simbot.qguild.request -import love.forte.simbot.qguild.requestBy -import love.forte.simbot.qguild.requestRaw -import love.forte.simbot.qguild.requestRawBy - -/** - * 直接通过bot进行请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@JvmSynthetic -public suspend inline fun QQGuildApi.requestBy(bot: QGBot): R { - return requestBy(bot.source) -} - -/** - * 直接通过bot进行请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@JvmSynthetic -public suspend inline fun QGBot.request(api: QQGuildApi): R { - return source.request(api) -} - -/** - * 直接通过bot进行请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@JvmSynthetic -public suspend inline fun QQGuildApi<*>.requestRawBy(bot: QGBot): String { - return requestRawBy(bot.source) -} - -/** - * 直接通过bot进行请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@JvmSynthetic -public suspend inline fun QGBot.requestRaw(api: QQGuildApi<*>): String { - return source.requestRaw(api) -} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/util/QGBotRequestUtil.kt b/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/util/QGBotRequestUtil.kt deleted file mode 100644 index 4330e87e..00000000 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/util/QGBotRequestUtil.kt +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright (c) 2022-2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -@file:JvmName("QGBotRequestUtil") - -package love.forte.simbot.component.qguild.util - -import kotlinx.coroutines.future.future -import love.forte.simbot.Api4J -import love.forte.simbot.component.qguild.QGBot -import love.forte.simbot.qguild.api.QQGuildApi -import love.forte.simbot.qguild.request -import love.forte.simbot.qguild.requestBy -import love.forte.simbot.qguild.requestRaw -import love.forte.simbot.qguild.requestRawBy -import love.forte.simbot.utils.runInNoScopeBlocking -import java.util.concurrent.CompletableFuture - - -/** - * 直接通过bot进行请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@Api4J -public fun QQGuildApi.requestBlockingBy(bot: QGBot): R = runInNoScopeBlocking { - requestBy(bot.source) -} - -/** - * 直接通过bot进行请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@Api4J -public fun QQGuildApi.requestAsyncBy(bot: QGBot): CompletableFuture = bot.source.future { - requestBy(bot.source) -} - -/** - * 直接通过bot进行请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@Api4J -public fun QGBot.requestBlocking(api: QQGuildApi): R = runInNoScopeBlocking { - source.request(api) -} - -/** - * 直接通过bot进行请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@Api4J -public fun QGBot.requestAsync(api: QQGuildApi): CompletableFuture = source.future { - source.request(api) -} - -/** - * 直接通过bot进行请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@Api4J -public fun QGBot.requestRawBlocking(api: QQGuildApi<*>): String = runInNoScopeBlocking { - source.requestRaw(api) -} - -/** - * 直接通过bot进行请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@Api4J -public fun QGBot.requestRawAsync(api: QQGuildApi<*>): CompletableFuture = source.future { - source.requestRaw(api) -} - -/** - * 直接通过bot进行请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@Api4J -public fun QQGuildApi<*>.requestRawBlockingBy(bot: QGBot): String = runInNoScopeBlocking { - requestRawBy(bot.source) -} - -/** - * 直接通过bot进行请求。 - * - * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 - */ -@Api4J -public fun QQGuildApi<*>.requestRawAsyncBy(bot: QGBot): CompletableFuture = bot.source.future { - requestRawBy(bot.source) -} diff --git a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/util/RootJobUtil.kt b/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/util/RootJobUtil.kt deleted file mode 100644 index 32c58e16..00000000 --- a/simbot-component-qq-guild-core-common/src/main/kotlin/love/forte/simbot/component/qguild/util/RootJobUtil.kt +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. If not, see . - */ - -package love.forte.simbot.component.qguild.util - -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.DisposableHandle -import kotlinx.coroutines.Job - - -/** - * 为当前bot指定一个 `Root Job`。 - * - * `Root Job` 不会像 `Parent Job` 那样与当前Job硬性关联, - * 其唯一的作用就是当 [rootJob] 完成的时候通知并关闭当前Job, - * 这是通过 [Job.invokeOnCompletion] 实现的。 - * - * 当前job既不会出现在 [rootJob] 的 [children][Job.children] - * 中,也不会因为异常等情况导致 `Root Job` 被关闭。 - * - * 可以通过返回的 [DisposableHandle] 来取消这种关联。 - * - */ -public fun Job.registerRootJob(rootJob: Job): DisposableHandle { - val subJob = this - return rootJob.invokeOnCompletion { e -> - when (e) { - null -> subJob.cancel() - is CancellationException -> subJob.cancel(e) - else -> subJob.cancel(CancellationException(e.localizedMessage, e)) - } - } -} diff --git a/simbot-component-qq-guild-core-common/src/main/resources/META-INF/services/love.forte.simbot.ComponentAutoRegistrarFactory b/simbot-component-qq-guild-core-common/src/main/resources/META-INF/services/love.forte.simbot.ComponentAutoRegistrarFactory deleted file mode 100644 index e12223eb..00000000 --- a/simbot-component-qq-guild-core-common/src/main/resources/META-INF/services/love.forte.simbot.ComponentAutoRegistrarFactory +++ /dev/null @@ -1 +0,0 @@ -love.forte.simbot.component.qguild.QQGuildComponentRegistrarFactory diff --git a/simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.native.kt b/simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.native.kt new file mode 100644 index 00000000..38bf6501 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.native.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild + +internal actual fun loadQGComponentConfigurers(): Sequence? = + null diff --git a/simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.native.kt b/simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.native.kt new file mode 100644 index 00000000..75ebde16 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.native.kt @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.bot + +internal actual fun loadQQGuildBotManagerConfigurers(): Sequence? = null diff --git a/simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.native.kt b/simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.native.kt new file mode 100644 index 00000000..e6bbf9a6 --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.native.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.bot.config + +internal actual fun systemProperty(name: String): String? = null +internal actual fun systemEnv(name: String): String? = null diff --git a/simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.native.kt b/simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.native.kt new file mode 100644 index 00000000..edec521d --- /dev/null +++ b/simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.native.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.message + +import love.forte.simbot.message.Messages +import love.forte.simbot.message.OfflineImage + +internal actual fun processOfflineImage0( + index: Int, + element: OfflineImage, + messages: Messages?, + builderContext: SendingMessageParser.BuilderContext +): Boolean = false diff --git a/simbot-component-qq-guild-core-common/src/test/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt b/simbot-component-qq-guild-core-common/src/test/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt deleted file mode 100644 index 6104447b..00000000 --- a/simbot-component-qq-guild-core-common/src/test/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt +++ /dev/null @@ -1,23 +0,0 @@ -package love.forte.simbot.component.qgguild.test - -import love.forte.simbot.component.qguild.config.IntentsConfig -import love.forte.simbot.qguild.event.EventIntents -import kotlin.test.Test - - -/** - * - * @author ForteScarlet - */ -class FileConfigTest { - - @Test - fun intentsConfigTest() { - assert(IntentsConfig.Names(setOf("PublicGuildMessages")).intents == EventIntents.PublicGuildMessages.intents) - assert(IntentsConfig.Names(setOf("public_guild_messages")).intents == EventIntents.PublicGuildMessages.intents) - - assert(IntentsConfig.Names(setOf("PublicGuildMessages", "Guilds")).intents == EventIntents.Guilds.intents + EventIntents.PublicGuildMessages.intents) - assert(IntentsConfig.Names(setOf("public_guild_messages", "guilds")).intents == EventIntents.Guilds.intents + EventIntents.PublicGuildMessages.intents) - } - -} diff --git a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.jvm.kt b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.jvm.kt index b9576e96..c6373a21 100644 --- a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.jvm.kt +++ b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/BotRequests.jvm.kt @@ -24,9 +24,39 @@ import io.ktor.client.statement.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.future.future import love.forte.simbot.annotations.Api4J +import love.forte.simbot.annotations.InternalSimbotAPI import love.forte.simbot.qguild.api.QQGuildApi +import love.forte.simbot.suspendrunner.reserve.SuspendReserve +import love.forte.simbot.suspendrunner.reserve.suspendReserve import love.forte.simbot.suspendrunner.runInNoScopeBlocking import java.util.concurrent.CompletableFuture +import kotlin.coroutines.EmptyCoroutineContext + +/** + * 直接通过bot进行请求。 + */ +@Api4J +public fun Bot.requestBlocking(api: QQGuildApi<*>): HttpResponse = runInNoScopeBlocking { + request(api) +} + +/** + * 直接通过bot进行请求。 + */ +@Api4J +public fun Bot.requestTextBlocking(api: QQGuildApi<*>): String = runInNoScopeBlocking { + requestText(api) +} + +/** + * 直接通过bot进行请求。 + * + * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 + */ +@Api4J +public fun Bot.requestDataBlocking(api: QQGuildApi): R = runInNoScopeBlocking { + requestData(api) +} /** @@ -69,28 +99,52 @@ public fun Bot.requestDataAsync( requestData(api) } + /** * 直接通过bot进行请求。 + * + * [SuspendReserve] 可能使用的作用域 [scope] 默认为 [Bot.apiClient]。 + * + * @see SuspendReserve */ @Api4J -public fun Bot.requestBlocking(api: QQGuildApi<*>): HttpResponse = runInNoScopeBlocking { - request(api) -} +@JvmOverloads +@OptIn(InternalSimbotAPI::class) +public fun Bot.requestReserve(api: QQGuildApi<*>, scope: CoroutineScope? = null): SuspendReserve = + suspendReserve(scope ?: apiClient, EmptyCoroutineContext) { + request(api) + } /** * 直接通过bot进行请求。 + * + * [SuspendReserve] 可能使用的作用域 [scope] 默认为 [Bot.apiClient]。 + * + * @see SuspendReserve */ @Api4J -public fun Bot.requestTextBlocking(api: QQGuildApi<*>): String = runInNoScopeBlocking { - requestText(api) -} +@JvmOverloads +@OptIn(InternalSimbotAPI::class) +public fun Bot.requestTextReserve(api: QQGuildApi<*>, scope: CoroutineScope? = null): SuspendReserve = + suspendReserve(scope ?: apiClient, EmptyCoroutineContext) { + requestText(api) + } /** * 直接通过bot进行请求。 * + * [SuspendReserve] 可能使用的作用域 [scope] 默认为 [Bot.apiClient]。 + * * @throws love.forte.simbot.qguild.QQGuildApiException 如果返回状态码不在 200..300之间。 + * + * @see SuspendReserve */ @Api4J -public fun Bot.requestDataBlocking(api: QQGuildApi): R = runInNoScopeBlocking { +@JvmOverloads +@OptIn(InternalSimbotAPI::class) +public fun Bot.requestDataReserve( + api: QQGuildApi, + scope: CoroutineScope? = null +): SuspendReserve = suspendReserve(scope ?: apiClient, EmptyCoroutineContext) { requestData(api) } From 31ec1c0fa1cf779d6fc812d8860b76acf41eb54e Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 15 Jan 2024 15:11:31 +0800 Subject: [PATCH 13/71] Module: core --- README.md | 2 +- build.gradle.kts | 2 +- gradle/libs.versions.toml | 2 +- settings.gradle.kts | 5 +- .../simbot/qguild/QQGuildApiException.kt | 8 +- .../love/forte/simbot/qguild/initCause.js.kt | 5 +- .../love/forte/simbot/qguild/initCause.jvm.kt | 5 +- .../forte/simbot/qguild/initCause.native.kt | 3 +- simbot-component-qq-guild-core-1/Module.md | 4 + simbot-component-qq-guild-core-1/README.md | 234 ++++++++++++++++++ .../build.gradle.kts | 48 ++++ .../component/qguild/QQGuildBotManager.kt | 0 .../qguild/QQGuildBotManagerUsage.kt | 0 .../component/qguild/QQGuildComponentUsage.kt | 0 .../component/qguild/internal/InstantUtil.kt | 0 .../component/qguild/internal/QGBotImpl.kt | 0 .../qguild/internal/QGChannelCategoryImpl.kt | 0 .../qguild/internal/QGEventProcess.kt | 0 .../qguild/internal/QGGuildBotImpl.kt | 0 .../component/qguild/internal/QGGuildImpl.kt | 0 .../component/qguild/internal/QGMemberImpl.kt | 0 .../qguild/internal/QGNonTextChannelImpl.kt | 0 .../qguild/internal/QGTextChannelImpl.kt | 0 .../qguild/internal/QQGuildBotManagerImpl.kt | 0 .../event/QGAtMessageCreateEventImpl.kt | 0 .../internal/event/QGChannelEventImpls.kt | 0 .../internal/event/QGForumEventImpls.kt | 0 .../internal/event/QGGuildEventImpls.kt | 0 .../internal/event/QGInternalBotEventImpl.kt | 0 .../internal/event/QGMemberEventImpls.kt | 0 .../internal/event/QGOpenForumEventImpls.kt | 0 .../internal/event/QGUnsupportedEventImpl.kt | 0 .../internal/forum/QGForumChannelImpl.kt | 0 .../qguild/internal/forum/QGForumsImpl.kt | 0 .../qguild/internal/forum/QGPostImpl.kt | 0 .../qguild/internal/forum/QGReplyImpl.kt | 0 .../internal/forum/QGThreadCreatorImpl.kt | 0 .../qguild/internal/forum/QGThreadImpl.kt | 0 .../message/QGReceiveMessageContentImpl.kt | 0 .../message/QGSingleMessageReceiptImpl.kt | 0 .../qguild/internal/role/BaseQGRole.kt | 0 .../qguild/internal/role/QGGuildRoleImpl.kt | 0 .../qguild/internal/role/QGMemberRoleImpl.kt | 0 .../qguild/internal/role/QGRoleCreatorImpl.kt | 0 .../qguild/internal/role/QGRoleUpdaterImpl.kt | 0 .../internal/utils/TimestampDelegate.kt | 0 .../component/qguild/message/MessageSender.kt | 0 ...lication.EventProviderAutoRegistrarFactory | 0 .../resources/simbot-logger-slf4j.properties | 0 .../Module.md | 4 - .../README.md | 101 -------- .../qguild/internal/forum/QGForumsImpl.kt | 72 ------ .../qguild/internal/role/QGMemberRoleImpl.kt | 81 ------ simbot-component-qq-guild-core/Module.md | 6 +- simbot-component-qq-guild-core/README.md | 233 ++++------------- .../build.gradle.kts | 75 ++++-- .../component/qguild/QGObjectiveContainer.kt | 0 .../qguild/QQGuildBotManagerUsage.kt | 0 .../component/qguild/QQGuildComponent.kt | 0 .../component/qguild/QQGuildComponentUsage.kt | 0 .../component/qguild/QQGuildInitException.kt | 0 .../simbot/component/qguild/annotations.kt | 0 .../simbot/component/qguild/bot/QGBot.kt | 0 .../component/qguild/bot/QQGuildBotManager.kt | 0 .../qguild/bot/config/CacheStrategyConfig.kt | 0 .../qguild/bot/config/IntentsConfig.kt | 0 .../bot/config/QGBotComponentConfiguration.kt | 0 .../bot/config/QGBotFileConfiguration.kt | 0 .../qguild/bot/config/ShardConfig.kt | 0 .../qguild/bot/config/TicketConfiguration.kt | 0 .../component/qguild/channel/QGCategory.kt | 0 .../component/qguild/channel/QGChannel.kt | 0 .../qguild/channel/QGForumChannel.kt | 0 .../component/qguild/channel/QGTextChannel.kt | 0 .../component/qguild/event/QGChannelEvents.kt | 0 .../simbot/component/qguild/event/QGEvent.kt | 0 .../component/qguild/event/QGForumEvents.kt | 14 +- .../component/qguild/event/QGGuildEvents.kt | 5 +- .../qguild/event/QGGuildMemberEvents.kt | 10 +- .../qguild/event/QGInternalBotEvent.kt | 12 + .../component/qguild/event/QGMessageEvents.kt | 10 +- .../qguild/event/QGOpenForumEvents.kt | 8 + .../qguild/event/QGUnsupportedEvent.kt | 4 +- .../qguild/forum/QGForumInfoContainer.kt | 0 .../simbot/component/qguild/forum/QGPost.kt | 0 .../simbot/component/qguild/forum/QGReply.kt | 0 .../simbot/component/qguild/forum/QGThread.kt | 0 .../component/qguild/forum/QGThreadCreator.kt | 0 .../simbot/component/qguild/guild/QGGuild.kt | 43 +++- .../component/qguild/guild/QGGuildRelation.kt | 29 +++ .../simbot/component/qguild/guild/QGMember.kt | 2 + .../qguild/internal/bot/QGBotImpl.kt | 184 +++++++------- .../internal/bot/QQGuildBotManagerImpl.kt | 0 .../internal/channel/QGChannelCategoryImpl.kt | 15 +- .../internal/channel/QGForumChannelImpl.kt | 30 ++- .../internal/channel/QGNonTextChannelImpl.kt | 34 +-- .../internal/channel/QGTextChannelImpl.kt | 40 +-- .../event/QGAtMessageCreateEventImpl.kt | 113 +++++++++ .../internal/event/QGChannelEventImpls.kt | 73 ++++++ .../internal/event/QGForumEventImpls.kt | 150 +++++++++++ .../internal/event/QGGuildEventImpls.kt | 67 +++++ .../internal/event/QGInternalBotEventImpl.kt | 27 ++ .../internal/event/QGMemberEventImpls.kt | 119 +++++++++ .../internal/event/QGOpenForumEventImpls.kt | 105 ++++++++ .../internal/event/QGUnsupportedEventImpl.kt | 33 +++ .../qguild/internal/forum/QGForumsImpl.kt | 16 ++ .../qguild/internal/forum/QGPostImpl.kt | 35 +-- .../qguild/internal/forum/QGReplyImpl.kt | 35 +-- .../internal/forum/QGThreadCreatorImpl.kt | 19 +- .../qguild/internal/forum/QGThreadImpl.kt | 70 +++--- .../qguild/internal/guild/QGGuildImpl.kt | 136 +++++----- .../qguild/internal/guild/QGMemberImpl.kt | 38 +-- .../message/QGReceiveMessageContentImpl.kt | 0 .../message/QGSingleMessageReceiptImpl.kt | 0 .../qguild/internal/role/BaseQGRole.kt | 2 +- .../qguild/internal/role/QGGuildRoleImpl.kt | 66 +++-- .../qguild/internal/role/QGMemberRoleImpl.kt | 100 ++++++++ .../qguild/internal/role/QGRoleCreatorImpl.kt | 23 +- .../qguild/internal/role/QGRoleUpdaterImpl.kt | 17 +- .../component/qguild/message/ImageParser.kt | 0 .../component/qguild/message/MessageSender.kt | 0 .../simbot/component/qguild/message/QGArk.kt | 0 .../component/qguild/message/QGAtMessages.kt | 0 .../qguild/message/QGAttachmentMessage.kt | 0 .../component/qguild/message/QGContentText.kt | 0 .../component/qguild/message/QGEmbed.kt | 0 .../component/qguild/message/QGImages.kt | 0 .../qguild/message/QGMessageContent.kt | 0 .../qguild/message/QGMessageElement.kt | 0 .../qguild/message/QGMessageReceipt.kt | 0 .../component/qguild/message/QGReference.kt | 0 .../component/qguild/message/QGReplyTo.kt | 0 .../qguild/message/SendingMessageParser.kt | 0 .../qguild/message/StandardMessageParsers.kt | 0 .../component/qguild/message/annotations.kt | 0 .../component/qguild/role/QGGuildRole.kt | 5 +- .../component/qguild/role/QGMemberRole.kt | 2 +- .../simbot/component/qguild/role/QGRole.kt | 0 .../component/qguild/role/QGRoleCreator.kt | 0 .../component/qguild/role/QGRoleUpdater.kt | 0 .../component/qguild/utils/TimestampUtil.kt | 0 .../component/qgguild/test/FileConfigTest.kt | 0 .../component/qguild/QQGuildComponent.js.kt | 0 .../qguild/bot/QQGuildBotManager.js.kt | 0 .../bot/config/TicketConfiguration.js.kt | 0 .../qguild/message/ImageParser.js.kt | 0 .../src/jvmMain/java/module-info.java | 0 .../component/qguild/QQGuildComponent.jvm.kt | 0 .../qguild/bot/QQGuildBotManager.jvm.kt | 0 .../bot/config/TicketConfiguration.jvm.kt | 0 .../qguild/message/ImageParser.jvm.kt | 0 ....simbot.component.ComponentFactoryProvider | 0 ....forte.simbot.plugin.PluginFactoryProvider | 0 .../qguild/QQGuildComponent.native.kt | 0 .../qguild/bot/QQGuildBotManager.native.kt | 0 .../bot/config/TicketConfiguration.native.kt | 0 .../qguild/message/ImageParser.native.kt | 0 .../application-test}/build.gradle.kts | 16 +- .../src/commonMain/kotlin/TestMain.kt | 21 ++ 159 files changed, 1705 insertions(+), 913 deletions(-) create mode 100644 simbot-component-qq-guild-core-1/Module.md create mode 100644 simbot-component-qq-guild-core-1/README.md create mode 100644 simbot-component-qq-guild-core-1/build.gradle.kts rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildBotManager.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildBotManagerUsage.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/InstantUtil.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGBotImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGChannelCategoryImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGEventProcess.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGGuildBotImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGGuildImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGMemberImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGNonTextChannelImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGTextChannelImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/QQGuildBotManagerImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGAtMessageCreateEventImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGChannelEventImpls.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGForumEventImpls.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGGuildEventImpls.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGInternalBotEventImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGMemberEventImpls.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGOpenForumEventImpls.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGUnsupportedEventImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumChannelImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGPostImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGReplyImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadCreatorImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/message/QGReceiveMessageContentImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/message/QGSingleMessageReceiptImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/BaseQGRole.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleCreatorImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleUpdaterImpl.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/internal/utils/TimestampDelegate.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/kotlin/love/forte/simbot/component/qguild/message/MessageSender.kt (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/main/resources/META-INF/services/love.forte.simbot.application.EventProviderAutoRegistrarFactory (100%) rename {simbot-component-qq-guild-core => simbot-component-qq-guild-core-1}/src/test/resources/simbot-logger-slf4j.properties (100%) delete mode 100644 simbot-component-qq-guild-core-common/Module.md delete mode 100644 simbot-component-qq-guild-core-common/README.md delete mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt delete mode 100644 simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/QGObjectiveContainer.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildBotManagerUsage.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildInitException.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/annotations.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QGBot.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/CacheStrategyConfig.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/IntentsConfig.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotComponentConfiguration.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotFileConfiguration.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/ShardConfig.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGCategory.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGChannel.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGForumChannel.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGTextChannel.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGEvent.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt (94%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildEvents.kt (96%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildMemberEvents.kt (94%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGInternalBotEvent.kt (81%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt (92%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt (96%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt (95%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGForumInfoContainer.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGPost.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGReply.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGThread.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGThreadCreator.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuild.kt (82%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuildRelation.kt (75%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGMember.kt (98%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QGBotImpl.kt (72%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGChannelCategoryImpl.kt (87%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGForumChannelImpl.kt (89%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGNonTextChannelImpl.kt (75%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGTextChannelImpl.kt (86%) create mode 100644 simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGAtMessageCreateEventImpl.kt create mode 100644 simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGChannelEventImpls.kt create mode 100644 simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGForumEventImpls.kt create mode 100644 simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGGuildEventImpls.kt create mode 100644 simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGInternalBotEventImpl.kt create mode 100644 simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGMemberEventImpls.kt create mode 100644 simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGOpenForumEventImpls.kt create mode 100644 simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGUnsupportedEventImpl.kt create mode 100644 simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGPostImpl.kt (59%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGReplyImpl.kt (59%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadCreatorImpl.kt (71%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt (51%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGGuildImpl.kt (54%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGMemberImpl.kt (84%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGReceiveMessageContentImpl.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGSingleMessageReceiptImpl.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/BaseQGRole.kt (95%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt (75%) create mode 100644 simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleCreatorImpl.kt (77%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleUpdaterImpl.kt (79%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/MessageSender.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGArk.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGAtMessages.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGAttachmentMessage.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGContentText.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGEmbed.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGImages.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageContent.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageElement.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageReceipt.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGReference.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGReplyTo.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/SendingMessageParser.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/StandardMessageParsers.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/annotations.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGGuildRole.kt (94%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGMemberRole.kt (96%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRole.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRoleCreator.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRoleUpdater.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonMain/kotlin/love/forte/simbot/component/qguild/utils/TimestampUtil.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/commonTest/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/jsMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.js.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.js.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.js.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/jsMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.js.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/jvmMain/java/module-info.java (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/jvmMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.jvm.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.jvm.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.jvm.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/jvmMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.jvm.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/jvmMain/resources/META-INF/services/love.forte.simbot.component.ComponentFactoryProvider (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/jvmMain/resources/META-INF/services/love.forte.simbot.plugin.PluginFactoryProvider (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/nativeMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.native.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.native.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.native.kt (100%) rename {simbot-component-qq-guild-core-common => simbot-component-qq-guild-core}/src/nativeMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.native.kt (100%) rename {simbot-component-qq-guild-core-common => tests/application-test}/build.gradle.kts (82%) create mode 100644 tests/application-test/src/commonMain/kotlin/TestMain.kt diff --git a/README.md b/README.md index bf90534f..6995d970 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,7 @@ 基于 [标准库模块](simbot-component-qq-guild-stdlib) 对 [simbot3核心库](https://github.com/simple-robot/simpler-robot) 的组件实现, 是一个相对高度封装的模块,并提供simbot3大部分能力,包括事件监听、多组件协同、Spring Boot Starter 等。 -👉 [前往模块](simbot-component-qq-guild-core) 了解更多。 +👉 [前往模块](simbot-component-qq-guild-core-1) 了解更多。 ## 法欧莉 diff --git a/build.gradle.kts b/build.gradle.kts index a7724346..fe1b9603 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -52,7 +52,7 @@ allprojects { } // // configurations.all { -// resolutionStrategy.cacheChangingModulesFor(60, "seconds") +// resolutionStrategy.cacheChangingModulesFor(15, TimeUnit.MINUTES) // } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ef3cf930..b863117e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ ktor = "2.3.7" openjdk-jmh = "1.35" log4j = "2.20.0" atomicfu = "0.20.1" -simbot = "4.0.0-dev1-SNAPSHOT" +simbot = "4.0.0-dev1" [libraries] # simbot diff --git a/settings.gradle.kts b/settings.gradle.kts index ea776ffe..3fbd6191 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -21,8 +21,11 @@ rootProject.name = "qq-guild" //include(":builder-generator") include(":simbot-component-qq-guild-api") include(":simbot-component-qq-guild-stdlib") -include(":simbot-component-qq-guild-core-common") +include(":simbot-component-qq-guild-core") //include(":simbot-component-qq-guild-core") //include(":simbot-component-qq-guild-benchmark") +// tests +include(":tests:application-test") +include(":tests:spring-boot-test") diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuildApiException.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuildApiException.kt index 930a2687..fd35f906 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuildApiException.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuildApiException.kt @@ -28,8 +28,8 @@ import kotlinx.serialization.json.JsonNull * 在 JVM 平台上生效。 * 在其他平台会使用 [addSuppressed] 添加 [cause]。 */ -@PublishedApi -internal expect inline fun T.initCause0(cause: Throwable): T +@QGInternalApi +public expect inline fun T.initCause0(cause: Throwable): T /** * QQ频道API请求过程中出现的异常 @@ -71,7 +71,7 @@ public fun QQGuildApiException.copyCurrent(): QQGuildApiException = addStackTrac */ @QGInternalApi public inline fun E.addStackTrace(block: () -> String? = { null }): E { - addSuppressed(APIStackException(block())) + addSuppressed(APIStack(block())) return this } @@ -81,7 +81,7 @@ public inline fun E.addStackTrace(block: () -> String? */ @QGInternalApi @PublishedApi -internal class APIStackException @PublishedApi internal constructor(message: String? = null) : Exception(message) +internal class APIStack @PublishedApi internal constructor(message: String? = null) : Exception(message) /** * 判断 [QQGuildApiException.value] 的值是否为 `404` diff --git a/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/initCause.js.kt b/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/initCause.js.kt index 6c816520..60b13e73 100644 --- a/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/initCause.js.kt +++ b/simbot-component-qq-guild-api/src/jsMain/kotlin/love/forte/simbot/qguild/initCause.js.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -20,5 +20,4 @@ package love.forte.simbot.qguild /** * @suppress */ -@PublishedApi -internal actual inline fun T.initCause0(cause: Throwable): T = this.also { addSuppressed(cause) } +public actual inline fun T.initCause0(cause: Throwable): T = this.also { addSuppressed(cause) } diff --git a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/initCause.jvm.kt b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/initCause.jvm.kt index 603a76f9..b3185474 100644 --- a/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/initCause.jvm.kt +++ b/simbot-component-qq-guild-api/src/jvmMain/kotlin/love/forte/simbot/qguild/initCause.jvm.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -20,7 +20,6 @@ package love.forte.simbot.qguild /** * @suppress */ -@PublishedApi -internal actual inline fun T.initCause0(cause: Throwable): T = apply { +public actual inline fun T.initCause0(cause: Throwable): T = apply { initCause(cause) } diff --git a/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/initCause.native.kt b/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/initCause.native.kt index 4ad4c953..60b13e73 100644 --- a/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/initCause.native.kt +++ b/simbot-component-qq-guild-api/src/nativeMain/kotlin/love/forte/simbot/qguild/initCause.native.kt @@ -20,5 +20,4 @@ package love.forte.simbot.qguild /** * @suppress */ -@PublishedApi -internal actual inline fun T.initCause0(cause: Throwable): T = this.also { addSuppressed(cause) } +public actual inline fun T.initCause0(cause: Throwable): T = this.also { addSuppressed(cause) } diff --git a/simbot-component-qq-guild-core-1/Module.md b/simbot-component-qq-guild-core-1/Module.md new file mode 100644 index 00000000..fd864592 --- /dev/null +++ b/simbot-component-qq-guild-core-1/Module.md @@ -0,0 +1,4 @@ +# Module simbot-component-qq-guild-core + +基于core模块与 [simbot3核心库](https://github.com/simple-robot/simpler-robot/tree/v3-main) +对 [QQ频道API](https://bot.q.qq.com/wiki/develop/api/) 功能的完整性实现。 diff --git a/simbot-component-qq-guild-core-1/README.md b/simbot-component-qq-guild-core-1/README.md new file mode 100644 index 00000000..7565260f --- /dev/null +++ b/simbot-component-qq-guild-core-1/README.md @@ -0,0 +1,234 @@ +# core组件模块 + +> **Note** +> 请先阅读 [**模块说明**](Module.md) . + +此模块是此仓库作为simbot组件库的主要 **"证据"** ,也是实际意义上的组件模块。 + +core模块尽可能大限度的以QQ频道的角度去实现simbot核心库中的API,是一个**较高封装程度**的模块。同样的,使用此模块可以使用绝大多数的simbot特性, +包括它的API风格以及Spring Boot Starter等特性。 + +有关simbot3的内容,可以前往其[**官方网站**](https://simbot.forte.love)或[**仓库首页**](https://github.com/simple-robot/simpler-robot/tree/v3-main)了解更多。 + + +## 使用 + +> 注:core模块中的 `ktor-client` 引擎默认使用 `ktor-client-cio-jvm`。如有需要可自行排除替换,例如在 `Java 11+` 的环境下替换使用 `ktor-client-java`。 + +**Gradle Kotlin DSL** + +```kotlin +implementation("love.forte.simbot:simbot-core:$SIMBOT_VERSION") // 必须显式指定simbot相关依赖比如此核心库或spring-boot-starter +implementation("love.forte.simbot.component:simbot-component-qq-guild-core:$VERSION") +``` + +**Gradle Groovy** + +```groovy +implementation 'love.forte.simbot:simbot-core:$SIMBOT_VERSION' // 必须显式指定simbot相关依赖,如此核心库或spring-boot-starter +implementation 'love.forte.simbot.component:simbot-component-qq-guild-core:$VERSION' +``` + +**Maven** + +```xml + + + love.forte.simbot + simbot-core + ${SIMBOT_VERSION} + + + love.forte.simbot.component + simbot-component-qq-guild-core + ${VERSION} + +``` + + +## 命名说明 + +在QQ频道组件中,所有相关的类型(包括但不限于事件、频道、子频道等相关对象)均会以 `QG` 为前缀命名 (例如 `QGGuild` 、`QGBot`),代表 `QQ-Guild`。 +其中除了 `Component` 的实现 `QQGuildComponent`。组件的实现使用全名 `QQGuild` 作为前缀。 + + +## 事件支持 + +目前,组件支持的事件有: + +| 事件 | 描述 | +|:----------------------------------|:----------------| +| `QGGuildEvent` | 频道服务器相关事件 | +| -> `QGGuildCreateEvent` | 频道服务器进入 | +| -> `QGGuildUpdateEvent` | 频道服务器信息更新 | +| -> `QGGuildDeleteEvent` | 频道服务器离开 | +| `QGChannelEvent` | 子频道相关事件 | +| -> `QGChannelCreateEvent` | 子频道新增 | +| -> `QGChannelUpdateEvent` | 子频道信息变更 | +| -> `QGChannelDeleteEvent` | 子频道删除 | +| -> `QGChannelCategoryCreateEvent` | 子频道分类新增 | +| -> `QGChannelCategoryUpdateEvent` | 子频道分类信息变更 | +| -> `QGChannelCategoryDeleteEvent` | 子频道分类删除 | +| `QGMemberEvent` | 成员相关事件 | +| -> `QGMemberAddEvent` | 新增频道成员 | +| -> `QGMemberUpdateEvent` | 频道成员信息更新 | +| -> `QGMemberRemoveEvent` | 频道成员离开/移除 | +| `QGMessageEvent` | 消息事件 | +| -> `QGAtMessageCreateEvent` | At消息(公域消息)事件 | + +以及一个用于兜底儿的 `QGUnsupportedEvent` 类型事件。 + +## 示例 + +### 直接使用 + +```kotlin +suspend fun main() { + val app = createSimpleApplication { + useQQGuild() + } + + // 注册事件 + app.eventListenerManager.listeners { + // 监听事件: 频道成员信息变更事件 + QGMemberUpdateEvent { event -> + println("UPDATE: $event") + println("member: ${event.member()}") + println("operator: ${event.operator()}") + println("guild: ${event.guild()}") + } + + // 公域消息事件 + // 也可以使用 ChannelMessageEvent -> 这是来自 simbot 标准API的事件类型,也是 QGAtMessageCreateEvent 的父类 + QGAtMessageCreateEvent { event -> + event.reply("纯文本消息,<会自动转义>") + event.reply(QGContentText("content文本消息,不会转义,会引发内嵌格式 <#123456>")) + + // 使用消息链 + event.reply(Face(1.ID) + "纯文本".toText() + QGContentText("content文本") + AtAll) + + // 也可以使用 send 发送消息 + event.channel().send("纯文本") + } + + } + + // 注册qq频道bot + app.qqGuildBots { + val bot = register( + "APP ID", + "SECRET", + "TOKEN" + ) { + // 标准库里的bot配置 + botConfig { + // 其他配置,例如使用沙箱环境 + useSandboxServerUrl() + } + } + + // 启动bot + bot.start() + + // 直接操作bot获取一些信息 + // 获取bot的频道服务器列表 + val list = bot.guilds.toList() + println(list) + println(list.size) + + // 找到一个频道(这里我找的是沙箱的灰度频道) + val home = list.first { "法欧莉" in it.name } + + // 获取所有的子频道 + home.channels.collect { + println("Channel: $it") + // 得到这个频道的所属分组 + val channelCategory = it.category.resolve() + } + + // 获取所有的分类频道 + home.categories.collect { + println("Category: $it") + } + + // 这个频道的一些其他信息 + println("Owner: ${home.owner()}") + println("Permissions: ${home.permissions()}") + + // ... + + } + + + app.join() + +} +``` + +
Java + +```java +SimpleApplication application = Applications.createSimbotApplication(Simple.INSTANCE, (c) -> {}, (builder, configuration) -> { + builder.install(QQGuildComponent.Factory, ($1, $2) -> Unit.INSTANCE); + builder.install(QGBotManager.Factory, ($1, $2) -> Unit.INSTANCE); +}); + +// 监听一个事件 +application.getEventListenerManager().register(SimpleListeners.listener(QGMemberUpdateEvent.Key, (context, event) -> { + System.out.println("event: " + event); + System.out.println("event.raw: " + event.getEventRaw()); + System.out.println("event.operator: " + event.getOperator()); + System.out.println("event.guild: " + event.getGuild()); +})); + +// 寻找并注册一个QQ频道Bot +for (BotManager botManager : application.getBotManagers()) { + if (botManager instanceof QGBotManager) { + QGBot bot = ((QGBotManager) botManager).register("101986850", "972f64f7c426096f9344b74ba85102fb", "g57N4WsHHRIx1udptqy7GBAEVsfLgynq", (config) -> { + config.botConfig((bc) -> { + bc.useSandboxServerUrl(); + return Unit.INSTANCE; + }); + + return Unit.INSTANCE; + }); + + bot.startBlocking(); + QGGuild forliy = null; + List guilds = bot.getGuilds().collectToList(); + for (QGGuild guild : guilds) { + System.out.println("Guild: " + guild); + if (guild.getName().contains("法欧莉")) { + forliy = guild; + } + } + assert forliy != null; + + System.out.println("================"); + + forliy.getChannels().collect((channel) -> { + System.out.println("Channel: " + channel); + //System.out.println("Channel.category: " + channel.getCategory().resolveBlocking()); + }); + + forliy.getCategories().collect((category) -> { + System.out.println("Category: " + category); + }); + + break; + } + + +} + +application.joinBlocking(); +``` + +
+ +### Spring Boot + +如果希望使用 Spring Boot,参考 https://simbot.forte.love/docs/quick-start/spring-boot-starter 中的说明。 +QQ频道组件实现了 `simbot` 约定的 `service SPI`,支持自动加载。 + +## 注意事项 diff --git a/simbot-component-qq-guild-core-1/build.gradle.kts b/simbot-component-qq-guild-core-1/build.gradle.kts new file mode 100644 index 00000000..a34d1c75 --- /dev/null +++ b/simbot-component-qq-guild-core-1/build.gradle.kts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +plugins { + `simbot-tcg-suspend-transform-configure` + id("simbot-tencent-guild.module-conventions") + id("simbot-tencent-guild.maven-publish") + kotlin("plugin.serialization") + `qq-guild-dokka-partial-configure` +} + +kotlin { + sourceSets.configureEach { + languageSettings { + optIn("love.forte.simbot.InternalSimbotApi") + optIn("love.forte.simbot.qguild.QGInternalApi") + } + } +} + +dependencies { + api(project(":simbot-component-qq-guild-core-common")) + compileOnly(simbotCore) + + compileOnly(libs.kotlinx.serialization.properties) + compileOnly(libs.charleskorn.kaml) + + testImplementation(simbotCore) + testImplementation(libs.charleskorn.kaml) + testImplementation(simbotLoggerSlf4jImpl) + testImplementation("love.forte.simbot:simbot-logger-slf4j-impl:3.0.0-RC.3") + +} + diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildBotManager.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildBotManager.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildBotManager.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildBotManager.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildBotManagerUsage.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildBotManagerUsage.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildBotManagerUsage.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildBotManagerUsage.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/InstantUtil.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/InstantUtil.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/InstantUtil.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/InstantUtil.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGBotImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGBotImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGBotImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGBotImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGChannelCategoryImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGChannelCategoryImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGChannelCategoryImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGChannelCategoryImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGEventProcess.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGEventProcess.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGEventProcess.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGEventProcess.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGGuildBotImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGGuildBotImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGGuildBotImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGGuildBotImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGGuildImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGGuildImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGGuildImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGGuildImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGMemberImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGMemberImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGMemberImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGMemberImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGNonTextChannelImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGNonTextChannelImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGNonTextChannelImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGNonTextChannelImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGTextChannelImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGTextChannelImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGTextChannelImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QGTextChannelImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QQGuildBotManagerImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QQGuildBotManagerImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/QQGuildBotManagerImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/QQGuildBotManagerImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGAtMessageCreateEventImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGAtMessageCreateEventImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGAtMessageCreateEventImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGAtMessageCreateEventImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGChannelEventImpls.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGChannelEventImpls.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGChannelEventImpls.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGChannelEventImpls.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGForumEventImpls.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGForumEventImpls.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGForumEventImpls.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGForumEventImpls.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGGuildEventImpls.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGGuildEventImpls.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGGuildEventImpls.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGGuildEventImpls.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGInternalBotEventImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGInternalBotEventImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGInternalBotEventImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGInternalBotEventImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGMemberEventImpls.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGMemberEventImpls.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGMemberEventImpls.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGMemberEventImpls.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGOpenForumEventImpls.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGOpenForumEventImpls.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGOpenForumEventImpls.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGOpenForumEventImpls.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGUnsupportedEventImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGUnsupportedEventImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGUnsupportedEventImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/event/QGUnsupportedEventImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumChannelImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumChannelImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumChannelImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumChannelImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGPostImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGPostImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGPostImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGPostImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGReplyImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGReplyImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGReplyImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGReplyImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadCreatorImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadCreatorImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadCreatorImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadCreatorImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/message/QGReceiveMessageContentImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/message/QGReceiveMessageContentImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/message/QGReceiveMessageContentImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/message/QGReceiveMessageContentImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/message/QGSingleMessageReceiptImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/message/QGSingleMessageReceiptImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/message/QGSingleMessageReceiptImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/message/QGSingleMessageReceiptImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/BaseQGRole.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/BaseQGRole.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/BaseQGRole.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/BaseQGRole.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleCreatorImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleCreatorImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleCreatorImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleCreatorImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleUpdaterImpl.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleUpdaterImpl.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleUpdaterImpl.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleUpdaterImpl.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/utils/TimestampDelegate.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/utils/TimestampDelegate.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/internal/utils/TimestampDelegate.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/internal/utils/TimestampDelegate.kt diff --git a/simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/message/MessageSender.kt b/simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/message/MessageSender.kt similarity index 100% rename from simbot-component-qq-guild-core/src/main/kotlin/love/forte/simbot/component/qguild/message/MessageSender.kt rename to simbot-component-qq-guild-core-1/src/main/kotlin/love/forte/simbot/component/qguild/message/MessageSender.kt diff --git a/simbot-component-qq-guild-core/src/main/resources/META-INF/services/love.forte.simbot.application.EventProviderAutoRegistrarFactory b/simbot-component-qq-guild-core-1/src/main/resources/META-INF/services/love.forte.simbot.application.EventProviderAutoRegistrarFactory similarity index 100% rename from simbot-component-qq-guild-core/src/main/resources/META-INF/services/love.forte.simbot.application.EventProviderAutoRegistrarFactory rename to simbot-component-qq-guild-core-1/src/main/resources/META-INF/services/love.forte.simbot.application.EventProviderAutoRegistrarFactory diff --git a/simbot-component-qq-guild-core/src/test/resources/simbot-logger-slf4j.properties b/simbot-component-qq-guild-core-1/src/test/resources/simbot-logger-slf4j.properties similarity index 100% rename from simbot-component-qq-guild-core/src/test/resources/simbot-logger-slf4j.properties rename to simbot-component-qq-guild-core-1/src/test/resources/simbot-logger-slf4j.properties diff --git a/simbot-component-qq-guild-core-common/Module.md b/simbot-component-qq-guild-core-common/Module.md deleted file mode 100644 index 33b24015..00000000 --- a/simbot-component-qq-guild-core-common/Module.md +++ /dev/null @@ -1,4 +0,0 @@ -# Module simbot-component-qq-guild-core-common - -基于 `stdlib` 和 `api` 模块,使用[simbot3核心库](https://github.com/simple-robot/simpler-robot/tree/v3-main) 对 [QQ频道API](https://bot.q.qq.com/wiki/develop/api/) 功能的完整性实现。 -这是此仓库作为simbot组件库的主要 **"证据"** ,也是实际意义上的组件模块。 diff --git a/simbot-component-qq-guild-core-common/README.md b/simbot-component-qq-guild-core-common/README.md deleted file mode 100644 index 244385cb..00000000 --- a/simbot-component-qq-guild-core-common/README.md +++ /dev/null @@ -1,101 +0,0 @@ -# core-common - -> **Note** -> 请参考 [simbot-component-qq-guild-core](../simbot-component-qq-guild-core) 模块 - -`core-common` 是 `core` 模块中绝大部分的公开定义类型,此模块被 `core` 模块依赖,不能直接使用。如果需要使用,更多应用参考 [`simbot-component-tencent-guild-core` 模块](../simbot-component-qq-guild-core) 。 - -有关simbot3的内容,可以前往其[**官方网站**](https://simbot.forte.love)或[**仓库首页**](https://github.com/simple-robot/simpler-robot/tree/v3-main)了解更多。 - -## 使用 - -
Gradle Kotlin DSL - -```kotlin -implementation("love.forte.simbot:simbot-core:$SIMBOT_VERSION") // 必须显式指定simbot相关依赖比如此核心库或spring-boot-starter -implementation("love.forte.simbot.component:simbot-component-qq-guild-core:$VERSION") -``` - -
- - -
Gradle Groovy - -```groovy -implementation 'love.forte.simbot:simbot-core:$SIMBOT_VERSION' // 必须显式指定simbot相关依赖,如此核心库或spring-boot-starter -implementation 'love.forte.simbot.component:simbot-component-qq-guild-core:$VERSION' -``` - -
- -
Maven - -```xml - - - love.forte.simbot - simbot-core - ${SIMBOT_VERSION} - - - love.forte.simbot.component - simbot-component-qq-guild-core - ${VERSION} - -``` - -
- -或者你可以在借助 `installAll` 的情况下,将 `core` 模块隐藏起来,仅使用 `core-common`。这样可以避免被一些内部类型打扰。 - -
Gradle Kotlin DSL - -```kotlin -implementation("love.forte.simbot:simbot-core:$SIMBOT_VERSION") // 必须显式指定simbot相关依赖比如此核心库或spring-boot-starter -implementation("love.forte.simbot.component:simbot-component-qq-guild-core-common:$VERSION") -runtimeOnly("love.forte.simbot.component:simbot-component-qq-guild-core:$VERSION") // 仅运行时 -``` - -
- - -
Gradle Groovy - -```groovy -implementation 'love.forte.simbot:simbot-core:$SIMBOT_VERSION' // 必须显式指定simbot相关依赖,如此核心库或spring-boot-starter -implementation 'love.forte.simbot.component:simbot-component-qq-guild-core-common:$VERSION' -runtimeOnly 'love.forte.simbot.component:simbot-component-qq-guild-core:$VERSION' // 仅运行时 -``` - -
- -
Maven - -```xml - - - love.forte.simbot - simbot-core - ${SIMBOT_VERSION} - - - love.forte.simbot.component - simbot-component-qq-guild-core-common - ${VERSION} - - - love.forte.simbot.component - simbot-component-qq-guild-core - ${VERSION} - runtime - -``` - -
- -此时,如果希望从 Application 中主动寻找 `BotManager`, 您应当使用 `BaseQGBotManager` 。 - -## 命名说明 - -在QQ频道组件中,所有相关的类型(包括但不限于事件、频道、子频道等相关对象)均会以 `QG` 为前缀命名 (例如 `QGGuild` 、`QGBot`),代表 `QQ-Guild`。 -其中除了 `Component` 的实现 `QQGuildComponent`。组件的实现使用全名 `QQGuild` 作为前缀。 diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt deleted file mode 100644 index 759b80fc..00000000 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2023-2024. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.component.qguild.internal.forum - -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.map -import love.forte.simbot.common.id.ID -import love.forte.simbot.component.qguild.QGBot -import love.forte.simbot.component.qguild.QGGuild -import love.forte.simbot.component.qguild.bot.QGBot -import love.forte.simbot.component.qguild.channel.QGForumChannel -import love.forte.simbot.component.qguild.forum.QGForums -import love.forte.simbot.component.qguild.guild.QGGuild -import love.forte.simbot.component.qguild.internal.bot.newSupervisorCoroutineContext -import love.forte.simbot.component.qguild.internal.channel.QGForumChannelImpl -import love.forte.simbot.component.qguild.internal.channel.asForumChannel -import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl -import love.forte.simbot.component.qguild.internal.newSupervisorCoroutineContext -import love.forte.simbot.qguild.model.ChannelType -import love.forte.simbot.utils.item.Items -import love.forte.simbot.utils.item.effectedItemsByFlow -import kotlin.coroutines.CoroutineContext - - -/** - * - * @author ForteScarlet - */ -internal class QGForumsImpl( - private val sourceGuild: QGGuildImpl, -) : QGForums { - override val coroutineContext: CoroutineContext = sourceGuild.newSupervisorCoroutineContext() - override val bot: QGBot get() = sourceGuild.baseBot - - override suspend fun guild(): QGGuild = sourceGuild - - override val forumChannels: Items - get() = effectedItemsByFlow { - sourceGuild.baseBot.channelFlowWithCategoryId(sourceGuild.source.id, sourceGuild) - .filter { (info, _) -> - info.type == ChannelType.FORUM - }.map { (info, category) -> - QGForumChannelImpl( - bot = sourceGuild.bot, - source = info, - sourceGuild = sourceGuild.baseBot.checkIfTransmitCacheable(sourceGuild), - category = category - ) - } - } - - override suspend fun forumChannel(id: ID): QGForumChannel? { - val channel = sourceGuild.channel(id) ?: return null - - return channel.asForumChannel(channel.source) - } -} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt b/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt deleted file mode 100644 index 82e7d079..00000000 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2023-2024. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -package love.forte.simbot.component.qguild.internal.role - -import love.forte.simbot.ExperimentalSimbotApi -import love.forte.simbot.common.id.ID -import love.forte.simbot.common.id.literal -import love.forte.simbot.component.qguild.guild.QGGuild -import love.forte.simbot.component.qguild.guild.QGMember -import love.forte.simbot.component.qguild.internal.QGBotImpl -import love.forte.simbot.component.qguild.internal.QGMemberImpl -import love.forte.simbot.component.qguild.internal.bot.QGBotImpl -import love.forte.simbot.component.qguild.internal.bot.newSupervisorCoroutineContext -import love.forte.simbot.component.qguild.internal.guild.QGMemberImpl -import love.forte.simbot.component.qguild.internal.newSupervisorCoroutineContext -import love.forte.simbot.component.qguild.role.QGMemberRole -import love.forte.simbot.component.qguild.role.QGRoleUpdater -import love.forte.simbot.component.qguild.util.requestBy -import love.forte.simbot.literal -import love.forte.simbot.qguild.api.member.GetMemberApi -import love.forte.simbot.qguild.api.role.RemoveMemberRoleApi -import love.forte.simbot.qguild.model.Role -import kotlin.coroutines.CoroutineContext - - -/** - * - * @author ForteScarlet - */ -@OptIn(ExperimentalSimbotApi::class) -internal class QGMemberRoleImpl( - override val guildRole: QGGuildRoleImpl, - override val memberId: ID, - private val sourceMember: QGMember?, -) : BaseQGRole(), QGMemberRole { - override val coroutineContext: CoroutineContext = guildRole.bot.newSupervisorCoroutineContext() - - override val id: ID get() = guildRole.id - - override val guildId: ID - get() = guildRole.guildId - - override val bot: QGBotImpl - get() = guildRole.bot - - override var source: Role - get() = guildRole.source - set(value) { - guildRole.source = value - } - - override suspend fun guild(): QGGuild = guildRole.guild() - override fun updater(): QGRoleUpdater = guildRole.updater() - override suspend fun member(): QGMember { - return sourceMember ?: GetMemberApi.create(guildId.literal, memberId.literal).requestBy(bot) - .let { QGMemberImpl(bot, it, guildId) } - } - - override suspend fun delete(): Boolean = delete0(null) - override suspend fun delete(channelId: ID): Boolean = delete0(channelId.literal) - - private suspend fun delete0(channelId: String?): Boolean { - RemoveMemberRoleApi.create(guildId.literal, memberId.literal, id.literal, channelId).requestBy(bot) - return true - } -} diff --git a/simbot-component-qq-guild-core/Module.md b/simbot-component-qq-guild-core/Module.md index fd864592..33b24015 100644 --- a/simbot-component-qq-guild-core/Module.md +++ b/simbot-component-qq-guild-core/Module.md @@ -1,4 +1,4 @@ -# Module simbot-component-qq-guild-core +# Module simbot-component-qq-guild-core-common -基于core模块与 [simbot3核心库](https://github.com/simple-robot/simpler-robot/tree/v3-main) -对 [QQ频道API](https://bot.q.qq.com/wiki/develop/api/) 功能的完整性实现。 +基于 `stdlib` 和 `api` 模块,使用[simbot3核心库](https://github.com/simple-robot/simpler-robot/tree/v3-main) 对 [QQ频道API](https://bot.q.qq.com/wiki/develop/api/) 功能的完整性实现。 +这是此仓库作为simbot组件库的主要 **"证据"** ,也是实际意义上的组件模块。 diff --git a/simbot-component-qq-guild-core/README.md b/simbot-component-qq-guild-core/README.md index 7565260f..244385cb 100644 --- a/simbot-component-qq-guild-core/README.md +++ b/simbot-component-qq-guild-core/README.md @@ -1,35 +1,34 @@ -# core组件模块 +# core-common > **Note** -> 请先阅读 [**模块说明**](Module.md) . +> 请参考 [simbot-component-qq-guild-core](../simbot-component-qq-guild-core) 模块 -此模块是此仓库作为simbot组件库的主要 **"证据"** ,也是实际意义上的组件模块。 - -core模块尽可能大限度的以QQ频道的角度去实现simbot核心库中的API,是一个**较高封装程度**的模块。同样的,使用此模块可以使用绝大多数的simbot特性, -包括它的API风格以及Spring Boot Starter等特性。 +`core-common` 是 `core` 模块中绝大部分的公开定义类型,此模块被 `core` 模块依赖,不能直接使用。如果需要使用,更多应用参考 [`simbot-component-tencent-guild-core` 模块](../simbot-component-qq-guild-core) 。 有关simbot3的内容,可以前往其[**官方网站**](https://simbot.forte.love)或[**仓库首页**](https://github.com/simple-robot/simpler-robot/tree/v3-main)了解更多。 - ## 使用 -> 注:core模块中的 `ktor-client` 引擎默认使用 `ktor-client-cio-jvm`。如有需要可自行排除替换,例如在 `Java 11+` 的环境下替换使用 `ktor-client-java`。 - -**Gradle Kotlin DSL** +
Gradle Kotlin DSL ```kotlin implementation("love.forte.simbot:simbot-core:$SIMBOT_VERSION") // 必须显式指定simbot相关依赖比如此核心库或spring-boot-starter implementation("love.forte.simbot.component:simbot-component-qq-guild-core:$VERSION") ``` -**Gradle Groovy** +
+ + +
Gradle Groovy ```groovy implementation 'love.forte.simbot:simbot-core:$SIMBOT_VERSION' // 必须显式指定simbot相关依赖,如此核心库或spring-boot-starter implementation 'love.forte.simbot.component:simbot-component-qq-guild-core:$VERSION' ``` -**Maven** +
+ +
Maven ```xml @@ -45,190 +44,58 @@ implementation 'love.forte.simbot.component:simbot-component-qq-guild-core:$VERS ``` +
-## 命名说明 - -在QQ频道组件中,所有相关的类型(包括但不限于事件、频道、子频道等相关对象)均会以 `QG` 为前缀命名 (例如 `QGGuild` 、`QGBot`),代表 `QQ-Guild`。 -其中除了 `Component` 的实现 `QQGuildComponent`。组件的实现使用全名 `QQGuild` 作为前缀。 +或者你可以在借助 `installAll` 的情况下,将 `core` 模块隐藏起来,仅使用 `core-common`。这样可以避免被一些内部类型打扰。 +
Gradle Kotlin DSL -## 事件支持 +```kotlin +implementation("love.forte.simbot:simbot-core:$SIMBOT_VERSION") // 必须显式指定simbot相关依赖比如此核心库或spring-boot-starter +implementation("love.forte.simbot.component:simbot-component-qq-guild-core-common:$VERSION") +runtimeOnly("love.forte.simbot.component:simbot-component-qq-guild-core:$VERSION") // 仅运行时 +``` -目前,组件支持的事件有: +
-| 事件 | 描述 | -|:----------------------------------|:----------------| -| `QGGuildEvent` | 频道服务器相关事件 | -| -> `QGGuildCreateEvent` | 频道服务器进入 | -| -> `QGGuildUpdateEvent` | 频道服务器信息更新 | -| -> `QGGuildDeleteEvent` | 频道服务器离开 | -| `QGChannelEvent` | 子频道相关事件 | -| -> `QGChannelCreateEvent` | 子频道新增 | -| -> `QGChannelUpdateEvent` | 子频道信息变更 | -| -> `QGChannelDeleteEvent` | 子频道删除 | -| -> `QGChannelCategoryCreateEvent` | 子频道分类新增 | -| -> `QGChannelCategoryUpdateEvent` | 子频道分类信息变更 | -| -> `QGChannelCategoryDeleteEvent` | 子频道分类删除 | -| `QGMemberEvent` | 成员相关事件 | -| -> `QGMemberAddEvent` | 新增频道成员 | -| -> `QGMemberUpdateEvent` | 频道成员信息更新 | -| -> `QGMemberRemoveEvent` | 频道成员离开/移除 | -| `QGMessageEvent` | 消息事件 | -| -> `QGAtMessageCreateEvent` | At消息(公域消息)事件 | -以及一个用于兜底儿的 `QGUnsupportedEvent` 类型事件。 +
Gradle Groovy -## 示例 +```groovy +implementation 'love.forte.simbot:simbot-core:$SIMBOT_VERSION' // 必须显式指定simbot相关依赖,如此核心库或spring-boot-starter +implementation 'love.forte.simbot.component:simbot-component-qq-guild-core-common:$VERSION' +runtimeOnly 'love.forte.simbot.component:simbot-component-qq-guild-core:$VERSION' // 仅运行时 +``` -### 直接使用 +
-```kotlin -suspend fun main() { - val app = createSimpleApplication { - useQQGuild() - } - - // 注册事件 - app.eventListenerManager.listeners { - // 监听事件: 频道成员信息变更事件 - QGMemberUpdateEvent { event -> - println("UPDATE: $event") - println("member: ${event.member()}") - println("operator: ${event.operator()}") - println("guild: ${event.guild()}") - } - - // 公域消息事件 - // 也可以使用 ChannelMessageEvent -> 这是来自 simbot 标准API的事件类型,也是 QGAtMessageCreateEvent 的父类 - QGAtMessageCreateEvent { event -> - event.reply("纯文本消息,<会自动转义>") - event.reply(QGContentText("content文本消息,不会转义,会引发内嵌格式 <#123456>")) - - // 使用消息链 - event.reply(Face(1.ID) + "纯文本".toText() + QGContentText("content文本") + AtAll) - - // 也可以使用 send 发送消息 - event.channel().send("纯文本") - } - - } - - // 注册qq频道bot - app.qqGuildBots { - val bot = register( - "APP ID", - "SECRET", - "TOKEN" - ) { - // 标准库里的bot配置 - botConfig { - // 其他配置,例如使用沙箱环境 - useSandboxServerUrl() - } - } - - // 启动bot - bot.start() - - // 直接操作bot获取一些信息 - // 获取bot的频道服务器列表 - val list = bot.guilds.toList() - println(list) - println(list.size) - - // 找到一个频道(这里我找的是沙箱的灰度频道) - val home = list.first { "法欧莉" in it.name } - - // 获取所有的子频道 - home.channels.collect { - println("Channel: $it") - // 得到这个频道的所属分组 - val channelCategory = it.category.resolve() - } - - // 获取所有的分类频道 - home.categories.collect { - println("Category: $it") - } - - // 这个频道的一些其他信息 - println("Owner: ${home.owner()}") - println("Permissions: ${home.permissions()}") - - // ... - - } - - - app.join() - -} -``` +
Maven -
Java - -```java -SimpleApplication application = Applications.createSimbotApplication(Simple.INSTANCE, (c) -> {}, (builder, configuration) -> { - builder.install(QQGuildComponent.Factory, ($1, $2) -> Unit.INSTANCE); - builder.install(QGBotManager.Factory, ($1, $2) -> Unit.INSTANCE); -}); - -// 监听一个事件 -application.getEventListenerManager().register(SimpleListeners.listener(QGMemberUpdateEvent.Key, (context, event) -> { - System.out.println("event: " + event); - System.out.println("event.raw: " + event.getEventRaw()); - System.out.println("event.operator: " + event.getOperator()); - System.out.println("event.guild: " + event.getGuild()); -})); - -// 寻找并注册一个QQ频道Bot -for (BotManager botManager : application.getBotManagers()) { - if (botManager instanceof QGBotManager) { - QGBot bot = ((QGBotManager) botManager).register("101986850", "972f64f7c426096f9344b74ba85102fb", "g57N4WsHHRIx1udptqy7GBAEVsfLgynq", (config) -> { - config.botConfig((bc) -> { - bc.useSandboxServerUrl(); - return Unit.INSTANCE; - }); - - return Unit.INSTANCE; - }); - - bot.startBlocking(); - QGGuild forliy = null; - List guilds = bot.getGuilds().collectToList(); - for (QGGuild guild : guilds) { - System.out.println("Guild: " + guild); - if (guild.getName().contains("法欧莉")) { - forliy = guild; - } - } - assert forliy != null; - - System.out.println("================"); - - forliy.getChannels().collect((channel) -> { - System.out.println("Channel: " + channel); - //System.out.println("Channel.category: " + channel.getCategory().resolveBlocking()); - }); - - forliy.getCategories().collect((category) -> { - System.out.println("Category: " + category); - }); - - break; - } - - -} - -application.joinBlocking(); +```xml + + + love.forte.simbot + simbot-core + ${SIMBOT_VERSION} + + + love.forte.simbot.component + simbot-component-qq-guild-core-common + ${VERSION} + + + love.forte.simbot.component + simbot-component-qq-guild-core + ${VERSION} + runtime + ```
-### Spring Boot +此时,如果希望从 Application 中主动寻找 `BotManager`, 您应当使用 `BaseQGBotManager` 。 -如果希望使用 Spring Boot,参考 https://simbot.forte.love/docs/quick-start/spring-boot-starter 中的说明。 -QQ频道组件实现了 `simbot` 约定的 `service SPI`,支持自动加载。 +## 命名说明 -## 注意事项 +在QQ频道组件中,所有相关的类型(包括但不限于事件、频道、子频道等相关对象)均会以 `QG` 为前缀命名 (例如 `QGGuild` 、`QGBot`),代表 `QQ-Guild`。 +其中除了 `Component` 的实现 `QQGuildComponent`。组件的实现使用全名 `QQGuild` 作为前缀。 diff --git a/simbot-component-qq-guild-core/build.gradle.kts b/simbot-component-qq-guild-core/build.gradle.kts index a34d1c75..c173301e 100644 --- a/simbot-component-qq-guild-core/build.gradle.kts +++ b/simbot-component-qq-guild-core/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -15,34 +15,77 @@ * If not, see . */ +import love.forte.gradle.common.kotlin.multiplatform.applyTier1 +import love.forte.gradle.common.kotlin.multiplatform.applyTier2 +import love.forte.gradle.common.kotlin.multiplatform.applyTier3 + plugins { - `simbot-tcg-suspend-transform-configure` - id("simbot-tencent-guild.module-conventions") - id("simbot-tencent-guild.maven-publish") + kotlin("multiplatform") kotlin("plugin.serialization") `qq-guild-dokka-partial-configure` + `simbot-tcg-suspend-transform-configure` } +configJavaCompileWithModule("simbot.component.qqguild.core") +apply(plugin = "qq-guild-multiplatform-maven-publish") + +configJsTestTasks() + kotlin { + explicitApi() + applyDefaultHierarchyTemplate() + sourceSets.configureEach { languageSettings { - optIn("love.forte.simbot.InternalSimbotApi") optIn("love.forte.simbot.qguild.QGInternalApi") } } -} -dependencies { - api(project(":simbot-component-qq-guild-core-common")) - compileOnly(simbotCore) + configKotlinJvm() - compileOnly(libs.kotlinx.serialization.properties) - compileOnly(libs.charleskorn.kaml) + js(IR) { + configJs() + } - testImplementation(simbotCore) - testImplementation(libs.charleskorn.kaml) - testImplementation(simbotLoggerSlf4jImpl) - testImplementation("love.forte.simbot:simbot-logger-slf4j-impl:3.0.0-RC.3") + applyTier1() + applyTier2() + applyTier3(supportKtorClient = true) -} + sourceSets { + commonMain.dependencies { + api(libs.simbot.api) + api(project(":simbot-component-qq-guild-stdlib")) + compileOnly(libs.simbot.common.annotations) + // ktor + api(libs.ktor.client.contentNegotiation) + api(libs.ktor.serialization.kotlinxJson) + api(libs.ktor.client.ws) + // datetime + api(libs.kotlinx.datetime) + } + commonTest.dependencies { + implementation(kotlin("test")) + implementation(libs.kotlinx.coroutines.test) + } + + jvmTest.dependencies { + runtimeOnly(libs.ktor.client.cio) + implementation(libs.log4j.api) + implementation(libs.log4j.core) + implementation(libs.log4j.slf4j2) + } + + jsMain.dependencies { + api(libs.simbot.common.annotations) + } + + nativeMain.dependencies { + api(libs.simbot.common.annotations) + } + + mingwTest.dependencies { + implementation(libs.ktor.client.winhttp) + } + } +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QGObjectiveContainer.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QGObjectiveContainer.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QGObjectiveContainer.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QGObjectiveContainer.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildBotManagerUsage.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildBotManagerUsage.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildBotManagerUsage.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildBotManagerUsage.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildInitException.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildInitException.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildInitException.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildInitException.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/annotations.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/annotations.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/annotations.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/annotations.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QGBot.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QGBot.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QGBot.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QGBot.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/CacheStrategyConfig.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/CacheStrategyConfig.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/CacheStrategyConfig.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/CacheStrategyConfig.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/IntentsConfig.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/IntentsConfig.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/IntentsConfig.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/IntentsConfig.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotComponentConfiguration.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotComponentConfiguration.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotComponentConfiguration.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotComponentConfiguration.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotFileConfiguration.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotFileConfiguration.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotFileConfiguration.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotFileConfiguration.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/ShardConfig.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/ShardConfig.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/ShardConfig.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/ShardConfig.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGCategory.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGCategory.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGCategory.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGCategory.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGChannel.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGChannel.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGChannel.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGChannel.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGForumChannel.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGForumChannel.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGForumChannel.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGForumChannel.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGTextChannel.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGTextChannel.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGTextChannel.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGTextChannel.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGEvent.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGEvent.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGEvent.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGEvent.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt similarity index 94% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt index 8537bb79..6edd8a0d 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt @@ -17,8 +17,11 @@ package love.forte.simbot.component.qguild.event +import love.forte.simbot.annotations.ExperimentalSimbotAPI import love.forte.simbot.common.id.ID import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.common.id.UUID +import love.forte.simbot.common.time.Timestamp import love.forte.simbot.component.qguild.channel.QGForumChannel import love.forte.simbot.component.qguild.forum.QGPost import love.forte.simbot.component.qguild.forum.QGReply @@ -45,6 +48,14 @@ import love.forte.simbot.suspendrunner.STP @STP @PrivateDomainOnly public abstract class QGForumEvent : QGBotEvent(), ChannelEvent { + /** + * 事件ID。是一个随机ID。 + */ + override val id: ID = UUID.random() + + @OptIn(ExperimentalSimbotAPI::class) + override val time: Timestamp = Timestamp.now() + /** * 频道ID * @@ -76,7 +87,8 @@ public abstract class QGForumEvent : QGBotEvent(), ChannelEvent * @throws NoSuchElementException 对应子频道已不存在 */ override suspend fun guild(): QGGuild = - bot.guildRelation.guild(guildId) ?: throw NoSuchElementException("guild(id=$guildId)") + bot.guildRelation.guild(guildId) + ?: throw NoSuchElementException("guild(id=$guildId)") /** * 得到本次事件 [channelId] 对应的论坛子频道。 diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildEvents.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildEvents.kt similarity index 96% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildEvents.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildEvents.kt index 99b72794..731308ca 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildEvents.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildEvents.kt @@ -18,6 +18,7 @@ package love.forte.simbot.component.qguild.event import love.forte.simbot.annotations.ExperimentalSimbotAPI +import love.forte.simbot.annotations.FragileSimbotAPI import love.forte.simbot.common.id.ID import love.forte.simbot.common.id.StringID.Companion.ID import love.forte.simbot.common.time.Timestamp @@ -100,7 +101,7 @@ public abstract class QGGuildUpdateEvent : QGGuildEvent(), GuildEvent, Organizat * - 机器人被移除 */ @STP -public abstract class QGGuildDeleteEvent : QGGuildEvent(), ChangeEvent { +public abstract class QGGuildDeleteEvent : QGGuildEvent() { @OptIn(ExperimentalSimbotAPI::class) override val time: Timestamp = Timestamp.now() @@ -113,6 +114,6 @@ public abstract class QGGuildDeleteEvent : QGGuildEvent(), ChangeEvent { * * 如果希望得到较为可靠的**频道信息**,使用 [sourceEventEntity]。 */ -// @FragileSimbotApi + @FragileSimbotAPI public abstract val guild: QGGuild? } diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildMemberEvents.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildMemberEvents.kt similarity index 94% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildMemberEvents.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildMemberEvents.kt index 6734233d..188ffbc1 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildMemberEvents.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGGuildMemberEvents.kt @@ -26,7 +26,7 @@ import love.forte.simbot.component.qguild.guild.QGMember import love.forte.simbot.event.GuildMemberDecreaseEvent import love.forte.simbot.event.GuildMemberIncreaseEvent import love.forte.simbot.event.MemberChangeEvent -import love.forte.simbot.event.OrganizationAwareEvent +import love.forte.simbot.event.OrganizationSourceEvent import love.forte.simbot.qguild.event.EventIntents import love.forte.simbot.qguild.event.EventMember import love.forte.simbot.suspendrunner.STP @@ -86,7 +86,7 @@ public abstract class QGMemberAddEvent : QGMemberEvent(), GuildMemberIncreaseEve * 频道成员信息更新事件。 */ @STP -public abstract class QGMemberUpdateEvent : QGMemberEvent(), MemberChangeEvent, OrganizationAwareEvent { +public abstract class QGMemberUpdateEvent : QGMemberEvent(), MemberChangeEvent, OrganizationSourceEvent { @OptIn(ExperimentalSimbotAPI::class) override val time: Timestamp = Timestamp.now() @@ -109,12 +109,12 @@ public abstract class QGMemberUpdateEvent : QGMemberEvent(), MemberChangeEvent, /** * 发生变更的成员的所属频道。 */ - abstract override suspend fun organization(): QGGuild + abstract override suspend fun source(): QGGuild /** - * 同 [organization] + * 同 [source] */ - public suspend fun guild(): QGGuild = organization() + public suspend fun guild(): QGGuild = source() } /** diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGInternalBotEvent.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGInternalBotEvent.kt similarity index 81% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGInternalBotEvent.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGInternalBotEvent.kt index 5ef9f259..d1f23ed1 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGInternalBotEvent.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGInternalBotEvent.kt @@ -17,6 +17,10 @@ package love.forte.simbot.component.qguild.event +import love.forte.simbot.annotations.ExperimentalSimbotAPI +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.UUID +import love.forte.simbot.common.time.Timestamp import love.forte.simbot.component.qguild.bot.QGBot import love.forte.simbot.component.qguild.bot.QQGuildBotManager import love.forte.simbot.event.BotEvent @@ -40,6 +44,10 @@ public sealed interface QGInternalBotEvent : Event */ public abstract class QGBotRegisteredEvent : QGInternalBotEvent, BotEvent { abstract override val bot: QGBot + + @OptIn(ExperimentalSimbotAPI::class) + override val time: Timestamp = Timestamp.now() + override val id: ID = UUID.random() } @@ -52,4 +60,8 @@ public abstract class QGBotRegisteredEvent : QGInternalBotEvent, BotEvent { */ public abstract class QGBotStartedEvent : QGInternalBotEvent, BotEvent { abstract override val bot: QGBot + + @OptIn(ExperimentalSimbotAPI::class) + override val time: Timestamp = Timestamp.now() + override val id: ID = UUID.random() } diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt similarity index 92% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt index af4800c9..4d1e5ea8 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt @@ -17,6 +17,8 @@ package love.forte.simbot.component.qguild.event +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID import love.forte.simbot.common.time.Timestamp import love.forte.simbot.component.qguild.ExperimentalQGApi import love.forte.simbot.component.qguild.channel.QGTextChannel @@ -91,7 +93,7 @@ public sealed class QGMessageEvent : QGBotEvent(), MessageEvent * 新的@了bot的消息被创建,也就是公域的at消息事件。 * * 发送时机 - * * - 用户发送消息,@当前机器人或回复机器人消息时 + * - 用户发送消息,@当前机器人或回复机器人消息时 * * @author ForteScarlet */ @@ -101,6 +103,9 @@ public abstract class QGAtMessageCreateEvent : QGMessageEvent(), ChatChannelMess override val time: Timestamp get() = sourceEventEntity.timestamp.toTimestamp() + override val authorId: ID + get() = sourceEventEntity.author.id.ID + /** * 发送消息的用户 * @@ -119,6 +124,9 @@ public abstract class QGAtMessageCreateEvent : QGMessageEvent(), ChatChannelMess /** * 接收到消息的子频道的所属频道服务器。 + * + * @throws QQGuildApiException 请求失败,例如无权限 + * @throws NoSuchElementException 没有找到结果 */ abstract override suspend fun guild(): QGGuild } diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt similarity index 96% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt index 64977c85..72be92d6 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt @@ -17,8 +17,11 @@ package love.forte.simbot.component.qguild.event +import love.forte.simbot.annotations.ExperimentalSimbotAPI import love.forte.simbot.common.id.ID import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.common.id.UUID +import love.forte.simbot.common.time.Timestamp import love.forte.simbot.component.qguild.channel.QGForumChannel import love.forte.simbot.component.qguild.guild.QGGuild import love.forte.simbot.component.qguild.guild.QGMember @@ -39,6 +42,11 @@ import love.forte.simbot.suspendrunner.STP */ @STP public abstract class QGOpenForumEvent : QGBotEvent(), ChannelEvent { + override val id: ID = UUID.random() + + @OptIn(ExperimentalSimbotAPI::class) + override val time: Timestamp = Timestamp.now() + /** * 频道ID * diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt similarity index 95% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt index c1e0a7bd..6378e591 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt @@ -18,6 +18,7 @@ package love.forte.simbot.component.qguild.event import love.forte.simbot.annotations.ExperimentalSimbotAPI +import love.forte.simbot.annotations.FragileSimbotAPI import love.forte.simbot.common.id.ID import love.forte.simbot.common.id.UUID import love.forte.simbot.common.time.Timestamp @@ -42,7 +43,7 @@ import love.forte.simbot.qguild.event.Signal * * @author ForteScarlet */ -@ExperimentalSimbotAPI +@FragileSimbotAPI public abstract class QGUnsupportedEvent : QGEvent() { /** * 事件ID。一个随机ID。 @@ -52,6 +53,7 @@ public abstract class QGUnsupportedEvent : QGEvent() { /** * 事件构建时间,即此对象被构建的时间 */ + @OptIn(ExperimentalSimbotAPI::class) override val time: Timestamp = Timestamp.now() /** diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGForumInfoContainer.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGForumInfoContainer.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGForumInfoContainer.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGForumInfoContainer.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGPost.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGPost.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGPost.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGPost.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGReply.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGReply.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGReply.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGReply.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGThread.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGThread.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGThread.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGThread.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGThreadCreator.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGThreadCreator.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGThreadCreator.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/forum/QGThreadCreator.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuild.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuild.kt similarity index 82% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuild.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuild.kt index ee64ec65..6372eb08 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuild.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuild.kt @@ -27,6 +27,7 @@ import love.forte.simbot.component.qguild.QGObjectiveContainer import love.forte.simbot.component.qguild.channel.* import love.forte.simbot.component.qguild.role.QGGuildRole import love.forte.simbot.component.qguild.role.QGRoleCreator +import love.forte.simbot.component.qguild.utils.toTimestamp import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.api.apipermission.ApiPermissions import love.forte.simbot.qguild.api.channel.GetGuildChannelListApi @@ -69,8 +70,9 @@ public interface QGGuild : SimbotGuild, CoroutineScope, QGObjectiveContainer + public val forums: Collectable /** * 根据ID寻找匹配的 **帖子类型** [ChannelType.FORUM] 的子频道 @@ -174,8 +175,8 @@ public interface QGGuild : SimbotGuild, CoroutineScope, QGObjectiveContainer + get() = members(batch = GetGuildMemberListApi.MAX_LIMIT) + + /** + * 频道所有成员列表。 + * + * _可以通过 [permissions] 手动检查是否存在 [GetGuildMemberListApi] 的权限。 + * 通常情况下此权限仅限私域类型的BOT。_ + * + * @param batch 内部分页时每批次的查询数量。 + * 默认为 [GetGuildMemberListApi.MAX_LIMIT] + * + * @throws IllegalArgumentException 如果 [batch] <= 0 + * @throws QQGuildApiException 请求失败,例如没有权限 + */ + public fun members(batch: Int): Collectable + + /** + * 将 bot 的信息作为 [QGMember] 查询 + * + * @throws QQGuildApiException 请求失败,例如没有权限 + */ + override suspend fun botAsMember(): QGMember //endregion /** diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuildRelation.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuildRelation.kt similarity index 75% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuildRelation.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuildRelation.kt index eda20393..b754caf2 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuildRelation.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuildRelation.kt @@ -22,6 +22,7 @@ import love.forte.simbot.common.collectable.Collectable import love.forte.simbot.common.id.ID import love.forte.simbot.component.qguild.channel.* import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.qguild.api.user.GetBotGuildListApi import love.forte.simbot.qguild.model.ChannelType import love.forte.simbot.suspendrunner.ST import kotlin.jvm.JvmSynthetic @@ -34,7 +35,35 @@ import kotlin.jvm.JvmSynthetic */ public interface QGGuildRelation : GuildRelation { + /** + * 获取所有的频道服务器列表。 + * 默认批次使用 [GetBotGuildListApi.DEFAULT_LIMIT] + * + * @see guilds + */ override val guilds: Collectable + get() = guilds(lastId = null, batch = GetBotGuildListApi.DEFAULT_LIMIT) + + /** + * 获取所有的频道服务器列表。 + * + * @param batch 内部每一页数据的数据量,需要 > 0, + * 默认为 [GetBotGuildListApi.DEFAULT_LIMIT] + * @throws IllegalArgumentException [batch] 不符合要求(需要大于0) + */ + public fun guilds(batch: Int): Collectable = + guilds(lastId = null, batch = batch) + + /** + * 获取所有的频道服务器列表。 + * + * @param lastId 可手动指定一个“上一页”的最后ID来控制分页的起始页码, + * 默认为 `null` + * @param batch 内部每一页数据的数据量,需要 > 0, + * 默认为 [GetBotGuildListApi.DEFAULT_LIMIT] + * @throws IllegalArgumentException [batch] 不符合要求(需要大于0) + */ + public fun guilds(lastId: ID?, batch: Int): Collectable @ST(blockingBaseName = "getGuild", blockingSuffix = "", asyncBaseName = "getGuild") override suspend fun guild(id: ID): QGGuild? diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGMember.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGMember.kt similarity index 98% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGMember.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGMember.kt index eaf25278..35ff29ac 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGMember.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGMember.kt @@ -22,6 +22,7 @@ import io.ktor.client.request.* import kotlinx.coroutines.CoroutineScope import love.forte.simbot.common.collectable.Collectable import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID import love.forte.simbot.common.time.Timestamp import love.forte.simbot.component.qguild.ExperimentalQGApi import love.forte.simbot.component.qguild.QGObjectiveContainer @@ -47,6 +48,7 @@ public interface QGMember : Member, CoroutineScope, QGObjectiveContainer - get() = queryGuildList().flatMapConcat { it.asFlow() }.map { + override fun guilds(lastId: ID?, batch: Int): Collectable { + require(batch > 0) { "'batch' must be > 0, but $batch" } + return queryGuildList(lastId = lastId?.literal, batch).flatMapConcat { it.asFlow() }.map { qgGuild(this@QGBotImpl, it) }.asCollectable() - // TODO Spec Collectable type? - + } override suspend fun guild(id: ID): QGGuild? = queryGuild(id.literal) override suspend fun guildCount(): Int = GetBotGuildListApi.createFlow { requestDataBy(source) }.count() - override suspend fun channel(channelId: ID): QGChannel? = channel(channelId.literal, null) + override suspend fun channel(channelId: ID): QGChannel? = queryChannel(channelId.literal, null) override suspend fun chatChannel(channelId: ID): QGTextChannel? { val channel = channel(channelId) ?: return null - return channel as? QGTextChannel - ?: throw IllegalStateException("The type of channel(id=${channel.source.id}, name=${channel.source.name}) is not category (${ChannelType.TEXT}), it is ${channel.source.type}") + return channel.castChannel { ChannelType.TEXT } } override suspend fun category(channelId: ID): QGCategoryChannel? { val channel = channel(channelId) ?: return null - return channel as? QGCategoryChannel - ?: throw IllegalStateException("The type of channel(id=${channel.source.id}, name=${channel.source.name}) is not category (${ChannelType.CATEGORY}), it is ${channel.source.type}") + return channel.castChannel { ChannelType.CATEGORY } } override suspend fun forumChannel(id: ID): QGForumChannel? { val channel = channel(id) ?: return null - return channel as? QGForumChannel - ?: throw IllegalStateException("The type of channel(id=${channel.source.id}, name=${channel.source.name}) is not category (${ChannelType.FORUM}), it is ${channel.source.type}") + return channel.castChannel { ChannelType.FORUM } } } - internal suspend fun queryGuild(id: String): QGGuildImpl? { return try { GetGuildApi.create(id).requestDataBy(source) @@ -173,18 +172,20 @@ internal class QGBotImpl( }?.let { guild -> qgGuild(this, guild) } } - private fun queryGuildList(batch: Int = 0): Flow> = flow { - val limit = batch.takeIf { it > 0 } ?: GetBotGuildListApi.DEFAULT_LIMIT - var lastId: String? = null + private fun queryGuildList( + lastId: String? = null, + batch: Int = GetBotGuildListApi.DEFAULT_LIMIT + ): Flow> = flow { + var lastId0: String? = lastId while (true) { - val list = GetBotGuildListApi.create(after = lastId, limit = limit).requestDataBy(source) + val list = GetBotGuildListApi.create(after = lastId0, limit = batch).requestDataBy(source) if (list.isEmpty()) break - lastId = list.last().id + lastId0 = list.last().id emit(list) } } - internal suspend fun querySimpleChannel(id: String): SimpleChannel? { + private suspend fun querySimpleChannel(id: String): SimpleChannel? { return try { GetChannelApi.create(id).requestDataBy(source) } catch (apiEx: QQGuildApiException) { @@ -192,7 +193,7 @@ internal class QGBotImpl( } } - internal fun channelFlow(guildId: String): Flow { + internal fun querySimpleChannelFlow(guildId: String): Flow { return flow { GetGuildChannelListApi.create(guildId) .requestDataBy(source) @@ -203,105 +204,81 @@ internal class QGBotImpl( } - internal suspend fun channel(id: String, sourceGuild: QGGuildImpl?): QGChannel? { + internal suspend fun queryChannel( + id: String, + sourceGuild: QGGuildImpl? = null, + currentMsgId: String? = null + ): QGChannel? { val channelInfo = querySimpleChannel(id) ?: return null return when (channelInfo.type) { - ChannelType.CATEGORY -> QGCategoryChannelImpl( + ChannelType.CATEGORY -> channelInfo.toCategoryChannel( bot = this, - source = channelInfo, sourceGuild = checkIfTransmitCacheable(sourceGuild), ) - ChannelType.TEXT -> QGTextChannelImpl( + ChannelType.TEXT -> channelInfo.toTextChannel( bot = this, - source = channelInfo, sourceGuild = checkIfTransmitCacheable(sourceGuild), + currentMsgId = currentMsgId, ) - ChannelType.FORUM -> QGForumChannelImpl( + ChannelType.FORUM -> channelInfo.toForumChannel( bot = this, - source = channelInfo, sourceGuild = checkIfTransmitCacheable(sourceGuild), ) - else -> QGNonTextChannelImpl( + else -> channelInfo.toTextChannel( bot = this, - source = channelInfo, sourceGuild = checkIfTransmitCacheable(sourceGuild), ) } } - internal data class ChannelInfoWithCategory(val info: SimpleChannel, val category: QGCategory) - - internal fun channelFlowWithCategoryId(guildId: String, sourceGuild: QGGuildImpl?): Flow { + /** + * 通过API实时查询channels列表 + */ + internal fun queryChannels(guildId: String, sourceGuild: QGGuildImpl?): Flow { val categoryMap = concurrentMutableMap() + return querySimpleChannelFlow(guildId) + .map { channel -> + val gid = channel.guildId.ID - return channelFlow(guildId).filter { info -> - val gid = info.guildId.ID - if (info.type.isCategory) { - categoryMap.computeValueIfAbsent(info.id) { - QGCategoryImpl( - bot = this, - guildId = gid, - id = info.id.ID, - sourceGuild = checkIfTransmitCacheable(sourceGuild), - source = QGCategoryChannelImpl( + fun resolveCategory(): QGCategory = + categoryMap.computeValueIfAbsent(channel.parentId) { cid -> + QGCategoryImpl( bot = this, - source = info, - sourceGuild = sourceGuild, - ), + guildId = gid, + id = cid.ID, + sourceGuild = checkIfTransmitCacheable(sourceGuild), + ) + } + + when (channel.type) { + ChannelType.TEXT -> channel.toTextChannel( + bot = this@QGBotImpl, + sourceGuild = checkIfTransmitCacheable(sourceGuild), + category = resolveCategory(), ) - } - false - } else { - true - } - }.map { info -> - val gid = info.guildId.ID - val category = categoryMap.computeValueIfAbsent(info.parentId) { cid -> - QGCategoryImpl( - bot = this, - guildId = gid, - id = cid.ID, - sourceGuild = checkIfTransmitCacheable(sourceGuild), - ) - } + ChannelType.FORUM -> channel.toForumChannel( + bot = this@QGBotImpl, + sourceGuild = checkIfTransmitCacheable(sourceGuild), + category = resolveCategory(), + ) - ChannelInfoWithCategory(info, category) - } - } + ChannelType.CATEGORY -> channel.toCategoryChannel( + bot = this@QGBotImpl, + sourceGuild = checkIfTransmitCacheable(sourceGuild), + ) - /** - * 通过API实时查询channels列表 - */ - internal fun queryChannels(guildId: String, sourceGuild: QGGuildImpl?): Flow = flow { - channelFlowWithCategoryId(guildId, sourceGuild).map { (info, category) -> - when (info.type) { - ChannelType.TEXT -> QGTextChannelImpl( - bot = this@QGBotImpl, - source = info, - sourceGuild = checkIfTransmitCacheable(sourceGuild), - category = category, - ) - - ChannelType.FORUM -> QGForumChannelImpl( - bot = this@QGBotImpl, - source = info, - sourceGuild = checkIfTransmitCacheable(sourceGuild), - category = category, - ) - - else -> QGNonTextChannelImpl( - bot = this@QGBotImpl, - source = info, - sourceGuild = checkIfTransmitCacheable(sourceGuild), - category = category - ) + else -> channel.toNonTextChannel( + bot = this@QGBotImpl, + sourceGuild = checkIfTransmitCacheable(sourceGuild), + category = resolveCategory() + ) + } } - } } override suspend fun sendTo(channelId: ID, text: String): QGMessageReceipt { @@ -328,7 +305,7 @@ internal class QGBotImpl( } } - internal suspend fun member(guildId: String, userId: String, sourceGuild: QGGuildImpl?): QGMemberImpl? { + internal suspend fun queryMember(guildId: String, userId: String, sourceGuild: QGGuildImpl? = null): QGMemberImpl? { val member = try { GetMemberApi.create(guildId, userId).requestDataBy(source) } catch (apiEx: QQGuildApiException) { @@ -345,6 +322,23 @@ internal class QGBotImpl( } } + internal fun queryGuildRoles(guildId: String, sourceGuild: QGGuildImpl? = null): Flow = flow { + GetGuildRoleListApi.create(guildId).requestDataBy(source).roles.forEach { + val role = it.toGuildRole(bot = this@QGBotImpl, guildId = guildId.ID, sourceGuild = sourceGuild) + emit(role) + } + } + + internal fun queryMemberRoles( + guildId: String, + roleIds: Set, + memberId: ID, + sourceGuild: QGGuildImpl? = null + ): Flow = + queryGuildRoles(guildId, sourceGuild = sourceGuild) + .filter { it.source.id in roleIds } + .map { it.toMemberRole(bot = this@QGBotImpl, memberId = memberId) } + private val startLock = Mutex() override suspend fun me(withCache: Boolean): QGUser { @@ -415,3 +409,9 @@ internal class QGBotImpl( */ internal fun CoroutineScope.newSupervisorCoroutineContext(): CoroutineContext = coroutineContext + SupervisorJob(coroutineContext[Job]) + + +internal inline fun QGChannel.castChannel(target: () -> ChannelType): T { + return this as? T + ?: throw IllegalStateException("The type of channel(id=${source.id}, name=${source.name}) is not ${target()}, it is ${source.type}") +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGChannelCategoryImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGChannelCategoryImpl.kt similarity index 87% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGChannelCategoryImpl.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGChannelCategoryImpl.kt index 5600a32c..4f34cf6b 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGChannelCategoryImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGChannelCategoryImpl.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import love.forte.simbot.common.id.ID import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.common.id.literal import love.forte.simbot.component.qguild.channel.QGCategory import love.forte.simbot.component.qguild.channel.QGCategoryChannel import love.forte.simbot.component.qguild.guild.QGGuild @@ -61,7 +62,7 @@ internal class QGCategoryImpl( private suspend fun guild(): QGGuild = sourceGuild - ?: bot.guildRelation.guild(guildId) + ?: bot.queryGuild(guildId.literal) ?: throw NoSuchElementException("guild(id=$guildId)") override fun toString(): String { @@ -77,4 +78,16 @@ internal class QGCategoryChannelImpl( override val coroutineContext: CoroutineContext = bot.newSupervisorCoroutineContext() override val category: QGCategory get() = QGCategoryImpl(bot, source.guildId.ID, id, sourceGuild, this) + + override fun toString(): String = + "QGCategoryChannel(id=${source.id}, name=${source.name}, guildId=${source.guildId})" } + +internal fun Channel.toCategoryChannel( + bot: QGBotImpl, + sourceGuild: QGGuild? = null +): QGCategoryChannelImpl = QGCategoryChannelImpl( + bot = bot, + source = this, + sourceGuild = sourceGuild, +) diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGForumChannelImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGForumChannelImpl.kt similarity index 89% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGForumChannelImpl.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGForumChannelImpl.kt index da1376cf..2dc5d344 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGForumChannelImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGForumChannelImpl.kt @@ -54,22 +54,16 @@ internal class QGForumChannelImpl( private val bot: QGBotImpl, override val source: Channel, internal val sourceGuild: QGGuild?, - override val category: QGCategory = QGCategoryImpl( + category: QGCategory? = null +) : QGForumChannel { + override val category: QGCategory = category ?: QGCategoryImpl( bot = bot, guildId = source.guildId.ID, id = source.parentId.ID, sourceGuild = sourceGuild, - ), -) : QGForumChannel { + ) + override val coroutineContext: CoroutineContext = bot.newSupervisorCoroutineContext() -// override val guildId: ID = source.guildId.ID -// override val ownerId: ID = source.ownerId.ID -// -// override suspend fun owner(): QGMember = -// guild().member(ownerId) ?: throw NoSuchElementException("owner(id=$ownerId)") -// -// override suspend fun guild(): QGGuild = -// sourceGuild ?: bot.guild(guildId) ?: throw NoSuchElementException("guild(id=$guildId)") private fun threadFlow(): Flow = flow { GetThreadListApi.create(source.id).requestDataBy(bot.source).threads.forEach { @@ -90,7 +84,7 @@ internal class QGForumChannelImpl( private fun Thread.toQGThread(): QGThread = QGThreadImpl(bot, this, this@QGForumChannelImpl) - override fun threadCreator(): QGThreadCreator = QGThreadCreatorImpl(this) + override fun threadCreator(): QGThreadCreator = QGThreadCreatorImpl(this, bot) override fun toString(): String { return "QGForumChannel(id=$id, name=$name, category=$category)" @@ -104,3 +98,15 @@ internal fun QGChannel.asForumChannel(source: Channel = this.source): QGForumCha this as? QGForumChannel ?: throw IllegalStateException("The type of channel(id=${source.id}, name=${source.name}) in guild(id=${source.guildId}) is not category (${ChannelType.CATEGORY}), but ${source.type}") +internal fun Channel.toForumChannel( + bot: QGBotImpl, + sourceGuild: QGGuild? = null, + category: QGCategory? = null, +): QGForumChannelImpl { + return QGForumChannelImpl( + bot = bot, + source = this, + sourceGuild = sourceGuild, + category = category + ) +} diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGNonTextChannelImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGNonTextChannelImpl.kt similarity index 75% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGNonTextChannelImpl.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGNonTextChannelImpl.kt index 8c3b4be6..4c2eef2d 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGNonTextChannelImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGNonTextChannelImpl.kt @@ -17,7 +17,6 @@ package love.forte.simbot.component.qguild.internal.channel -import love.forte.simbot.common.id.ID import love.forte.simbot.common.id.StringID.Companion.ID import love.forte.simbot.component.qguild.channel.QGCategory import love.forte.simbot.component.qguild.channel.QGNonTextChannel @@ -36,29 +35,32 @@ import kotlin.coroutines.CoroutineContext * @author ForteScarlet */ internal class QGNonTextChannelImpl( - private val bot: QGBotImpl, + bot: QGBotImpl, override val source: Channel, - private val sourceGuild: QGGuild? = null, - override val category: QGCategory = QGCategoryImpl( + sourceGuild: QGGuild? = null, + category: QGCategory? = null, +) : QGNonTextChannel { + override val category: QGCategory = category ?: QGCategoryImpl( bot = bot, guildId = source.guildId.ID, id = source.parentId.ID, sourceGuild = sourceGuild, - ), -) : QGNonTextChannel { - override val coroutineContext: CoroutineContext = bot.newSupervisorCoroutineContext() - override val id: ID = source.id.ID -// private val guildId: ID get() = source.guildId.ID -// private val ownerId: ID get() = source.ownerId.ID + ) -// private suspend fun owner(): QGMember = guild().member(ownerId) ?: throw NoSuchElementException("owner(id=$ownerId)") -// -// private suspend fun guild(): QGGuild = -// sourceGuild -// ?: bot.guildRelation.guild(guildId) -// ?: throw NoSuchElementException("guild(id=$guildId)") + override val coroutineContext: CoroutineContext = bot.newSupervisorCoroutineContext() override fun toString(): String { return "QGNonTextChannel(id=$id, name=$name, category=$category)" } } + +internal fun Channel.toNonTextChannel( + bot: QGBotImpl, + sourceGuild: QGGuild? = null, + category: QGCategory? = null +): QGNonTextChannelImpl = QGNonTextChannelImpl( + bot = bot, + source = this, + sourceGuild = sourceGuild, + category = category +) diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGTextChannelImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGTextChannelImpl.kt similarity index 86% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGTextChannelImpl.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGTextChannelImpl.kt index 3508f7d4..212c7517 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGTextChannelImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/channel/QGTextChannelImpl.kt @@ -30,6 +30,7 @@ import love.forte.simbot.message.Message import love.forte.simbot.message.MessageContent import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.addStackTrace +import love.forte.simbot.qguild.model.Channel import kotlin.coroutines.CoroutineContext import love.forte.simbot.qguild.model.Channel as QGSourceChannel @@ -40,30 +41,22 @@ import love.forte.simbot.qguild.model.Channel as QGSourceChannel internal class QGTextChannelImpl internal constructor( private val bot: QGBotImpl, override val source: QGSourceChannel, - private val sourceGuild: QGGuild? = null, - override val category: QGCategory = QGCategoryImpl( - bot = bot, - guildId = source.guildId.ID, - id = source.parentId.ID, - sourceGuild = sourceGuild, - ), + sourceGuild: QGGuild? = null, + category: QGCategory? = null, /** * 如果是从一个事件而来,提供可用于消息回复的 msgId 来避免 event.channel().send(...) 出现问题 */ private val currentMsgId: String? = null, ) : QGTextChannel { + override val category: QGCategory = category ?: QGCategoryImpl( + bot = bot, + guildId = source.guildId.ID, + id = source.parentId.ID, + sourceGuild = sourceGuild, + ) + override val coroutineContext: CoroutineContext = bot.newSupervisorCoroutineContext() override val id: ID = source.id.ID -// private val guildId: ID get() = source.guildId.ID -// private val ownerId: ID get() = source.ownerId.ID - -// private suspend fun guild(): QGGuild = -// sourceGuild -// ?: bot.guildRelation.guild(guildId) -// ?: throw NoSuchElementException("guild(id=$guildId)") -// -// private suspend fun owner(): QGMember = -// guild().member(ownerId) ?: throw NoSuchElementException("owner(id=$ownerId)") override suspend fun send(message: Message): QGMessageReceipt { return try { @@ -106,6 +99,17 @@ internal class QGTextChannelImpl internal constructor( } } - +internal fun Channel.toTextChannel( + bot: QGBotImpl, + sourceGuild: QGGuild? = null, + category: QGCategory? = null, + currentMsgId: String? = null, +): QGTextChannelImpl = QGTextChannelImpl( + bot = bot, + source = this, + sourceGuild = sourceGuild, + category = category, + currentMsgId = currentMsgId +) diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGAtMessageCreateEventImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGAtMessageCreateEventImpl.kt new file mode 100644 index 00000000..928810cd --- /dev/null +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGAtMessageCreateEventImpl.kt @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.event + +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.component.qguild.channel.QGTextChannel +import love.forte.simbot.component.qguild.event.QGAtMessageCreateEvent +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.bot.castChannel +import love.forte.simbot.component.qguild.internal.guild.QGMemberImpl +import love.forte.simbot.component.qguild.internal.message.QGMessageContentImpl +import love.forte.simbot.component.qguild.message.QGMessageReceipt +import love.forte.simbot.component.qguild.message.sendMessage +import love.forte.simbot.message.MessageContent +import love.forte.simbot.message.MessageReceipt +import love.forte.simbot.qguild.model.ChannelType +import love.forte.simbot.qguild.model.Message + + +/** + * + * @author ForteScarlet + */ +internal class QGAtMessageCreateEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: Message +) : QGAtMessageCreateEvent() { + override val id: ID + get() = with(sourceEventEntity) { + buildString(guildId.length + channelId.length + id.length + 3) { + append(guildId).append('.') + append(channelId).append('.') + append(id).append('.') + } + }.ID + + override val messageContent: QGMessageContentImpl = QGMessageContentImpl(sourceEventEntity) + + override suspend fun reply(message: love.forte.simbot.message.Message): QGMessageReceipt { + var ref: Message.Reference? = null + return bot.sendMessage(sourceEventEntity.channelId, message) { + if (msgId == null) { + msgId = sourceEventEntity.id + } + if (messageReference == null) { + messageReference = ref ?: Message.Reference(sourceEventEntity.id).also { ref = it } + } + } + } + + override suspend fun reply(messageContent: MessageContent): MessageReceipt { + var ref: Message.Reference? = null + return bot.sendMessage(sourceEventEntity.channelId, messageContent) { + if (msgId == null) { + msgId = sourceEventEntity.id + } + if (messageReference == null) { + messageReference = ref ?: Message.Reference(sourceEventEntity.id).also { ref = it } + } + } + } + + override suspend fun reply(text: String): MessageReceipt { + var ref: Message.Reference? = null + return bot.sendMessage(sourceEventEntity.channelId, text) { + if (msgId == null) { + msgId = sourceEventEntity.id + } + if (messageReference == null) { + messageReference = ref ?: Message.Reference(sourceEventEntity.id).also { ref = it } + } + } + } + + override suspend fun guild(): QGGuild { + return with(sourceEventEntity) { + bot.queryGuild(guildId) ?: throw NoSuchElementException("guild(id=$guildId)") + } + } + + override suspend fun author(): QGMemberImpl { + return with(sourceEventEntity) { + bot.queryMember(guildId = guildId, userId = author.id) + ?: throw NoSuchElementException("member(id=${author.id})") + } + } + + override suspend fun content(): QGTextChannel { + return with(sourceEventEntity) { + bot.queryChannel(id = channelId, currentMsgId = id) + ?.castChannel { ChannelType.TEXT } + ?: throw NoSuchElementException("channel(id=${channelId})") + } + } +} diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGChannelEventImpls.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGChannelEventImpls.kt new file mode 100644 index 00000000..4dbf21e7 --- /dev/null +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGChannelEventImpls.kt @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.event + +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.component.qguild.channel.QGTextChannel +import love.forte.simbot.component.qguild.event.QGChannelCreateEvent +import love.forte.simbot.component.qguild.event.QGChannelDeleteEvent +import love.forte.simbot.component.qguild.event.QGChannelUpdateEvent +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.channel.QGTextChannelImpl +import love.forte.simbot.qguild.event.EventChannel + + +internal class QGChannelCreateEventImpl( + override val sourceEventRaw: String, + override val sourceEventEntity: EventChannel, + override val bot: QGBotImpl, + private val _channel: QGTextChannelImpl, +) : QGChannelCreateEvent() { + override val id: ID get() = tcgChannelModifyId(0, bot.id, sourceEventEntity.id, hashCode()) + override suspend fun channel(): QGTextChannel = _channel + override suspend fun content(): QGGuild = with(sourceEventEntity) { + bot.queryGuild(guildId) ?: throw NoSuchElementException("guild(id=${guildId})") + } +} + +internal class QGChannelUpdateEventImpl( + override val sourceEventRaw: String, + override val sourceEventEntity: EventChannel, + override val bot: QGBotImpl, + private val _channel: QGTextChannelImpl, +) : QGChannelUpdateEvent() { + override val id: ID get() = tcgChannelModifyId(1, bot.id, sourceEventEntity.id, hashCode()) + override suspend fun content(): QGTextChannel = _channel + override suspend fun guild(): QGGuild = with(sourceEventEntity) { + bot.queryGuild(guildId) ?: throw NoSuchElementException("guild(id=${guildId})") + } +} + +internal class QGChannelDeleteEventImpl( + override val sourceEventRaw: String, + override val sourceEventEntity: EventChannel, + override val bot: QGBotImpl, + private val _channel: QGTextChannelImpl +) : QGChannelDeleteEvent() { + override val id: ID get() = tcgChannelModifyId(2, bot.id, sourceEventEntity.id, hashCode()) + override suspend fun channel(): QGTextChannel = _channel + override suspend fun content(): QGGuild = with(sourceEventEntity) { + bot.queryGuild(guildId) ?: throw NoSuchElementException("guild(id=${guildId})") + } +} + + +private fun tcgChannelModifyId(t: Int, sourceBot: ID, sourceChannel: String, hash: Int): ID = + "$t$sourceBot.$sourceChannel.$hash".ID diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGForumEventImpls.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGForumEventImpls.kt new file mode 100644 index 00000000..441c9c08 --- /dev/null +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGForumEventImpls.kt @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.event + +import love.forte.simbot.component.qguild.channel.QGForumChannel +import love.forte.simbot.component.qguild.event.* +import love.forte.simbot.component.qguild.forum.QGPost +import love.forte.simbot.component.qguild.forum.QGReply +import love.forte.simbot.component.qguild.forum.QGThread +import love.forte.simbot.component.qguild.guild.QGMember +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.forum.QGPostImpl +import love.forte.simbot.component.qguild.internal.forum.QGReplyImpl +import love.forte.simbot.component.qguild.internal.forum.QGThreadImpl +import love.forte.simbot.qguild.model.forum.AuditResult +import love.forte.simbot.qguild.model.forum.Post +import love.forte.simbot.qguild.model.forum.Reply +import love.forte.simbot.qguild.model.forum.Thread + +internal class QGForumThreadCreateEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: Thread +) : QGForumThreadCreateEvent() { + override suspend fun content(): QGForumChannel = + bot.forumChannel(sourceEventEntity) + + override suspend fun author(): QGMember = + bot.author(sourceEventEntity) + + override val thread: QGThread = QGThreadImpl(bot, sourceEventEntity, null) +} + + +internal class QGForumThreadUpdateEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: Thread +) : QGForumThreadUpdateEvent() { + + override suspend fun content(): QGForumChannel = + bot.forumChannel(sourceEventEntity) + + override suspend fun author(): QGMember = + bot.author(sourceEventEntity) + + override val thread: QGThread = QGThreadImpl(bot, sourceEventEntity, null) +} + + +internal class QGForumThreadDeleteEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: Thread +) : QGForumThreadDeleteEvent() { + + override suspend fun content(): QGForumChannel = + bot.forumChannel(sourceEventEntity) + + override suspend fun author(): QGMember = + bot.author(sourceEventEntity) + + override val thread: QGThread = QGThreadImpl(bot, sourceEventEntity, null) +} + + +internal class QGForumPostCreateEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: Post +) : QGForumPostCreateEvent() { + + override suspend fun content(): QGForumChannel = + bot.forumChannel(sourceEventEntity) + + override suspend fun author(): QGMember = + bot.author(sourceEventEntity) + + override val post: QGPost = QGPostImpl(bot, sourceEventEntity, null) +} + +internal class QGForumPostDeleteEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: Post +) : QGForumPostDeleteEvent() { + + override suspend fun content(): QGForumChannel = + bot.forumChannel(sourceEventEntity) + + override suspend fun author(): QGMember = + bot.author(sourceEventEntity) + + override val post: QGPost = QGPostImpl(bot, sourceEventEntity, null) +} + +internal class QGForumReplyCreateEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: Reply +) : QGForumReplyCreateEvent() { + override suspend fun content(): QGForumChannel = + bot.forumChannel(sourceEventEntity) + + override suspend fun author(): QGMember = + bot.author(sourceEventEntity) + + override val post: QGReply = QGReplyImpl(bot, sourceEventEntity, null) +} + +internal class QGForumReplyDeleteEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: Reply +) : QGForumReplyDeleteEvent() { + override suspend fun content(): QGForumChannel = + bot.forumChannel(sourceEventEntity) + + override suspend fun author(): QGMember = + bot.author(sourceEventEntity) + + override val post: QGReply = QGReplyImpl(bot, sourceEventEntity, null) +} + +internal class QGForumPublishAuditResultEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: AuditResult, +) : QGForumPublishAuditResultEvent() { + override suspend fun content(): QGForumChannel = + bot.forumChannel(sourceEventEntity) + + override suspend fun author(): QGMember = + bot.author(sourceEventEntity) +} diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGGuildEventImpls.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGGuildEventImpls.kt new file mode 100644 index 00000000..53cdbc84 --- /dev/null +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGGuildEventImpls.kt @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.event + +import love.forte.simbot.annotations.FragileSimbotAPI +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.component.qguild.event.QGGuildCreateEvent +import love.forte.simbot.component.qguild.event.QGGuildDeleteEvent +import love.forte.simbot.component.qguild.event.QGGuildUpdateEvent +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl +import love.forte.simbot.qguild.event.EventGuild + +internal class QGGuildCreateEventImpl( + override val sourceEventRaw: String, + override val sourceEventEntity: EventGuild, + override val bot: QGBotImpl, + private val _guild: QGGuildImpl, +) : QGGuildCreateEvent() { + override val id: ID get() = tcgGuildModifyId(0, bot.id, sourceEventEntity.id, hashCode()) + override suspend fun content(): QGGuild = _guild +} + + +internal class QGGuildUpdateEventImpl( + override val sourceEventRaw: String, + override val sourceEventEntity: EventGuild, + override val bot: QGBotImpl, + private val _guild: QGGuildImpl, +) : QGGuildUpdateEvent() { + override val id: ID get() = tcgGuildModifyId(1, bot.id, sourceEventEntity.id, hashCode()) + override suspend fun content(): QGGuild = _guild +} + + +internal class QGGuildDeleteEventImpl( + override val sourceEventRaw: String, + override val sourceEventEntity: EventGuild, + override val bot: QGBotImpl, + private val _guild: QGGuildImpl?, +) : QGGuildDeleteEvent() { + override val id: ID get() = tcgGuildModifyId(2, bot.id, sourceEventEntity.id, hashCode()) + + @FragileSimbotAPI + override val guild: QGGuild? get() = _guild +} + + +private fun tcgGuildModifyId(t: Int, sourceBot: ID, sourceGuild: String, hash: Int): ID = + "$t$sourceBot.$sourceGuild.$hash".ID diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGInternalBotEventImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGInternalBotEventImpl.kt new file mode 100644 index 00000000..1be51804 --- /dev/null +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGInternalBotEventImpl.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.event + +import love.forte.simbot.component.qguild.event.QGBotRegisteredEvent +import love.forte.simbot.component.qguild.event.QGBotStartedEvent +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl + + +internal class QGBotRegisteredEventImpl(override val bot: QGBotImpl) : QGBotRegisteredEvent() + +internal class QGBotStartedEventImpl(override val bot: QGBotImpl) : QGBotStartedEvent() diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGMemberEventImpls.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGMemberEventImpls.kt new file mode 100644 index 00000000..2cc8c1e9 --- /dev/null +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGMemberEventImpls.kt @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.event + +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.component.qguild.event.QGMemberAddEvent +import love.forte.simbot.component.qguild.event.QGMemberRemoveEvent +import love.forte.simbot.component.qguild.event.QGMemberUpdateEvent +import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.guild.QGMember +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl +import love.forte.simbot.component.qguild.internal.guild.QGMemberImpl +import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.qguild.addStackTrace +import love.forte.simbot.qguild.event.EventMember +import love.forte.simbot.qguild.isUnauthorized + + +internal class QGMemberAddEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: EventMember, + private val _member: QGMemberImpl, +) : QGMemberAddEvent() { + override val id: ID get() = memberEventId(0, bot.id, sourceEventEntity.user.id, hashCode()) + override suspend fun member(): QGMemberImpl = _member + + override suspend fun operator(): QGMemberImpl? { + return with(sourceEventEntity) { + try { + bot.queryMember(guildId, opUserId) + } catch (apiEx: QQGuildApiException) { + // process no auth + if (apiEx.isUnauthorized) null else throw apiEx.addStackTrace { "QGMemberAddEvent.operator(opUserId=$opUserId)" } + } + } + } + + override suspend fun content(): QGGuild { + return with(sourceEventEntity) { + bot.queryGuild(guildId) ?: throw NoSuchElementException("guild(id=$guildId)") + } + } +} + +internal class QGMemberUpdateEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: EventMember, + private val _member: QGMemberImpl, +) : QGMemberUpdateEvent() { + override val id: ID get() = memberEventId(1, bot.id, sourceEventEntity.user.id, hashCode()) + + override suspend fun source(): QGGuild { + return with(sourceEventEntity) { + bot.queryGuild(guildId) ?: throw NoSuchElementException("guild(id=$guildId)") + } + } + + override suspend fun operator(): QGMemberImpl? { + return with(sourceEventEntity) { + try { + bot.queryMember(guildId, opUserId) + } catch (apiEx: QQGuildApiException) { + // process no auth + if (apiEx.isUnauthorized) null else throw apiEx.addStackTrace { "QGMemberAddEvent.operator(opUserId=$opUserId)" } + } + } + } + + override suspend fun content(): QGMember = _member +} + +internal class QGMemberRemoveEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: EventMember, + private val _member: QGMemberImpl, +) : QGMemberRemoveEvent() { + override val id: ID get() = memberEventId(2, bot.id, sourceEventEntity.user.id, hashCode()) + override suspend fun member(): QGMemberImpl = _member + + override suspend fun operator(): QGMemberImpl? { + return with(sourceEventEntity) { + try { + bot.queryMember(guildId, opUserId) + } catch (apiEx: QQGuildApiException) { + // process no auth + if (apiEx.isUnauthorized) null else throw apiEx.addStackTrace { "QGMemberAddEvent.operator(opUserId=$opUserId)" } + } + } + } + + override suspend fun content(): QGGuildImpl { + return with(sourceEventEntity) { + bot.queryGuild(guildId) ?: throw NoSuchElementException("guild(id=$guildId)") + } + } +} + +private fun memberEventId(t: Int, sourceBot: ID, sourceUserId: String, hash: Int): ID = + "$t$sourceBot.$sourceUserId.$hash".ID diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGOpenForumEventImpls.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGOpenForumEventImpls.kt new file mode 100644 index 00000000..13174566 --- /dev/null +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGOpenForumEventImpls.kt @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.event + +import love.forte.simbot.component.qguild.channel.QGForumChannel +import love.forte.simbot.component.qguild.event.* +import love.forte.simbot.component.qguild.guild.QGMember +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.bot.castChannel +import love.forte.simbot.qguild.event.OpenForumPostData +import love.forte.simbot.qguild.event.OpenForumReplyData +import love.forte.simbot.qguild.event.OpenForumThreadData +import love.forte.simbot.qguild.model.ChannelType +import love.forte.simbot.qguild.model.forum.ForumSourceInfo + + +internal suspend fun QGBotImpl.forumChannel(data: ForumSourceInfo): QGForumChannel { + val channel = queryChannel(data.channelId, null) + ?: throw NoSuchElementException("channel(id=${data.channelId})") + + return channel.castChannel { ChannelType.FORUM } +} + +internal suspend fun QGBotImpl.author(data: ForumSourceInfo): QGMember = + queryMember(data.guildId, data.authorId, null) + ?: throw NoSuchElementException("member(id=${data.authorId})") + +internal class QGOpenForumThreadCreateEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: OpenForumThreadData, +) : QGOpenForumThreadCreateEvent() { + override suspend fun content(): QGForumChannel = bot.forumChannel(sourceEventEntity) + override suspend fun author(): QGMember = bot.author(sourceEventEntity) +} + +internal class QGOpenForumThreadUpdateEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: OpenForumThreadData, +) : QGOpenForumThreadUpdateEvent() { + override suspend fun content(): QGForumChannel = bot.forumChannel(sourceEventEntity) + override suspend fun author(): QGMember = bot.author(sourceEventEntity) +} + +internal class QGOpenForumThreadDeleteEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: OpenForumThreadData, +) : QGOpenForumThreadDeleteEvent() { + override suspend fun content(): QGForumChannel = bot.forumChannel(sourceEventEntity) + override suspend fun author(): QGMember = bot.author(sourceEventEntity) +} + + +internal class QGOpenForumPostCreateEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: OpenForumPostData, +) : QGOpenForumPostCreateEvent() { + override suspend fun content(): QGForumChannel = bot.forumChannel(sourceEventEntity) + override suspend fun author(): QGMember = bot.author(sourceEventEntity) +} + +internal class QGOpenForumPostDeleteEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: OpenForumPostData, +) : QGOpenForumPostDeleteEvent() { + override suspend fun content(): QGForumChannel = bot.forumChannel(sourceEventEntity) + override suspend fun author(): QGMember = bot.author(sourceEventEntity) +} + +internal class QGOpenForumReplyCreateEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: OpenForumReplyData, +) : QGOpenForumReplyCreateEvent() { + override suspend fun content(): QGForumChannel = bot.forumChannel(sourceEventEntity) + override suspend fun author(): QGMember = bot.author(sourceEventEntity) +} + +internal class QGOpenForumReplyDeleteEventImpl( + override val bot: QGBotImpl, + override val sourceEventRaw: String, + override val sourceEventEntity: OpenForumReplyData, +) : QGOpenForumReplyDeleteEvent() { + override suspend fun content(): QGForumChannel = bot.forumChannel(sourceEventEntity) + override suspend fun author(): QGMember = bot.author(sourceEventEntity) +} diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGUnsupportedEventImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGUnsupportedEventImpl.kt new file mode 100644 index 00000000..c93de96e --- /dev/null +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGUnsupportedEventImpl.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.event + +import love.forte.simbot.annotations.FragileSimbotAPI +import love.forte.simbot.component.qguild.event.QGUnsupportedEvent +import love.forte.simbot.qguild.event.Signal + + +/** + * + * @author ForteScarlet + */ +@OptIn(FragileSimbotAPI::class) +internal data class QGUnsupportedEventImpl( + override val sourceEventEntity: Signal.Dispatch, + override val sourceEventRaw: String +) : QGUnsupportedEvent() diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt new file mode 100644 index 00000000..5bac3d89 --- /dev/null +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGForumsImpl.kt @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGPostImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGPostImpl.kt similarity index 59% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGPostImpl.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGPostImpl.kt index 5c603576..bb384fd8 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGPostImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGPostImpl.kt @@ -17,57 +17,34 @@ package love.forte.simbot.component.qguild.internal.forum -import love.forte.simbot.ExperimentalSimbotApi -import love.forte.simbot.Timestamp -import love.forte.simbot.common.time.Timestamp -import love.forte.simbot.component.qguild.QGGuild -import love.forte.simbot.component.qguild.QGMember -import love.forte.simbot.component.qguild.channel.QGForumChannel import love.forte.simbot.component.qguild.forum.QGPost import love.forte.simbot.component.qguild.forum.QGThread -import love.forte.simbot.component.qguild.internal.QGGuildImpl +import love.forte.simbot.component.qguild.guild.QGMember import love.forte.simbot.component.qguild.internal.bot.QGBotImpl import love.forte.simbot.component.qguild.internal.channel.QGForumChannelImpl -import love.forte.simbot.component.qguild.internal.channel.asForumChannel import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl -import love.forte.simbot.component.qguild.internal.toTimestamp -import love.forte.simbot.component.qguild.util.requestBy import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.api.forum.GetThreadApi import love.forte.simbot.qguild.ifNotFoundThenNoSuch import love.forte.simbot.qguild.model.forum.Post -import love.forte.simbot.qguild.stdlib.requestBy +import love.forte.simbot.qguild.stdlib.requestDataBy /** * * @author ForteScarlet */ internal class QGPostImpl( - override val bot: QGBotImpl, + private val bot: QGBotImpl, override val source: Post, private val sourceChannel: QGForumChannelImpl? ) : QGPost { - - @OptIn(ExperimentalSimbotApi::class) - override val datetime: Timestamp = source.postInfo.dateTime.toTimestamp() - - override suspend fun guild(): QGGuild = - sourceChannel?.guild() - ?: bot.queryGuild(source.guildId) - ?: throw NoSuchElementException("guild(id=${source.guildId})") - - - override suspend fun channel(): QGForumChannel = - sourceChannel - ?: (bot.channel(source.channelId, null) - ?: throw NoSuchElementException("channel(id=${source.channelId})")).asForumChannel() - override suspend fun author(): QGMember = - bot.member(source.guildId, source.authorId, sourceChannel?.sourceGuild as? QGGuildImpl) ?: throw NoSuchElementException("author(id=$authorId)") + bot.queryMember(source.guildId, source.authorId, sourceChannel?.sourceGuild as? QGGuildImpl) + ?: throw NoSuchElementException("author(id=$authorId)") override suspend fun thread(): QGThread { val (thread) = try { - GetThreadApi.create(source.channelId, source.postInfo.threadId).requestBy(bot) + GetThreadApi.create(source.channelId, source.postInfo.threadId).requestDataBy(bot.source) } catch (apiEx: QQGuildApiException) { apiEx.ifNotFoundThenNoSuch { "thread(id=${source.postInfo.threadId}) by post(id=${source.postInfo.postId})" } } diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGReplyImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGReplyImpl.kt similarity index 59% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGReplyImpl.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGReplyImpl.kt index 35d0d8b2..e51cecc8 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGReplyImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGReplyImpl.kt @@ -17,57 +17,34 @@ package love.forte.simbot.component.qguild.internal.forum -import love.forte.simbot.ExperimentalSimbotApi -import love.forte.simbot.Timestamp -import love.forte.simbot.common.time.Timestamp -import love.forte.simbot.component.qguild.QGGuild -import love.forte.simbot.component.qguild.QGMember -import love.forte.simbot.component.qguild.channel.QGForumChannel import love.forte.simbot.component.qguild.forum.QGReply import love.forte.simbot.component.qguild.forum.QGThread -import love.forte.simbot.component.qguild.internal.QGGuildImpl +import love.forte.simbot.component.qguild.guild.QGMember import love.forte.simbot.component.qguild.internal.bot.QGBotImpl import love.forte.simbot.component.qguild.internal.channel.QGForumChannelImpl -import love.forte.simbot.component.qguild.internal.channel.asForumChannel import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl -import love.forte.simbot.component.qguild.internal.toTimestamp -import love.forte.simbot.component.qguild.util.requestBy import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.api.forum.GetThreadApi import love.forte.simbot.qguild.ifNotFoundThenNoSuch import love.forte.simbot.qguild.model.forum.Reply -import love.forte.simbot.qguild.stdlib.requestBy +import love.forte.simbot.qguild.stdlib.requestDataBy /** * * @author ForteScarlet */ internal class QGReplyImpl( - override val bot: QGBotImpl, + private val bot: QGBotImpl, override val source: Reply, private val sourceChannel: QGForumChannelImpl? ) : QGReply { - @OptIn(ExperimentalSimbotApi::class) - override val datetime: Timestamp = source.replyInfo.dateTime.toTimestamp() - - override suspend fun guild(): QGGuild = - sourceChannel?.guild() - ?: bot.queryGuild(source.guildId) - ?: throw NoSuchElementException("guild(id=${source.guildId})") - - - override suspend fun channel(): QGForumChannel = - sourceChannel - ?: (bot.channel(source.channelId, null) - ?: throw NoSuchElementException("channel(id=${source.channelId})")).asForumChannel() - - override suspend fun author(): QGMember = - bot.member(source.guildId, source.authorId, sourceChannel?.sourceGuild as? QGGuildImpl) ?: throw NoSuchElementException("author(id=$authorId)") + bot.queryMember(source.guildId, source.authorId, sourceChannel?.sourceGuild as? QGGuildImpl) + ?: throw NoSuchElementException("author(id=$authorId)") override suspend fun thread(): QGThread { val (thread) = try { - GetThreadApi.create(source.channelId, source.replyInfo.threadId).requestBy(bot) + GetThreadApi.create(source.channelId, source.replyInfo.threadId).requestDataBy(bot.source) } catch (apiEx: QQGuildApiException) { apiEx.ifNotFoundThenNoSuch { "thread(id=${source.replyInfo.threadId}) by reply(id=${source.replyInfo.replyId})" } } diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadCreatorImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadCreatorImpl.kt similarity index 71% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadCreatorImpl.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadCreatorImpl.kt index 9e34c01a..625b2103 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadCreatorImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadCreatorImpl.kt @@ -18,24 +18,25 @@ package love.forte.simbot.component.qguild.internal.forum import love.forte.simbot.component.qguild.forum.QGThreadCreator +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl import love.forte.simbot.component.qguild.internal.channel.QGForumChannelImpl -import love.forte.simbot.component.qguild.util.requestBy import love.forte.simbot.qguild.api.forum.PublishThreadApi import love.forte.simbot.qguild.api.forum.ThreadPublishFormat import love.forte.simbot.qguild.api.forum.ThreadPublishResult -import love.forte.simbot.qguild.stdlib.requestBy +import love.forte.simbot.qguild.stdlib.requestDataBy /** * * @author ForteScarlet */ -internal class QGThreadCreatorImpl(private val channel: QGForumChannelImpl) : QGThreadCreator { +internal class QGThreadCreatorImpl(private val channel: QGForumChannelImpl, private val bot: QGBotImpl) : + QGThreadCreator { override var title: String? = null override var content: String? = null override var format: Int? = null override var formatType: ThreadPublishFormat? - get() = format?.let { ThreadPublishFormat.values().find { f -> f.value == it } } + get() = format?.let { ThreadPublishFormat.entries.find { f -> f.value == it } } set(value) { format = value?.value } @@ -43,13 +44,13 @@ internal class QGThreadCreatorImpl(private val channel: QGForumChannelImpl) : QG override suspend fun publish(): ThreadPublishResult { return PublishThreadApi.create( channelId = channel.source.id, - title = title ?: throw IllegalArgumentException("Required property 'title' is null"), - content = content ?: throw IllegalArgumentException("Required property 'content' is null"), - format = format ?: throw IllegalArgumentException("Required property 'format' is null"), - ).requestBy(channel.bot) + title = requireNotNull(title) { "Required property 'title' is null" }, + content = requireNotNull(content) { "Required property 'content' is null" }, + format = requireNotNull(format) { "Required property 'format' is null" }, + ).requestDataBy(bot.source) } override fun toString(): String { - return "QGThreadCreatorImpl(title=$title, content=$content, format=$format, formatType=$formatType)" + return "QGThreadCreator(title=$title, content=$content, format=$format, formatType=$formatType)" } } diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt similarity index 51% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt index a2d6d1db..51f7f9d2 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt @@ -17,24 +17,22 @@ package love.forte.simbot.component.qguild.internal.forum -import love.forte.simbot.ExperimentalSimbotApi -import love.forte.simbot.Timestamp -import love.forte.simbot.component.qguild.QGGuild -import love.forte.simbot.component.qguild.QGMember -import love.forte.simbot.component.qguild.channel.QGForumChannel -import love.forte.simbot.component.qguild.forum.QGForumChannel +import love.forte.simbot.ability.DeleteFailureException +import love.forte.simbot.ability.DeleteOption +import love.forte.simbot.ability.StandardDeleteOption.* +import love.forte.simbot.ability.StandardDeleteOption.Companion.standardAnalysis import love.forte.simbot.component.qguild.forum.QGThread -import love.forte.simbot.component.qguild.internal.QGBotImpl -import love.forte.simbot.component.qguild.internal.QGGuildImpl +import love.forte.simbot.component.qguild.guild.QGMember import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.internal.bot.newSupervisorCoroutineContext import love.forte.simbot.component.qguild.internal.channel.QGForumChannelImpl -import love.forte.simbot.component.qguild.internal.newSupervisorCoroutineContext -import love.forte.simbot.component.qguild.internal.toTimestamp -import love.forte.simbot.component.qguild.util.requestBy +import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.api.forum.DeleteThreadApi -import love.forte.simbot.qguild.ifNotFoundThen +import love.forte.simbot.qguild.initCause0 +import love.forte.simbot.qguild.isNotFound import love.forte.simbot.qguild.model.forum.Thread +import love.forte.simbot.qguild.stdlib.requestDataBy import kotlin.coroutines.CoroutineContext @@ -43,42 +41,40 @@ import kotlin.coroutines.CoroutineContext * @author ForteScarlet */ internal class QGThreadImpl( - override val bot: QGBotImpl, + private val bot: QGBotImpl, override val source: Thread, private val sourceChannel: QGForumChannelImpl?, ) : QGThread { - override val coroutineContext: CoroutineContext = bot.newSupervisorCoroutineContext() - override suspend fun guild(): QGGuild = - sourceChannel?.guild() - ?: bot.queryGuild(source.guildId) - ?: throw NoSuchElementException("guild(id=${source.guildId})") - override suspend fun author(): QGMember = - bot.member(source.guildId, source.authorId, sourceChannel?.sourceGuild as? QGGuildImpl) ?: throw NoSuchElementException("author(id=$authorId)") + bot.queryMember(source.guildId, source.authorId, sourceChannel?.sourceGuild as? QGGuildImpl) + ?: throw NoSuchElementException("author(id=$authorId)") - @OptIn(ExperimentalSimbotApi::class) - override val dateTime: Timestamp - get() = source.threadInfo.dateTime.toTimestamp() - override suspend fun channel(): QGForumChannel { - if (sourceChannel != null) { - return sourceChannel - } + override suspend fun delete(vararg options: DeleteOption) { + kotlin.runCatching { + DeleteThreadApi.create(source.channelId, source.threadInfo.threadId).requestDataBy(bot.source) + }.onFailure { e -> + val stdOpts = options.standardAnalysis() + if (e is QQGuildApiException) { + if (e.isNotFound) { + if (IGNORE_ON_NO_SUCH_TARGET !in stdOpts) { + throw NoSuchElementException(e.message).apply { initCause0(e) } + } + return + } - val channel = bot.channel(source.channelId, null) - ?: throw NoSuchElementException("channel(id=${source.channelId})") + if (IGNORE_ON_FAILURE !in stdOpts) { + throw DeleteFailureException(e) + } - return channel.asForumChannel() - } + return + } - override suspend fun delete(): Boolean { - return try { - DeleteThreadApi.create(source.channelId, source.threadInfo.threadId).requestBy(bot) - true - } catch (apiEx: QQGuildApiException) { - apiEx.ifNotFoundThen { false } + if (IGNORE_ON_ANY_FAILURE !in stdOpts) { + throw e + } } } diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGGuildImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGGuildImpl.kt similarity index 54% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGGuildImpl.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGGuildImpl.kt index 1800f532..b791b56f 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGGuildImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGGuildImpl.kt @@ -20,9 +20,8 @@ package love.forte.simbot.component.qguild.internal.guild import kotlinx.coroutines.CompletableJob import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.flow.emitAll +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.map import love.forte.simbot.common.collectable.Collectable import love.forte.simbot.common.collectable.asCollectable @@ -34,15 +33,21 @@ import love.forte.simbot.component.qguild.channel.QGChannel import love.forte.simbot.component.qguild.channel.QGForumChannel import love.forte.simbot.component.qguild.channel.QGTextChannel import love.forte.simbot.component.qguild.guild.QGGuild +import love.forte.simbot.component.qguild.guild.QGMember import love.forte.simbot.component.qguild.internal.bot.QGBotImpl import love.forte.simbot.component.qguild.internal.channel.QGCategoryChannelImpl +import love.forte.simbot.component.qguild.internal.channel.toCategoryChannel +import love.forte.simbot.component.qguild.internal.channel.toForumChannel +import love.forte.simbot.component.qguild.internal.channel.toTextChannel +import love.forte.simbot.component.qguild.internal.role.QGRoleCreatorImpl import love.forte.simbot.component.qguild.role.QGGuildRole import love.forte.simbot.component.qguild.role.QGRoleCreator -import love.forte.simbot.definition.Member +import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.api.apipermission.ApiPermissions import love.forte.simbot.qguild.api.apipermission.GetApiPermissionListApi import love.forte.simbot.qguild.api.member.GetGuildMemberListApi import love.forte.simbot.qguild.api.member.createFlow +import love.forte.simbot.qguild.isNotFound import love.forte.simbot.qguild.model.ChannelType import love.forte.simbot.qguild.model.Guild import love.forte.simbot.qguild.model.isCategory @@ -62,99 +67,76 @@ internal class QGGuildImpl private constructor( override suspend fun permissions(): ApiPermissions = GetApiPermissionListApi.create(source.id).requestDataBy(bot.source) - override suspend fun botAsMember(): Member { - TODO("Not yet implemented") + override suspend fun botAsMember(): QGMember { + val member = try { + bot.queryMember(source.id, bot.userId.literal, this) + ?: throw NoSuchElementException("Bot as member(id=${bot.userId})") + } catch (apiEx: QQGuildApiException) { + if (apiEx.isNotFound) throw NoSuchElementException("Bot as member(id=${bot.userId})") else throw apiEx + } + + return member + } + + override fun members(batch: Int): Collectable { + require(batch > 0) { "'batch' must be > 0, but $batch" } + return GetGuildMemberListApi.createFlow(guildId = source.id, batch = batch) { requestDataBy(bot.source) } + .map { m -> + QGMemberImpl( + bot = bot, + source = m, + guildId = this@QGGuildImpl.id, + sourceGuild = bot.checkIfTransmitCacheable(this@QGGuildImpl) + ) + }.asCollectable() } - override val members: Collectable - get() = queryMembers() - - // TODO Spec Collectable type? - private fun queryMembers(): Collectable = flow { // prop -> - // 批次 -// val batchLimit = prop.batch.takeIf { it > 0 } ?: GetGuildMemberListApi.MAX_LIMIT - val batchLimit = GetGuildMemberListApi.MAX_LIMIT - val flow = - GetGuildMemberListApi.createFlow(guildId = source.id, batch = batchLimit) { requestDataBy(bot.source) } -// .let(prop::effectOn) - - emitAll(flow.map { m -> - QGMemberImpl( - bot = bot, - source = m, - guildId = this@QGGuildImpl.id, - sourceGuild = bot.checkIfTransmitCacheable(this@QGGuildImpl) - ) - }) - }.asCollectable() - - override suspend fun member(id: ID): QGMemberImpl? = member(id.literal) - - internal suspend fun member(id: String): QGMemberImpl? = bot.member(source.id, id, this) - - // TODO + override suspend fun member(id: ID): QGMemberImpl? = + bot.queryMember(source.id, id.literal, this) + @ExperimentalQGApi override val roles: Collectable - get() = TODO() -// get() = bot.effectedFlowItems { -// GetGuildRoleListApi.create(source.id).requestBy(bot).roles.forEach { info -> -// val roleImpl = QGGuildRoleImpl( -// bot = bot, -// guildId = id, -// source = info, -// sourceGuild = bot.checkIfTransmitCacheable(this@QGGuildImpl) -// ) -// -// emit(roleImpl) -// } -// } + get() = bot.queryGuildRoles(guildId = source.id, this).asCollectable() @ExperimentalQGApi - override fun roleCreator(): QGRoleCreator = TODO() //QGRoleCreatorImpl(this) + override fun roleCreator(): QGRoleCreator = QGRoleCreatorImpl(this) + + private fun channelFlow(): Flow = + bot.queryChannels(source.id, bot.checkIfTransmitCacheable(this)) - // override val channels: Collectable - get() = bot.queryChannels(source.id, bot.checkIfTransmitCacheable(this)) - .asCollectable() + get() = channelFlow().asCollectable() override suspend fun channel(id: ID): QGChannel? = - bot.channel(id.literal, this) + bot.queryChannel(id.literal, this) override val chatChannels: Collectable - get() = TODO("Not yet implemented") + get() = bot.querySimpleChannelFlow(source.id) + .filter { it.type == ChannelType.TEXT } + .map { it.toTextChannel(bot = bot, sourceGuild = this) } + .asCollectable() - override suspend fun chatChannel(id: ID): QGTextChannel? { - TODO("Not yet implemented") - } + override suspend fun chatChannel(id: ID): QGTextChannel? = + bot.queryChannel(id.literal, this) as? QGTextChannel - override val forumChannels: Collectable - get() = TODO("Not yet implemented") + override val forums: Collectable + get() = bot.querySimpleChannelFlow(source.id) + .filter { it.type == ChannelType.FORUM } + .map { it.toForumChannel(bot = bot, sourceGuild = this) } + .asCollectable() - override suspend fun forumChannel(id: ID): QGForumChannel? { - TODO("Not yet implemented") - } + override suspend fun forum(id: ID): QGForumChannel? = + bot.queryChannel(id.literal, this) as? QGForumChannel override val categories: Collectable - get() = queryCategories() - - private fun queryCategories(): Collectable = bot.channelFlow(source.id) - .filter { it.type.isCategory } - .map { info -> - QGCategoryChannelImpl( - bot = bot, - source = info, - sourceGuild = this, - ) - }.asCollectable() - - override suspend fun category(id: ID): QGCategoryChannel? { - val channel = channel(id) ?: return null -// - return channel as? QGCategoryChannel - ?: throw IllegalStateException("The type of channel(id=${channel.source.id}, name=${channel.source.name}) in guild(id=${source.id}, name=$name) is not category (${ChannelType.CATEGORY}), but ${channel.source.type}") - } + get() = bot.querySimpleChannelFlow(source.id) + .filter { it.type.isCategory } + .map { it.toCategoryChannel(bot = bot, sourceGuild = this) } + .asCollectable() + override suspend fun category(id: ID): QGCategoryChannel? = + bot.queryChannel(id.literal, this) as? QGCategoryChannel override fun toString(): String { return "QGGuild(id=$id, name=$name)" diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGMemberImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGMemberImpl.kt similarity index 84% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGMemberImpl.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGMemberImpl.kt index 5b68ee08..7675f026 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGMemberImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/guild/QGMemberImpl.kt @@ -20,6 +20,7 @@ package love.forte.simbot.component.qguild.internal.guild import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import love.forte.simbot.common.collectable.Collectable +import love.forte.simbot.common.collectable.asCollectable import love.forte.simbot.common.id.ID import love.forte.simbot.common.id.StringID.Companion.ID import love.forte.simbot.common.id.literal @@ -58,15 +59,7 @@ internal class QGMemberImpl( private val sourceGuild: QGGuildImpl? = null ) : QGMember { override val coroutineContext: CoroutineContext = bot.newSupervisorCoroutineContext() - private val user get() = source.user - - override val id: ID = user.id.ID - -// override suspend fun guild(): QGGuildImpl = -// sourceGuild ?: bot.guild(guildId) ?: throw NoSuchElementException("guild(id=$guildId)") - - // TODO private val roleIdSet = source.roles.toSet() private val dmsInitLock = Mutex() @@ -89,30 +82,15 @@ internal class QGMemberImpl( } } - // TODO - @ExperimentalQGApi override val roles: Collectable - get() = TODO("Not yet implemented") - - // TODO -// @ExperimentalSimbotApi -// override val roles: Collectable -// get() { -// return effectedFlowItems { -// guild().roles.collect { -// if (it.id.literal in roleIdSet) { -// emit( -// QGMemberRoleImpl( -// guildRole = it, -// memberId = this@QGMemberImpl.id, -// sourceMember = bot.checkIfTransmitCacheable(this@QGMemberImpl) -// ) -// ) -// } -// } -// } -// } + get() = + bot.queryMemberRoles( + guildId = guildId.literal, + roleIds = roleIdSet, + memberId = source.user.id.ID, + sourceGuild = sourceGuild + ).asCollectable() @JvmSynthetic diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGReceiveMessageContentImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGReceiveMessageContentImpl.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGReceiveMessageContentImpl.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGReceiveMessageContentImpl.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGSingleMessageReceiptImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGSingleMessageReceiptImpl.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGSingleMessageReceiptImpl.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/message/QGSingleMessageReceiptImpl.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/BaseQGRole.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/BaseQGRole.kt similarity index 95% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/BaseQGRole.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/BaseQGRole.kt index 31d4cff7..c6edd8f9 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/BaseQGRole.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/BaseQGRole.kt @@ -32,7 +32,7 @@ import kotlin.coroutines.CoroutineContext * @author ForteScarlet */ @ExperimentalQGApi -internal abstract class BaseQGRole(protected val bot: QGBotImpl) : QGRole { +internal abstract class BaseQGRole(internal val bot: QGBotImpl) : QGRole { override val coroutineContext: CoroutineContext = bot.newSupervisorCoroutineContext() abstract override var source: Role diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt similarity index 75% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt index 69988eed..3176a384 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt @@ -19,8 +19,9 @@ package love.forte.simbot.component.qguild.internal.role import io.ktor.util.internal.* import kotlinx.coroutines.flow.map +import love.forte.simbot.ability.DeleteFailureException import love.forte.simbot.ability.DeleteOption -import love.forte.simbot.ability.StandardDeleteOption +import love.forte.simbot.ability.StandardDeleteOption.* import love.forte.simbot.ability.StandardDeleteOption.Companion.standardAnalysis import love.forte.simbot.common.collectable.Collectable import love.forte.simbot.common.collectable.asCollectable @@ -38,6 +39,7 @@ import love.forte.simbot.qguild.api.member.GetGuildRoleMemberListApi import love.forte.simbot.qguild.api.member.createFlow import love.forte.simbot.qguild.api.role.AddMemberRoleApi import love.forte.simbot.qguild.api.role.DeleteGuildRoleApi +import love.forte.simbot.qguild.isNotFound import love.forte.simbot.qguild.model.Role import love.forte.simbot.qguild.stdlib.requestDataBy import kotlin.concurrent.Volatile @@ -67,12 +69,12 @@ internal class QGGuildRoleImpl( private suspend fun grantTo0(memberId: ID, channelId: String?): QGMemberRoleImpl { AddMemberRoleApi.create(guildId.literal, memberId.literal, id.literal, channelId).requestDataBy(bot.source) - return QGMemberRoleImpl(this, memberId, null) + return QGMemberRoleImpl(bot, this, memberId) } private suspend fun grantTo0(member: QGMember, channelId: String?): QGMemberRoleImpl { AddMemberRoleApi.create(guildId.literal, member.id.literal, id.literal, channelId).requestDataBy(bot.source) - return QGMemberRoleImpl(this, member.id, member) + return QGMemberRoleImpl(bot, this, member.id) } override suspend fun delete(vararg options: DeleteOption) { @@ -81,17 +83,27 @@ internal class QGGuildRoleImpl( DeleteGuildRoleApi.create(guildId.literal, source.id).requestDataBy(bot.source) }.onFailure { e -> if (e is QQGuildApiException) { - if (e.value == 404 && StandardDeleteOption.IGNORE_ON_NO_SUCH_TARGET !in stdOpts) { - // TODO - throw NoSuchElementException().also { it.initCauseBridge(e) } + if (e.isNotFound) { + if (IGNORE_ON_NO_SUCH_TARGET !in stdOpts) { + throw NoSuchElementException().also { it.initCauseBridge(e) } + } + return } + + if (IGNORE_ON_FAILURE !in stdOpts) { + throw DeleteFailureException(e) + } + return + } + + if (IGNORE_ON_ANY_FAILURE !in stdOpts) { + throw e } - // TODO } } override fun members(batch: Int): Collectable { - require(batch > 0) { "'batch' must > 0, but: $batch" } + require(batch > 0) { "'batch' must be > 0, but: $batch" } val guildId = guildId val guildIdLiteral = guildId.literal val roleId = source.id @@ -104,31 +116,17 @@ internal class QGGuildRoleImpl( bot = bot, source = it, guildId = guildId, sourceGuild = this.sourceGuild ) }.asCollectable() - - } - -// override val members: Collectable -// get() { -// val guildId = guildId -// val guildIdLiteral = guildId.literal -// val roleId = source.id -// -// return flow {// prop -> -// GetGuildRoleMemberListApi.createFlow( -// guildIdLiteral, -// roleId, -// prop.batch.takeIf { it < 1 } ?: GetGuildRoleMemberListApi.MAX_LIMIT -// ) { requestBy(bot) } -// .let(prop::effectOn) -// .map { -// QGMemberImpl( -// bot = bot, -// source = it, -// guildId = guildId, -// sourceGuild = this.sourceGuild -// ) -// } -// } -// } } + + +internal fun Role.toGuildRole( + bot: QGBotImpl, + guildId: ID, + sourceGuild: QGGuildImpl? = null +): QGGuildRoleImpl = QGGuildRoleImpl( + bot = bot, + guildId = guildId, + source = this, + sourceGuild = sourceGuild +) diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt new file mode 100644 index 00000000..ab41f5b0 --- /dev/null +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2023-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.role + +import love.forte.simbot.ability.DeleteFailureException +import love.forte.simbot.ability.DeleteOption +import love.forte.simbot.ability.StandardDeleteOption.* +import love.forte.simbot.ability.StandardDeleteOption.Companion.standardAnalysis +import love.forte.simbot.common.id.ID +import love.forte.simbot.common.id.literal +import love.forte.simbot.component.qguild.ExperimentalQGApi +import love.forte.simbot.component.qguild.internal.bot.QGBotImpl +import love.forte.simbot.component.qguild.role.QGMemberRole +import love.forte.simbot.component.qguild.role.QGRoleUpdater +import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.qguild.addStackTrace +import love.forte.simbot.qguild.api.role.RemoveMemberRoleApi +import love.forte.simbot.qguild.initCause0 +import love.forte.simbot.qguild.isNotFound +import love.forte.simbot.qguild.model.Role +import love.forte.simbot.qguild.stdlib.requestDataBy + + +/** + * + * @author ForteScarlet + */ +@OptIn(ExperimentalQGApi::class) +internal class QGMemberRoleImpl( + bot: QGBotImpl, + override val guildRole: QGGuildRoleImpl, + override val memberId: ID, +) : BaseQGRole(bot), QGMemberRole { + + override val guildId: ID + get() = guildRole.guildId + + override var source: Role by guildRole::source + + override fun updater(): QGRoleUpdater = guildRole.updater() + + override suspend fun delete(vararg options: DeleteOption) { + delete0(channelId = null, options = options) + } + + override suspend fun delete(channelId: ID?, vararg options: DeleteOption) { + delete0(channelId = channelId?.literal, options = options) + } + + private suspend fun delete0(channelId: String?, vararg options: DeleteOption) { + kotlin.runCatching { + RemoveMemberRoleApi.create(guildId.literal, memberId.literal, id.literal, channelId) + .requestDataBy(bot.source) + }.onFailure { e -> + val stdOpts = options.standardAnalysis() + if (e is QQGuildApiException) { + if (e.isNotFound) { + if (IGNORE_ON_NO_SUCH_TARGET !in stdOpts) { + throw NoSuchElementException().apply { initCause0(e.addStackTrace { "QGMemberRole.delete" }) } + } + return + } + + if (IGNORE_ON_FAILURE !in stdOpts) { + throw DeleteFailureException(e) + } + return + } + + if (IGNORE_ON_ANY_FAILURE !in stdOpts) { + throw e + } + } + } +} + + +internal fun QGGuildRoleImpl.toMemberRole( + bot: QGBotImpl = this.bot, + memberId: ID, +): QGMemberRoleImpl = QGMemberRoleImpl( + bot = bot, + guildRole = this, + memberId = memberId +) diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleCreatorImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleCreatorImpl.kt similarity index 77% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleCreatorImpl.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleCreatorImpl.kt index d583f4f3..532e1642 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleCreatorImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleCreatorImpl.kt @@ -17,23 +17,21 @@ package love.forte.simbot.component.qguild.internal.role -import love.forte.simbot.ExperimentalSimbotApi import love.forte.simbot.common.id.literal +import love.forte.simbot.component.qguild.ExperimentalQGApi import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl import love.forte.simbot.component.qguild.role.QGGuildRole import love.forte.simbot.component.qguild.role.QGRoleCreator -import love.forte.simbot.component.qguild.util.requestBy -import love.forte.simbot.literal import love.forte.simbot.qguild.api.role.CreateGuildRoleApi import love.forte.simbot.qguild.api.role.ModifyGuildRoleApi -import love.forte.simbot.qguild.stdlib.requestBy +import love.forte.simbot.qguild.stdlib.requestDataBy /** * * @author ForteScarlet */ -@OptIn(ExperimentalSimbotApi::class) +@OptIn(ExperimentalQGApi::class) internal class QGRoleCreatorImpl(private val guild: QGGuildImpl) : QGRoleCreator { override var name: String? = null override var color: Int? = null @@ -46,19 +44,20 @@ internal class QGRoleCreatorImpl(private val guild: QGGuildImpl) : QGRoleCreator val hoist = isHoist?.let { if (it) 1 else 0 } val created = CreateGuildRoleApi.create(guild.id.literal, name, color, hoist) - .requestBy(guild.baseBot) + .requestDataBy(guild.bot.source) + + val role = created.role + ?: ModifyGuildRoleApi.create(guild.id.literal, created.roleId, name, color, hoist) + .requestDataBy(guild.bot.source).role - val role = created.role ?: ModifyGuildRoleApi.create(guild.id.literal, created.roleId, name, color, hoist) - .requestBy(guild.baseBot).role // ?: GetGuildRoleListApi.create(guild.id.literal).requestBy(guild.baseBot) // .roles.find { it.id == created.roleId }!! - return QGGuildRoleImpl( - bot = guild.baseBot, + return role.toGuildRole( + bot = guild.bot, guildId = guild.id, - source = role, - sourceGuild = guild.baseBot.checkIfTransmitCacheable(guild) + sourceGuild = guild.bot.checkIfTransmitCacheable(guild) ) } } diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleUpdaterImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleUpdaterImpl.kt similarity index 79% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleUpdaterImpl.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleUpdaterImpl.kt index 5fbba561..36f6f63b 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleUpdaterImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGRoleUpdaterImpl.kt @@ -17,20 +17,18 @@ package love.forte.simbot.component.qguild.internal.role -import love.forte.simbot.ExperimentalSimbotApi import love.forte.simbot.common.id.literal +import love.forte.simbot.component.qguild.ExperimentalQGApi import love.forte.simbot.component.qguild.role.QGRoleUpdater -import love.forte.simbot.component.qguild.util.requestBy -import love.forte.simbot.literal import love.forte.simbot.qguild.api.role.ModifyGuildRoleApi -import love.forte.simbot.qguild.stdlib.requestBy +import love.forte.simbot.qguild.stdlib.requestDataBy /** * * @author ForteScarlet */ -@OptIn(ExperimentalSimbotApi::class) +@OptIn(ExperimentalQGApi::class) internal class QGRoleUpdaterImpl(private val role: BaseQGRole) : QGRoleUpdater { override var name: String? = null override var color: Int? = null @@ -42,8 +40,13 @@ internal class QGRoleUpdaterImpl(private val role: BaseQGRole) : QGRoleUpdater { if (name == null && color == null && isHoist == null) { throw IllegalArgumentException("No parameters are set") } - val modified = ModifyGuildRoleApi.create(role.guildId.literal, role.source.id, name, color, isHoist?.let { if (it) 1 else 0 }) - .requestBy(role.bot) + val modified = ModifyGuildRoleApi.create( + role.guildId.literal, + role.source.id, + name, + color, + isHoist?.let { if (it) 1 else 0 }) + .requestDataBy(role.bot.source) // update value role.source = modified.role diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/MessageSender.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/MessageSender.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/MessageSender.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/MessageSender.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGArk.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGArk.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGArk.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGArk.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGAtMessages.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGAtMessages.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGAtMessages.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGAtMessages.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGAttachmentMessage.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGAttachmentMessage.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGAttachmentMessage.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGAttachmentMessage.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGContentText.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGContentText.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGContentText.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGContentText.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGEmbed.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGEmbed.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGEmbed.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGEmbed.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGImages.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGImages.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGImages.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGImages.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageContent.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageContent.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageContent.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageContent.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageElement.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageElement.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageElement.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageElement.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageReceipt.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageReceipt.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageReceipt.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGMessageReceipt.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGReference.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGReference.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGReference.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGReference.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGReplyTo.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGReplyTo.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGReplyTo.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGReplyTo.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/SendingMessageParser.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/SendingMessageParser.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/SendingMessageParser.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/SendingMessageParser.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/StandardMessageParsers.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/StandardMessageParsers.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/StandardMessageParsers.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/StandardMessageParsers.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/annotations.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/annotations.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/annotations.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/annotations.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGGuildRole.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGGuildRole.kt similarity index 94% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGGuildRole.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGGuildRole.kt index 5c69aaa7..df6bd96f 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGGuildRole.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGGuildRole.kt @@ -17,6 +17,7 @@ package love.forte.simbot.component.qguild.role +import love.forte.simbot.ability.DeleteFailureException import love.forte.simbot.ability.DeleteOption import love.forte.simbot.ability.DeleteSupport import love.forte.simbot.common.collectable.Collectable @@ -107,7 +108,9 @@ public interface QGGuildRole : QGRole, DeleteSupport { /** * 删除频道下对应的角色(身份组)。 * - * @throws QQGuildApiException API异常,例如没有权限或对象不存在 + * @throws NoSuchElementException 没有供删除的目标 + * @throws DeleteFailureException 删除失败,通常来自 [QQGuildApiException] (cause 中) + * @throws Exception 其他非API或参数校验而产生的异常 */ @ST override suspend fun delete(vararg options: DeleteOption) diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGMemberRole.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGMemberRole.kt similarity index 96% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGMemberRole.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGMemberRole.kt index 84ae42fb..901504ec 100644 --- a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGMemberRole.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGMemberRole.kt @@ -59,5 +59,5 @@ public interface QGMemberRole : QGRole, DeleteSupport { * @throws QQGuildApiException API请求错误,例如无权限 */ @ST - public suspend fun delete(channelId: ID, vararg options: DeleteOption): Boolean + public suspend fun delete(channelId: ID?, vararg options: DeleteOption) } diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRole.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRole.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRole.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRole.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRoleCreator.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRoleCreator.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRoleCreator.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRoleCreator.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRoleUpdater.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRoleUpdater.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRoleUpdater.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/role/QGRoleUpdater.kt diff --git a/simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/utils/TimestampUtil.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/utils/TimestampUtil.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonMain/kotlin/love/forte/simbot/component/qguild/utils/TimestampUtil.kt rename to simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/utils/TimestampUtil.kt diff --git a/simbot-component-qq-guild-core-common/src/commonTest/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt b/simbot-component-qq-guild-core/src/commonTest/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/commonTest/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt rename to simbot-component-qq-guild-core/src/commonTest/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt diff --git a/simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.js.kt b/simbot-component-qq-guild-core/src/jsMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.js.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.js.kt rename to simbot-component-qq-guild-core/src/jsMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.js.kt diff --git a/simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.js.kt b/simbot-component-qq-guild-core/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.js.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.js.kt rename to simbot-component-qq-guild-core/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.js.kt diff --git a/simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.js.kt b/simbot-component-qq-guild-core/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.js.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.js.kt rename to simbot-component-qq-guild-core/src/jsMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.js.kt diff --git a/simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.js.kt b/simbot-component-qq-guild-core/src/jsMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.js.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/jsMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.js.kt rename to simbot-component-qq-guild-core/src/jsMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.js.kt diff --git a/simbot-component-qq-guild-core-common/src/jvmMain/java/module-info.java b/simbot-component-qq-guild-core/src/jvmMain/java/module-info.java similarity index 100% rename from simbot-component-qq-guild-core-common/src/jvmMain/java/module-info.java rename to simbot-component-qq-guild-core/src/jvmMain/java/module-info.java diff --git a/simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.jvm.kt b/simbot-component-qq-guild-core/src/jvmMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.jvm.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.jvm.kt rename to simbot-component-qq-guild-core/src/jvmMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.jvm.kt diff --git a/simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.jvm.kt b/simbot-component-qq-guild-core/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.jvm.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.jvm.kt rename to simbot-component-qq-guild-core/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.jvm.kt diff --git a/simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.jvm.kt b/simbot-component-qq-guild-core/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.jvm.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.jvm.kt rename to simbot-component-qq-guild-core/src/jvmMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.jvm.kt diff --git a/simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.jvm.kt b/simbot-component-qq-guild-core/src/jvmMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.jvm.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/jvmMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.jvm.kt rename to simbot-component-qq-guild-core/src/jvmMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.jvm.kt diff --git a/simbot-component-qq-guild-core-common/src/jvmMain/resources/META-INF/services/love.forte.simbot.component.ComponentFactoryProvider b/simbot-component-qq-guild-core/src/jvmMain/resources/META-INF/services/love.forte.simbot.component.ComponentFactoryProvider similarity index 100% rename from simbot-component-qq-guild-core-common/src/jvmMain/resources/META-INF/services/love.forte.simbot.component.ComponentFactoryProvider rename to simbot-component-qq-guild-core/src/jvmMain/resources/META-INF/services/love.forte.simbot.component.ComponentFactoryProvider diff --git a/simbot-component-qq-guild-core-common/src/jvmMain/resources/META-INF/services/love.forte.simbot.plugin.PluginFactoryProvider b/simbot-component-qq-guild-core/src/jvmMain/resources/META-INF/services/love.forte.simbot.plugin.PluginFactoryProvider similarity index 100% rename from simbot-component-qq-guild-core-common/src/jvmMain/resources/META-INF/services/love.forte.simbot.plugin.PluginFactoryProvider rename to simbot-component-qq-guild-core/src/jvmMain/resources/META-INF/services/love.forte.simbot.plugin.PluginFactoryProvider diff --git a/simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.native.kt b/simbot-component-qq-guild-core/src/nativeMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.native.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.native.kt rename to simbot-component-qq-guild-core/src/nativeMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.native.kt diff --git a/simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.native.kt b/simbot-component-qq-guild-core/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.native.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.native.kt rename to simbot-component-qq-guild-core/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.native.kt diff --git a/simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.native.kt b/simbot-component-qq-guild-core/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.native.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.native.kt rename to simbot-component-qq-guild-core/src/nativeMain/kotlin/love/forte/simbot/component/qguild/bot/config/TicketConfiguration.native.kt diff --git a/simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.native.kt b/simbot-component-qq-guild-core/src/nativeMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.native.kt similarity index 100% rename from simbot-component-qq-guild-core-common/src/nativeMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.native.kt rename to simbot-component-qq-guild-core/src/nativeMain/kotlin/love/forte/simbot/component/qguild/message/ImageParser.native.kt diff --git a/simbot-component-qq-guild-core-common/build.gradle.kts b/tests/application-test/build.gradle.kts similarity index 82% rename from simbot-component-qq-guild-core-common/build.gradle.kts rename to tests/application-test/build.gradle.kts index c173301e..a1d21a26 100644 --- a/simbot-component-qq-guild-core-common/build.gradle.kts +++ b/tests/application-test/build.gradle.kts @@ -26,14 +26,13 @@ plugins { `simbot-tcg-suspend-transform-configure` } -configJavaCompileWithModule("simbot.component.qqguild.core") +configJavaCompileWithModule() apply(plugin = "qq-guild-multiplatform-maven-publish") configJsTestTasks() kotlin { explicitApi() - applyDefaultHierarchyTemplate() sourceSets.configureEach { languageSettings { @@ -45,6 +44,7 @@ kotlin { js(IR) { configJs() + binaries.executable() } applyTier1() @@ -53,15 +53,9 @@ kotlin { sourceSets { commonMain.dependencies { - api(libs.simbot.api) - api(project(":simbot-component-qq-guild-stdlib")) - compileOnly(libs.simbot.common.annotations) - // ktor - api(libs.ktor.client.contentNegotiation) - api(libs.ktor.serialization.kotlinxJson) - api(libs.ktor.client.ws) - // datetime - api(libs.kotlinx.datetime) + api(libs.simbot.core) + api(project(":simbot-component-qq-guild-core")) +// compileOnly(libs.simbot.common.annotations) } commonTest.dependencies { diff --git a/tests/application-test/src/commonMain/kotlin/TestMain.kt b/tests/application-test/src/commonMain/kotlin/TestMain.kt new file mode 100644 index 00000000..236efcc1 --- /dev/null +++ b/tests/application-test/src/commonMain/kotlin/TestMain.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + + +public suspend fun main() { + +} From 75fa464492e345bae4b680a0dbf874ed16b0dabb Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 15 Jan 2024 22:07:57 +0800 Subject: [PATCH 14/71] =?UTF-8?q?=E4=B8=80=E4=BA=9B=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=B9=8B=E7=B1=BB=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 2 +- buildSrc/src/main/kotlin/P.kt | 6 - ...ncent-guild.changelog-generator.gradle.kts | 54 +- gradle/libs.versions.toml | 5 +- kotlin-js-store/yarn.lock | 903 +++++++++++++++++- settings.gradle.kts | 1 + .../forte/simbot/qguild/api/ApiRequests.kt | 5 +- .../component/qguild/QQGuildComponent.kt | 5 + .../component/qguild/QQGuildInitException.kt | 3 + .../qguild/event/QGUnsupportedEvent.kt | 2 +- .../qguild/internal/bot/QGBotImpl.kt | 39 +- .../qguild/internal/bot/QGEventProcess.kt | 192 ++++ .../internal/event/QGUnsupportedEventImpl.kt | 2 + tests/application-test/build.gradle.kts | 29 +- .../src/commonMain/kotlin/TestMain.kt | 76 +- .../resources/simbot-logger-slf4j.properties | 1 + .../src/jsMain/kotlin/TestMain.js.kt | 59 ++ .../src/jvmMain/kotlin/TestMain.jvm.kt | 53 + .../src/nativeMain/kotlin/TestMain.native.kt | 61 ++ tests/plugin-test/build.gradle.kts | 63 ++ .../src/commonMain/kotlin/FooComponent.kt | 74 ++ tests/spring-boot-test/build.gradle.kts | 77 ++ .../src/main/resources/application.yml | 6 + .../main/resources/simbot-bots/test.bot.json | 11 + 24 files changed, 1658 insertions(+), 71 deletions(-) create mode 100644 simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QGEventProcess.kt create mode 100644 tests/application-test/src/commonMain/resources/simbot-logger-slf4j.properties create mode 100644 tests/application-test/src/jsMain/kotlin/TestMain.js.kt create mode 100644 tests/application-test/src/jvmMain/kotlin/TestMain.jvm.kt create mode 100644 tests/application-test/src/nativeMain/kotlin/TestMain.native.kt create mode 100644 tests/plugin-test/build.gradle.kts create mode 100644 tests/plugin-test/src/commonMain/kotlin/FooComponent.kt create mode 100644 tests/spring-boot-test/build.gradle.kts create mode 100644 tests/spring-boot-test/src/main/resources/application.yml create mode 100644 tests/spring-boot-test/src/main/resources/simbot-bots/test.bot.json diff --git a/build.gradle.kts b/build.gradle.kts index fe1b9603..fbf676e5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -48,7 +48,7 @@ allprojects { snapshotsOnly() } } - mavenLocal() + //mavenLocal() } // // configurations.all { diff --git a/buildSrc/src/main/kotlin/P.kt b/buildSrc/src/main/kotlin/P.kt index e1dc21ed..0c7da9cc 100644 --- a/buildSrc/src/main/kotlin/P.kt +++ b/buildSrc/src/main/kotlin/P.kt @@ -61,15 +61,9 @@ object P { private val baseVersion = v(4, 0, 0) - v("dev1") - //private val alphaSuffix = v("beta", 2) - val snapshotVersion = baseVersion - Version.SNAPSHOT override val version = if (isSnapshot()) snapshotVersion else baseVersion - val versionIfSnap get() = (if (isSnapshot()) snapshotVersion else version).toString() - - val VERSION: String get() = versionIfSnap.toString() - override val developers: List = developers { developer { id = "forte" diff --git a/buildSrc/src/main/kotlin/simbot-tencent-guild.changelog-generator.gradle.kts b/buildSrc/src/main/kotlin/simbot-tencent-guild.changelog-generator.gradle.kts index 2a385d59..69701064 100644 --- a/buildSrc/src/main/kotlin/simbot-tencent-guild.changelog-generator.gradle.kts +++ b/buildSrc/src/main/kotlin/simbot-tencent-guild.changelog-generator.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022-2023. ForteScarlet. + * Copyright (c) 2022-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -28,22 +28,38 @@ tasks.create("createChangelog") { } val file = File(changelogDir, "$version.md") if (!file.exists()) { - file.createNewFile() + + val libs = rootProject.extensions.getByType() + .named("libs") + + val simbotVersion = libs.findVersion("simbot").get() + val coreVersion = simbotVersion - val autoGenerateText = """ - > 对应核心版本: [**v$coreVersion**](https://github.com/simple-robot/simpler-robot/releases/tag/v$coreVersion) - - > **Warning** - > **目前版本处于 `BETA` 阶段,代表我们会尽量保证不再大面积变更API,且仍然可能存在一些未知问题、未完善的内容和落后于官方更新的内容。** - - 我们欢迎并期望着您的的[反馈](https://github.com/simple-robot/simbot-component-qq-guild/issues)或[协助](https://github.com/simple-robot/simbot-component-qq-guild/pulls), - 感谢您的贡献与支持! + val autoGenerateText = buildString { + appendLine("> 对应核心版本: [**v$coreVersion**](https://github.com/simple-robot/simpler-robot/releases/tag/v$coreVersion)\n\n") - 也欢迎您为我们献上一颗 `star`,这是对我们最大的鼓励与认可! - - """.trimIndent() + if ("dev" in version) { + appendLine( + """ + > [!warning] + > **目前版本处于 `dev` 阶段,代表此版本是一个开发预览版,可能不稳定、可能随时发生更改、且不保证可用性。** + + + """.trimIndent() + ) + } + appendLine( + """ + 我们欢迎并期望着您的的[反馈](https://github.com/simple-robot/simbot-component-qq-guild/issues)或[协助](https://github.com/simple-robot/simbot-component-qq-guild/pulls), + 感谢您的贡献与支持! + 也欢迎您为我们献上一颗 `star`,这是对我们最大的鼓励与认可! + """.trimIndent() + ) + } + + file.createNewFile() file.writeText(autoGenerateText) } } @@ -75,5 +91,15 @@ tasks.create("updateWebsiteVersionJson") { fun repoRow(moduleName: String, group: String, id: String, version: String): String { - return "| $moduleName | [$moduleName: v$version](https://repo1.maven.org/maven2/${group.replace(".", "/")}/${id.replace(".", "/")}/$version) | [$moduleName: v$version](https://search.maven.org/artifact/$group/$id/$version/jar) |" + return "| $moduleName | [$moduleName: v$version](https://repo1.maven.org/maven2/${ + group.replace( + ".", + "/" + ) + }/${ + id.replace( + ".", + "/" + ) + }/$version) | [$moduleName: v$version](https://search.maven.org/artifact/$group/$id/$version/jar) |" } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b863117e..a7599c71 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,13 +7,15 @@ ktor = "2.3.7" openjdk-jmh = "1.35" log4j = "2.20.0" atomicfu = "0.20.1" -simbot = "4.0.0-dev1" +simbot = "4.0.0-dev2" [libraries] # simbot simbot-api = { group = "love.forte.simbot", name = "simbot-api", version.ref = "simbot" } simbot-core = { group = "love.forte.simbot", name = "simbot-core", version.ref = "simbot" } +simbot-spring = { group = "love.forte.simbot", name = "simbot-core-spring-boot-starter", version.ref = "simbot" } simbot-logger = { group = "love.forte.simbot.logger", name = "simbot-logger", version.ref = "simbot" } +simbot-logger-slf4jimpl = { group = "love.forte.simbot.logger", name = "simbot-logger-slf4j2-impl", version.ref = "simbot" } simbot-common-apidefinition = { group = "love.forte.simbot.common", name = "simbot-common-apidefinition", version.ref = "simbot" } simbot-common-atomic = { group = "love.forte.simbot.common", name = "simbot-common-atomic", version.ref = "simbot" } simbot-common-core = { group = "love.forte.simbot.common", name = "simbot-common-core", version.ref = "simbot" } @@ -60,6 +62,7 @@ ktor-client-ws = { group = "io.ktor", name = "ktor-client-websockets", version.r ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" } ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio", version.ref = "ktor" } ktor-client-js = { group = "io.ktor", name = "ktor-client-js", version.ref = "ktor" } +ktor-client-java = { group = "io.ktor", name = "ktor-client-java", version.ref = "ktor" } # for linuxX64, macosX64, macosArm64, mingwX64 # see https://ktor.io/docs/http-client-engines.html#curl diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index fd7abab4..0e3eeaf5 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -75,11 +75,46 @@ resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== +"@leichtgewicht/ip-codec@^2.0.1": + version "2.0.4" + resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" + integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== + "@socket.io/component-emitter@~3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== +"@types/body-parser@*": + version "1.19.5" + resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" + integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== + dependencies: + "@types/connect" "*" + "@types/node" "*" + +"@types/bonjour@^3.5.9": + version "3.5.13" + resolved "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" + integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== + dependencies: + "@types/node" "*" + +"@types/connect-history-api-fallback@^1.3.5": + version "1.5.4" + resolved "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" + integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== + dependencies: + "@types/express-serve-static-core" "*" + "@types/node" "*" + +"@types/connect@*": + version "3.4.38" + resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" + integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== + dependencies: + "@types/node" "*" + "@types/cookie@^0.4.1": version "0.4.1" resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" @@ -118,11 +153,60 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== -"@types/json-schema@*", "@types/json-schema@^7.0.8": +"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": + version "4.17.41" + resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz#5077defa630c2e8d28aa9ffc2c01c157c305bef6" + integrity sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA== + dependencies: + "@types/node" "*" + "@types/qs" "*" + "@types/range-parser" "*" + "@types/send" "*" + +"@types/express@*", "@types/express@^4.17.13": + version "4.17.21" + resolved "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" + integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== + dependencies: + "@types/body-parser" "*" + "@types/express-serve-static-core" "^4.17.33" + "@types/qs" "*" + "@types/serve-static" "*" + +"@types/http-errors@*": + version "2.0.4" + resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" + integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== + +"@types/http-proxy@^1.17.8": + version "1.17.14" + resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz#57f8ccaa1c1c3780644f8a94f9c6b5000b5e2eec" + integrity sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w== + dependencies: + "@types/node" "*" + +"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== +"@types/mime@*": + version "3.0.4" + resolved "https://registry.npmjs.org/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45" + integrity sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw== + +"@types/mime@^1": + version "1.3.5" + resolved "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" + integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== + +"@types/node-forge@^1.3.0": + version "1.3.11" + resolved "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" + integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== + dependencies: + "@types/node" "*" + "@types/node@*": version "20.1.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.1.2.tgz#8fd63447e3f99aba6c3168fd2ec4580d5b97886f" @@ -135,6 +219,59 @@ dependencies: undici-types "~5.26.4" +"@types/qs@*": + version "6.9.11" + resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz#208d8a30bc507bd82e03ada29e4732ea46a6bbda" + integrity sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ== + +"@types/range-parser@*": + version "1.2.7" + resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" + integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== + +"@types/retry@0.12.0": + version "0.12.0" + resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" + integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== + +"@types/send@*": + version "0.17.4" + resolved "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" + integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== + dependencies: + "@types/mime" "^1" + "@types/node" "*" + +"@types/serve-index@^1.9.1": + version "1.9.4" + resolved "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" + integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== + dependencies: + "@types/express" "*" + +"@types/serve-static@*", "@types/serve-static@^1.13.10": + version "1.15.5" + resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz#15e67500ec40789a1e8c9defc2d32a896f05b033" + integrity sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ== + dependencies: + "@types/http-errors" "*" + "@types/mime" "*" + "@types/node" "*" + +"@types/sockjs@^0.3.33": + version "0.3.36" + resolved "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" + integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== + dependencies: + "@types/node" "*" + +"@types/ws@^8.5.1": + version "8.5.10" + resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" + integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== + dependencies: + "@types/node" "*" + "@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": version "1.11.6" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" @@ -293,7 +430,7 @@ abort-controller@3.0.0: dependencies: event-target-shim "^5.0.0" -accepts@~1.3.4: +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -311,11 +448,25 @@ acorn@^8.7.1, acorn@^8.8.2: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== +ajv-formats@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" + integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== + dependencies: + ajv "^8.0.0" + ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== +ajv-keywords@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" + integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== + dependencies: + fast-deep-equal "^3.1.3" + ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -326,11 +477,26 @@ ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +ajv@^8.0.0, ajv@^8.9.0: + version "8.12.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + ansi-colors@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== +ansi-html-community@^0.0.8: + version "0.0.8" + resolved "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" + integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" @@ -356,6 +522,11 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +array-flatten@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" + integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -366,11 +537,34 @@ base64id@2.0.0, base64id@~2.0.0: resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== +batch@0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" + integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== + binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +body-parser@1.20.1: + version "1.20.1" + resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" + integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== + dependencies: + bytes "3.1.2" + content-type "~1.0.4" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.1" + type-is "~1.6.18" + unpipe "1.0.0" + body-parser@^1.19.0: version "1.20.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" @@ -389,6 +583,14 @@ body-parser@^1.19.0: type-is "~1.6.18" unpipe "1.0.0" +bonjour-service@^1.0.11: + version "1.2.1" + resolved "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz#eb41b3085183df3321da1264719fbada12478d02" + integrity sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw== + dependencies: + fast-deep-equal "^3.1.3" + multicast-dns "^7.2.5" + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -431,6 +633,11 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +bytes@3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" + integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== + bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" @@ -463,7 +670,7 @@ chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chokidar@3.5.3, chokidar@^3.5.1: +chokidar@3.5.3, chokidar@^3.5.1, chokidar@^3.5.3: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -513,7 +720,7 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^2.0.14: +colorette@^2.0.10, colorette@^2.0.14: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== @@ -528,11 +735,36 @@ commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +compressible@~2.0.16: + version "2.0.18" + resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" + integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== + dependencies: + mime-db ">= 1.43.0 < 2" + +compression@^1.7.4: + version "1.7.4" + resolved "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" + integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== + dependencies: + accepts "~1.3.5" + bytes "3.0.0" + compressible "~2.0.16" + debug "2.6.9" + on-headers "~1.0.2" + safe-buffer "5.1.2" + vary "~1.1.2" + concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +connect-history-api-fallback@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" + integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== + connect@^3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" @@ -543,16 +775,38 @@ connect@^3.7.0: parseurl "~1.3.3" utils-merge "1.0.1" -content-type@~1.0.5: +content-disposition@0.5.4: + version "0.5.4" + resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" + integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== + dependencies: + safe-buffer "5.2.1" + +content-type@~1.0.4, content-type@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== +cookie-signature@1.0.6: + version "1.0.6" + resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" + integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== + +cookie@0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" + integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== + cookie@~0.4.1: version "0.4.2" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +core-util-is@~1.0.0: + version "1.0.3" + resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" + integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== + cors@~2.8.5: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" @@ -587,7 +841,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4.3.4, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: +debug@4.3.4, debug@^4.1.0, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -599,6 +853,13 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== +default-gateway@^6.0.3: + version "6.0.3" + resolved "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" + integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== + dependencies: + execa "^5.0.0" + define-data-property@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" @@ -608,16 +869,31 @@ define-data-property@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== +depd@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" + integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== + destroy@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== +detect-node@^2.0.4: + version "2.1.0" + resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" + integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== + di@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" @@ -628,6 +904,13 @@ diff@5.0.0: resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== +dns-packet@^5.2.2: + version "5.6.1" + resolved "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" + integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== + dependencies: + "@leichtgewicht/ip-codec" "^2.0.1" + dom-serialize@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" @@ -742,6 +1025,11 @@ estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== +etag@~1.8.1: + version "1.8.1" + resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" + integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== + event-target-shim@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" @@ -757,12 +1045,64 @@ events@^3.2.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +express@^4.17.3: + version "4.18.2" + resolved "https://registry.npmjs.org/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" + integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== + dependencies: + accepts "~1.3.8" + array-flatten "1.1.1" + body-parser "1.20.1" + content-disposition "0.5.4" + content-type "~1.0.4" + cookie "0.5.0" + cookie-signature "1.0.6" + debug "2.6.9" + depd "2.0.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + finalhandler "1.2.0" + fresh "0.5.2" + http-errors "2.0.0" + merge-descriptors "1.0.1" + methods "~1.1.2" + on-finished "2.4.1" + parseurl "~1.3.3" + path-to-regexp "0.1.7" + proxy-addr "~2.0.7" + qs "6.11.0" + range-parser "~1.2.1" + safe-buffer "5.2.1" + send "0.18.0" + serve-static "1.15.0" + setprototypeof "1.2.0" + statuses "2.0.1" + type-is "~1.6.18" + utils-merge "1.0.1" + vary "~1.1.2" + extend@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -fast-deep-equal@^3.1.1: +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -777,6 +1117,13 @@ fastest-levenshtein@^1.0.12: resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== +faye-websocket@^0.11.3: + version "0.11.4" + resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" + integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== + dependencies: + websocket-driver ">=0.5.1" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -797,6 +1144,19 @@ finalhandler@1.1.2: statuses "~1.5.0" unpipe "~1.0.0" +finalhandler@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" + integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "2.4.1" + parseurl "~1.3.3" + statuses "2.0.1" + unpipe "~1.0.0" + find-up@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" @@ -833,6 +1193,16 @@ format-util@^1.0.5: resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== +forwarded@0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" + integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + +fresh@0.5.2: + version "0.5.2" + resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" + integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== + fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -842,6 +1212,11 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" +fs-monkey@^1.0.4: + version "1.0.5" + resolved "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz#fe450175f0db0d7ea758102e1d84096acb925788" + integrity sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -872,6 +1247,11 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" +get-stream@^6.0.0: + version "6.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -920,6 +1300,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10, resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== +handle-thing@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" + integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== + has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" @@ -954,6 +1339,26 @@ he@1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +hpack.js@^2.1.6: + version "2.1.6" + resolved "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" + integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== + dependencies: + inherits "^2.0.1" + obuf "^1.0.0" + readable-stream "^2.0.1" + wbuf "^1.1.0" + +html-entities@^2.3.2: + version "2.4.0" + resolved "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz#edd0cee70402584c8c76cc2c0556db09d1f45061" + integrity sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ== + +http-deceiver@^1.2.7: + version "1.2.7" + resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" + integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== + http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -965,6 +1370,32 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" +http-errors@~1.6.2: + version "1.6.3" + resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" + integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== + dependencies: + depd "~1.1.2" + inherits "2.0.3" + setprototypeof "1.1.0" + statuses ">= 1.4.0 < 2" + +http-parser-js@>=0.5.1: + version "0.5.8" + resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" + integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== + +http-proxy-middleware@^2.0.3: + version "2.0.6" + resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" + integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== + dependencies: + "@types/http-proxy" "^1.17.8" + http-proxy "^1.18.1" + is-glob "^4.0.1" + is-plain-obj "^3.0.0" + micromatch "^4.0.2" + http-proxy@^1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" @@ -974,6 +1405,11 @@ http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -1004,16 +1440,31 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +inherits@2.0.3: + version "2.0.3" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== + interpret@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== +ipaddr.js@1.9.1: + version "1.9.1" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" + integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + +ipaddr.js@^2.0.1: + version "2.1.0" + resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f" + integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ== + is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -1028,6 +1479,11 @@ is-core-module@^2.13.0: dependencies: hasown "^2.0.0" +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -1055,6 +1511,11 @@ is-plain-obj@^2.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== +is-plain-obj@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" + integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== + is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -1062,11 +1523,28 @@ is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== + isbinaryfile@^4.0.8: version "4.0.10" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" @@ -1108,6 +1586,11 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -1180,6 +1663,14 @@ kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== +launch-editor@^2.6.0: + version "2.6.1" + resolved "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz#f259c9ef95cbc9425620bbbd14b468fcdb4ffe3c" + integrity sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw== + dependencies: + picocolors "^1.0.0" + shell-quote "^1.8.1" + loader-runner@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" @@ -1228,28 +1719,68 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== +memfs@^3.4.3: + version "3.6.0" + resolved "https://registry.npmjs.org/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6" + integrity sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ== + dependencies: + fs-monkey "^1.0.4" + +merge-descriptors@1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" + integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== + merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -mime-db@1.52.0: +methods@~1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" + integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== + +micromatch@^4.0.2: + version "4.0.5" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" +mime@1.6.0: + version "1.6.0" + resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + mime@^2.5.2: version "2.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +minimalistic-assert@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" + integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== + minimatch@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" @@ -1318,6 +1849,14 @@ ms@2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +multicast-dns@^7.2.5: + version "7.2.5" + resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" + integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== + dependencies: + dns-packet "^5.2.2" + thunky "^1.0.2" + nanoid@3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" @@ -1340,6 +1879,11 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" +node-forge@^1: + version "1.3.1" + resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" + integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== + node-releases@^2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" @@ -1350,6 +1894,13 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + object-assign@^4: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -1360,6 +1911,11 @@ object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== +obuf@^1.0.0, obuf@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" + integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== + on-finished@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" @@ -1374,6 +1930,11 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" +on-headers@~1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" + integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== + once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -1381,6 +1942,22 @@ once@^1.3.0: dependencies: wrappy "1" +onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +open@^8.0.9: + version "8.4.2" + resolved "https://registry.npmjs.org/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -1409,12 +1986,20 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" +p-retry@^4.5.0: + version "4.6.2" + resolved "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" + integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== + dependencies: + "@types/retry" "0.12.0" + retry "^0.13.1" + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -parseurl@~1.3.3: +parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -1429,7 +2014,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^3.1.0: +path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -1439,12 +2024,17 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-to-regexp@0.1.7: + version "0.1.7" + resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" + integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -1456,6 +2046,19 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +process-nextick-args@~2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" + integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== + +proxy-addr@~2.0.7: + version "2.0.7" + resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" + integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + dependencies: + forwarded "0.2.0" + ipaddr.js "1.9.1" + punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" @@ -1480,11 +2083,21 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -range-parser@^1.2.1: +range-parser@^1.2.1, range-parser@~1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== +raw-body@2.5.1: + version "2.5.1" + resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" + integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + raw-body@2.5.2: version "2.5.2" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" @@ -1495,6 +2108,28 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" +readable-stream@^2.0.1: + version "2.3.8" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" + integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.3" + isarray "~1.0.0" + process-nextick-args "~2.0.0" + safe-buffer "~5.1.1" + string_decoder "~1.1.1" + util-deprecate "~1.0.1" + +readable-stream@^3.0.6: + version "3.6.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -1514,6 +2149,11 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -1540,6 +2180,11 @@ resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + rfdc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" @@ -1552,7 +2197,12 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -safe-buffer@^5.1.0: +safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: + version "5.1.2" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" + integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -1571,6 +2221,48 @@ schema-utils@^3.1.1, schema-utils@^3.1.2: ajv "^6.12.5" ajv-keywords "^3.5.2" +schema-utils@^4.0.0: + version "4.2.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" + integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== + dependencies: + "@types/json-schema" "^7.0.9" + ajv "^8.9.0" + ajv-formats "^2.1.1" + ajv-keywords "^5.1.0" + +select-hose@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" + integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== + +selfsigned@^2.1.1: + version "2.4.1" + resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" + integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== + dependencies: + "@types/node-forge" "^1.3.0" + node-forge "^1" + +send@0.18.0: + version "0.18.0" + resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" + integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== + dependencies: + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + encodeurl "~1.0.2" + escape-html "~1.0.3" + etag "~1.8.1" + fresh "0.5.2" + http-errors "2.0.0" + mime "1.6.0" + ms "2.1.3" + on-finished "2.4.1" + range-parser "~1.2.1" + statuses "2.0.1" + serialize-javascript@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" @@ -1585,6 +2277,29 @@ serialize-javascript@^6.0.1: dependencies: randombytes "^2.1.0" +serve-index@^1.9.1: + version "1.9.1" + resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" + integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== + dependencies: + accepts "~1.3.4" + batch "0.6.1" + debug "2.6.9" + escape-html "~1.0.3" + http-errors "~1.6.2" + mime-types "~2.1.17" + parseurl "~1.3.2" + +serve-static@1.15.0: + version "1.15.0" + resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" + integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== + dependencies: + encodeurl "~1.0.2" + escape-html "~1.0.3" + parseurl "~1.3.3" + send "0.18.0" + set-function-length@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" @@ -1595,6 +2310,11 @@ set-function-length@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" +setprototypeof@1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" + integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== + setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -1619,6 +2339,11 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== +shell-quote@^1.8.1: + version "1.8.1" + resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" + integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== + side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -1628,6 +2353,11 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +signal-exit@^3.0.3: + version "3.0.7" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + socket.io-adapter@~2.5.2: version "2.5.2" resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz#5de9477c9182fdc171cd8c8364b9a8894ec75d12" @@ -1656,6 +2386,15 @@ socket.io@^4.4.1: socket.io-adapter "~2.5.2" socket.io-parser "~4.2.4" +sockjs@^0.3.24: + version "0.3.24" + resolved "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" + integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== + dependencies: + faye-websocket "^0.11.3" + uuid "^8.3.2" + websocket-driver "^0.7.4" + source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" @@ -1683,12 +2422,35 @@ source-map@^0.6.0, source-map@^0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +spdy-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" + integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== + dependencies: + debug "^4.1.0" + detect-node "^2.0.4" + hpack.js "^2.1.6" + obuf "^1.1.2" + readable-stream "^3.0.6" + wbuf "^1.7.3" + +spdy@^4.0.2: + version "4.0.2" + resolved "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" + integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== + dependencies: + debug "^4.1.0" + handle-thing "^2.0.0" + http-deceiver "^1.2.7" + select-hose "^2.0.0" + spdy-transport "^3.0.0" + statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -statuses@~1.5.0: +"statuses@>= 1.4.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== @@ -1711,6 +2473,20 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -1718,6 +2494,11 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + strip-json-comments@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -1768,6 +2549,11 @@ terser@^5.26.0: commander "^2.20.0" source-map-support "~0.5.20" +thunky@^1.0.2: + version "1.1.0" + resolved "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" + integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== + tmp@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" @@ -1840,12 +2626,22 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" +util-deprecate@^1.0.1, util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== -vary@^1: +uuid@^8.3.2: + version "8.3.2" + resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" + integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + +vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== @@ -1863,6 +2659,13 @@ watchpack@^2.4.0: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" +wbuf@^1.1.0, wbuf@^1.7.3: + version "1.7.3" + resolved "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" + integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== + dependencies: + minimalistic-assert "^1.0.0" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -1887,6 +2690,53 @@ webpack-cli@5.1.0: rechoir "^0.8.0" webpack-merge "^5.7.3" +webpack-dev-middleware@^5.3.1: + version "5.3.3" + resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f" + integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA== + dependencies: + colorette "^2.0.10" + memfs "^3.4.3" + mime-types "^2.1.31" + range-parser "^1.2.1" + schema-utils "^4.0.0" + +webpack-dev-server@4.15.0: + version "4.15.0" + resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.0.tgz#87ba9006eca53c551607ea0d663f4ae88be7af21" + integrity sha512-HmNB5QeSl1KpulTBQ8UT4FPrByYyaLxpJoQ0+s7EvUrMc16m0ZS1sgb1XGqzmgCPk0c9y+aaXxn11tbLzuM7NQ== + dependencies: + "@types/bonjour" "^3.5.9" + "@types/connect-history-api-fallback" "^1.3.5" + "@types/express" "^4.17.13" + "@types/serve-index" "^1.9.1" + "@types/serve-static" "^1.13.10" + "@types/sockjs" "^0.3.33" + "@types/ws" "^8.5.1" + ansi-html-community "^0.0.8" + bonjour-service "^1.0.11" + chokidar "^3.5.3" + colorette "^2.0.10" + compression "^1.7.4" + connect-history-api-fallback "^2.0.0" + default-gateway "^6.0.3" + express "^4.17.3" + graceful-fs "^4.2.6" + html-entities "^2.3.2" + http-proxy-middleware "^2.0.3" + ipaddr.js "^2.0.1" + launch-editor "^2.6.0" + open "^8.0.9" + p-retry "^4.5.0" + rimraf "^3.0.2" + schema-utils "^4.0.0" + selfsigned "^2.1.1" + serve-index "^1.9.1" + sockjs "^0.3.24" + spdy "^4.0.2" + webpack-dev-middleware "^5.3.1" + ws "^8.13.0" + webpack-merge@^4.1.5: version "4.2.2" resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" @@ -1938,6 +2788,20 @@ webpack@5.82.0: watchpack "^2.4.0" webpack-sources "^3.2.3" +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: + version "0.7.4" + resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" + integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== + dependencies: + http-parser-js ">=0.5.1" + safe-buffer ">=5.1.0" + websocket-extensions ">=0.1.1" + +websocket-extensions@>=0.1.1: + version "0.1.4" + resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" + integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -1989,6 +2853,11 @@ ws@8.5.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== +ws@^8.13.0: + version "8.16.0" + resolved "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" + integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== + ws@~8.11.0: version "8.11.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" diff --git a/settings.gradle.kts b/settings.gradle.kts index 3fbd6191..307042b2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -28,4 +28,5 @@ include(":simbot-component-qq-guild-core") // tests include(":tests:application-test") include(":tests:spring-boot-test") +include(":tests:plugin-test") diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt index 885b0b1c..0442d987 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt @@ -70,6 +70,9 @@ public suspend fun QQGuildApi.request( appendAll(api.headers) } } + if (!contains(HttpHeaders.ContentType)) { + contentType(ContentType.Application.Json) + } } url { @@ -112,7 +115,7 @@ public suspend fun QQGuildApi.request( } } - apiLogger.debug("[{} /{}] =====> {}, body: {}", method.logName, url.encodedPath, url.host, api.body) + apiLogger.debug("[{} {}] =====> {}, body: {}", method.logName, url.encodedPath, url.host, api.body) } } diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt index ea7b9028..1de71cc1 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt @@ -26,6 +26,7 @@ import love.forte.simbot.common.function.ConfigurerFunction import love.forte.simbot.common.function.invokeBy import love.forte.simbot.component.* import love.forte.simbot.component.qguild.bot.config.QGBotFileConfiguration +import love.forte.simbot.component.qguild.bot.config.TicketConfiguration import love.forte.simbot.component.qguild.message.* import love.forte.simbot.message.At import love.forte.simbot.message.Message @@ -131,6 +132,10 @@ public class QQGuildComponent : Component { serializableBotConfigurationPolymorphic { subclass(QGBotFileConfiguration.serializer()) } + + polymorphic(TicketConfiguration::class) { + defaultDeserializer { TicketConfiguration.Plain.serializer() } + } } diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildInitException.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildInitException.kt index 02cb8fa2..ffcc2452 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildInitException.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildInitException.kt @@ -17,6 +17,9 @@ package love.forte.simbot.component.qguild +import love.forte.simbot.component.qguild.bot.QGBot +import love.forte.simbot.component.qguild.guild.QGGuild + /** * 用于当 [QGGuild] 或 [QGBot] 等类型实现类内部初始化信息过程中出现的异常。 * @author ForteScarlet diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt index 6378e591..2565cd49 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGUnsupportedEvent.kt @@ -44,7 +44,7 @@ import love.forte.simbot.qguild.event.Signal * @author ForteScarlet */ @FragileSimbotAPI -public abstract class QGUnsupportedEvent : QGEvent() { +public abstract class QGUnsupportedEvent : QGBotEvent() { /** * 事件ID。一个随机ID。 */ diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QGBotImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QGBotImpl.kt index 866bc7ea..e6ab6ac7 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QGBotImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QGBotImpl.kt @@ -36,6 +36,7 @@ import love.forte.simbot.component.qguild.channel.* import love.forte.simbot.component.qguild.guild.QGGuild import love.forte.simbot.component.qguild.guild.QGGuildRelation import love.forte.simbot.component.qguild.internal.channel.* +import love.forte.simbot.component.qguild.internal.event.QGBotStartedEventImpl import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl.Companion.qgGuild import love.forte.simbot.component.qguild.internal.guild.QGMemberImpl @@ -46,6 +47,7 @@ import love.forte.simbot.component.qguild.internal.role.toMemberRole import love.forte.simbot.component.qguild.message.QGMessageReceipt import love.forte.simbot.component.qguild.message.sendMessage import love.forte.simbot.event.EventDispatcher +import love.forte.simbot.event.onEachError import love.forte.simbot.logger.LoggerFactory import love.forte.simbot.message.Message import love.forte.simbot.message.MessageContent @@ -62,6 +64,7 @@ import love.forte.simbot.qguild.ifNotFoundThenNull import love.forte.simbot.qguild.model.ChannelType import love.forte.simbot.qguild.model.SimpleChannel import love.forte.simbot.qguild.model.SimpleGuild +import love.forte.simbot.qguild.stdlib.DisposableHandle import love.forte.simbot.qguild.stdlib.requestDataBy import kotlin.concurrent.Volatile import kotlin.coroutines.CoroutineContext @@ -75,10 +78,10 @@ import love.forte.simbot.qguild.stdlib.Bot as StdlibBot internal class QGBotImpl( override val source: StdlibBot, override val component: QQGuildComponent, - private val eventDispatcher: EventDispatcher, + internal val eventDispatcher: EventDispatcher, configuration: QGBotComponentConfiguration ) : QGBot, JobBasedBot() { - private val logger = + internal val logger = LoggerFactory.getLogger("love.forte.simbot.component.qguild.bot.${source.ticket.secret}") override val job: Job @@ -358,19 +361,26 @@ internal class QGBotImpl( */ override suspend fun start() { startLock.withLock { + sourceListenerDisposableHandle?.also { handle -> + handle.dispose() + sourceListenerDisposableHandle = null + } + + sourceListenerDisposableHandle = registerEventProcessor() + source.start().also { - // just set everytime. + // set everytime. botSelf = me().also { me -> logger.debug("bot own information: {}", me) } - suspend fun pushStartedEvent() { - // TODO -// if (eventDispatcher.isProcessable(QGBotStartedEvent)) { -// launch { -// eventProcessor.push(QGBotStartedEventImpl(this@QGBotImpl)) -// } -// } + fun pushStartedEvent() { + launch { + val event = QGBotStartedEventImpl(this@QGBotImpl) + eventDispatcher.push(event) + .onEachError { e -> logger.error("Event {} process failed: {}", event, e, e.content) } + .collect() + } } if (!isStarted) { @@ -378,16 +388,11 @@ internal class QGBotImpl( return@also } - sourceListenerDisposableHandle?.also { handle -> - handle.dispose() - sourceListenerDisposableHandle = null - } - // TODO -// sourceListenerDisposableHandle = registerEventProcessor() + + isStarted = true pushStartedEvent() } - isStarted = true } } diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QGEventProcess.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QGEventProcess.kt new file mode 100644 index 00000000..afe3050c --- /dev/null +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QGEventProcess.kt @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package love.forte.simbot.component.qguild.internal.bot + +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch +import love.forte.simbot.common.id.StringID.Companion.ID +import love.forte.simbot.component.qguild.internal.channel.QGTextChannelImpl +import love.forte.simbot.component.qguild.internal.channel.toTextChannel +import love.forte.simbot.component.qguild.internal.event.* +import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl +import love.forte.simbot.component.qguild.internal.guild.QGGuildImpl.Companion.qgGuild +import love.forte.simbot.component.qguild.internal.guild.QGMemberImpl +import love.forte.simbot.event.Event +import love.forte.simbot.event.onEachError +import love.forte.simbot.qguild.api.channel.GetChannelApi +import love.forte.simbot.qguild.event.* +import love.forte.simbot.qguild.model.isCategory +import love.forte.simbot.qguild.stdlib.DisposableHandle +import love.forte.simbot.qguild.stdlib.requestDataBy + +private fun QGBotImpl.guild0(eventGuild: EventGuild): QGGuildImpl = qgGuild(this, eventGuild) + +private suspend fun QGBotImpl.channel0(eventChannel: EventChannel, currentMsgId: String? = null): QGTextChannelImpl { + val channel = GetChannelApi.create(eventChannel.id).requestDataBy(source) + return channel.toTextChannel(this, currentMsgId = currentMsgId) +} + +private fun QGBotImpl.member0(eventMember: EventMember): QGMemberImpl = + QGMemberImpl(this, eventMember, eventMember.guildId.ID) + +/** + * simbot针对QQ频道注册的时候只注册一个普通处理函数。 + */ +internal fun QGBotImpl.registerEventProcessor(): DisposableHandle { + val bot = this + return source.registerProcessor { raw -> + when (val event = this) { + //region Guild相关 + is GuildCreate -> { + val guild = guild0(event.data) + pushEvent { + QGGuildCreateEventImpl(raw, event.data, bot, guild) + } + } + + is GuildUpdate -> { + val guild = guild0(event.data) + pushEvent { + QGGuildUpdateEventImpl(raw, event.data, bot, guild) + } + } + + is GuildDelete -> { + val guild = guild0(event.data) + pushEvent { + QGGuildDeleteEventImpl(raw, event.data, bot, guild) + } + } + //endregion + + //region Channel相关 + is ChannelCreate -> { + if (event.data.type.isCategory) { + bot.logger.warn( + "Received category create event [raw={}]. report this log to issues https://github.com/simple-robot/simbot-component-qq-guild/issues/new/choose", + raw + ) + } + + val channel = channel0(event.data) + pushEvent { + QGChannelCreateEventImpl(raw, event.data, bot, channel) + } + + } + + is ChannelUpdate -> { + if (event.data.type.isCategory) { + bot.logger.warn( + "Received category update event [raw={}]. report this log to issues https://github.com/simple-robot/simbot-component-qq-guild/issues/new/choose", + raw + ) + } + val channel = channel0(event.data) + pushEvent { + QGChannelUpdateEventImpl(raw, event.data, bot, channel) + } + } + + is ChannelDelete -> { + if (event.data.type.isCategory) { + bot.logger.warn( + "Received category delete event [raw={}]. report this log to issues https://github.com/simple-robot/simbot-component-qq-guild/issues/new/choose", + raw + ) + } + + val channel = channel0(event.data) + pushEvent { + QGChannelDeleteEventImpl(raw, event.data, bot, channel) + } + } + //endregion + + //region Member相关 + is GuildMemberAdd -> { + val member = member0(event.data) + pushEvent { + QGMemberAddEventImpl(bot, raw, event.data, member) + } + } + + is GuildMemberUpdate -> { + val member = member0(event.data) + pushEvent { + QGMemberUpdateEventImpl(bot, raw, event.data, member) + } + } + + is GuildMemberRemove -> { + val member = member0(event.data) + pushEvent { + QGMemberRemoveEventImpl(bot, raw, event.data, member) + } + } + //endregion + + + // 消息 + is AtMessageCreate -> { + pushEvent { QGAtMessageCreateEventImpl(bot, raw, event.data) } + } + + // OpenForum + is OpenForumDispatch -> when (event) { + is OpenForumThreadCreate -> pushEvent { QGOpenForumThreadCreateEventImpl(bot, raw, event.data) } + is OpenForumThreadDelete -> pushEvent { QGOpenForumThreadDeleteEventImpl(bot, raw, event.data) } + is OpenForumThreadUpdate -> pushEvent { QGOpenForumThreadUpdateEventImpl(bot, raw, event.data) } + is OpenForumPostCreate -> pushEvent { QGOpenForumPostCreateEventImpl(bot, raw, event.data) } + is OpenForumPostDelete -> pushEvent { QGOpenForumPostDeleteEventImpl(bot, raw, event.data) } + is OpenForumReplyCreate -> pushEvent { QGOpenForumReplyCreateEventImpl(bot, raw, event.data) } + is OpenForumReplyDelete -> pushEvent { QGOpenForumReplyDeleteEventImpl(bot, raw, event.data) } + } + + // Forum + is ForumDispatch -> when (event) { + is ForumPostCreate -> pushEvent { QGForumPostCreateEventImpl(bot, raw, event.data) } + is ForumPostDelete -> pushEvent { QGForumPostDeleteEventImpl(bot, raw, event.data) } + is ForumReplyCreate -> pushEvent { QGForumReplyCreateEventImpl(bot, raw, event.data) } + is ForumReplyDelete -> pushEvent { QGForumReplyDeleteEventImpl(bot, raw, event.data) } + is ForumThreadCreate -> pushEvent { QGForumThreadCreateEventImpl(bot, raw, event.data) } + is ForumThreadDelete -> pushEvent { QGForumThreadDeleteEventImpl(bot, raw, event.data) } + is ForumThreadUpdate -> pushEvent { QGForumThreadUpdateEventImpl(bot, raw, event.data) } + is ForumPublishAuditResult -> pushEvent { QGForumPublishAuditResultEventImpl(bot, raw, event.data) } + } + + + // unsupported + else -> { + pushEvent { QGUnsupportedEventImpl(bot, event, raw) } + } + } + } + + +} + + +private inline fun QGBotImpl.pushEvent(crossinline block: () -> Event) { + launch { + val event = block() + eventDispatcher.push(event) + .onEachError { e -> logger.error("Event {} process failed: {}", event, e, e.content) } + .collect() + } +} diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGUnsupportedEventImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGUnsupportedEventImpl.kt index c93de96e..1a297c2b 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGUnsupportedEventImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGUnsupportedEventImpl.kt @@ -18,6 +18,7 @@ package love.forte.simbot.component.qguild.internal.event import love.forte.simbot.annotations.FragileSimbotAPI +import love.forte.simbot.component.qguild.bot.QGBot import love.forte.simbot.component.qguild.event.QGUnsupportedEvent import love.forte.simbot.qguild.event.Signal @@ -28,6 +29,7 @@ import love.forte.simbot.qguild.event.Signal */ @OptIn(FragileSimbotAPI::class) internal data class QGUnsupportedEventImpl( + override val bot: QGBot, override val sourceEventEntity: Signal.Dispatch, override val sourceEventRaw: String ) : QGUnsupportedEvent() diff --git a/tests/application-test/build.gradle.kts b/tests/application-test/build.gradle.kts index a1d21a26..c59f1433 100644 --- a/tests/application-test/build.gradle.kts +++ b/tests/application-test/build.gradle.kts @@ -21,19 +21,11 @@ import love.forte.gradle.common.kotlin.multiplatform.applyTier3 plugins { kotlin("multiplatform") - kotlin("plugin.serialization") - `qq-guild-dokka-partial-configure` - `simbot-tcg-suspend-transform-configure` } configJavaCompileWithModule() -apply(plugin = "qq-guild-multiplatform-maven-publish") - -configJsTestTasks() kotlin { - explicitApi() - sourceSets.configureEach { languageSettings { optIn("love.forte.simbot.qguild.QGInternalApi") @@ -43,14 +35,21 @@ kotlin { configKotlinJvm() js(IR) { - configJs() + browser() + nodejs() + binaries.executable() + useEsModules() } applyTier1() applyTier2() applyTier3(supportKtorClient = true) + mingwX64 { + binaries.executable() + } + sourceSets { commonMain.dependencies { api(libs.simbot.core) @@ -63,22 +62,28 @@ kotlin { implementation(libs.kotlinx.coroutines.test) } + jvmMain.dependencies { + implementation(libs.simbot.logger.slf4jimpl) +// implementation(libs.ktor.client.cio) + implementation(libs.ktor.client.java) + } + jvmTest.dependencies { - runtimeOnly(libs.ktor.client.cio) implementation(libs.log4j.api) implementation(libs.log4j.core) implementation(libs.log4j.slf4j2) } jsMain.dependencies { - api(libs.simbot.common.annotations) + implementation(libs.ktor.client.js) + implementation(libs.simbot.common.annotations) } nativeMain.dependencies { api(libs.simbot.common.annotations) } - mingwTest.dependencies { + mingwMain.dependencies { implementation(libs.ktor.client.winhttp) } } diff --git a/tests/application-test/src/commonMain/kotlin/TestMain.kt b/tests/application-test/src/commonMain/kotlin/TestMain.kt index 236efcc1..e2cf96c9 100644 --- a/tests/application-test/src/commonMain/kotlin/TestMain.kt +++ b/tests/application-test/src/commonMain/kotlin/TestMain.kt @@ -15,7 +15,81 @@ * If not, see . */ +import kotlinx.coroutines.flow.take +import love.forte.simbot.component.qguild.event.QGAtMessageCreateEvent +import love.forte.simbot.component.qguild.message.QGReplyTo +import love.forte.simbot.component.qguild.qqGuildBots +import love.forte.simbot.core.application.SimpleApplication +import love.forte.simbot.event.EventResult +import love.forte.simbot.event.listen +import love.forte.simbot.message.messagesOf +import love.forte.simbot.message.toText +import love.forte.simbot.qguild.event.EventIntentsInstances -public suspend fun main() { +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + + +fun main(vararg args: String) { + launchApp(*args) +} + +expect fun launchApp(vararg args: String) + +internal suspend fun SimpleApplication.onApp(appid: String, token: String) { + + eventDispatcher.apply { + listen { + println("event: $it") + it.bot.guildRelation.guilds(30) + .asFlow() + .take(30) + .collect { g -> + println("Guild: $g") + } + it.content().send(messagesOf("Hello!".toText(), QGReplyTo(it.messageContent.id))).also { r -> + println("sent: $r") + } + it.reply("mua!").also { r -> + println("replied: $r") + } + EventResult.empty() + } + +// listen { +// println("event: $it") +// it.reply("mua2!").also { r -> +// println("replied: $r") +// } +// EventResult.empty() +// } + } + + qqGuildBots { + register(appid, "", token) { + botConfig { + useSandboxServerUrl() + intentsValue = EventIntentsInstances.foldRight(0) { a, b -> a.intentsValue or b } + } + }.apply { + start() + println("Bot $this started") + } + } + println("App: $this") } diff --git a/tests/application-test/src/commonMain/resources/simbot-logger-slf4j.properties b/tests/application-test/src/commonMain/resources/simbot-logger-slf4j.properties new file mode 100644 index 00000000..99350e25 --- /dev/null +++ b/tests/application-test/src/commonMain/resources/simbot-logger-slf4j.properties @@ -0,0 +1 @@ +console.level.love.forte.simbot=DEBUG diff --git a/tests/application-test/src/jsMain/kotlin/TestMain.js.kt b/tests/application-test/src/jsMain/kotlin/TestMain.js.kt new file mode 100644 index 00000000..5d7a6a99 --- /dev/null +++ b/tests/application-test/src/jsMain/kotlin/TestMain.js.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +import kotlinx.coroutines.DelicateCoroutinesApi +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.promise +import love.forte.simbot.component.qguild.useQQGuild +import love.forte.simbot.core.application.launchSimpleApplication + +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +@OptIn(DelicateCoroutinesApi::class) +actual fun launchApp(vararg args: String) { + GlobalScope.promise(Dispatchers.Default) { + val app = launchSimpleApplication { + config { + coroutineContext = Dispatchers.Default + } + + useQQGuild() + } + + app.onApp(args[0], args[1]) + + app + }.then { app -> + println("Promise app: $app") + } +} diff --git a/tests/application-test/src/jvmMain/kotlin/TestMain.jvm.kt b/tests/application-test/src/jvmMain/kotlin/TestMain.jvm.kt new file mode 100644 index 00000000..89a9cb07 --- /dev/null +++ b/tests/application-test/src/jvmMain/kotlin/TestMain.jvm.kt @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import love.forte.simbot.component.qguild.useQQGuild +import love.forte.simbot.core.application.launchSimpleApplication + +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +actual fun launchApp(vararg args: String) { + runBlocking { + val app = launchSimpleApplication { + config { + coroutineContext = Dispatchers.Default + } + + useQQGuild() + } + + app.onApp(args[0], args[1]) + app.join() + } +} diff --git a/tests/application-test/src/nativeMain/kotlin/TestMain.native.kt b/tests/application-test/src/nativeMain/kotlin/TestMain.native.kt new file mode 100644 index 00000000..78f779d7 --- /dev/null +++ b/tests/application-test/src/nativeMain/kotlin/TestMain.native.kt @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import love.forte.simbot.component.qguild.useQQGuild +import love.forte.simbot.core.application.launchSimpleApplication +import kotlin.time.measureTimedValue + +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +actual fun launchApp(vararg args: String) { + println(args.joinToString(", ")) + + runBlocking { + val (app, time) = measureTimedValue { + launchSimpleApplication { + config { + coroutineContext = Dispatchers.Default + } + + useQQGuild() + }.apply { + onApp(args[0], args[1]) + } + } + + println("Launch Time: $time") + + app.join() + } +} diff --git a/tests/plugin-test/build.gradle.kts b/tests/plugin-test/build.gradle.kts new file mode 100644 index 00000000..797f0ca3 --- /dev/null +++ b/tests/plugin-test/build.gradle.kts @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +import love.forte.gradle.common.kotlin.multiplatform.applyTier1 +import love.forte.gradle.common.kotlin.multiplatform.applyTier2 +import love.forte.gradle.common.kotlin.multiplatform.applyTier3 + +plugins { + kotlin("multiplatform") +} + +configJavaCompileWithModule() + +kotlin { + sourceSets.configureEach { + languageSettings { + optIn("love.forte.simbot.qguild.QGInternalApi") + } + } + + configKotlinJvm() + + js(IR) { + browser() + nodejs() + + binaries.executable() + useEsModules() + } + + applyTier1() + applyTier2() + applyTier3(supportKtorClient = true) + + mingwX64 { + binaries.executable() + } + + sourceSets { + commonMain.dependencies { + implementation(libs.simbot.api) + implementation(libs.simbot.core) + } + + commonTest.dependencies { + implementation(kotlin("test")) + } + } +} diff --git a/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt b/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt new file mode 100644 index 00000000..157c411e --- /dev/null +++ b/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +import kotlinx.serialization.modules.EmptySerializersModule +import kotlinx.serialization.modules.SerializersModule +import love.forte.simbot.common.function.ConfigurerFunction +import love.forte.simbot.component.Component +import love.forte.simbot.component.ComponentConfigureContext +import love.forte.simbot.component.ComponentFactory +import love.forte.simbot.core.application.launchSimpleApplication +import love.forte.simbot.plugin.Plugin +import love.forte.simbot.plugin.PluginConfigureContext +import love.forte.simbot.plugin.PluginFactory + +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +class FooComponent : Component { + override val id: String = "example.foo" + override val serializersModule: SerializersModule = EmptySerializersModule() + + companion object Factory : ComponentFactory { + override val key: ComponentFactory.Key = object : ComponentFactory.Key {} + override fun create(context: ComponentConfigureContext, configurer: ConfigurerFunction): FooComponent { + return FooComponent() + } + } +} + +class FooPlugin : Plugin { + companion object Factory : PluginFactory { + override val key: PluginFactory.Key = object : PluginFactory.Key {} + + override fun create(context: PluginConfigureContext, configurer: ConfigurerFunction): FooPlugin { + return FooPlugin() + } + } +} + +suspend fun main() { + val app = launchSimpleApplication { + install(FooComponent) + install(FooPlugin) + } + +} diff --git a/tests/spring-boot-test/build.gradle.kts b/tests/spring-boot-test/build.gradle.kts new file mode 100644 index 00000000..9045133e --- /dev/null +++ b/tests/spring-boot-test/build.gradle.kts @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +/* + * Copyright (c) 2022-2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +plugins { + kotlin("jvm") + id("org.springframework.boot") version "3.2.1" + id("io.spring.dependency-management") version "1.1.4" + kotlin("plugin.spring") version "1.9.21" +} + +repositories { + mavenCentral() +} + +//configJavaCompileWithModule(jvmVersion = "17") + +kotlin { +// configKotlinJvm(jdkVersion = 17) + + dependencies { + implementation("org.springframework.boot:spring-boot-starter") + implementation(kotlin("reflect")) + implementation(project(":simbot-component-qq-guild-core")) + implementation(libs.simbot.core) + implementation(libs.simbot.spring) + implementation(libs.ktor.client.java) + } +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 +} + + +tasks.withType { + kotlinOptions { + freeCompilerArgs += "-Xjsr305=strict" + jvmTarget = "17" + } +} + +tasks.withType { + useJUnitPlatform() +} diff --git a/tests/spring-boot-test/src/main/resources/application.yml b/tests/spring-boot-test/src/main/resources/application.yml new file mode 100644 index 00000000..aa09809c --- /dev/null +++ b/tests/spring-boot-test/src/main/resources/application.yml @@ -0,0 +1,6 @@ +simbot: + application: + application-launch-mode: thread + bots: + configuration-json-resources: + - 'classpath:simbot-bots/*.bot.json' diff --git a/tests/spring-boot-test/src/main/resources/simbot-bots/test.bot.json b/tests/spring-boot-test/src/main/resources/simbot-bots/test.bot.json new file mode 100644 index 00000000..9e263189 --- /dev/null +++ b/tests/spring-boot-test/src/main/resources/simbot-bots/test.bot.json @@ -0,0 +1,11 @@ +{ + "component": "simbot.qqguild", + "ticket": { + "appId": "102051700", + "secret": "", + "token": "xH2wbDzsrTWqGk8KW81fhOCf98drwsb7" + }, + "config": { + "serverUrl": "SANDBOX" + } +} From 8de23f3bd9faf215124290a65274788bdaf84b83 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 15 Jan 2024 23:14:54 +0800 Subject: [PATCH 15/71] v4.0.0-dev1 --- .changelog/v4.0.0-dev1.md | 11 ++ .github/workflows/publish-v4-release.yml | 162 ++++++++++++++++++ .../publishToMavenLocal.run.xml | 29 ++++ 3 files changed, 202 insertions(+) create mode 100644 .changelog/v4.0.0-dev1.md create mode 100644 .github/workflows/publish-v4-release.yml create mode 100644 .run/runConfigurations/publishToMavenLocal.run.xml diff --git a/.changelog/v4.0.0-dev1.md b/.changelog/v4.0.0-dev1.md new file mode 100644 index 00000000..639b35a9 --- /dev/null +++ b/.changelog/v4.0.0-dev1.md @@ -0,0 +1,11 @@ +> 对应核心版本: [**v4.0.0-dev2**](https://github.com/simple-robot/simpler-robot/releases/tag/v4.0.0-dev2) + + +> [!warning] +> **目前版本处于 `dev` 阶段,代表此版本是一个开发预览版,可能不稳定、可能随时发生更改、且不保证可用性。** + + +我们欢迎并期望着您的的[反馈](https://github.com/simple-robot/simbot-component-qq-guild/issues)或[协助](https://github.com/simple-robot/simbot-component-qq-guild/pulls), +感谢您的贡献与支持! + +也欢迎您为我们献上一颗 `star`,这是对我们最大的鼓励与认可! diff --git a/.github/workflows/publish-v4-release.yml b/.github/workflows/publish-v4-release.yml new file mode 100644 index 00000000..dd669a72 --- /dev/null +++ b/.github/workflows/publish-v4-release.yml @@ -0,0 +1,162 @@ +name: Publish V4 Release +on: + push: + tags: + - v4.**.** + +env: + IS_CI: true + GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} + GPG_SECRET_KEY: ${{ secrets.GPG_SECRET_KEY }} + GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }} + OSSRH_USER: ${{ secrets.SONATYPE_USERNAME }} + OSSRH_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + SONATYPE_USERNAME: ${{ secrets.SONATYPE_USERNAME }} + SONATYPE_PASSWORD: ${{ secrets.SONATYPE_PASSWORD }} + GRADLE_OPTS: "-Xmx8g -Xms2g -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8" + + +jobs: + run-test-and-publish-v4: + name: Run test and publish V4 + strategy: + matrix: + os: [ macos-latest, windows-latest, ubuntu-latest ] + runs-on: ${{ matrix.os }} + steps: + # 检出仓库代码 + - name: Check Out Repo + uses: actions/checkout@v3 + + # setup Java + - name: Setup Java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: 21 + cache: 'gradle' + + # setup Gradle + - name: Gradle Run Test + uses: gradle/gradle-build-action@v2 + with: + gradle-version: 8.5 + arguments: assemble test -Porg.gradle.daemon=false + + # setup Gradle + - name: Publish Release + uses: gradle/gradle-build-action@v2 + with: + gradle-version: 8.5 + arguments: | + publishToSonatype + closeAndReleaseStagingRepository + --info + --warning-mode all + -x test + --build-cache + -Porg.gradle.jvmargs="-Xmx8g -Xms2g -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8" + -Porg.gradle.daemon=false + + env: + SIMBOT_IS_SNAPSHOT: false + SIMBOT_RELEASES_ONLY: true + + create-release: + name: Create release + runs-on: ubuntu-latest + needs: run-test-and-publish-v4 + permissions: + contents: write + steps: + # 检出仓库代码 + - name: Check Out Repo + uses: actions/checkout@v3 + + # Create gitHub release + - name: Create Github Release + uses: softprops/action-gh-release@v0.1.14 + with: + token: ${{ secrets.PUSH_TOKEN }} + body_path: .changelog/${{ github.ref_name }}.md + generate_release_notes: true + prerelease: ${{ contains(github.ref_name, 'preview') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') }} + + publish-snapshot: + name: Publish snapshot + strategy: + matrix: + os: [ macos-latest, windows-latest, ubuntu-latest ] + runs-on: ${{ matrix.os }} + needs: run-test-and-publish-v4 + steps: + # 检出仓库代码 + - name: Check out repo + uses: actions/checkout@v3 + + # setup Java + - name: Setup java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: 21 + + # setup Gradle + - name: Gradle publish snapshot + uses: gradle/gradle-build-action@v2 + with: + gradle-version: 8.5 + arguments: | + publishToSonatype + closeAndReleaseStagingRepository + --info + --warning-mode all + -x test + --build-cache + -Porg.gradle.jvmargs="-Xmx8g -Xms2g -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8" + -Porg.gradle.daemon=false + env: + SIMBOT_IS_SNAPSHOT: true + SIMBOT_SNAPSHOT_ONLY: true + + deploy-doc: + name: Deploy-doc + runs-on: ubuntu-latest + needs: run-test-and-publish-v4 + steps: + # 检出仓库代码 + - name: Check out repo + uses: actions/checkout@v3 + with: + persist-credentials: false + fetch-depth: 0 + # setup Java + - name: Setup java + uses: actions/setup-java@v3 + with: + distribution: 'zulu' + java-version: 21 + + # setup Gradle + - name: Gradle generate documentation + uses: gradle/gradle-build-action@v2 + with: + gradle-version: 8.5 + arguments: | + dokkaHtmlMultiModule + --info + --warning-mode all + -x test + --build-cache + -Porg.gradle.jvmargs="-Xmx8g -Xms2g -XX:MaxMetaspaceSize=1g -Dfile.encoding=UTF-8" + -Porg.gradle.daemon=false + + - name: Push to doc repository + uses: peaceiris/actions-gh-pages@v3 + with: + personal_token: ${{ secrets.PUSH_TOKEN }} + external_repository: simple-robot-library/simbot3-api-docs + publish_branch: kdoc-deploy/component-qq-guild-v4 + publish_dir: ./build/dokka/html + # deploy to sub dir + destination_dir: components/qq-guild-v4 diff --git a/.run/runConfigurations/publishToMavenLocal.run.xml b/.run/runConfigurations/publishToMavenLocal.run.xml new file mode 100644 index 00000000..205cbed1 --- /dev/null +++ b/.run/runConfigurations/publishToMavenLocal.run.xml @@ -0,0 +1,29 @@ + + + + + + + + true + true + false + false + + + \ No newline at end of file From 0fdf7df11687241b8b70daf8fd9434e21042bd74 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 15 Jan 2024 23:21:24 +0800 Subject: [PATCH 16/71] v4.0.0-dev1 --- settings.gradle.kts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/settings.gradle.kts b/settings.gradle.kts index 307042b2..b04cecfe 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -26,7 +26,9 @@ include(":simbot-component-qq-guild-core") //include(":simbot-component-qq-guild-benchmark") // tests -include(":tests:application-test") -include(":tests:spring-boot-test") -include(":tests:plugin-test") +if (!System.getenv("IS_CI").toBoolean()) { + include(":tests:application-test") + include(":tests:spring-boot-test") + include(":tests:plugin-test") +} From 0bc2bb60830d8942a058883e53d15af90649d8be Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Tue, 16 Jan 2024 01:42:58 +0800 Subject: [PATCH 17/71] =?UTF-8?q?=E9=83=A8=E5=88=86=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E3=80=81=E6=B8=85=E7=90=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- buildSrc/src/main/kotlin/P.kt | 2 +- .../component/qguild/QQGuildComponent.kt | 1 - .../src/commonMain/kotlin/FooComponent.kt | 34 +++++++++++++++---- .../java/JFooComponentFactoryProvider.java | 23 +++++++++++++ 4 files changed, 51 insertions(+), 9 deletions(-) create mode 100644 tests/plugin-test/src/jvmTest/java/JFooComponentFactoryProvider.java diff --git a/buildSrc/src/main/kotlin/P.kt b/buildSrc/src/main/kotlin/P.kt index 0c7da9cc..2d3eac2e 100644 --- a/buildSrc/src/main/kotlin/P.kt +++ b/buildSrc/src/main/kotlin/P.kt @@ -59,7 +59,7 @@ object P { override val homepage: String get() = HOMEPAGE - private val baseVersion = v(4, 0, 0) - v("dev1") + private val baseVersion = v(4, 0, 0) - v("dev2") val snapshotVersion = baseVersion - Version.SNAPSHOT override val version = if (isSnapshot()) snapshotVersion else baseVersion diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt index 1de71cc1..edba8262 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt @@ -164,7 +164,6 @@ public class QQGuildComponentFactoryProvider : override fun provide(): ComponentFactory<*, QQGuildComponentConfiguration> = QQGuildComponent } -// TODO add to package-info /** * 用于 [QQGuildComponentFactoryProvider.loadConfigurers] 的可加载的额外配置器。 * diff --git a/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt b/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt index 157c411e..2546d645 100644 --- a/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt +++ b/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt @@ -18,13 +18,13 @@ import kotlinx.serialization.modules.EmptySerializersModule import kotlinx.serialization.modules.SerializersModule import love.forte.simbot.common.function.ConfigurerFunction -import love.forte.simbot.component.Component -import love.forte.simbot.component.ComponentConfigureContext -import love.forte.simbot.component.ComponentFactory +import love.forte.simbot.common.function.invokeBy +import love.forte.simbot.component.* import love.forte.simbot.core.application.launchSimpleApplication import love.forte.simbot.plugin.Plugin import love.forte.simbot.plugin.PluginConfigureContext import love.forte.simbot.plugin.PluginFactory +import kotlin.jvm.JvmField /* * Copyright (c) 2024. ForteScarlet. @@ -44,17 +44,37 @@ import love.forte.simbot.plugin.PluginFactory */ class FooComponent : Component { - override val id: String = "example.foo" - override val serializersModule: SerializersModule = EmptySerializersModule() + override val id: String get() = ID_VALUE + override val serializersModule: SerializersModule get() = SerializersModule + + /** 伴生对象实现的工厂实现 */ + companion object Factory : ComponentFactory { + /** id 常量化 */ + const val ID_VALUE: String = "com.example.foo" + /** serializersModule "静态化" */ + @JvmField + val SerializersModule: SerializersModule = EmptySerializersModule() - companion object Factory : ComponentFactory { override val key: ComponentFactory.Key = object : ComponentFactory.Key {} - override fun create(context: ComponentConfigureContext, configurer: ConfigurerFunction): FooComponent { + override fun create(context: ComponentConfigureContext, configurer: ConfigurerFunction): FooComponent { + FooComponentConfiguration().invokeBy(configurer) return FooComponent() } } } +class FooComponentFactoryProvider : ComponentFactoryProvider { + override fun loadConfigurers(): Sequence>? { + return null + } + + override fun provide(): ComponentFactory<*, FooComponentConfiguration> { + return FooComponent.Factory + } +} + +class FooComponentConfiguration + class FooPlugin : Plugin { companion object Factory : PluginFactory { override val key: PluginFactory.Key = object : PluginFactory.Key {} diff --git a/tests/plugin-test/src/jvmTest/java/JFooComponentFactoryProvider.java b/tests/plugin-test/src/jvmTest/java/JFooComponentFactoryProvider.java new file mode 100644 index 00000000..314eed5f --- /dev/null +++ b/tests/plugin-test/src/jvmTest/java/JFooComponentFactoryProvider.java @@ -0,0 +1,23 @@ +import kotlin.sequences.Sequence; +import love.forte.simbot.component.ComponentFactory; +import love.forte.simbot.component.ComponentFactoryConfigurerProvider; +import love.forte.simbot.component.ComponentFactoryProvider; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author ForteScarlet + */ +public class JFooComponentFactoryProvider implements ComponentFactoryProvider { + @Nullable + @Override + public Sequence> loadConfigurers() { + return null; + } + + @NotNull + @Override + public ComponentFactory provide() { + return JFooComponent.FACTORY; + } +} From 61f0726655c8cc15a1105c357cea0cdb1e13f85d Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Tue, 16 Jan 2024 01:54:37 +0800 Subject: [PATCH 18/71] =?UTF-8?q?=E9=83=A8=E5=88=86=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E3=80=81=E6=B8=85=E7=90=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/plugin-test/src/commonMain/kotlin/FooComponent.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt b/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt index 2546d645..d411db0d 100644 --- a/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt +++ b/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt @@ -88,7 +88,9 @@ class FooPlugin : Plugin { suspend fun main() { val app = launchSimpleApplication { install(FooComponent) - install(FooPlugin) } + // Kotlin 可以直接通过扩展函数根据类型寻找目标 + val fooComponent = app.components.find() + println(fooComponent) } From 350f2a35d7c71388e4af4d5ae5a2a70b93d326e0 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Wed, 17 Jan 2024 20:18:02 +0800 Subject: [PATCH 19/71] v4.0.0-dev2 --- .changelog/v4.0.0-dev2.md | 11 + .github/workflows/publish-v4-release.yml | 2 +- Writerside/c.list | 6 + Writerside/cfg/buildprofiles.xml | 36 + Writerside/cfg/glossary.xml | 5 + Writerside/d.tree | 16 + Writerside/images/logo-icon.svg | 1205 +++++++++++++++++ Writerside/topics/API.md | 3 + Writerside/topics/ark/api_ark.md | 4 + Writerside/topics/embed/api_embed.md | 4 + Writerside/topics/forum/api_forum.md | 624 +++++++++ Writerside/topics/role/api_role.md | 4 + Writerside/topics/starter-topic.md | 1 + Writerside/v.list | 8 + Writerside/writerside.cfg | 8 + gradle/libs.versions.toml | 2 +- .../build.gradle.kts | 2 + .../component/qguild/bot/QQGuildBotManager.kt | 16 +- .../bot/config/QGBotComponentConfiguration.kt | 17 +- .../internal/bot/QQGuildBotManagerImpl.kt | 13 +- .../qguild/stdlib/EventProcessor.jvm.kt | 53 +- 21 files changed, 2019 insertions(+), 21 deletions(-) create mode 100644 .changelog/v4.0.0-dev2.md create mode 100644 Writerside/c.list create mode 100644 Writerside/cfg/buildprofiles.xml create mode 100644 Writerside/cfg/glossary.xml create mode 100644 Writerside/d.tree create mode 100644 Writerside/images/logo-icon.svg create mode 100644 Writerside/topics/API.md create mode 100644 Writerside/topics/ark/api_ark.md create mode 100644 Writerside/topics/embed/api_embed.md create mode 100644 Writerside/topics/forum/api_forum.md create mode 100644 Writerside/topics/role/api_role.md create mode 100644 Writerside/topics/starter-topic.md create mode 100644 Writerside/v.list create mode 100644 Writerside/writerside.cfg diff --git a/.changelog/v4.0.0-dev2.md b/.changelog/v4.0.0-dev2.md new file mode 100644 index 00000000..4dc01f77 --- /dev/null +++ b/.changelog/v4.0.0-dev2.md @@ -0,0 +1,11 @@ +> 对应核心版本: [**v4.0.0-dev4**](https://github.com/simple-robot/simpler-robot/releases/tag/v4.0.0-dev4) + + +> [!warning] +> **目前版本处于 `dev` 阶段,代表此版本是一个开发预览版,可能不稳定、可能随时发生更改、且不保证可用性。** + + +我们欢迎并期望着您的的[反馈](https://github.com/simple-robot/simbot-component-qq-guild/issues)或[协助](https://github.com/simple-robot/simbot-component-qq-guild/pulls), +感谢您的贡献与支持! + +也欢迎您为我们献上一颗 `star`,这是对我们最大的鼓励与认可! diff --git a/.github/workflows/publish-v4-release.yml b/.github/workflows/publish-v4-release.yml index dd669a72..3f7c9d8f 100644 --- a/.github/workflows/publish-v4-release.yml +++ b/.github/workflows/publish-v4-release.yml @@ -80,7 +80,7 @@ jobs: token: ${{ secrets.PUSH_TOKEN }} body_path: .changelog/${{ github.ref_name }}.md generate_release_notes: true - prerelease: ${{ contains(github.ref_name, 'preview') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') }} + prerelease: ${{ contains(github.ref_name, 'preview') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') || contains(github.ref_name, 'dev') }} publish-snapshot: name: Publish snapshot diff --git a/Writerside/c.list b/Writerside/c.list new file mode 100644 index 00000000..c4c77a29 --- /dev/null +++ b/Writerside/c.list @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/Writerside/cfg/buildprofiles.xml b/Writerside/cfg/buildprofiles.xml new file mode 100644 index 00000000..fd8d9c87 --- /dev/null +++ b/Writerside/cfg/buildprofiles.xml @@ -0,0 +1,36 @@ + + + + + ruby + logo-icon.svg + logo-icon.svg + https://github.com/simple-robot/simbot-component-qq-guild/ + GitHub + https://github.com/simple-robot/simbot-component-qq-guild/ + true + + true + zh-CN + false + + + + + false + + + +
+ %copyright-year% Forte Scarlet. + simbot官网手册 + 文档引导站 + 社区 + 反馈 + 组织库 + 核心库仓库 + + +
+
diff --git a/Writerside/cfg/glossary.xml b/Writerside/cfg/glossary.xml new file mode 100644 index 00000000..cb5ce8f3 --- /dev/null +++ b/Writerside/cfg/glossary.xml @@ -0,0 +1,5 @@ + + + + 组件:针对一组一个或多个「组件标识」和「插件」的统称。 + diff --git a/Writerside/d.tree b/Writerside/d.tree new file mode 100644 index 00000000..31875bda --- /dev/null +++ b/Writerside/d.tree @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/Writerside/images/logo-icon.svg b/Writerside/images/logo-icon.svg new file mode 100644 index 00000000..03a946a3 --- /dev/null +++ b/Writerside/images/logo-icon.svg @@ -0,0 +1,1205 @@ + + + + diff --git a/Writerside/topics/API.md b/Writerside/topics/API.md new file mode 100644 index 00000000..f1f3c801 --- /dev/null +++ b/Writerside/topics/API.md @@ -0,0 +1,3 @@ +# API + +Start typing here... \ No newline at end of file diff --git a/Writerside/topics/ark/api_ark.md b/Writerside/topics/ark/api_ark.md new file mode 100644 index 00000000..3cbf1edf --- /dev/null +++ b/Writerside/topics/ark/api_ark.md @@ -0,0 +1,4 @@ +# 消息 Ark + +QQ频道中有一些针对 `Ark消息` 的API。 + diff --git a/Writerside/topics/embed/api_embed.md b/Writerside/topics/embed/api_embed.md new file mode 100644 index 00000000..696ea394 --- /dev/null +++ b/Writerside/topics/embed/api_embed.md @@ -0,0 +1,4 @@ +# 消息 Embed + +QQ频道中有一些针对 `Embed消息` 的API。 + diff --git a/Writerside/topics/forum/api_forum.md b/Writerside/topics/forum/api_forum.md new file mode 100644 index 00000000..8addbf94 --- /dev/null +++ b/Writerside/topics/forum/api_forum.md @@ -0,0 +1,624 @@ +--- +switcher-label: Java API +--- + +# 论坛 Forum + +QQ频道中有一些针对 `论坛子频道` 的API。([参考文档](https://bot.q.qq.com/wiki/develop/api/openapi/forum/model.html#thread)) + + +## API + +首先是 `API` 模块中对相关API的封装类型,它们在 `love.forte.simbot.qguild.api.forum` 中: + +- `DeleteThreadApi` +- `GetThreadApi` +- `GetThreadListApi` +- `PublishThreadApi` + +使用它们的方式都差不多,我们选其中一个 `GetThreadListApi` 作为示例: + + + + +```kotlin +// QQ频道API请求用的 token +val token = "Bot xxx" + +// Ktor 的 HttpClient +// 在不同平台下请注意选择可用的引擎,比如在JS平台下使用 `JS` 引擎,windows系统平台下使用 `WinHttp` 等。 +val client = HttpClient() + +// 请求的服务器地址 +// 此处为沙箱地址,也可选择正式地址或其他第三方代理地址 +val server = QQGuild.SANDBOX_URL + +val api = GetThreadListApi.create("channel ID") +val result = api.requestData(client, server, token) + +result.threads.forEach { thread -> + // 遍历结果... +} +``` + + + + + +```java +// QQ频道API请求用的 token +String token = "Bot xxx"; + +// Ktor 的 HttpClient +// 此处选择使用 HttpClientJvmKt.HttpClient 自动加载环境中存在的 HttpClient 引擎 +// 请保证classpath中存在一个可用的 HttpClient JVM 引擎 +HttpClient client = HttpClientJvmKt.HttpClient(config -> Unit.INSTANCE); + +// 请求的服务器地址 +// 此处为沙箱地址,也可选择正式地址或其他第三方代理地址 +Url server = QQGuild.SANDBOX_URL; + +GetThreadListApi api = GetThreadListApi.create("channel ID"); +CompletableFuture result = ApiRequests.requestDataAsync(api, client, token, server); + +result.thenApply(ThreadListResult::getThreads) + .thenAccept(threads -> { + for (Thread thread : threads) { + // 遍历结果 + } + }); +``` +{switcher-key="%ja%"} + +```java +// QQ频道API请求用的 token +String token = "Bot xxx"; + +// Ktor 的 HttpClient +// 此处选择使用 HttpClientJvmKt.HttpClient 自动加载环境中存在的 HttpClient 引擎 +// 请保证classpath中存在一个可用的 HttpClient JVM 引擎 +HttpClient client = HttpClientJvmKt.HttpClient(config -> Unit.INSTANCE); + +// 请求的服务器地址 +// 此处为沙箱地址,也可选择正式地址或其他第三方代理地址 +Url server = QQGuild.SANDBOX_URL; + +GetThreadListApi api = GetThreadListApi.create("channel ID"); +ThreadListResult result =.requestDataBlocking(api, client, token, server); + +for (Thread thread : result.getThreads()) { +// 遍历结果 +} +``` +{switcher-key="%jb%"} + + + + +## 组件能力 + +在组件模块 `core` 中,也同样针对论坛子频道的相关内容提供了API。 +在组件模块中提供了一些新的类型: + +- `QGForumChannel` : 表示论坛子频道的 `Channel` 实现 +- `QGForums` : 表示一个 `QGGuild` 针对帖子的相关操作 +- `QGThread` : 表示一个主题帖 +- `QGThreadCreator` : 一个用于构造并发布帖子的构造器 + + +> 这些操作大多从 `QGGuild` 作为入口提供。 + + + + + +```kotlin +val guild: QGGuild = .... + +// 在所有的子频道中筛选出 论坛子频道 +guild.channels.asFlow() + // highlight-next-line + .filterIsInstance() + .collect { channel: QGForumChannel -> + // ... + channel.threads.collect { + // 获取所有的主题帖 + } + val thread: QGThread? = channel.thread("123".ID) // 获取指定ID的主题帖 + + // 构造并发布一个主题贴 + channel.createThread { + title = ... + content = ... + format = ... + } + + // 假设其不为null + // 删除某个主题帖 + thread!!.delete() +} +``` + +除了在 `channels` 中通过类型筛选以外,也可以通过 `QGGuild.forums` 来进行操作: + +```kotlin +val guild: QGGuild = .... + +// 在所有的子频道中筛选出 论坛子频道 +// highlight-next-line +guild.forums.forumChannels + .collect { channel: QGForumChannel -> + // ... + channel.threads.collect { + // 获取所有的主题帖 + } + val thread: QGThread? = channel.thread("123".ID) // 获取指定ID的主题帖 + // 构造并发布一个主题贴 + channel.createThread { + title = ... + content = ... + format = ... + } + // 假设其不为null + // 删除某个主题帖 + thread!!.delete() +} +// 根据ID获取指定的 论坛子频道 实例 +val forumChannel: QGForumChannel? = guild.forums.forumChannel("666".ID) + +``` + + + + +```java +QGGuild guild = ...; + +// 直接遍历 +// 你也可以选择转为列表后再操作 +guild.getChannels().collectAsync(channel -> { + // 遍历所有的论坛子频道 + // highlight-next-line + if (channel instanceof QGForumChannel forumChannel) { + // 获取所有的主题帖并遍历 + forumChannel.getThreads().collectAsync(thread -> { + // ... + }); // thenXxx? + // 获取指定ID的主题帖 + CompletableFuture threadAsync = forumChannel.getThreadAsync(Identifies.ID("123")); + // 构造并发布一个主题贴 + forumChannel.threadCreator() + .title(...) + .content(...) + .format(...) + .publishAsync(); + + threadAsync.thenAccept(thread -> { // or use thenCompose + // 假设其不为null + assert thread != null; + // 删除某个主题帖 + thread.deleteAsync(); + }); + } +}); +``` +{switcher-key="%ja%"} + +```java +QGGuild guild = ...; + +// 直接遍历 +// 你也可以选择转为列表后再操作 +guild.getChannels().collect(channel -> { + // 遍历所有的论坛子频道 + // highlight-next-line + if (channel instanceof QGForumChannel forumChannel) { + // 获取所有的主题帖并遍历 + forumChannel.getThreads().collect(thread -> { + // ... + }); + // 获取指定ID的主题帖 + QGThread thread = forumChannel.getThread(Identifies.ID("123")); // nullable + // 构造并发布一个主题贴 + forumChannel.threadCreator() + .title(...) + .content(...) + .format(...) + .publishBlocking(); + + // 假设其不为null + assert thread != null; + // 删除某个主题帖 + thread.deleteBlocking(); + } +}); +``` +{switcher-key="%jb%"} + +除了在 `channels` 中通过类型筛选以外,也可以通过 `QGGuild.forums` 来进行操作: + +```java +QGGuild guild = ...; + +// 直接遍历 +// 你也可以选择转为列表后再操作 +guild.getForums() + // highlight-next-line + .getForumChannels() + .collectAsync(forumChannel -> { + // 获取所有的主题帖并遍历 + forumChannel.getThreads().collectAsync(thread -> { + // ... + }); // thenXxx? + // 获取指定ID的主题帖 + CompletableFuture threadAsync = forumChannel.getThreadAsync(Identifies.ID("123")); + // 构造并发布一个主题贴 + forumChannel.threadCreator() + .title(...) + .content(...) + .format(...) + .publishAsync(); + threadAsync.thenAccept(thread -> { // or use thenCompose + // 假设其不为null + assert thread != null; + // 删除某个主题帖 + thread.deleteAsync(); + }); + }); +``` +{switcher-key="%ja%"} + +```java +QGGuild guild = ...; + +// 直接遍历 +// 你也可以选择转为列表后再操作 +guild.getForums() + // highlight-next-line + .getForumChannels() + .collect(forumChannel -> { + // 遍历所有的论坛子频道 + // 获取所有的主题帖并遍历 + forumChannel.getThreads().collect(thread -> { + // ... + }); + // 获取指定ID的主题帖 + QGThread thread = forumChannel.getThread(Identifies.ID("123")); // nullable + // 构造并发布一个主题贴 + forumChannel.threadCreator() + .title(...) + .content(...) + .format(...) + .publishBlocking(); + + // 假设其不为null + assert thread != null; + // 删除某个主题帖 + thread.deleteBlocking(); + }); +``` +{switcher-key="%jb%"} + + + + + +## API事件 {id="api_dispatch"} + +API模块实现了与论坛相关的事件类型,它们的类型(与继承关系)如下: + +- `OpenForumDispatch` : **开放论坛事件** + - `OpenForumThreadDispatch` : 开放论坛事件 - **主题贴事件** + - `OpenForumThreadCreate` : 主题贴事件: **主题贴创建** + - `OpenForumThreadUpdate` : 主题贴事件: **主题贴更新** + - `OpenForumThreadDelete` : 主题贴事件: **主题贴删除** + - `OpenForumPostDispatch` : 开放论坛事件 - **评论事件** + - `OpenForumPostCreate` : 评论事件 - **评论创建** + - `OpenForumPostDelete` : 评论事件 - **评论删除** + - `OpenForumReplyDispatch` : 开放论坛事件 - **回复事件** + - `OpenForumReplyCreate` : 回复事件 - **回复创建** + - `OpenForumReplyDelete` : 回复事件 - **回复删除** + +:::note 开放论坛事件 + +对应的 `instents` 为 `EventIntents.OpenForumsEvent.intents` + +更多可参考 [官方文档](https://bot.q.qq.com/wiki/develop/api/gateway/open_forum.html#oepn-forum-event-intents-open-forum-event) + +::: + +- `ForumDispatch` : **论坛事件** + - `ForumThreadDispatch` : 论坛事件 - **主题贴事件** + - `ForumThreadCreate` : 主题贴事件: **主题贴创建** + - `ForumThreadUpdate` : 主题贴事件: **主题贴更新** + - `ForumThreadDelete` : 主题贴事件: **主题贴删除** + - `ForumPostDispatch` : 论坛事件 - **评论事件** + - `ForumPostCreate` : 评论事件 - **评论创建** + - `ForumPostDelete` : 评论事件 - **评论删除** + - `ForumReplyDispatch` : 论坛事件 - **回复事件** + - `ForumReplyCreate` : 回复事件 - **回复创建** + - `ForumReplyDelete` : 回复事件 - **回复删除** + - `ForumPublishAuditResult` : 论坛事件 - **帖子审核事件** + + +:::note 论坛事件 + +对应的 `instents` 为 `EventIntents.ForumsEvent.intents` + +更多可参考 [官方文档](https://bot.q.qq.com/wiki/develop/api/gateway/forum.html) + +::: + +:::info 仅私域 + +非开放的论坛事件是仅支持**私域BOT**的。 + +::: + +### 标准库应用 + +在使用 `stdlib` 标准库时可以对它们进行监听,以 `OpenForumThreadCreate` 为例: + + + + +```kotlin +// 配置并创建bot +val bot = BotFactory.create("app id", "sec", "token") { + useSandboxServerUrl() + // 为了示例,增加对 OpenForumsEvent 事件的支持 + intents += EventIntents.OpenForumsEvent.intents +} + +bot.process { raw -> + println("OpenForumThreadCreate: $this") + println("OpenForumThreadCreate.raw: $raw") +} + +bot.start() +bot.join() +``` + + + + +```java +Bot bot = BotFactory.create("appid", "sec", "token", (config) -> { + config.useSandboxServerUrl(); + // 追加对 OpenForumsEvent 事件的订阅:OpenForumsEvent 与默认订阅合并 + config.setIntentsValue( + config.getIntentsValue() | EventIntents.OpenForumsEvent.getIntents() + ); + }); + +bot.registerProcessor(EventProcessors.async(OpenForumThreadCreate.class, (event, raw) -> { + System.out.println("OpenForumThreadCreate: " + event); + System.out.println("OpenForumThreadCreate.raw: " + raw); + return CompletableFuture.completedFuture(null); // Void? + })); + +bot.startAsync().thenCompose((v) -> bot.joinAsync()).join(); +``` +{switcher-key="%ja%"} + +```java +Bot bot = BotFactory.create("appid", "sec", "token", (config) -> { + config.useSandboxServerUrl(); + // 追加对 OpenForumsEvent 事件的订阅:OpenForumsEvent 与默认订阅合并 + config.setIntentsValue( + config.getIntentsValue() | EventIntents.OpenForumsEvent.getIntents() + ); + return Unit.INSTANCE; + }); + +bot.registerBlockingProcessor(EventProcessors.block(OpenForumThreadCreate.class, (event, raw) -> { + System.out.println("OpenForumThreadCreate: " + event); + System.out.println("OpenForumThreadCreate.raw: " + raw); + })); + +bot.startBlocking(); +bot.joinBlocking(); +``` +{switcher-key="%jb%"} + + + + +### 组件模块应用 + +#### core 组件模块 + +core 组件模块基于 simbot api 针对上述事件提供了进一步的封装实现: + +- `QGOpenForumEvent` : **开放论坛事件** + - `QGOpenForumThreadEvent` : 开放论坛事件 - **主题贴事件** + - `QGOpenForumThreadCreateEvent` : 主题贴事件: **主题贴创建** + - `QGOpenForumThreadUpdateEvent` : 主题贴事件: **主题贴更新** + - `QGOpenForumThreadDeleteEvent` : 主题贴事件: **主题贴删除** + - `QGOpenForumPostEvent` : 开放论坛事件 - **评论事件** + - `QGOpenForumPostCreateEvent` : 评论事件 - **评论创建** + - `QGOpenForumPostDeleteEvent` : 评论事件 - **评论删除** + - `QGOpenForumReplyEvent` : 开放论坛事件 - **回复事件** + - `QGOpenForumReplyCreateEvent` : 回复事件 - **回复创建** + - `QGOpenForumReplyDeleteEvent` : 回复事件 - **回复删除** + + +- `QGForumEvent` : **论坛事件** + - `QGForumThreadEvent` : 论坛事件 - **主题贴事件** + - `QGForumThreadCreateEvent` : 主题贴事件: **主题贴创建** + - `QGForumThreadUpdateEvent` : 主题贴事件: **主题贴更新** + - `QGForumThreadDeleteEvent` : 主题贴事件: **主题贴删除** + - `QGForumPostEvent` : 论坛事件 - **评论事件** + - `QGForumPostCreateEvent` : 评论事件 - **评论创建** + - `QGForumPostDeleteEvent` : 评论事件 - **评论删除** + - `QGForumReplyEvent` : 论坛事件 - **回复事件** + - `QGForumReplyCreateEvent` : 回复事件 - **回复创建** + - `QGForumReplyDeleteEvent` : 回复事件 - **回复删除** + - `QGForumPublishAuditResultEvent` : 论坛事件 - **帖子审核事件** + +它们基本上与 API 模块中的基础实现类型一一对应。 + +在使用 simbot 核心库时: + + + + +```kotlin +val app = launchSimpleApplication { + useQQGuild() +} + +app.eventDispatcher.apply { + // 所有开放论坛事件 + listen { + println("Open forum event: $it") + + EventResult.empty() + } + + // 所有论坛事件 + listen { + println("Forum event: $it") + + EventResult.empty() + } +} + +app.qqGuildBots { + val bot = register("appid", "sec", "token") { + botConfig { + useSandboxServerUrl() + // 追加事件订阅 + intents += EventIntents.OpenForumsEvent.intents + EventIntents.ForumsEvent.intents + } + } + + bot.start() +} + +app.join() +``` + + + + + +```java +var future = Applications.launchApplicationAsync(Simple.INSTANCE, configurer -> { + configurer.install(QQGuildBotManager.Factory); + configurer.install(QQGuildComponent.Factory); +}).asFuture().thenCompose(application -> { + // 所有开放论坛事件 + application.getEventDispatcher().register(EventListeners.async(QGOpenForumEvent.class, (context, event) -> { + System.out.println("QG open forum event: " + event); + return CompletableFuture.completedFuture(EventResult.empty()); + })); + // 所有论坛事件 + application.getEventDispatcher().register(EventListeners.async(QGForumEvent.class, (context, event) -> { + System.out.println("QG forum event: " + event); + return CompletableFuture.completedFuture(EventResult.empty()); + })); + // 寻找并注册 bot + for (var botManager : application.getBotManagers()) { + if (botManager instanceof QQGuildBotManager qqGuildBotManager) { + var bot = qqGuildBotManager.register("appid", "sec", "token", (qgBotConfig) -> { + qgBotConfig.botConfig(botConfig -> { + botConfig.useSandboxServerUrl(); + // 追加对 OpenForumsEvent 事件的订阅:OpenForumsEvent 与默认订阅合并 + botConfig.setIntentsValue( + botConfig.getIntentsValue() | EventIntents.OpenForumsEvent.getIntents() + ); + }); + }); + bot.startAsync(); + break; + } + } + + return application.asFuture(); +}); + +future.join(); +``` +{switcher-key="%ja%"} + +```java +final var application = Applications.launchApplicationBlocking(Simple.INSTANCE, configurer -> { + configurer.install(QQGuildBotManager.Factory); + configurer.install(QQGuildComponent.Factory); +}); + +// 所有开放论坛事件 +application.getEventDispatcher().register(EventListeners.block(QGOpenForumEvent.class, (context, event) -> { + System.out.println("QG open forum event: " + event); + return EventResult.empty(); + })); + +// 所有论坛事件 +application.getEventDispatcher().register(EventListeners.block(QGForumEvent.class, (context, event) -> { + System.out.println("QG forum event: " + event); + return EventResult.empty(); + })); + +// 寻找并注册 bot +for (var botManager : application.getBotManagers()) { + if (botManager instanceof QQGuildBotManager qqGuildBotManager) { + var bot = qqGuildBotManager.register("appid", "sec", "token", (qgBotConfig) -> { + qgBotConfig.botConfig(botConfig -> { + botConfig.useSandboxServerUrl(); + // 追加对 OpenForumsEvent 事件的订阅:OpenForumsEvent 与默认订阅合并 + botConfig.setIntentsValue( + botConfig.getIntentsValue() | EventIntents.OpenForumsEvent.getIntents() + ); + }); + }); + bot.startBlocking(); + break; + } +} + +application.joinBlocking(); +``` +{switcher-key="%jb%"} + + + + +#### SpringBoot + +或在 SpringBoot 中: + +> _省略掉有关SpringBoot项目本身的配置说明_ + + + + +```kotlin +@Listener +suspend fun onForumEvent(event: QGForumEvent) { + println("Event: $event") +} +``` + + + + +```java +@Listener +public CompletableFuture onForumEvent(QGForumEvent event) { + System.out.println("Event: " + event); + return CompletableFuture.completedFuture(null); +} +``` +{switcher-key="%ja%"} + +```java +@Listener +public void onForumEvent(QGForumEvent event) { + System.out.println("Event: " + event); +} +``` +{switcher-key="%jb%"} + + + diff --git a/Writerside/topics/role/api_role.md b/Writerside/topics/role/api_role.md new file mode 100644 index 00000000..0ddf7b6b --- /dev/null +++ b/Writerside/topics/role/api_role.md @@ -0,0 +1,4 @@ +# 角色 Role + +QQ频道中有一些针对 `角色` 的API。 + diff --git a/Writerside/topics/starter-topic.md b/Writerside/topics/starter-topic.md new file mode 100644 index 00000000..141cfb47 --- /dev/null +++ b/Writerside/topics/starter-topic.md @@ -0,0 +1 @@ +# 欢迎! diff --git a/Writerside/v.list b/Writerside/v.list new file mode 100644 index 00000000..79af5860 --- /dev/null +++ b/Writerside/v.list @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Writerside/writerside.cfg b/Writerside/writerside.cfg new file mode 100644 index 00000000..35e746ff --- /dev/null +++ b/Writerside/writerside.cfg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a7599c71..01f159bd 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ ktor = "2.3.7" openjdk-jmh = "1.35" log4j = "2.20.0" atomicfu = "0.20.1" -simbot = "4.0.0-dev2" +simbot = "4.0.0-dev4" [libraries] # simbot diff --git a/simbot-component-qq-guild-core/build.gradle.kts b/simbot-component-qq-guild-core/build.gradle.kts index c173301e..f85ec4b9 100644 --- a/simbot-component-qq-guild-core/build.gradle.kts +++ b/simbot-component-qq-guild-core/build.gradle.kts @@ -67,6 +67,8 @@ kotlin { commonTest.dependencies { implementation(kotlin("test")) implementation(libs.kotlinx.coroutines.test) + api(libs.simbot.core) + api(libs.simbot.common.core) } jvmTest.dependencies { diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt index c9ad1c79..97b279f7 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt @@ -100,7 +100,7 @@ public abstract class QQGuildBotManager : BotManager, JobBasedBotManager() { * 参考 [Job.linkTo]。 * * 如果 [coroutineContext][ConfigurableBotConfiguration.coroutineContext] - * 中不存在自定义的Job,则会直接使用 [QQGuildBotManager][love.forte.simbot.component.qguild.QQGuildBotManager] + * 中不存在自定义的Job,则会直接使用 [QQGuildBotManager][love.forte.simbot.component.qguild.bot.QQGuildBotManager] * 内的 [Job] 作为 parent Job 。 * */ @@ -108,7 +108,7 @@ public abstract class QQGuildBotManager : BotManager, JobBasedBotManager() { appId: String, secret: String, token: String, - block: QGBotComponentConfiguration.() -> Unit = {}, + block: ConfigurerFunction? = null, ): QGBot /** @@ -125,7 +125,7 @@ public abstract class QQGuildBotManager : BotManager, JobBasedBotManager() { * * 如果 [coroutineContext][ConfigurableBotConfiguration.coroutineContext] * 中不存在自定义的Job,则会直接使用 - * [QQGuildBotManager][love.forte.simbot.component.qguild.QQGuildBotManager] + * [QQGuildBotManager][love.forte.simbot.component.qguild.bot.QQGuildBotManager] * 内的 [Job] 作为 parent Job 。 * */ @@ -133,7 +133,7 @@ public abstract class QQGuildBotManager : BotManager, JobBasedBotManager() { appId: String, secret: String, token: String - ): QGBot = register(appId, secret, token) {} + ): QGBot = register(appId, secret, token, null) /** * 通过所需信息注册一个bot。 @@ -149,13 +149,13 @@ public abstract class QQGuildBotManager : BotManager, JobBasedBotManager() { * * 如果 [coroutineContext][ConfigurableBotConfiguration.coroutineContext] * 中不存在自定义的Job,则会直接使用 - * [QQGuildBotManager][love.forte.simbot.component.qguild.QQGuildBotManager] + * [QQGuildBotManager][love.forte.simbot.component.qguild.bot.QQGuildBotManager] * 内的 [Job] 作为 parent Job 。 * */ public abstract fun register( ticket: Bot.Ticket, - block: QGBotComponentConfiguration.() -> Unit = {}, + block: ConfigurerFunction? = null, ): QGBot /** @@ -172,11 +172,11 @@ public abstract class QQGuildBotManager : BotManager, JobBasedBotManager() { * * 如果 [coroutineContext][ConfigurableBotConfiguration.coroutineContext] * 中不存在自定义的Job,则会直接使用 - * [QQGuildBotManager][love.forte.simbot.component.qguild.QQGuildBotManager] + * [QQGuildBotManager][love.forte.simbot.component.qguild.bot.QQGuildBotManager] * 内的 [Job] 作为 parent Job 。 * */ - public fun register(ticket: Bot.Ticket): QGBot = register(ticket) {} + public fun register(ticket: Bot.Ticket): QGBot = register(ticket, null) /** * [QQGuildBotManager] 的注册工厂。 diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotComponentConfiguration.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotComponentConfiguration.kt index f2c10e44..dafd5aad 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotComponentConfiguration.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotComponentConfiguration.kt @@ -17,6 +17,8 @@ package love.forte.simbot.component.qguild.bot.config +import love.forte.simbot.common.function.ConfigurerFunction +import love.forte.simbot.common.function.invokeWith import love.forte.simbot.qguild.stdlib.Bot import love.forte.simbot.qguild.stdlib.BotConfiguration import love.forte.simbot.qguild.stdlib.ConfigurableBotConfiguration @@ -35,17 +37,22 @@ public class QGBotComponentConfiguration { * 可直接覆盖,或通过 [`botConfig { ... }`][botConfig] 组合配置。 * */ - public var botConfigure: ConfigurableBotConfiguration.() -> Unit = {} + public var botConfigure: ConfigurerFunction? = null /** * 使用 [block] 与当前 [botConfigure] 组合。 * */ - public fun botConfig(block: ConfigurableBotConfiguration.() -> Unit) { + public fun botConfig(block: ConfigurerFunction) { val bc = botConfigure - botConfigure = { - bc() - block() + if (bc == null) { + botConfigure = block + return + } + + botConfigure = ConfigurerFunction { + bc.invokeWith(this) + block.invokeWith(this) } } diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt index 225e45b3..0bcdd69d 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt @@ -26,6 +26,9 @@ import love.forte.simbot.common.atomic.atomic import love.forte.simbot.common.collection.ExperimentalSimbotCollectionApi import love.forte.simbot.common.collection.createConcurrentQueue import love.forte.simbot.common.coroutines.linkTo +import love.forte.simbot.common.function.ConfigurerFunction +import love.forte.simbot.common.function.invokeBy +import love.forte.simbot.common.function.invokeWith import love.forte.simbot.common.id.ID import love.forte.simbot.component.qguild.QQGuildComponent import love.forte.simbot.component.qguild.bot.QGBot @@ -97,7 +100,7 @@ internal class QQGuildBotManagerImpl( private val onRegister = atomic(false) - override fun register(ticket: Bot.Ticket, block: QGBotComponentConfiguration.() -> Unit): QGBot { + override fun register(ticket: Bot.Ticket, block: ConfigurerFunction?): QGBot { while (true) { if (!isActive) throw IllegalStateException("This manager has been completed") if (onRegister.compareAndSet(expect = false, value = true)) { @@ -116,15 +119,15 @@ internal class QQGuildBotManagerImpl( appId: String, secret: String, token: String, - block: QGBotComponentConfiguration.() -> Unit, + block: ConfigurerFunction?, ): QGBot = register(Bot.Ticket(appId, secret, token), block) private fun doRegister( ticket: Bot.Ticket, - block: QGBotComponentConfiguration.() -> Unit, + block: ConfigurerFunction?, ): QGBot { val configure = configuration.botConfigure - val config = QGBotComponentConfiguration().also(block) + val config = QGBotComponentConfiguration().invokeBy(block) val sourceBot = BotFactory.create(ticket) { // init context coroutineContext = if (coroutineContext == EmptyCoroutineContext) { @@ -134,7 +137,7 @@ internal class QQGuildBotManagerImpl( } configure(ticket.appId, ticket.secret, ticket.token) - config.botConfigure(this) + config.botConfigure?.invokeWith(this) val customJob = coroutineContext[Job] if (customJob == null) { diff --git a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.jvm.kt b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.jvm.kt index a232627f..e6d3f68c 100644 --- a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.jvm.kt +++ b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.jvm.kt @@ -29,6 +29,7 @@ import love.forte.simbot.suspendrunner.runInBlocking import org.jetbrains.annotations.Blocking import org.jetbrains.annotations.NonBlocking import java.util.concurrent.CompletableFuture +import java.util.concurrent.CompletionStage /** * 以阻塞的形式实现 [EventProcessor],是提供给 Java 的友好类型。 @@ -52,6 +53,21 @@ public fun interface JBlockEventProcessor : EventProcessor { } } +/** + * 以阻塞的形式实现 [EventProcessor],是提供给 Java 的友好类型。 + * 可直接使用 `EventProcessors.block((event, raw) -> { ... })` 构建。 + * + * [block] 最终会在 [Dispatchers.IO] 的调度器下使用 [runInterruptible] 执行。 + */ +public fun interface TypedJBlockEventProcessor { + /** + * 处理事件。 + */ + @Blocking + @Throws(Exception::class) + public fun block(event: T, raw: String) +} + /** * 以异步的形式实现 [EventProcessor],是提供给 Java 的友好类型。 * 可直接使用 `EventProcessors.async((event, raw) -> { ... })` 构建。 @@ -62,7 +78,7 @@ public fun interface JAsyncEventProcessor : EventProcessor { */ @Throws(Exception::class) @NonBlocking - public fun async(event: Signal.Dispatch, raw: String): CompletableFuture + public fun async(event: Signal.Dispatch, raw: String): CompletionStage @JvmSynthetic override suspend fun Signal.Dispatch.invoke(raw: String) { @@ -70,6 +86,19 @@ public fun interface JAsyncEventProcessor : EventProcessor { } } +/** + * 以异步的形式实现 [EventProcessor],是提供给 Java 的友好类型。 + * 可直接使用 `EventProcessors.async((event, raw) -> { ... })` 构建。 + */ +public fun interface TypedJAsyncEventProcessor { + /** + * 处理事件。 + */ + @Throws(Exception::class) + @NonBlocking + public fun async(event: Signal.Dispatch, raw: String): CompletionStage +} + /** * 构建一个 [JBlockEventProcessor]. */ @@ -79,3 +108,25 @@ public fun block(function: JBlockEventProcessor): JBlockEventProcessor = functio * 构建一个 [JAsyncEventProcessor]. */ public fun async(function: JAsyncEventProcessor): JAsyncEventProcessor = function + +/** + * 构建一个 [JBlockEventProcessor]. + */ +public fun block(type: Class, function: TypedJBlockEventProcessor): JBlockEventProcessor = + JBlockEventProcessor { event, raw -> + if (type.isInstance(event)) { + function.block(type.cast(event), raw) + } + } + +/** + * 构建一个 [JAsyncEventProcessor]. + */ +public fun async(type: Class, function: TypedJAsyncEventProcessor): JAsyncEventProcessor = + JAsyncEventProcessor { event, raw -> + if (type.isInstance(event)) { + function.async(type.cast(event), raw) + } else { + CompletableFuture.completedFuture(null) + } + } From 7e9bd14916ea24efa040c9201a1a50a135cad5df Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Fri, 19 Jan 2024 00:46:24 +0800 Subject: [PATCH 20/71] v4.0.0-dev3 --- build.gradle.kts | 3 ++ buildSrc/src/main/kotlin/P.kt | 2 +- gradle.properties | 4 +- gradle/libs.versions.toml | 2 +- .../build.gradle.kts | 3 ++ .../forte/simbot/qguild/event/messages.kt | 42 +++++++++++++++++-- .../build.gradle.kts | 3 ++ .../component/qguild/QQGuildComponentUsage.kt | 1 + .../component/qguild/bot/QQGuildBotManager.kt | 26 ++++++------ .../component/qguild/event/QGChannelEvents.kt | 2 +- .../component/qguild/event/QGForumEvents.kt | 2 +- .../component/qguild/event/QGMessageEvents.kt | 2 +- .../qguild/event/QGOpenForumEvents.kt | 2 +- .../internal/bot/QQGuildBotManagerImpl.kt | 7 ++-- .../event/QGAtMessageCreateEventImpl.kt | 2 +- .../internal/event/QGChannelEventImpls.kt | 2 +- .../build.gradle.kts | 3 ++ .../src/commonMain/kotlin/TestMain.kt | 18 ++++---- .../src/nativeMain/kotlin/TestMain.native.kt | 21 ++-------- tests/plugin-test/build.gradle.kts | 4 +- .../src/commonMain/kotlin/FooComponent.kt | 19 ++++----- 21 files changed, 102 insertions(+), 68 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index fbf676e5..c5e11e15 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,6 +15,7 @@ * If not, see . */ +import love.forte.gradle.common.core.project.setup import love.forte.gradle.common.core.repository.Repositories @@ -25,6 +26,8 @@ plugins { id("simbot-tencent-guild.nexus-publish") } +setup(P.ComponentQQGuild) + buildscript { repositories { mavenCentral() diff --git a/buildSrc/src/main/kotlin/P.kt b/buildSrc/src/main/kotlin/P.kt index 2d3eac2e..3a3b1f6a 100644 --- a/buildSrc/src/main/kotlin/P.kt +++ b/buildSrc/src/main/kotlin/P.kt @@ -59,7 +59,7 @@ object P { override val homepage: String get() = HOMEPAGE - private val baseVersion = v(4, 0, 0) - v("dev2") + private val baseVersion = v(4, 0, 0) - v("dev3") val snapshotVersion = baseVersion - Version.SNAPSHOT override val version = if (isSnapshot()) snapshotVersion else baseVersion diff --git a/gradle.properties b/gradle.properties index 62459fef..ad34dc55 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,5 +25,5 @@ org.gradle.caching=true #kotlin.experimental.tryK2=true -http.proxyHost=localhost -http.proxyPort=7790 +#http.proxyHost=localhost +#http.proxyPort=7790 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 01f159bd..58df50b6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,7 @@ ktor = "2.3.7" openjdk-jmh = "1.35" log4j = "2.20.0" atomicfu = "0.20.1" -simbot = "4.0.0-dev4" +simbot = "4.0.0-dev6" [libraries] # simbot diff --git a/simbot-component-qq-guild-api/build.gradle.kts b/simbot-component-qq-guild-api/build.gradle.kts index 491b1a99..3ad94969 100644 --- a/simbot-component-qq-guild-api/build.gradle.kts +++ b/simbot-component-qq-guild-api/build.gradle.kts @@ -15,6 +15,7 @@ * If not, see . */ +import love.forte.gradle.common.core.project.setup import love.forte.gradle.common.kotlin.multiplatform.applyTier1 import love.forte.gradle.common.kotlin.multiplatform.applyTier2 import love.forte.gradle.common.kotlin.multiplatform.applyTier3 @@ -26,6 +27,8 @@ plugins { `qq-guild-dokka-partial-configure` } +setup(P.ComponentQQGuild) + configJavaCompileWithModule("simbot.component.qqguild.api") //apply(plugin = "qq-guild-dokka-partial-configure") apply(plugin = "qq-guild-multiplatform-maven-publish") diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/messages.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/messages.kt index da35a9a9..3d3c39ff 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/messages.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/messages.kt @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023. ForteScarlet. + * Copyright (c) 2023-2024. ForteScarlet. * * This file is part of simbot-component-qq-guild. * @@ -46,6 +46,19 @@ public sealed class MessageDispatch : Signal.Dispatch() { @SerialName(EventIntents.PublicGuildMessages.AT_MESSAGE_CREATE_TYPE) public data class AtMessageCreate(override val s: Long, @SerialName("d") override val data: Message) : MessageDispatch() +/** + * 消息事件 + * `PUBLIC_MESSAGE_DELETE_TYPE` + * + */ +@Serializable +@SerialName(EventIntents.PublicGuildMessages.PUBLIC_MESSAGE_DELETE_TYPE) +public data class PublicMessageDeleteCreate( + override val s: Long, + @SerialName("d") override val data: Unit /* TODO 文档没找到描述。 */ +) : + Signal.Dispatch() + /** * 私信消息事件 * [`DIRECT_MESSAGE_CREATE (intents DIRECT_MESSAGE)`](https://bot.q.qq.com/wiki/develop/api/gateway/direct_message.html#direct-message-create-intents-direct-message) @@ -56,7 +69,8 @@ public data class AtMessageCreate(override val s: Long, @SerialName("d") overrid */ @Serializable @SerialName(EventIntents.DirectMessage.DIRECT_MESSAGE_CREATE_TYPE) -public data class DirectMessageCreate(override val s: Long, @SerialName("d") override val data: Message) : MessageDispatch() +public data class DirectMessageCreate(override val s: Long, @SerialName("d") override val data: Message) : + MessageDispatch() /** * 与 [MessageAudited] 相关的事件类型。[data] 类型为 [MessageAudited]。 @@ -68,6 +82,24 @@ public sealed class MessageAuditedDispatch : Signal.Dispatch() { abstract override val data: MessageAudited } +/** + * 发送消息事件,代表频道内的全部消息,而不只是 at 机器人的消息。内容与 AT_MESSAGE_CREATE 相同 + */ +@Serializable +@SerialName(EventIntents.GuildMessages.MESSAGE_CREATE_TYPE) +public data class MessageCreate(override val s: Long, @SerialName("d") override val data: Message) : MessageDispatch() + +/** + * 删除(撤回)消息事件 + */ +@Serializable +@SerialName(EventIntents.GuildMessages.MESSAGE_DELETE_TYPE) +public data class MessageDelete( + override val s: Long, + @SerialName("d") override val data: Unit /* TODO 文档没找到描述。 */ +) : Signal.Dispatch() + + /** * 消息审核事件 * [`MESSAGE_AUDIT_PASS(intents MESSAGE_AUDIT)`](https://bot.q.qq.com/wiki/develop/api/gateway/message.html#message-audit-pass-intents-message-audit) @@ -78,7 +110,8 @@ public sealed class MessageAuditedDispatch : Signal.Dispatch() { */ @Serializable @SerialName(EventIntents.MessageAudit.MESSAGE_AUDIT_PASS_TYPE) -public data class MessageAuditPass(override val s: Long, @SerialName("d") override val data: MessageAudited) : MessageAuditedDispatch() +public data class MessageAuditPass(override val s: Long, @SerialName("d") override val data: MessageAudited) : + MessageAuditedDispatch() /** * 消息审核事件 @@ -90,4 +123,5 @@ public data class MessageAuditPass(override val s: Long, @SerialName("d") overri */ @Serializable @SerialName(EventIntents.MessageAudit.MESSAGE_AUDIT_REJECT_TYPE) -public data class MessageAuditReject(override val s: Long, @SerialName("d") override val data: MessageAudited) : MessageAuditedDispatch() +public data class MessageAuditReject(override val s: Long, @SerialName("d") override val data: MessageAudited) : + MessageAuditedDispatch() diff --git a/simbot-component-qq-guild-core/build.gradle.kts b/simbot-component-qq-guild-core/build.gradle.kts index f85ec4b9..ac3e5c15 100644 --- a/simbot-component-qq-guild-core/build.gradle.kts +++ b/simbot-component-qq-guild-core/build.gradle.kts @@ -15,6 +15,7 @@ * If not, see . */ +import love.forte.gradle.common.core.project.setup import love.forte.gradle.common.kotlin.multiplatform.applyTier1 import love.forte.gradle.common.kotlin.multiplatform.applyTier2 import love.forte.gradle.common.kotlin.multiplatform.applyTier3 @@ -26,6 +27,8 @@ plugins { `simbot-tcg-suspend-transform-configure` } +setup(P.ComponentQQGuild) + configJavaCompileWithModule("simbot.component.qqguild.core") apply(plugin = "qq-guild-multiplatform-maven-publish") diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt index 2aa541cd..4ea13603 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponentUsage.kt @@ -129,6 +129,7 @@ public inline fun
A.qqGuildBots(block: QQGuildBotManager.() -> */ @DslMarker @Target(AnnotationTarget.FUNCTION) +@Retention(AnnotationRetention.BINARY) internal annotation class QQGuildUsageBuilderDsl diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt index 97b279f7..94a50d6c 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt @@ -21,7 +21,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import love.forte.simbot.application.ApplicationConfiguration import love.forte.simbot.bot.BotManager -import love.forte.simbot.bot.JobBasedBotManager import love.forte.simbot.bot.SerializableBotConfiguration import love.forte.simbot.bot.UnsupportedBotConfigurationException import love.forte.simbot.common.coroutines.linkTo @@ -34,7 +33,6 @@ import love.forte.simbot.component.qguild.bot.config.QGBotComponentConfiguration import love.forte.simbot.component.qguild.bot.config.QGBotFileConfiguration import love.forte.simbot.component.qguild.internal.bot.QQGuildBotManagerImpl import love.forte.simbot.event.EventDispatcher -import love.forte.simbot.logger.Logger import love.forte.simbot.plugin.PluginConfigureContext import love.forte.simbot.plugin.PluginFactory import love.forte.simbot.plugin.PluginFactoryConfigurerProvider @@ -56,11 +54,9 @@ import kotlin.coroutines.EmptyCoroutineContext * * @author ForteScarlet */ -public abstract class QQGuildBotManager : BotManager, JobBasedBotManager() { - public abstract val eventDispatcher: EventDispatcher - protected abstract val logger: Logger - protected abstract val component: QQGuildComponent - public abstract val configuration: QQGuildBotManagerConfiguration +public interface QQGuildBotManager : BotManager { + public val eventDispatcher: EventDispatcher + public val configuration: QQGuildBotManagerConfiguration @OptIn(ExperimentalContracts::class) private fun checkConfig(configuration: SerializableBotConfiguration): Boolean { @@ -84,7 +80,9 @@ public abstract class QQGuildBotManager : BotManager, JobBasedBotManager() { // no config val (appId, secret, token) = c.ticket.toTicket() - return register(appId, secret, token, c::includeConfig) + return register(appId, secret, token) { + c.includeConfig(this) + } } /** @@ -104,11 +102,11 @@ public abstract class QQGuildBotManager : BotManager, JobBasedBotManager() { * 内的 [Job] 作为 parent Job 。 * */ - public abstract fun register( + public fun register( appId: String, secret: String, token: String, - block: ConfigurerFunction? = null, + block: ConfigurerFunction?, ): QGBot /** @@ -133,7 +131,7 @@ public abstract class QQGuildBotManager : BotManager, JobBasedBotManager() { appId: String, secret: String, token: String - ): QGBot = register(appId, secret, token, null) + ): QGBot = register(appId, secret, token, block = null) /** * 通过所需信息注册一个bot。 @@ -153,9 +151,9 @@ public abstract class QQGuildBotManager : BotManager, JobBasedBotManager() { * 内的 [Job] 作为 parent Job 。 * */ - public abstract fun register( + public fun register( ticket: Bot.Ticket, - block: ConfigurerFunction? = null, + block: ConfigurerFunction?, ): QGBot /** @@ -176,7 +174,7 @@ public abstract class QQGuildBotManager : BotManager, JobBasedBotManager() { * 内的 [Job] 作为 parent Job 。 * */ - public fun register(ticket: Bot.Ticket): QGBot = register(ticket, null) + public fun register(ticket: Bot.Ticket): QGBot = register(ticket, block = null) /** * [QQGuildBotManager] 的注册工厂。 diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt index 820076f6..26a9f31f 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGChannelEvents.kt @@ -97,7 +97,7 @@ public abstract class QGChannelUpdateEvent : QGChannelEvent(), ChangeEvent, Chan /** * 发生的所在频道服务器 */ - abstract override suspend fun guild(): QGGuild + abstract override suspend fun source(): QGGuild } /** diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt index 6edd8a0d..ebcc0fed 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt @@ -86,7 +86,7 @@ public abstract class QGForumEvent : QGBotEvent(), ChannelEvent * @throws QQGuildApiException API请求过程出现异常 * @throws NoSuchElementException 对应子频道已不存在 */ - override suspend fun guild(): QGGuild = + override suspend fun source(): QGGuild = bot.guildRelation.guild(guildId) ?: throw NoSuchElementException("guild(id=$guildId)") diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt index 4d1e5ea8..b0e80a3f 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt @@ -128,5 +128,5 @@ public abstract class QGAtMessageCreateEvent : QGMessageEvent(), ChatChannelMess * @throws QQGuildApiException 请求失败,例如无权限 * @throws NoSuchElementException 没有找到结果 */ - abstract override suspend fun guild(): QGGuild + abstract override suspend fun source(): QGGuild } diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt index 72be92d6..25f3e975 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGOpenForumEvents.kt @@ -77,7 +77,7 @@ public abstract class QGOpenForumEvent : QGBotEvent(), Chann * @throws QQGuildApiException API请求过程出现异常 * @throws NoSuchElementException 对应子频道已不存在 */ - override suspend fun guild(): QGGuild = + override suspend fun source(): QGGuild = bot.guildRelation.guild(guildId) ?: throw NoSuchElementException("guild(id=$guildId)") /** diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt index 0bcdd69d..9b22896a 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt @@ -21,6 +21,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.cancel import love.forte.simbot.bot.ConflictBotException +import love.forte.simbot.bot.JobBasedBotManager import love.forte.simbot.bot.NoSuchBotException import love.forte.simbot.common.atomic.atomic import love.forte.simbot.common.collection.ExperimentalSimbotCollectionApi @@ -52,15 +53,15 @@ import kotlin.coroutines.EmptyCoroutineContext internal class QQGuildBotManagerImpl( override val eventDispatcher: EventDispatcher, override val configuration: QQGuildBotManagerConfiguration, - override val component: QQGuildComponent, + private val component: QQGuildComponent, override val job: Job, private val coroutineContext: CoroutineContext // 不应包含 Job -) : QQGuildBotManager() { +) : QQGuildBotManager, JobBasedBotManager() { companion object { private val LOGGER = LoggerFactory.logger() } - override val logger: Logger + private val logger: Logger get() = LOGGER /** diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGAtMessageCreateEventImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGAtMessageCreateEventImpl.kt index 928810cd..1ea133b1 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGAtMessageCreateEventImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGAtMessageCreateEventImpl.kt @@ -90,7 +90,7 @@ internal class QGAtMessageCreateEventImpl( } } - override suspend fun guild(): QGGuild { + override suspend fun source(): QGGuild { return with(sourceEventEntity) { bot.queryGuild(guildId) ?: throw NoSuchElementException("guild(id=$guildId)") } diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGChannelEventImpls.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGChannelEventImpls.kt index 4dbf21e7..d60b9274 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGChannelEventImpls.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/event/QGChannelEventImpls.kt @@ -50,7 +50,7 @@ internal class QGChannelUpdateEventImpl( ) : QGChannelUpdateEvent() { override val id: ID get() = tcgChannelModifyId(1, bot.id, sourceEventEntity.id, hashCode()) override suspend fun content(): QGTextChannel = _channel - override suspend fun guild(): QGGuild = with(sourceEventEntity) { + override suspend fun source(): QGGuild = with(sourceEventEntity) { bot.queryGuild(guildId) ?: throw NoSuchElementException("guild(id=${guildId})") } } diff --git a/simbot-component-qq-guild-stdlib/build.gradle.kts b/simbot-component-qq-guild-stdlib/build.gradle.kts index 3c54a073..3a4e2247 100644 --- a/simbot-component-qq-guild-stdlib/build.gradle.kts +++ b/simbot-component-qq-guild-stdlib/build.gradle.kts @@ -15,6 +15,7 @@ * If not, see . */ +import love.forte.gradle.common.core.project.setup import love.forte.gradle.common.kotlin.multiplatform.applyTier1 import love.forte.gradle.common.kotlin.multiplatform.applyTier2 import love.forte.gradle.common.kotlin.multiplatform.applyTier3 @@ -26,6 +27,8 @@ plugins { `simbot-tcg-suspend-transform-configure` } +setup(P.ComponentQQGuild) + configJavaCompileWithModule("simbot.component.qqguild.stdlib") apply(plugin = "qq-guild-multiplatform-maven-publish") diff --git a/tests/application-test/src/commonMain/kotlin/TestMain.kt b/tests/application-test/src/commonMain/kotlin/TestMain.kt index e2cf96c9..f89a3f9e 100644 --- a/tests/application-test/src/commonMain/kotlin/TestMain.kt +++ b/tests/application-test/src/commonMain/kotlin/TestMain.kt @@ -16,10 +16,12 @@ */ import kotlinx.coroutines.flow.take +import love.forte.simbot.application.listeners import love.forte.simbot.component.qguild.event.QGAtMessageCreateEvent import love.forte.simbot.component.qguild.message.QGReplyTo import love.forte.simbot.component.qguild.qqGuildBots import love.forte.simbot.core.application.SimpleApplication +import love.forte.simbot.event.ChatChannelMessageEvent import love.forte.simbot.event.EventResult import love.forte.simbot.event.listen import love.forte.simbot.message.messagesOf @@ -52,7 +54,7 @@ expect fun launchApp(vararg args: String) internal suspend fun SimpleApplication.onApp(appid: String, token: String) { - eventDispatcher.apply { + listeners { listen { println("event: $it") it.bot.guildRelation.guilds(30) @@ -70,13 +72,13 @@ internal suspend fun SimpleApplication.onApp(appid: String, token: String) { EventResult.empty() } -// listen { -// println("event: $it") -// it.reply("mua2!").also { r -> -// println("replied: $r") -// } -// EventResult.empty() -// } + listen { + println("event: $it") + it.reply("mua2!").also { r -> + println("replied: $r") + } + EventResult.empty() + } } qqGuildBots { diff --git a/tests/application-test/src/nativeMain/kotlin/TestMain.native.kt b/tests/application-test/src/nativeMain/kotlin/TestMain.native.kt index 78f779d7..2db58adc 100644 --- a/tests/application-test/src/nativeMain/kotlin/TestMain.native.kt +++ b/tests/application-test/src/nativeMain/kotlin/TestMain.native.kt @@ -17,27 +17,12 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking +import love.forte.simbot.component.qguild.QQGuildComponent +import love.forte.simbot.component.qguild.bot.QQGuildBotManager import love.forte.simbot.component.qguild.useQQGuild import love.forte.simbot.core.application.launchSimpleApplication import kotlin.time.measureTimedValue -/* - * Copyright (c) 2024. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - actual fun launchApp(vararg args: String) { println(args.joinToString(", ")) @@ -49,6 +34,8 @@ actual fun launchApp(vararg args: String) { } useQQGuild() + install(QQGuildComponent) + install(QQGuildBotManager) }.apply { onApp(args[0], args[1]) } diff --git a/tests/plugin-test/build.gradle.kts b/tests/plugin-test/build.gradle.kts index 797f0ca3..bc2612c5 100644 --- a/tests/plugin-test/build.gradle.kts +++ b/tests/plugin-test/build.gradle.kts @@ -38,7 +38,7 @@ kotlin { browser() nodejs() - binaries.executable() + //binaries.executable() useEsModules() } @@ -47,7 +47,7 @@ kotlin { applyTier3(supportKtorClient = true) mingwX64 { - binaries.executable() + //binaries.executable() } sourceSets { diff --git a/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt b/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt index d411db0d..539507aa 100644 --- a/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt +++ b/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt @@ -20,7 +20,6 @@ import kotlinx.serialization.modules.SerializersModule import love.forte.simbot.common.function.ConfigurerFunction import love.forte.simbot.common.function.invokeBy import love.forte.simbot.component.* -import love.forte.simbot.core.application.launchSimpleApplication import love.forte.simbot.plugin.Plugin import love.forte.simbot.plugin.PluginConfigureContext import love.forte.simbot.plugin.PluginFactory @@ -85,12 +84,12 @@ class FooPlugin : Plugin { } } -suspend fun main() { - val app = launchSimpleApplication { - install(FooComponent) - } - - // Kotlin 可以直接通过扩展函数根据类型寻找目标 - val fooComponent = app.components.find() - println(fooComponent) -} +//suspend fun main() { +// val app = launchSimpleApplication { +// install(FooComponent) +// } +// +// // Kotlin 可以直接通过扩展函数根据类型寻找目标 +// val fooComponent = app.components.find() +// println(fooComponent) +//} From ecd1bbeaae7f1271578357de396b6cb675bc501f Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Fri, 19 Jan 2024 02:42:30 +0800 Subject: [PATCH 21/71] tree --- Writerside/d.tree | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Writerside/d.tree b/Writerside/d.tree index 31875bda..51dfc5cc 100644 --- a/Writerside/d.tree +++ b/Writerside/d.tree @@ -7,6 +7,8 @@ start-page="starter-topic.md"> + + From 5b24a21dc5c23dda0d1d7c960fd63472407b7fe6 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Sat, 20 Jan 2024 01:23:19 +0800 Subject: [PATCH 22/71] v4.0.0-dev3 --- .changelog/v4.0.0-dev3.md | 11 +++++++++++ gradle/libs.versions.toml | 5 ++++- simbot-component-qq-guild-core/build.gradle.kts | 3 +++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 .changelog/v4.0.0-dev3.md diff --git a/.changelog/v4.0.0-dev3.md b/.changelog/v4.0.0-dev3.md new file mode 100644 index 00000000..585a55c7 --- /dev/null +++ b/.changelog/v4.0.0-dev3.md @@ -0,0 +1,11 @@ +> 对应核心版本: [**v4.0.0-dev11**](https://github.com/simple-robot/simpler-robot/releases/tag/v4.0.0-dev11) + + +> [!warning] +> **目前版本处于 `dev` 阶段,代表此版本是一个开发预览版,可能不稳定、可能随时发生更改、且不保证可用性。** + + +我们欢迎并期望着您的的[反馈](https://github.com/simple-robot/simbot-component-qq-guild/issues)或[协助](https://github.com/simple-robot/simbot-component-qq-guild/pulls), +感谢您的贡献与支持! + +也欢迎您为我们献上一颗 `star`,这是对我们最大的鼓励与认可! diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 58df50b6..5b61232c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -7,7 +7,8 @@ ktor = "2.3.7" openjdk-jmh = "1.35" log4j = "2.20.0" atomicfu = "0.20.1" -simbot = "4.0.0-dev6" +simbot = "4.0.0-dev11" +reactor = "3.6.2" [libraries] # simbot @@ -86,3 +87,5 @@ log4j-slf4j2 = { group = "org.apache.logging.log4j", name = "log4j-slf4j2-impl", openjdk-jmh-core = { group = "org.openjdk.jmh", name = "jmh-core", version.ref = "openjdk-jmh" } openjdk-jmh-generator-annprocess = { group = "org.openjdk.jmh", name = "jmh-generator-annprocess", version.ref = "openjdk-jmh" } +# reactor +reactor-core = { group = "io.projectreactor", name = "reactor-core", version.ref = "reactor" } diff --git a/simbot-component-qq-guild-core/build.gradle.kts b/simbot-component-qq-guild-core/build.gradle.kts index ac3e5c15..8525eec4 100644 --- a/simbot-component-qq-guild-core/build.gradle.kts +++ b/simbot-component-qq-guild-core/build.gradle.kts @@ -76,6 +76,9 @@ kotlin { jvmTest.dependencies { runtimeOnly(libs.ktor.client.cio) + runtimeOnly(libs.kotlinx.coroutines.reactor) + implementation(libs.reactor.core) + implementation(libs.log4j.api) implementation(libs.log4j.core) implementation(libs.log4j.slf4j2) From c3ff7a2f7c4cfba05ace1152c4c073d63d77f84f Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Sat, 20 Jan 2024 01:24:31 +0800 Subject: [PATCH 23/71] v4.0.0-dev4 --- .changelog/v4.0.0-dev3.md | 2 +- .changelog/v4.0.0-dev4.md | 11 +++++++++++ buildSrc/src/main/kotlin/P.kt | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 .changelog/v4.0.0-dev4.md diff --git a/.changelog/v4.0.0-dev3.md b/.changelog/v4.0.0-dev3.md index 585a55c7..da50602d 100644 --- a/.changelog/v4.0.0-dev3.md +++ b/.changelog/v4.0.0-dev3.md @@ -1,4 +1,4 @@ -> 对应核心版本: [**v4.0.0-dev11**](https://github.com/simple-robot/simpler-robot/releases/tag/v4.0.0-dev11) +> 对应核心版本: [**v4.0.0-dev6**](https://github.com/simple-robot/simpler-robot/releases/tag/v4.0.0-dev6) > [!warning] diff --git a/.changelog/v4.0.0-dev4.md b/.changelog/v4.0.0-dev4.md new file mode 100644 index 00000000..585a55c7 --- /dev/null +++ b/.changelog/v4.0.0-dev4.md @@ -0,0 +1,11 @@ +> 对应核心版本: [**v4.0.0-dev11**](https://github.com/simple-robot/simpler-robot/releases/tag/v4.0.0-dev11) + + +> [!warning] +> **目前版本处于 `dev` 阶段,代表此版本是一个开发预览版,可能不稳定、可能随时发生更改、且不保证可用性。** + + +我们欢迎并期望着您的的[反馈](https://github.com/simple-robot/simbot-component-qq-guild/issues)或[协助](https://github.com/simple-robot/simbot-component-qq-guild/pulls), +感谢您的贡献与支持! + +也欢迎您为我们献上一颗 `star`,这是对我们最大的鼓励与认可! diff --git a/buildSrc/src/main/kotlin/P.kt b/buildSrc/src/main/kotlin/P.kt index 3a3b1f6a..cca84ff0 100644 --- a/buildSrc/src/main/kotlin/P.kt +++ b/buildSrc/src/main/kotlin/P.kt @@ -59,7 +59,7 @@ object P { override val homepage: String get() = HOMEPAGE - private val baseVersion = v(4, 0, 0) - v("dev3") + private val baseVersion = v(4, 0, 0) - v("dev4") val snapshotVersion = baseVersion - Version.SNAPSHOT override val version = if (isSnapshot()) snapshotVersion else baseVersion From 4e3ca1d1788f1fcb3f17a472cf682bb20213b562 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Sun, 21 Jan 2024 23:59:11 +0800 Subject: [PATCH 24/71] =?UTF-8?q?:memo:=20=E7=BC=96=E5=86=99=E6=96=87?= =?UTF-8?q?=E6=A1=A3=20=E8=BF=98=E6=9C=89=E4=B8=80=E4=BA=9B=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=E7=AD=89=E4=B8=9C=E8=A5=BF=E7=9A=84=E4=BF=AE=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/config.yml | 2 +- .github/workflows/deploy-v4-website.yml | 109 ++++ ...ploy-website.yml => deploy-website.yml.bk} | 0 README.md | 36 +- Writerside/.idea/.gitignore | 6 + Writerside/d.tree | 32 +- Writerside/topics/API.md | 3 - Writerside/topics/Home.md | 53 ++ Writerside/topics/bot-config.md | 267 ++++++++ .../topics/old/3.2.0.0/api/old-api-forum.md | 617 ++++++++++++++++++ .../topics/old/3.2.0.0/old-bot-config.md | 223 +++++++ .../topics/old/3.2.0.0/quickstart/old-api.md | 188 ++++++ .../topics/old/3.2.0.0/quickstart/old-core.md | 495 ++++++++++++++ .../old/3.2.0.0/quickstart/old-spring-boot.md | 284 ++++++++ .../old/3.2.0.0/quickstart/old-stdlib.md | 311 +++++++++ Writerside/topics/snippets.md | 205 ++++++ Writerside/topics/starter-topic.md | 1 - Writerside/topics/use-api.md | 232 +++++++ Writerside/topics/use-core.md | 240 +++++++ Writerside/topics/use-spring-boot.md | 167 +++++ Writerside/topics/use-stdlib.md | 266 ++++++++ Writerside/v.list | 4 +- build.gradle.kts | 3 - buildSrc/build.gradle.kts | 24 +- buildSrc/settings.gradle.kts | 25 + buildSrc/src/main/kotlin/P.kt | 2 +- buildSrc/src/main/kotlin/SuspendTransforms.kt | 173 ----- ...tcg-suspend-transform-configure.gradle.kts | 19 + gradle/libs.versions.toml | 24 +- .../build.gradle.kts | 2 + .../love/forte/simbot/qguild/event/Signal.kt | 2 +- .../simbot/component/qguild/bot/QGBot.kt | 2 +- .../qguild/bot/config/CacheStrategyConfig.kt | 7 +- .../bot/config/QGBotFileConfiguration.kt | 11 +- .../qguild/bot/config/ShardConfig.kt | 2 +- .../qguild/channel/QGForumChannel.kt | 3 +- .../simbot/component/qguild/guild/QGGuild.kt | 16 +- .../component/qguild/guild/QGGuildRelation.kt | 10 +- .../build.gradle.kts | 1 + .../qguild/stdlib/EventProcessor.jvm.kt | 6 +- 40 files changed, 3821 insertions(+), 252 deletions(-) create mode 100644 .github/workflows/deploy-v4-website.yml rename .github/workflows/{deploy-website.yml => deploy-website.yml.bk} (100%) create mode 100644 Writerside/.idea/.gitignore delete mode 100644 Writerside/topics/API.md create mode 100644 Writerside/topics/Home.md create mode 100644 Writerside/topics/bot-config.md create mode 100644 Writerside/topics/old/3.2.0.0/api/old-api-forum.md create mode 100644 Writerside/topics/old/3.2.0.0/old-bot-config.md create mode 100644 Writerside/topics/old/3.2.0.0/quickstart/old-api.md create mode 100644 Writerside/topics/old/3.2.0.0/quickstart/old-core.md create mode 100644 Writerside/topics/old/3.2.0.0/quickstart/old-spring-boot.md create mode 100644 Writerside/topics/old/3.2.0.0/quickstart/old-stdlib.md create mode 100644 Writerside/topics/snippets.md delete mode 100644 Writerside/topics/starter-topic.md create mode 100644 Writerside/topics/use-api.md create mode 100644 Writerside/topics/use-core.md create mode 100644 Writerside/topics/use-spring-boot.md create mode 100644 Writerside/topics/use-stdlib.md create mode 100644 buildSrc/settings.gradle.kts delete mode 100644 buildSrc/src/main/kotlin/SuspendTransforms.kt diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 6664a981..160b7ab2 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -3,7 +3,7 @@ blank_issues_enabled: true contact_links: - name: 问题反馈 url: https://github.com/simple-robot/simpler-robot/issues/new/choose - about: 统一的问题反馈处 + about: simbot统一的问题反馈处 - name: 社区 url: https://github.com/orgs/simple-robot/discussions diff --git a/.github/workflows/deploy-v4-website.yml b/.github/workflows/deploy-v4-website.yml new file mode 100644 index 00000000..1c58f047 --- /dev/null +++ b/.github/workflows/deploy-v4-website.yml @@ -0,0 +1,109 @@ +name: Deploy Website +on: + push: + branches: +# - main +# - dev/ver/** +# - dev/main + - v4-dev/main + - v4-dev/v4-upgrade + + paths: + - 'Writerside/**' + # Specify to run a workflow manually from the Actions tab on GitHub + workflow_dispatch: + +# Gives the workflow permissions to clone the repo and create a page deployment +permissions: + id-token: write + pages: write + +env: + # Name of module and id separated by a slash + INSTANCE: Writerside/d + # Replace HI with the ID of the instance in capital letters + ARTIFACT: webHelpD2-all.zip + # Writerside docker image version + DOCKER_VERSION: 232.10275 + # Add the variable below to upload Algolia indexes + # Replace HI with the ID of the instance in capital letters +# ALGOLIA_ARTIFACT: algolia-indexes-HI.zip + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Build Writerside docs using Docker + uses: JetBrains/writerside-github-action@v4 + with: + instance: ${{ env.INSTANCE }} + artifact: ${{ env.ARTIFACT }} + docker-version: ${{ env.DOCKER_VERSION }} + + - name: Upload documentation + uses: actions/upload-artifact@v3 + with: + name: docs + path: | + artifacts/${{ env.ARTIFACT }} + artifacts/report.json + retention-days: 7 + + # Add the step below to upload Algolia indexes + # - name: Upload algolia-indexes + # uses: actions/upload-artifact@v3 + # with: + # name: algolia-indexes + # path: artifacts/${{ env.ALGOLIA_ARTIFACT }} + # retention-days: 7 + + # Add the job below and artifacts/report.json on Upload documentation step above if you want to fail the build when documentation contains errors + test: + # Requires build job results + needs: build + runs-on: ubuntu-latest + + steps: + - name: Download artifacts + uses: actions/download-artifact@v1 + with: + name: docs + path: artifacts + + - name: Test documentation + uses: JetBrains/writerside-checker-action@v1 + with: + instance: ${{ env.INSTANCE }} + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + # Requires the build job results + needs: test + runs-on: ubuntu-latest + steps: + - name: Download artifact + uses: actions/download-artifact@v3 + with: + name: docs + + - name: Unzip artifact + run: unzip -O UTF-8 -qq ${{ env.ARTIFACT }} -d dir + + - name: Setup Pages + uses: actions/configure-pages@v2 + + - name: Upload artifact + uses: actions/upload-pages-artifact@v1 + with: + path: dir + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v1 + diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml.bk similarity index 100% rename from .github/workflows/deploy-website.yml rename to .github/workflows/deploy-website.yml.bk diff --git a/README.md b/README.md index 6995d970..953967e6 100644 --- a/README.md +++ b/README.md @@ -21,37 +21,40 @@ -这是 [**Simple Robot v3**](https://github.com/simple-robot/simpler-robot) -下的子项目,是针对 [**QQ频道机器人**](https://bot.q.qq.com/wiki/develop/api/) 各方面的实现, +这是 +[**Simple Robot v4**](https://github.com/simple-robot/simpler-robot/tree/v4-dev) +下的子项目,是针对 +[**QQ频道机器人**](https://bot.q.qq.com/wiki/develop/api/) +各方面的实现, 包括对 `API` 内容的实现、事件相关的实现以及BOT对于事件的监听与交互等。 -- 基于 [`Kotlin`](https://kotlinlang.org/) 提供[多平台](https://kotlinlang.org/docs/multiplatform.html)/JVM平台(core模块) 特性 +- 基于 [`Kotlin`](https://kotlinlang.org/) 提供 [KMP 多平台](https://kotlinlang.org/docs/multiplatform.html) 特性 - 基于 [`Kotlin coroutines`](https://github.com/Kotlin/kotlinx.coroutines) 与 [`Ktor`](https://ktor.io/) 提供高效易用的API; - 基于 [`Kotlin serialization`](https://github.com/Kotlin/kotlinx.serialization) 进行数据序列化/反序列化操作。 > [!Note] -> 下文中 `Simple Robot v3` 简称为 `simbot3` +> 下文中 `Simple Robot v4` 简称为 `simbot4` ## 文档 -- 了解simbot3: [**simbot3官网**](https://simbot.forte.love) -- **QQ频道组件**手册:https://simple-robot.github.io/simbot-component-qq-guild/ (尚在 _🔧建设中_,暂未配置域名) +- 了解simbot: [**simbot官网**](https://simbot.forte.love) +- **QQ频道组件**手册: - **API文档**: [**文档引导站点**](https://docs.simbot.forte.love) 中QQ频道的 [**KDoc站点**](https://docs.simbot.forte.love/components/qq-guild) --- -我们欢迎并期望着您的的[反馈](https://github.com/simple-robot/simbot-component-qq-guild/issues)或[协助](https://github.com/simple-robot/simbot-component-qq-guild/pulls), +我们欢迎并期望着您的 +[反馈](https://github.com/simple-robot/simbot-component-qq-guild/issues) +或 +[协助](https://github.com/simple-robot/simbot-component-qq-guild/pulls), 感谢您的贡献与支持! ## 模块引导 ### API模块 -> JVM | JS | native - 基于 `Ktor` 针对 [QQ频道API](https://bot.q.qq.com/wiki/develop/api/) 的基本完整的[KMP](https://kotlinlang.org/docs/multiplatform.html)多平台封装实现, -支持 JVM、JS 和 native 平台, 是一个简单高效轻量级的API实现模块。 此模块基本不会提供什么多余的实现,其目标为在提供封装的情况下尽可能地保留原始API的使用手感,不做过多的封装。 @@ -60,12 +63,8 @@ ### 标准库模块 -> JVM | JS | native - 基于 [API模块](simbot-component-qq-guild-api) 针对bot的"登录"鉴权实现简单高效轻量级的事件订阅功能。 -通过[KMP](https://kotlinlang.org/docs/multiplatform.html)多平台支持 JVM、JS 和 native 平台, - 此模块在API模块的基础上提供了针对事件相关的功能实现,包括事件订阅的能力。 同样的,其目标为在提供封装的情况下尽可能地保留原始API的使用手感,不做过多的封装。 @@ -73,10 +72,11 @@ ### 核心组件模块 -> JVM Only - -基于 [标准库模块](simbot-component-qq-guild-stdlib) 对 [simbot3核心库](https://github.com/simple-robot/simpler-robot) 的组件实现, -是一个相对高度封装的模块,并提供simbot3大部分能力,包括事件监听、多组件协同、Spring Boot Starter 等。 +基于 +[标准库模块](simbot-component-qq-guild-stdlib) +对 [simbot4核心库](https://github.com/simple-robot/simpler-robot) +的组件实现, +是一个相对高度封装的模块,并提供simbot4大部分能力,包括事件监听、多组件协同、Spring Boot Starter 等。 👉 [前往模块](simbot-component-qq-guild-core-1) 了解更多。 diff --git a/Writerside/.idea/.gitignore b/Writerside/.idea/.gitignore new file mode 100644 index 00000000..df2b2e45 --- /dev/null +++ b/Writerside/.idea/.gitignore @@ -0,0 +1,6 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml + +vcs.xml +modules.xml \ No newline at end of file diff --git a/Writerside/d.tree b/Writerside/d.tree index 51dfc5cc..aefa4bed 100644 --- a/Writerside/d.tree +++ b/Writerside/d.tree @@ -3,13 +3,33 @@ SYSTEM "https://resources.jetbrains.com/writerside/1.0/product-profile.dtd"> + name="docs" + start-page="Home.md"> - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Writerside/topics/API.md b/Writerside/topics/API.md deleted file mode 100644 index f1f3c801..00000000 --- a/Writerside/topics/API.md +++ /dev/null @@ -1,3 +0,0 @@ -# API - -Start typing here... \ No newline at end of file diff --git a/Writerside/topics/Home.md b/Writerside/topics/Home.md new file mode 100644 index 00000000..7320b249 --- /dev/null +++ b/Writerside/topics/Home.md @@ -0,0 +1,53 @@ +# 欢迎! + +这里是 +[**Simple Robot v4**](https://github.com/simple-robot/simpler-robot/tree/v4-dev) (下文简称 simbot) +的 +[QQ频道组件](https://github.com/simple-robot/simbot-component-qq-guild/) +的应用手册! + +## 概述 + +**QQ频道组件** 是针对 +[**QQ频道机器人**](https://bot.q.qq.com/wiki/develop/api/) +各方面的 simbot 组件库实现, +包括对 `API` 内容的实现、事件相关的实现以及BOT对于事件的监听与交互等。 + +- 基于 [`Kotlin`](https://kotlinlang.org/) 提供 [KMP 多平台](https://kotlinlang.org/docs/multiplatform.html) 特性,提供 Java 友好的API。 +- 基于 [`Kotlin coroutines`](https://github.com/Kotlin/kotlinx.coroutines) 与 [`Ktor`](https://ktor.io/) 提供轻量高效的API。 + + + +[**Simple Robot 官网**](https://simbot.forte.love) + + + +## 反馈与协助! + +我们欢迎并期望着您的 +[反馈](https://github.com/simple-robot/simbot-component-qq-guild/issues) +或 +[协助](https://github.com/simple-robot/simbot-component-qq-guild/pulls), +感谢您的贡献与支持! + +## 法欧莉 + +如果你想看一看通过 **QQ频道组件** 的具体作品,可以前往QQ频道添加亲爱的 [法欧莉斯卡雷特](https://qun.qq.com/qunpro/robot/share?robot_appid=101986850) 来体验~ + + +## License + +`simbot-component-qq-guild` 使用 `LGPLv3` 许可证开源。 + +``` +This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General +Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) +any later version. + +This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied +warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along with this program. +If not, see . +``` diff --git a/Writerside/topics/bot-config.md b/Writerside/topics/bot-config.md new file mode 100644 index 00000000..4c472eae --- /dev/null +++ b/Writerside/topics/bot-config.md @@ -0,0 +1,267 @@ +# Bot配置文件 + + +

在使用 Spring Boot 时自动注册 bot 所需的配置文件。

+
+ +## 示例 + +```json +{ + "component": "simbot.qqguild", + "ticket": { + "appId": "你的botId", + "secret": "你的bot secret, 如果用不到也可以给空字符串", + "token": "你的bot token" + } +} +``` +{collapsible="true" default-state="expanded" collapsed-title="简单示例"} + +```json +{ + "component": "simbot.qqguild", + "ticket": { + "appId": "你的botId", + "secret": "你的bot secret, 如果用不到也可以给空字符串", + "token": "你的bot token" + }, + "config": { + "serverUrl": null, + "shard": { + "type": "full" + }, + "intents": { + "type": "raw", + "intents": 1073741827 + }, + "clientProperties": null, + "timeout": null, + "cache": { + "enable": true, + "transmit": null, + "dynamic": null, + "dispatcher": null + } + } +} +``` +{collapsible="true" default-state="collapsed" collapsed-title="完整示例"} + +## 属性描述 + + + + +固定值:`simbot.qqguild` + + + + +bot用于登录的票据信息,必填。 + + + + +`String` + +bot开发配置中的 `appID` + + + + +`String` + +bot开发配置中的 `AppSecret`, 如果用不到可以给空字符串 + + + + +`String` + +bot开发配置中的 `Token`。 + + + + + + + +可选项,提供一些额外的可配置属性。 + + + + +`String` + +目标服务器地址。默认为 `null`。 + +当值为特殊值:`"SANDBOX"` 时会选择使用 `QQGuild.SANDBOX_URL_STRING`, +也就是沙箱服务器地址。 + + + + +`ShardConfig` + +分片信息配置,默认为 `Full`。 + +根据 `type` 值的不同,可使用不同的属性。 + + + + +无额外属性,代表一个全量单片。 + +```json +{ + "type": "full" +} +``` + + + + +```json +{ + "type": "simple", + "value": 0, + "total": 1 +} +``` + + +对应的分片信息属性。 +对应的分片信息属性。 + + + + + + + + + +`IntentsConfig?` + +要订阅的事件的 intents 信息。默认 `1073741827`, +也就是订阅: +- 频道相关事件 +- 频道成员相关事件 +- 公域消息相关事件 + +根据 `type` 的不同可选的属性不同。 + + + + +直接使用 `intents` 原始的标记位最终数值。 + +```json +{ + "type": "raw", + "intents": 1073741824 +} +``` + + + + +通过名称寻找所有可用的 `EventIntents` 并合并为最终的 `intents`。 +名称基于继承了 `EventIntents` 的 object 的简单类名,例如 `Guilds`。 + +```json +{ + "type": "nameBased", + "names": ["Guilds", "PublicGuildMessages"] +} +``` + + + + + + + +`Map?` + +用作 `Signal.Identify.Data.properties` 中的参数。 + +```json +{ + "config": { + "clientProperties": { + "k1": "v1", + "foo": "bar" + } + } +} +``` + + + + +`TimeoutConfig?` + +与部分超时相关的配置信息。 +当任意属性不为 `null` 时会为 bot 中用于请求API的 `HttpClient` +配置 [HttpTimeout][HttpTimeout] 插件。 + +默认为 `null`。 + + + + +`Long?` + +API请求中的超时请求配置。参考 [HttpTimeout][HttpTimeout] 中的相关说明。 + +默认为 `null`。 + + + + +`Long?` + +API请求中的超时请求配置。参考 [HttpTimeout][HttpTimeout] 中的相关说明。 + +默认为 `null`。 + + + + +`Long?` + +API请求中的超时请求配置。参考 [HttpTimeout][HttpTimeout] 中的相关说明。 + +默认为 `null`。 + + + + + + + +`CacheConfig?` + +缓存相关配置。 + +```json +"config": { + "cache": { + "transmit": { + "enable": true + } + } +} +``` + +有关 `transmit` 的详细描述, +请参考 `TransmitCacheConfig` 的文档注释或 API Doc。 + + + + + + +[HttpTimeout]: https://ktor.io/docs/timeout.html diff --git a/Writerside/topics/old/3.2.0.0/api/old-api-forum.md b/Writerside/topics/old/3.2.0.0/api/old-api-forum.md new file mode 100644 index 00000000..0933a060 --- /dev/null +++ b/Writerside/topics/old/3.2.0.0/api/old-api-forum.md @@ -0,0 +1,617 @@ +# 论坛 + +QQ频道中有一些针对 `论坛子频道` 的API。( [参考文档](https://bot.q.qq.com/wiki/develop/api/openapi/forum/model.html#thread) ) + + +## API {id="api_1"} + +首先是 `API` 模块中对相关API的封装类型,它们在 `love.forte.simbot.qguild.api.forum` 中: + +- `DeleteThreadApi` +- `GetThreadApi` +- `GetThreadListApi` +- `PublishThreadApi` + +使用它们的方式都差不多,我们选其中一个 `GetThreadListApi` 作为示例: + + + + +```kotlin +// QQ频道API请求用的 token +val token = "Bot xxx" + +// Ktor 的 HttpClient +// 在不同平台下请注意选择可用的引擎,比如在JS平台下使用 `JS` 引擎,windows系统平台下使用 `WinHttp` 等。 +val client = HttpClient() + +// 请求的服务器地址 +// 此处为沙箱地址,也可选择正式地址或其他第三方代理地址 +val server = QQGuild.SANDBOX_URL + +val api = GetThreadListApi.create("channel ID") +val result = api.request(client, server, token) + +result.threads.forEach { thread -> + // 遍历结果... +} +``` + + + + +```java +// QQ频道API请求用的 token +String token = "Bot xxx"; + +// Ktor 的 HttpClient +// 此处选择使用 HttpClientJvmKt.HttpClient 自动加载环境中存在的 HttpClient 引擎 +// 请保证classpath中存在一个可用的 HttpClient JVM 引擎 +HttpClient client = HttpClientJvmKt.HttpClient(config -> Unit.INSTANCE); + +// 请求的服务器地址 +// 此处为沙箱地址,也可选择正式地址或其他第三方代理地址 +Url server = QQGuild.SANDBOX_URL; + +GetThreadListApi api = GetThreadListApi.create("channel ID"); +ThreadListResult result = api.doRequestBlocking(client, server, token); + +for (Thread thread : result.getThreads()) { +// 遍历结果 +} +``` + + + + +```java +// QQ频道API请求用的 token +String token = "Bot xxx"; + +// Ktor 的 HttpClient +// 此处选择使用 HttpClientJvmKt.HttpClient 自动加载环境中存在的 HttpClient 引擎 +// 请保证classpath中存在一个可用的 HttpClient JVM 引擎 +HttpClient client = HttpClientJvmKt.HttpClient(config -> Unit.INSTANCE); + +// 请求的服务器地址 +// 此处为沙箱地址,也可选择正式地址或其他第三方代理地址 +Url server = QQGuild.SANDBOX_URL; + +GetThreadListApi api = GetThreadListApi.create("channel ID"); +CompletableFuture result = api.doRequestAsync(client, server, token); + +result.thenApply(ThreadListResult::getThreads) + .thenAccept(threads -> { + for (Thread thread : threads) { + // 遍历结果 + } + }); +``` + + + + +## 组件能力 + +在组件模块 `core` 中,也同样针对论坛子频道的相关内容提供了API。 +在组件模块中提供了一些新的类型: + +- `QGForumChannel` : 表示论坛子频道的 `Channel` 实现 +- `QGForums` : 表示一个 `QGGuild` 针对帖子的相关操作 +- `QGThread` : 表示一个主题帖 +- `QGThreadCreator` : 一个用于构造并发布帖子的构造器 + + +> 这些操作大多从 `QGGuild` 作为入口提供。 + + + + + +```kotlin +val guild: QGGuild = .... + +// 在所有的子频道中筛选出 论坛子频道 +guild.channels.asFlow() + // highlight-next-line + .filterIsInstance() + .collect { channel: QGForumChannel -> + // ... + channel.threads.collect { + // 获取所有的主题帖 + } + val thread: QGThread? = channel.thread("123".ID) // 获取指定ID的主题帖 + + // 构造并发布一个主题贴 + channel.createThread { + title = ... + content = ... + format = ... + } + + // 假设其不为null + // 删除某个主题帖 + thread!!.delete() +} +``` + +除了在 `channels` 中通过类型筛选以外,也可以通过 `QGGuild.forums` 来进行操作: + +```kotlin +val guild: QGGuild = .... + +// 在所有的子频道中筛选出 论坛子频道 +// highlight-next-line +guild.forums.forumChannels + .collect { channel: QGForumChannel -> + // ... + channel.threads.collect { + // 获取所有的主题帖 + } + val thread: QGThread? = channel.thread("123".ID) // 获取指定ID的主题帖 + // 构造并发布一个主题贴 + channel.createThread { + title = ... + content = ... + format = ... + } + // 假设其不为null + // 删除某个主题帖 + thread!!.delete() +} +// 根据ID获取指定的 论坛子频道 实例 +val forumChannel: QGForumChannel? = guild.forums.forumChannel("666".ID) + +``` + + + + +```java +QGGuild guild = ...; + +// 直接遍历 +// 你也可以选择转为列表后再操作 +guild.getChannels().collect(channel -> { + // 遍历所有的论坛子频道 + // highlight-next-line + if (channel instanceof QGForumChannel forumChannel) { + // 获取所有的主题帖并遍历 + forumChannel.getThreads().collect(thread -> { + // ... + }); + // 获取指定ID的主题帖 + QGThread thread = forumChannel.getThread(Identifies.ID("123")); // nullable + // 构造并发布一个主题贴 + forumChannel.threadCreator() + .title(...) + .content(...) + .format(...) + .publishBlocking(); + // 假设其不为null + assert thread != null; + // 删除某个主题帖 + thread.deleteBlocking(); + } +}); +``` + +除了在 `channels` 中通过类型筛选以外,也可以通过 `QGGuild.forums` 来进行操作: + +```java +QGGuild guild = ...; + +// 直接遍历 +// 你也可以选择转为列表后再操作 +guild.getForums() + // highlight-next-line + .getForumChannels() + .collect(forumChannel -> { + // 遍历所有的论坛子频道 + // 获取所有的主题帖并遍历 + forumChannel.getThreads().collect(thread -> { + // ... + }); + // 获取指定ID的主题帖 + QGThread thread = forumChannel.getThread(Identifies.ID("123")); // nullable + // 构造并发布一个主题贴 + forumChannel.threadCreator() + .title(...) + .content(...) + .format(...) + .publishBlocking(); + // 假设其不为null + assert thread != null; + // 删除某个主题帖 + thread.deleteBlocking(); + }); +``` + + + + +```java +QGGuild guild = ...; + +// 直接遍历 +// 你也可以选择转为列表后再操作 +guild.getChannels().collectAsync(channel -> { + // 遍历所有的论坛子频道 + // highlight-next-line + if (channel instanceof QGForumChannel forumChannel) { + // 获取所有的主题帖并遍历 + forumChannel.getThreads().collectAsync(thread -> { + // ... + }); // thenXxx? + // 获取指定ID的主题帖 + CompletableFuture threadAsync = forumChannel.getThreadAsync(Identifies.ID("123")); + // 构造并发布一个主题贴 + forumChannel.threadCreator() + .title(...) + .content(...) + .format(...) + .publishAsync(); + threadAsync.thenAccept(thread -> { // or use thenCompose + // 假设其不为null + assert thread != null; + // 删除某个主题帖 + thread.deleteAsync(); + }); + } +}); +``` + +除了在 `channels` 中通过类型筛选以外,也可以通过 `QGGuild.forums` 来进行操作: + +```java +QGGuild guild = ...; + +// 直接遍历 +// 你也可以选择转为列表后再操作 +guild.getForums() + // highlight-next-line + .getForumChannels() + .collectAsync(forumChannel -> { + // 获取所有的主题帖并遍历 + forumChannel.getThreads().collectAsync(thread -> { + // ... + }); // thenXxx? + // 获取指定ID的主题帖 + CompletableFuture threadAsync = forumChannel.getThreadAsync(Identifies.ID("123")); + // 构造并发布一个主题贴 + forumChannel.threadCreator() + .title(...) + .content(...) + .format(...) + .publishAsync(); + threadAsync.thenAccept(thread -> { // or use thenCompose + // 假设其不为null + assert thread != null; + // 删除某个主题帖 + thread.deleteAsync(); + }); + }); +``` + + + + + +## API事件 + +API模块实现了与论坛相关的事件类型,它们的类型(与继承关系)如下: + +- `OpenForumDispatch` : **开放论坛事件** + - `OpenForumThreadDispatch` : 开放论坛事件 - **主题贴事件** + - `OpenForumThreadCreate` : 主题贴事件: **主题贴创建** + - `OpenForumThreadUpdate` : 主题贴事件: **主题贴更新** + - `OpenForumThreadDelete` : 主题贴事件: **主题贴删除** + - `OpenForumPostDispatch` : 开放论坛事件 - **评论事件** + - `OpenForumPostCreate` : 评论事件 - **评论创建** + - `OpenForumPostDelete` : 评论事件 - **评论删除** + - `OpenForumReplyDispatch` : 开放论坛事件 - **回复事件** + - `OpenForumReplyCreate` : 回复事件 - **回复创建** + - `OpenForumReplyDelete` : 回复事件 - **回复删除** + + + + +对应的 `instents` 为 `EventIntents.OpenForumsEvent.intents` + +更多可参考 [官方文档](https://bot.q.qq.com/wiki/develop/api/gateway/open_forum.html#oepn-forum-event-intents-open-forum-event) + + + + +- `ForumDispatch` : **论坛事件** + - `ForumThreadDispatch` : 论坛事件 - **主题贴事件** + - `ForumThreadCreate` : 主题贴事件: **主题贴创建** + - `ForumThreadUpdate` : 主题贴事件: **主题贴更新** + - `ForumThreadDelete` : 主题贴事件: **主题贴删除** + - `ForumPostDispatch` : 论坛事件 - **评论事件** + - `ForumPostCreate` : 评论事件 - **评论创建** + - `ForumPostDelete` : 评论事件 - **评论删除** + - `ForumReplyDispatch` : 论坛事件 - **回复事件** + - `ForumReplyCreate` : 回复事件 - **回复创建** + - `ForumReplyDelete` : 回复事件 - **回复删除** + - `ForumPublishAuditResult` : 论坛事件 - **帖子审核事件** + + + +对应的 `instents` 为 `EventIntents.ForumsEvent.intents` + +更多可参考 [官方文档](https://bot.q.qq.com/wiki/develop/api/gateway/forum.html) + + + + +> 非开放的论坛事件是仅支持**私域BOT**的。 + +### 标准库应用 + +在使用 `stdlib` 标准库时可以对它们进行监听,以 `OpenForumThreadCreate` 为例: + + + + +```kotlin +// 配置并创建bot +val bot = BotFactory.create("app id", "sec", "token") { + useSandboxServerUrl() + // 为了示例,增加对 OpenForumsEvent 事件的支持 + intents += EventIntents.OpenForumsEvent.intents +} + +bot.registerProcessor { raw -> + println("OpenForumThreadCreate: $this") + println("OpenForumThreadCreate.raw: $raw") +} + +bot.start() +bot.join() +``` + + + + +```java +Bot bot = BotFactory.create("appid", "sec", "token", (config) -> { + config.useSandboxServerUrl(); + // 追加对 OpenForumsEvent 事件的订阅:OpenForumsEvent 与默认订阅合并 + config.setIntentsValue( + config.getIntentsValue() | EventIntents.OpenForumsEvent.getIntents() + ); + return Unit.INSTANCE; + }); + +bot.registerBlockingProcessor(OpenForumThreadCreate.class, (event, raw) -> { + System.out.println("OpenForumThreadCreate: " + event); + System.out.println("OpenForumThreadCreate.raw: " + raw); +}); + +bot.startBlocking(); +bot.joinBlocking(); +``` + + + + +```java +Bot bot = BotFactory.create("appid", "sec", "token", (config) -> { + config.useSandboxServerUrl(); + // 追加对 OpenForumsEvent 事件的订阅:OpenForumsEvent 与默认订阅合并 + config.setIntentsValue( + config.getIntentsValue() | EventIntents.OpenForumsEvent.getIntents() + ); + return Unit.INSTANCE; + }); + +bot.registerAsyncProcessor(OpenForumThreadCreate.class, (event, raw) -> { + System.out.println("OpenForumThreadCreate: " + event); + System.out.println("OpenForumThreadCreate.raw: " + raw); + return CompletableFuture.completedFuture(null); // Void? +}); + +bot.startAsync().thenCompose((v) -> bot.joinAsync()).join(); +``` + + + + +### 组件模块应用 + +#### core 组件模块 + +core 组件模块基于 simbot api 针对上述事件提供了进一步的封装实现: + +- `QGOpenForumEvent` : **开放论坛事件** + - `QGOpenForumThreadEvent` : 开放论坛事件 - **主题贴事件** + - `QGOpenForumThreadCreateEvent` : 主题贴事件: **主题贴创建** + - `QGOpenForumThreadUpdateEvent` : 主题贴事件: **主题贴更新** + - `QGOpenForumThreadDeleteEvent` : 主题贴事件: **主题贴删除** + - `QGOpenForumPostEvent` : 开放论坛事件 - **评论事件** + - `QGOpenForumPostCreateEvent` : 评论事件 - **评论创建** + - `QGOpenForumPostDeleteEvent` : 评论事件 - **评论删除** + - `QGOpenForumReplyEvent` : 开放论坛事件 - **回复事件** + - `QGOpenForumReplyCreateEvent` : 回复事件 - **回复创建** + - `QGOpenForumReplyDeleteEvent` : 回复事件 - **回复删除** + + +- `QGForumEvent` : **论坛事件** + - `QGForumThreadEvent` : 论坛事件 - **主题贴事件** + - `QGForumThreadCreateEvent` : 主题贴事件: **主题贴创建** + - `QGForumThreadUpdateEvent` : 主题贴事件: **主题贴更新** + - `QGForumThreadDeleteEvent` : 主题贴事件: **主题贴删除** + - `QGForumPostEvent` : 论坛事件 - **评论事件** + - `QGForumPostCreateEvent` : 评论事件 - **评论创建** + - `QGForumPostDeleteEvent` : 评论事件 - **评论删除** + - `QGForumReplyEvent` : 论坛事件 - **回复事件** + - `QGForumReplyCreateEvent` : 回复事件 - **回复创建** + - `QGForumReplyDeleteEvent` : 回复事件 - **回复删除** + - `QGForumPublishAuditResultEvent` : 论坛事件 - **帖子审核事件** + +它们基本上与 API 模块中的基础实现类型一一对应。 + +在使用 simbot 核心库时: + + + + +```kotlin +val app = createSimpleApplication { + useQQGuild() +} + +app.eventListenerManager.listeners { + // 所有开放论坛事件 + QGOpenForumEvent { + println("Open forum event: $it") + } + + // 所有论坛事件 + QGForumEvent { + println("Forum event: $it") + } +} + +app.qqGuildBots { + val bot = register("appid", "sec", "token") { + botConfig { + useSandboxServerUrl() + // 追加事件订阅 + intents += EventIntents.OpenForumsEvent.intents + EventIntents.ForumsEvent.intents + } + } + + bot.start() +} + +app.join() +``` + + + + +```java +SimpleApplication application = Applications.buildSimbotApplication(Simple.INSTANCE) + .build((builder, c) -> { + // 安装QQ频道组件相关的内容 + builder.install(QQGuildComponent.Factory, (__, ___) -> Unit.INSTANCE); + builder.install(QQGuildBotManager.Factory, (__, ___) -> Unit.INSTANCE); + }).createBlocking(); + +// 所有开放论坛事件 +application.getEventListenerManager().register(SimpleListeners.listener(QGOpenForumEvent.Key, (context, event) -> { + System.out.println("QG open forum event: " + event); +})); + +// 所有论坛事件 +application.getEventListenerManager().register(SimpleListeners.listener(QGForumEvent.Key, (context, event) -> { + System.out.println("QG forum event: " + event); +})); + +// 寻找并注册QQGuildBot +for (BotManager botManager : application.getBotManagers()) { + if (botManager instanceof QQGuildBotManager qqGuildBotManager) { + QGBog bot = qqGuildBotManager.register("appid", "sec", "token", (config) -> { + config.botConfig(botConfig -> { + botConfig.useSandboxServerUrl(); + // 追加对 OpenForumsEvent 事件的订阅:OpenForumsEvent 与默认订阅合并 + botConfig.setIntentsValue( + botConfig.getIntentsValue() | EventIntents.OpenForumsEvent.getIntents() + ); + return Unit.INSTANCE; + }); + return Unit.INSTANCE; + }); + + bot.startBlocking(); + break; + } +} + +application.joinBlocking(); +``` + + + + +```java +Applications.buildSimbotApplication(Simple.INSTANCE) + .build((builder, c) -> { + // 安装QQ频道组件相关的内容 + builder.install(QQGuildComponent.Factory, (__, ___) -> Unit.INSTANCE); + builder.install(QQGuildBotManager.Factory, (__, ___) -> Unit.INSTANCE); + }).createAsync().thenAccept(application -> { + // 所有开放论坛事件 + application.getEventListenerManager().register(SimpleListeners.listener(QGOpenForumEvent.Key, (context, event) -> { + System.out.println("QG open forum event: " + event); + })); + // 所有论坛事件 + application.getEventListenerManager().register(SimpleListeners.listener(QGForumEvent.Key, (context, event) -> { + System.out.println("QG forum event: " + event); + })); + // 寻找并注册QQGuildBot + for (BotManager botManager : application.getBotManagers()) { + if (botManager instanceof QQGuildBotManager qqGuildBotManager) { + QGBot bot = qqGuildBotManager.register("appid", "sec", "token", (config) -> { + config.botConfig(botConfig -> { + botConfig.useSandboxServerUrl(); + // 追加对 OpenForumsEvent 事件的订阅:OpenForumsEvent 与默认订阅合并 + botConfig.setIntentsValue( + botConfig.getIntentsValue() | EventIntents.OpenForumsEvent.getIntents() + ); + return Unit.INSTANCE; + }); + return Unit.INSTANCE; + }); + bot.startAsync(); + break; + } + } + }) + .join(); +``` + + + + +#### SpringBoot + +或在 SpringBoot 中: + +> _省略掉有关SpringBoot项目本身的配置说明_ + + + + +```kotlin +@Listener +suspend fun onForumEvent(event: QGForumEvent) { + println("Event: $event") +} +``` + + + + +```java +@Listener +public void onForumEvent(QGForumEvent event) { + System.out.println("Event: " + event); +} +``` + + + + +```java +@Listener +public CompletableFuture onForumEvent(QGForumEvent event) { + System.out.println("Event: " + event); + return CompletableFuture.completedFuture(null); +} +``` + + + diff --git a/Writerside/topics/old/3.2.0.0/old-bot-config.md b/Writerside/topics/old/3.2.0.0/old-bot-config.md new file mode 100644 index 00000000..b58764f1 --- /dev/null +++ b/Writerside/topics/old/3.2.0.0/old-bot-config.md @@ -0,0 +1,223 @@ +# BOT配置文件 + +```json +{ + "component": "simbot.qqguild", + "ticket": { + "type": "plain", + "appId": "APPID", + "token": "TOKEN", + "secret": "SECRET" + }, + "config": { + "serverUrl": null, + "shard": { + "type": "full" + }, + "intents": { + "type": "raw", + "intents": 1073741827 + }, + "timeout": { + "apiHttpRequestTimeoutMillis": null, + "apiHttpConnectTimeoutMillis": null, + "apiHttpSocketTimeoutMillis": null + }, + "cache": { + "enable": true, + "transmit": { + "enable": true + } + }, + "clientProperties": null + } +} +``` + + +> 文件配置类的各属性定义可参考API文档: +> [`QGBotFileConfiguration`](https://docs.simbot.forte.love/components/qq-guild/simbot-component-qq-guild-core-common/love.forte.simbot.component.qguild.config/-q-g-bot-file-configuration/index.html) + +## 配置项 + +### component + +固定值 `simbot.qqguild`,**必填**,代表此配置文件为QQ频道组件的。 + +### ticket + +bot的票据信息,**必填**。 + +- `type`: 配置属性的类型,详见后文 +- `appId`: BotAppID +- `token`: 机器人令牌 +- `secret`: 机器人密钥 (目前可能不会用到,可以用 `""` 代替) +- +#### plain + +当 `type=plain` 时,与 `Ticket` 属性基本一致的配置类型, 也是默认的方案。 + +```json +{ + "ticket": { + "type": "plain", + "appId": "appId-value", + "secret": "secret-value", + "token": "token-value" + } +} +``` + +:::note 省略type + +当 simbot-core 版本为 `3.2.0+` 时,`type` 作为默认值 `plain` 时可以省略: + +```json +{ + "ticket": { + "appId": "appId-value", + "secret": "secret-value", + "token": "token-value" + } +} +``` + +::: + +#### env + +当 `type=env` 时,使用环境变量的方式进行配置。 + +```json +{ + "ticket": { + "type": "env", + "appId": "APP_ID", + "secret": "SECRET", + "token": "TOKEN", + "plain": false + } +} +``` + +解析时会首先尝试获取 JVM 参数,即运行时的 `-Dxxx=xxx` (也就是 `System.getProperty`), +当不存在时会尝试通过环境变量获取(即 `System.getenv`)。 + +**原始输入** + +当 `plain` 为 `true` 时(默认为 `false`),如果某属性通过上述流程无法获取到值,则会尝试直接使用原始输入值。 + +例如: + +```json +{ + "ticket": { + "type": "env", + "appId": "aaa", + "secret": "MY_SECRET", + "token": "MY_TOKEN", + "plain": true + } +} +``` + +示例中的 `appId` 并没有找到名为 `aaa` 的 JVM 参数或环境变量,因此它会直接使用 `aaa` 作为 `appId`。 +而如果 `plain` 为 `false`,则会直接抛出 `IllegalStateException` 异常。 + +当一个属性以 `PLAIN:` (区分大小写) 为前缀,则会直接使用原始输入值,不会尝试从环境变量中获取。 + +例如: + +```json +{ + "ticket": { + "type": "env", + "appId": "PLAIN:aaa", + "secret": "MY_SECRET", + "token": "MY_TOKEN", + "plain": false + } +} +``` + +示例中 `appId` 会直接使用 `aaa` 作为 `appId`,而不会尝试从 JVM 参数或环境变量中获取。 + +### config + +其他配置,可选,默认为 `null`。 + +#### config.serverUrl + +内部进行API请求时的服务器地址,参考[官方文档](https://bot.q.qq.com/wiki/develop/api/) + +默认为 `null`,为 `null` 时为正式环境,可使用一个固定值 `SANDBOX` 代表使用沙箱环境 + +```json +{ + "config": { + "serverUrl": "SANDBOX" + } +} +``` + +或者使用一个具体的其他服务器地址 + +```json +{ + "config": { + "serverUrl": "https://example.com" + } +} +``` + +#### config.shard + +[分片信息](https://bot.q.qq.com/wiki/develop/api/gateway/shard.html),默认为 `type=full`,即使用 `[0, 1]` 的分片。 + +可以使用 `type=simple` 自定义分片: + +```json +{ + "config": { + "shard": { + "type": "simple", + "value": 0, + "total": 2 + } + } +} +``` + +#### config.intents + +[订阅的事件](https://bot.q.qq.com/wiki/develop/api/gateway/intents.html),默认情况下订阅: + +- `Guilds` +- `GuildMembers` +- `PublicGuildMessages` + +可通过 `type=raw` 来直接指定一个原始的订阅标记结果值: + +```json +{ + "config": { + "intents": { + "type": "raw", + "intents": 1073741827 + } + } +} +``` + +或者使用 `type=nameBased` 通过指定名称(名称选择参考 `EventIntents` 类的所有 `object` 类型的字类类名): + +```json +{ + "config": { + "intents": { + "type": "nameBased", + "names": ["Guilds", "GuildMembers", "PublicGuildMessages"] + } + } +} +``` diff --git a/Writerside/topics/old/3.2.0.0/quickstart/old-api.md b/Writerside/topics/old/3.2.0.0/quickstart/old-api.md new file mode 100644 index 00000000..e5de4e03 --- /dev/null +++ b/Writerside/topics/old/3.2.0.0/quickstart/old-api.md @@ -0,0 +1,188 @@ +# 使用API + +**API模块**是独立的、多平台的,你可以单独使用它作为 [QQ频道API](https://bot.q.qq.com/wiki/develop/api/) 的封装库。 + + +## 安装 + + + + +```Kotlin +// 不要忘记使用 Gradle 的 kotlin 插件来允许自动选择对应平台,比如JVM或JS等。 +implementation("love.forte.simbot.component:simbot-component-qq-gulid-api:3.2.0.0") // 或参考下文所述的 Releases +``` + + + + +```Groovy +// 不要忘记使用 Gradle 的 kotlin 插件来允许自动选择对应平台,比如JVM或JS等。 +implementation 'love.forte.simbot.component:simbot-component-qq-gulid-api:3.2.0.0' // 版本参考下文所述的 Releases +``` + + + + +```xml + + love.forte.simbot.component + + simbot-component-qq-guild-api-jvm + + 3.2.0.0 + +``` + + + + + +:::info 版本参考 + +版本可前往 [**Releases**](https://github.com/simple-robot/simbot-component-qq-guild/releases) 查阅。 + +::: + +## 使用 + +:::tip 太多了 + +我们不会在此处一一列举所有的API做演示,这不太现实。 +所有的API都在包路径 `love.forte.simbot.qguild.api` 下,你可以通过 [API文档](https://docs.simbot.forte.love/) 或查阅源码的方式来寻找你所需要的API。 + +API包装类的命名也存在一定的规律,比如一个 `获取某列表` 的API通常会被命名为 `GetXxxListApi`。 + +下文会选择一小部分API来做示例。 + +::: + +### 获取用户频道服务器列表 + +以 [获取用户(BOT)频道服务器列表](https://bot.q.qq.com/wiki/develop/api/openapi/user/guilds.html) 为例。 + + + + +```kotlin +// 准备参数 +// 用于请求的token +val token = "Bot 123456789.ABABABABABABABABABABABABABABABABAB" +// 用于请求的 Ktor HttpClient,如有必要则需要自行引入并选择需要使用的引擎。参考:https://ktor.io/docs/http-client-engines.html +val client = HttpClient() +// 需要请求的环境的服务器地址,比如正式环境或沙箱环境,亦或是某个自己定义代理的第三方环境 +// 可以通过 QQGuild 得到一些预定义的常量信息 +val server = QQGuild.SANDBOX_URL + +// 使用 GetBotGuildListApi 获取频道列表 +// 创建了一个参数 limit=100 的 GetBotGuildListApi,并使用上述准备好的参数进行请求。 +val list: List = GetBotGuildListApi.create(limit = 100).request(client, server, token) + +list.forEach { ... } +``` + +也可以通过额外的扩展函数来获得一个**全量数据**的数据流。 + +```kotlin +// 准备参数 +// 用于请求的token +val token = "Bot 123456789.ABABABABABABABABABABABABABABABABAB" +// 用于请求的 Ktor HttpClient,如有必要则需要自行引入并选择需要使用的引擎。参考:https://ktor.io/docs/http-client-engines.html +val client = HttpClient() +// 需要请求的环境的服务器地址,比如正式环境或沙箱环境,亦或是某个自己定义代理的第三方环境 +// 可以通过 QQGuild 得到一些预定义的常量信息 +val server = QQGuild.SANDBOX_URL + +// 使用 GetBotGuildListApi 获取频道列表 +// 创建了一个每页数据的数据量都为 100 的全量数据流,每一页都使用上述准备好的参数进行请求。 +val guildFlow: Flow = GetBotGuildListApi.createFlow(batch = 100) { request(client, QQGuild.SANDBOX_URL, token) } +guildFlow.collect { guild -> + // ... +} +``` + + + + +```java +// 准备参数 +// 用于请求的token +String token = "Bot 123456789.ABABABABABABABABABABABABABABABABAB"; +// 用于请求的 Ktor HttpClient,如有必要则需要自行引入并选择需要使用的引擎。参考:https://ktor.io/docs/http-client-engines.html +HttpClient client = HttpClientJvmKt.HttpClient(($1) -> Unit.INSTANCE); +// 或者通过jvm平台库提供的工具类来构建一个默认的 client。(需要环境中存在一种引擎) +HttpClient newClient = ApiRequestUtil.newHttpClient(); +// 需要请求的环境的服务器地址,比如正式环境或沙箱环境,亦或是某个自己定义代理的第三方环境 +// 可以通过 QQGuild 得到一些预定义的常量信息 +Url server = QQGuild.SANDBOX_URL; + +// 使用 GetBotGuildListApi 获取频道列表, +// 创建了一个 limit = 100 的 GetBotGuildListApi +GetBotGuildListApi api = GetBotGuildListApi.create(100); + +// 发起请求并得到结果 +List guildList = api.doRequestBlocking(client, server, token); + +for( +SimpleGuild guild :guildList){ + // ... + } +``` + + + + +```java +// 准备参数 +// 用于请求的token +String token = "Bot 123456789.ABABABABABABABABABABABABABABABABAB"; +// 用于请求的 Ktor HttpClient,如有必要则需要自行引入并选择需要使用的引擎。参考:https://ktor.io/docs/http-client-engines.html +HttpClient client = HttpClientJvmKt.HttpClient(($1) -> Unit.INSTANCE); +// 或者通过jvm平台库提供的工具类来构建一个默认的 client。(需要环境中存在一种引擎) +HttpClient newClient = ApiRequestUtil.newHttpClient(); +// 需要请求的环境的服务器地址,比如正式环境或沙箱环境,亦或是某个自己定义代理的第三方环境 +// 可以通过 QQGuild 得到一些预定义的常量信息 +Url server = QQGuild.SANDBOX_URL; + +// 使用 GetBotGuildListApi 获取频道列表, +// 创建了一个 limit = 100 的 GetBotGuildListApi +GetBotGuildListApi api = GetBotGuildListApi.create(100); + +// 发起请求并得到 Future 结果 +api.doRequestAsync(client, server, token).thenAccept(guildList -> { + for (SimpleGuild guild : guildList) { + // ... + } +}); +``` + + + + +```java +// 准备参数 +// 用于请求的token +String token = "Bot 123456789.ABABABABABABABABABABABABABABABABAB"; +// 用于请求的 Ktor HttpClient,如有必要则需要自行引入并选择需要使用的引擎。参考:https://ktor.io/docs/http-client-engines.html +HttpClient client = HttpClientJvmKt.HttpClient(($1) -> Unit.INSTANCE); +// 或者通过jvm平台库提供的工具类来构建一个默认的 client。(需要环境中存在一种引擎) +HttpClient newClient = ApiRequestUtil.newHttpClient(); +// 需要请求的环境的服务器地址,比如正式环境或沙箱环境,亦或是某个自己定义代理的第三方环境 +// 可以通过 QQGuild 得到一些预定义的常量信息 +Url server = QQGuild.SANDBOX_URL; + +// 使用 GetBotGuildListApi 获取频道列表, +// 创建了一个 limit = 100 的 GetBotGuildListApi +GetBotGuildListApi api = GetBotGuildListApi.create(100); + +// 发起请求并得到响应式结果 +Flux guildFlux = Mono.fromCompletionStage(api.doRequestAsync(client, server, token)) + .flatMapIterable(Function.identity()); + +return guildFlux; +``` + + + + + diff --git a/Writerside/topics/old/3.2.0.0/quickstart/old-core.md b/Writerside/topics/old/3.2.0.0/quickstart/old-core.md new file mode 100644 index 00000000..8c5b2b9e --- /dev/null +++ b/Writerside/topics/old/3.2.0.0/quickstart/old-core.md @@ -0,0 +1,495 @@ +# 使用simbot核心库 + +通过**core模块**配合**simbot核心库**来使用强大而简单的事件调度能力。 + + + +core模块亦可以称之为simbot3的**组件库**,因为它事实上实现了simbot所提供的各种API和对simbot事件类型的封装。 + + + +## 前提准备 + +首先你应当准备至少一个可用的 [QQ频道机器人](https://q.qq.com/bot) 。 + + + +simbot核心库中的API大多是以**DSL风格**为主的。尽管其依旧是对 Java "友好", 但以 Java 的代码风格而言依旧会 _略显臃肿_。 + +如果你不介意,我们更建议 Java 开发者直接使用 [SpringBoot](old-spring-boot.md),因为这更符合大多数 Java 开发者的习惯。 + +你也可以观察后续的代码示例,来体会DSL风格的API在 Kotlin 和 Java 之间的差异。 + + + +## 安装 + + + + +```kotlin +// simbot core starter +implementation("love.forte.simbot:simbot-core:3.3.0") // 版本请参考下文的参考链接 +// QQ频道组件 +implementation("love.forte.simbot.component:simbot-component-qq-guild-core:3.2.0.0") // 或参考下文的参考链接 +``` + + + + +```gradle +// simbot core starter +implementation 'love.forte.simbot:simbot-core:3.3.0' // 版本请参考下文的参考链接 +// QQ频道组件 +implementation 'love.forte.simbot.component:simbot-component-qq-guild-core:3.2.0.0' // 或参考下文的参考链接 +``` + + + + +```xml + + + love.forte.simbot + simbot-core + \${SIMBOT_VERSION} + + + +love.forte.simbot.component +simbot-component-qq-guild-core +3.2.0.0 + + +``` + + + + + + + + + +simbot核心库(`simbot-core`)的版本可前往 [**simbot Releases**](https://github.com/simple-robot/simpler-robot/releases) +查阅。 + +QQ频道组件版本可前往 [**Releases**](https://github.com/simple-robot/simbot-component-qq-guild/releases) 查阅。 + + + + +## 构建Application + +首先构建一个 simbot 的 `Application`,这里以 `SimpleApplication` (也就是最基础的实现) 为例: + + + + +```kotlin title='com.example.App' +val application = createSimpleApplication { + // 安装QQ频道相关的组件和Bot管理器,并暂且忽略配置 + install(QQGuildComponent) + install(QQGuildBotManager) +} + +application.join() // 挂起直到被关闭 +``` + +或使用扩展函数来简化上述代码: + +```kotlin title='com.example.App' +val application = createSimpleApplication { + // 安装QQ频道相关的组件和Bot管理器,并暂且忽略配置 + useQQGuild() +} + +application.join() // 挂起直到被关闭 +``` + + + + +```java title='com.example.App' +final ApplicationLauncher launcher = Applications.simbotApplication(Simple.INSTANCE, c -> { /* Application配置 */ }, (builder, configuration) -> { + // 安装QQ频道相关的组件和Bot管理器,并暂且忽略配置 + builder.install(QQGuildComponent.Factory, (__, ___) -> Unit.INSTANCE); + builder.install(QQGuildBotManager.Factory, (__, ___) -> Unit.INSTANCE); +}); + +// 启动 application +SimpleApplication application = launcher.launchBlocking(); +application. + +joinBlocking(); // 阻塞直到被关闭 +``` + + + + +```java title='com.example.App' +final ApplicationLauncher launcher = Applications.simbotApplication(Simple.INSTANCE, c -> { /* Application配置 */ }, (builder, configuration) -> { + // 安装QQ频道相关的组件和Bot管理器,并暂且忽略配置 + builder.install(QQGuildComponent.Factory, (__, ___) -> Unit.INSTANCE); + builder.install(QQGuildBotManager.Factory, (__, ___) -> Unit.INSTANCE); +}); + +// 启动 application, 结束后转化为 Future 并阻塞直到完成(application被关闭) +launcher. + +launchAsync(). + +thenCompose(SimpleApplication::asFuture). + +join(); +``` + + + + + +## 监听函数 + +完成对Application的构建之后,我们便可以开始注册监听函数用以处理事件了。 + +下面我们实现如此功能: 当收到一个**(公域)子频道消息**,且内容为 "你好",则**引用回复**一句 "你也好"。 +流程大致为: + +```text +用户: +@BOT 你好 + +BOT: +> 用户: @BOT 你好 +你也好 +``` + +> 在公域中的BOT要想收到消息必须被 `@` 。 + + + + +```kotlin title='com.example.App' +val application = createSimpleApplication { + // 安装QQ频道相关的组件和Bot管理器,并暂且忽略配置 + install(QQGuildComponent) + install(QQGuildBotManager) +} + +// 获取 eventListenerManager 并注册监听函数 +application.eventListenerManager.listeners { + // highlight-start + ChannelMessageEvent { event -> + // 当下述条件满足,则此处才被执行,并引用回复一句 '你也好' + event.reply("你也好") + } onMatch { event -> + // 匹配判断: 当消息中存在任何 AT 消息,且at的目标是bot,且消息文本为 '你好' 时 + val bot = event.bot + event.messageContent.messages.any { it is At && bot.isMe(it.target) } + && event.messageContent.plainText.trim() == "你好" + } + // highlight-end +} + +application.join() // 挂起直到被关闭 +``` + + + + +```java title='com.example.App' +final ApplicationLauncher launcher = Applications.simbotApplication(Simple.INSTANCE, c -> { /* Application配置 */ }, (builder, configuration) -> { + // 安装QQ频道相关的组件和Bot管理器,并暂且忽略配置 + builder.install(QQGuildComponent.Factory, (__, ___) -> Unit.INSTANCE); + builder.install(QQGuildBotManager.Factory, (__, ___) -> Unit.INSTANCE); +}); + +// 启动 application +SimpleApplication application = launcher.launchBlocking(); + +// 获取 EventListenerManager 并用于注册监听函数 +EventListenerManager eventListenerManager = application.getEventListenerManager(); + +// 注册一个 EventListener 实例,此实例通过 SimpleListeners.listener(...) 构建而得。 +// highlight-start +eventListenerManager. + +register(SimpleListeners.listener(ChannelMessageEvent.Key, (context, event) ->{ +// 匹配判断: 当消息中存在任何 AT 消息,且at的目标是bot,且消息文本为 '你好' 时 +Bot bot = event.getBot(); +boolean atBot = false; +MessageContent messageContent = event.getMessageContent(); + for( +Message.Element message :messageContent. + +getMessages()){ + if(message instanceof +At at &&bot. + +isMe(at.getTarget())){ +atBot =true; + break; + } + } + + return atBot &&"你好". + +equals(messageContent.getPlainText(). + +trim()); + },(context,event)->{ + // 当上面的匹配通过,则此处才被执行,并引用回复一句 '你也好' + event. + +replyBlocking("你也好"); +})); +// highlight-end + + application. + +joinBlocking(); // 阻塞直到被关闭 +``` + + + + +```java title='com.example.App' +final ApplicationLauncher launcher = Applications.simbotApplication(Simple.INSTANCE, c -> { /* Application配置 */ }, (builder, configuration) -> { + // 安装QQ频道相关的组件和Bot管理器,并暂且忽略配置 + builder.install(QQGuildComponent.Factory, (__, ___) -> Unit.INSTANCE); + builder.install(QQGuildBotManager.Factory, (__, ___) -> Unit.INSTANCE); +}); + +// 启动 application 结束后转化为 Future 并阻塞直到完成(application被关闭) +launcher. + +launchAsync(). + +thenCompose(application ->{ +SimpleEventListenerManager eventListenerManager = application.getEventListenerManager(); +// highlight-start + eventListenerManager. + +register(SimpleListeners.listener(ChannelMessageEvent.Key, (context, event) ->{ +// 匹配判断: 当消息中存在任何 AT 消息,且at的目标是bot,且消息文本为 '你好' 时 +Bot bot = event.getBot(); +boolean atBot = false; +MessageContent messageContent = event.getMessageContent(); + for( +Message.Element message :messageContent. + +getMessages()){ + if(message instanceof +At at &&bot. + +isMe(at.getTarget())){ +atBot =true; + break; + } + } + + return atBot &&"你好". + +equals(messageContent.getPlainText(). + +trim()); + },(context,event)->{ +// 当上面的匹配通过,则此处才被执行,并引用回复一句 '你也好' +CompletableFuture replyAsync = event.replyAsync("你也好"); + return EventResult. + +of(replyAsync); // 通过 EventResult.of(future) 将你的异步结果返回,事件处理器会挂起并处理此异步结果 + })); + // highlight-end + + return application. + +asFuture(); +}). + +join(); +``` + + + + + +> 其实上述示例中,在BOT为公域的情况下也可以不去判断 `At` 消息类型,直接判断 `plainText` 是否为 `"你好"` 即可。 + +## 注册bot + +当监听函数注册完了之后,我们就需要注册bot了。当bot被注册并启动后,它们收到的事件就会流入到Application中的事件处理器中被统一处理。 + + + + +```kotlin title='com.example.App' +val application = createSimpleApplication { + // 安装QQ频道相关的组件和Bot管理器,并暂且忽略配置 + install(QQGuildComponent) + install(QQGuildBotManager) +} + +application.eventListenerManager.listeners { + // 注册事件... +} + +// 寻找到一个 QQGuildBotManager 并注册bot +// highlight-start +val qqGuildBotManager = application.botManagers.first { it is QQGuildBotManager } as QQGuildBotManager +val bot: QGBot = qqGuildBotManager.register("APP ID", "secret", "token") { // this: QGBotComponentConfiguration + // **组件BOT**的额外配置信息 + this.cacheConfig = CacheConfig(enable = true, TransmitCacheConfig(enable = true)) // 启用 '传递性缓存', 此缓存配置默认启用 + // 其他额外配置... + + botConfig { + // **此处为对应 stdlib bot 的配置信息 + } +} +bot.start() // 启用bot +// highlight-end + +application.join() // 挂起直到被关闭 +``` + +或者通过扩展函数来简化上述代码: + +```kotlin +val application = createSimpleApplication { + // 安装QQ频道相关的组件和Bot管理器,并暂且忽略配置 + install(QQGuildComponent) + install(QQGuildBotManager) +} + +application.eventListenerManager.listeners { + // 注册监听函数... +} + +// 寻找到一个 QQGuildBotManager 并注册bot +// highlight-start +application.qgGuildBots { + val bot: QGBot = register("APP ID", "secret", "token") { // this: QGBotComponentConfiguration + // **组件BOT**的额外配置信息 + this.cacheConfig = CacheConfig(enable = true, TransmitCacheConfig(enable = true)) // 启用 '传递性缓存', 此缓存配置默认启用 + // 其他额外配置... + + botConfig { + // **此处为对应 stdlib bot 的配置信息 + } + } + bot.start() // 启用bot +} +// highlight-end + +application.join() // 挂起直到被关闭 +``` + + + + +```java title='com.example.App' +final ApplicationLauncher launcher = Applications.simbotApplication(Simple.INSTANCE, c -> { /* Application配置 */ }, (builder, configuration) -> { + // 安装QQ频道相关的组件和Bot管理器,并暂且忽略配置 + builder.install(QQGuildComponent.Factory, (__, ___) -> Unit.INSTANCE); + builder.install(QQGuildBotManager.Factory, (__, ___) -> Unit.INSTANCE); +}); + +// 启动 application +SimpleApplication application = launcher.launchBlocking(); +// 注册监听函数... + +// highlight-start +for( +BotManager botManager :application. + +getBotManagers()){ + // 找到第一个 QQGuildBotManager 并对其进行操作 + if(botManager instanceof +QQGuildBotManager qqGuildBotManager){ +QGBot bot = qqGuildBotManager.register("APP ID", "secret", "token", configuration -> { + // **组件BOT**的额外配置信息 + configuration.setCacheConfig(...); + configuration.botConfig(botConfig -> { + // **此处为对应 stdlib bot 的配置信息 + return Unit.INSTANCE; // 结束配置 + }); + + return Unit.INSTANCE; // 结束配置 +}); + + bot. + +startBlocking(); // 阻塞启动 + + break; + } + } +// highlight-end + + application. + +joinBlocking(); // 阻塞直到被关闭 +``` + + + + +```java title='com.example.App' +final ApplicationLauncher launcher = Applications.simbotApplication(Simple.INSTANCE, c -> { /* Application配置 */ }, (builder, configuration) -> { + // 安装QQ频道相关的组件和Bot管理器,并暂且忽略配置 + builder.install(QQGuildComponent.Factory, (__, ___) -> Unit.INSTANCE); + builder.install(QQGuildBotManager.Factory, (__, ___) -> Unit.INSTANCE); +}); + +// 启动 application 结束后转化为 Future 并阻塞直到完成(application被关闭) +launcher. + +launchAsync(). + +thenCompose(application ->{ + // 注册监听函数... + // highlight-start + for( +BotManager botManager :application. + +getBotManagers()){ + // 找到第一个 QQGuildBotManager 并操作 + if(botManager instanceof +QQGuildBotManager qqGuildBotManager){ +QGBot bot = qqGuildBotManager.register("APP ID", "secret", "token", configuration -> { + // **组件BOT**的额外配置信息 + configuration.setCacheConfig(...); + configuration.botConfig(botConfig -> { + // **此处为对应 stdlib bot 的配置信息 + return Unit.INSTANCE; // 结束配置 + }); + + return Unit.INSTANCE; // 结束配置 +}); + + bot. + +startAsync(); // 异步启动。此处异步直接放养,如果需要处理异常等则需要进行操作。 + + break; + } + } + // highlight-end + + return application. + +asFuture(); +}). + +join(); +``` + + + + +## 启动 + +接下来,启动程序并在你的沙箱频道中@它试试看吧。 + +当然,如果遇到了预期外的问题也不要慌,积极反馈问题才能使我们变得更好,可以前往 [Issues](https://github.com/simple-robot/simpler-robot/issues) +反馈问题、[社区](https://github.com/orgs/simple-robot/discussions) 提出疑问。 diff --git a/Writerside/topics/old/3.2.0.0/quickstart/old-spring-boot.md b/Writerside/topics/old/3.2.0.0/quickstart/old-spring-boot.md new file mode 100644 index 00000000..2b3d1177 --- /dev/null +++ b/Writerside/topics/old/3.2.0.0/quickstart/old-spring-boot.md @@ -0,0 +1,284 @@ +# 使用 SpringBoot + +通过**core模块**配合 **simbot starter** 在 **Spring Boot** 中轻松使用。 + +## 前提准备 + +首先你应当准备至少一个可用的 [QQ频道机器人](https://q.qq.com/bot) 。 + +## 项目构建 + +首先准备一个SpringBoot项目。可以考虑前往 [start.spring.io](https://start.spring.io) 或借助IDE等工具。 + +然后再额外添加我们需要的依赖: + + + + +注意,在使用 Spring Boot 的时候你需要一些能够使程序保持运行的组件,例如通过 `spring-web` 启用一个服务器,否则程序可能会自动终止。 +因为simbot的 starter 并不提供维持程序运行的能力。 + +下述示例我们选择使用 `spring-boot-starter-webflux`,具体选择请根据你的实际需求决定。 + + + +> 下述配置示例基于 [start.spring.io](https://start.spring.io) 生成,版本号等信息请根据实际情况做修改。 + + + + +```kotlin +// simbot core starter +implementation("love.forte.simbot.boot:simboot-core-spring-boot-starter:3.3.0") // 版本请参考下文的参考链接 +// QQ频道组件 +implementation("love.forte.simbot.component:simbot-component-qq-guild-core:3.2.0.0") // 或参考下文的参考链接 +``` + + + + +```gradle +// simbot core starter +implementation 'love.forte.simbot.boot:simboot-core-spring-boot-starter:3.3.0' // 版本请参考下文的参考链接 +// QQ频道组件 +implementation 'love.forte.simbot.component:simbot-component-qq-guild-core:3.2.0.0' // 或参考下文的参考链接 +``` + + + + +```xml + + + love.forte.simbot.boot + simboot-core-spring-boot-starter + \${SIMBOT_VERSION} + + + +love.forte.simbot.component +simbot-component-qq-guild-core +3.2.0.0 + +``` + + + + + + + +- `love.forte.simbot.boot:simboot-core-spring-boot-starter` + [**版本参考**](https://github.com/simple-robot/simpler-robot/releases) +- `love.forte.simbot.component:simbot-component-qq-guild-core` + [**版本参考**](https://github.com/simple-robot/simbot-component-qq-guild/releases) + + + +## BOT配置 + +接下来,在项目**资源文件**目录下的 `simbot-bots` 文件夹中创建一个用于配置bot的配置文件 `xxx.bot.json` ( +文件名随意,扩展名应为 `.bot` 或 `.bot.json` ) , +而配置文件的内容则参考章节 [**BOT配置文件**](old-bot-config.md) 。 + +> 此路径以 IDEA 的项目结构风格为准,如果是其他IDE,使用对应的资源文件目录。 + +``` +${PROJECT_SRC}/main/resources/simbot-bots/xxx.bot.json +``` + + + +如果想要修改此路径,可在 Spring Boot 的配置文件中进行配置: + + + + +``` +# 自定义配置bot资源文件的扫描路径。 +# 默认为 classpath:simbot-bots/*.bot* +# 如果要使用本地文件可以使用 `file:` 开头 +simbot.bot-configuration-resources[0]=classpath:simbot-bots/*.bot* +``` + + + + +```yaml +simbot: + + # 自定义配置bot资源文件的扫描路径。 + # 默认为 classpath:simbot-bots/*.bot* + # 如果要使用本地文件可以使用 `file:` 开头 + bot-configuration-resources: + - 'classpath:simbot-bots/*.bot*' +``` + + + + + + +## 启动类 + +像每一个 Spring Boot 应用一样,你需要一个启动类,并通过标注 `@EnableSimbot` 来启用 `simbot` : + + + + +```kotlin title='com.example.App' +@EnableSimbot +@SpringBootApplication +class App + +fun main(vararg args: String) { + runApplication(args = args) +} +``` + + + + +> 如果你在Java中遇到了无法引用 `@EnableSimbot` 等情况,或许可以参考 +> [**这篇FAQ**](https://simple-robot-library.github.io/simbot3-website/faq/包引用异常/)。 + +```java title='com.example.App' + +@EnableSimbot +@SpringBootApplication +public class App { + public static void main(String[] args) { + SpringApplication.run(App.class, args); + } +} +``` + + + + + +## 监听事件 + +接下来就是逻辑代码所在的地方了,编写一个监听函数并监听一个事件。 + +此处我们监听 `ChannelMessageEvent`,也就是 **_子频道的消息事件_**。 + +假设:要求bot必须**被AT**,并且说一句 `你好`,此时bot会**引用**用户发送的消息并回复 `你也好!` ,类似于: + +```text +用户: +@BOT 你好 + +BOT: +> 用户: @BOT 你好 +你也好! +``` + + + + +```kotlin title='com.example.listener.ExampleListener.kt' +import love.forte.simboot.annotation.ContentTrim +import love.forte.simboot.annotation.Filter +import love.forte.simboot.annotation.Listener +import love.forte.simbot.event.ChannelMessageEvent + +@Component +class ExampleListener { + + @Listener + @Filter(value = "你好", targets = Filter.Targets(atBot = true)) + @ContentTrim // 当匹配被at时,将'at'这个特殊消息移除后,剩余的文本消息大概率存在前后空格,通过此注解在匹配的时候忽略前后空格 + suspend fun onChannelMessage(event: ChannelMessageEvent) { // 将要监听的事件类型放在参数里,即代表监听此类型的消息 + event.reply("你也好!") + } +} + + +``` + + + + +```java title='com.example.listener.ExampleListener.java' +import love.forte.simboot.annotation.ContentTrim +import love.forte.simboot.annotation.Filter +import love.forte.simboot.annotation.Listener +import love.forte.simbot.event.ChannelMessageEvent + +@Component +public class ExampleListener { + + @Listener + @Filter(value = "你好", targets = @Filter.Targets(atBot = true)) + @ContentTrim // 当匹配被at时,将'at'这个特殊消息移除后,剩余的文本消息大概率存在前后空格,通过此注解在匹配的时候忽略前后空格 + public void onChannelMessage(ChannelMessageEvent event) { // 将要监听的事件类型放在参数里,即代表监听此类型的消息 + // Java中的阻塞式API + event.replyBlocking("你也好!"); + } + +} +``` + + + + +```java title='com.example.listener.ExampleListener.java' +import love.forte.simboot.annotation.ContentTrim +import love.forte.simboot.annotation.Filter +import love.forte.simboot.annotation.Listener +import love.forte.simbot.event.ChannelMessageEvent + +@Component +public class ExampleListener { + + @Listener + @Filter(value = "你好", targets = @Filter.Targets(atBot = true)) + @ContentTrim // 当匹配被at时,将'at'这个特殊消息移除后,剩余的文本消息大概率存在前后空格,通过此注解在匹配的时候忽略前后空格 + public CompletableFuture onChannelMessage(ChannelMessageEvent event) { // 将要监听的事件类型放在参数里,即代表监听此类型的消息 + // 将 CompletableFuture 作为返回值,simbot会以非阻塞的形式处理它 + return event.replyAsync("你也好!"); + } + +} +``` + + + + +> > 如果返回值是需要第三方库的响应式类型,那么你的项目环境依赖中必须存在 `Kotlin courotines` 对其的支持库才可使用。 +> 你可以参考文档中 +> [ +_响应式的处理结果_](https://simple-robot-library.github.io/simbot3-website/docs/basic/event-listener#可响应式的处理结果) +> > 的内容。 + +```java title='com.example.listener.ExampleListener.java' +import love.forte.simboot.annotation.ContentTrim +import love.forte.simboot.annotation.Filter +import love.forte.simboot.annotation.Listener +import love.forte.simbot.event.ChannelMessageEvent + +@Component +public class ExampleListener { + + @Listener + @Filter(value = "你好", targets = @Filter.Targets(atBot = true)) + @ContentTrim // 当匹配被at时,将'at'这个特殊消息移除后,剩余的文本消息大概率存在前后空格,通过此注解在匹配的时候忽略前后空格 + public Mono onChannelMessage(ChannelMessageEvent event) { // 将要监听的事件类型放在参数里,即代表监听此类型的消息 + // 将 Mono 等响应式类型作为返回值,simbot会以非阻塞的形式处理它 + return Mono.fromCompletionStage(event.replyAsync("你也好!")); + } + +} +``` + + + + + +## 启动 + +接下来,启动程序并在你的沙箱频道中@它试试看吧。 + +当然,如果遇到了预期外的问题也不要慌,积极反馈问题才能使我们变得更好,可以前往 [Issues](https://github.com/simple-robot/simpler-robot/issues) +反馈问题、[社区](https://github.com/orgs/simple-robot/discussions) 提出疑问。 diff --git a/Writerside/topics/old/3.2.0.0/quickstart/old-stdlib.md b/Writerside/topics/old/3.2.0.0/quickstart/old-stdlib.md new file mode 100644 index 00000000..205f1e41 --- /dev/null +++ b/Writerside/topics/old/3.2.0.0/quickstart/old-stdlib.md @@ -0,0 +1,311 @@ +# 使用标准库 + +**stdlib(标准库)模块** 在 `API` 模块的基础上提供简单而轻量级的事件订阅能力。 + +## 前提准备 + +首先你应当准备至少一个可用的 [QQ频道机器人](https://q.qq.com/bot) 。 + +## 安装 + + + + +```kotlin +// 不要忘记使用 Gradle 的 kotlin 插件来允许自动选择对应平台,比如JVM或JS等。 +implementation("love.forte.simbot.component:simbot-component-qq-guild-stdlib:3.2.0.0") // 或参考下文所述的 Releases + +``` + + + + + +```gradle +// 不要忘记使用 Gradle 的 kotlin 插件来允许自动选择对应平台,比如JVM或JS等。 +implementation 'love.forte.simbot.component:simbot-component-qq-guild-stdlib:3.2.0.0' // 版本参考下文所述的 Releases + +``` + + + + +```xml + + love.forte.simbot.component + + simbot-component-qq-guild-stdlib-jvm + + 3.2.0.0 + + +``` + + + + +> 版本可前往 [**Releases**](https://github.com/simple-robot/simbot-component-qq-guild/releases) 查阅。 + +## BOT配置&注册 + +环境准备完毕后,接下来我们注册一个bot。 + + + + +```kotlin +val bot = BotFactory.create("APP ID", "secret", "token") { + // config + this.wsClientEngine = ... // 进行事件订阅所使用的 Ktor client 引擎,默认情况下会**尝试**自动加载,或直接手动指定一个支持ws连接的引擎。 + this.apiClientEngine = ... // 使用API时所使用的 Ktor client 引擎,默认情况下会**尝试**自动加载,或直接手动指定一个支持 HTTP API 的引擎。 + this.serverUrl = QQGuild.URL // 使用正式环境,默认即为正式环境 + this.useSandboxServerUrl() // 使用沙箱环境 + this.intents = ... // 要订阅的事件标记。默认订阅 频道、频道成员、公域消息 三种事件 + // 其他配置... +} + +bot.start() // 启动bot +bot.join() // 挂起bot直到bot终止 +``` + + + + +```java +Bot bot = BotFactory.create("APP ID", "secret", "token", (configuration) -> { + configuration.setWsClientEngine(...); // 进行事件订阅所使用的 Ktor client 引擎,默认情况下会**尝试**自动加载,或直接手动指定一个支持ws连接的引擎。 + configuration.setApiClientEngine(...); // 使用API时所使用的 Ktor client 引擎,默认情况下会**尝试**自动加载,或直接手动指定一个支持 HTTP API 的引擎。 + configuration.setServerUrl(QQGuild.URL); // 使用正式环境,默认即为正式环境 + configuration.useSandboxServerUrl(); // 使用沙箱环境 + configuration.setIntentsValue(...); // 要订阅的事件标记值。默认订阅 频道、频道成员、公域消息 三种事件 + // 其他配置... + + return Unit.INSTANCE; // 结束配置 +}); + +bot.startBlocking(); // 启动bot +bot.joinBlocking(); // 阻塞t直到bot终止 +``` + + + + +```java +Bot bot = BotFactory.create("APP ID", "secret", "token", (configuration) -> { + configuration.setWsClientEngine(...); // 进行事件订阅所使用的 Ktor client 引擎,默认情况下会**尝试**自动加载,或直接手动指定一个支持ws连接的引擎。 + configuration.setApiClientEngine(...); // 使用API时所使用的 Ktor client 引擎,默认情况下会**尝试**自动加载,或直接手动指定一个支持 HTTP API 的引擎。 + configuration.setServerUrl(QQGuild.URL); // 使用正式环境,默认即为正式环境 + configuration.useSandboxServerUrl(); // 使用沙箱环境 + configuration.setIntentsValue(...); // 要订阅的事件标记。默认订阅 频道、频道成员、公域消息 三种事件 + // 其他配置... + + return Unit.INSTANCE; // 结束配置 +}); + +// 阻塞直到bot终止 +bot.startAsync().thenCompose(__ -> bot.joinAsync()).join(); +``` + + + + + + +引擎的选择可参考 [**Ktor文档**](https://ktor.io/docs/http-client-engines.html#limitations)。 + +大多数情况下你需要手动指定一个具体的引擎 +(例如使用 `mingwX64` 目标时可选用 [**WinHttp**](https://ktor.io/docs/http-client-engines.html#winhttp) 引擎 ), +或者至少保证程序的运行时环境中存在可用引擎(在JVM平台下的自动加载)。 + + + + +## 事件监听(订阅) + +在启动创建bot、启动bot这个过程中间,你可以注册一些**事件处理器**来对订阅的事件进行处理。 + + + + +```kotlin +val bot = BotFactory.create("APP ID", "secret", "token") { + // ... +} + +// 订阅所有类型的事件 +// highlight-start +bot.registerProcessor { raw -> // this: Signal.Dispatch + // ... +} +// highlight-end + +// 订阅具体的事件类型 +// highlight-start +bot.registerProcessor { raw -> // this: GuildMemberAdd + // ... +} +// highlight-end + +bot.start() // 启动bot +bot.join() // 挂起bot直到bot终止 +``` + + + + +```java +Bot bot = BotFactory.create("APP ID", "secret", "token", (configuration) -> { + // ... + return Unit.INSTANCE; // 结束配置 +}); + +// 订阅所有事件类型 +// highlight-start +bot.registerBlockingProcessor((Signal.Dispatch event, String raw) -> { + // ... +}); +// highlight-end + +// 订阅具体的事件类型 +// highlight-start +bot.registerBlockingProcessor(GuildMemberAdd.class, (GuildMemberAdd event, String raw) -> { + // ... +}); +// highlight-end + +bot.startBlocking(); // 启动bot +bot.joinBlocking(); // 阻塞直到bot终止 +``` + + + + +```java +Bot bot = BotFactory.create("APP ID", "secret", "token", (configuration) -> { + // ... + return Unit.INSTANCE; // 结束配置 +}); + +// 订阅所有事件类型 +// highlight-start +bot.registerAsyncProcessor((Signal.Dispatch event, String raw) -> { + // ... + return CompletableFuture.completedFuture(null); // 异步事件处理器要求返回 CompletionStage 类型的结果 +}); +// highlight-end + +// 订阅具体的事件类型 +// highlight-start +bot.registerAsyncProcessor(GuildMemberAdd.class, (GuildMemberAdd event, String raw) -> { + // ... + return CompletableFuture.completedFuture(null); // 异步事件处理器要求返回 CompletionStage 类型的结果 +}); +// highlight-end + +// 阻塞直到bot终止 +bot.startAsync().thenCompose(__ -> bot.joinAsync()).join(); +``` + + + +当你选择了使用异步API,那就要尽最大努力来避免再使用阻塞API。 + + + + + + +可以看到,在进行事件处理的时候,你可以得到两个参数: +一个是接收到的事件类型 `event`,它是类型 `Signal.Dispatch` 的字类; +另外一个是字符串 `raw`,它代表是这个事件原始的JSON字符串。 + +> 可以监听的事件类型参考 [`Signal.Dispatch`](https://simple-robot-library.github.io/simbot3-website/components/qq-guild/simbot-component-qq-guild-api/love.forte.simbot.qguild.event/-signal/-dispatch/index.html) +以及它的所有子类型 (`Inheritors`) 。 + + +在事件监听的过程中,配合使用 `API` 来实现你的功能。 + +举个例子,当监听到 **子频道更新事件** (`ChannelUpdate`) 时,查询一下这个子频道所属的 **频道服务器** (`Guild`) 是什么。 + + + + +```kotlin +val bot = BotFactory.create("APP ID", "secret", "token") { + // ... +} + +// 订阅 ChannelUpdate +// highlight-start +bot.registerProcessor { raw -> // this: ChannelUpdate + // 查询这个子频道所属的频道服务器 + val guild = GetGuildApi.create(this.data.guildId).requestBy(bot) + println("guild: $guild") +} +// highlight-end + +bot.start() // 启动bot +bot.join() // 挂起bot直到bot终止 +``` + + + + +```java +Bot bot = BotFactory.create("APP ID", "secret", "token", (configuration) -> { + // ... + return Unit.INSTANCE; // 结束配置 +}); + +// 订阅 ChannelUpdate +// highlight-start +bot.registerBlockingProcessor(ChannelUpdate.class, (event, raw) -> { + // 查询这个子频道所属的频道服务器 + GetGuildApi getGuildApi = GetGuildApi.create(event.getData().getGuildId()); + SimpleGuild guild = BotRequestUtil.requestBlocking(bot, getGuildApi); + System.out.println("guild: " + guild); +}); +// highlight-end + +bot.startBlocking(); // 启动bot +bot.joinBlocking(); // 阻塞直到bot终止 +``` + + + + +```java +Bot bot = BotFactory.create("APP ID", "secret", "token", (configuration) -> { + // ... + return Unit.INSTANCE; // 结束配置 +}); + +// 订阅 ChannelUpdate +// highlight-start +bot.registerAsyncProcessor(ChannelUpdate.class, (event, raw) -> { + // 查询这个子频道所属的频道服务器 + GetGuildApi getGuildApi = GetGuildApi.create(event.getData().getGuildId()); + return BotRequestUtil.requestAsync(bot, getGuildApi).thenAccept(guild -> { + System.out.println("guild: " + guild); + }); +}); +// highlight-end + +// 阻塞直到bot终止 +bot.startAsync().thenCompose(__ -> bot.joinAsync()).join(); +``` + + + +当你选择了使用异步API,那就要尽最大努力来避免再使用阻塞API。 + + + + + + +## 启动 + +接下来,启动程序并试试看吧。 + +当然,如果遇到了预期外的问题也不要慌,积极反馈问题才能使我们变得更好,可以前往 [Issues](https://github.com/simple-robot/simpler-robot/issues) 反馈问题、[社区](https://github.com/orgs/simple-robot/discussions) 提出疑问。 diff --git a/Writerside/topics/snippets.md b/Writerside/topics/snippets.md new file mode 100644 index 00000000..61775b85 --- /dev/null +++ b/Writerside/topics/snippets.md @@ -0,0 +1,205 @@ +[//]: # (Ktor 引擎选择) + + + + + +你可以前往 [Ktor文档](https://ktor.io/docs/http-client-engines.html) +处选择一个对应所用平台下合适的 `Client Engine`。 +这里会根据不同平台提供几个示例,你可以选择其他可用目标。 + + + +
+ +[CIO](https://ktor.io/docs/http-client-engines.html#cio) 是一个比较通用的引擎。 +在不知道选什么的情况下,可以考虑使用它。 + + + + +```kotlin +runtimeOnly("io.ktor:ktor-client-cio-jvm:$ktor_version") +``` + + + + +```groovy +runtimeOnly 'io.ktor:ktor-client-cio-jvm:$ktor_version' +``` + + + + +```xml + + io.ktor + ktor-client-cio-jvm + ${ktor_version} + runtime + +``` + + + + +
+ +如果你打算使用 Java11+,也可以选择 [Java](https://ktor.io/docs/http-client-engines.html#java) 引擎。 + +
+ + + + +```kotlin +runtimeOnly("io.ktor:ktor-client-java:$ktor_version") +``` + +
+ +> 如果你想要显式配置引擎,那么就不能使用 `runtimeOnly` 了。 + +
+ + +```groovy +runtimeOnly 'io.ktor:ktor-client-java:$ktor_version' +``` + +
+ +> 如果你想要显式配置引擎,那么就不能使用 `runtimeOnly` 了。 + +
+ + +```xml + + io.ktor + ktor-client-java-jvm + ${ktor_version} + runtime + +``` + +
+ +> 如果你想要显式配置引擎,那么就不能使用 `runtime scope` 了。 + +
+
+ +
+ + +
+ +JavaScript 平台下可以选择 [Js](https://ktor.io/docs/http-client-engines.html#js) 引擎。 + + + + +```kotlin +implementation("io.ktor:ktor-client-js:$ktor_version") +``` + + + + +```groovy +implementation 'io.ktor:ktor-client-js:$ktor_version' +``` + + + + +
+ + +
+ +native 平台目标下,可能需要根据不同的平台类型选择不同的引擎。 + + + +
+ +可以选择 [WinHttp](https://ktor.io/docs/http-client-engines.html#winhttp) 引擎。 + + + + +```kotlin +implementation("io.ktor:ktor-client-winhttp:$ktor_version") +``` + + + + +```groovy +implementation 'io.ktor:ktor-client-winhttp:$ktor_version' +``` + + + + +
+ +
+ +Linux 下依旧可以选择 [CIO](https://ktor.io/docs/http-client-engines.html#cio) 引擎。 + + + + +```kotlin +implementation("io.ktor:ktor-client-cio:$ktor_version") +``` + + + + +```groovy +implementation 'io.ktor:ktor-client-cio:$ktor_version' +``` + + + + +
+ +
+ +可以选择 [Darwin](https://ktor.io/docs/http-client-engines.html#darwin) 引擎。 + + + + +```kotlin +implementation("io.ktor:ktor-client-darwin:$ktor_version") +``` + + + + +```groovy +implementation 'io.ktor:ktor-client-darwin:$ktor_version' +``` + + + + +
+
+ +
+
+ +
+
+ + +
+ diff --git a/Writerside/topics/starter-topic.md b/Writerside/topics/starter-topic.md deleted file mode 100644 index 141cfb47..00000000 --- a/Writerside/topics/starter-topic.md +++ /dev/null @@ -1 +0,0 @@ -# 欢迎! diff --git a/Writerside/topics/use-api.md b/Writerside/topics/use-api.md new file mode 100644 index 00000000..35c82ae9 --- /dev/null +++ b/Writerside/topics/use-api.md @@ -0,0 +1,232 @@ +--- +switcher-label: Java API +--- + + + +# 使用 API + + +

本章节介绍如何使用 API 模块 来构建、请求一个QQ频道的API。

+
+ +## 安装 + + + + +```Kotlin +implementation("love.forte.simbot.component:simbot-component-qq-guild-api:%version%") +``` + + + +如果你使用 Java 而不配合使用 Gradle 的 `kotlin` 插件, +那么你需要指定依赖的后缀为 `-jvm`。 + +```Kotlin +implementation("love.forte.simbot.component:simbot-component-qq-guild-api-jvm:%version%") +``` + + + + + + +```Groovy +implementation 'love.forte.simbot.component:simbot-component-qq-guild-api:%version%' +``` + + + +如果你使用 Java 而不配合使用 Gradle 的 `kotlin` 插件, +那么你需要指定依赖的后缀为 `-jvm`。 + +```Groovy +implementation 'love.forte.simbot.component:simbot-component-qq-guild-api-jvm:%version%' +``` + + + + + + +```xml + + love.forte.simbot.component + + simbot-component-qq-guild-api-jvm + %version% + +``` + + + + +### 引擎选择 + + + +## 使用 + +QQ频道组件的API模块提供了针对 +[QQ频道API](https://bot.q.qq.com/wiki/develop/api/) +的基本对应封装。 + +API封装的命名与API具有一定关联,例如 [`获取用户(BOT)频道服务器列表`](https://bot.q.qq.com/wiki/develop/api/openapi/user/guilds.html): + + + +```HTTP +GET /users/@me/guilds +``` + +``` +love.forte.simbot.qguild.api.user.GetBotGuildListApi +``` + + +> 所有的API实现均在包路径 `love.forte.simbot.miyoushe.api` 中。 + +API的应用大差不差,因此此处仅使用部分类型作为示例, +不会演示所有API。 +如果想浏览或寻找需要的 API,可前往 [APIDoc引导](https://docs.simbot.forte.love) +中进入QQ频道组件的 KDoc 查阅,或可以简单的借助IDE的智能提示进行寻找。 + +以 [获取用户(BOT)频道服务器列表](https://bot.q.qq.com/wiki/develop/api/openapi/user/guilds.html) 为例。 + + + + +```kotlin + // 准备参数 +// 用于请求的token +val token = "Bot 123456789.ABABABABABABABABABABABABABABABABAB" +// 用于请求的 Ktor HttpClient,如有必要则需要自行引入并选择需要使用的引擎。 +// 参考:[[[Ktor Engines|https://ktor.io/docs/http-client-engines.html]]] +val client = HttpClient() +// 需要请求的环境的服务器地址,比如正式环境或沙箱环境,亦或是某个自己定义代理的第三方环境 +// 可以通过 QQGuild 得到一些预定义的常量信息 +val server = QQGuild.SANDBOX_URL + +// 使用 GetBotGuildListApi 获取频道列表 +// 创建了一个参数 limit=100 的 GetBotGuildListApi,并使用上述准备好的参数进行请求。 +val resultList = GetBotGuildListApi.create(limit = 100).requestData(client, token, server) + +resultList.forEach { // it: SimpleGuild + ... +} +``` + +也可以通过额外的扩展函数来获得一个**全量数据**的数据流。 + +```kotlin +// 准备参数 +// 用于请求的token +val token = "Bot 123456789.ABABABABABABABABABABABABABABABABAB" +// 用于请求的 Ktor HttpClient,如有必要则需要自行引入并选择需要使用的引擎。 +// 参考:[[[Ktor Engines|https://ktor.io/docs/http-client-engines.html]]] +val client = HttpClient() +// 需要请求的环境的服务器地址,比如正式环境或沙箱环境,亦或是某个自己定义代理的第三方环境 +// 可以通过 QQGuild 得到一些预定义的常量信息 +val server = QQGuild.SANDBOX_URL + +// 使用 GetBotGuildListApi 获取频道列表 +// 创建了一个基于 GetBotGuildListApi 获取全量数据的流,并使用上述准备好的参数进行请求。 +val resultFlow = GetBotGuildListApi.createFlow { requestData(client, token, server) } + +resultFlow.collect { // it: SimpleGuild + println(it) +} +``` + + + + +```java + // 准备参数 +// 用于请求的token +var token = "Bot 123456789.ABABABABABABABABABABABABABABABABAB"; +// 用于请求的 Ktor HttpClient,如有必要则需要自行引入并选择需要使用的引擎。 +// 参考:https://ktor.io/docs/http-client-engines.html +var client = HttpClientJvmKt.HttpClient(($1) -> Unit.INSTANCE); +// 或者通过jvm平台库提供的工具类来构建一个默认的 client。(需要环境中存在一种引擎) +var newClient = ApiRequests.newHttpClient(); + +// 需要请求的环境的服务器地址,比如正式环境或沙箱环境,亦或是某个自己定义代理的第三方环境 +// 可以通过 QQGuild 得到一些预定义的常量信息 +var server = QQGuild.SANDBOX_URL; + +// 使用 GetBotGuildListApi 获取频道列表, +// 创建了一个 limit = 100 的 GetBotGuildListApi +var api = GetBotGuildListApi.create(100); + +ApiRequests.requestDataAsync(api, client, token, server) + .thenAccept(guildList -> { + for (var guild : guildList) { + // ... + } + }); +``` +{switcher-key="%ja%"} + +```java +// 准备参数 +// 用于请求的token +var token = "Bot 123456789.ABABABABABABABABABABABABABABABABAB"; +// 用于请求的 Ktor HttpClient,如有必要则需要自行引入并选择需要使用的引擎。 +// 参考:https://ktor.io/docs/http-client-engines.html +var client = HttpClientJvmKt.HttpClient(($1) -> Unit.INSTANCE); +// 或者通过jvm平台库提供的工具类来构建一个默认的 client。(需要环境中存在一种引擎) +var newClient = ApiRequests.newHttpClient(); + +// 需要请求的环境的服务器地址,比如正式环境或沙箱环境,亦或是某个自己定义代理的第三方环境 +// 可以通过 QQGuild 得到一些预定义的常量信息 +var server = QQGuild.SANDBOX_URL; + +// 使用 GetBotGuildListApi 获取频道列表, +// 创建了一个 limit = 100 的 GetBotGuildListApi +var api = GetBotGuildListApi.create(100); + +// 发起请求并得到结果 +var guildList = ApiRequests.requestDataBlocking(api, client, token, server); + +for (var guild : guildList) { + // ... +} +``` +{switcher-key="%jb%"} + + +```java + // 准备参数 +// 用于请求的token +var token = "Bot 123456789.ABABABABABABABABABABABABABABABABAB"; +// 用于请求的 Ktor HttpClient,如有必要则需要自行引入并选择需要使用的引擎。 +// 参考:https://ktor.io/docs/http-client-engines.html +var client = HttpClientJvmKt.HttpClient(($1) -> Unit.INSTANCE); +// 或者通过jvm平台库提供的工具类来构建一个默认的 client。(需要环境中存在一种引擎) +var newClient = ApiRequests.newHttpClient(); + +// 需要请求的环境的服务器地址,比如正式环境或沙箱环境,亦或是某个自己定义代理的第三方环境 +// 可以通过 QQGuild 得到一些预定义的常量信息 +var server = QQGuild.SANDBOX_URL; + +// 使用 GetBotGuildListApi 获取频道列表, +// 创建了一个 limit = 100 的 GetBotGuildListApi +var api = GetBotGuildListApi.create(100); + +// 发起请求并得到结果 +ApiRequests.requestDataReserve(api, client, token, server) + .transform(SuspendReserves.mono()) + .subscribe(guildList -> { + for (var guild : guildList) { + // ... + } + }); +``` +{switcher-key="%jr%"} + + + + diff --git a/Writerside/topics/use-core.md b/Writerside/topics/use-core.md new file mode 100644 index 00000000..1b21623e --- /dev/null +++ b/Writerside/topics/use-core.md @@ -0,0 +1,240 @@ +--- +switcher-label: Java API +--- + +# 使用核心库 + + + +

使用 核心库(core 模块) 配合 simbot4 核心库来将QQ频道作为 simbot4 的组件之一应用在 Application 中。

+
+ + +站在 simbot 核心库的视角来看,“QQ频道组件的‘核心库’” 也可以被称为QQ频道组件库。 + + +## 安装 + +首先,要配合使用 simbot4,就必须添加 `simbot-core` 的依赖。组件对于 simbot 的库依赖一般都是仅编译器的。 + + + +simbot 核心库的版本尽量不要低于 `v%minimum-core-version%`,可前往 +[GitHub Releases](https://github.com/simple-robot/simpler-robot/releases) +查看各版本及其说明。 + +此处以 `simbot-core v%minimum-core-version%` 作为**示例**。 + + + + + + + +```Kotlin +// simbot4核心库 +implementation("love.forte.simbot:simbot-core:%minimum-core-version%") +// QQ频道组件库 +implementation("love.forte.simbot.component:simbot-component-qq-guild-core:%version%") +``` + + + +如果你使用 Java 而不配合使用 Gradle 的 `kotlin` 插件, +那么你需要指定依赖的后缀为 `-jvm`。 + +```Kotlin +// simbot4核心库 +implementation("love.forte.simbot:simbot-core-jvm:%minimum-core-version%") +// QQ频道组件库 +implementation("love.forte.simbot.component:simbot-component-qq-guild-core-jvm:%version%") +``` + + + + + + +```Groovy +// simbot4核心库 +implementation 'love.forte.simbot:simbot-core:%minimum-core-version%' +// QQ频道组件库 +implementation 'love.forte.simbot.component:simbot-component-qq-guild-core:%version%' +``` + + + +如果你使用 Java 而不配合使用 Gradle 的 `kotlin` 插件, +那么你需要指定依赖的后缀为 `-jvm`。 + +```Groovy +// simbot4核心库 +implementation 'love.forte.simbot:simbot-core-jvm:%minimum-core-version%' +// QQ频道组件库 +implementation 'love.forte.simbot.component:simbot-component-qq-guild-core-jvm:%version%' +``` + + + + + + +```xml + + + love.forte.simbot + simbot-core-jvm + %minimum-core-version% + + + + love.forte.simbot.component + simbot-component-qq-guild-core-jvm + %version% + +``` + + + + +## 使用 +### 安装到 Application + +向 Application 中安装 `QQGuildComponent` 和 `QQGuildBotManager`。 + + + + +```kotlin +val app = launchSimpleApplication { + // 使用 useQQGuild 简写, + // 代表同时安装 `QQGuildComponent` 和 `QQGuildBotManager` + useQQGuild() + + // 其他配置.. +} +``` + + + + + +```java +var appFuture = Applications.launchApplicationAsync(Simple.INSTANCE, appConfigurer -> { + // 安装 `QQGuildComponent` 和 `QQGuildBotManager` + appConfigurer.install(QQGuildComponent.Factory); + appConfigurer.install(QQGuildBotManager.Factory); +}).asFuture(); +``` +{switcher-key="%ja%"} + +```java +var app = Applications.launchApplicationBlocking(Simple.INSTANCE, appConfigurer -> { + // 安装 `QQGuildComponent` 和 `QQGuildBotManager` + appConfigurer.install(QQGuildComponent.Factory); + appConfigurer.install(QQGuildBotManager.Factory); +}); +``` +{switcher-key="%jb%"} + + + + + +### 注册事件处理器 + + + + +```kotlin +val app = ... + +// 注册各种事件处理器 +app.listeners { + // 注册一个事件处理器 + // 所有子频道消息事件 + // 其中就包括QQ频道的公域消息事件 + listen { + println("context: $this") + println("context.event: $event") + + // 返回事件处理结果 + EventResult.empty() + } + + // 再注册一个事件处理器 + // 明确监听QQ频道的公域消息事件 + // 使用 process 不需要返回值 + process { + println("context: $this") + println("context.event: $event") + } +} +``` + + + + + +```java +// 假设通过 future 的 thenAccept 或其他什么地方得到了 Application +var app = ...; + +// 注册一个事件处理器 +// 所有子频道消息事件 +// 其中就包括QQ频道的公域消息事件 +eventDispatcher.register(EventListeners.async(ChatChannelMessageEvent.class, (context, event) -> { + System.out.println("context: " + context); + System.out.println("context.event: " + event); + + // 返回异步的事件处理结果 + return CompletableFuture.completedFuture(EventResult.empty()); +})); + +// 再注册一个事件处理器 +// 明确监听QQ频道的公域消息事件 +eventDispatcher.register(EventListeners.async(QGAtMessageCreateEvent.class, (context, event) -> { + System.out.println("context: " + context); + System.out.println("context.event: " + event); + + // 返回异步的事件处理结果 + return CompletableFuture.completedFuture(EventResult.empty()); +})); +``` +{switcher-key="%ja%"} + +```java +var app = ...; + +// 注册一个事件处理器 +// 所有子频道消息事件 +// 其中就包括QQ频道的公域消息事件 +eventDispatcher.register(EventListeners.block(ChatChannelMessageEvent.class, (context, event) -> { + System.out.println("context: " + context); + System.out.println("context.event: " + event); + + // 返回事件处理结果 + return EventResult.empty(); +})); + +// 再注册一个事件处理器 +// 明确监听QQ频道的公域消息事件 +eventDispatcher.register(EventListeners.block(QGAtMessageCreateEvent.class, (context, event) -> { + System.out.println("context: " + context); + System.out.println("context.event: " + event); + + // 返回异步的事件处理结果 + return EventResult.empty(); +})); +``` +{switcher-key="%jb%"} + + + + +### 注册、启动Bot + + +## 更多有关 simbot API + +前往 [simbot官方手册](https://simbot.forte.love/) 阅读有关 simbot API 的各种介绍与示例吧! diff --git a/Writerside/topics/use-spring-boot.md b/Writerside/topics/use-spring-boot.md new file mode 100644 index 00000000..5e9d9c23 --- /dev/null +++ b/Writerside/topics/use-spring-boot.md @@ -0,0 +1,167 @@ +# 使用 Spring Boot + + + +

使用 核心库(core 模块) 配合 simbot4 Spring Boot starter 来将QQ频道组件作为 simbot4 的组件之一应用在 Spring Boot 3 中。

+
+ + +## 准备 + +### Java 17 + +simbot4 的 Spring Boot starter 基于 Spring Boot 3,因此 Java 的版本至少为 **Java17** 。 + +### 创建 Spring Boot 3 项目 + +首先你得有个 Spring Boot 3 项目, 你可以前往 [Spring Initializr][spring.start] +或者借助 IDE (比如 IDEA) 的相关功能创建一个 Spring Boot 3 的项目。 +你可以自由选择需要添加的任何其他 Spring Boot 组件,比如 `spring-boot-starter-web` 之类的。 + + + +- [Spring Initializr][spring.start] +- [Spring Quickstart Guide](https://spring.io/quickstart/) + + + +## 安装 + + + +simbot 核心库的版本尽量不要低于 `v%minimum-core-version%`,可前往 +[GitHub Releases](https://github.com/simple-robot/simpler-robot/releases) +查看各版本及其说明。 + +此处以 `v%minimum-core-version%` 作为**示例**。 + + + + + + +```Kotlin +// simbot4核心库 +implementation("love.forte.simbot:simbot-core-spring-boot-starter:%minimum-core-version%") +// QQ频道组件库 +implementation("love.forte.simbot.component:simbot-component-qq-guild-core:%version%") +``` + + + +如果你使用 Java 而不配合使用 Gradle 的 `kotlin` 插件, +那么你需要为组件库指定依赖的后缀为 `-jvm`。 + +```Kotlin +// simbot4核心库 +implementation("love.forte.simbot:simbot-core-spring-boot-starter:%minimum-core-version%") +// QQ频道组件库 +implementation("love.forte.simbot.component:simbot-component-qq-guild-core-jvm:%version%") +``` + + + + + + +```Groovy +// simbot4核心库 +implementation 'love.forte.simbot:simbot-core-spring-boot-starter:%minimum-core-version%' +// QQ频道组件库 +implementation 'love.forte.simbot.component:simbot-component-qq-guild-core:%version%' +``` + + + +如果你使用 Java 而不配合使用 Gradle 的 `kotlin` 插件, +那么你需要为组件库指定依赖的后缀为 `-jvm`。 + +```Kotlin +// simbot4核心库 +implementation 'love.forte.simbot:simbot-core-spring-boot-starter:%minimum-core-version%' +// QQ频道组件库 +implementation 'love.forte.simbot.component:simbot-component-qq-guild-core-jvm:%version%' +``` + + + + + + +```xml + + + love.forte.simbot + simbot-core-spring-boot-starter + %minimum-core-version% + + + + love.forte.simbot.component + simbot-component-qq-guild-core-jvm + %version% + +``` + + + + +## Bot 配置 + +在你的资源目录中: +resources/simbot-bots/ +中创建任意的一个或多个bot配置文件,并以 `.bot.json` 作为扩展名。 + +配置文件的内容可前往参考 [Bot配置文件](bot-config.md) 章节。 + +> 这个扫描目录是可配置的。 +> 这是属于 simbot4 Spring Boot starter 的配置,下文会有相关的引导链接到 simbot4 应用手册的相关内容处。 + +## 使用 +### 添加启动注解 + +在你的启动类上添加 `@EnableSimbot` 注解来启用 simbot。 + + + + +```Kotlin +@EnableSimbot // 启用 simbot +@SpringBootApplication +class MyApplication + +fun main(args: Array) { + runApplicarion(*args) +} +``` + + + + +```Java +@EnableSimbot // 启用 simbot +@SpringBootApplication +public class MyApplication { + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } +} +``` + + + + + +QQ频道组件支持使用 SPI **自动加载** 组件和插件。 +添加依赖到项目环境中、编写完 Bot 配置文件后,可前往 +[simbot手册: 使用 Spring Boot 3](https://simbot.forte.love/start-use-spring-boot-3.html) +了解更多 simbot4 Spring Boot starter 的可配置内容以及启动注解等信息。 + +## 更多相关参考 + +- 前往 [simbot官方手册](https://simbot.forte.love/) 阅读有关 simbot API 的相关介绍与示例。 +- 前往 [simbot手册: 使用 Spring Boot 3](https://simbot.forte.love/start-use-spring-boot-3.html) + 了解更多 simbot4 Spring Boot starter 的可配置内容以及启动注解等信息。 + + +[spring.start]: https://start.spring.io diff --git a/Writerside/topics/use-stdlib.md b/Writerside/topics/use-stdlib.md new file mode 100644 index 00000000..99588d15 --- /dev/null +++ b/Writerside/topics/use-stdlib.md @@ -0,0 +1,266 @@ +--- +switcher-label: Java API +--- + +# 使用标准库 + + +

本章节介绍如何使用 标准库(stdlib模块) 来构建 Bot 实例、订阅并处理事件。

+
+ +## 安装 + + + + +```Kotlin +implementation("love.forte.simbot.component:simbot-component-qq-guild-stdlib:%version%") +``` + + + +如果你使用 Java 而不配合使用 Gradle 的 `kotlin` 插件, +那么你需要指定依赖的后缀为 `-jvm`。 + +```Kotlin +implementation("love.forte.simbot.component:simbot-component-qq-guild-stdlib-jvm:%version%") +``` + + + + + + +```Groovy +implementation 'love.forte.simbot.component:simbot-component-qq-guild-stdlib:%version%' +``` + + + +如果你使用 Java 而不配合使用 Gradle 的 `kotlin` 插件, +那么你需要指定依赖的后缀为 `-jvm`。 + +```Groovy +implementation 'love.forte.simbot.component:simbot-component-qq-guild-stdlib-jvm:%version%' +``` + + + + + + +```xml + + love.forte.simbot.component + + simbot-component-qq-guild-stdlib-jvm + %version% + +``` + + + + +### 引擎选择 + + + + + +## 使用 + +QQ频道组件的标准库模块在 [API模块](use-api.md) 的基础之上提供了构建 Bot、订阅并处理事件的能力。 + + + + +```kotlin + // 准备 bot 的必要信息 +val botId = "xxxx" +val botSecret = "" // secret 如果用不到可使用空字符串 +val botToken = "xxxx" +// 用于注册 bot 的 “票据” 信息。 +val ticket = Bot.Ticket(botId, botSecret, botToken) + +// 构建一个 Bot,并可选的进行一些配置。 +val bot = BotFactory.create(ticket) { + // 各种配置... + // 比如切换服务地址为沙箱频道的服务地址 + useSandboxServerUrl() + + // 指定需要订阅的事件的 intents,默认会订阅: + // - 频道相关事件 + // - 频道成员相关事件 + // - 公域消息相关事件 + intents // = xxx + + // 自定义一个 shard,默认是 Shard.FULL + shard = Shard.FULL + + // 其他各种配置... +} + +// 注册事件有一些不同但类似的方式 +// 1️⃣ 通过 registerProcessor 注册一个普通的事件处理器,此处理器会接收并处理所有类型的事件 +// registerProcessor 是最基本的注册方式,也是其他方式的最终汇集点 +bot.registerProcessor { raw -> + // raw 代表事件的原始JSON字符串 + // this: Signal.Dispatch, 也就是解析出来的事件结构体 + println("event: $this") + println("event.data: $data") + println("raw: $raw") +} + +// 2️⃣ 通过 processEvent 注册一个针对具体 Signal.Dispatch 事件类型的事件处理器, +// 它只有在接收到的 Signal.Dispatch 与目标类型一致时才会处理。 +// 此示例展示处理 AtMessageCreate 也就公域是消息事件,并在对方发送了包含 'stop' 的文本时终止 bot。 +bot.process { + println("event message: $data") + + if ("stop" in data.content) { + // 终止 bot + bot.cancel() + } +} + +// 启动 bot, 此时会开始获取ws、连接并接收消息。 +bot.start() + +// 挂起 bot,直到它结束(被终止) +bot.join() +``` + + + + + +```java +// 准备 bot 的必要信息 +var botId = "xxxx"; +var botSecret = ""; // secret 如果用不到可使用空字符串 +var botToken = "xxxx"; +// 用于注册 bot 的 “票据” 信息。 +var ticket = new Bot.Ticket(botId, botSecret, botToken); + +// 构建一个 Bot,并可选的进行一些配置。 +Bot bot = BotFactory.create(ticket, config -> { + // 各种配置... + // 比如切换服务地址为沙箱频道的服务地址 + config.useSandboxServerUrl(); + + // 指定需要订阅的事件的 intents,默认会订阅: + // - 频道相关事件 + // - 频道成员相关事件 + // - 公域消息相关事件 + // config.setIntentsValue(...); + + // 自定义一个 shard,默认是 Shard.FULL + config.setShard(Shard.FULL); + + // 其他各种配置... +}); + +// 通过 registerProcessor 注册一个普通的事件处理器, +// 此处理器会接收并处理所有类型的事件 +bot.registerProcessor(EventProcessors.async((event, raw) -> { + // raw 代表事件的原始JSON字符串 + // event: Signal.Dispatch, 也就是解析出来的事件结构体 + System.out.println("event: " + event); + System.out.println("event.data: " + event.getData()); + System.out.println("raw: " + raw); + + // 异步处理器必须返回 CompletableFuture + return CompletableFuture.completedFuture(null); +})); + +// 通过 registerProcessor 注册一个普通的事件处理器, +// 此处理器会接收并处理指定的类型 AtMessageCreate 的事件 +// 此示例展示处理 AtMessageCreate 也就公域是消息事件,并在对方发送了包含 'stop' 的文本时终止 bot。 +bot.registerProcessor(EventProcessors.async(AtMessageCreate.class, (event, raw) -> { + System.out.println("event message: $data"); + + if (event.getData().getContent().contains("stop")) { + // 终止 bot + bot.cancel(); + } + + // 异步处理器必须返回 CompletableFuture + return CompletableFuture.completedFuture(null); +})); + +// 异步地启动bot,并在 bot 启动完成后, +// 将 bot 转化为 future,这个 future 会在 bot 被终止时完成 +// 其实也可以把 startAsync 和 asFuture 拆开写,效果相同。 +var future = bot.startAsync() + .thenCompose(unit -> bot.asFuture()); + +// 阻塞当前线程,直到 bot 被终止。 +future.join(); +``` +{switcher-key="%ja%"} + +```java +// 准备 bot 的必要信息 +var botId = "xxxx"; +var botSecret = ""; // secret 如果用不到可使用空字符串 +var botToken = "xxxx"; +// 用于注册 bot 的 “票据” 信息。 +var ticket = new Bot.Ticket(botId, botSecret, botToken); + +// 构建一个 Bot,并可选的进行一些配置。 +Bot bot = BotFactory.create(ticket, config -> { + // 各种配置... + // 比如切换服务地址为沙箱频道的服务地址 + config.useSandboxServerUrl(); + + // 指定需要订阅的事件的 intents,默认会订阅: + // - 频道相关事件 + // - 频道成员相关事件 + // - 公域消息相关事件 + // config.setIntentsValue(...); + + // 自定义一个 shard,默认是 Shard.FULL + config.setShard(Shard.FULL); + + // 其他各种配置... +}); + +// 通过 registerProcessor 注册一个普通的事件处理器, +// 此处理器会接收并处理所有类型的事件 +bot.registerProcessor(EventProcessors.block((event, raw) -> { + // raw 代表事件的原始JSON字符串 + // event: Signal.Dispatch, 也就是解析出来的事件结构体 + System.out.println("event: " + event); + System.out.println("event.data: " + event.getData()); + System.out.println("raw: " + raw); +})); + +// 通过 registerProcessor 注册一个普通的事件处理器, +// 此处理器会接收并处理指定的类型 AtMessageCreate 的事件 +// 此示例展示处理 AtMessageCreate 也就公域是消息事件,并在对方发送了包含 'stop' 的文本时终止 bot。 +bot.registerProcessor(EventProcessors.block(AtMessageCreate.class, (event, raw) -> { + System.out.println("event message: $data"); + + if (event.getData().getContent().contains("stop")) { + // 终止 bot + bot.cancel(); + } +})); + +// 启动bot +bot.startBlocking(); + +// 阻塞当前线程,直到 bot 被终止。 +bot.joinBlocking(); +``` +{switcher-key="%jb%"} + + + +当使用异步API时候,请尽可能避免在异步的事件处理器中使用任何阻塞API。 + + + + + +> 封装的事件的结构、属性以及属性类型都基本与大别野文档中的一致或具有对应关系。 diff --git a/Writerside/v.list b/Writerside/v.list index 79af5860..1d64f81e 100644 --- a/Writerside/v.list +++ b/Writerside/v.list @@ -3,6 +3,6 @@ - - + + diff --git a/build.gradle.kts b/build.gradle.kts index c5e11e15..60c1aadc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -32,9 +32,6 @@ buildscript { repositories { mavenCentral() } -// dependencies { -// classpath("org.jetbrains.kotlinx:atomicfu-gradle-plugin:${libs.versions.atomicfu.get()}") -// } } //group = P.ComponentTencentGuild.GROUP diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index 706be426..b675d32a 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -24,30 +24,24 @@ repositories { gradlePluginPortal() } -val kotlinVersion = "1.9.21" -val dokkaPluginVersion = "1.9.10" -val suspendTransformVersion = "0.6.0-beta3" -val gradleCommon = "0.2.0" -//val atomicfuVersion = "0.20.0" +val kotlinVersion: String = libs.versions.kotlin.get() dependencies { implementation(kotlin("gradle-plugin", kotlinVersion)) implementation(kotlin("serialization", kotlinVersion)) - implementation("org.jetbrains.dokka:dokka-gradle-plugin:$dokkaPluginVersion") - implementation("org.jetbrains.dokka:dokka-base:$dokkaPluginVersion") - - // https://github.com/Kotlin/kotlinx-atomicfu -// implementation("org.jetbrains.kotlinx:atomicfu-gradle-plugin:$atomicfuVersion") + implementation(libs.bundles.dokka) // see https://github.com/gradle-nexus/publish-plugin implementation("io.github.gradle-nexus:publish-plugin:1.1.0") -// implementation("love.forte.plugin.suspend-transform:suspend-transform-plugin-gradle:0.0.5") - implementation("love.forte.plugin.suspend-transform:suspend-transform-plugin-gradle:$suspendTransformVersion") - implementation("love.forte.gradle.common:gradle-common-core:$gradleCommon") - implementation("love.forte.gradle.common:gradle-common-kotlin-multiplatform:$gradleCommon") - implementation("love.forte.gradle.common:gradle-common-publication:$gradleCommon") + // simbot suspend transform gradle common + implementation(libs.simbot.gradle) + + // suspend transform + implementation(libs.suspend.transform.gradle) + // gradle common + implementation(libs.bundles.gradle.common) } diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts new file mode 100644 index 00000000..f6a40f20 --- /dev/null +++ b/buildSrc/settings.gradle.kts @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +// https://stackoverflow.com/questions/67795324/gradle7-version-catalog-how-to-use-it-with-buildsrc +dependencyResolutionManagement { + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} diff --git a/buildSrc/src/main/kotlin/P.kt b/buildSrc/src/main/kotlin/P.kt index cca84ff0..a3e52805 100644 --- a/buildSrc/src/main/kotlin/P.kt +++ b/buildSrc/src/main/kotlin/P.kt @@ -59,7 +59,7 @@ object P { override val homepage: String get() = HOMEPAGE - private val baseVersion = v(4, 0, 0) - v("dev4") + private val baseVersion = v(4, 0, 0) - v("dev5") val snapshotVersion = baseVersion - Version.SNAPSHOT override val version = if (isSnapshot()) snapshotVersion else baseVersion diff --git a/buildSrc/src/main/kotlin/SuspendTransforms.kt b/buildSrc/src/main/kotlin/SuspendTransforms.kt deleted file mode 100644 index 5d70e035..00000000 --- a/buildSrc/src/main/kotlin/SuspendTransforms.kt +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2023-2024. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - -import love.forte.plugin.suspendtrans.* - - -object SuspendTransforms { - private val javaIncludeAnnotationApi4JClassInfo = ClassInfo("love.forte.simbot.annotations", "Api4J") - private val javaIncludeAnnotationApi4J = IncludeAnnotation(javaIncludeAnnotationApi4JClassInfo) - private val javaIncludeAnnotations = listOf(javaIncludeAnnotationApi4J) - - private val jsIncludeAnnotationApi4JsClassInfo = ClassInfo("love.forte.simbot.annotations", "Api4Js") - private val jsIncludeAnnotationApi4Js = IncludeAnnotation(jsIncludeAnnotationApi4JsClassInfo) - private val jsIncludeAnnotations = listOf(jsIncludeAnnotationApi4Js) - - - private val SuspendReserveClassInfo = ClassInfo( - packageName = "love.forte.simbot.suspendrunner.reserve", - className = "SuspendReserve", - - ) - - /** - * JvmBlocking - */ - val jvmBlockingTransformer = SuspendTransformConfiguration.jvmBlockingTransformer.copy( - syntheticFunctionIncludeAnnotations = javaIncludeAnnotations, - transformFunctionInfo = FunctionInfo("love.forte.simbot.suspendrunner", null, "$\$runInBlocking"), - copyAnnotationExcludes = SuspendTransformConfiguration.jvmBlockingTransformer.copyAnnotationExcludes + SuspendTransformConfiguration.jvmBlockingTransformer.markAnnotation.classInfo - ) - - /** - * JvmAsync - */ - val jvmAsyncTransformer = SuspendTransformConfiguration.jvmAsyncTransformer.copy( - syntheticFunctionIncludeAnnotations = javaIncludeAnnotations, - transformFunctionInfo = FunctionInfo("love.forte.simbot.suspendrunner", null, "$\$runInAsyncNullable"), - copyAnnotationExcludes = SuspendTransformConfiguration.jvmAsyncTransformer.copyAnnotationExcludes + SuspendTransformConfiguration.jvmAsyncTransformer.markAnnotation.classInfo - ) - - /** - * JvmReserve - */ - val jvmReserveTransformer = SuspendTransformConfiguration.jvmAsyncTransformer.copy( - syntheticFunctionIncludeAnnotations = javaIncludeAnnotations, - transformFunctionInfo = FunctionInfo("love.forte.simbot.suspendrunner", null, "$\$asReserve"), - copyAnnotationExcludes = SuspendTransformConfiguration.jvmAsyncTransformer.copyAnnotationExcludes + SuspendTransformConfiguration.jvmAsyncTransformer.markAnnotation.classInfo, - transformReturnType = SuspendReserveClassInfo, - transformReturnTypeGeneric = true, - ) - - /** - * JsPromise - */ - val jsPromiseTransformer = SuspendTransformConfiguration.jsPromiseTransformer.copy( - syntheticFunctionIncludeAnnotations = javaIncludeAnnotations, - transformFunctionInfo = FunctionInfo("love.forte.simbot.suspendrunner", null, "$\$runInPromise"), - copyAnnotationExcludes = SuspendTransformConfiguration.jsPromiseTransformer.copyAnnotationExcludes + SuspendTransformConfiguration.jsPromiseTransformer.markAnnotation.classInfo, - ) - - //region @JvmSuspendTrans - private val suspendTransMarkAnnotationClassInfo = ClassInfo("love.forte.simbot.suspendrunner", "SuspendTrans") - - private val jvmSuspendTransMarkAnnotationForBlocking = MarkAnnotation( - suspendTransMarkAnnotationClassInfo, - baseNameProperty = "blockingBaseName", - suffixProperty = "blockingSuffix", - asPropertyProperty = "blockingAsProperty", - defaultSuffix = SuspendTransformConfiguration.jvmBlockingAnnotationInfo.defaultSuffix, - ) - private val jvmSuspendTransMarkAnnotationForAsync = MarkAnnotation( - suspendTransMarkAnnotationClassInfo, - baseNameProperty = "asyncBaseName", - suffixProperty = "asyncSuffix", - asPropertyProperty = "asyncAsProperty", - defaultSuffix = SuspendTransformConfiguration.jvmAsyncAnnotationInfo.defaultSuffix, - ) - private val jvmSuspendTransMarkAnnotationForReserve = MarkAnnotation( - suspendTransMarkAnnotationClassInfo, - baseNameProperty = "reserveBaseName", - suffixProperty = "reserveSuffix", - asPropertyProperty = "reserveAsProperty", - defaultSuffix = "Reserve", - ) - private val jsSuspendTransMarkAnnotationForPromise = MarkAnnotation( - suspendTransMarkAnnotationClassInfo, - baseNameProperty = "jsPromiseBaseName", - suffixProperty = "jsPromiseSuffix", - asPropertyProperty = "jsPromiseAsProperty", - defaultSuffix = "Async", - ) - - val suspendTransTransformerForJvmBlocking = jvmBlockingTransformer.copy( - markAnnotation = jvmSuspendTransMarkAnnotationForBlocking, - copyAnnotationExcludes = SuspendTransformConfiguration.jvmBlockingTransformer.copyAnnotationExcludes + jvmSuspendTransMarkAnnotationForBlocking.classInfo - ) - - val suspendTransTransformerForJvmAsync = jvmAsyncTransformer.copy( - markAnnotation = jvmSuspendTransMarkAnnotationForAsync, - copyAnnotationExcludes = SuspendTransformConfiguration.jvmAsyncTransformer.copyAnnotationExcludes + jvmSuspendTransMarkAnnotationForAsync.classInfo - ) - - val suspendTransTransformerForJvmReserve = jvmReserveTransformer.copy( - markAnnotation = jvmSuspendTransMarkAnnotationForReserve, - copyAnnotationExcludes = jvmReserveTransformer.copyAnnotationExcludes + jvmSuspendTransMarkAnnotationForReserve.classInfo, - ) - - val suspendTransTransformerForJsPromise = jsPromiseTransformer.copy( - markAnnotation = jvmSuspendTransMarkAnnotationForReserve, - copyAnnotationExcludes = jsPromiseTransformer.copyAnnotationExcludes + jsSuspendTransMarkAnnotationForPromise.classInfo, - ) - //endregion - - //region @JvmSuspendTransProperty - private val jvmSuspendTransPropMarkAnnotationClassInfo = - ClassInfo("love.forte.simbot.suspendrunner", "SuspendTransProperty") - - private val jvmSuspendTransPropMarkAnnotationForBlocking = MarkAnnotation( - jvmSuspendTransPropMarkAnnotationClassInfo, - baseNameProperty = "blockingBaseName", - suffixProperty = "blockingSuffix", - asPropertyProperty = "blockingAsProperty", - defaultSuffix = "", - defaultAsProperty = true - ) - private val jvmSuspendTransPropMarkAnnotationForAsync = MarkAnnotation( - jvmSuspendTransPropMarkAnnotationClassInfo, - baseNameProperty = "asyncBaseName", - suffixProperty = "asyncSuffix", - asPropertyProperty = "asyncAsProperty", - defaultSuffix = SuspendTransformConfiguration.jvmAsyncAnnotationInfo.defaultSuffix, - defaultAsProperty = true - ) - private val jvmSuspendTransPropMarkAnnotationForReserve = MarkAnnotation( - jvmSuspendTransPropMarkAnnotationClassInfo, - baseNameProperty = "reserveBaseName", - suffixProperty = "reserveSuffix", - asPropertyProperty = "reserveAsProperty", - defaultSuffix = "Reserve", - defaultAsProperty = true - ) - - val jvmSuspendTransPropTransformerForBlocking = jvmBlockingTransformer.copy( - markAnnotation = jvmSuspendTransPropMarkAnnotationForBlocking, - copyAnnotationExcludes = SuspendTransformConfiguration.jvmBlockingTransformer.copyAnnotationExcludes + jvmSuspendTransPropMarkAnnotationForBlocking.classInfo - ) - - val jvmSuspendTransPropTransformerForAsync = jvmAsyncTransformer.copy( - markAnnotation = jvmSuspendTransPropMarkAnnotationForAsync, - copyAnnotationExcludes = SuspendTransformConfiguration.jvmAsyncTransformer.copyAnnotationExcludes + jvmSuspendTransPropMarkAnnotationForAsync.classInfo - ) - - val jvmSuspendTransPropTransformerForReserve = jvmReserveTransformer.copy( - markAnnotation = jvmSuspendTransPropMarkAnnotationForReserve, - copyAnnotationExcludes = jvmReserveTransformer.copyAnnotationExcludes + jvmSuspendTransPropMarkAnnotationForReserve.classInfo - ) - //endregion -} - diff --git a/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts b/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts index 3034d846..ea7af7fe 100644 --- a/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts +++ b/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts @@ -1,3 +1,22 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +import love.forte.simbot.gradle.suspendtransforms.SuspendTransforms + /* * Copyright (c) 2024. ForteScarlet. * diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5b61232c..0fe7b445 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,14 +1,18 @@ [versions] +kotlin = "1.9.21" kotlinx-coroutines = "1.7.3" kotlinx-serialization = "1.6.2" kotlinx-datetime = "0.5.0" +dokka = "1.9.10" okio = "3.3.0" ktor = "2.3.7" openjdk-jmh = "1.35" log4j = "2.20.0" -atomicfu = "0.20.1" -simbot = "4.0.0-dev11" reactor = "3.6.2" +# simbot +simbot = "4.0.0-dev13" +suspendTransform = "0.6.0" +gradleCommon = "0.2.0" [libraries] # simbot @@ -23,6 +27,7 @@ simbot-common-core = { group = "love.forte.simbot.common", name = "simbot-common simbot-common-suspend = { group = "love.forte.simbot.common", name = "simbot-common-suspend-runner", version.ref = "simbot" } simbot-common-annotations = { group = "love.forte.simbot.common", name = "simbot-common-annotations", version.ref = "simbot" } simbot-common-loop = { group = "love.forte.simbot.common", name = "simbot-common-stage-loop", version.ref = "simbot" } +simbot-gradle = { group = "love.forte.simbot.gradle", name = "simbot-gradle-suspendtransforms", version.ref = "simbot" } # jetbrains-annotation jetbrains-annotations = "org.jetbrains:annotations:24.0.1" @@ -89,3 +94,18 @@ openjdk-jmh-generator-annprocess = { group = "org.openjdk.jmh", name = "jmh-gene # reactor reactor-core = { group = "io.projectreactor", name = "reactor-core", version.ref = "reactor" } + +# dokka +dokka-plugin = { group = "org.jetbrains.dokka", name = "dokka-gradle-plugin", version.ref = "dokka" } +dokka-base = { group = "org.jetbrains.dokka", name = "dokka-base", version.ref = "dokka" } + +suspend-transform-gradle = { module = "love.forte.plugin.suspend-transform:suspend-transform-plugin-gradle", version.ref = "suspendTransform" } + +# gradle-common +gradle-common-core = { group = "love.forte.gradle.common", name = "gradle-common-core", version.ref = "gradleCommon" } +gradle-common-multiplatform = { group = "love.forte.gradle.common", name = "gradle-common-kotlin-multiplatform", version.ref = "gradleCommon" } +gradle-common-publication = { group = "love.forte.gradle.common", name = "gradle-common-publication", version.ref = "gradleCommon" } + +[bundles] +gradle-common = ["gradle-common-core", "gradle-common-multiplatform", "gradle-common-publication"] +dokka = ["dokka-plugin", "dokka-base"] diff --git a/simbot-component-qq-guild-api/build.gradle.kts b/simbot-component-qq-guild-api/build.gradle.kts index 3ad94969..75e30b72 100644 --- a/simbot-component-qq-guild-api/build.gradle.kts +++ b/simbot-component-qq-guild-api/build.gradle.kts @@ -92,6 +92,8 @@ kotlin { implementation(libs.log4j.api) implementation(libs.log4j.core) implementation(libs.log4j.slf4j2) + implementation(libs.kotlinx.coroutines.reactor) + implementation(libs.reactor.core) } jsMain.dependencies { diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/Signal.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/Signal.kt index 08cca7f8..ce856e3f 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/Signal.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/Signal.kt @@ -113,7 +113,7 @@ public sealed class Signal(@Serializable(Opcode.SerializerByCode::class) publ * 当发送心跳成功之后,就会收到该消息 */ @Serializable - public object HeartbeatACK : Signal(Opcode.HeartbeatACK), ReceivedSignal { + public data object HeartbeatACK : Signal(Opcode.HeartbeatACK), ReceivedSignal { /** * 没有data信息 */ diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QGBot.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QGBot.kt index 996870ea..92883146 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QGBot.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QGBot.kt @@ -203,7 +203,7 @@ public interface QGBot : Bot { * * @return API得到的用户信息结果 */ - @ST(blockingBaseName = "getMe", blockingSuffix = "", asyncBaseName = "getMe") + @ST(blockingBaseName = "getMe", blockingSuffix = "", asyncBaseName = "getMe", reserveBaseName = "getMe") public suspend fun me(withCache: Boolean): QGSourceUser /** diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/CacheStrategyConfig.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/CacheStrategyConfig.kt index 12fc07de..f1c51e71 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/CacheStrategyConfig.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/CacheStrategyConfig.kt @@ -35,10 +35,10 @@ public sealed class CacheStrategyConfig { * */ @SerialName("invalid") - public object Invalid : CacheStrategyConfig() + public data object Invalid : CacheStrategyConfig() @SerialName("transferability") - public object Transferability : CacheStrategyConfig() + public data object Transferability : CacheStrategyConfig() } @@ -55,8 +55,7 @@ public data class CacheConfig( /** * '传递性' 缓存。 - * - * 默认启用。 + * 默认为 `null`。 * * ```json * "config": { diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotFileConfiguration.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotFileConfiguration.kt index 01f38608..f10254cf 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotFileConfiguration.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotFileConfiguration.kt @@ -180,11 +180,12 @@ public data class QGBotFileConfiguration( * * ```json * { - * "config": { - * "clientProperties": { - * "k1": "v1", - * "foo": "bar" - * } + * "config": { + * "clientProperties": { + * "k1": "v1", + * "foo": "bar" + * } + * } * } * ``` * diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/ShardConfig.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/ShardConfig.kt index d0e72a9d..0a62a9dc 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/ShardConfig.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/ShardConfig.kt @@ -49,7 +49,7 @@ public sealed class ShardConfig { @Serializable @SerialName("full") @UsedOnlyForConfigSerialization - public object Full : ShardConfig() { + public data object Full : ShardConfig() { override val shard: Shard get() = Shard.FULL } diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGForumChannel.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGForumChannel.kt index d7fd0efc..cd42cb38 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGForumChannel.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/channel/QGForumChannel.kt @@ -24,6 +24,7 @@ import love.forte.simbot.component.qguild.forum.QGThreadCreator import love.forte.simbot.qguild.QQGuildApiException import love.forte.simbot.qguild.api.forum.ThreadPublishResult import love.forte.simbot.qguild.model.ChannelType +import love.forte.simbot.qguild.model.forum.Thread import love.forte.simbot.suspendrunner.ST import love.forte.simbot.qguild.model.Channel as QGSourceChannel @@ -59,7 +60,7 @@ public interface QGForumChannel : QGNonTextChannel { * * @throws QQGuildApiException api请求异常 */ - @ST(blockingBaseName = "getThread", blockingSuffix = "", asyncBaseName = "getThread") + @ST(blockingBaseName = "getThread", blockingSuffix = "", asyncBaseName = "getThread", reserveBaseName = "getThread") public suspend fun thread(id: ID): QGThread? /** diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuild.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuild.kt index 6372eb08..84e55301 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuild.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/guild/QGGuild.kt @@ -24,8 +24,12 @@ import love.forte.simbot.common.id.StringID.Companion.ID import love.forte.simbot.common.time.Timestamp import love.forte.simbot.component.qguild.ExperimentalQGApi import love.forte.simbot.component.qguild.QGObjectiveContainer -import love.forte.simbot.component.qguild.channel.* +import love.forte.simbot.component.qguild.channel.QGCategoryChannel +import love.forte.simbot.component.qguild.channel.QGChannel +import love.forte.simbot.component.qguild.channel.QGForumChannel +import love.forte.simbot.component.qguild.channel.QGTextChannel import love.forte.simbot.component.qguild.role.QGGuildRole +import love.forte.simbot.component.qguild.role.QGRole import love.forte.simbot.component.qguild.role.QGRoleCreator import love.forte.simbot.component.qguild.utils.toTimestamp import love.forte.simbot.qguild.QQGuildApiException @@ -128,7 +132,7 @@ public interface QGGuild : SimbotGuild, CoroutineScope, QGObjectiveContainer - @ST(blockingBaseName = "getGuild", blockingSuffix = "", asyncBaseName = "getGuild") + @ST(blockingBaseName = "getGuild", blockingSuffix = "", asyncBaseName = "getGuild", reserveBaseName = "getGuild") override suspend fun guild(id: ID): QGGuild? /** @@ -83,7 +83,7 @@ public interface QGGuildRelation : GuildRelation { * * @throws QQGuildApiException 请求失败,例如没有权限 */ - @ST(blockingBaseName = "getChannel", blockingSuffix = "", asyncBaseName = "getChannel") + @ST(blockingBaseName = "getChannel", blockingSuffix = "", asyncBaseName = "getChannel", reserveBaseName = "getChannel") public suspend fun channel(channelId: ID): QGChannel? /** @@ -94,7 +94,7 @@ public interface QGGuildRelation : GuildRelation { * @throws QQGuildApiException 请求失败,例如没有权限 * @throws IllegalStateException 当目标子频道的类型不属于 [文字类型][ChannelType.TEXT] 时 */ - @ST(blockingBaseName = "getChatChannel", blockingSuffix = "", asyncBaseName = "getChatChannel") + @ST(blockingBaseName = "getChatChannel", blockingSuffix = "", asyncBaseName = "getChatChannel", reserveBaseName = "getChatChannel") public suspend fun chatChannel(channelId: ID): QGTextChannel? @@ -105,7 +105,7 @@ public interface QGGuildRelation : GuildRelation { * @throws IllegalStateException 当目标子频道的类型不属于 [分组类型][ChannelType.CATEGORY] 时 * */ - @ST(blockingBaseName = "getCategory", blockingSuffix = "", asyncBaseName = "getCategory") + @ST(blockingBaseName = "getCategory", blockingSuffix = "", asyncBaseName = "getCategory", reserveBaseName = "getCategory") public suspend fun category(channelId: ID): QGCategoryChannel? /** @@ -114,7 +114,7 @@ public interface QGGuildRelation : GuildRelation { * @throws QQGuildApiException API请求过程中出现的异常 * @throws IllegalStateException 当目标子频道类型不是 [ChannelType.FORUM] 时 */ - @ST(blockingBaseName = "getForumChannel", blockingSuffix = "", asyncBaseName = "getForumChannel") + @ST(blockingBaseName = "getForumChannel", blockingSuffix = "", asyncBaseName = "getForumChannel", reserveBaseName = "getForumChannel") public suspend fun forumChannel(id: ID): QGForumChannel? } diff --git a/simbot-component-qq-guild-stdlib/build.gradle.kts b/simbot-component-qq-guild-stdlib/build.gradle.kts index 3a4e2247..e0aae6c6 100644 --- a/simbot-component-qq-guild-stdlib/build.gradle.kts +++ b/simbot-component-qq-guild-stdlib/build.gradle.kts @@ -59,6 +59,7 @@ kotlin { api(project(":simbot-component-qq-guild-api")) api(libs.simbot.common.loop) api(libs.simbot.common.atomic) + api(libs.simbot.common.core) compileOnly(libs.simbot.common.annotations) // ktor api(libs.ktor.client.contentNegotiation) diff --git a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.jvm.kt b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.jvm.kt index e6d3f68c..eb3fa937 100644 --- a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.jvm.kt +++ b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.jvm.kt @@ -90,13 +90,13 @@ public fun interface JAsyncEventProcessor : EventProcessor { * 以异步的形式实现 [EventProcessor],是提供给 Java 的友好类型。 * 可直接使用 `EventProcessors.async((event, raw) -> { ... })` 构建。 */ -public fun interface TypedJAsyncEventProcessor { +public fun interface TypedJAsyncEventProcessor { /** * 处理事件。 */ @Throws(Exception::class) @NonBlocking - public fun async(event: Signal.Dispatch, raw: String): CompletionStage + public fun async(event: T, raw: String): CompletionStage } /** @@ -122,7 +122,7 @@ public fun block(type: Class, function: TypedJBlockEven /** * 构建一个 [JAsyncEventProcessor]. */ -public fun async(type: Class, function: TypedJAsyncEventProcessor): JAsyncEventProcessor = +public fun async(type: Class, function: TypedJAsyncEventProcessor): JAsyncEventProcessor = JAsyncEventProcessor { event, raw -> if (type.isInstance(event)) { function.async(type.cast(event), raw) From 4bdd1ab2cb0ee8960c5dc04a7c2967c7c1923100 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 22 Jan 2024 00:12:28 +0800 Subject: [PATCH 25/71] =?UTF-8?q?=E5=B0=9D=E8=AF=95dev=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E5=8F=91=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-v4-website.yml | 2 +- Writerside/c.list | 2 +- Writerside/d.tree | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/deploy-v4-website.yml b/.github/workflows/deploy-v4-website.yml index 1c58f047..1f0ea6b9 100644 --- a/.github/workflows/deploy-v4-website.yml +++ b/.github/workflows/deploy-v4-website.yml @@ -81,7 +81,7 @@ jobs: deploy: environment: - name: github-pages + name: github-pages-dev url: ${{ steps.deployment.outputs.page_url }} # Requires the build job results needs: test diff --git a/Writerside/c.list b/Writerside/c.list index c4c77a29..5babfee2 100644 --- a/Writerside/c.list +++ b/Writerside/c.list @@ -3,4 +3,4 @@ SYSTEM "https://resources.jetbrains.com/writerside/1.0/categories.dtd"> - \ No newline at end of file + diff --git a/Writerside/d.tree b/Writerside/d.tree index aefa4bed..d02a9bc9 100644 --- a/Writerside/d.tree +++ b/Writerside/d.tree @@ -3,7 +3,7 @@ SYSTEM "https://resources.jetbrains.com/writerside/1.0/product-profile.dtd"> From 5d72f38f10f4f53587d59a75ab654c6da1098c47 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 22 Jan 2024 00:22:35 +0800 Subject: [PATCH 26/71] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E5=8F=91=E5=B8=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-v4-website.yml | 2 +- Writerside/d.tree | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-v4-website.yml b/.github/workflows/deploy-v4-website.yml index 1f0ea6b9..1c58f047 100644 --- a/.github/workflows/deploy-v4-website.yml +++ b/.github/workflows/deploy-v4-website.yml @@ -81,7 +81,7 @@ jobs: deploy: environment: - name: github-pages-dev + name: github-pages url: ${{ steps.deployment.outputs.page_url }} # Requires the build job results needs: test diff --git a/Writerside/d.tree b/Writerside/d.tree index d02a9bc9..c2a67b12 100644 --- a/Writerside/d.tree +++ b/Writerside/d.tree @@ -6,6 +6,7 @@ name="Simple Robot | QQ频道" start-page="Home.md"> + From 5ce7e61fd2f9122d637c08b21e4c908b866a59e4 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 22 Jan 2024 00:25:11 +0800 Subject: [PATCH 27/71] Home and readme --- README.md | 10 ++++++---- Writerside/topics/Home.md | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 953967e6..e1c834c2 100644 --- a/README.md +++ b/README.md @@ -25,20 +25,22 @@ [**Simple Robot v4**](https://github.com/simple-robot/simpler-robot/tree/v4-dev) 下的子项目,是针对 [**QQ频道机器人**](https://bot.q.qq.com/wiki/develop/api/) -各方面的实现, +各方面的 simbot 组件库实现, 包括对 `API` 内容的实现、事件相关的实现以及BOT对于事件的监听与交互等。 - 基于 [`Kotlin`](https://kotlinlang.org/) 提供 [KMP 多平台](https://kotlinlang.org/docs/multiplatform.html) 特性 - 基于 [`Kotlin coroutines`](https://github.com/Kotlin/kotlinx.coroutines) 与 [`Ktor`](https://ktor.io/) 提供高效易用的API; -- 基于 [`Kotlin serialization`](https://github.com/Kotlin/kotlinx.serialization) 进行数据序列化/反序列化操作。 + +- 基于 [`Kotlin`](https://kotlinlang.org/) 提供 [KMP 多平台](https://kotlinlang.org/docs/multiplatform.html) 特性,提供 Java 友好的API。 +- 基于 [`Kotlin coroutines`](https://github.com/Kotlin/kotlinx.coroutines) 与 [`Ktor`](https://ktor.io/) 提供轻量高效的API。 > [!Note] > 下文中 `Simple Robot v4` 简称为 `simbot4` ## 文档 -- 了解simbot: [**simbot官网**](https://simbot.forte.love) -- **QQ频道组件**手册: +- 了解simbot: [**Simple Robot 应用手册**](https://simbot.forte.love) +- **QQ频道组件**手册: (即当前仓库的 GitHub Pages) - **API文档**: [**文档引导站点**](https://docs.simbot.forte.love) 中QQ频道的 [**KDoc站点**](https://docs.simbot.forte.love/components/qq-guild) --- diff --git a/Writerside/topics/Home.md b/Writerside/topics/Home.md index 7320b249..d11b8824 100644 --- a/Writerside/topics/Home.md +++ b/Writerside/topics/Home.md @@ -18,7 +18,7 @@ -[**Simple Robot 官网**](https://simbot.forte.love) +[**Simple Robot 应用手册**](https://simbot.forte.love) From 3adff7195da09efa7236ca8e412243222ba33f94 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 22 Jan 2024 00:28:47 +0800 Subject: [PATCH 28/71] v4.0.0-dev5 --- .changelog/v4.0.0-dev5.md | 11 +++++++++++ Writerside/v.list | 2 +- .../forte/simbot/component/qguild/QQGuildComponent.kt | 2 +- .../simbot/component/qguild/bot/QQGuildBotManager.kt | 2 +- 4 files changed, 14 insertions(+), 3 deletions(-) create mode 100644 .changelog/v4.0.0-dev5.md diff --git a/.changelog/v4.0.0-dev5.md b/.changelog/v4.0.0-dev5.md new file mode 100644 index 00000000..7b688fcd --- /dev/null +++ b/.changelog/v4.0.0-dev5.md @@ -0,0 +1,11 @@ +> 对应核心版本: [**v4.0.0-dev13**](https://github.com/simple-robot/simpler-robot/releases/tag/v4.0.0-dev13) + + +> [!warning] +> **目前版本处于 `dev` 阶段,代表此版本是一个开发预览版,可能不稳定、可能随时发生更改、且不保证可用性。** + + +我们欢迎并期望着您的的[反馈](https://github.com/simple-robot/simbot-component-qq-guild/issues)或[协助](https://github.com/simple-robot/simbot-component-qq-guild/pulls), +感谢您的贡献与支持! + +也欢迎您为我们献上一颗 `star`,这是对我们最大的鼓励与认可! diff --git a/Writerside/v.list b/Writerside/v.list index 1d64f81e..65e3732c 100644 --- a/Writerside/v.list +++ b/Writerside/v.list @@ -3,6 +3,6 @@ - + diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt index edba8262..c068ad55 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/QQGuildComponent.kt @@ -158,7 +158,7 @@ public class QQGuildComponentConfiguration */ public class QQGuildComponentFactoryProvider : ComponentFactoryProvider { - override fun loadConfigurers(): Sequence? = + override fun loadConfigures(): Sequence? = loadQGComponentConfigurers() override fun provide(): ComponentFactory<*, QQGuildComponentConfiguration> = QQGuildComponent diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt index 94a50d6c..02358d9b 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/QQGuildBotManager.kt @@ -279,7 +279,7 @@ public class QQGuildBotManagerConfiguration { * Provide [QQGuildBotManager.Factory] to SPI */ public class QQGuildBotManagerProvider : PluginFactoryProvider { - override fun configurersLoader(): Sequence? = + override fun loadConfigures(): Sequence? = loadQQGuildBotManagerConfigurers() override fun provide(): PluginFactory<*, QQGuildBotManagerConfiguration> = QQGuildBotManager From beefde378ab7b070723f905f6ea0d088ece24b6b Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 22 Jan 2024 15:25:22 +0800 Subject: [PATCH 29/71] Update use-spring-boot.md --- Writerside/topics/use-spring-boot.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Writerside/topics/use-spring-boot.md b/Writerside/topics/use-spring-boot.md index 5e9d9c23..43842d96 100644 --- a/Writerside/topics/use-spring-boot.md +++ b/Writerside/topics/use-spring-boot.md @@ -91,13 +91,13 @@ implementation 'love.forte.simbot.component:simbot-component-qq-guild-core-jvm:% ```xml - love.forte.simbot + love.forte.simbot simbot-core-spring-boot-starter %minimum-core-version% - love.forte.simbot.component + love.forte.simbot.component simbot-component-qq-guild-core-jvm %version% From aafcc01fb53ac2cd3743821fe3c93df87d254cb3 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Tue, 23 Jan 2024 20:31:46 +0800 Subject: [PATCH 30/71] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E5=92=8C=E4=BE=9D=E8=B5=96=E7=89=88=E6=9C=AC=EF=BC=8C=E4=BB=A5?= =?UTF-8?q?=E5=8F=8A=E4=B8=80=E4=BA=9BTODO?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/kdoc.yml.bk | 4 +- .github/workflows/publish-release.yml | 4 +- .github/workflows/publish-snapshot.yml | 4 +- .github/workflows/publish-v4-release.yml | 4 +- .github/workflows/publish-v4-snapshot.yml | 4 +- Writerside/d.tree | 5 +- Writerside/topics/api.md | 5 + Writerside/topics/ark/api_ark.md | 2 +- Writerside/topics/bot-config.md | 13 + Writerside/topics/embed/api_embed.md | 2 +- Writerside/topics/event.md | 104 +++++ Writerside/topics/forum/api_forum.md | 2 +- Writerside/topics/messages.md | 35 ++ Writerside/topics/use-api.md | 11 +- Writerside/topics/use-core.md | 7 +- Writerside/topics/use-spring-boot.md | 366 +++++++++++++++++- Writerside/topics/use-stdlib.md | 2 +- Writerside/v.list | 2 +- buildSrc/src/main/kotlin/P.kt | 34 +- gradle/libs.versions.toml | 2 +- .../love/forte/simbot/qguild/event/Signal.kt | 16 +- .../build.gradle.kts | 1 + .../bot/config/QGBotFileConfiguration.kt | 2 + .../component/qguild/event/QGForumEvents.kt | 2 + .../component/qguild/event/QGMessageEvents.kt | 45 +++ .../component/qguild/message/QGContentText.kt | 4 +- .../component/qgguild/test/FileConfigTest.kt | 37 ++ 27 files changed, 672 insertions(+), 47 deletions(-) create mode 100644 Writerside/topics/api.md create mode 100644 Writerside/topics/event.md create mode 100644 Writerside/topics/messages.md diff --git a/.github/workflows/kdoc.yml.bk b/.github/workflows/kdoc.yml.bk index bb6c23fd..ede31418 100644 --- a/.github/workflows/kdoc.yml.bk +++ b/.github/workflows/kdoc.yml.bk @@ -44,7 +44,7 @@ jobs: with: personal_token: ${{ secrets.PUSH_TOKEN }} external_repository: simple-robot-library/simbot3-api-docs - publish_branch: kdoc-deploy/component-tencent-guild + publish_branch: kdoc-deploy/component-tencent-guild-v3 publish_dir: ./build/dokka/html # deploy to sub dir - destination_dir: components/tencent-guild + destination_dir: components/tencent-guild-v3 diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index e9d40e79..bde9bcb8 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -156,7 +156,7 @@ jobs: with: personal_token: ${{ secrets.PUSH_TOKEN }} external_repository: simple-robot-library/simbot3-api-docs - publish_branch: kdoc-deploy/component-qq-guild + publish_branch: kdoc-deploy/component-qq-guild-v3 publish_dir: ./build/dokka/html # deploy to sub dir - destination_dir: components/qq-guild + destination_dir: components/qq-guild-v3 diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml index 5e279b8d..39a6c086 100644 --- a/.github/workflows/publish-snapshot.yml +++ b/.github/workflows/publish-snapshot.yml @@ -99,7 +99,7 @@ jobs: with: personal_token: ${{ secrets.PUSH_TOKEN }} external_repository: simple-robot-library/simbot3-api-docs - publish_branch: kdoc-deploy/snapshots/component-qq-guild + publish_branch: kdoc-deploy/snapshots/component-qq-guild-v3 publish_dir: ./build/dokka/html # deploy to sub dir - destination_dir: snapshots/components/qq-guild + destination_dir: snapshots/components/qq-guild-v3 diff --git a/.github/workflows/publish-v4-release.yml b/.github/workflows/publish-v4-release.yml index 3f7c9d8f..22dae6eb 100644 --- a/.github/workflows/publish-v4-release.yml +++ b/.github/workflows/publish-v4-release.yml @@ -156,7 +156,7 @@ jobs: with: personal_token: ${{ secrets.PUSH_TOKEN }} external_repository: simple-robot-library/simbot3-api-docs - publish_branch: kdoc-deploy/component-qq-guild-v4 + publish_branch: kdoc-deploy/component-qq-guild publish_dir: ./build/dokka/html # deploy to sub dir - destination_dir: components/qq-guild-v4 + destination_dir: components/qq-guild diff --git a/.github/workflows/publish-v4-snapshot.yml b/.github/workflows/publish-v4-snapshot.yml index b7c29b9c..97b26e61 100644 --- a/.github/workflows/publish-v4-snapshot.yml +++ b/.github/workflows/publish-v4-snapshot.yml @@ -105,7 +105,7 @@ jobs: with: personal_token: ${{ secrets.PUSH_TOKEN }} external_repository: simple-robot-library/simbot3-api-docs - publish_branch: kdoc-deploy/snapshots/component-qq-guild-v4 + publish_branch: kdoc-deploy/snapshots/component-qq-guild publish_dir: ./build/dokka/html # deploy to sub dir - destination_dir: snapshots/components/qq-guild-v4 + destination_dir: snapshots/components/qq-guild diff --git a/Writerside/d.tree b/Writerside/d.tree index c2a67b12..b3f9a51c 100644 --- a/Writerside/d.tree +++ b/Writerside/d.tree @@ -22,7 +22,7 @@ - + @@ -31,9 +31,12 @@ + + + diff --git a/Writerside/topics/api.md b/Writerside/topics/api.md new file mode 100644 index 00000000..ee2089a8 --- /dev/null +++ b/Writerside/topics/api.md @@ -0,0 +1,5 @@ +# 概述 + +提供一些针对QQ频道提供的一些特殊消息、具体功能概念实现的描述。 + +比如,[Ark消息](api_ark.md) 即为一种特殊类型的消息,而 [角色 Role](api_role.md) 便是一种功能概念实现。 diff --git a/Writerside/topics/ark/api_ark.md b/Writerside/topics/ark/api_ark.md index 3cbf1edf..bee184f6 100644 --- a/Writerside/topics/ark/api_ark.md +++ b/Writerside/topics/ark/api_ark.md @@ -1,4 +1,4 @@ -# 消息 Ark +# Ark消息 QQ频道中有一些针对 `Ark消息` 的API。 diff --git a/Writerside/topics/bot-config.md b/Writerside/topics/bot-config.md index 4c472eae..e67d653d 100644 --- a/Writerside/topics/bot-config.md +++ b/Writerside/topics/bot-config.md @@ -4,6 +4,19 @@

在使用 Spring Boot 时自动注册 bot 所需的配置文件。

+ + +如果你在使用 Spring Boot, +将配置文件放在在你的资源目录中: +resources/simbot-bots/, +并以 `.bot.json` 作为扩展名,例如 `mybot.bot.json`。 + +这个扫描目录是可配置的。 +这是属于 simbot4 Spring Boot starter 的配置,可参考 +[simbot手册: 使用 Spring Boot 3](https://simbot.forte.love/start-use-spring-boot-3.html)。 + + + ## 示例 ```json diff --git a/Writerside/topics/embed/api_embed.md b/Writerside/topics/embed/api_embed.md index 696ea394..66a519dd 100644 --- a/Writerside/topics/embed/api_embed.md +++ b/Writerside/topics/embed/api_embed.md @@ -1,4 +1,4 @@ -# 消息 Embed +# Embed消息 QQ频道中有一些针对 `Embed消息` 的API。 diff --git a/Writerside/topics/event.md b/Writerside/topics/event.md new file mode 100644 index 00000000..e78c437c --- /dev/null +++ b/Writerside/topics/event.md @@ -0,0 +1,104 @@ +# 事件类型 + +QQ频道组件中的**事件类型**包含两个层面: + +1. **API 模块** 中,对 QQ频道 API 中官方定义的事件结构的基本封装与实现。 +2. **核心模块** 中,基于 API 模块中的事件封装,对 simbot4 标准库中的 `Event` 事件类型的实现。 + + +## API 模块事件封装 + +API 模块所有的事件封装类型都在包 `love.forte.simbot.qguild.event` 中, +并且基本上命名与官网API中的事件类型名称有一定关联。 + +所有事件封装类型均继承密封类 `love.forte.simbot.qguild.event.Signal.Dispatch`。 + + +子频道创建事件 CHANNEL_CREATE +子频道修改事件 CHANNEL_UPDATE +子频道删除事件 CHANNEL_DELETE +主题创建事件 FORUM_THREAD_CREATE +主题更新事件 FORUM_THREAD_UPDATE +主题删除事件 FORUM_THREAD_DELETE +帖子创建事件 FORUM_POST_CREATE +帖子删除事件 FORUM_POST_DELETE +回复创建事件 FORUM_REPLY_CREATE +回复删除事件 FORUM_REPLY_DELETE +帖子审核事件 FORUM_PUBLISH_AUDIT_RESULT +"开放"创建主题事件 OPEN_FORUM_THREAD_CREATE +"开放"更新主题事件 OPEN_FORUM_THREAD_UPDATE +"开放"删除主题事件 OPEN_FORUM_THREAD_DELETE +"开放"帖子创建(评论)事件 OPEN_FORUM_POST_CREATE +"开放"帖子删除(评论)事件 OPEN_FORUM_POST_DELETE +"开放"回复创建事件 OPEN_FORUM_REPLY_CREATE +"开放"回复删除事件 OPEN_FORUM_REPLY_DELETE +Bot加入频道事件 GUILD_CREATE +频道信息变更事件 GUILD_UPDATE +Bot因各种原因退出频道事件 GUILD_DELETE +新用户加入频道事件 GUILD_MEMBER_ADD +用户的频道属性发生变化事件 GUILD_MEMBER_UPDATE +用户离开频道事件 GUILD_MEMBER_REMOVE +收到公域at消息事件 AT_MESSAGE_CREATE + +PUBLIC_MESSAGE_DELETE +此事件官网文档似乎没有详细说明,请慎重使用 + +私信消息事件 DIRECT_MESSAGE_CREATE +(私域)发送消息事件 MESSAGE_CREATE + +MESSAGE_DELETE +此事件官网文档似乎没有详细说明,请慎重使用 + +消息审核通过事件 MESSAGE_AUDIT_PASS +消息审核不通过事件 MESSAGE_AUDIT_REJECT + + +API 模块事件封装可以使用在 **标准库模块 (stdlib)** 中,使用 `Bot` 类型对他们进行监听与处理。 + + +## simbot Event 实现 + +使用核心库,可以在 simbot4 的 `Application` 或 Spring Boot 中使用这些事件类型实现。 + +核心模块所有的 simbot Event 实现类型定义都在包 `love.forte.simbot.component.qguild.event` 中。 + +所有实现类型均继承 `love.forte.simbot.qguild.component.event.QGEvent`。 + +> QQ频道的 simbot 事件实现会根据含义,选择性的实现一些特定的类型。 +> 举个例子,`QGAtMessageCreateEvent` 可以代表“子频道消息事件”, +> 因此它实现了 `ChatChannelMessageEvent`。 + + +仔细观察可以发现,大部分 simbot Event 实现类型都可以与 API 模块的事件封装类型相对应。 + + + +子频道创建事件 +子频道修改事件 +子频道删除事件 +主题创建事件 +主题更新事件 +主题删除事件 +帖子创建事件 +帖子删除事件 +回复创建事件 +回复删除事件 +帖子审核事件 +新用户加入频道事件 +用户的频道属性发生变化事件 +用户离开频道事件 +新用户加入频道事件 +用户的频道属性发生变化事件 +用户离开频道事件 +收到公域at消息事件 +"开放"创建主题事件 +"开放"更新主题事件 +"开放"删除主题事件 +"开放"帖子创建(评论)事件 +"开放"帖子删除(评论)事件 +"开放"回复创建事件 +"开放"回复删除事件 + +特殊的事件类型,用于包装兼容那些尚未被封装支持的 API 模块的事件封装类型。 + + diff --git a/Writerside/topics/forum/api_forum.md b/Writerside/topics/forum/api_forum.md index 8addbf94..2c5a8f2e 100644 --- a/Writerside/topics/forum/api_forum.md +++ b/Writerside/topics/forum/api_forum.md @@ -1,5 +1,5 @@ --- -switcher-label: Java API +switcher-label: Java API 风格 --- # 论坛 Forum diff --git a/Writerside/topics/messages.md b/Writerside/topics/messages.md new file mode 100644 index 00000000..30aa93d6 --- /dev/null +++ b/Writerside/topics/messages.md @@ -0,0 +1,35 @@ +# 消息类型 + + + +- 对那些 **核心库** 中、实现了 simbot4 标准库的 `Message.Element` 消息元素类型的说明, +- 对一些与 **核心库** 中“消息”相关的内容的补充说明。 + + + +## 消息元素实现 + +所有的 `Message.Element` 特殊实现类型均定义在包 `love.forte.simbot.component.qguild.message` 中。 + +它们都继承了 `love.forte.simbot.component.qguild.message.QGMessageElement` 。 + + +对 API 模块中 Ark 消息的包装体,可用来发送 Ark 消息。 + +对 API 模块中 Embed 消息的包装体,可用来发送 Embed 消息。 +TODO Message type +TODO Message type + + +发送消息时,QQ频道的消息引用。与官方发送消息API中的 `reference` 对应。 + + + + +仅用于发送。 + +发送消息时,指定一个需要回复的目标消息ID。 + + + + diff --git a/Writerside/topics/use-api.md b/Writerside/topics/use-api.md index 35c82ae9..5f5de68e 100644 --- a/Writerside/topics/use-api.md +++ b/Writerside/topics/use-api.md @@ -1,5 +1,5 @@ --- -switcher-label: Java API +switcher-label: Java API 风格 --- @@ -10,6 +10,13 @@ switcher-label: Java API

本章节介绍如何使用 API 模块 来构建、请求一个QQ频道的API。

+ + +API 模块 是一个“基础”模块,它仅提供针对 QQ频道 API 的封装, +没有 Bot、事件处理等功能。 + + + ## 安装 @@ -218,6 +225,8 @@ var api = GetBotGuildListApi.create(100); // 发起请求并得到结果 ApiRequests.requestDataReserve(api, client, token, server) + // 使用此转化器需要确保运行时环境中存在 + // [[[kotlinx-coroutines-reactor|https://github.com/Kotlin/kotlinx.coroutines/tree/master/reactive]]] 的相关依赖。 .transform(SuspendReserves.mono()) .subscribe(guildList -> { for (var guild : guildList) { diff --git a/Writerside/topics/use-core.md b/Writerside/topics/use-core.md index 1b21623e..7f41f1f4 100644 --- a/Writerside/topics/use-core.md +++ b/Writerside/topics/use-core.md @@ -1,5 +1,5 @@ --- -switcher-label: Java API +switcher-label: Java API 风格 --- # 使用核心库 @@ -97,6 +97,11 @@ implementation 'love.forte.simbot.component:simbot-component-qq-guild-core-jvm:% +### 引擎选择 + + + + ## 使用 ### 安装到 Application diff --git a/Writerside/topics/use-spring-boot.md b/Writerside/topics/use-spring-boot.md index 43842d96..1f12af21 100644 --- a/Writerside/topics/use-spring-boot.md +++ b/Writerside/topics/use-spring-boot.md @@ -1,3 +1,9 @@ +--- +switcher-label: Java API 风格 +--- + + + # 使用 Spring Boot @@ -106,16 +112,23 @@ implementation 'love.forte.simbot.component:simbot-component-qq-guild-core-jvm:% +### 引擎选择 + + + + ## Bot 配置 在你的资源目录中: resources/simbot-bots/ -中创建任意的一个或多个bot配置文件,并以 `.bot.json` 作为扩展名。 +中创建任意的一个或多个bot配置文件,并以 `.bot.json` 作为扩展名, +例如 `mybot.bot.json`。 配置文件的内容可前往参考 [Bot配置文件](bot-config.md) 章节。 > 这个扫描目录是可配置的。 -> 这是属于 simbot4 Spring Boot starter 的配置,下文会有相关的引导链接到 simbot4 应用手册的相关内容处。 +> 这是属于 simbot4 Spring Boot starter 的配置,可参考 +> [simbot手册: 使用 Spring Boot 3](https://simbot.forte.love/start-use-spring-boot-3.html)。 ## 使用 ### 添加启动注解 @@ -164,4 +177,353 @@ QQ频道组件支持使用 SPI **自动加载** 组件和插件。 了解更多 simbot4 Spring Boot starter 的可配置内容以及启动注解等信息。 +## 简单示例 + +几个简单的**事件监听**示例。 + + + + + + +```Kotlin +@EnableSimbot // 启用 simbot +@SpringBootApplication +class MyApplication + +fun main(args: Array) { + runApplicarion(*args) +} + +private val logger = LoggerFactory.getLogger(MyHandlers::class.java) + +@Component // 需要交给Spring管理 +class MyHandlers { + @Listener // 标记事件处理函数 + suspend fun handle(event: Event) { + logger.info("Event: {}", event) + } +} +``` + + + + +```Java +@EnableSimbot // 启用 simbot +@SpringBootApplication +public class MyApplication { + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } + + @Component // 需要交给Spring管理 + public static class MyHandlers { + private static final Logger logger = Logger.getLogger(MyHandlers.class); + + @Listener // 标记事件处理函数 + public void handle(Event event) { + logger.info("Event: {}", event) + } + } +} +``` + + + + + + +此处以 "子频道消息事件" 为例,此事件类型为 `ChatChannelMessageEvent`。 + +事件逻辑:当收到文本内 **包含"你好"** 的消息,回复"你也好"。 + + + + +```Kotlin +@EnableSimbot // 启用 simbot +@SpringBootApplication +class MyApplication + +fun main(args: Array) { + runApplicarion(*args) +} + +@Component // 需要交给Spring管理 +class MyHandlers { + @Listener // 标记事件处理函数 + @Filter( // 简单的事件过滤注解 + value = "你好", // 需要包含的文字 + matchType = MatchType.TEXT_CONENT // 调整匹配模式为 "文本包含" + ) + // 如果需要,添加此注解来去除匹配用的文本前后的空白字符。 + // 由于 QQ 频道的公域 bot 都是需要被 at 的, + // 而被 at 时,文本消息很有可能存在一些前后空格,所以会比较有用 + @ContentTrim + suspend fun handle(event: ChatChannelMessageEvent) { + event.reply("你也好") + } +} +``` + + + + +```Java +@EnableSimbot // 启用 simbot +@SpringBootApplication +public class MyApplication { + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } + + @Component // 需要交给Spring管理 + public static class MyHandlers { + @Listener // 标记事件处理函数 + @Filter( // 简单的事件过滤注解 + value = "你好", // 需要包含的文字 + matchType = MatchType.TEXT_CONENT // 调整匹配模式为 "文本包含" + ) + // 如果需要,添加此注解来去除匹配用的文本前后的空白字符。 + // 由于 QQ 频道的公域 bot 都是需要被 at 的, + // 而被 at 时,文本消息很有可能存在一些前后空格,所以会比较有用 + @ContentTrim + public CompletableFuture handle(ChatChannelMessageEvent event) { + return event.replyAsync("你也好"); + // 当使用异步API (CompletableFuture) 时, + // 你需要格外注意异常处理问题。 + // 如果你不 return, + // 那么你就要处理异常,否则你无法感知到异步任务中出现的错误。 + // 比如: + // .whenComplete((value, e) -> { + // if (e != null) { + // // 比如输出错误日志 + // } + // }) + } + } +} +``` +{switcher-key="%ja%"} + +```Java +@EnableSimbot // 启用 simbot +@SpringBootApplication +public class MyApplication { + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } + + @Component // 需要交给Spring管理 + public static class MyHandlers { + @Listener // 标记事件处理函数 + @Filter( // 简单的事件过滤注解 + value = "你好", // 需要包含的文字 + matchType = MatchType.TEXT_CONENT // 调整匹配模式为 "文本包含" + ) + // 如果需要,添加此注解来去除匹配用的文本前后的空白字符。 + // 由于 QQ 频道的公域 bot 都是需要被 at 的, + // 而被 at 时,文本消息很有可能存在一些前后空格,所以会比较有用 + @ContentTrim + public void handle(ChatChannelMessageEvent event) { + event.replyBlocking("你也好"); + } + } +} +``` +{switcher-key="%jb%"} + +```Java +@EnableSimbot // 启用 simbot +@SpringBootApplication +public class MyApplication { + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } + + @Component // 需要交给Spring管理 + public static class MyHandlers { + @Listener // 标记事件处理函数 + @Filter( // 简单的事件过滤注解 + value = "你好", // 需要包含的文字 + matchType = MatchType.TEXT_CONENT // 调整匹配模式为 "文本包含" + ) + // 如果需要,添加此注解来去除匹配用的文本前后的空白字符。 + // 由于 QQ 频道的公域 bot 都是需要被 at 的, + // 而被 at 时,文本消息很有可能存在一些前后空格,所以会比较有用 + @ContentTrim + public Mono handle(ChatChannelMessageEvent event) { + return event.replyReserve("你也好") + // 使用此转化器需要确保运行时环境中存在 + // [[[kotlinx-coroutines-reactor|https://github.com/Kotlin/kotlinx.coroutines/tree/master/reactive]]] 的相关依赖。 + .transform(SuspendReserves.mono()); + // 当使用异步API (比如此处的 Mono) 时, + // 你需要格外注意异常处理问题。 + // 如果你不 return, + // 那么你就要处理异常,否则你无法感知到异步任务中出现的错误。 + // 比如: + // .doOnError(err -> { + // // 比如输出错误日志 + // }); + } + } +} +``` +{switcher-key="%jr%"} + + + + + + +此处以 "QQ频道的公域子频道At消息事件" 为例, +此事件类型为 `QGAtMessageCreateEvent`。 + +这个事件是QQ频道组件里实现的专有事件类型,它继承 +`ChatChannelMessageEvent`。 + +事件逻辑:当收到文本内 **包含"你好"** 的消息,回复"你也好"。 + + + + +```Kotlin +@EnableSimbot // 启用 simbot +@SpringBootApplication +class MyApplication + +fun main(args: Array) { + runApplicarion(*args) +} + +@Component // 需要交给Spring管理 +class MyHandlers { + @Listener // 标记事件处理函数 + @Filter( // 简单的事件过滤注解 + value = "你好", // 需要包含的文字 + matchType = MatchType.TEXT_CONENT // 调整匹配模式为 "文本包含" + ) + // 如果需要,添加此注解来去除匹配用的文本前后的空白字符。 + // 由于 QQ 频道的公域 bot 都是需要被 at 的, + // 而被 at 时,文本消息很有可能存在一些前后空格,所以会比较有用 + @ContentTrim + suspend fun handle(event: QGAtMessageCreateEvent) { + event.reply("你也好") + } +} +``` + + + + +```Java +@EnableSimbot // 启用 simbot +@SpringBootApplication +public class MyApplication { + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } + + @Component // 需要交给Spring管理 + public static class MyHandlers { + @Listener // 标记事件处理函数 + @Filter( // 简单的事件过滤注解 + value = "你好", // 需要包含的文字 + matchType = MatchType.TEXT_CONENT // 调整匹配模式为 "文本包含" + ) + // 如果需要,添加此注解来去除匹配用的文本前后的空白字符。 + // 由于 QQ 频道的公域 bot 都是需要被 at 的, + // 而被 at 时,文本消息很有可能存在一些前后空格,所以会比较有用 + @ContentTrim + public CompletableFuture handle(QGAtMessageCreateEvent event) { + return event.replyAsync("你也好"); + // 当使用异步API (CompletableFuture) 时, + // 你需要格外注意异常处理问题。 + // 如果你不 return, + // 那么你就要处理异常,否则你无法感知到异步任务中出现的错误。 + // 比如: + // .whenComplete((value, e) -> { + // if (e != null) { + // // 比如输出错误日志 + // } + // }) + } + } +} +``` +{switcher-key="%ja%"} + +```Java +@EnableSimbot // 启用 simbot +@SpringBootApplication +public class MyApplication { + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } + + @Component // 需要交给Spring管理 + public static class MyHandlers { + @Listener // 标记事件处理函数 + @Filter( // 简单的事件过滤注解 + value = "你好", // 需要包含的文字 + matchType = MatchType.TEXT_CONENT // 调整匹配模式为 "文本包含" + ) + // 如果需要,添加此注解来去除匹配用的文本前后的空白字符。 + // 由于 QQ 频道的公域 bot 都是需要被 at 的, + // 而被 at 时,文本消息很有可能存在一些前后空格,所以会比较有用 + @ContentTrim + public void handle(QGAtMessageCreateEvent event) { + event.replyBlocking("你也好"); + } + } +} +``` +{switcher-key="%jb%"} + +```Java +@EnableSimbot // 启用 simbot +@SpringBootApplication +public class MyApplication { + public static void main(String[] args) { + SpringApplication.run(MyApplication.class, args); + } + + @Component // 需要交给Spring管理 + public static class MyHandlers { + @Listener // 标记事件处理函数 + @Filter( // 简单的事件过滤注解 + value = "你好", // 需要包含的文字 + matchType = MatchType.TEXT_CONENT // 调整匹配模式为 "文本包含" + ) + // 如果需要,添加此注解来去除匹配用的文本前后的空白字符。 + // 由于 QQ 频道的公域 bot 都是需要被 at 的, + // 而被 at 时,文本消息很有可能存在一些前后空格,所以会比较有用 + @ContentTrim + public Mono handle(QGAtMessageCreateEvent event) { + return event.replyReserve("你也好") + // 使用此转化器需要确保运行时环境中存在 + // [[[kotlinx-coroutines-reactor|https://github.com/Kotlin/kotlinx.coroutines/tree/master/reactive]]] 的相关依赖。 + .transform(SuspendReserves.mono()); + // 当使用响应式API (比如此处的 Mono) 时, + // 你需要格外注意异常处理问题。 + // 如果你不 return, + // 那么你就要处理异常,否则你无法感知到异步任务中出现的错误。 + // 甚至你如果不 return,可能都无法真正的执行逻辑,因为这个 Mono 没有被消费。 + // 比如: + // .doOnError(err -> { + // // 比如输出错误日志 + // }); + } + } +} +``` +{switcher-key="%jr%"} + + + + + + + [spring.start]: https://start.spring.io diff --git a/Writerside/topics/use-stdlib.md b/Writerside/topics/use-stdlib.md index 99588d15..46c32601 100644 --- a/Writerside/topics/use-stdlib.md +++ b/Writerside/topics/use-stdlib.md @@ -1,5 +1,5 @@ --- -switcher-label: Java API +switcher-label: Java API 风格 --- # 使用标准库 diff --git a/Writerside/v.list b/Writerside/v.list index 65e3732c..093ff7cc 100644 --- a/Writerside/v.list +++ b/Writerside/v.list @@ -4,5 +4,5 @@ - + diff --git a/buildSrc/src/main/kotlin/P.kt b/buildSrc/src/main/kotlin/P.kt index a3e52805..efdce511 100644 --- a/buildSrc/src/main/kotlin/P.kt +++ b/buildSrc/src/main/kotlin/P.kt @@ -21,22 +21,22 @@ import love.forte.gradle.common.core.project.minus import love.forte.gradle.common.core.property.systemProp import love.forte.gradle.common.core.project.version as v -val simbotVersion = v(3, 2, 0) -//- v("RC", 3) - -fun simbot(name: String, version: String = simbotVersion.toString()): String = "love.forte.simbot:simbot-$name:$version" -fun simboot(name: String, version: String = simbotVersion.toString()): String = - "love.forte.simbot.boot:simboot-$name:$version" - -val simbotApi = simbot("api") -val simbotCore = simbot("core") -val simbotLogger = simbot("logger") -val simbotLoggerJvm = simbot("logger-jvm") -val simbotLoggerSlf4jImpl = simbot("logger-slf4j-impl") - -val simbotUtilLoop = "love.forte.simbot.util:simbot-util-stage-loop:$simbotVersion" -val simbotUtilSuspendTransformer = "love.forte.simbot.util:simbot-util-suspend-transformer:$simbotVersion" -val simbotUtilAnnotations = "love.forte.simbot.util:simbot-annotations:$simbotVersion" +//val simbotVersion = v(3, 2, 0) +////- v("RC", 3) +// +//fun simbot(name: String, version: String = simbotVersion.toString()): String = "love.forte.simbot:simbot-$name:$version" +//fun simboot(name: String, version: String = simbotVersion.toString()): String = +// "love.forte.simbot.boot:simboot-$name:$version" +// +//val simbotApi = simbot("api") +//val simbotCore = simbot("core") +//val simbotLogger = simbot("logger") +//val simbotLoggerJvm = simbot("logger-jvm") +//val simbotLoggerSlf4jImpl = simbot("logger-slf4j-impl") +// +//val simbotUtilLoop = "love.forte.simbot.util:simbot-util-stage-loop:$simbotVersion" +//val simbotUtilSuspendTransformer = "love.forte.simbot.util:simbot-util-suspend-transformer:$simbotVersion" +//val simbotUtilAnnotations = "love.forte.simbot.util:simbot-annotations:$simbotVersion" const val SIMBOT_GROUP = "love.forte.simbot" @@ -59,7 +59,7 @@ object P { override val homepage: String get() = HOMEPAGE - private val baseVersion = v(4, 0, 0) - v("dev5") + private val baseVersion = v(4, 0, 0) - v("dev6") val snapshotVersion = baseVersion - Version.SNAPSHOT override val version = if (isSnapshot()) snapshotVersion else baseVersion diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0fe7b445..a67c2234 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ openjdk-jmh = "1.35" log4j = "2.20.0" reactor = "3.6.2" # simbot -simbot = "4.0.0-dev13" +simbot = "4.0.0-dev14" suspendTransform = "0.6.0" gradleCommon = "0.2.0" diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/Signal.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/Signal.kt index ce856e3f..75f54832 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/Signal.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/event/Signal.kt @@ -17,10 +17,7 @@ package love.forte.simbot.qguild.event -import kotlinx.serialization.Contextual -import kotlinx.serialization.KSerializer -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable +import kotlinx.serialization.* import kotlinx.serialization.builtins.IntArraySerializer import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.encoding.Decoder @@ -157,7 +154,9 @@ public sealed class Signal(@Serializable(Opcode.SerializerByCode::class) publ * @see love.forte.simbot.qguild.event * */ + @OptIn(ExperimentalSerializationApi::class) @Serializable + @JsonClassDiscriminator(Dispatch.DISPATCH_CLASS_DISCRIMINATOR) public sealed class Dispatch : Signal<@Contextual Any>(Opcode.Dispatch) { /** @@ -203,7 +202,6 @@ public sealed class Signal(@Serializable(Opcode.SerializerByCode::class) publ } - /** * 用于承载未知类型事件的事件类型。 * @@ -212,14 +210,16 @@ public sealed class Signal(@Serializable(Opcode.SerializerByCode::class) publ * [Unknown] 的构建仅由内部完成。 * */ - public data class Unknown @QGInternalApi constructor(override val s: Long, override val data: JsonElement, val raw: String) : Dispatch() + public data class Unknown @QGInternalApi constructor( + override val s: Long, + override val data: JsonElement, + val raw: String + ) : Dispatch() } } - - /** * 获取当前json结构体中的 `op` 字段。 * diff --git a/simbot-component-qq-guild-core/build.gradle.kts b/simbot-component-qq-guild-core/build.gradle.kts index 8525eec4..48c8eb04 100644 --- a/simbot-component-qq-guild-core/build.gradle.kts +++ b/simbot-component-qq-guild-core/build.gradle.kts @@ -70,6 +70,7 @@ kotlin { commonTest.dependencies { implementation(kotlin("test")) implementation(libs.kotlinx.coroutines.test) + implementation(libs.kotlinx.serialization.json) api(libs.simbot.core) api(libs.simbot.common.core) } diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotFileConfiguration.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotFileConfiguration.kt index f10254cf..3840f8fc 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotFileConfiguration.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/bot/config/QGBotFileConfiguration.kt @@ -18,6 +18,7 @@ package love.forte.simbot.component.qguild.bot.config import io.ktor.http.* +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import love.forte.simbot.bot.SerializableBotConfiguration @@ -106,6 +107,7 @@ public data class QGBotFileConfiguration( * ``` * */ + @OptIn(ExperimentalSerializationApi::class) @Serializable @UsedOnlyForConfigSerialization public data class Config( diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt index ebcc0fed..9531417a 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGForumEvents.kt @@ -269,3 +269,5 @@ public abstract class QGForumPublishAuditResultEvent : QGForumEvent() { override fun toString(): String = "QGForumPublishAuditResultEvent(sourceEventEntity=$sourceEventEntity)" } + +// TODO Open forum event? diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt index b0e80a3f..abd6da57 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/event/QGMessageEvents.kt @@ -130,3 +130,48 @@ public abstract class QGAtMessageCreateEvent : QGMessageEvent(), ChatChannelMess */ abstract override suspend fun source(): QGGuild } + +// TODO 非公域的 Message event +///** +// * 新的频道全量消息被创建。 +// * +// * @author ForteScarlet +// */ +//@STP +//public abstract class QGMessageCreateEvent : QGMessageEvent(), ChatChannelMessageEvent { +// @OptIn(ExperimentalQGApi::class) +// override val time: Timestamp +// get() = sourceEventEntity.timestamp.toTimestamp() +// +// override val authorId: ID +// get() = sourceEventEntity.author.id.ID +// +// /** +// * 发送消息的用户 +// * +// * @throws QQGuildApiException 请求失败,例如无权限 +// * @throws NoSuchElementException 没有找到结果 +// */ +// abstract override suspend fun author(): QGMember +// +// /** +// * 接收到消息的子频道。 +// * +// * @throws QQGuildApiException 请求失败,例如无权限 +// * @throws NoSuchElementException 没有找到结果 +// */ +// abstract override suspend fun content(): QGTextChannel +// +// /** +// * 接收到消息的子频道的所属频道服务器。 +// * +// * @throws QQGuildApiException 请求失败,例如无权限 +// * @throws NoSuchElementException 没有找到结果 +// */ +// abstract override suspend fun source(): QGGuild +//} + + + +// TODO 私聊 Message event +// TODO 消息删除事件 diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGContentText.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGContentText.kt index 44d3515f..c7f0120a 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGContentText.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGContentText.kt @@ -30,7 +30,9 @@ import love.forte.simbot.qguild.message.ContentTextEncoder * 直接作用在 [Message.content][love.forte.simbot.qguild.model.Message.content] 上 * 不经转义处理的消息。 * - * 当收到消息类型时便会将 `content` 转化为此类而不是 [Text]。 + * 当收到消息事件时, + * 如果消息中不包含任何其他特殊信息(主要是提及信息,例如提及所有人、提及用户、提及频道等) + * 便会将 `content` 直接转化为此类而不是 [Text]。 * * 当发送消息使用 [QGContentText] 时消息内容不会被尝试转义为无[内嵌格式](https://bot.q.qq.com/wiki/develop/api/openapi/message/message_format.html)的消息文本,而是直接追加 [content] 中的内容。 * diff --git a/simbot-component-qq-guild-core/src/commonTest/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt b/simbot-component-qq-guild-core/src/commonTest/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt index b95917c5..9bcc0f6a 100644 --- a/simbot-component-qq-guild-core/src/commonTest/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt +++ b/simbot-component-qq-guild-core/src/commonTest/kotlin/love/forte/simbot/component/qgguild/test/FileConfigTest.kt @@ -1,9 +1,17 @@ package love.forte.simbot.component.qgguild.test +import kotlinx.serialization.json.Json +import kotlinx.serialization.modules.plus +import love.forte.simbot.bot.SerializableBotConfiguration +import love.forte.simbot.component.qguild.QQGuildComponent import love.forte.simbot.component.qguild.bot.config.IntentsConfig +import love.forte.simbot.component.qguild.bot.config.QGBotFileConfiguration +import love.forte.simbot.component.qguild.bot.config.ShardConfig import love.forte.simbot.qguild.event.EventIntents import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertNotNull /** @@ -32,4 +40,33 @@ class FileConfigTest { ) } + @Test + fun botFileConfigurationTest() { + val json = Json { + isLenient = true + serializersModule += QQGuildComponent.messageSerializersModule + } + + val jsonStr = """ + { + "component": "simbot.qqguild", + "ticket": { + "appId": "appId-value", + "secret": "secret-value", + "token": "token-value" + }, + "config": { + "shard": { + "type": "full" + } + } + } + """.trimIndent() + + val decoded = json.decodeFromString(SerializableBotConfiguration.serializer(), jsonStr) + assertIs(decoded) + assertNotNull(decoded.config) + assertIs(decoded.config!!.shardConfig) + } + } From cc30f3dbebc65896e2f974af91988cb137514c67 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Tue, 23 Jan 2024 21:09:45 +0800 Subject: [PATCH 31/71] v4.0.0-dev6 --- .changelog/v4.0.0-dev6.md | 11 +++++++++ .run/runConfigurations/allTests.run.xml | 24 +++++++++++++++++++ Writerside/v.list | 2 +- .../src/commonMain/kotlin/FooComponent.kt | 2 +- 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 .changelog/v4.0.0-dev6.md create mode 100644 .run/runConfigurations/allTests.run.xml diff --git a/.changelog/v4.0.0-dev6.md b/.changelog/v4.0.0-dev6.md new file mode 100644 index 00000000..74435d6d --- /dev/null +++ b/.changelog/v4.0.0-dev6.md @@ -0,0 +1,11 @@ +> 对应核心版本: [**v4.0.0-dev14**](https://github.com/simple-robot/simpler-robot/releases/tag/v4.0.0-dev14) + + +> [!warning] +> **目前版本处于 `dev` 阶段,代表此版本是一个开发预览版,可能不稳定、可能随时发生更改、且不保证可用性。** + + +我们欢迎并期望着您的的[反馈](https://github.com/simple-robot/simbot-component-qq-guild/issues)或[协助](https://github.com/simple-robot/simbot-component-qq-guild/pulls), +感谢您的贡献与支持! + +也欢迎您为我们献上一颗 `star`,这是对我们最大的鼓励与认可! diff --git a/.run/runConfigurations/allTests.run.xml b/.run/runConfigurations/allTests.run.xml new file mode 100644 index 00000000..7a7b84bd --- /dev/null +++ b/.run/runConfigurations/allTests.run.xml @@ -0,0 +1,24 @@ + + + + + + + true + true + false + false + + + \ No newline at end of file diff --git a/Writerside/v.list b/Writerside/v.list index 093ff7cc..370db3ed 100644 --- a/Writerside/v.list +++ b/Writerside/v.list @@ -3,6 +3,6 @@ - + diff --git a/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt b/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt index 539507aa..ed917358 100644 --- a/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt +++ b/tests/plugin-test/src/commonMain/kotlin/FooComponent.kt @@ -63,7 +63,7 @@ class FooComponent : Component { } class FooComponentFactoryProvider : ComponentFactoryProvider { - override fun loadConfigurers(): Sequence>? { + override fun loadConfigures(): Sequence>? { return null } From c0ac5f845782d480982a8d993416210c7f883d33 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Tue, 23 Jan 2024 21:36:33 +0800 Subject: [PATCH 32/71] Home --- Writerside/topics/Home.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/Writerside/topics/Home.md b/Writerside/topics/Home.md index d11b8824..a28c575f 100644 --- a/Writerside/topics/Home.md +++ b/Writerside/topics/Home.md @@ -1,26 +1,29 @@ # 欢迎! 这里是 -[**Simple Robot v4**](https://github.com/simple-robot/simpler-robot/tree/v4-dev) (下文简称 simbot) +[**Simple Robot v4**](https://github.com/simple-robot/simpler-robot/tree/v4-dev) 的 [QQ频道组件](https://github.com/simple-robot/simbot-component-qq-guild/) -的应用手册! +应用手册! ## 概述 **QQ频道组件** 是针对 [**QQ频道机器人**](https://bot.q.qq.com/wiki/develop/api/) -各方面的 simbot 组件库实现, -包括对 `API` 内容的实现、事件相关的实现以及BOT对于事件的监听与交互等。 +各方面的 Simple Robot 组件库实现, +包括对 `API`、事件的封装、bot 订阅与处理事件的实现以及作为 Simple Robot 的组件库的实现。 - 基于 [`Kotlin`](https://kotlinlang.org/) 提供 [KMP 多平台](https://kotlinlang.org/docs/multiplatform.html) 特性,提供 Java 友好的API。 - 基于 [`Kotlin coroutines`](https://github.com/Kotlin/kotlinx.coroutines) 与 [`Ktor`](https://ktor.io/) 提供轻量高效的API。 - +### 了解 Simple Robot -[**Simple Robot 应用手册**](https://simbot.forte.love) +- [Simple Robot 应用手册](https://simbot.forte.love) +- [Simple Robot 组织库](https://github.com/simple-robot) - +### API文档 + +- [API文档引导站](https://docs.simbot.forte.love) ## 反馈与协助! @@ -32,7 +35,8 @@ ## 法欧莉 -如果你想看一看通过 **QQ频道组件** 的具体作品,可以前往QQ频道添加亲爱的 [法欧莉斯卡雷特](https://qun.qq.com/qunpro/robot/share?robot_appid=101986850) 来体验~ +如果你想看一看通过 **QQ频道组件** 实现的应用案例, +可以前往QQ频道添加亲爱的 [法欧莉斯卡雷特](https://qun.qq.com/qunpro/robot/share?robot_appid=101986850) 来体验~ ## License From 71d54c868c72ebf6dcd256bbafee8eb754e0516f Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Wed, 24 Jan 2024 04:35:28 +0800 Subject: [PATCH 33/71] =?UTF-8?q?=E6=96=87=E6=A1=A3=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Writerside/d.tree | 28 ++++++++++++++-------------- Writerside/topics/api.md | 6 +++++- Writerside/topics/event.md | 2 +- Writerside/topics/use-api.md | 8 +++++--- Writerside/topics/use-spring-boot.md | 4 ++-- Writerside/topics/use-stdlib.md | 9 +++++++++ 6 files changed, 36 insertions(+), 21 deletions(-) diff --git a/Writerside/d.tree b/Writerside/d.tree index b3f9a51c..163705b0 100644 --- a/Writerside/d.tree +++ b/Writerside/d.tree @@ -8,20 +8,6 @@ - - - - - - - - - - - - - - @@ -39,4 +25,18 @@ + + + + + + + + + + + + + +
diff --git a/Writerside/topics/api.md b/Writerside/topics/api.md index ee2089a8..10e49c17 100644 --- a/Writerside/topics/api.md +++ b/Writerside/topics/api.md @@ -2,4 +2,8 @@ 提供一些针对QQ频道提供的一些特殊消息、具体功能概念实现的描述。 -比如,[Ark消息](api_ark.md) 即为一种特殊类型的消息,而 [角色 Role](api_role.md) 便是一种功能概念实现。 +比如, +
+即为一种特殊类型的消息,而 + +便是一种功能概念实现。 diff --git a/Writerside/topics/event.md b/Writerside/topics/event.md index e78c437c..2706dbd5 100644 --- a/Writerside/topics/event.md +++ b/Writerside/topics/event.md @@ -56,7 +56,7 @@ API 模块所有的事件封装类型都在包 `love.forte.simbot.qguild.event` API 模块事件封装可以使用在 **标准库模块 (stdlib)** 中,使用 `Bot` 类型对他们进行监听与处理。 -## simbot Event 实现 +## Simbot 标准库 Event 实现 使用核心库,可以在 simbot4 的 `Application` 或 Spring Boot 中使用这些事件类型实现。 diff --git a/Writerside/topics/use-api.md b/Writerside/topics/use-api.md index 5f5de68e..42ac3e08 100644 --- a/Writerside/topics/use-api.md +++ b/Writerside/topics/use-api.md @@ -12,8 +12,10 @@ switcher-label: Java API 风格 -API 模块 是一个“基础”模块,它仅提供针对 QQ频道 API 的封装, -没有 Bot、事件处理等功能。 +API 模块 是一个“基础”的模块,它仅提供针对 QQ频道 API 的封装, +没有 Bot、事件处理等功能,是一种“底层库”。 + +API 模块无法直接作为 Simple Robot 组件使用。 @@ -93,7 +95,7 @@ love.forte.simbot.qguild.api.user.GetBotGuildListApi ``` -> 所有的API实现均在包路径 `love.forte.simbot.miyoushe.api` 中。 +> 所有的API实现均在包路径 `love.forte.simbot.qguild.api` 中。 API的应用大差不差,因此此处仅使用部分类型作为示例, 不会演示所有API。 diff --git a/Writerside/topics/use-spring-boot.md b/Writerside/topics/use-spring-boot.md index 1f12af21..c658db28 100644 --- a/Writerside/topics/use-spring-boot.md +++ b/Writerside/topics/use-spring-boot.md @@ -126,8 +126,8 @@ implementation 'love.forte.simbot.component:simbot-component-qq-guild-core-jvm:% 配置文件的内容可前往参考 [Bot配置文件](bot-config.md) 章节。 -> 这个扫描目录是可配置的。 -> 这是属于 simbot4 Spring Boot starter 的配置,可参考 +> 这个扫描目录是**可配置的**。 +> 这属于 simbot4 Spring Boot starter 的配置,可参考 > [simbot手册: 使用 Spring Boot 3](https://simbot.forte.love/start-use-spring-boot-3.html)。 ## 使用 diff --git a/Writerside/topics/use-stdlib.md b/Writerside/topics/use-stdlib.md index 46c32601..f483cfc5 100644 --- a/Writerside/topics/use-stdlib.md +++ b/Writerside/topics/use-stdlib.md @@ -8,6 +8,15 @@ switcher-label: Java API 风格

本章节介绍如何使用 标准库(stdlib模块) 来构建 Bot 实例、订阅并处理事件。

+ + +标准库模块 是一个“较为基础”的模块,它在 API 模块之上, +仅提供**最基础**的 Bot 实现和事件处理。是一种**轻量级实现库**。 + +标准库模块无法直接作为 Simple Robot 组件使用。 + + + ## 安装 From d44f970186e1bb293c8e9d5517d589748ff3ab05 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Wed, 24 Jan 2024 05:04:39 +0800 Subject: [PATCH 34/71] =?UTF-8?q?=E6=96=87=E6=A1=A3=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Writerside/topics/use-api.md | 19 +++++++++++++++++++ Writerside/topics/use-stdlib.md | 24 ++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/Writerside/topics/use-api.md b/Writerside/topics/use-api.md index 42ac3e08..ba205518 100644 --- a/Writerside/topics/use-api.md +++ b/Writerside/topics/use-api.md @@ -19,6 +19,25 @@ API 模块无法直接作为 Simple Robot 组件使用。 + + +当你不确定自己的应用场景是否应该选择 **直接使用** API 模块时, +这里为你提供了一些参考: + + + +- 你的应用只需要一个 API 实现库,你希望使用更**原始的**风格调用API。 +- 你的应用**不需要**、或希望**自行处理**对事件的订阅与解析。(包括一切工作,例如建立网络连接) + + + + +- 你希望库实现事件订阅能力,可以替你处理网络连接、事件调度等。 +- 你希望有一个 Bot 实现,它可以管理网络连接的生命周期、可以注册事件处理逻辑、替你接收事件,并进行处理。 + + + + ## 安装 diff --git a/Writerside/topics/use-stdlib.md b/Writerside/topics/use-stdlib.md index f483cfc5..55b038e4 100644 --- a/Writerside/topics/use-stdlib.md +++ b/Writerside/topics/use-stdlib.md @@ -17,6 +17,30 @@ switcher-label: Java API 风格 + + +当你不确定自己的应用场景是否应该选择 **直接使用** 标准库模块时, +这里为你提供了一些参考: + + + +- 你希望有一个 Bot 实现,它可以支持订阅事件、管理网络连接的生命周期、可以注册事件处理逻辑、替你接收事件,并进行处理/调度。 +- 你不需要太多额外的功能,例如处理事件时,如果你想要发消息,那么**自行构建** API 模块中提供的实现。 +- 你不需要多组件协同。 +- 你不需要 Simple Robot 标准库提供的任何功能。 + + + + + +- 你希望使用 Simple Robot 标准库提供的功能。 +- 你希望使用一个有更多**高级功能**封装的库,而不是一个仅有基础功能的库。例如处理事件时,你只需要组装好消息文本或封装好的**消息元素对象**,并简单的调用 `send` 即可发送,不需要自行构建 API、自行发送请求。 +- 你希望能够支持多组件协同。 +- 你希望使用支持 Spring Boot 的库。 + + + + ## 安装 From a2e7afac782c6cc4b50fc746f2c50643d773afa9 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Thu, 25 Jan 2024 14:56:32 +0800 Subject: [PATCH 35/71] idea markdown config --- Writerside/.idea/markdown.xml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 Writerside/.idea/markdown.xml diff --git a/Writerside/.idea/markdown.xml b/Writerside/.idea/markdown.xml new file mode 100644 index 00000000..f6d2542c --- /dev/null +++ b/Writerside/.idea/markdown.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file From 1d31418218a351834414ae53ca97c22511d8767f Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Thu, 25 Jan 2024 15:05:11 +0800 Subject: [PATCH 36/71] test config --- .github/workflows/test-branch.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-branch.yml b/.github/workflows/test-branch.yml index 585cb689..66a4532b 100644 --- a/.github/workflows/test-branch.yml +++ b/.github/workflows/test-branch.yml @@ -32,8 +32,8 @@ jobs: - uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: 11 - cache: 'gradle' + java-version: 21 +# cache: 'gradle' - name: Run All Tests uses: gradle/gradle-build-action@v2 From a88baf5ecc669cc10d2262e8a7d9f630cf2522d7 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Thu, 25 Jan 2024 15:43:48 +0800 Subject: [PATCH 37/71] CI Config --- .github/workflows/test-branch.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/test-branch.yml b/.github/workflows/test-branch.yml index 66a4532b..0e798f52 100644 --- a/.github/workflows/test-branch.yml +++ b/.github/workflows/test-branch.yml @@ -27,7 +27,6 @@ jobs: os: [ macos-latest, windows-latest, ubuntu-latest ] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - uses: actions/setup-java@v3 with: From 012237f1294d569b0088e290f98a4161a5aa8446 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Thu, 25 Jan 2024 16:01:20 +0800 Subject: [PATCH 38/71] settings and yarn.lock --- kotlin-js-store/yarn.lock | 903 +------------------------------------- settings.gradle.kts | 10 +- 2 files changed, 22 insertions(+), 891 deletions(-) diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index 0e3eeaf5..fd7abab4 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -75,46 +75,11 @@ resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== -"@leichtgewicht/ip-codec@^2.0.1": - version "2.0.4" - resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" - integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== - "@socket.io/component-emitter@~3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== -"@types/body-parser@*": - version "1.19.5" - resolved "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz#04ce9a3b677dc8bd681a17da1ab9835dc9d3ede4" - integrity sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg== - dependencies: - "@types/connect" "*" - "@types/node" "*" - -"@types/bonjour@^3.5.9": - version "3.5.13" - resolved "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz#adf90ce1a105e81dd1f9c61fdc5afda1bfb92956" - integrity sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ== - dependencies: - "@types/node" "*" - -"@types/connect-history-api-fallback@^1.3.5": - version "1.5.4" - resolved "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz#7de71645a103056b48ac3ce07b3520b819c1d5b3" - integrity sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw== - dependencies: - "@types/express-serve-static-core" "*" - "@types/node" "*" - -"@types/connect@*": - version "3.4.38" - resolved "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz#5ba7f3bc4fbbdeaff8dded952e5ff2cc53f8d858" - integrity sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug== - dependencies: - "@types/node" "*" - "@types/cookie@^0.4.1": version "0.4.1" resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" @@ -153,60 +118,11 @@ resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== -"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.33": - version "4.17.41" - resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.41.tgz#5077defa630c2e8d28aa9ffc2c01c157c305bef6" - integrity sha512-OaJ7XLaelTgrvlZD8/aa0vvvxZdUmlCn6MtWeB7TkiKW70BQLc9XEPpDLPdbo52ZhXUCrznlWdCHWxJWtdyajA== - dependencies: - "@types/node" "*" - "@types/qs" "*" - "@types/range-parser" "*" - "@types/send" "*" - -"@types/express@*", "@types/express@^4.17.13": - version "4.17.21" - resolved "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz#c26d4a151e60efe0084b23dc3369ebc631ed192d" - integrity sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ== - dependencies: - "@types/body-parser" "*" - "@types/express-serve-static-core" "^4.17.33" - "@types/qs" "*" - "@types/serve-static" "*" - -"@types/http-errors@*": - version "2.0.4" - resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz#7eb47726c391b7345a6ec35ad7f4de469cf5ba4f" - integrity sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA== - -"@types/http-proxy@^1.17.8": - version "1.17.14" - resolved "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz#57f8ccaa1c1c3780644f8a94f9c6b5000b5e2eec" - integrity sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w== - dependencies: - "@types/node" "*" - -"@types/json-schema@*", "@types/json-schema@^7.0.8", "@types/json-schema@^7.0.9": +"@types/json-schema@*", "@types/json-schema@^7.0.8": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== -"@types/mime@*": - version "3.0.4" - resolved "https://registry.npmjs.org/@types/mime/-/mime-3.0.4.tgz#2198ac274de6017b44d941e00261d5bc6a0e0a45" - integrity sha512-iJt33IQnVRkqeqC7PzBHPTC6fDlRNRW8vjrgqtScAhrmMwe8c4Eo7+fUGTa+XdWrpEgpyKWMYmi2dIwMAYRzPw== - -"@types/mime@^1": - version "1.3.5" - resolved "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz#1ef302e01cf7d2b5a0fa526790c9123bf1d06690" - integrity sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w== - -"@types/node-forge@^1.3.0": - version "1.3.11" - resolved "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz#0972ea538ddb0f4d9c2fa0ec5db5724773a604da" - integrity sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ== - dependencies: - "@types/node" "*" - "@types/node@*": version "20.1.2" resolved "https://registry.yarnpkg.com/@types/node/-/node-20.1.2.tgz#8fd63447e3f99aba6c3168fd2ec4580d5b97886f" @@ -219,59 +135,6 @@ dependencies: undici-types "~5.26.4" -"@types/qs@*": - version "6.9.11" - resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz#208d8a30bc507bd82e03ada29e4732ea46a6bbda" - integrity sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ== - -"@types/range-parser@*": - version "1.2.7" - resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" - integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== - -"@types/retry@0.12.0": - version "0.12.0" - resolved "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" - integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== - -"@types/send@*": - version "0.17.4" - resolved "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz#6619cd24e7270793702e4e6a4b958a9010cfc57a" - integrity sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA== - dependencies: - "@types/mime" "^1" - "@types/node" "*" - -"@types/serve-index@^1.9.1": - version "1.9.4" - resolved "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz#e6ae13d5053cb06ed36392110b4f9a49ac4ec898" - integrity sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug== - dependencies: - "@types/express" "*" - -"@types/serve-static@*", "@types/serve-static@^1.13.10": - version "1.15.5" - resolved "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz#15e67500ec40789a1e8c9defc2d32a896f05b033" - integrity sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ== - dependencies: - "@types/http-errors" "*" - "@types/mime" "*" - "@types/node" "*" - -"@types/sockjs@^0.3.33": - version "0.3.36" - resolved "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz#ce322cf07bcc119d4cbf7f88954f3a3bd0f67535" - integrity sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q== - dependencies: - "@types/node" "*" - -"@types/ws@^8.5.1": - version "8.5.10" - resolved "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz#4acfb517970853fa6574a3a6886791d04a396787" - integrity sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A== - dependencies: - "@types/node" "*" - "@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": version "1.11.6" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" @@ -430,7 +293,7 @@ abort-controller@3.0.0: dependencies: event-target-shim "^5.0.0" -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: +accepts@~1.3.4: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -448,25 +311,11 @@ acorn@^8.7.1, acorn@^8.8.2: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== -ajv-formats@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" - integrity sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA== - dependencies: - ajv "^8.0.0" - ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv-keywords@^5.1.0: - version "5.1.0" - resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz#69d4d385a4733cdbeab44964a1170a88f87f0e16" - integrity sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw== - dependencies: - fast-deep-equal "^3.1.3" - ajv@^6.12.5: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -477,26 +326,11 @@ ajv@^6.12.5: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.9.0: - version "8.12.0" - resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" - integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - ansi-colors@4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-html-community@^0.0.8: - version "0.0.8" - resolved "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz#69fbc4d6ccbe383f9736934ae34c3f8290f1bf41" - integrity sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw== - ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" @@ -522,11 +356,6 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg== - balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -537,34 +366,11 @@ base64id@2.0.0, base64id@~2.0.0: resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== -batch@0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" - integrity sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw== - binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== -body-parser@1.20.1: - version "1.20.1" - resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" - integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw== - dependencies: - bytes "3.1.2" - content-type "~1.0.4" - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - http-errors "2.0.0" - iconv-lite "0.4.24" - on-finished "2.4.1" - qs "6.11.0" - raw-body "2.5.1" - type-is "~1.6.18" - unpipe "1.0.0" - body-parser@^1.19.0: version "1.20.2" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" @@ -583,14 +389,6 @@ body-parser@^1.19.0: type-is "~1.6.18" unpipe "1.0.0" -bonjour-service@^1.0.11: - version "1.2.1" - resolved "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz#eb41b3085183df3321da1264719fbada12478d02" - integrity sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw== - dependencies: - fast-deep-equal "^3.1.3" - multicast-dns "^7.2.5" - brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -633,11 +431,6 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw== - bytes@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" @@ -670,7 +463,7 @@ chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" -chokidar@3.5.3, chokidar@^3.5.1, chokidar@^3.5.3: +chokidar@3.5.3, chokidar@^3.5.1: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -720,7 +513,7 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colorette@^2.0.10, colorette@^2.0.14: +colorette@^2.0.14: version "2.0.20" resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== @@ -735,36 +528,11 @@ commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -compressible@~2.0.16: - version "2.0.18" - resolved "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.npmjs.org/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== - dependencies: - accepts "~1.3.5" - bytes "3.0.0" - compressible "~2.0.16" - debug "2.6.9" - on-headers "~1.0.2" - safe-buffer "5.1.2" - vary "~1.1.2" - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== -connect-history-api-fallback@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz#647264845251a0daf25b97ce87834cace0f5f1c8" - integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA== - connect@^3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" @@ -775,38 +543,16 @@ connect@^3.7.0: parseurl "~1.3.3" utils-merge "1.0.1" -content-disposition@0.5.4: - version "0.5.4" - resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" - integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== - dependencies: - safe-buffer "5.2.1" - -content-type@~1.0.4, content-type@~1.0.5: +content-type@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ== - -cookie@0.5.0: - version "0.5.0" - resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" - integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== - cookie@~0.4.1: version "0.4.2" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== -core-util-is@~1.0.0: - version "1.0.3" - resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85" - integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== - cors@~2.8.5: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" @@ -841,7 +587,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4.3.4, debug@^4.1.0, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: +debug@4.3.4, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== @@ -853,13 +599,6 @@ decamelize@^4.0.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== -default-gateway@^6.0.3: - version "6.0.3" - resolved "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz#819494c888053bdb743edbf343d6cdf7f2943a71" - integrity sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg== - dependencies: - execa "^5.0.0" - define-data-property@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" @@ -869,31 +608,16 @@ define-data-property@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" -define-lazy-prop@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" - integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== - depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== - destroy@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== -detect-node@^2.0.4: - version "2.1.0" - resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" - integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== - di@^0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" @@ -904,13 +628,6 @@ diff@5.0.0: resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== -dns-packet@^5.2.2: - version "5.6.1" - resolved "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz#ae888ad425a9d1478a0674256ab866de1012cf2f" - integrity sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw== - dependencies: - "@leichtgewicht/ip-codec" "^2.0.1" - dom-serialize@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" @@ -1025,11 +742,6 @@ estraverse@^5.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== - event-target-shim@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" @@ -1045,64 +757,12 @@ events@^3.2.0: resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== -execa@^5.0.0: - version "5.1.1" - resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - -express@^4.17.3: - version "4.18.2" - resolved "https://registry.npmjs.org/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59" - integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ== - dependencies: - accepts "~1.3.8" - array-flatten "1.1.1" - body-parser "1.20.1" - content-disposition "0.5.4" - content-type "~1.0.4" - cookie "0.5.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "2.0.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.2.0" - fresh "0.5.2" - http-errors "2.0.0" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "2.4.1" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.7" - qs "6.11.0" - range-parser "~1.2.1" - safe-buffer "5.2.1" - send "0.18.0" - serve-static "1.15.0" - setprototypeof "1.2.0" - statuses "2.0.1" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - extend@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: +fast-deep-equal@^3.1.1: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== @@ -1117,13 +777,6 @@ fastest-levenshtein@^1.0.12: resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== -faye-websocket@^0.11.3: - version "0.11.4" - resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz#7f0d9275cfdd86a1c963dc8b65fcc451edcbb1da" - integrity sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g== - dependencies: - websocket-driver ">=0.5.1" - fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -1144,19 +797,6 @@ finalhandler@1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -finalhandler@1.2.0: - version "1.2.0" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz#7d23fe5731b207b4640e4fcd00aec1f9207a7b32" - integrity sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "2.4.1" - parseurl "~1.3.3" - statuses "2.0.1" - unpipe "~1.0.0" - find-up@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" @@ -1193,16 +833,6 @@ format-util@^1.0.5: resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== -forwarded@0.2.0: - version "0.2.0" - resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" - integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== - fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -1212,11 +842,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-monkey@^1.0.4: - version "1.0.5" - resolved "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz#fe450175f0db0d7ea758102e1d84096acb925788" - integrity sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew== - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1247,11 +872,6 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" -get-stream@^6.0.0: - version "6.0.1" - resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -1300,11 +920,6 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10, resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== -handle-thing@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" - integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== - has-flag@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" @@ -1339,26 +954,6 @@ he@1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== -hpack.js@^2.1.6: - version "2.1.6" - resolved "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" - integrity sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ== - dependencies: - inherits "^2.0.1" - obuf "^1.0.0" - readable-stream "^2.0.1" - wbuf "^1.1.0" - -html-entities@^2.3.2: - version "2.4.0" - resolved "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz#edd0cee70402584c8c76cc2c0556db09d1f45061" - integrity sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ== - -http-deceiver@^1.2.7: - version "1.2.7" - resolved "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" - integrity sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw== - http-errors@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" @@ -1370,32 +965,6 @@ http-errors@2.0.0: statuses "2.0.1" toidentifier "1.0.1" -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -http-parser-js@>=0.5.1: - version "0.5.8" - resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz#af23090d9ac4e24573de6f6aecc9d84a48bf20e3" - integrity sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q== - -http-proxy-middleware@^2.0.3: - version "2.0.6" - resolved "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz#e1a4dd6979572c7ab5a4e4b55095d1f32a74963f" - integrity sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw== - dependencies: - "@types/http-proxy" "^1.17.8" - http-proxy "^1.18.1" - is-glob "^4.0.1" - is-plain-obj "^3.0.0" - micromatch "^4.0.2" - http-proxy@^1.18.1: version "1.18.1" resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" @@ -1405,11 +974,6 @@ http-proxy@^1.18.1: follow-redirects "^1.0.0" requires-port "^1.0.0" -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== - iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -1440,31 +1004,16 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: +inherits@2, inherits@2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== - interpret@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== -ipaddr.js@1.9.1: - version "1.9.1" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -ipaddr.js@^2.0.1: - version "2.1.0" - resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f" - integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ== - is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" @@ -1479,11 +1028,6 @@ is-core-module@^2.13.0: dependencies: hasown "^2.0.0" -is-docker@^2.0.0, is-docker@^2.1.1: - version "2.2.1" - resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" - integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== - is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -1511,11 +1055,6 @@ is-plain-obj@^2.1.0: resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== -is-plain-obj@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz#af6f2ea14ac5a646183a5bbdb5baabbc156ad9d7" - integrity sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA== - is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" @@ -1523,28 +1062,11 @@ is-plain-object@^2.0.4: dependencies: isobject "^3.0.1" -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== -is-wsl@^2.2.0: - version "2.2.0" - resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== - isbinaryfile@^4.0.8: version "4.0.10" resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" @@ -1586,11 +1108,6 @@ json-schema-traverse@^0.4.1: resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== -json-schema-traverse@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2" - integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== - jsonfile@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" @@ -1663,14 +1180,6 @@ kind-of@^6.0.2: resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== -launch-editor@^2.6.0: - version "2.6.1" - resolved "https://registry.npmjs.org/launch-editor/-/launch-editor-2.6.1.tgz#f259c9ef95cbc9425620bbbd14b468fcdb4ffe3c" - integrity sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw== - dependencies: - picocolors "^1.0.0" - shell-quote "^1.8.1" - loader-runner@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" @@ -1719,68 +1228,28 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== -memfs@^3.4.3: - version "3.6.0" - resolved "https://registry.npmjs.org/memfs/-/memfs-3.6.0.tgz#d7a2110f86f79dd950a8b6df6d57bc984aa185f6" - integrity sha512-EGowvkkgbMcIChjMTMkESFDbZeSh8xZ7kNSF0hAiAN4Jh6jgHCRS0Ga/+C8y6Au+oqpezRHCfPsmJ2+DwAgiwQ== - dependencies: - fs-monkey "^1.0.4" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w== - merge-stream@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w== - -micromatch@^4.0.2: - version "4.0.5" - resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== - dependencies: - braces "^3.0.2" - picomatch "^2.3.1" - -mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": +mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" -mime@1.6.0: - version "1.6.0" - resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - mime@^2.5.2: version "2.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -minimalistic-assert@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - minimatch@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" @@ -1849,14 +1318,6 @@ ms@2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -multicast-dns@^7.2.5: - version "7.2.5" - resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" - integrity sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg== - dependencies: - dns-packet "^5.2.2" - thunky "^1.0.2" - nanoid@3.3.3: version "3.3.3" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" @@ -1879,11 +1340,6 @@ node-fetch@2.6.7: dependencies: whatwg-url "^5.0.0" -node-forge@^1: - version "1.3.1" - resolved "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" - integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== - node-releases@^2.0.14: version "2.0.14" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" @@ -1894,13 +1350,6 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - object-assign@^4: version "4.1.1" resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -1911,11 +1360,6 @@ object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== -obuf@^1.0.0, obuf@^1.1.2: - version "1.1.2" - resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" - integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== - on-finished@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" @@ -1930,11 +1374,6 @@ on-finished@~2.3.0: dependencies: ee-first "1.1.1" -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -1942,22 +1381,6 @@ once@^1.3.0: dependencies: wrappy "1" -onetime@^5.1.2: - version "5.1.2" - resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -open@^8.0.9: - version "8.4.2" - resolved "https://registry.npmjs.org/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" - integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== - dependencies: - define-lazy-prop "^2.0.0" - is-docker "^2.1.1" - is-wsl "^2.2.0" - p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -1986,20 +1409,12 @@ p-locate@^5.0.0: dependencies: p-limit "^3.0.2" -p-retry@^4.5.0: - version "4.6.2" - resolved "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" - integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== - dependencies: - "@types/retry" "0.12.0" - retry "^0.13.1" - p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== -parseurl@~1.3.2, parseurl@~1.3.3: +parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -2014,7 +1429,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^3.0.0, path-key@^3.1.0: +path-key@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -2024,17 +1439,12 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== - picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: +picomatch@^2.0.4, picomatch@^2.2.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -2046,19 +1456,6 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -proxy-addr@~2.0.7: - version "2.0.7" - resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025" - integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== - dependencies: - forwarded "0.2.0" - ipaddr.js "1.9.1" - punycode@^2.1.0: version "2.3.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" @@ -2083,21 +1480,11 @@ randombytes@^2.1.0: dependencies: safe-buffer "^5.1.0" -range-parser@^1.2.1, range-parser@~1.2.1: +range-parser@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== -raw-body@2.5.1: - version "2.5.1" - resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz#fe1b1628b181b700215e5fd42389f98b71392857" - integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig== - dependencies: - bytes "3.1.2" - http-errors "2.0.0" - iconv-lite "0.4.24" - unpipe "1.0.0" - raw-body@2.5.2: version "2.5.2" resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" @@ -2108,28 +1495,6 @@ raw-body@2.5.2: iconv-lite "0.4.24" unpipe "1.0.0" -readable-stream@^2.0.1: - version "2.3.8" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz#91125e8042bba1b9887f49345f6277027ce8be9b" - integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@^3.0.6: - version "3.6.2" - resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - readdirp@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" @@ -2149,11 +1514,6 @@ require-directory@^2.1.1: resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== -require-from-string@^2.0.2: - version "2.0.2" - resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" - integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== - requires-port@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" @@ -2180,11 +1540,6 @@ resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -retry@^0.13.1: - version "0.13.1" - resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" - integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== - rfdc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" @@ -2197,12 +1552,7 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: +safe-buffer@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -2221,48 +1571,6 @@ schema-utils@^3.1.1, schema-utils@^3.1.2: ajv "^6.12.5" ajv-keywords "^3.5.2" -schema-utils@^4.0.0: - version "4.2.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz#70d7c93e153a273a805801882ebd3bff20d89c8b" - integrity sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw== - dependencies: - "@types/json-schema" "^7.0.9" - ajv "^8.9.0" - ajv-formats "^2.1.1" - ajv-keywords "^5.1.0" - -select-hose@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" - integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg== - -selfsigned@^2.1.1: - version "2.4.1" - resolved "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz#560d90565442a3ed35b674034cec4e95dceb4ae0" - integrity sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q== - dependencies: - "@types/node-forge" "^1.3.0" - node-forge "^1" - -send@0.18.0: - version "0.18.0" - resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz#670167cc654b05f5aa4a767f9113bb371bc706be" - integrity sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg== - dependencies: - debug "2.6.9" - depd "2.0.0" - destroy "1.2.0" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "2.0.0" - mime "1.6.0" - ms "2.1.3" - on-finished "2.4.1" - range-parser "~1.2.1" - statuses "2.0.1" - serialize-javascript@6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" @@ -2277,29 +1585,6 @@ serialize-javascript@^6.0.1: dependencies: randombytes "^2.1.0" -serve-index@^1.9.1: - version "1.9.1" - resolved "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" - integrity sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw== - dependencies: - accepts "~1.3.4" - batch "0.6.1" - debug "2.6.9" - escape-html "~1.0.3" - http-errors "~1.6.2" - mime-types "~2.1.17" - parseurl "~1.3.2" - -serve-static@1.15.0: - version "1.15.0" - resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz#faaef08cffe0a1a62f60cad0c4e513cff0ac9540" - integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.18.0" - set-function-length@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" @@ -2310,11 +1595,6 @@ set-function-length@^1.1.1: gopd "^1.0.1" has-property-descriptors "^1.0.0" -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - setprototypeof@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" @@ -2339,11 +1619,6 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -shell-quote@^1.8.1: - version "1.8.1" - resolved "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz#6dbf4db75515ad5bac63b4f1894c3a154c766680" - integrity sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA== - side-channel@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" @@ -2353,11 +1628,6 @@ side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" -signal-exit@^3.0.3: - version "3.0.7" - resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - socket.io-adapter@~2.5.2: version "2.5.2" resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz#5de9477c9182fdc171cd8c8364b9a8894ec75d12" @@ -2386,15 +1656,6 @@ socket.io@^4.4.1: socket.io-adapter "~2.5.2" socket.io-parser "~4.2.4" -sockjs@^0.3.24: - version "0.3.24" - resolved "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz#c9bc8995f33a111bea0395ec30aa3206bdb5ccce" - integrity sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ== - dependencies: - faye-websocket "^0.11.3" - uuid "^8.3.2" - websocket-driver "^0.7.4" - source-map-js@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" @@ -2422,35 +1683,12 @@ source-map@^0.6.0, source-map@^0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -spdy-transport@^3.0.0: - version "3.0.0" - resolved "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" - integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== - dependencies: - debug "^4.1.0" - detect-node "^2.0.4" - hpack.js "^2.1.6" - obuf "^1.1.2" - readable-stream "^3.0.6" - wbuf "^1.7.3" - -spdy@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" - integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== - dependencies: - debug "^4.1.0" - handle-thing "^2.0.0" - http-deceiver "^1.2.7" - select-hose "^2.0.0" - spdy-transport "^3.0.0" - statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== -"statuses@>= 1.4.0 < 2", statuses@~1.5.0: +statuses@~1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== @@ -2473,20 +1711,6 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -2494,11 +1718,6 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - strip-json-comments@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -2549,11 +1768,6 @@ terser@^5.26.0: commander "^2.20.0" source-map-support "~0.5.20" -thunky@^1.0.2: - version "1.1.0" - resolved "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" - integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== - tmp@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" @@ -2626,22 +1840,12 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - utils-merge@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== -uuid@^8.3.2: - version "8.3.2" - resolved "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" - integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== - -vary@^1, vary@~1.1.2: +vary@^1: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== @@ -2659,13 +1863,6 @@ watchpack@^2.4.0: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" -wbuf@^1.1.0, wbuf@^1.7.3: - version "1.7.3" - resolved "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" - integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== - dependencies: - minimalistic-assert "^1.0.0" - webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -2690,53 +1887,6 @@ webpack-cli@5.1.0: rechoir "^0.8.0" webpack-merge "^5.7.3" -webpack-dev-middleware@^5.3.1: - version "5.3.3" - resolved "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz#efae67c2793908e7311f1d9b06f2a08dcc97e51f" - integrity sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA== - dependencies: - colorette "^2.0.10" - memfs "^3.4.3" - mime-types "^2.1.31" - range-parser "^1.2.1" - schema-utils "^4.0.0" - -webpack-dev-server@4.15.0: - version "4.15.0" - resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.0.tgz#87ba9006eca53c551607ea0d663f4ae88be7af21" - integrity sha512-HmNB5QeSl1KpulTBQ8UT4FPrByYyaLxpJoQ0+s7EvUrMc16m0ZS1sgb1XGqzmgCPk0c9y+aaXxn11tbLzuM7NQ== - dependencies: - "@types/bonjour" "^3.5.9" - "@types/connect-history-api-fallback" "^1.3.5" - "@types/express" "^4.17.13" - "@types/serve-index" "^1.9.1" - "@types/serve-static" "^1.13.10" - "@types/sockjs" "^0.3.33" - "@types/ws" "^8.5.1" - ansi-html-community "^0.0.8" - bonjour-service "^1.0.11" - chokidar "^3.5.3" - colorette "^2.0.10" - compression "^1.7.4" - connect-history-api-fallback "^2.0.0" - default-gateway "^6.0.3" - express "^4.17.3" - graceful-fs "^4.2.6" - html-entities "^2.3.2" - http-proxy-middleware "^2.0.3" - ipaddr.js "^2.0.1" - launch-editor "^2.6.0" - open "^8.0.9" - p-retry "^4.5.0" - rimraf "^3.0.2" - schema-utils "^4.0.0" - selfsigned "^2.1.1" - serve-index "^1.9.1" - sockjs "^0.3.24" - spdy "^4.0.2" - webpack-dev-middleware "^5.3.1" - ws "^8.13.0" - webpack-merge@^4.1.5: version "4.2.2" resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" @@ -2788,20 +1938,6 @@ webpack@5.82.0: watchpack "^2.4.0" webpack-sources "^3.2.3" -websocket-driver@>=0.5.1, websocket-driver@^0.7.4: - version "0.7.4" - resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" - integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== - dependencies: - http-parser-js ">=0.5.1" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" - -websocket-extensions@>=0.1.1: - version "0.1.4" - resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" - integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== - whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -2853,11 +1989,6 @@ ws@8.5.0: resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== -ws@^8.13.0: - version "8.16.0" - resolved "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz#d1cd774f36fbc07165066a60e40323eab6446fd4" - integrity sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ== - ws@~8.11.0: version "8.11.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" diff --git a/settings.gradle.kts b/settings.gradle.kts index b04cecfe..50389de1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -26,9 +26,9 @@ include(":simbot-component-qq-guild-core") //include(":simbot-component-qq-guild-benchmark") // tests -if (!System.getenv("IS_CI").toBoolean()) { - include(":tests:application-test") - include(":tests:spring-boot-test") - include(":tests:plugin-test") -} +//if (!System.getenv("IS_CI").toBoolean()) { +// include(":tests:application-test") +// include(":tests:spring-boot-test") +// include(":tests:plugin-test") +//} From 7b6cdcbe54a8ab678e20c149c0ae6424bf4910ed Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Fri, 26 Jan 2024 20:31:15 +0800 Subject: [PATCH 39/71] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E5=90=AF=E7=94=A8K2?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E5=99=A8=EF=BC=9B=E5=A2=9E=E5=8A=A0=E9=83=A8?= =?UTF-8?q?=E5=88=86=E6=96=87=E6=A1=A3=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Writerside/d.tree | 4 +- Writerside/topics/api-list.md | 264 ++++++++++++++++++ Writerside/topics/api.md | 9 - buildSrc/build.gradle.kts | 8 +- buildSrc/src/main/kotlin/K2Config.kt | 29 ++ buildSrc/src/main/kotlin/P.kt | 2 +- ...tcg-suspend-transform-configure.gradle.kts | 2 + builder-generator/build.gradle.kts | 46 --- .../bgenor/BuilderGeneratorSymbolProcessor.kt | 166 ----------- ...BuilderGeneratorSymbolProcessorProvider.kt | 31 -- .../src/main/kotlin/bgenor/annotations.kt | 18 -- ...ols.ksp.processing.SymbolProcessorProvider | 13 - gradle/libs.versions.toml | 8 +- .../build.gradle.kts | 1 + .../build.gradle.kts | 2 + .../build.gradle.kts | 1 + 16 files changed, 312 insertions(+), 292 deletions(-) create mode 100644 Writerside/topics/api-list.md delete mode 100644 Writerside/topics/api.md create mode 100644 buildSrc/src/main/kotlin/K2Config.kt delete mode 100644 builder-generator/build.gradle.kts delete mode 100644 builder-generator/src/main/kotlin/bgenor/BuilderGeneratorSymbolProcessor.kt delete mode 100644 builder-generator/src/main/kotlin/bgenor/BuilderGeneratorSymbolProcessorProvider.kt delete mode 100644 builder-generator/src/main/kotlin/bgenor/annotations.kt delete mode 100644 builder-generator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider diff --git a/Writerside/d.tree b/Writerside/d.tree index 163705b0..92be3181 100644 --- a/Writerside/d.tree +++ b/Writerside/d.tree @@ -16,8 +16,8 @@ - - + + diff --git a/Writerside/topics/api-list.md b/Writerside/topics/api-list.md new file mode 100644 index 00000000..39a2000c --- /dev/null +++ b/Writerside/topics/api-list.md @@ -0,0 +1,264 @@ +# API列表 + +此处会列举 `API 模块` 中、`love.forte.simbot.qguild.api` 包下定义的所有 `QQGuildApi` 实现。 + +> 对于一个具体的API的详细说明,我们建议你前往 [API 文档](https://docs.simbot.forte.love/) 或源码注释查阅, +> 因为那是最贴合真实情况且最全面的。 + + + + +> `love.forte.simbot.qguild.api.announces.CreateAnnouncesApi` + + + + +> `love.forte.simbot.qguild.api.announces.DeleteAnnouncesApi` + + + + +> `love.forte.simbot.qguild.api.apipermission.DemandApiPermissionApi` + + + + +> `love.forte.simbot.qguild.api.apipermission.GetApiPermissionListApi` + + + + +> `love.forte.simbot.qguild.api.channel.CreateChannelApi` + + + + +> `love.forte.simbot.qguild.api.channel.DeleteChannelApi` + + + + +> `love.forte.simbot.qguild.api.channel.GetChannelApi` + + + + +> `love.forte.simbot.qguild.api.channel.GetChannelOnlineNumsApi` + + + + +> `love.forte.simbot.qguild.api.channel.GetGuildChannelListApi` + + + + +> `love.forte.simbot.qguild.api.channel.ModifyChannelApi` + + + + +> `love.forte.simbot.qguild.api.channel.permissions.GetChannelMemberPermissionsApi` + + + + +> `love.forte.simbot.qguild.api.channel.permissions.GetChannelRolePermissionsApi` + + + + +> `love.forte.simbot.qguild.api.channel.permissions.ModifyChannelMemberPermissionsApi` + + + + +> `love.forte.simbot.qguild.api.channel.permissions.ModifyChannelRolePermissionsApi` + + + + +> `love.forte.simbot.qguild.api.channel.pins.AddPinsMessageApi` + + + + +> `love.forte.simbot.qguild.api.channel.pins.DeletePinsMessageApi` + + + + +> `love.forte.simbot.qguild.api.channel.pins.GetPinsMessageApi` + + + + +> `love.forte.simbot.qguild.api.channel.schedules.CreateScheduleApi` + + + + +> `love.forte.simbot.qguild.api.channel.schedules.DeleteScheduleApi` + + + + +> `love.forte.simbot.qguild.api.channel.schedules.GetScheduleApi` + + + + +> `love.forte.simbot.qguild.api.channel.schedules.GetScheduleListApi` + + + + +> `love.forte.simbot.qguild.api.channel.schedules.ModifyScheduleApi` + + + + +> `love.forte.simbot.qguild.api.forum.DeleteThreadApi` + + + + +> `love.forte.simbot.qguild.api.forum.GetThreadApi` + + + + +> `love.forte.simbot.qguild.api.forum.GetThreadListApi` + + + + +> `love.forte.simbot.qguild.api.forum.PublishThreadApi` + + + + +> `love.forte.simbot.qguild.api.GatewayApis$Normal` + + + + +> `love.forte.simbot.qguild.api.GatewayApis$Shared` + + + + +> `love.forte.simbot.qguild.api.guild.GetGuildApi` + + + + +> `love.forte.simbot.qguild.api.guild.mute.MuteAllApi` + + + + +> `love.forte.simbot.qguild.api.guild.mute.MuteMemberApi` + + + + +> `love.forte.simbot.qguild.api.guild.mute.MuteMultiMemberApi` + + + + +> `love.forte.simbot.qguild.api.member.DeleteMemberApi` + + + + +> `love.forte.simbot.qguild.api.member.GetGuildMemberListApi` + + + + +> `love.forte.simbot.qguild.api.member.GetGuildRoleMemberListApi` + + + + +> `love.forte.simbot.qguild.api.member.GetMemberApi` + + + + +> `love.forte.simbot.qguild.api.message.DeleteMessageApi` + + + + +> `love.forte.simbot.qguild.api.message.direct.CreateDmsApi` + + + + +> `love.forte.simbot.qguild.api.message.direct.DeleteDmsApi` + + + + +> `love.forte.simbot.qguild.api.message.direct.DmsSendApi` + + + + +> `love.forte.simbot.qguild.api.message.GetMessageApi` + + + + +> `love.forte.simbot.qguild.api.message.MessageSendApi` + + + + +> `love.forte.simbot.qguild.api.message.setting.GetMessageSettingApi` + + + + +> `love.forte.simbot.qguild.api.role.AddMemberRoleApi` + + + + +> `love.forte.simbot.qguild.api.role.CreateGuildRoleApi` + + + + +> `love.forte.simbot.qguild.api.role.DeleteGuildRoleApi` + + + + +> `love.forte.simbot.qguild.api.role.GetGuildRoleListApi` + + + + +> `love.forte.simbot.qguild.api.role.ModifyGuildRoleApi` + + + + +> `love.forte.simbot.qguild.api.role.RemoveMemberRoleApi` + + + + +> `love.forte.simbot.qguild.api.user.GetBotGuildListApi` + + + + +> `love.forte.simbot.qguild.api.user.GetBotInfoApi` + + + diff --git a/Writerside/topics/api.md b/Writerside/topics/api.md deleted file mode 100644 index 10e49c17..00000000 --- a/Writerside/topics/api.md +++ /dev/null @@ -1,9 +0,0 @@ -# 概述 - -提供一些针对QQ频道提供的一些特殊消息、具体功能概念实现的描述。 - -比如, -
-即为一种特殊类型的消息,而 - -便是一种功能概念实现。 diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index b675d32a..ef8d18e0 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -22,6 +22,7 @@ plugins { repositories { mavenCentral() gradlePluginPortal() + mavenLocal() } val kotlinVersion: String = libs.versions.kotlin.get() @@ -44,5 +45,8 @@ dependencies { implementation(libs.bundles.gradle.common) } - - +//tasks.withType { +// kotlinOptions { +// languageVersion = "2.0" +// } +//} diff --git a/buildSrc/src/main/kotlin/K2Config.kt b/buildSrc/src/main/kotlin/K2Config.kt new file mode 100644 index 00000000..31661a38 --- /dev/null +++ b/buildSrc/src/main/kotlin/K2Config.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +import org.gradle.api.Project +import org.gradle.kotlin.dsl.withType + + +fun Project.useK2(languageVersion: String = "2.0") { + tasks.withType { + kotlinOptions { + // useK2 + this.languageVersion = languageVersion + } + } +} diff --git a/buildSrc/src/main/kotlin/P.kt b/buildSrc/src/main/kotlin/P.kt index efdce511..d98a5e74 100644 --- a/buildSrc/src/main/kotlin/P.kt +++ b/buildSrc/src/main/kotlin/P.kt @@ -59,7 +59,7 @@ object P { override val homepage: String get() = HOMEPAGE - private val baseVersion = v(4, 0, 0) - v("dev6") + private val baseVersion = v(4, 0, 0) - v("dev7") val snapshotVersion = baseVersion - Version.SNAPSHOT override val version = if (isSnapshot()) snapshotVersion else baseVersion diff --git a/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts b/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts index ea7af7fe..f7ba0e0f 100644 --- a/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts +++ b/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts @@ -63,3 +63,5 @@ suspendTransform { // SuspendTransforms.suspendTransTransformerForJsPromise, // ) } + + diff --git a/builder-generator/build.gradle.kts b/builder-generator/build.gradle.kts deleted file mode 100644 index 6bb17202..00000000 --- a/builder-generator/build.gradle.kts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-tencent-guild. - * - * simbot-component-tencent-guild is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - * - * simbot-component-tencent-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-tencent-guild. If not, see . - */ - -plugins { - kotlin("jvm") -} - -repositories { - mavenCentral() -} - -//buildscript { -// dependencies { -// classpath(kotlin("gradle-plugin")) -// } -//} - -dependencies { - api("com.google.devtools.ksp:symbol-processing-api:1.8.10-1.0.9") - api("com.squareup:kotlinpoet:1.12.0") - api("com.squareup:kotlinpoet-ksp:1.12.0") - -} - -tasks.withType().configureEach { - kotlinOptions { - javaParameters = true - jvmTarget = "1.8" - freeCompilerArgs = freeCompilerArgs + listOf("-Xjvm-default=all") - } -} - -tasks.withType { - sourceCompatibility = "1.8" - targetCompatibility = "1.8" - options.encoding = "UTF-8" -} diff --git a/builder-generator/src/main/kotlin/bgenor/BuilderGeneratorSymbolProcessor.kt b/builder-generator/src/main/kotlin/bgenor/BuilderGeneratorSymbolProcessor.kt deleted file mode 100644 index c7e3c2c5..00000000 --- a/builder-generator/src/main/kotlin/bgenor/BuilderGeneratorSymbolProcessor.kt +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-tencent-guild. - * - * simbot-component-tencent-guild is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - * - * simbot-component-tencent-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-tencent-guild. If not, see . - */ - -package bgenor - -import com.google.devtools.ksp.isPublic -import com.google.devtools.ksp.processing.Resolver -import com.google.devtools.ksp.processing.SymbolProcessor -import com.google.devtools.ksp.processing.SymbolProcessorEnvironment -import com.google.devtools.ksp.symbol.* -import com.squareup.kotlinpoet.* -import com.squareup.kotlinpoet.ksp.toClassName -import com.squareup.kotlinpoet.ksp.toTypeName -import com.squareup.kotlinpoet.ksp.writeTo - - -/** - * - * @author ForteScarlet - */ -class BuilderGeneratorSymbolProcessor(private val environment: SymbolProcessorEnvironment) : SymbolProcessor { - private val logger get() = environment.logger - override fun process(resolver: Resolver): List { - val classes = resolver.getSymbolsWithAnnotation("bgenor.GenerateBuilder", false) - .filterIsInstance() - .toSet() - - logger.info("annotated @bgenor.GenerateBuilder classes: ${classes.joinToString { it.simpleName.asString() }}") - - val codeGenerator = BgenorCodeGenerator(environment) - - classes.forEach { ksClass -> - val context = BgenorContext() - - ksClass.primaryConstructor?.also { context.resolvePrimaryConstructor(it) } - - ksClass.getAllProperties().forEach { - context.resolveProperty(it) - } - - codeGenerator.generate(resolver, ksClass, context) - } - - return emptyList() - } - - -} - -internal class BgenorContext { - internal val constructorParams = mutableListOf() - internal val properties = mutableListOf() - - - fun resolvePrimaryConstructor(func: KSFunctionDeclaration) { - for (parameter in func.parameters) { - constructorParams.add(Param(parameter)) - } - } - - fun resolveProperty(property: KSPropertyDeclaration) { - if (!property.isMutable) { - return - } - - if (!property.isPublic()) { - return - } - - properties.add(Property(property)) - } - - internal data class Property(val property: KSPropertyDeclaration) - internal data class Param(val property: KSValueParameter) - -} - - -private class BgenorCodeGenerator( - private val environment: SymbolProcessorEnvironment, -) { - fun generate( - resolver: Resolver, - target: KSClassDeclaration, - context: BgenorContext - ) { - val targetClassName = target.simpleName.asString() + "Builder" - - val fileSpecBuilder = FileSpec.builder( - target.packageName.asString(), - "${target.simpleName.asString()}$\$Builder" - ) - - - val constructorProperties = context.constructorParams.map { param -> - val p = param.property - - - val modifiers: Array - val initNull: Boolean - if (p.hasDefault) { - modifiers = arrayOf(KModifier.PUBLIC) - initNull = true - } else { - modifiers = arrayOf(KModifier.PUBLIC, KModifier.LATEINIT) - initNull = false - } - - - - PropertySpec.builder( - p.name!!.asString(), - p.type.toTypeName().copy(nullable = initNull), *modifiers - ).apply { -// this.addKdoc() // TODO - mutable() - if (initNull) { - initializer("null") - } - }.build() - } - - - val builderClass = TypeSpec.classBuilder("${target.simpleName.asString()}Builder").apply { - addProperties(constructorProperties) - addFunction(FunSpec.builder("build").apply { - returns(target.toClassName()) - - propertySpecs.forEach { p -> - addCode( - """ - val ${p.name} = this.${p.name} - - """.trimIndent() - ) - } - - addCode( - """ - return TODO() - """.trimIndent() - ) - }.build()) - }.build() - - - fileSpecBuilder.addType(builderClass).build().writeTo(environment.codeGenerator, false) - } - -} - - - - - - - diff --git a/builder-generator/src/main/kotlin/bgenor/BuilderGeneratorSymbolProcessorProvider.kt b/builder-generator/src/main/kotlin/bgenor/BuilderGeneratorSymbolProcessorProvider.kt deleted file mode 100644 index 7fd8b14d..00000000 --- a/builder-generator/src/main/kotlin/bgenor/BuilderGeneratorSymbolProcessorProvider.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-tencent-guild. - * - * simbot-component-tencent-guild is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - * - * simbot-component-tencent-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-tencent-guild. If not, see . - */ - -package bgenor - -import com.google.devtools.ksp.processing.SymbolProcessor -import com.google.devtools.ksp.processing.SymbolProcessorEnvironment -import com.google.devtools.ksp.processing.SymbolProcessorProvider - - -/** - * - * @author ForteScarlet - */ -class BuilderGeneratorSymbolProcessorProvider : SymbolProcessorProvider { - override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor { - return BuilderGeneratorSymbolProcessor(environment) - } -} - - - diff --git a/builder-generator/src/main/kotlin/bgenor/annotations.kt b/builder-generator/src/main/kotlin/bgenor/annotations.kt deleted file mode 100644 index 9d4a2d06..00000000 --- a/builder-generator/src/main/kotlin/bgenor/annotations.kt +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright (c) 2023. ForteScarlet. - * - * This file is part of simbot-component-tencent-guild. - * - * simbot-component-tencent-guild is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - * - * simbot-component-tencent-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-tencent-guild. If not, see . - */ - -package bgenor - - -@Target(AnnotationTarget.CLASS) -@Retention(AnnotationRetention.BINARY) -annotation class GenerateBuilder() diff --git a/builder-generator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/builder-generator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider deleted file mode 100644 index f8e4f014..00000000 --- a/builder-generator/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider +++ /dev/null @@ -1,13 +0,0 @@ -# -# Copyright (c) 2023. ForteScarlet. -# -# This file is part of simbot-component-tencent-guild. -# -# simbot-component-tencent-guild is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. -# -# simbot-component-tencent-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. -# -# You should have received a copy of the GNU Lesser General Public License along with simbot-component-tencent-guild. If not, see . -# - -bgenor.BuilderGeneratorSymbolProcessorProvider diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a67c2234..06f5f592 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,5 +1,5 @@ [versions] -kotlin = "1.9.21" +kotlin = "1.9.22" kotlinx-coroutines = "1.7.3" kotlinx-serialization = "1.6.2" kotlinx-datetime = "0.5.0" @@ -10,8 +10,8 @@ openjdk-jmh = "1.35" log4j = "2.20.0" reactor = "3.6.2" # simbot -simbot = "4.0.0-dev14" -suspendTransform = "0.6.0" +simbot = "4.0.0-dev15" +suspendTransform = "0.7.0-beta1" gradleCommon = "0.2.0" [libraries] @@ -27,7 +27,7 @@ simbot-common-core = { group = "love.forte.simbot.common", name = "simbot-common simbot-common-suspend = { group = "love.forte.simbot.common", name = "simbot-common-suspend-runner", version.ref = "simbot" } simbot-common-annotations = { group = "love.forte.simbot.common", name = "simbot-common-annotations", version.ref = "simbot" } simbot-common-loop = { group = "love.forte.simbot.common", name = "simbot-common-stage-loop", version.ref = "simbot" } -simbot-gradle = { group = "love.forte.simbot.gradle", name = "simbot-gradle-suspendtransforms", version.ref = "simbot" } +simbot-gradle = { group = "love.forte.simbot.gradle", name = "simbot-gradle-suspendtransforms", version = "4.0.0-dev15" } # jetbrains-annotation jetbrains-annotations = "org.jetbrains:annotations:24.0.1" diff --git a/simbot-component-qq-guild-api/build.gradle.kts b/simbot-component-qq-guild-api/build.gradle.kts index 75e30b72..5330410f 100644 --- a/simbot-component-qq-guild-api/build.gradle.kts +++ b/simbot-component-qq-guild-api/build.gradle.kts @@ -29,6 +29,7 @@ plugins { setup(P.ComponentQQGuild) +useK2() configJavaCompileWithModule("simbot.component.qqguild.api") //apply(plugin = "qq-guild-dokka-partial-configure") apply(plugin = "qq-guild-multiplatform-maven-publish") diff --git a/simbot-component-qq-guild-core/build.gradle.kts b/simbot-component-qq-guild-core/build.gradle.kts index 48c8eb04..eb4c1bac 100644 --- a/simbot-component-qq-guild-core/build.gradle.kts +++ b/simbot-component-qq-guild-core/build.gradle.kts @@ -29,6 +29,7 @@ plugins { setup(P.ComponentQQGuild) +useK2() configJavaCompileWithModule("simbot.component.qqguild.core") apply(plugin = "qq-guild-multiplatform-maven-publish") @@ -69,6 +70,7 @@ kotlin { commonTest.dependencies { implementation(kotlin("test")) + implementation(kotlin("reflect")) implementation(libs.kotlinx.coroutines.test) implementation(libs.kotlinx.serialization.json) api(libs.simbot.core) diff --git a/simbot-component-qq-guild-stdlib/build.gradle.kts b/simbot-component-qq-guild-stdlib/build.gradle.kts index e0aae6c6..72f2e372 100644 --- a/simbot-component-qq-guild-stdlib/build.gradle.kts +++ b/simbot-component-qq-guild-stdlib/build.gradle.kts @@ -29,6 +29,7 @@ plugins { setup(P.ComponentQQGuild) +useK2() configJavaCompileWithModule("simbot.component.qqguild.stdlib") apply(plugin = "qq-guild-multiplatform-maven-publish") From 53967f043840c6d3572191a03ac773d7ec64327b Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Fri, 26 Jan 2024 20:53:34 +0800 Subject: [PATCH 40/71] release: v4.0.0-dev7 --- .changelog/v4.0.0-dev7.md | 11 +++++++++++ qudana.yaml | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .changelog/v4.0.0-dev7.md diff --git a/.changelog/v4.0.0-dev7.md b/.changelog/v4.0.0-dev7.md new file mode 100644 index 00000000..1d88140f --- /dev/null +++ b/.changelog/v4.0.0-dev7.md @@ -0,0 +1,11 @@ +> 对应核心版本: [**v4.0.0-dev15**](https://github.com/simple-robot/simpler-robot/releases/tag/v4.0.0-dev15) + + +> [!warning] +> **目前版本处于 `dev` 阶段,代表此版本是一个开发预览版,可能不稳定、可能随时发生更改、且不保证可用性。** + + +我们欢迎并期望着您的的[反馈](https://github.com/simple-robot/simbot-component-qq-guild/issues)或[协助](https://github.com/simple-robot/simbot-component-qq-guild/pulls), +感谢您的贡献与支持! + +也欢迎您为我们献上一颗 `star`,这是对我们最大的鼓励与认可! diff --git a/qudana.yaml b/qudana.yaml index 87f204fb..ad790dd5 100644 --- a/qudana.yaml +++ b/qudana.yaml @@ -18,7 +18,7 @@ profile: # paths: # - -projectJDK: azul-11 #(Applied in CI/CD pipeline) +projectJDK: azul-21 #(Applied in CI/CD pipeline) #Execute shell command before Qodana execution (Applied in CI/CD pipeline) #bootstrap: sh ./prepare-qodana.sh From 8f6bd90ac673a367ce6238c68908d5785f988f16 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Fri, 26 Jan 2024 21:26:09 +0800 Subject: [PATCH 41/71] =?UTF-8?q?ci:=20=E5=90=88=E5=B9=B6=20CI=20=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=EF=BC=8C=E7=A7=BB=E9=99=A4=E4=B8=B4=E6=97=B6=E7=9A=84?= =?UTF-8?q?=20-v4=20CI=E9=85=8D=E7=BD=AE=E7=9A=84=E6=9C=89=E6=95=88?= =?UTF-8?q?=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-v4-website.yml | 10 ++-- .github/workflows/publish-release.yml | 12 ++--- .github/workflows/publish-snapshot.yml | 21 +++++--- ...-release.yml => publish-v4-release.yml.bk} | 0 ...napshot.yml => publish-v4-snapshot.yml.bk} | 0 ...q-guild-dokka-partial-configure.gradle.kts | 18 +++---- ...tcg-suspend-transform-configure.gradle.kts | 17 ------- ...encent-guild.dokka-multi-module.gradle.kts | 8 +-- ...encent-guild.module-conventions.gradle.kts | 51 ------------------- gradle/libs.versions.toml | 4 +- 10 files changed, 40 insertions(+), 101 deletions(-) rename .github/workflows/{publish-v4-release.yml => publish-v4-release.yml.bk} (100%) rename .github/workflows/{publish-v4-snapshot.yml => publish-v4-snapshot.yml.bk} (100%) delete mode 100644 buildSrc/src/main/kotlin/simbot-tencent-guild.module-conventions.gradle.kts diff --git a/.github/workflows/deploy-v4-website.yml b/.github/workflows/deploy-v4-website.yml index 1c58f047..9fe44509 100644 --- a/.github/workflows/deploy-v4-website.yml +++ b/.github/workflows/deploy-v4-website.yml @@ -2,11 +2,11 @@ name: Deploy Website on: push: branches: -# - main -# - dev/ver/** -# - dev/main - - v4-dev/main - - v4-dev/v4-upgrade + - main + - dev/ver/** + - dev/main +# - v4-dev/main +# - v4-dev/v4-upgrade paths: - 'Writerside/**' diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index bde9bcb8..a190d549 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -2,7 +2,7 @@ name: Publish Release on: push: tags: - - v3.**.** + - v4.**.** env: IS_CI: true @@ -33,7 +33,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: 11 + java-version: 21 cache: 'gradle' # setup Gradle @@ -99,7 +99,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: 11 + java-version: 21 # setup Gradle - name: Gradle publish snapshot @@ -135,7 +135,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: 11 + java-version: 21 # setup Gradle - name: Gradle generate documentation @@ -156,7 +156,7 @@ jobs: with: personal_token: ${{ secrets.PUSH_TOKEN }} external_repository: simple-robot-library/simbot3-api-docs - publish_branch: kdoc-deploy/component-qq-guild-v3 + publish_branch: kdoc-deploy/component-qq-guild publish_dir: ./build/dokka/html # deploy to sub dir - destination_dir: components/qq-guild-v3 + destination_dir: components/qq-guild diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml index 39a6c086..280af81d 100644 --- a/.github/workflows/publish-snapshot.yml +++ b/.github/workflows/publish-snapshot.yml @@ -5,11 +5,16 @@ on: - main - dev/ver/** - dev/main - paths: - - '**src/main/kotlin/**.kt' - - '**src/main/java/**.java' - - 'buildSrc/**' + - 'buildSrc' + - '**src/**/kotlin/**.kt' + - '**src/**/java/**.java' + - '**/src/**/kotlin/**.kt' + - '**/src/**/java/**.java' + - '**/build.gradle.kts' + - 'build.gradle.kts' + - 'settings.gradle.kts' + - 'gradle.properties' # 手动触发工作流 workflow_dispatch: @@ -45,7 +50,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: 11 + java-version: 21 # setup Gradle - name: Gradle test and publish snapshot @@ -77,7 +82,7 @@ jobs: uses: actions/setup-java@v3 with: distribution: 'zulu' - java-version: 11 + java-version: 21 # setup Gradle - name: Gradle generate documentation @@ -99,7 +104,7 @@ jobs: with: personal_token: ${{ secrets.PUSH_TOKEN }} external_repository: simple-robot-library/simbot3-api-docs - publish_branch: kdoc-deploy/snapshots/component-qq-guild-v3 + publish_branch: kdoc-deploy/snapshots/component-qq-guild publish_dir: ./build/dokka/html # deploy to sub dir - destination_dir: snapshots/components/qq-guild-v3 + destination_dir: snapshots/components/qq-guild diff --git a/.github/workflows/publish-v4-release.yml b/.github/workflows/publish-v4-release.yml.bk similarity index 100% rename from .github/workflows/publish-v4-release.yml rename to .github/workflows/publish-v4-release.yml.bk diff --git a/.github/workflows/publish-v4-snapshot.yml b/.github/workflows/publish-v4-snapshot.yml.bk similarity index 100% rename from .github/workflows/publish-v4-snapshot.yml rename to .github/workflows/publish-v4-snapshot.yml.bk diff --git a/buildSrc/src/main/kotlin/qq-guild-dokka-partial-configure.gradle.kts b/buildSrc/src/main/kotlin/qq-guild-dokka-partial-configure.gradle.kts index 6670c383..51b37a3e 100644 --- a/buildSrc/src/main/kotlin/qq-guild-dokka-partial-configure.gradle.kts +++ b/buildSrc/src/main/kotlin/qq-guild-dokka-partial-configure.gradle.kts @@ -17,7 +17,7 @@ import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.gradle.DokkaTaskPartial -import java.net.URL +import java.net.URI plugins { id("org.jetbrains.dokka") @@ -57,7 +57,7 @@ tasks.withType().configureEach { sourceLink { localDirectory.set(projectDir.resolve("src")) val relativeTo = projectDir.relativeTo(rootProject.projectDir) - remoteUrl.set(URL("${P.ComponentQQGuild.HOMEPAGE}/tree/main/$relativeTo/src")) + remoteUrl.set(URI.create("${P.ComponentQQGuild.HOMEPAGE}/tree/main/$relativeTo/src/").toURL()) remoteLineSuffix.set("#L") } @@ -67,24 +67,24 @@ tasks.withType().configureEach { } - fun externalDocumentation(docUrl: URL) { + fun externalDocumentation(docUri: URI) { externalDocumentationLink { - url.set(docUrl) - packageListUrl.set(URL(docUrl, "${docUrl.path}/package-list")) + url.set(docUri.toURL()) + packageListUrl.set(docUri.resolve("package-list").toURL()) } } // kotlin-coroutines doc - externalDocumentation(URL("https://kotlinlang.org/api/kotlinx.coroutines")) + externalDocumentation(URI.create("https://kotlinlang.org/api/kotlinx.coroutines/")) // kotlin-serialization doc - externalDocumentation(URL("https://kotlinlang.org/api/kotlinx.serialization")) + externalDocumentation(URI.create("https://kotlinlang.org/api/kotlinx.serialization/")) // ktor - externalDocumentation(URL("https://api.ktor.io")) + externalDocumentation(URI.create("https://api.ktor.io/")) // simbot doc - externalDocumentation(URL("https://docs.simbot.forte.love/main")) + externalDocumentation(URI.create("https://docs.simbot.forte.love/main/")) } } diff --git a/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts b/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts index f7ba0e0f..39108655 100644 --- a/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts +++ b/buildSrc/src/main/kotlin/simbot-tcg-suspend-transform-configure.gradle.kts @@ -17,23 +17,6 @@ import love.forte.simbot.gradle.suspendtransforms.SuspendTransforms -/* - * Copyright (c) 2024. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ - plugins { id("love.forte.plugin.suspend-transform") } diff --git a/buildSrc/src/main/kotlin/simbot-tencent-guild.dokka-multi-module.gradle.kts b/buildSrc/src/main/kotlin/simbot-tencent-guild.dokka-multi-module.gradle.kts index 676781c7..79823dcd 100644 --- a/buildSrc/src/main/kotlin/simbot-tencent-guild.dokka-multi-module.gradle.kts +++ b/buildSrc/src/main/kotlin/simbot-tencent-guild.dokka-multi-module.gradle.kts @@ -28,15 +28,17 @@ repositories { } fun org.jetbrains.dokka.gradle.AbstractDokkaTask.configOutput(format: String) { - moduleName.set("Simple Robot Component | QQ Guild") + moduleName.set("Simple Robot 组件 | QQ Guild") outputDirectory.set(rootProject.file("build/dokka/$format")) } tasks.named("dokkaHtmlMultiModule") { configOutput("html") - if (isSnapshot()) { - version = P.ComponentQQGuild.snapshotVersion.toString() + + rootProject.file("README.md").takeIf { it.exists() }?.also { + includes.from(it) } + pluginConfiguration { customAssets = listOf(rootProject.file(".simbot/dokka-assets/logo-icon.svg")) customStyleSheets = listOf(rootProject.file(".simbot/dokka-assets/css/kdoc-style.css")) diff --git a/buildSrc/src/main/kotlin/simbot-tencent-guild.module-conventions.gradle.kts b/buildSrc/src/main/kotlin/simbot-tencent-guild.module-conventions.gradle.kts deleted file mode 100644 index a725312d..00000000 --- a/buildSrc/src/main/kotlin/simbot-tencent-guild.module-conventions.gradle.kts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2022-2024. ForteScarlet. - * - * This file is part of simbot-component-qq-guild. - * - * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms - * of the GNU Lesser General Public License as published by the Free Software Foundation, - * either version 3 of the License, or (at your option) any later version. - * - * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. - * If not, see . - */ -import love.forte.gradle.common.core.project.setup - - -plugins { - idea -} - -setup(P.ComponentQQGuild) - -// -//kotlin { -// explicitApi() -// sourceSets.configureEach { -// languageSettings { -// optIn("kotlin.RequiresOptIn") -// } -// } -// -// sourceSets.getByName("test").kotlin { -// srcDir("src/samples") -// } -//} - -configurations.all { - // check for updates every build - resolutionStrategy.cacheChangingModulesFor(0, "seconds") -} - - -idea { - module { - isDownloadSources = true - isDownloadJavadoc = true - } -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 06f5f592..5bdae927 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -80,8 +80,8 @@ ktor-client-winhttp = { group = "io.ktor", name = "ktor-client-winhttp", version # see https://ktor.io/docs/http-client-engines.html#darwin ktor-client-darwin = { group = "io.ktor", name = "ktor-client-darwin", version.ref = "ktor" } -# nodejs -#kotlinx-nodejs = "" +# nodejsn +##kotlinx-odejs = "" # log4j log4j-api = { group = "org.apache.logging.log4j", name = "log4j-api", version.ref = "log4j" } From 333083912b01780a330e1728fafcdf8139b647be Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Fri, 26 Jan 2024 21:36:58 +0800 Subject: [PATCH 42/71] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=83=A8=E5=88=86?= =?UTF-8?q?=E6=A8=A1=E5=9D=97=E7=9A=84=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 5 ----- simbot-component-qq-guild-api/build.gradle.kts | 6 +++--- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 60c1aadc..37747ef5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -48,12 +48,7 @@ allprojects { snapshotsOnly() } } - //mavenLocal() } -// -// configurations.all { -// resolutionStrategy.cacheChangingModulesFor(15, TimeUnit.MINUTES) -// } } idea { diff --git a/simbot-component-qq-guild-api/build.gradle.kts b/simbot-component-qq-guild-api/build.gradle.kts index 5330410f..4863a6ac 100644 --- a/simbot-component-qq-guild-api/build.gradle.kts +++ b/simbot-component-qq-guild-api/build.gradle.kts @@ -89,7 +89,6 @@ kotlin { jvmTest.dependencies { implementation(libs.ktor.client.cio) -// implementation(simbotApi) // use @Api4J annotation implementation(libs.log4j.api) implementation(libs.log4j.core) implementation(libs.log4j.slf4j2) @@ -99,10 +98,11 @@ kotlin { jsMain.dependencies { api(libs.ktor.client.js) + implementation(libs.simbot.common.annotations) } - jsTest.dependencies { - api(libs.ktor.client.js) + nativeMain.dependencies { + implementation(libs.simbot.common.annotations) } mingwTest.dependencies { From 875cb756192ec8dfe913b672d6f6b0fab8434415 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Fri, 26 Jan 2024 21:54:32 +0800 Subject: [PATCH 43/71] README --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e1c834c2..9227bf3d 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,10 @@ 各方面的 simbot 组件库实现, 包括对 `API` 内容的实现、事件相关的实现以及BOT对于事件的监听与交互等。 +QQ频道组件库可以作为底层API依赖使用、 +轻量级的QQ频道事件调度框架使用, +也可以基于 simbot 核心库的种种快速开发一个功能强大的QQ频道机器人! + - 基于 [`Kotlin`](https://kotlinlang.org/) 提供 [KMP 多平台](https://kotlinlang.org/docs/multiplatform.html) 特性 - 基于 [`Kotlin coroutines`](https://github.com/Kotlin/kotlinx.coroutines) 与 [`Ktor`](https://ktor.io/) 提供高效易用的API; @@ -84,7 +88,8 @@ ## 法欧莉 -如果你想看一看通过 `simbot-qq-guild` 组件的具体作品,可以前往QQ频道添加亲爱的 [法欧莉斯卡雷特](https://qun.qq.com/qunpro/robot/share?robot_appid=101986850) 来体验。 +如果你想看一看使用QQ频道组件实现的具体作品, +可以前往QQ频道添加亲爱的 [法欧莉斯卡雷特](https://qun.qq.com/qunpro/robot/share?robot_appid=101986850) 来体验喔~ ## License From b74a0e615d2c0fe557cb3cdb9c0ac652cfafa2ce Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Sat, 27 Jan 2024 00:57:44 +0800 Subject: [PATCH 44/71] =?UTF-8?q?=E5=80=9F=E7=94=A8=20ksp=20=E8=BE=85?= =?UTF-8?q?=E5=8A=A9=E6=96=87=E6=A1=A3=E7=9A=84=E7=BC=96=E5=86=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + Writerside/topics/api-list.md | 674 ++++++++++++++++-- Writerside/topics/event.md | 527 +++++++++++++- gradle/libs.versions.toml | 12 + .../api-reader/build.gradle.kts | 48 ++ .../apireader/ApiReaderProcessor.kt | 139 ++++ .../apireader/ApiReaderProcessorProvider.kt | 52 ++ .../apireader/EventReaderProcessor.kt | 128 ++++ .../qg/internal/processors/apireader/utils.kt | 22 + ...ols.ksp.processing.SymbolProcessorProvider | 2 + settings.gradle.kts | 2 + .../build.gradle.kts | 11 + 12 files changed, 1524 insertions(+), 94 deletions(-) create mode 100644 internal-processors/api-reader/build.gradle.kts create mode 100644 internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessor.kt create mode 100644 internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessorProvider.kt create mode 100644 internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/EventReaderProcessor.kt create mode 100644 internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/utils.kt create mode 100644 internal-processors/api-reader/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider diff --git a/.gitignore b/.gitignore index 0bfbdf65..46529603 100644 --- a/.gitignore +++ b/.gitignore @@ -157,3 +157,4 @@ fabric.properties # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser +generated-docs diff --git a/Writerside/topics/api-list.md b/Writerside/topics/api-list.md index 39a2000c..61d8535b 100644 --- a/Writerside/topics/api-list.md +++ b/Writerside/topics/api-list.md @@ -1,4 +1,4 @@ -# API列表 +# API定义列表 此处会列举 `API 模块` 中、`love.forte.simbot.qguild.api` 包下定义的所有 `QQGuildApi` 实现。 @@ -6,259 +6,813 @@ > 因为那是最贴合真实情况且最全面的。 + + +`love.forte.simbot.qguild.api.GatewayApis` + +获取网关信息。 + +通过`Normal] 或 [Shared`的形式根据bot信息获取使用 Websocket 接入时间通知的链接。 + +> 参考文档 + + + + + + + +`love.forte.simbot.qguild.api.Normal` + +获取通用 WSS 接入点 + +> 参考文档 + + + + + +`love.forte.simbot.qguild.api.Shared` + +获取带分片 WSS 接入点 + +> 参考文档 + + + + + + -> `love.forte.simbot.qguild.api.announces.CreateAnnouncesApi` +`love.forte.simbot.qguild.api.announces.CreateAnnouncesApi` + +机器人设置消息为指定子频道公告。 + +创建子频道公告 + + + -> `love.forte.simbot.qguild.api.announces.DeleteAnnouncesApi` +`love.forte.simbot.qguild.api.announces.DeleteAnnouncesApi` + +删除子频道公告 + +机器人删除指定子频道公告 + + -> `love.forte.simbot.qguild.api.apipermission.DemandApiPermissionApi` +`love.forte.simbot.qguild.api.apipermission.DemandApiPermissionApi` + +创建频道 API 接口权限授权链接 + +用于创建 API 接口权限授权链接,该链接指向 `guild_id` 对应的频道 。 + +需要注意,私信场景中,当需要查询私信来源频道的权限时,应使用 `src_guild_id` ,即 message +中的 `src_guild_id` + +每天只能在一个频道内发 `3` 条(默认值)频道权限授权链接。 + + -> `love.forte.simbot.qguild.api.apipermission.GetApiPermissionListApi` +`love.forte.simbot.qguild.api.apipermission.GetApiPermissionListApi` + +获取频道可用权限列表 + +用于获取机器人在频道 `guild_id` 内可以使用的权限列表。 + + -> `love.forte.simbot.qguild.api.channel.CreateChannelApi` +`love.forte.simbot.qguild.api.channel.CreateChannelApi` + +创建子频道 + +用于在 `guild_id` 指定的频道下创建一个子频道。 + +- 要求操作人具有管理频道的权限,如果是机器人,则需要将机器人设置为管理员。 +- 创建成功后,返回创建成功的子频道对象,同时会触发一个频道创建的事件通知。 + + -> `love.forte.simbot.qguild.api.channel.DeleteChannelApi` +`love.forte.simbot.qguild.api.channel.DeleteChannelApi` + +删除子频道 + +用于删除 `channel_id` 指定的子频道。 + +- 要求操作人具有 `管理子频道` 的权限,如果是机器人,则需要将机器人设置为管理员。 +- 修改成功后,会触发子频道删除事件。 + + +**注意** + +子频道的删除是无法撤回的,一旦删除,将无法恢复。 + + -> `love.forte.simbot.qguild.api.channel.GetChannelApi` +`love.forte.simbot.qguild.api.channel.GetChannelApi` + +获取子频道信息 + + -> `love.forte.simbot.qguild.api.channel.GetChannelOnlineNumsApi` +`love.forte.simbot.qguild.api.channel.GetChannelOnlineNumsApi` + +获取在线成员数 + +用于查询音视频/直播子频道 channel_id 的在线成员数。 + + -> `love.forte.simbot.qguild.api.channel.GetGuildChannelListApi` +`love.forte.simbot.qguild.api.channel.GetGuildChannelListApi` + +获取子频道列表 + +用于获取 `guild_id` 指定的频道下的子频道列表。 + + -> `love.forte.simbot.qguild.api.channel.ModifyChannelApi` +`love.forte.simbot.qguild.api.channel.ModifyChannelApi` + +修改子频道 + +用于修改 `channel_id` 指定的子频道的信息。 + +- 要求操作人具有 `管理子频道` 的权限,如果是机器人,则需要将机器人设置为管理员。 +- 修改成功后,会触发子频道更新事件。 + + -> `love.forte.simbot.qguild.api.channel.permissions.GetChannelMemberPermissionsApi` +`love.forte.simbot.qguild.api.channel.permissions.GetChannelMemberPermissionsApi` + +获取指定子频道的权限 + +用于获取 子频道 `channel_id` 下用户 `user_id` 的权限。 + +- 获取子频道用户权限。 +- 要求操作人具有管理子频道的权限,如果是机器人,则需要将机器人设置为管理员。 + + -> `love.forte.simbot.qguild.api.channel.permissions.GetChannelRolePermissionsApi` +`love.forte.simbot.qguild.api.channel.permissions.GetChannelRolePermissionsApi` + +获取子频道身份组权限 + +用于获取子频道 `channel_id` 下身份组 `role_id` 的权限。 + +- 要求操作人具有管理子频道的权限,如果是机器人,则需要将机器人设置为管理员。 + + -> `love.forte.simbot.qguild.api.channel.permissions.ModifyChannelMemberPermissionsApi` +`love.forte.simbot.qguild.api.channel.permissions.ModifyChannelMemberPermissionsApi` + +修改子频道权限 + +用于修改子频道 `channel_id` 下用户 `user_id` 的权限。 + +- 要求操作人具有 `管理子频道` 的权限,如果是机器人,则需要将机器人设置为管理员。 +- 参数包括 `add` 和 `remove` 两个字段,分别表示授予的权限以及删除的权限。 + 要授予用户权限即把 `add` 对应位置 1,删除用户权限即把 `remove` 对应位置 1。当两个字段同一位都为 1,表现为删除权限。 +- 本接口不支持修改 `可管理子频道` 权限。 + + -> `love.forte.simbot.qguild.api.channel.permissions.ModifyChannelRolePermissionsApi` +`love.forte.simbot.qguild.api.channel.permissions.ModifyChannelRolePermissionsApi` + +修改子频道身份组权限 + +用于修改子频道 `channel_id` 下身份组 `role_id` 的权限。 + +- 要求操作人具有管理子频道的权限,如果是机器人,则需要将机器人设置为管理员。 +- 参数包括 `add` 和 `remove` 两个字段,分别表示授予的权限以及删除的权限。 + 要授予身份组权限即把 `add` 对应位置 1,删除身份组权限即把 `remove` 对应位置 1。当两个字段同一位都为 1,表现为删除权限。 +- 本接口不支持修改 `可管理子频道` 权限。 + + -> `love.forte.simbot.qguild.api.channel.pins.AddPinsMessageApi` +`love.forte.simbot.qguild.api.channel.pins.AddPinsMessageApi` + +添加精华消息 + +用于添加子频道 `channel_id` 内的精华消息。 + +- 精华消息在一个子频道内最多只能创建 `20` 条。 +- 只有可见的消息才能被设置为精华消息。 +- 接口返回对象中 `message_ids` 为当前请求后子频道内所有精华消息 `message_id` 数组。 + + -> `love.forte.simbot.qguild.api.channel.pins.DeletePinsMessageApi` +`love.forte.simbot.qguild.api.channel.pins.DeletePinsMessageApi` + +删除精华消息 + +用于删除子频道 `channel_id` 下指定 `message_id` 的精华消息。 + +- 删除子频道内全部精华消息,请将 `message_id` 设置为 [`all`][DELETE_ALL_MESSAGE_ID]。 + + -> `love.forte.simbot.qguild.api.channel.pins.GetPinsMessageApi` +`love.forte.simbot.qguild.api.channel.pins.GetPinsMessageApi` + +获取精华消息 + +用于获取子频道 `channel_id` 内的精华消息。 + + -> `love.forte.simbot.qguild.api.channel.schedules.CreateScheduleApi` +`love.forte.simbot.qguild.api.channel.schedules.CreateScheduleApi` + +创建日程 + +用于在 `channel_id` 指定的 `日程子频道` 下创建一个日程。 + +- 要求操作人具有 `管理频道` 的权限,如果是机器人,则需要将机器人设置为管理员。 +- 创建成功后,返回创建成功的日程对象。 +- 创建操作频次限制 +- 单个管理员每天限 `10` 次。 +- 单个频道每天 `100` 次。 + + -> `love.forte.simbot.qguild.api.channel.schedules.DeleteScheduleApi` +`love.forte.simbot.qguild.api.channel.schedules.DeleteScheduleApi` + +修改日程 + +用于修改日程子频道 `channel_id` 下 `schedule_id` 指定的日程的详情。 + +- 要求操作人具有 `管理频道` 的权限,如果是机器人,则需要将机器人设置为管理员。 + + -> `love.forte.simbot.qguild.api.channel.schedules.GetScheduleApi` +`love.forte.simbot.qguild.api.channel.schedules.GetScheduleApi` + +获取日程详情 + +获取日程子频道 `channel_id` 下 `schedule_id` 指定的的日程的详情。 + + -> `love.forte.simbot.qguild.api.channel.schedules.GetScheduleListApi` +`love.forte.simbot.qguild.api.channel.schedules.GetScheduleListApi` + +获取频道日程列表 + +用于获取 `channel_id` 指定的子频道中当天的日程列表。 + +- 若带了参数 `since`,则返回在 `since` 对应当天的日程列表;若未带参数 `since`,则默认返回今天的日程列表。 + + -> `love.forte.simbot.qguild.api.channel.schedules.ModifyScheduleApi` +`love.forte.simbot.qguild.api.channel.schedules.ModifyScheduleApi` + +修改日程 + +用于修改日程子频道 `channel_id` 下 `schedule_id` 指定的日程的详情。 + +- 要求操作人具有 `管理频道` 的权限,如果是机器人,则需要将机器人设置为管理员。 + + -> `love.forte.simbot.qguild.api.forum.DeleteThreadApi` +`love.forte.simbot.qguild.api.forum.DeleteThreadApi` + +删除帖子 + +该接口用于删除指定子频道下的某个帖子。 + + -> `love.forte.simbot.qguild.api.forum.GetThreadApi` +`love.forte.simbot.qguild.api.forum.GetThreadApi` + +获取帖子详情 + +该接口用于获取子频道下的帖子详情。 + + + -> `love.forte.simbot.qguild.api.forum.GetThreadListApi` +`love.forte.simbot.qguild.api.forum.GetThreadListApi` + +获取帖子列表 + +该接口用于获取子频道下的帖子列表。 + + -> `love.forte.simbot.qguild.api.forum.PublishThreadApi` +`love.forte.simbot.qguild.api.forum.PublishThreadApi` - - +发表帖子 -> `love.forte.simbot.qguild.api.GatewayApis$Normal` - - -> `love.forte.simbot.qguild.api.GatewayApis$Shared` -> `love.forte.simbot.qguild.api.guild.GetGuildApi` +`love.forte.simbot.qguild.api.guild.GetGuildApi` + +获取频道详情 + +用于获取 `guildId` 指定的频道的详情。 + -> `love.forte.simbot.qguild.api.guild.mute.MuteAllApi` +`love.forte.simbot.qguild.api.guild.mute.MuteAllApi` + +禁言全员 + +用于将频道的全体成员(非管理员)禁言。 + +需要使用的 `token` 对应的用户具备管理员权限。如果是机器人,要求被添加为管理员。 +该接口同样可用于解除禁言,将 `mute_end_timestamp` 或 `mute_seconds` 传值为字符串'0'即可。 + + + + -> `love.forte.simbot.qguild.api.guild.mute.MuteMemberApi` +`love.forte.simbot.qguild.api.guild.mute.MuteMemberApi` + +禁言指定成员 + +用于禁言频道 `guild_id` 下的成员 `user_id`。 + +需要使用的 `token` 对应的用户具备管理员权限。如果是机器人,要求被添加为管理员。 +该接口同样可用于解除禁言,将 `mute_end_timestamp` 或 `mute_seconds` 传值为字符串'0'即可。 + + -> `love.forte.simbot.qguild.api.guild.mute.MuteMultiMemberApi` +`love.forte.simbot.qguild.api.guild.mute.MuteMultiMemberApi` + +禁言批量成员 + +用于将频道的指定批量成员(非管理员)禁言。 + +需要使用的 `token` 对应的用户具备管理员权限。如果是机器人,要求被添加为管理员。 +该接口同样可用于批量解除禁言,将 `mute_end_timestamp` 或 `mute_seconds` 传值为字符串'0'即可,及需要批量解除禁言的成员的user_id列表user_ids'。 + + -> `love.forte.simbot.qguild.api.member.DeleteMemberApi` +`love.forte.simbot.qguild.api.member.DeleteMemberApi` + +删除频道成员 + +用于删除 guild_id 指定的频道下的成员 user_id。 + +- 需要使用的 token 对应的用户具备踢人权限。如果是机器人,要求被添加为管理员。 +- 操作成功后,会触发频道成员删除事件。 +- 无法移除身份为管理员的成员 + + -> `love.forte.simbot.qguild.api.member.GetGuildMemberListApi` +`love.forte.simbot.qguild.api.member.GetGuildMemberListApi` + +获取频道成员列表 + +用于获取 guild_id 指定的频道中所有成员的详情列表,支持分页。 + + +**有关返回结果的说明** + +1. 在每次翻页的过程中,可能会返回上一次请求已经返回过的 `member` 信息,需要调用方自己根据 `user id` 来进行去重。 +2. 每次返回的 `member` 数量与 `limit` 不一定完全相等。翻页请使用最后一个 `member` 的 `user id` 作为下一次请求的 `after` 参数,直到回包为空,拉取结束。 + + + -> `love.forte.simbot.qguild.api.member.GetGuildRoleMemberListApi` +`love.forte.simbot.qguild.api.member.GetGuildRoleMemberListApi` + +获取频道身份组成员列表 + +用于获取 `guild_id` 频道中指定 `role_id` 身份组下所有成员的详情列表,支持分页。 + + +**有关返回结果的说明** + +1. 每次返回的member数量与limit不一定完全相等。特定管理身份组下的成员可能存在一次性返回全部的情况 + + -> `love.forte.simbot.qguild.api.member.GetMemberApi` +`love.forte.simbot.qguild.api.member.GetMemberApi` + +获取某个成员信息 + +用于获取 `guild_id` 指定的频道中 `user_id` 对应成员的详细信息。 + + -> `love.forte.simbot.qguild.api.message.DeleteMessageApi` +`love.forte.simbot.qguild.api.message.DeleteMessageApi` + +撤回消息 + +用于撤回子频道 `channel_id` 下的消息 `message_id`。 + +- 管理员可以撤回普通成员的消息。 +- 频道主可以撤回所有人的消息。 + + + + + + +`love.forte.simbot.qguild.api.message.GetMessageApi` + +获取指定消息 + +用于获取子频道 `channel_id` 下的消息 `message_id` 的详情。 + + + + + + +`love.forte.simbot.qguild.api.message.MessageSendApi` + +发送消息 + +用于向 `channel_id` 指定的子频道发送消息。 + +- 要求操作人在该子频道具有 `发送消息` 的权限。 +- 主动消息在频道主或管理设置了情况下,按设置的数量进行限频。在未设置的情况遵循如下限制: +- 主动推送消息,默认每天往每个子频道可推送的消息数是 `20` 条,超过会被限制。 +- 主动推送消息在每个频道中,每天可以往 `2` 个子频道推送消息。超过后会被限制。 +- 不论主动消息还是被动消息,在一个子频道中,每 `1s` 只能发送 `5` 条消息。 +- 被动回复消息有效期为 `5` 分钟。超时会报错。 +- 发送消息接口要求机器人接口需要连接到 websocket 上保持在线状态 +- 有关主动消息审核,可以通过 Intents + 中审核事件 `MESSAGE_AUDIT` 返回 MessageAudited 对象获取结果。 + +
+ + +**主动消息与被动消息** + +- 主动消息:发送消息时,未填充 `msg_id/event_id` 字段的消息。 +- 被动消息:发送消息时,填充了 `msg_id/event_id` 字段的消息。`msg_id` 和 `event_id` 两个字段任意填一个即为被动消息。 + 接口使用此 `msg_id/event_id` 拉取用户的消息或事件,同时判断用户消息或事件的发送时间,如果超过被动消息回复时效,将会不允许发送该消息。 + +更多参考 文档 + + +**发送 ARK 模板消息** + +通过指定 `ark` 字段发送模板消息。 + +- 要求操作人在该子频道具有发送消息和 对应 `ARK 模板` 的权限。 +- 调用前需要先申请消息模板,这一步会得到一个模板 `id`,在请求时填在 `ark.template_id` 上。 +- 发送成功之后,会触发一个创建消息的事件。 +- 可用模板参考可用模板。 + +更多参考 文档 + + +**发送引用消息** + + +- 只支持引用机器人自己发送到的消息以及用户@机器人产生的消息。 +- 发送成功之后,会触发一个创建消息的事件。 + +不能单独发送引用消息,引用消息需要和其他消息类型组合发送,参数请见发送消息。 + +更多参考 文档 + + +**发送含有消息按钮组件的消息** + + +通过指定 `keyboard` 字段发送带按钮的消息,支持 `keyboard 模版` 和 `自定义 keyboard` 两种请求格式。 + +- 要求操作人在该子频道具有 `发送消息` 和 `对应消息按钮组件` 的权限。 +- 请求参数 `keyboard 模版` 和 `自定义 keyboard` 只能单一传值。 +- `keyboard 模版` +- 调用前需要先申请消息按钮组件模板,这一步会得到一个模板 id,在请求时填在 `keyboard` 字段上。 +- 申请消息按钮组件模板需要提供响应的 json,具体格式参考 InlineKeyboard。 +- 仅 `markdown` 消息支持消息按钮。 + +更多参考 文档 + + +**内嵌格式** + +利用 `content` 字段发送内嵌格式的消息。 + +- 内嵌格式仅在 `content` 中会生效,在 `Ark` 和 `Embed` 中不生效。 +- 为了区分是文本还是内嵌格式,消息抄送和发送会对消息内容进行相关的转义。 + + +**转义内容** + + +| **源字符** | **转义后** | +|----------|----------| +| `&` | `&` | +| `<` | `<` | +| `>` | `>` | + +可参考使用`ContentTextDecoder`和 [ContentTextEncoder] + + +**消息审核** + + +> 其中推送、回复消息的 code 错误码 `304023`、`304024` 会在 响应数据包 `data` 中返回 `MessageAudit` 审核消息的信息 + +当响应结果为上述错误码时,请求实体对象结果的API时会抛出`MessageAuditedException`异常并携带相关的对象信息。 + +详见文档 发送消息 中的相关描述以及 +[MessageAuditedException] 的文档描述。 + +
+ +更多参考 文档 + + +
-> `love.forte.simbot.qguild.api.message.direct.CreateDmsApi` +`love.forte.simbot.qguild.api.message.direct.CreateDmsApi` + +创建私信会话 + +用于机器人和在同一个频道内的成员创建私信会话。 + +机器人和用户存在共同频道才能创建私信会话。 +创建成功后,返回创建成功的频道 `id` ,子频道 `id` 和创建时间。 + + +**参数** + +| 字段名 | 类型 | 描述 | +|-----|-----|-----| +| `recipient_id` | `string` | 接收者 id | +| `source_guild_id` | `string` | 源频道 id | + + + -> `love.forte.simbot.qguild.api.message.direct.DeleteDmsApi` +`love.forte.simbot.qguild.api.message.direct.DeleteDmsApi` + +撤回私信 + +用于撤回私信频道 `guild_id` 中 `message_id` 指定的私信消息。只能用于撤回机器人自己发送的私信。 + + + -> `love.forte.simbot.qguild.api.message.direct.DmsSendApi` +`love.forte.simbot.qguild.api.message.direct.DmsSendApi` + +发送私信 + +**接口** + +`POST /dms/{guild_id}/messages` + + +**功能描述** + +用于发送私信消息,前提是已经创建了私信会话。 + +* 私信的 `guild_id` 在创建私信会话时以及私信消息事件中获取。 +* 私信场景下,每个机器人每天可以对一个用户发 `2` 条主动消息。 +* 私信场景下,每个机器人每天累计可以发 `200` 条主动消息。 +* 私信场景下,被动消息没有条数限制。 + + +**参数** + +和`发送消息][MessageSendApi`参数一致。 + + +**返回** + +和`发送消息][MessageSendApi`返回一致。 - - -> `love.forte.simbot.qguild.api.message.GetMessageApi` - - -> `love.forte.simbot.qguild.api.message.MessageSendApi` -> `love.forte.simbot.qguild.api.message.setting.GetMessageSettingApi` +`love.forte.simbot.qguild.api.message.setting.GetMessageSettingApi` + +获取频道消息频率设置 + +用于获取机器人在频道 `guild_id` 内的消息频率设置。 + -> `love.forte.simbot.qguild.api.role.AddMemberRoleApi` +`love.forte.simbot.qguild.api.role.AddMemberRoleApi` + +增加频道身份组成员 + +用于将频道 `guild_id` 下的用户 `user_id` 添加到身份组 `role_id` 。 + +- 需要使用的 `token` 对应的用户具备增加身份组成员权限。如果是机器人,要求被添加为管理员。 +- 如果要增加的身份组 `ID` 是 [`5-子频道管理员`][love.forte.simbot.qguild.model.Role.DEFAULT_ID_CHANNEL_ADMIN], + 需要增加 `channel` 对象来指定具体是哪个子频道。 + + -> `love.forte.simbot.qguild.api.role.CreateGuildRoleApi` +`love.forte.simbot.qguild.api.role.CreateGuildRoleApi` + +创建频道身份组 + +用于在 `guild_id` 指定的频道下创建一个身份组。 + +- 需要使用的 `token` 对应的用户具备创建身份组权限。如果是机器人,要求被添加为管理员。 +- 参数为非必填,但至少需要传其中之一,默认为空或 0。 + + -> `love.forte.simbot.qguild.api.role.DeleteGuildRoleApi` +`love.forte.simbot.qguild.api.role.DeleteGuildRoleApi` + +删除频道身份组 + +用于删除频道 `guild_id` 下 `role_id` 对应的身份组。 + +需要使用的 `token` 对应的用户具备删除身份组权限。如果是机器人,要求被添加为管理员。 + -> `love.forte.simbot.qguild.api.role.GetGuildRoleListApi` +`love.forte.simbot.qguild.api.role.GetGuildRoleListApi` + +获取频道身份组列表 + +用于获取 `guild_id` 指定的频道下的身份组列表。 + + -> `love.forte.simbot.qguild.api.role.ModifyGuildRoleApi` +`love.forte.simbot.qguild.api.role.ModifyGuildRoleApi` + +修改频道身份组 + +用于修改频道 `guild_id` 下 `role_id` 指定的身份组。 + +- 需要使用的 `token` 对应的用户具备修改身份组权限。如果是机器人,要求被添加为管理员。 +- 接口会修改传入的字段,不传入的默认不会修改,至少要传入一个参数。 + + -> `love.forte.simbot.qguild.api.role.RemoveMemberRoleApi` +`love.forte.simbot.qguild.api.role.RemoveMemberRoleApi` + +删除频道身份组成员 + +用于将 用户 `user_id` 从 频道 `guild_id` 的 `role_id` 身份组中移除。 + +- 需要使用的 `token` 对应的用户具备删除身份组成员权限。如果是机器人,要求被添加为管理员。 +- 如果要删除的身份组 `ID` 是 [`5-子频道管理员`][love.forte.simbot.qguild.model.Role.DEFAULT_ID_CHANNEL_ADMIN], + 需要增加 `channel` 对象来指定具体是哪个子频道。 + + -> `love.forte.simbot.qguild.api.user.GetBotGuildListApi` +`love.forte.simbot.qguild.api.user.GetBotGuildListApi` + +获取用户频道列表 + +用于获取当前用户(机器人)所加入的频道列表,支持分页。 + +当 `HTTP Authorization` 中填入 `Bot Token` 是获取机器人的数据,填入 `Bearer Token` 则获取用户的数据。 + -> `love.forte.simbot.qguild.api.user.GetBotInfoApi` +`love.forte.simbot.qguild.api.user.GetBotInfoApi` + +获取用户详情 + +用于获取当前用户(机器人)详情。 + +由于`GetBotInfoApi] 本身为 `object` 类型, 因此 [ApiDescription] 由内部对象 [Description`提供而不是伴生对象。 + +[GetBotInfoApi] 得到的`User] 中,[User.isBot`始终为 `true`。 + + + + diff --git a/Writerside/topics/event.md b/Writerside/topics/event.md index 2706dbd5..9d0377a1 100644 --- a/Writerside/topics/event.md +++ b/Writerside/topics/event.md @@ -13,44 +13,503 @@ API 模块所有的事件封装类型都在包 `love.forte.simbot.qguild.event` 所有事件封装类型均继承密封类 `love.forte.simbot.qguild.event.Signal.Dispatch`。 - -子频道创建事件 CHANNEL_CREATE -子频道修改事件 CHANNEL_UPDATE -子频道删除事件 CHANNEL_DELETE -主题创建事件 FORUM_THREAD_CREATE -主题更新事件 FORUM_THREAD_UPDATE -主题删除事件 FORUM_THREAD_DELETE -帖子创建事件 FORUM_POST_CREATE -帖子删除事件 FORUM_POST_DELETE -回复创建事件 FORUM_REPLY_CREATE -回复删除事件 FORUM_REPLY_DELETE -帖子审核事件 FORUM_PUBLISH_AUDIT_RESULT -"开放"创建主题事件 OPEN_FORUM_THREAD_CREATE -"开放"更新主题事件 OPEN_FORUM_THREAD_UPDATE -"开放"删除主题事件 OPEN_FORUM_THREAD_DELETE -"开放"帖子创建(评论)事件 OPEN_FORUM_POST_CREATE -"开放"帖子删除(评论)事件 OPEN_FORUM_POST_DELETE -"开放"回复创建事件 OPEN_FORUM_REPLY_CREATE -"开放"回复删除事件 OPEN_FORUM_REPLY_DELETE -Bot加入频道事件 GUILD_CREATE -频道信息变更事件 GUILD_UPDATE -Bot因各种原因退出频道事件 GUILD_DELETE -新用户加入频道事件 GUILD_MEMBER_ADD -用户的频道属性发生变化事件 GUILD_MEMBER_UPDATE -用户离开频道事件 GUILD_MEMBER_REMOVE -收到公域at消息事件 AT_MESSAGE_CREATE + + + +`love.forte.simbot.qguild.event.Ready` + +事件类型名: `"READY"` + +鉴权成功之后,后台会下发的 Ready Event. + + + + +`love.forte.simbot.qguild.event.Resumed` + +事件类型名: `"RESUMED"` + +4.恢复连接 +恢复成功之后,就开始补发遗漏事件,所有事件补发完成之后,会下发一个 `Resumed Event` + + + + +`love.forte.simbot.qguild.event.ChannelDispatch` + +channel相关的事件类型。[data] 类型为 [EventChannel]。 + + + + +`love.forte.simbot.qguild.event.ChannelCreate` + +事件类型名: `"CHANNEL_CREATE"` + +子频道事件 CHANNEL_CREATE + +**发送时机** + +- 子频道被创建 + + + + +`love.forte.simbot.qguild.event.ChannelUpdate` + +事件类型名: `"CHANNEL_UPDATE"` + +子频道事件 CHANNEL_UPDATE + +**发送时机** + +- 子频道信息变更 + + + + +`love.forte.simbot.qguild.event.ChannelDelete` + +事件类型名: `"CHANNEL_DELETE"` + +子频道事件 CHANNEL_DELETE + +**发送时机** + +- 子频道被删除 + + + + +`love.forte.simbot.qguild.event.ForumDispatch` + +论坛事件(ForumEvent) + +**发送时机** + +用户在话题子频道内发帖、评论、回复评论时产生该事件 + +**主题事件** + +- FORUM_THREAD_CREATE +- FORUM_THREAD_UPDATE +- FORUM_THREAD_DELETE + 事件内容为`Thread`对象 + +**帖子事件** + +- FORUM_POST_CREATE +- FORUM_POST_DELETE + 事件内容为`Post`对象 + +**回复事件** + +- FORUM_REPLY_CREATE +- FORUM_REPLY_DELETE + 事件内容为`Reply`对象 + +**帖子审核事件** + +- FORUM_PUBLISH_AUDIT_RESULT + 事件内容为`AuditResult`对象 + + + + +`love.forte.simbot.qguild.event.ForumThreadDispatch` + +论坛事件:主题事件 + + + + +`love.forte.simbot.qguild.event.ForumThreadCreate` + +事件类型名: `"FORUM_THREAD_CREATE"` + +主题创建事件。 + + + + +`love.forte.simbot.qguild.event.ForumThreadUpdate` + +事件类型名: `"FORUM_THREAD_UPDATE"` + +主题更新事件。 + + + + +`love.forte.simbot.qguild.event.ForumThreadDelete` + +事件类型名: `"FORUM_THREAD_DELETE"` + +主题删除事件。 + + + + +`love.forte.simbot.qguild.event.ForumPostDispatch` + +论坛事件:帖子事件 + + + + +`love.forte.simbot.qguild.event.ForumPostCreate` + +事件类型名: `"FORUM_POST_CREATE"` + +帖子创建事件 + + + + +`love.forte.simbot.qguild.event.ForumPostDelete` + +事件类型名: `"FORUM_POST_DELETE"` + +帖子删除事件 + + + + +`love.forte.simbot.qguild.event.ForumReplyDispatch` + +论坛事件:回复事件 + + + + +`love.forte.simbot.qguild.event.ForumReplyCreate` + +事件类型名: `"FORUM_REPLY_CREATE"` + +回复创建事件 + + + + +`love.forte.simbot.qguild.event.ForumReplyDelete` + +事件类型名: `"FORUM_REPLY_DELETE"` + +回复删除事件 + + + + +`love.forte.simbot.qguild.event.ForumPublishAuditResult` + +事件类型名: `"FORUM_PUBLISH_AUDIT_RESULT"` + +帖子审核事件 + + + + +`love.forte.simbot.qguild.event.EventGuildDispatch` + +Guild相关事件类型。[data] 类型为 [EventGuild]。 + + + + +`love.forte.simbot.qguild.event.GuildCreate` + +事件类型名: `"GUILD_CREATE"` + +GUILD_CREATE + +**发送时机** + +- 机器人被加入到某个频道的时候 + + + + +`love.forte.simbot.qguild.event.GuildUpdate` + +事件类型名: `"GUILD_UPDATE"` + +GUILD_UPDATE + +**发送时机** + +- 频道信息变更 +- 事件内容为变更后的数据 + + + + +`love.forte.simbot.qguild.event.GuildDelete` + +事件类型名: `"GUILD_DELETE"` + +GUILD_DELETE + +**发送时机** + +- 频道被解散 +- 机器人被移除 +- 事件内容为变更前的数据 + + + + +`love.forte.simbot.qguild.event.GuildMemberAdd` + +事件类型名: `"GUILD_MEMBER_ADD"` + +`GUILD_MEMBER_ADD` + +**发送时机** + +- 新用户加入频道 + + + + +`love.forte.simbot.qguild.event.GuildMemberUpdate` + +事件类型名: `"GUILD_MEMBER_UPDATE"` + +`GUILD_MEMBER_UPDATE` + +**发送时机** + +- 用户的频道属性发生变化,如频道昵称,或者身份组 + + + + +`love.forte.simbot.qguild.event.GuildMemberRemove` + +事件类型名: `"GUILD_MEMBER_REMOVE"` + +`GUILD_MEMBER_REMOVE` + +**发送时机** + +- 用户离开频道 + + + + +`love.forte.simbot.qguild.event.MessageDispatch` + +与 `message` 相关的事件类型。[data] 类型为 [Message] + + + + +`love.forte.simbot.qguild.event.AtMessageCreate` + +事件类型名: `"AT_MESSAGE_CREATE"` + +消息事件 +`AT_MESSAGE_CREATE(intents PUBLIC_GUILD_MESSAGES)` + +**发送时机** + +- 用户发送消息,@当前机器人或回复机器人消息时 +- 为保障消息投递的速度,消息顺序我们虽然会尽量有序,但是并不保证是严格有序的, + 如开发者对消息顺序有严格有序的需求,可以自行缓冲消息事件之后,基于`seq`进行排序 + + -PUBLIC_MESSAGE_DELETE -此事件官网文档似乎没有详细说明,请慎重使用 + +`love.forte.simbot.qguild.event.PublicMessageDeleteCreate` + +事件类型名: `"PUBLIC_MESSAGE_DELETE"` + +消息事件 +`PUBLIC_MESSAGE_DELETE_TYPE` + + + + +`love.forte.simbot.qguild.event.DirectMessageCreate` + +事件类型名: `"DIRECT_MESSAGE_CREATE"` + +私信消息事件 +`DIRECT_MESSAGE_CREATE (intents DIRECT_MESSAGE)` + +**发送时机** + +- 用户通过私信发消息给机器人时 + + + + +`love.forte.simbot.qguild.event.MessageAuditedDispatch` + +与`MessageAudited] 相关的事件类型。[data`类型为 [MessageAudited]。 + + + + +`love.forte.simbot.qguild.event.MessageCreate` + +事件类型名: `"MESSAGE_CREATE"` + +发送消息事件,代表频道内的全部消息,而不只是 at 机器人的消息。内容与 AT_MESSAGE_CREATE 相同 + -私信消息事件 DIRECT_MESSAGE_CREATE -(私域)发送消息事件 MESSAGE_CREATE -MESSAGE_DELETE -此事件官网文档似乎没有详细说明,请慎重使用 + +`love.forte.simbot.qguild.event.MessageDelete` + +事件类型名: `"MESSAGE_DELETE"` + +删除(撤回)消息事件 + + + + +`love.forte.simbot.qguild.event.MessageAuditPass` + +事件类型名: `"MESSAGE_AUDIT_PASS"` + +消息审核事件 +`MESSAGE_AUDIT_PASS(intents MESSAGE_AUDIT)` + +**发送时机** + +- 消息审核通过 + + + + +`love.forte.simbot.qguild.event.MessageAuditReject` + +事件类型名: `"MESSAGE_AUDIT_REJECT"` + +消息审核事件 +`MESSAGE_AUDIT_REJECT(intents MESSAGE_AUDIT)` + +**发送时机** + +- 消息审核不通过 + + + + +`love.forte.simbot.qguild.event.OpenForumDispatch` + +开放论坛事件(OpenForumEvent) 相关的事件父类。 + +**发送时机** + +用户在话题子频道内发帖、评论、回复评论时产生该事件 + +**主题事件** + +- OPEN_FORUM_THREAD_CREATE +- OPEN_FORUM_THREAD_UPDATE +- OPEN_FORUM_THREAD_DELETE + 参考 [OpenForumThreadDispatch] + +**帖子(评论)事件** + +- OPEN_FORUM_POST_CREATE +- OPEN_FORUM_POST_DELETE + 参考 [OpenForumPostDispatch] + +**回复事件** + +- OPEN_FORUM_REPLY_CREATE +- OPEN_FORUM_REPLY_DELETE + 参考 [OpenForumReplyDispatch] + + + + +`love.forte.simbot.qguild.event.OpenForumThreadDispatch` + +开放论坛事件的 **_主题事件_**。 + + + + +`love.forte.simbot.qguild.event.OpenForumThreadCreate` + +事件类型名: `"OPEN_FORUM_THREAD_CREATE"` + +主题事件:创建主题 + + + + +`love.forte.simbot.qguild.event.OpenForumThreadUpdate` + +事件类型名: `"OPEN_FORUM_THREAD_UPDATE"` + +主题事件:更新主题 + + + + +`love.forte.simbot.qguild.event.OpenForumThreadDelete` + +事件类型名: `"OPEN_FORUM_THREAD_DELETE"` + +主题事件:删除主题 + + + + +`love.forte.simbot.qguild.event.OpenForumPostDispatch` + +开放论坛事件的 **_帖子(评论)事件_**。 + + + + +`love.forte.simbot.qguild.event.OpenForumPostCreate` + +事件类型名: `"OPEN_FORUM_POST_CREATE"` + +帖子事件:创建帖子(评论) + + + + +`love.forte.simbot.qguild.event.OpenForumPostDelete` + +事件类型名: `"OPEN_FORUM_POST_DELETE"` + +帖子事件:删除帖子(评论) + + + + +`love.forte.simbot.qguild.event.OpenForumReplyDispatch` + +开放论坛事件的 **_回复事件_**。 + + + + +`love.forte.simbot.qguild.event.OpenForumReplyCreate` + +事件类型名: `"OPEN_FORUM_REPLY_CREATE"` + +回复事件:创建回复 + + + + +`love.forte.simbot.qguild.event.OpenForumReplyDelete` + +事件类型名: `"OPEN_FORUM_REPLY_DELETE"` + +回复事件:删除回复 + -消息审核通过事件 MESSAGE_AUDIT_PASS -消息审核不通过事件 MESSAGE_AUDIT_REJECT + API 模块事件封装可以使用在 **标准库模块 (stdlib)** 中,使用 `Bot` 类型对他们进行监听与处理。 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5bdae927..7684693e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -13,6 +13,10 @@ reactor = "3.6.2" simbot = "4.0.0-dev15" suspendTransform = "0.7.0-beta1" gradleCommon = "0.2.0" +# ksp +ksp = "1.9.22-1.0.17" +# https://square.github.io/kotlinpoet/ +kotlinPoet = "1.16.0" [libraries] # simbot @@ -92,6 +96,11 @@ log4j-slf4j2 = { group = "org.apache.logging.log4j", name = "log4j-slf4j2-impl", openjdk-jmh-core = { group = "org.openjdk.jmh", name = "jmh-core", version.ref = "openjdk-jmh" } openjdk-jmh-generator-annprocess = { group = "org.openjdk.jmh", name = "jmh-generator-annprocess", version.ref = "openjdk-jmh" } +# ksp +ksp = { module = "com.google.devtools.ksp:symbol-processing-api", version.ref = "ksp" } +# https://square.github.io/kotlinpoet/interop-ksp/ +kotlinPoet-ksp = { module = "com.squareup:kotlinpoet-ksp", version.ref = "kotlinPoet" } + # reactor reactor-core = { group = "io.projectreactor", name = "reactor-core", version.ref = "reactor" } @@ -106,6 +115,9 @@ gradle-common-core = { group = "love.forte.gradle.common", name = "gradle-common gradle-common-multiplatform = { group = "love.forte.gradle.common", name = "gradle-common-kotlin-multiplatform", version.ref = "gradleCommon" } gradle-common-publication = { group = "love.forte.gradle.common", name = "gradle-common-publication", version.ref = "gradleCommon" } +[plugins] +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } + [bundles] gradle-common = ["gradle-common-core", "gradle-common-multiplatform", "gradle-common-publication"] dokka = ["dokka-plugin", "dokka-base"] diff --git a/internal-processors/api-reader/build.gradle.kts b/internal-processors/api-reader/build.gradle.kts new file mode 100644 index 00000000..5f97a9cb --- /dev/null +++ b/internal-processors/api-reader/build.gradle.kts @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +import org.jetbrains.kotlin.gradle.dsl.JvmTarget + +plugins { + kotlin("jvm") +} + +repositories { + mavenCentral() +} + +kotlin { + jvmToolchain(11) + compilerOptions { + javaParameters = true + jvmTarget.set(JvmTarget.JVM_11) + } +} + +configJavaCompileWithModule() + +dependencies { +// implementation(project(":annotations")) + api(libs.ksp) + api(libs.kotlinPoet.ksp) + testImplementation(kotlin("test-junit5")) +} + +tasks.getByName("test") { + useJUnitPlatform() +} + diff --git a/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessor.kt b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessor.kt new file mode 100644 index 00000000..10d395a7 --- /dev/null +++ b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessor.kt @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package qg.internal.processors.apireader + +import com.google.devtools.ksp.KspExperimental +import com.google.devtools.ksp.getKotlinClassByName +import com.google.devtools.ksp.isAbstract +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.processing.SymbolProcessorEnvironment +import com.google.devtools.ksp.symbol.KSAnnotated +import com.google.devtools.ksp.symbol.KSClassDeclaration +import java.io.BufferedWriter +import java.io.File +import java.nio.file.StandardOpenOption +import kotlin.io.path.bufferedWriter + +private const val QG_API_CLASS_NAME = "love.forte.simbot.qguild.api.QQGuildApi" + +private const val API_READ_TARGET_FILE_OPTION_KEY = "qg.api.finder.api.output" + +abstract class ReaderProcessor(private val environment: SymbolProcessorEnvironment) : SymbolProcessor { + abstract val optionName: String + abstract val targetClassName: String + + @OptIn(KspExperimental::class) + override fun process(resolver: Resolver): List { + val targetFilePath: String? = environment.options[optionName] + + val targetFile = File(targetFilePath ?: run { + val msg = "target output file option ['$optionName'] is null!" + environment.logger.error(msg) + throw NullPointerException(msg) + }) + + environment.logger.info("target output file: ${targetFile.absolutePath}") + val targetClass = resolver.getKotlinClassByName(targetClassName) + environment.logger.info("apiClass: $targetClassName") + targetClass ?: return emptyList() + + val targetClasses = resolver.getAllFiles().flatMap { it.declarations } + .filterIsInstance() + .filter { + targetClass.asStarProjectedType().isAssignableFrom(it.asStarProjectedType()) + } + .filter { !it.isAbstract() } + .toList() + + if (!targetFile.exists()) { + targetFile.parentFile.mkdirs() + } else { + targetFile.delete() + } + + targetFile.toPath().bufferedWriter( + options = arrayOf( + StandardOpenOption.WRITE, + StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.CREATE + ) + ).use { writer -> + writer.writeDeflistTo(targetClasses) + } + + + return emptyList() + } + +} + +/** + * + * @author ForteScarlet + */ +class ApiReaderProcessor(private val environment: SymbolProcessorEnvironment) : ReaderProcessor(environment) { + override val optionName: String = API_READ_TARGET_FILE_OPTION_KEY + override val targetClassName: String = QG_API_CLASS_NAME +} + +//class EventReaderProcessor(private val environment: SymbolProcessorEnvironment) : ReaderProcessor(environment) { +// override val optionName: String = EVENT_READ_TARGET_FILE_OPTION_KEY +// override val targetClassName: String = QG_EVENT_CLASS_NAME +//} + +private fun BufferedWriter.writeDeflistTo(list: List) { + write("\n") + list.forEach { declaration -> + write("\n") + newLine() + write("`${declaration.packageName.asString()}.${declaration.simpleName.asString()}`\n") + newLine() + + val lines = declaration.docString?.trim() + ?.lines() + ?.map { it.trim() } + ?.filter { !it.startsWith('@') } + ?.map { line -> + line + .replace(linkRegex, "$1") + .replace(refRegex, "`$1`") + .replace(titleRegex, "\n**$1**\n") + } + + if (lines != null) { + lines.forEach { line -> + write(line) + newLine() + } + newLine() + } + + newLine() + + val sealedSub = declaration.getSealedSubclasses().toList() + if (sealedSub.isNotEmpty()) { + writeDeflistTo(sealedSub) + } + + write("\n") + } + newLine() + write("\n") + +} diff --git a/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessorProvider.kt b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessorProvider.kt new file mode 100644 index 00000000..22c03f8e --- /dev/null +++ b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessorProvider.kt @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package qg.internal.processors.apireader + +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.processing.SymbolProcessorEnvironment +import com.google.devtools.ksp.processing.SymbolProcessorProvider +import com.google.devtools.ksp.symbol.KSAnnotated + +private object NonProcessor : SymbolProcessor { + override fun process(resolver: Resolver): List = emptyList() +} + +private const val ENABLE_OPTION = "qg.api.reader.enable" + +/** + * + * @author ForteScarlet + */ +class ApiReaderProcessorProvider : SymbolProcessorProvider { + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = + if (environment.options[ENABLE_OPTION].toBoolean()) + ApiReaderProcessor(environment) + else NonProcessor +} + +/** + * + * @author ForteScarlet + */ +class EventReaderProcessorProvider : SymbolProcessorProvider { + override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor = + if (environment.options[ENABLE_OPTION].toBoolean()) + EventReaderProcessor(environment) + else NonProcessor +} diff --git a/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/EventReaderProcessor.kt b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/EventReaderProcessor.kt new file mode 100644 index 00000000..9293d744 --- /dev/null +++ b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/EventReaderProcessor.kt @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package qg.internal.processors.apireader + +import com.google.devtools.ksp.KspExperimental +import com.google.devtools.ksp.getKotlinClassByName +import com.google.devtools.ksp.isAbstract +import com.google.devtools.ksp.processing.Resolver +import com.google.devtools.ksp.processing.SymbolProcessor +import com.google.devtools.ksp.processing.SymbolProcessorEnvironment +import com.google.devtools.ksp.symbol.KSAnnotated +import com.google.devtools.ksp.symbol.KSClassDeclaration +import java.io.BufferedWriter +import java.io.File +import java.nio.file.StandardOpenOption +import kotlin.io.path.bufferedWriter + +private const val QG_EVENT_CLASS_NAME = "love.forte.simbot.qguild.event.Signal.Dispatch" +private const val EVENT_READ_TARGET_FILE_OPTION_KEY = "qg.api.finder.event.output" + + + +/** + * + * @author ForteScarlet + */ +class EventReaderProcessor(private val environment: SymbolProcessorEnvironment) : SymbolProcessor { + @OptIn(KspExperimental::class) + override fun process(resolver: Resolver): List { + val targetFilePath: String? = environment.options[EVENT_READ_TARGET_FILE_OPTION_KEY] + + val targetFile = File(targetFilePath ?: run { + val msg = "target output file option ['$EVENT_READ_TARGET_FILE_OPTION_KEY'] is null!" + environment.logger.error(msg) + throw NullPointerException(msg) + }) + + environment.logger.info("target output file: ${targetFile.absolutePath}") + val targetClass = resolver.getKotlinClassByName(QG_EVENT_CLASS_NAME) + environment.logger.info("apiClass: $QG_EVENT_CLASS_NAME") + targetClass ?: return emptyList() + + val targetClasses = resolver.getAllFiles().flatMap { it.declarations } + .filterIsInstance() + .filter { + targetClass.asStarProjectedType().isAssignableFrom(it.asStarProjectedType()) + } + .filter { !it.isAbstract() } + .toList() + + if (!targetFile.exists()) { + targetFile.parentFile.mkdirs() + } else { + targetFile.delete() + } + + targetFile.toPath().bufferedWriter( + options = arrayOf( + StandardOpenOption.WRITE, + StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.CREATE + ) + ).use { writer -> + writer.writeDeflistTo(targetClasses) + } + + return emptyList() + } + + private fun BufferedWriter.writeDeflistTo(list: List) { + write("\n") + list.forEach { declaration -> + write("\n") + newLine() + write("`${declaration.packageName.asString()}.${declaration.simpleName.asString()}`\n") + newLine() + val serName = declaration.annotations.find { + it.shortName.asString() == "SerialName" + }?.arguments?.find { it.name?.asString() == "value" }?.value?.toString() + + if (serName != null) { + write("事件类型名: `\"${serName}\"`\n") + newLine() + } + + val lines = declaration.docString?.trim() + ?.lines() + ?.filter { it.isNotBlank() } + ?.map { it.trim() } + ?.filter { !it.startsWith('@') } + ?.map { line -> + line + .replace(linkRegex, "$1") + .replace(refRegex, "`$1`") + .replace(titleRegex, "\n**$1**\n") + } + + if (lines != null) { + lines.forEach { line -> + write(line) + newLine() + } + newLine() + } + + write("\n") + } + newLine() + write("\n") + + } +} + diff --git a/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/utils.kt b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/utils.kt new file mode 100644 index 00000000..46de0922 --- /dev/null +++ b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/utils.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-qq-guild. + * + * simbot-component-qq-guild is free software: you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation, + * either version 3 of the License, or (at your option) any later version. + * + * simbot-component-qq-guild is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with simbot-component-qq-guild. + * If not, see . + */ + +package qg.internal.processors.apireader + +val linkRegex = Regex("\\[(?.+)]\\((?.+)\\)") +val refRegex = Regex(" \\[(.+)] ") +val titleRegex = Regex("#+ (.+)") diff --git a/internal-processors/api-reader/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider b/internal-processors/api-reader/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider new file mode 100644 index 00000000..a0451b37 --- /dev/null +++ b/internal-processors/api-reader/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider @@ -0,0 +1,2 @@ +qg.internal.processors.apireader.ApiReaderProcessorProvider +qg.internal.processors.apireader.EventReaderProcessorProvider diff --git a/settings.gradle.kts b/settings.gradle.kts index 50389de1..ea6e0d22 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,6 +17,8 @@ rootProject.name = "qq-guild" +// internals +include(":internal-processors:api-reader") //include(":builder-generator") include(":simbot-component-qq-guild-api") diff --git a/simbot-component-qq-guild-api/build.gradle.kts b/simbot-component-qq-guild-api/build.gradle.kts index 4863a6ac..dc5f7da8 100644 --- a/simbot-component-qq-guild-api/build.gradle.kts +++ b/simbot-component-qq-guild-api/build.gradle.kts @@ -20,11 +20,13 @@ import love.forte.gradle.common.kotlin.multiplatform.applyTier1 import love.forte.gradle.common.kotlin.multiplatform.applyTier2 import love.forte.gradle.common.kotlin.multiplatform.applyTier3 import love.forte.plugin.suspendtrans.gradle.withKotlinTargets +import util.isCi plugins { kotlin("multiplatform") kotlin("plugin.serialization") `qq-guild-dokka-partial-configure` + alias(libs.plugins.ksp) } setup(P.ComponentQQGuild) @@ -112,3 +114,12 @@ kotlin { } +dependencies { + add("kspJvm", project(":internal-processors:api-reader")) +} + +ksp { + arg("qg.api.reader.enable", (!isCi).toString()) + arg("qg.api.finder.api.output", rootDir.resolve("generated-docs/api-list.md").absolutePath) + arg("qg.api.finder.event.output", rootDir.resolve("generated-docs/event-list.md").absolutePath) +} From 81e9d8cced7160e3c1c8bda03001bd1ec4f1c55e Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Sun, 28 Jan 2024 22:38:20 +0800 Subject: [PATCH 45/71] =?UTF-8?q?=E9=83=A8=E5=88=86=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../love/forte/simbot/qguild/stdlib/EventProcessor.jvm.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.jvm.kt b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.jvm.kt index eb3fa937..fa07e056 100644 --- a/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.jvm.kt +++ b/simbot-component-qq-guild-stdlib/src/jvmMain/kotlin/love/forte/simbot/qguild/stdlib/EventProcessor.jvm.kt @@ -25,7 +25,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.future.await import kotlinx.coroutines.runInterruptible import love.forte.simbot.qguild.event.Signal -import love.forte.simbot.suspendrunner.runInBlocking import org.jetbrains.annotations.Blocking import org.jetbrains.annotations.NonBlocking import java.util.concurrent.CompletableFuture @@ -47,8 +46,6 @@ public fun interface JBlockEventProcessor : EventProcessor { @JvmSynthetic override suspend fun Signal.Dispatch.invoke(raw: String) { - runInBlocking { } - runInterruptible(Dispatchers.IO) { block(this, raw) } } } From d949d5f66c967572d82e01d3390fa3b5009deef9 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 29 Jan 2024 23:01:03 +0800 Subject: [PATCH 46/71] =?UTF-8?q?core=E6=A8=A1=E5=9D=97=E5=B0=86=E5=AF=B9?= =?UTF-8?q?=20simbot-api=20=E7=9A=84=E5=BC=95=E7=94=A8=E8=B0=83=E6=95=B4?= =?UTF-8?q?=E4=B8=BA=20compileOnly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- buildSrc/src/main/kotlin/P.kt | 2 +- simbot-component-qq-guild-core/build.gradle.kts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/buildSrc/src/main/kotlin/P.kt b/buildSrc/src/main/kotlin/P.kt index d98a5e74..37e948dd 100644 --- a/buildSrc/src/main/kotlin/P.kt +++ b/buildSrc/src/main/kotlin/P.kt @@ -59,7 +59,7 @@ object P { override val homepage: String get() = HOMEPAGE - private val baseVersion = v(4, 0, 0) - v("dev7") + private val baseVersion = v(4, 0, 0) - v("dev8") val snapshotVersion = baseVersion - Version.SNAPSHOT override val version = if (isSnapshot()) snapshotVersion else baseVersion diff --git a/simbot-component-qq-guild-core/build.gradle.kts b/simbot-component-qq-guild-core/build.gradle.kts index eb4c1bac..aa763275 100644 --- a/simbot-component-qq-guild-core/build.gradle.kts +++ b/simbot-component-qq-guild-core/build.gradle.kts @@ -57,7 +57,7 @@ kotlin { sourceSets { commonMain.dependencies { - api(libs.simbot.api) + compileOnly(libs.simbot.api) api(project(":simbot-component-qq-guild-stdlib")) compileOnly(libs.simbot.common.annotations) // ktor @@ -88,10 +88,12 @@ kotlin { } jsMain.dependencies { + implementation(libs.simbot.api) api(libs.simbot.common.annotations) } nativeMain.dependencies { + implementation(libs.simbot.api) api(libs.simbot.common.annotations) } From e76c76d8d8c8f721c926d15c5a7d23d863d7f561 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Tue, 30 Jan 2024 22:38:43 +0800 Subject: [PATCH 47/71] =?UTF-8?q?pref:=20=E4=BC=98=E5=8C=96API=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E4=B8=AD=E5=8F=AF=E8=83=BD=E5=87=BA=E7=8E=B0=E7=9A=84?= =?UTF-8?q?=E5=AF=B9=E7=BB=93=E6=9E=9C=E7=9A=84=E5=8F=8D=E5=BA=8F=E5=88=97?= =?UTF-8?q?=E5=8C=96=E5=BC=82=E5=B8=B8=E7=9A=84=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../simbot/qguild/QQGuildApiException.kt | 27 ++++++++++++++++--- .../forte/simbot/qguild/api/ApiRequests.kt | 24 +++++++++++++---- .../forte/simbot/qguild/api/QQGuildApi.kt | 8 ++++-- 3 files changed, 49 insertions(+), 10 deletions(-) diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuildApiException.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuildApiException.kt index fd35f906..d57d3c90 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuildApiException.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/QQGuildApiException.kt @@ -41,7 +41,19 @@ public open class QQGuildApiException : RuntimeException { public val value: Int public val description: String - public constructor(value: Int, description: String) : super("$value: $description") { + public constructor(value: Int, description: String) : this( + value = value, + description = description, + message = "$value: $description" + ) + + public constructor( + info: ErrInfo?, + value: Int, + description: String, + ) : this(value = value, description = description, message = "$value: $description; response info: $info") + + public constructor(value: Int, description: String, message: String) : super(message) { this.info = null this.value = value this.description = description @@ -51,14 +63,23 @@ public open class QQGuildApiException : RuntimeException { info: ErrInfo?, value: Int, description: String, - ) : super("$value: $description; response info: $info") { + message: String = "$value: $description; response info: $info" + ) : super(message) { this.info = info this.value = value this.description = description } - } +/** + * 当 API 相应结果反序列化失败 + */ +public open class QQGuildResultSerializationException( + value: Int, + description: String, + message: String = "$value: $description" +) : QQGuildApiException(value, description, message) + /** * @suppress */ diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt index 0442d987..ab537ef7 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt @@ -28,16 +28,16 @@ import io.ktor.client.statement.* import io.ktor.client.utils.* import io.ktor.http.* import io.ktor.http.content.* +import kotlinx.serialization.DeserializationStrategy import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.SerializationException import kotlinx.serialization.StringFormat import kotlinx.serialization.builtins.serializer import kotlinx.serialization.descriptors.StructureKind import kotlinx.serialization.json.Json import love.forte.simbot.common.serialization.guessSerializer import love.forte.simbot.logger.isDebugEnabled -import love.forte.simbot.qguild.ErrInfo -import love.forte.simbot.qguild.QQGuild -import love.forte.simbot.qguild.QQGuildApiException +import love.forte.simbot.qguild.* import kotlin.contracts.ExperimentalContracts import kotlin.contracts.InvocationKind import kotlin.contracts.contract @@ -212,16 +212,30 @@ internal fun checkStatus( status: HttpStatusCode, resp: HttpResponse, ) { + // 如果出现了序列化异常,抛出 QQGuildResultSerializationException + fun decodeFromStringWithCatch(deserializer: DeserializationStrategy, string: String): T { + return try { + decoder.decodeFromString(deserializer, remainingText) + } catch (serEx: SerializationException) { + // 反序列化异常 + throw QQGuildResultSerializationException(status.value, status.description, "Response(status=${status.value}) deserialization failed: ${serEx.message}").also { + it.initCause0(serEx) + } + } + } + + // TODO 201,202 异步操作成功,虽然说成功,但是会返回一个 error body,需要特殊处理 if (!status.isSuccess()) { - val info = decoder.decodeFromString(ErrInfo.serializer(), remainingText) + val info = decodeFromStringWithCatch(ErrInfo.serializer(), remainingText) + // throw err throw QQGuildApiException(info, status.value, status.description) } // 202 消息审核 if (status == HttpStatusCode.Accepted) { - val info = decoder.decodeFromString(ErrInfo.serializer(), remainingText) + val info = decodeFromStringWithCatch(ErrInfo.serializer(), remainingText) // maybe audited if (MessageAuditedException.isAuditResultCode(info.code)) { throw MessageAuditedException( diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.kt index f8c7894a..dd4be9c7 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/QQGuildApi.kt @@ -25,8 +25,12 @@ import love.forte.simbot.logger.Logger import love.forte.simbot.logger.LoggerFactory import love.forte.simbot.qguild.QQGuild -@PublishedApi -internal val apiLogger: Logger = LoggerFactory.getLogger("love.forte.simbot.qguild.api") +/** + * 用于在 [QQGuildApi.request] 及其衍生API中输出相关日志的 [Logger]。 + * 开启 `love.forte.simbot.qguild.api` 的 `DEBUG` 级别日志可以得到更多在 API 请求过程中产生的信息。 + * 注意:这可能会暴露其中的参数、返回值等,请注意保护敏感信息。 + */ +public val apiLogger: Logger = LoggerFactory.getLogger("love.forte.simbot.qguild.api") /** * [有关 traceID](https://bot.q.qq.com/wiki/develop/api/openapi/error/error.html#%E6%9C%89%E5%85%B3-traceid) From 85ce5076a10c74f9ddf495d544570f6b098e874d Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Wed, 31 Jan 2024 02:45:36 +0800 Subject: [PATCH 48/71] =?UTF-8?q?test:=20=E4=BC=98=E5=8C=96API=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E7=BB=93=E6=9E=9C=E5=8F=8D=E5=BA=8F=E5=88=97=E5=8C=96?= =?UTF-8?q?=E4=BB=A5=E5=8F=8A=E6=A0=A1=E9=AA=8C=E7=9A=84=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=AE=8C=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/qodana_code_quality.yml | 2 +- gradle/libs.versions.toml | 1 + .../build.gradle.kts | 2 + .../forte/simbot/qguild/api/ApiRequests.kt | 26 ++++++-- .../ApiResultSerializationCheckTests.kt | 64 +++++++++++++++++++ 5 files changed, 89 insertions(+), 6 deletions(-) create mode 100644 simbot-component-qq-guild-api/src/commonTest/kotlin/ApiResultSerializationCheckTests.kt diff --git a/.github/workflows/qodana_code_quality.yml b/.github/workflows/qodana_code_quality.yml index 65a91fbc..f68a0a89 100644 --- a/.github/workflows/qodana_code_quality.yml +++ b/.github/workflows/qodana_code_quality.yml @@ -15,6 +15,6 @@ jobs: with: fetch-depth: 0 - name: 'Qodana Scan' - uses: JetBrains/qodana-action@v2023.2 + uses: JetBrains/qodana-action@v2023.3.1 env: QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7684693e..21d88355 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -73,6 +73,7 @@ ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = ktor-client-cio = { group = "io.ktor", name = "ktor-client-cio", version.ref = "ktor" } ktor-client-js = { group = "io.ktor", name = "ktor-client-js", version.ref = "ktor" } ktor-client-java = { group = "io.ktor", name = "ktor-client-java", version.ref = "ktor" } +ktor-client-mock = { group = "io.ktor", name = "ktor-client-mock", version.ref = "ktor" } # for linuxX64, macosX64, macosArm64, mingwX64 # see https://ktor.io/docs/http-client-engines.html#curl diff --git a/simbot-component-qq-guild-api/build.gradle.kts b/simbot-component-qq-guild-api/build.gradle.kts index dc5f7da8..151977ce 100644 --- a/simbot-component-qq-guild-api/build.gradle.kts +++ b/simbot-component-qq-guild-api/build.gradle.kts @@ -83,6 +83,8 @@ kotlin { commonTest.dependencies { implementation(kotlin("test")) implementation(libs.kotlinx.coroutines.test) + // https://ktor.io/docs/http-client-testing.html + implementation(libs.ktor.client.mock) } jvmMain.dependencies { diff --git a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt index ab537ef7..1a9bebf3 100644 --- a/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt +++ b/simbot-component-qq-guild-api/src/commonMain/kotlin/love/forte/simbot/qguild/api/ApiRequests.kt @@ -186,7 +186,19 @@ public suspend fun QQGuildApi.requestData( checkStatus(text, QQGuild.DefaultJson, resp.status, resp) - return decodeResponse(decoder, text) + return try { + decodeResponse(decoder, text) + } catch (serEx: SerializationException) { + val status = resp.status + // 反序列化异常 + throw QQGuildResultSerializationException( + status.value, + status.description, + "Response(status=${status.value}) deserialization failed: ${serEx.message}" + ).also { + it.initCause0(serEx) + } + } } @@ -213,12 +225,16 @@ internal fun checkStatus( resp: HttpResponse, ) { // 如果出现了序列化异常,抛出 QQGuildResultSerializationException - fun decodeFromStringWithCatch(deserializer: DeserializationStrategy, string: String): T { + fun decodeFromStringWithCatch(deserializer: DeserializationStrategy): T { return try { decoder.decodeFromString(deserializer, remainingText) } catch (serEx: SerializationException) { // 反序列化异常 - throw QQGuildResultSerializationException(status.value, status.description, "Response(status=${status.value}) deserialization failed: ${serEx.message}").also { + throw QQGuildResultSerializationException( + status.value, + status.description, + "Response(status=${status.value}) deserialization failed: ${serEx.message}" + ).also { it.initCause0(serEx) } } @@ -227,7 +243,7 @@ internal fun checkStatus( // TODO 201,202 异步操作成功,虽然说成功,但是会返回一个 error body,需要特殊处理 if (!status.isSuccess()) { - val info = decodeFromStringWithCatch(ErrInfo.serializer(), remainingText) + val info = decodeFromStringWithCatch(ErrInfo.serializer()) // throw err throw QQGuildApiException(info, status.value, status.description) @@ -235,7 +251,7 @@ internal fun checkStatus( // 202 消息审核 if (status == HttpStatusCode.Accepted) { - val info = decodeFromStringWithCatch(ErrInfo.serializer(), remainingText) + val info = decodeFromStringWithCatch(ErrInfo.serializer()) // maybe audited if (MessageAuditedException.isAuditResultCode(info.code)) { throw MessageAuditedException( diff --git a/simbot-component-qq-guild-api/src/commonTest/kotlin/ApiResultSerializationCheckTests.kt b/simbot-component-qq-guild-api/src/commonTest/kotlin/ApiResultSerializationCheckTests.kt new file mode 100644 index 00000000..8e8d9b24 --- /dev/null +++ b/simbot-component-qq-guild-api/src/commonTest/kotlin/ApiResultSerializationCheckTests.kt @@ -0,0 +1,64 @@ +package test + +import io.ktor.client.* +import io.ktor.client.engine.mock.* +import io.ktor.client.statement.* +import io.ktor.http.* +import io.ktor.utils.io.* +import kotlinx.coroutines.test.runTest +import kotlinx.serialization.SerializationException +import love.forte.simbot.qguild.QQGuild +import love.forte.simbot.qguild.QQGuildResultSerializationException +import love.forte.simbot.qguild.api.GatewayApis +import love.forte.simbot.qguild.api.channel.GetChannelApi +import love.forte.simbot.qguild.api.checkStatus +import love.forte.simbot.qguild.api.request +import love.forte.simbot.qguild.api.requestData +import kotlin.test.Test +import kotlin.test.assertFails +import kotlin.test.assertIs + + +/** + * + * @author ForteScarlet + */ +class ApiResultSerializationCheckTests { + + @Test + fun statusDeserializationHtmlBadResultTest() = runTest { + val client = HttpClient( + MockEngine.invoke { // request -> + respond( + content = ByteReadChannel(""""""), + status = HttpStatusCode.BadRequest, +// headers = headersOf(HttpHeaders.ContentType, "application/json") + ) + }) { + + } + + val resp = GatewayApis.Normal.request(client, "test") + val ex = assertFails { checkStatus(resp.bodyAsText(), QQGuild.DefaultJson, resp.status, resp) } + assertIs(ex) + assertIs(ex.cause ?: ex.suppressedExceptions.firstOrNull()) + } + + @Test + fun statusDeserializationHtmlOKResultTest() = runTest { + val client = HttpClient( + MockEngine.invoke { // request -> + respond( + content = ByteReadChannel(""""""), + status = HttpStatusCode.OK, + ) + }) { + + } + + val ex = assertFails { GetChannelApi.create("test").requestData(client, "test") } + assertIs(ex) + assertIs(ex.cause ?: ex.suppressedExceptions.firstOrNull()) + } + +} From 997bdcfe7229c6de0249ac27b3863cd73766dfad Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Sun, 4 Feb 2024 17:58:55 +0800 Subject: [PATCH 49/71] update website docker version --- .github/workflows/deploy-v4-website.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-v4-website.yml b/.github/workflows/deploy-v4-website.yml index 9fe44509..2c06bbaf 100644 --- a/.github/workflows/deploy-v4-website.yml +++ b/.github/workflows/deploy-v4-website.yml @@ -24,7 +24,7 @@ env: # Replace HI with the ID of the instance in capital letters ARTIFACT: webHelpD2-all.zip # Writerside docker image version - DOCKER_VERSION: 232.10275 + DOCKER_VERSION: 233.14272 # Add the variable below to upload Algolia indexes # Replace HI with the ID of the instance in capital letters # ALGOLIA_ARTIFACT: algolia-indexes-HI.zip From 0789068a282604b0443669a67efb241795c2ac5a Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Sun, 4 Feb 2024 21:34:24 +0800 Subject: [PATCH 50/71] Upgrade CI config and simbot version in doc --- .github/workflows/deploy-v4-website.yml | 2 +- .github/workflows/doc-test-branch.yml | 2 +- .github/workflows/publish-release.yml | 22 +++++++++++----------- .github/workflows/publish-snapshot.yml | 12 ++++++------ .github/workflows/qodana_code_quality.yml | 2 +- .github/workflows/test-branch.yml | 6 +++--- Writerside/v.list | 2 +- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/.github/workflows/deploy-v4-website.yml b/.github/workflows/deploy-v4-website.yml index 2c06bbaf..ee0963e2 100644 --- a/.github/workflows/deploy-v4-website.yml +++ b/.github/workflows/deploy-v4-website.yml @@ -35,7 +35,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Build Writerside docs using Docker uses: JetBrains/writerside-github-action@v4 diff --git a/.github/workflows/doc-test-branch.yml b/.github/workflows/doc-test-branch.yml index e151d1e2..bec2070e 100644 --- a/.github/workflows/doc-test-branch.yml +++ b/.github/workflows/doc-test-branch.yml @@ -15,7 +15,7 @@ jobs: name: Build Website runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: actions/setup-node@v3 with: node-version: 16.x diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index a190d549..fcfa0351 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -26,11 +26,11 @@ jobs: steps: # 检出仓库代码 - name: Check Out Repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 # setup Java - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 @@ -38,14 +38,14 @@ jobs: # setup Gradle - name: Gradle Run Test - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@v3 with: gradle-version: 8.5 arguments: assemble test -Porg.gradle.daemon=false # setup Gradle - name: Publish Release - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@v3 with: gradle-version: 8.5 arguments: | @@ -71,7 +71,7 @@ jobs: steps: # 检出仓库代码 - name: Check Out Repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 # Create gitHub release - name: Create Github Release @@ -92,18 +92,18 @@ jobs: steps: # 检出仓库代码 - name: Check out repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 # setup Java - name: Setup java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 # setup Gradle - name: Gradle publish snapshot - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@v3 with: gradle-version: 8.5 arguments: | @@ -126,20 +126,20 @@ jobs: steps: # 检出仓库代码 - name: Check out repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: persist-credentials: false fetch-depth: 0 # setup Java - name: Setup java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 # setup Gradle - name: Gradle generate documentation - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@v3 with: gradle-version: 8.5 arguments: | diff --git a/.github/workflows/publish-snapshot.yml b/.github/workflows/publish-snapshot.yml index 280af81d..e79e2dcb 100644 --- a/.github/workflows/publish-snapshot.yml +++ b/.github/workflows/publish-snapshot.yml @@ -43,18 +43,18 @@ jobs: steps: # 检出仓库代码 - name: Check out repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 # setup Java - name: Setup java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 # setup Gradle - name: Gradle test and publish snapshot - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@v3 with: gradle-version: 8.5 arguments: | @@ -73,20 +73,20 @@ jobs: steps: # 检出仓库代码 - name: Check out repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: persist-credentials: false fetch-depth: 0 # setup Java - name: Setup java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 # setup Gradle - name: Gradle generate documentation - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@v3 with: gradle-version: 8.5 arguments: | diff --git a/.github/workflows/qodana_code_quality.yml b/.github/workflows/qodana_code_quality.yml index f68a0a89..b7b71453 100644 --- a/.github/workflows/qodana_code_quality.yml +++ b/.github/workflows/qodana_code_quality.yml @@ -11,7 +11,7 @@ jobs: qodana: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 - name: 'Qodana Scan' diff --git a/.github/workflows/test-branch.yml b/.github/workflows/test-branch.yml index 0e798f52..d57f18b5 100644 --- a/.github/workflows/test-branch.yml +++ b/.github/workflows/test-branch.yml @@ -27,15 +27,15 @@ jobs: os: [ macos-latest, windows-latest, ubuntu-latest ] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v3 - - uses: actions/setup-java@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 with: distribution: 'zulu' java-version: 21 # cache: 'gradle' - name: Run All Tests - uses: gradle/gradle-build-action@v2 + uses: gradle/actions/setup-gradle@v3 with: gradle-version: 8.5 arguments: | diff --git a/Writerside/v.list b/Writerside/v.list index 370db3ed..bafb3995 100644 --- a/Writerside/v.list +++ b/Writerside/v.list @@ -4,5 +4,5 @@ - + From 6d831b80681aa166f4b82e3d4d3cdaacab847e03 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Sun, 4 Feb 2024 22:21:24 +0800 Subject: [PATCH 51/71] update doc --- Writerside/topics/use-stdlib.md | 1 - 1 file changed, 1 deletion(-) diff --git a/Writerside/topics/use-stdlib.md b/Writerside/topics/use-stdlib.md index 55b038e4..b1b6889f 100644 --- a/Writerside/topics/use-stdlib.md +++ b/Writerside/topics/use-stdlib.md @@ -296,4 +296,3 @@ bot.joinBlocking(); -> 封装的事件的结构、属性以及属性类型都基本与大别野文档中的一致或具有对应关系。 From 99c23f354109562ccfc7e65dc5d24e6ff98a1293 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 5 Feb 2024 18:29:50 +0800 Subject: [PATCH 52/71] update doc CI config --- .github/workflows/doc-test-branch.yml | 54 ++++++++++++++++++++------- 1 file changed, 41 insertions(+), 13 deletions(-) diff --git a/.github/workflows/doc-test-branch.yml b/.github/workflows/doc-test-branch.yml index bec2070e..233bd38c 100644 --- a/.github/workflows/doc-test-branch.yml +++ b/.github/workflows/doc-test-branch.yml @@ -4,25 +4,53 @@ on: branches: - 'dev/**' paths: - - 'website/**' + - 'Writerside/**' + # Specify to run a workflow manually from the Actions tab on GitHub + workflow_dispatch: concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: - deploy-website: - name: Build Website + build: runs-on: ubuntu-latest + steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Build Writerside docs using Docker + uses: JetBrains/writerside-github-action@v4 + with: + instance: ${{ env.INSTANCE }} + artifact: ${{ env.ARTIFACT }} + docker-version: ${{ env.DOCKER_VERSION }} + + - name: Upload documentation + uses: actions/upload-artifact@v3 + with: + name: docs + path: | + artifacts/${{ env.ARTIFACT }} + artifacts/report.json + retention-days: 7 + + # Add the job below and artifacts/report.json on Upload documentation step above if you want to fail the build when documentation contains errors + test: + name: Test documentation built + # Requires build job results + needs: build + runs-on: ubuntu-latest + + steps: + - name: Download artifacts + uses: actions/download-artifact@v1 + with: + name: docs + path: artifacts + + - name: Test documentation + uses: JetBrains/writerside-checker-action@v1 with: - node-version: 16.x - cache: npm - cache-dependency-path: ./website/package-lock.json - - - run: | - npm ci - npm run build - working-directory: ./website + instance: ${{ env.INSTANCE }} From ca5043d8140551fbb9fe4e98c6212b82182813ab Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 5 Feb 2024 23:11:49 +0800 Subject: [PATCH 53/71] doc algolia config --- .github/workflows/deploy-v4-website.yml | 63 +++++++++++++++++++++---- Writerside/cfg/buildprofiles.xml | 8 +++- Writerside/{d.tree => qg.tree} | 2 +- Writerside/writerside.cfg | 2 +- 4 files changed, 62 insertions(+), 13 deletions(-) rename Writerside/{d.tree => qg.tree} (98%) diff --git a/.github/workflows/deploy-v4-website.yml b/.github/workflows/deploy-v4-website.yml index ee0963e2..29e95e44 100644 --- a/.github/workflows/deploy-v4-website.yml +++ b/.github/workflows/deploy-v4-website.yml @@ -20,18 +20,21 @@ permissions: env: # Name of module and id separated by a slash - INSTANCE: Writerside/d + INSTANCE: Writerside/qg + INSTANCE_NAME: qg # Replace HI with the ID of the instance in capital letters - ARTIFACT: webHelpD2-all.zip + ARTIFACT: webHelpQG2-all.zip # Writerside docker image version DOCKER_VERSION: 233.14272 # Add the variable below to upload Algolia indexes # Replace HI with the ID of the instance in capital letters -# ALGOLIA_ARTIFACT: algolia-indexes-HI.zip + ALGOLIA_ARTIFACT: algolia-indexes-QG.zip jobs: build: runs-on: ubuntu-latest + outputs: + config_json: ${{ steps.output-config-json.outputs.config_json }} steps: - name: Checkout repository @@ -54,12 +57,20 @@ jobs: retention-days: 7 # Add the step below to upload Algolia indexes - # - name: Upload algolia-indexes - # uses: actions/upload-artifact@v3 - # with: - # name: algolia-indexes - # path: artifacts/${{ env.ALGOLIA_ARTIFACT }} - # retention-days: 7 + - name: Upload algolia-indexes + uses: actions/upload-artifact@v3 + with: + name: algolia-indexes + path: artifacts/${{ env.ALGOLIA_ARTIFACT }} + retention-days: 7 + + - name: Unzip artifact + run: unzip -O UTF-8 -qq artifacts/${{ env.ARTIFACT }} -d dir + + - name: Output documentation config.json + id: output-config-json + run: | + echo "config_json=$(cat dir/config.json)" >> $GITHUB_OUTPUT # Add the job below and artifacts/report.json on Upload documentation step above if you want to fail the build when documentation contains errors test: @@ -107,3 +118,37 @@ jobs: id: deployment uses: actions/deploy-pages@v1 + # https://www.jetbrains.com/help/writerside/configure-search.html + publish-indexes: + # Requires the build-job results + needs: [build, test] + runs-on: ubuntu-latest + container: + image: registry.jetbrains.team/p/writerside/builder/algolia-publisher:2.0.32-2 + + env: + ALGOLIA_APP_NAME: VLLZ4JZE8Z + ALGOLIA_INDEX_NAME: doc + ALGOLIA_KEY: ${{ secrets.ALGOLIA_KEY }} + ALGOLIA_ARTIFACT: ${{ env.ALGOLIA_ARTIFACT }} + CONFIG_JSON_PRODUCT: ${{ env.INSTANCE_NAME }} + CONFIG_JSON_VERSION: ${{ fromJSON(needs.build.outputs.config_json).productVersion }} + + steps: + - name: Download artifact + uses: actions/download-artifact@v3 + with: + name: algolia-indexes + + + - name: Unzip artifact + run: | + unzip -O UTF-8 -qq ${{ env.ALGOLIA_ARTIFACT }} -d algolia-indexes + env "algolia-key=${{env.ALGOLIA_KEY}}" java -jar /opt/builder/help-publication-agent.jar \ + update-index \ + --application-name ${{env.ALGOLIA_APP_NAME}} \ + --index-name ${{env.ALGOLIA_INDEX_NAME}} \ + --product ${{env.CONFIG_JSON_PRODUCT}} \ + --version ${{env.CONFIG_JSON_VERSION}} \ + --index-directory algolia-indexes/ \ + 2>&1 | tee algolia-update-index-log.txt diff --git a/Writerside/cfg/buildprofiles.xml b/Writerside/cfg/buildprofiles.xml index fd8d9c87..9185f0ca 100644 --- a/Writerside/cfg/buildprofiles.xml +++ b/Writerside/cfg/buildprofiles.xml @@ -10,13 +10,17 @@ GitHub https://github.com/simple-robot/simbot-component-qq-guild/ true - + https://github.com/simple-robot/simbot-component-qq-guild/tree/dev/main/Writerside/ true zh-CN false + VLLZ4JZE8Z + doc + e60d9ee16618a0ad3a338ecc73cb840e + http://component-qqguild.simbot.forte.love - + false diff --git a/Writerside/d.tree b/Writerside/qg.tree similarity index 98% rename from Writerside/d.tree rename to Writerside/qg.tree index 92be3181..08ff0656 100644 --- a/Writerside/d.tree +++ b/Writerside/qg.tree @@ -2,7 +2,7 @@ - diff --git a/Writerside/writerside.cfg b/Writerside/writerside.cfg index 35e746ff..e2faeba2 100644 --- a/Writerside/writerside.cfg +++ b/Writerside/writerside.cfg @@ -4,5 +4,5 @@ - + From 045a9fbf45aec1e2d1e1e3505cdd9bad26cce5e4 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 5 Feb 2024 23:15:58 +0800 Subject: [PATCH 54/71] doc algolia config --- .github/workflows/deploy-v4-website.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy-v4-website.yml b/.github/workflows/deploy-v4-website.yml index 29e95e44..b3b0edf7 100644 --- a/.github/workflows/deploy-v4-website.yml +++ b/.github/workflows/deploy-v4-website.yml @@ -29,6 +29,9 @@ env: # Add the variable below to upload Algolia indexes # Replace HI with the ID of the instance in capital letters ALGOLIA_ARTIFACT: algolia-indexes-QG.zip + ALGOLIA_APP_NAME: VLLZ4JZE8Z + ALGOLIA_INDEX_NAME: doc + ALGOLIA_KEY: ${{ secrets.ALGOLIA_KEY }} jobs: build: @@ -127,11 +130,7 @@ jobs: image: registry.jetbrains.team/p/writerside/builder/algolia-publisher:2.0.32-2 env: - ALGOLIA_APP_NAME: VLLZ4JZE8Z - ALGOLIA_INDEX_NAME: doc - ALGOLIA_KEY: ${{ secrets.ALGOLIA_KEY }} - ALGOLIA_ARTIFACT: ${{ env.ALGOLIA_ARTIFACT }} - CONFIG_JSON_PRODUCT: ${{ env.INSTANCE_NAME }} + CONFIG_JSON_VERSION: ${{ fromJSON(needs.build.outputs.config_json).productVersion }} steps: @@ -148,7 +147,7 @@ jobs: update-index \ --application-name ${{env.ALGOLIA_APP_NAME}} \ --index-name ${{env.ALGOLIA_INDEX_NAME}} \ - --product ${{env.CONFIG_JSON_PRODUCT}} \ + --product ${{env.INSTANCE_NAME}} \ --version ${{env.CONFIG_JSON_VERSION}} \ --index-directory algolia-indexes/ \ 2>&1 | tee algolia-update-index-log.txt From 9c2cc9d043dad9e9a60b8375f6d286d861115a28 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 5 Feb 2024 23:18:24 +0800 Subject: [PATCH 55/71] doc algolia config --- .github/workflows/deploy-v4-website.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy-v4-website.yml b/.github/workflows/deploy-v4-website.yml index b3b0edf7..7e8fb7df 100644 --- a/.github/workflows/deploy-v4-website.yml +++ b/.github/workflows/deploy-v4-website.yml @@ -10,6 +10,7 @@ on: paths: - 'Writerside/**' + - '.github/workflows/deploy-v4-website.yml' # Specify to run a workflow manually from the Actions tab on GitHub workflow_dispatch: From 388c0921d0332c4d25d4e15c80eeb0ea0a9bb56d Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 5 Feb 2024 23:29:14 +0800 Subject: [PATCH 56/71] doc algolia config --- .github/workflows/deploy-v4-website.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-v4-website.yml b/.github/workflows/deploy-v4-website.yml index 7e8fb7df..ee5d2225 100644 --- a/.github/workflows/deploy-v4-website.yml +++ b/.github/workflows/deploy-v4-website.yml @@ -31,7 +31,7 @@ env: # Replace HI with the ID of the instance in capital letters ALGOLIA_ARTIFACT: algolia-indexes-QG.zip ALGOLIA_APP_NAME: VLLZ4JZE8Z - ALGOLIA_INDEX_NAME: doc + ALGOLIA_INDEX_NAME: qg-doc ALGOLIA_KEY: ${{ secrets.ALGOLIA_KEY }} jobs: From 6846ba11264f27a36967c401ba699d8ac2c2184a Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 5 Feb 2024 23:30:07 +0800 Subject: [PATCH 57/71] doc algolia config --- .github/workflows/deploy-v4-website.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/deploy-v4-website.yml b/.github/workflows/deploy-v4-website.yml index ee5d2225..48c4bf74 100644 --- a/.github/workflows/deploy-v4-website.yml +++ b/.github/workflows/deploy-v4-website.yml @@ -32,7 +32,6 @@ env: ALGOLIA_ARTIFACT: algolia-indexes-QG.zip ALGOLIA_APP_NAME: VLLZ4JZE8Z ALGOLIA_INDEX_NAME: qg-doc - ALGOLIA_KEY: ${{ secrets.ALGOLIA_KEY }} jobs: build: @@ -144,7 +143,7 @@ jobs: - name: Unzip artifact run: | unzip -O UTF-8 -qq ${{ env.ALGOLIA_ARTIFACT }} -d algolia-indexes - env "algolia-key=${{env.ALGOLIA_KEY}}" java -jar /opt/builder/help-publication-agent.jar \ + env "algolia-key=${{secrets.ALGOLIA_KEY}}" java -jar /opt/builder/help-publication-agent.jar \ update-index \ --application-name ${{env.ALGOLIA_APP_NAME}} \ --index-name ${{env.ALGOLIA_INDEX_NAME}} \ From 21204213b8b11a33c59dd50aea62f284ccbb2506 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 5 Feb 2024 23:35:25 +0800 Subject: [PATCH 58/71] doc algolia config --- Writerside/cfg/buildprofiles.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Writerside/cfg/buildprofiles.xml b/Writerside/cfg/buildprofiles.xml index 9185f0ca..8e647a4e 100644 --- a/Writerside/cfg/buildprofiles.xml +++ b/Writerside/cfg/buildprofiles.xml @@ -15,7 +15,7 @@ zh-CN false VLLZ4JZE8Z - doc + qg-doc e60d9ee16618a0ad3a338ecc73cb840e http://component-qqguild.simbot.forte.love From 9e1301ad1199dabe668648a1b7f100e6bfeb2e37 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 5 Feb 2024 23:42:38 +0800 Subject: [PATCH 59/71] =?UTF-8?q?doc:=20=E7=AE=80=E5=8D=95=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Writerside/qg.tree | 4 ++-- Writerside/topics/event.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Writerside/qg.tree b/Writerside/qg.tree index 08ff0656..7568a1a1 100644 --- a/Writerside/qg.tree +++ b/Writerside/qg.tree @@ -16,14 +16,14 @@ - + + - diff --git a/Writerside/topics/event.md b/Writerside/topics/event.md index 9d0377a1..4fff0f62 100644 --- a/Writerside/topics/event.md +++ b/Writerside/topics/event.md @@ -1,4 +1,4 @@ -# 事件类型 +# 事件定义列表 QQ频道组件中的**事件类型**包含两个层面: From f2e702b53984db65a59bc58e5a0ce0617c2efc7c Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Mon, 5 Feb 2024 23:58:08 +0800 Subject: [PATCH 60/71] doc: deflist def id --- Writerside/qg.tree | 2 +- Writerside/topics/api-list.md | 128 +++++++++--------- Writerside/topics/event.md | 114 ++++++++-------- .../apireader/ApiReaderProcessor.kt | 7 +- .../apireader/EventReaderProcessor.kt | 7 +- .../qg/internal/processors/apireader/utils.kt | 2 +- 6 files changed, 134 insertions(+), 126 deletions(-) diff --git a/Writerside/qg.tree b/Writerside/qg.tree index 7568a1a1..7c5341c9 100644 --- a/Writerside/qg.tree +++ b/Writerside/qg.tree @@ -19,12 +19,12 @@ + - diff --git a/Writerside/topics/api-list.md b/Writerside/topics/api-list.md index 61d8535b..10b76357 100644 --- a/Writerside/topics/api-list.md +++ b/Writerside/topics/api-list.md @@ -6,13 +6,13 @@ > 因为那是最贴合真实情况且最全面的。 - + `love.forte.simbot.qguild.api.GatewayApis` 获取网关信息。 -通过`Normal] 或 [Shared`的形式根据bot信息获取使用 Websocket 接入时间通知的链接。 +通过 `Normal` 或 `Shared` 的形式根据bot信息获取使用 Websocket 接入时间通知的链接。 > 参考文档 @@ -20,7 +20,7 @@ - + `love.forte.simbot.qguild.api.Normal` @@ -30,7 +30,7 @@ - + `love.forte.simbot.qguild.api.Shared` @@ -43,7 +43,7 @@ - + `love.forte.simbot.qguild.api.announces.CreateAnnouncesApi` @@ -55,7 +55,7 @@ - + `love.forte.simbot.qguild.api.announces.DeleteAnnouncesApi` @@ -66,7 +66,7 @@ - + `love.forte.simbot.qguild.api.apipermission.DemandApiPermissionApi` @@ -82,7 +82,7 @@ - + `love.forte.simbot.qguild.api.apipermission.GetApiPermissionListApi` @@ -93,7 +93,7 @@ - + `love.forte.simbot.qguild.api.channel.CreateChannelApi` @@ -107,7 +107,7 @@ - + `love.forte.simbot.qguild.api.channel.DeleteChannelApi` @@ -126,7 +126,7 @@ - + `love.forte.simbot.qguild.api.channel.GetChannelApi` @@ -135,7 +135,7 @@ - + `love.forte.simbot.qguild.api.channel.GetChannelOnlineNumsApi` @@ -146,7 +146,7 @@ - + `love.forte.simbot.qguild.api.channel.GetGuildChannelListApi` @@ -157,7 +157,7 @@ - + `love.forte.simbot.qguild.api.channel.ModifyChannelApi` @@ -171,7 +171,7 @@ - + `love.forte.simbot.qguild.api.channel.permissions.GetChannelMemberPermissionsApi` @@ -185,7 +185,7 @@ - + `love.forte.simbot.qguild.api.channel.permissions.GetChannelRolePermissionsApi` @@ -198,7 +198,7 @@ - + `love.forte.simbot.qguild.api.channel.permissions.ModifyChannelMemberPermissionsApi` @@ -214,7 +214,7 @@ - + `love.forte.simbot.qguild.api.channel.permissions.ModifyChannelRolePermissionsApi` @@ -230,7 +230,7 @@ - + `love.forte.simbot.qguild.api.channel.pins.AddPinsMessageApi` @@ -245,7 +245,7 @@ - + `love.forte.simbot.qguild.api.channel.pins.DeletePinsMessageApi` @@ -253,12 +253,12 @@ 用于删除子频道 `channel_id` 下指定 `message_id` 的精华消息。 -- 删除子频道内全部精华消息,请将 `message_id` 设置为 [`all`][DELETE_ALL_MESSAGE_ID]。 +- 删除子频道内全部精华消息,请将 `message_id` 设置为 [`all`] `DELETE_ALL_MESSAGE_ID` 。 - + `love.forte.simbot.qguild.api.channel.pins.GetPinsMessageApi` @@ -269,7 +269,7 @@ - + `love.forte.simbot.qguild.api.channel.schedules.CreateScheduleApi` @@ -286,7 +286,7 @@ - + `love.forte.simbot.qguild.api.channel.schedules.DeleteScheduleApi` @@ -299,7 +299,7 @@ - + `love.forte.simbot.qguild.api.channel.schedules.GetScheduleApi` @@ -310,7 +310,7 @@ - + `love.forte.simbot.qguild.api.channel.schedules.GetScheduleListApi` @@ -323,7 +323,7 @@ - + `love.forte.simbot.qguild.api.channel.schedules.ModifyScheduleApi` @@ -336,7 +336,7 @@ - + `love.forte.simbot.qguild.api.forum.DeleteThreadApi` @@ -347,7 +347,7 @@ - + `love.forte.simbot.qguild.api.forum.GetThreadApi` @@ -359,7 +359,7 @@ - + `love.forte.simbot.qguild.api.forum.GetThreadListApi` @@ -370,7 +370,7 @@ - + `love.forte.simbot.qguild.api.forum.PublishThreadApi` @@ -380,7 +380,7 @@ - + `love.forte.simbot.qguild.api.guild.GetGuildApi` @@ -390,7 +390,7 @@ - + `love.forte.simbot.qguild.api.guild.mute.MuteAllApi` @@ -406,7 +406,7 @@ - + `love.forte.simbot.qguild.api.guild.mute.MuteMemberApi` @@ -420,7 +420,7 @@ - + `love.forte.simbot.qguild.api.guild.mute.MuteMultiMemberApi` @@ -434,7 +434,7 @@ - + `love.forte.simbot.qguild.api.member.DeleteMemberApi` @@ -449,7 +449,7 @@ - + `love.forte.simbot.qguild.api.member.GetGuildMemberListApi` @@ -467,7 +467,7 @@ - + `love.forte.simbot.qguild.api.member.GetGuildRoleMemberListApi` @@ -483,7 +483,7 @@ - + `love.forte.simbot.qguild.api.member.GetMemberApi` @@ -494,7 +494,7 @@ - + `love.forte.simbot.qguild.api.message.DeleteMessageApi` @@ -508,7 +508,7 @@ - + `love.forte.simbot.qguild.api.message.GetMessageApi` @@ -519,7 +519,7 @@ - + `love.forte.simbot.qguild.api.message.MessageSendApi` @@ -604,7 +604,7 @@ | `<` | `<` | | `>` | `>` | -可参考使用`ContentTextDecoder`和 [ContentTextEncoder] +可参考使用 `ContentTextDecoder` 和 `ContentTextEncoder` **消息审核** @@ -612,10 +612,10 @@ > 其中推送、回复消息的 code 错误码 `304023`、`304024` 会在 响应数据包 `data` 中返回 `MessageAudit` 审核消息的信息 -当响应结果为上述错误码时,请求实体对象结果的API时会抛出`MessageAuditedException`异常并携带相关的对象信息。 +当响应结果为上述错误码时,请求实体对象结果的API时会抛出 `MessageAuditedException` 异常并携带相关的对象信息。 详见文档 发送消息 中的相关描述以及 -[MessageAuditedException] 的文档描述。 +`MessageAuditedException` 的文档描述。
@@ -625,7 +625,7 @@
- + `love.forte.simbot.qguild.api.message.direct.CreateDmsApi` @@ -648,7 +648,7 @@ - + `love.forte.simbot.qguild.api.message.direct.DeleteDmsApi` @@ -660,7 +660,7 @@ - + `love.forte.simbot.qguild.api.message.direct.DmsSendApi` @@ -683,19 +683,19 @@ **参数** -和`发送消息][MessageSendApi`参数一致。 +和 [发送消息] `MessageSendApi` 参数一致。 **返回** -和`发送消息][MessageSendApi`返回一致。 +和 [发送消息] `MessageSendApi` 返回一致。 - + `love.forte.simbot.qguild.api.message.setting.GetMessageSettingApi` @@ -705,7 +705,7 @@ - + `love.forte.simbot.qguild.api.role.AddMemberRoleApi` @@ -714,13 +714,13 @@ 用于将频道 `guild_id` 下的用户 `user_id` 添加到身份组 `role_id` 。 - 需要使用的 `token` 对应的用户具备增加身份组成员权限。如果是机器人,要求被添加为管理员。 -- 如果要增加的身份组 `ID` 是 [`5-子频道管理员`][love.forte.simbot.qguild.model.Role.DEFAULT_ID_CHANNEL_ADMIN], +- 如果要增加的身份组 `ID` 是 [`5-子频道管理员`] `love.forte.simbot.qguild.model.Role.DEFAULT_ID_CHANNEL_ADMIN` , 需要增加 `channel` 对象来指定具体是哪个子频道。 - + `love.forte.simbot.qguild.api.role.CreateGuildRoleApi` @@ -734,7 +734,7 @@ - + `love.forte.simbot.qguild.api.role.DeleteGuildRoleApi` @@ -746,7 +746,7 @@ - + `love.forte.simbot.qguild.api.role.GetGuildRoleListApi` @@ -757,7 +757,7 @@ - + `love.forte.simbot.qguild.api.role.ModifyGuildRoleApi` @@ -771,7 +771,7 @@ - + `love.forte.simbot.qguild.api.role.RemoveMemberRoleApi` @@ -780,13 +780,13 @@ 用于将 用户 `user_id` 从 频道 `guild_id` 的 `role_id` 身份组中移除。 - 需要使用的 `token` 对应的用户具备删除身份组成员权限。如果是机器人,要求被添加为管理员。 -- 如果要删除的身份组 `ID` 是 [`5-子频道管理员`][love.forte.simbot.qguild.model.Role.DEFAULT_ID_CHANNEL_ADMIN], +- 如果要删除的身份组 `ID` 是 [`5-子频道管理员`] `love.forte.simbot.qguild.model.Role.DEFAULT_ID_CHANNEL_ADMIN` , 需要增加 `channel` 对象来指定具体是哪个子频道。 - + `love.forte.simbot.qguild.api.user.GetBotGuildListApi` @@ -798,7 +798,7 @@ - + `love.forte.simbot.qguild.api.user.GetBotInfoApi` @@ -806,9 +806,9 @@ 用于获取当前用户(机器人)详情。 -由于`GetBotInfoApi] 本身为 `object` 类型, 因此 [ApiDescription] 由内部对象 [Description`提供而不是伴生对象。 +由于 `GetBotInfoApi` 本身为 `object` 类型, 因此 `ApiDescription` 由内部对象 `Description` 提供而不是伴生对象。 -[GetBotInfoApi] 得到的`User] 中,[User.isBot`始终为 `true`。 +`GetBotInfoApi` 得到的 `User` 中, `User.isBot` 始终为 `true`。 @@ -816,3 +816,5 @@
+ + diff --git a/Writerside/topics/event.md b/Writerside/topics/event.md index 4fff0f62..28d5cdeb 100644 --- a/Writerside/topics/event.md +++ b/Writerside/topics/event.md @@ -14,7 +14,7 @@ API 模块所有的事件封装类型都在包 `love.forte.simbot.qguild.event` 所有事件封装类型均继承密封类 `love.forte.simbot.qguild.event.Signal.Dispatch`。 - + `love.forte.simbot.qguild.event.Ready` @@ -23,7 +23,7 @@ API 模块所有的事件封装类型都在包 `love.forte.simbot.qguild.event` 鉴权成功之后,后台会下发的 Ready Event. - + `love.forte.simbot.qguild.event.Resumed` @@ -33,14 +33,14 @@ API 模块所有的事件封装类型都在包 `love.forte.simbot.qguild.event` 恢复成功之后,就开始补发遗漏事件,所有事件补发完成之后,会下发一个 `Resumed Event` - + `love.forte.simbot.qguild.event.ChannelDispatch` -channel相关的事件类型。[data] 类型为 [EventChannel]。 +channel相关的事件类型。 `data` 类型为 `EventChannel` 。 - + `love.forte.simbot.qguild.event.ChannelCreate` @@ -53,7 +53,7 @@ channel相关的事件类型。[data] 类型为 [EventChannel]。 - 子频道被创建 - + `love.forte.simbot.qguild.event.ChannelUpdate` @@ -66,7 +66,7 @@ channel相关的事件类型。[data] 类型为 [EventChannel]。 - 子频道信息变更 - + `love.forte.simbot.qguild.event.ChannelDelete` @@ -79,7 +79,7 @@ channel相关的事件类型。[data] 类型为 [EventChannel]。 - 子频道被删除 - + `love.forte.simbot.qguild.event.ForumDispatch` @@ -94,34 +94,34 @@ channel相关的事件类型。[data] 类型为 [EventChannel]。 - FORUM_THREAD_CREATE - FORUM_THREAD_UPDATE - FORUM_THREAD_DELETE - 事件内容为`Thread`对象 + 事件内容为 `Thread` 对象 **帖子事件** - FORUM_POST_CREATE - FORUM_POST_DELETE - 事件内容为`Post`对象 + 事件内容为 `Post` 对象 **回复事件** - FORUM_REPLY_CREATE - FORUM_REPLY_DELETE - 事件内容为`Reply`对象 + 事件内容为 `Reply` 对象 **帖子审核事件** - FORUM_PUBLISH_AUDIT_RESULT - 事件内容为`AuditResult`对象 + 事件内容为 `AuditResult` 对象 - + `love.forte.simbot.qguild.event.ForumThreadDispatch` 论坛事件:主题事件 - + `love.forte.simbot.qguild.event.ForumThreadCreate` @@ -130,7 +130,7 @@ channel相关的事件类型。[data] 类型为 [EventChannel]。 主题创建事件。 - + `love.forte.simbot.qguild.event.ForumThreadUpdate` @@ -139,7 +139,7 @@ channel相关的事件类型。[data] 类型为 [EventChannel]。 主题更新事件。 - + `love.forte.simbot.qguild.event.ForumThreadDelete` @@ -148,14 +148,14 @@ channel相关的事件类型。[data] 类型为 [EventChannel]。 主题删除事件。 - + `love.forte.simbot.qguild.event.ForumPostDispatch` 论坛事件:帖子事件 - + `love.forte.simbot.qguild.event.ForumPostCreate` @@ -164,7 +164,7 @@ channel相关的事件类型。[data] 类型为 [EventChannel]。 帖子创建事件 - + `love.forte.simbot.qguild.event.ForumPostDelete` @@ -173,14 +173,14 @@ channel相关的事件类型。[data] 类型为 [EventChannel]。 帖子删除事件 - + `love.forte.simbot.qguild.event.ForumReplyDispatch` 论坛事件:回复事件 - + `love.forte.simbot.qguild.event.ForumReplyCreate` @@ -189,7 +189,7 @@ channel相关的事件类型。[data] 类型为 [EventChannel]。 回复创建事件 - + `love.forte.simbot.qguild.event.ForumReplyDelete` @@ -198,7 +198,7 @@ channel相关的事件类型。[data] 类型为 [EventChannel]。 回复删除事件 - + `love.forte.simbot.qguild.event.ForumPublishAuditResult` @@ -207,14 +207,14 @@ channel相关的事件类型。[data] 类型为 [EventChannel]。 帖子审核事件 - + `love.forte.simbot.qguild.event.EventGuildDispatch` -Guild相关事件类型。[data] 类型为 [EventGuild]。 +Guild相关事件类型。 `data` 类型为 `EventGuild` 。 - + `love.forte.simbot.qguild.event.GuildCreate` @@ -227,7 +227,7 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 - 机器人被加入到某个频道的时候 - + `love.forte.simbot.qguild.event.GuildUpdate` @@ -241,7 +241,7 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 - 事件内容为变更后的数据 - + `love.forte.simbot.qguild.event.GuildDelete` @@ -256,7 +256,7 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 - 事件内容为变更前的数据 - + `love.forte.simbot.qguild.event.GuildMemberAdd` @@ -269,7 +269,7 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 - 新用户加入频道 - + `love.forte.simbot.qguild.event.GuildMemberUpdate` @@ -282,7 +282,7 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 - 用户的频道属性发生变化,如频道昵称,或者身份组 - + `love.forte.simbot.qguild.event.GuildMemberRemove` @@ -295,14 +295,14 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 - 用户离开频道 - + `love.forte.simbot.qguild.event.MessageDispatch` -与 `message` 相关的事件类型。[data] 类型为 [Message] +与 `message` 相关的事件类型。 `data` 类型为 `Message` - + `love.forte.simbot.qguild.event.AtMessageCreate` @@ -315,10 +315,10 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 - 用户发送消息,@当前机器人或回复机器人消息时 - 为保障消息投递的速度,消息顺序我们虽然会尽量有序,但是并不保证是严格有序的, - 如开发者对消息顺序有严格有序的需求,可以自行缓冲消息事件之后,基于`seq`进行排序 + 如开发者对消息顺序有严格有序的需求,可以自行缓冲消息事件之后,基于 `seq` 进行排序 - + `love.forte.simbot.qguild.event.PublicMessageDeleteCreate` @@ -328,7 +328,7 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 `PUBLIC_MESSAGE_DELETE_TYPE` - + `love.forte.simbot.qguild.event.DirectMessageCreate` @@ -342,14 +342,14 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 - 用户通过私信发消息给机器人时 - + `love.forte.simbot.qguild.event.MessageAuditedDispatch` -与`MessageAudited] 相关的事件类型。[data`类型为 [MessageAudited]。 +与 `MessageAudited` 相关的事件类型。 `data` 类型为 `MessageAudited` 。 - + `love.forte.simbot.qguild.event.MessageCreate` @@ -358,7 +358,7 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 发送消息事件,代表频道内的全部消息,而不只是 at 机器人的消息。内容与 AT_MESSAGE_CREATE 相同 - + `love.forte.simbot.qguild.event.MessageDelete` @@ -367,7 +367,7 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 删除(撤回)消息事件 - + `love.forte.simbot.qguild.event.MessageAuditPass` @@ -381,7 +381,7 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 - 消息审核通过 - + `love.forte.simbot.qguild.event.MessageAuditReject` @@ -395,7 +395,7 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 - 消息审核不通过 - + `love.forte.simbot.qguild.event.OpenForumDispatch` @@ -410,29 +410,29 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 - OPEN_FORUM_THREAD_CREATE - OPEN_FORUM_THREAD_UPDATE - OPEN_FORUM_THREAD_DELETE - 参考 [OpenForumThreadDispatch] + 参考 `OpenForumThreadDispatch` **帖子(评论)事件** - OPEN_FORUM_POST_CREATE - OPEN_FORUM_POST_DELETE - 参考 [OpenForumPostDispatch] + 参考 `OpenForumPostDispatch` **回复事件** - OPEN_FORUM_REPLY_CREATE - OPEN_FORUM_REPLY_DELETE - 参考 [OpenForumReplyDispatch] + 参考 `OpenForumReplyDispatch` - + `love.forte.simbot.qguild.event.OpenForumThreadDispatch` 开放论坛事件的 **_主题事件_**。 - + `love.forte.simbot.qguild.event.OpenForumThreadCreate` @@ -441,7 +441,7 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 主题事件:创建主题 - + `love.forte.simbot.qguild.event.OpenForumThreadUpdate` @@ -450,7 +450,7 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 主题事件:更新主题 - + `love.forte.simbot.qguild.event.OpenForumThreadDelete` @@ -459,14 +459,14 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 主题事件:删除主题 - + `love.forte.simbot.qguild.event.OpenForumPostDispatch` 开放论坛事件的 **_帖子(评论)事件_**。 - + `love.forte.simbot.qguild.event.OpenForumPostCreate` @@ -475,7 +475,7 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 帖子事件:创建帖子(评论) - + `love.forte.simbot.qguild.event.OpenForumPostDelete` @@ -484,14 +484,14 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 帖子事件:删除帖子(评论) - + `love.forte.simbot.qguild.event.OpenForumReplyDispatch` 开放论坛事件的 **_回复事件_**。 - + `love.forte.simbot.qguild.event.OpenForumReplyCreate` @@ -500,7 +500,7 @@ Guild相关事件类型。[data] 类型为 [EventGuild]。 回复事件:创建回复 - + `love.forte.simbot.qguild.event.OpenForumReplyDelete` diff --git a/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessor.kt b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessor.kt index 10d395a7..636c61a1 100644 --- a/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessor.kt +++ b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/ApiReaderProcessor.kt @@ -25,6 +25,7 @@ import com.google.devtools.ksp.processing.SymbolProcessor import com.google.devtools.ksp.processing.SymbolProcessorEnvironment import com.google.devtools.ksp.symbol.KSAnnotated import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.squareup.kotlinpoet.ksp.toClassName import java.io.BufferedWriter import java.io.File import java.nio.file.StandardOpenOption @@ -100,7 +101,9 @@ class ApiReaderProcessor(private val environment: SymbolProcessorEnvironment) : private fun BufferedWriter.writeDeflistTo(list: List) { write("\n") list.forEach { declaration -> - write("\n") + val className = declaration.toClassName().canonicalName + val idName = className.replace('.', '_') + write("\n") newLine() write("`${declaration.packageName.asString()}.${declaration.simpleName.asString()}`\n") newLine() @@ -112,7 +115,7 @@ private fun BufferedWriter.writeDeflistTo(list: List) { ?.map { line -> line .replace(linkRegex, "$1") - .replace(refRegex, "`$1`") + .replace(refRegex, " `$1` ") .replace(titleRegex, "\n**$1**\n") } diff --git a/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/EventReaderProcessor.kt b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/EventReaderProcessor.kt index 9293d744..4e1d8faf 100644 --- a/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/EventReaderProcessor.kt +++ b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/EventReaderProcessor.kt @@ -25,6 +25,7 @@ import com.google.devtools.ksp.processing.SymbolProcessor import com.google.devtools.ksp.processing.SymbolProcessorEnvironment import com.google.devtools.ksp.symbol.KSAnnotated import com.google.devtools.ksp.symbol.KSClassDeclaration +import com.squareup.kotlinpoet.ksp.toClassName import java.io.BufferedWriter import java.io.File import java.nio.file.StandardOpenOption @@ -85,7 +86,9 @@ class EventReaderProcessor(private val environment: SymbolProcessorEnvironment) private fun BufferedWriter.writeDeflistTo(list: List) { write("\n") list.forEach { declaration -> - write("\n") + val className = declaration.toClassName().canonicalName + val idName = className.replace('.', '_') + write("\n") newLine() write("`${declaration.packageName.asString()}.${declaration.simpleName.asString()}`\n") newLine() @@ -106,7 +109,7 @@ class EventReaderProcessor(private val environment: SymbolProcessorEnvironment) ?.map { line -> line .replace(linkRegex, "$1") - .replace(refRegex, "`$1`") + .replace(refRegex, " `$1` ") .replace(titleRegex, "\n**$1**\n") } diff --git a/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/utils.kt b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/utils.kt index 46de0922..fb9d5eed 100644 --- a/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/utils.kt +++ b/internal-processors/api-reader/src/main/kotlin/qg/internal/processors/apireader/utils.kt @@ -18,5 +18,5 @@ package qg.internal.processors.apireader val linkRegex = Regex("\\[(?.+)]\\((?.+)\\)") -val refRegex = Regex(" \\[(.+)] ") +val refRegex = Regex(" ?\\[([a-zA-Z0-9-_.$]+)] ?") val titleRegex = Regex("#+ (.+)") From e62a37f819c570ec12de3ad306241d1c6353eb11 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Tue, 6 Feb 2024 20:13:00 +0800 Subject: [PATCH 61/71] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20simbot=20=E5=88=B0?= =?UTF-8?q?=20v4.0.0-dev18;=20=E6=9B=B4=E6=96=B0=20kotlinx.coroutines=20?= =?UTF-8?q?=E5=88=B0=20v1.8.0-RC2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- gradle/libs.versions.toml | 6 +++--- simbot-component-qq-guild-api/build.gradle.kts | 1 + .../simbot/component/qguild/internal/forum/QGThreadImpl.kt | 6 ++++-- .../component/qguild/internal/role/QGGuildRoleImpl.kt | 6 ++++-- .../component/qguild/internal/role/QGMemberRoleImpl.kt | 6 ++++-- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 21d88355..e063d7b3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] kotlin = "1.9.22" -kotlinx-coroutines = "1.7.3" +kotlinx-coroutines = "1.8.0-RC2" kotlinx-serialization = "1.6.2" kotlinx-datetime = "0.5.0" dokka = "1.9.10" @@ -10,7 +10,7 @@ openjdk-jmh = "1.35" log4j = "2.20.0" reactor = "3.6.2" # simbot -simbot = "4.0.0-dev15" +simbot = "4.0.0-dev18" suspendTransform = "0.7.0-beta1" gradleCommon = "0.2.0" # ksp @@ -31,7 +31,7 @@ simbot-common-core = { group = "love.forte.simbot.common", name = "simbot-common simbot-common-suspend = { group = "love.forte.simbot.common", name = "simbot-common-suspend-runner", version.ref = "simbot" } simbot-common-annotations = { group = "love.forte.simbot.common", name = "simbot-common-annotations", version.ref = "simbot" } simbot-common-loop = { group = "love.forte.simbot.common", name = "simbot-common-stage-loop", version.ref = "simbot" } -simbot-gradle = { group = "love.forte.simbot.gradle", name = "simbot-gradle-suspendtransforms", version = "4.0.0-dev15" } +simbot-gradle = { group = "love.forte.simbot.gradle", name = "simbot-gradle-suspendtransforms", version.ref = "simbot" } # jetbrains-annotation jetbrains-annotations = "org.jetbrains:annotations:24.0.1" diff --git a/simbot-component-qq-guild-api/build.gradle.kts b/simbot-component-qq-guild-api/build.gradle.kts index 151977ce..3b7e2631 100644 --- a/simbot-component-qq-guild-api/build.gradle.kts +++ b/simbot-component-qq-guild-api/build.gradle.kts @@ -82,6 +82,7 @@ kotlin { commonTest.dependencies { implementation(kotlin("test")) + implementation(libs.kotlinx.coroutines.debug) implementation(libs.kotlinx.coroutines.test) // https://ktor.io/docs/http-client-testing.html implementation(libs.ktor.client.mock) diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt index 51f7f9d2..ec10f02f 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/forum/QGThreadImpl.kt @@ -19,8 +19,10 @@ package love.forte.simbot.component.qguild.internal.forum import love.forte.simbot.ability.DeleteFailureException import love.forte.simbot.ability.DeleteOption -import love.forte.simbot.ability.StandardDeleteOption.* import love.forte.simbot.ability.StandardDeleteOption.Companion.standardAnalysis +import love.forte.simbot.ability.StandardDeleteOption.IGNORE_ON_FAILURE +import love.forte.simbot.ability.StandardDeleteOption.IGNORE_ON_NO_SUCH_TARGET +import love.forte.simbot.ability.isIgnoreOnFailure import love.forte.simbot.component.qguild.forum.QGThread import love.forte.simbot.component.qguild.guild.QGMember import love.forte.simbot.component.qguild.internal.bot.QGBotImpl @@ -72,7 +74,7 @@ internal class QGThreadImpl( return } - if (IGNORE_ON_ANY_FAILURE !in stdOpts) { + if (!stdOpts.isIgnoreOnFailure) { throw e } } diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt index 3176a384..188dba25 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGGuildRoleImpl.kt @@ -21,8 +21,10 @@ import io.ktor.util.internal.* import kotlinx.coroutines.flow.map import love.forte.simbot.ability.DeleteFailureException import love.forte.simbot.ability.DeleteOption -import love.forte.simbot.ability.StandardDeleteOption.* import love.forte.simbot.ability.StandardDeleteOption.Companion.standardAnalysis +import love.forte.simbot.ability.StandardDeleteOption.IGNORE_ON_FAILURE +import love.forte.simbot.ability.StandardDeleteOption.IGNORE_ON_NO_SUCH_TARGET +import love.forte.simbot.ability.isIgnoreOnFailure import love.forte.simbot.common.collectable.Collectable import love.forte.simbot.common.collectable.asCollectable import love.forte.simbot.common.id.ID @@ -96,7 +98,7 @@ internal class QGGuildRoleImpl( return } - if (IGNORE_ON_ANY_FAILURE !in stdOpts) { + if (!stdOpts.isIgnoreOnFailure) { throw e } } diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt index ab41f5b0..9416a8f5 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/role/QGMemberRoleImpl.kt @@ -19,8 +19,10 @@ package love.forte.simbot.component.qguild.internal.role import love.forte.simbot.ability.DeleteFailureException import love.forte.simbot.ability.DeleteOption -import love.forte.simbot.ability.StandardDeleteOption.* import love.forte.simbot.ability.StandardDeleteOption.Companion.standardAnalysis +import love.forte.simbot.ability.StandardDeleteOption.IGNORE_ON_FAILURE +import love.forte.simbot.ability.StandardDeleteOption.IGNORE_ON_NO_SUCH_TARGET +import love.forte.simbot.ability.isIgnoreOnFailure import love.forte.simbot.common.id.ID import love.forte.simbot.common.id.literal import love.forte.simbot.component.qguild.ExperimentalQGApi @@ -82,7 +84,7 @@ internal class QGMemberRoleImpl( return } - if (IGNORE_ON_ANY_FAILURE !in stdOpts) { + if (!stdOpts.isIgnoreOnFailure) { throw e } } From 8eb5619dcda178b2861c593276e561f96311ab90 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Tue, 6 Feb 2024 20:36:41 +0800 Subject: [PATCH 62/71] config: release.yml --- .github/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/release.yml b/.github/release.yml index fe32d4c5..513752af 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -19,6 +19,9 @@ changelog: - title: '✨ 优化' labels: - '优化' + - title: '📦 依赖更新' + labels: + - 'dependencies' - title: '👀 其他变更' labels: - '*' From 5f455bf059df458bf161304ee52efe5e353d9b1d Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Tue, 6 Feb 2024 20:44:07 +0800 Subject: [PATCH 63/71] release: v4.0.0-dev8 --- .changelog/v4.0.0-dev8.md | 11 +++++++++++ Writerside/v.list | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .changelog/v4.0.0-dev8.md diff --git a/.changelog/v4.0.0-dev8.md b/.changelog/v4.0.0-dev8.md new file mode 100644 index 00000000..68bd4eb5 --- /dev/null +++ b/.changelog/v4.0.0-dev8.md @@ -0,0 +1,11 @@ +> 对应核心版本: [**v4.0.0-dev18**](https://github.com/simple-robot/simpler-robot/releases/tag/v4.0.0-dev18) + + +> [!warning] +> **目前版本处于 `dev` 阶段,代表此版本是一个开发预览版,可能不稳定、可能随时发生更改、且不保证可用性。** + + +我们欢迎并期望着您的的[反馈](https://github.com/simple-robot/simbot-component-qq-guild/issues)或[协助](https://github.com/simple-robot/simbot-component-qq-guild/pulls), +感谢您的贡献与支持! + +也欢迎您为我们献上一颗 `star`,这是对我们最大的鼓励与认可! diff --git a/Writerside/v.list b/Writerside/v.list index bafb3995..7e03a8cf 100644 --- a/Writerside/v.list +++ b/Writerside/v.list @@ -3,6 +3,6 @@ - + From 0012f1ff2abaa4524c5a8174bc497eb6b9663dd8 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Wed, 7 Feb 2024 22:07:37 +0800 Subject: [PATCH 64/71] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Writerside/topics/bot-config.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Writerside/topics/bot-config.md b/Writerside/topics/bot-config.md index e67d653d..9bbd598a 100644 --- a/Writerside/topics/bot-config.md +++ b/Writerside/topics/bot-config.md @@ -112,6 +112,13 @@ bot开发配置中的 `Token`。 当值为特殊值:`"SANDBOX"` 时会选择使用 `QQGuild.SANDBOX_URL_STRING`, 也就是沙箱服务器地址。 +| 配置值 | 实际值 | +|-------------|---------------------------------------| +| `null` | `"https://api.sgroup.qq.com"` | +| `"SANDBOX"` | `"https://sandbox.api.sgroup.qq.com"` | +| 其他 | 与配置值一致 | + + From 3c4294e96f897eed3d036a051342615604a787e8 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Tue, 20 Feb 2024 15:59:33 +0800 Subject: [PATCH 65/71] FUNDING.yml dependabot.yml --- .github/FUNDING.yml | 13 +++++++++++++ .github/dependabot.yml | 7 +++++++ 2 files changed, 20 insertions(+) create mode 100644 .github/FUNDING.yml create mode 100644 .github/dependabot.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 00000000..16d8cbbd --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,13 @@ +# These are supported funding model platforms + +github: ForteScarlet +patreon: ForteScarlet +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..be45766e --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + - package-ecosystem: gradle + directory: / + target-branch: "v4-dev" + schedule: + interval: daily From 1194042d064f13798f5041704724927db3d57a23 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Tue, 27 Feb 2024 19:17:27 +0800 Subject: [PATCH 66/71] dependabot.yml --- .github/dependabot.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index be45766e..9f225ac7 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -2,6 +2,8 @@ version: 2 updates: - package-ecosystem: gradle directory: / - target-branch: "v4-dev" + target-branch: "dev/main" schedule: interval: daily + labels: + - dependencies From 40b97bfcab5e52f9bc34bca0365c1614a3daaad8 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Tue, 27 Feb 2024 19:18:17 +0800 Subject: [PATCH 67/71] =?UTF-8?q?doc:=20=E8=B4=A1=E7=8C=AE=E6=8C=87?= =?UTF-8?q?=E5=8D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/CONTRIBUTING.md | 14 ++++ docs/CONTRIBUTING_CN.md | 152 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 docs/CONTRIBUTING.md create mode 100644 docs/CONTRIBUTING_CN.md diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 00000000..8dedca36 --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,14 @@ +[中文](CONTRIBUTING_CN.md) | _English_ + +# How to contribute + +**♥** First of all, thank you very much for taking the time to read this guide and learn how you can contribute to Simple Robot! **♥** + + +## I found a bug + +### Issues + +### Pull Request + +TODO diff --git a/docs/CONTRIBUTING_CN.md b/docs/CONTRIBUTING_CN.md new file mode 100644 index 00000000..7720ca09 --- /dev/null +++ b/docs/CONTRIBUTING_CN.md @@ -0,0 +1,152 @@ +_中文_ | [English](CONTRIBUTING.md) + +# 如何参与贡献 + +**♥** 首先,非常感谢您愿意花时间阅读本指南、并了解如何为 Simple Robot 做出贡献! **♥** + +> [!note] +> 本指南大部分内容与 [核心库](https://github.com/simple-robot/simpler-robot) +> 的 **贡献指南** 类似。 + +## 变得友好 + +不论是在提交反馈、提交贡献还是社区讨论,我们都应做到友好地对待一切。 +不使用过激言语,不对他人进行人身攻击或言语骚扰,不歧视任何地区/国家/种族/性别,不违反公序良俗与法律道德。 + +## 我发现了 Bug +### Issues + +如果您在QQ频道组件库或官方维护的组件库中发现了一个 bug,那么首先您可以前往 +[Issues](https://github.com/simple-robot/simbot-component-qq-guild/issues/) +中选择合适的分类并提交与此 bug 相关的详细信息 (包括使用的库版本、错误日志等); + +在反馈时,我们建议您使用 Markdown 语法[^about write] 来提高反馈内容的可读性与观感,这也可以帮助参与者快速阅读与定位问题所在。 + +[^about write]: [关于在 GitHub 上编写和设置格式](https://docs.github.com/zh/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/about-writing-and-formatting-on-github) + +### Pull Request + +如果您有兴趣与能力为我们贡献此 bug 的修复, +那么欢迎您通过 [Pull Request](https://github.com/simple-robot/simbot-component-qq-guild/pulls) 提交您的变更。 +当您提交代码变更时,需要了解如下几点: + +**代码贡献** + +您可以在 [代码贡献](#代码贡献) 小节阅读到更多有关代码贡献的细节。 + +**参与者** + +包括核心库与官方组件库在内,它们都会在项目中存在配置参与者的地方。 +本指南以QQ频道组件库为例,在 `buildSrc/src/main/kotlin/P.kt` 中的如下代码内添加有关您的信息: + +```Kotlin +override val developers: List = developers { + developer { + ... + } + ... + + 此处添加您的信息 +} +``` + +## 讨论区贡献 + +在 [讨论区][讨论区] 留下你的疑问或回答, +为其他来者留下一盏指路的明灯~ + +## 代码贡献 +### 注释风格 + +QQ频道组件库对源代码的注释有着一些约定。 + +* 要对所有访问级别为 `public`、`protected` 的内容 (包括类/接口、函数、属性等) + 使用**中文**或**易懂的英文**编写较为细致的文档注释[^KDoc]。 + 适当地添加文档 tag (例如 `@param`、`@throws`),不过如果在文档正文中已经提及那么也可省略。 + +* 内部访问级别 (例如 `private`、`internal`) 的内容,如果十分简单也可以不用编写完全细致的**文档注释**, + 但应适当添加一些有助于其他开发者阅读与理解的解释性注释。 + +* 被重写的抽象函数,如果其含义没有变化,那么可以省略文档注释。 + +* 对于类和接口类型,如果是新增的内容,尽可能添加您的作者信息 (`@author`) 和版本信息 (`@since`)。 + 不要添加非标准的 tag (比如 `@Date`、`@HelloWorld`)。 + +[^KDoc]: [Document Kotlin code: KDoc](https://kotlinlang.org/docs/kotlin-doc.html) + +### 代码风格 + +QQ频道组件库对整体的代码风格有着一些约定。 + +这些代码风格的大部分规则被作为配置文件保存在了 `.editorconfig` 中,部分IDE (例如 IDEA) 应当可以自动识别加载、或手动加载它。 +这些风格与 +[Kotlin Coding conventions](https://kotlinlang.org/docs/coding-conventions.html) +中的大部分描述很类似,你也可以以它为参考。 + +下面会再列举一些常见的风格约定。 + +* 中缀表达式 (例如 `infix` 或数字运算符 `+` 、`-` 等) 前后应当包含一个空格。例如应该是 `a += b` 而不是 `a+=b`。 + +* 变量的名称与类型之间,`:` 之后应该有一个空格。例如应该是 `a: Int` 而不是 `a:Int`。 + +* 函数的参数 (包括函数的定义与调用时的入参) 在同一行时,分隔的 `,` 后应当有一个空格。 + 例如应该是 `foo(1, 2)` 而不是 `foo(1,2)`; 应该是 `function name(a: Int, b: Int)` 而不是 `function name(a: Int,b: Int)` + +* 定义的函数参数较多、需要换行时,各参数应各自占一行且头部对齐,且不与 `(` 和 `)` 在同一行;如果参数需要添加注解,每个注解应该各自占一行,例如: + ```Kotlin + function name( + p1: Int, + @Anno2 + @Anno1 + p2: String + ) + ``` + +### KMP + +QQ频道组件库绝大多数模块都是支持 [KMP](https://kotlinlang.org/docs/multiplatform.html) 的。 + +### 二进制兼容 + +在版本迭代中应提供更好的兼容性保证。因此当一个 API 被废弃或需要被修改时,应保证修改后的内容对旧版本的二进制兼容, +并添加适当的废弃说明与警告。 + +新增的实验性内容也应当标记合适的 Opt 注解与适当的提示来向开发者发出警告。 + +### 友好 API + +QQ频道组件库对外提供的公开API中,都应该是 Java 友好、或有配套的 Java 友好 API,尤其指可挂起函数 `suspend fun`。 +大部分挂起函数的 Java 友好 API 通过编译器插件 +[Kotlin Suspend Transform compiler plugin](https://github.com/ForteScarlet/kotlin-suspend-transform-compiler-plugin) 实现, +可参阅手册中的 +[Java友好](https://simbot.forte.love/java-friendly.html) 章节 +和 +[组件开发 - 编译器插件](https://simbot.forte.love/component-dev-compiler-plugin.html) +章节。 + +## 文档贡献 +### API文档 + +API文档是使用 [Dokka](https://github.com/Kotlin/dokka) 根据源码的文档注释生成的。你可以在小节 [注释风格](#注释风格) +中了解更多。 + +### 手册贡献 + +QQ频道组件库手册使用 [Writerside](https://www.jetbrains.com/help/writerside) 编写, +你可以在目录 `Writerside` 中找到它的内容。 + +## 社区贡献 +### 应用作品 + +基于 simbot 开发的应用项目,并为项目标记 `simbot` tag,为社区生态添注活力! + +### 组件库/插件库/其他 + +基于 simbot 开发组件库/插件库,并为项目标记 `simbot` tag,为社区生态添注活力! +你可以: +- 前往手册的 [组件库开发](https://simbot.forte.love/component-dev.html) 了解更多开发组件库的细节! +- 前往 [讨论区][讨论区] 向其他人或开发团队寻求协助! +- 前往 [组织首页][组织首页] 添加其中的 **社群**,并寻求协助! + +[组织首页]: https://github.com/simple-robot +[讨论区]: https://github.com/orgs/simple-robot/discussions From ad9dda08f944053f590ebe9aada81e41f3fd2d14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 11:20:11 +0000 Subject: [PATCH 68/71] Bump kotlinx-coroutines from 1.8.0-RC2 to 1.8.0 Bumps `kotlinx-coroutines` from 1.8.0-RC2 to 1.8.0. Updates `org.jetbrains.kotlinx:kotlinx-coroutines-core` from 1.8.0-RC2 to 1.8.0 - [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases) - [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md) - [Commits](https://github.com/Kotlin/kotlinx.coroutines/compare/1.8.0-RC2...1.8.0) Updates `org.jetbrains.kotlinx:kotlinx-coroutines-jdk8` from 1.8.0-RC2 to 1.8.0 - [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases) - [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md) - [Commits](https://github.com/Kotlin/kotlinx.coroutines/compare/1.8.0-RC2...1.8.0) Updates `org.jetbrains.kotlinx:kotlinx-coroutines-reactive` from 1.8.0-RC2 to 1.8.0 - [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases) - [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md) - [Commits](https://github.com/Kotlin/kotlinx.coroutines/compare/1.8.0-RC2...1.8.0) Updates `org.jetbrains.kotlinx:kotlinx-coroutines-reactor` from 1.8.0-RC2 to 1.8.0 - [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases) - [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md) - [Commits](https://github.com/Kotlin/kotlinx.coroutines/compare/1.8.0-RC2...1.8.0) Updates `org.jetbrains.kotlinx:kotlinx-coroutines-rx2` from 1.8.0-RC2 to 1.8.0 - [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases) - [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md) - [Commits](https://github.com/Kotlin/kotlinx.coroutines/compare/1.8.0-RC2...1.8.0) Updates `org.jetbrains.kotlinx:kotlinx-coroutines-rx3` from 1.8.0-RC2 to 1.8.0 - [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases) - [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md) - [Commits](https://github.com/Kotlin/kotlinx.coroutines/compare/1.8.0-RC2...1.8.0) Updates `org.jetbrains.kotlinx:kotlinx-coroutines-guava` from 1.8.0-RC2 to 1.8.0 - [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases) - [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md) - [Commits](https://github.com/Kotlin/kotlinx.coroutines/compare/1.8.0-RC2...1.8.0) Updates `org.jetbrains.kotlinx:kotlinx-coroutines-slf4j` from 1.8.0-RC2 to 1.8.0 - [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases) - [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md) - [Commits](https://github.com/Kotlin/kotlinx.coroutines/compare/1.8.0-RC2...1.8.0) Updates `org.jetbrains.kotlinx:kotlinx-coroutines-play-services` from 1.8.0-RC2 to 1.8.0 - [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases) - [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md) - [Commits](https://github.com/Kotlin/kotlinx.coroutines/compare/1.8.0-RC2...1.8.0) Updates `org.jetbrains.kotlinx:kotlinx-coroutines-debug` from 1.8.0-RC2 to 1.8.0 - [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases) - [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md) - [Commits](https://github.com/Kotlin/kotlinx.coroutines/compare/1.8.0-RC2...1.8.0) Updates `org.jetbrains.kotlinx:kotlinx-coroutines-test` from 1.8.0-RC2 to 1.8.0 - [Release notes](https://github.com/Kotlin/kotlinx.coroutines/releases) - [Changelog](https://github.com/Kotlin/kotlinx.coroutines/blob/master/CHANGES.md) - [Commits](https://github.com/Kotlin/kotlinx.coroutines/compare/1.8.0-RC2...1.8.0) --- updated-dependencies: - dependency-name: org.jetbrains.kotlinx:kotlinx-coroutines-core dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jetbrains.kotlinx:kotlinx-coroutines-jdk8 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jetbrains.kotlinx:kotlinx-coroutines-reactive dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jetbrains.kotlinx:kotlinx-coroutines-reactor dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jetbrains.kotlinx:kotlinx-coroutines-rx2 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jetbrains.kotlinx:kotlinx-coroutines-rx3 dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jetbrains.kotlinx:kotlinx-coroutines-guava dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jetbrains.kotlinx:kotlinx-coroutines-slf4j dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jetbrains.kotlinx:kotlinx-coroutines-play-services dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jetbrains.kotlinx:kotlinx-coroutines-debug dependency-type: direct:production update-type: version-update:semver-patch - dependency-name: org.jetbrains.kotlinx:kotlinx-coroutines-test dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e063d7b3..c5fc14b7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,6 +1,6 @@ [versions] kotlin = "1.9.22" -kotlinx-coroutines = "1.8.0-RC2" +kotlinx-coroutines = "1.8.0" kotlinx-serialization = "1.6.2" kotlinx-datetime = "0.5.0" dokka = "1.9.10" From 95350d35f710b2497fcf835bf025f5d0885ff24c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 11:20:42 +0000 Subject: [PATCH 69/71] Bump openjdk-jmh from 1.35 to 1.37 Bumps `openjdk-jmh` from 1.35 to 1.37. Updates `org.openjdk.jmh:jmh-core` from 1.35 to 1.37 - [Commits](https://github.com/openjdk/jmh/compare/1.35...1.37) Updates `org.openjdk.jmh:jmh-generator-annprocess` from 1.35 to 1.37 - [Commits](https://github.com/openjdk/jmh/compare/1.35...1.37) --- updated-dependencies: - dependency-name: org.openjdk.jmh:jmh-core dependency-type: direct:production update-type: version-update:semver-minor - dependency-name: org.openjdk.jmh:jmh-generator-annprocess dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e063d7b3..d6915fd2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -6,7 +6,7 @@ kotlinx-datetime = "0.5.0" dokka = "1.9.10" okio = "3.3.0" ktor = "2.3.7" -openjdk-jmh = "1.35" +openjdk-jmh = "1.37" log4j = "2.20.0" reactor = "3.6.2" # simbot From 945a3a571989980107635b2122abd84e5346a71f Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Sun, 3 Mar 2024 14:54:30 +0800 Subject: [PATCH 70/71] =?UTF-8?q?refactor:=20=E4=B8=80=E4=BA=9B=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E3=80=81=E6=B3=A8=E9=87=8A=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/publish-release.yml | 4 +++- buildSrc/src/main/kotlin/P.kt | 2 +- gradle/libs.versions.toml | 2 +- .../qguild/internal/bot/QQGuildBotManagerImpl.kt | 7 +------ .../forte/simbot/component/qguild/message/QGImages.kt | 8 +++++++- 5 files changed, 13 insertions(+), 10 deletions(-) diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml index fcfa0351..d8557bcb 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/publish-release.yml @@ -80,7 +80,9 @@ jobs: token: ${{ secrets.PUSH_TOKEN }} body_path: .changelog/${{ github.ref_name }}.md generate_release_notes: true - prerelease: ${{ contains(github.ref_name, 'preview') || contains(github.ref_name, 'beta') || contains(github.ref_name, 'alpha') }} + prerelease: ${{ contains(github.ref_name, 'preview') || contains(github.ref_name, 'alpha') }} + + # || contains(github.ref_name, 'beta') publish-snapshot: name: Publish snapshot diff --git a/buildSrc/src/main/kotlin/P.kt b/buildSrc/src/main/kotlin/P.kt index 37e948dd..0af4a48d 100644 --- a/buildSrc/src/main/kotlin/P.kt +++ b/buildSrc/src/main/kotlin/P.kt @@ -59,7 +59,7 @@ object P { override val homepage: String get() = HOMEPAGE - private val baseVersion = v(4, 0, 0) - v("dev8") + private val baseVersion = v(4, 0, 0) - v("beta1") val snapshotVersion = baseVersion - Version.SNAPSHOT override val version = if (isSnapshot()) snapshotVersion else baseVersion diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 12f08f65..c7b99d2a 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -10,7 +10,7 @@ openjdk-jmh = "1.37" log4j = "2.20.0" reactor = "3.6.2" # simbot -simbot = "4.0.0-dev18" +simbot = "4.0.0-beta1" suspendTransform = "0.7.0-beta1" gradleCommon = "0.2.0" # ksp diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt index 9b22896a..d9dc1b6d 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/internal/bot/QQGuildBotManagerImpl.kt @@ -19,7 +19,6 @@ package love.forte.simbot.component.qguild.internal.bot import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob -import kotlinx.coroutines.cancel import love.forte.simbot.bot.ConflictBotException import love.forte.simbot.bot.JobBasedBotManager import love.forte.simbot.bot.NoSuchBotException @@ -72,11 +71,7 @@ internal class QQGuildBotManagerImpl( init { job.invokeOnCompletion { - // TODO to clear - bots.removeIf { - it.cancel("BotManager was on completion") - true - } + bots.clear() } } diff --git a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGImages.kt b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGImages.kt index 8c1a3dd2..3d260388 100644 --- a/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGImages.kt +++ b/simbot-component-qq-guild-core/src/commonMain/kotlin/love/forte/simbot/component/qguild/message/QGImages.kt @@ -17,14 +17,20 @@ package love.forte.simbot.component.qguild.message +import love.forte.simbot.message.Image +import love.forte.simbot.message.OfflineImage + /** * 用于发送的图片类型 + * @see Image + * @suppress TODO */ public class QGOfflineImage // TODO /** * 收到的图片类型 - * + * @see OfflineImage + * @suppress TODO */ public class QGImage // TODO From 43917e34cbaeeb29389f8c7138b117b1cffeb5d0 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Sun, 3 Mar 2024 14:55:09 +0800 Subject: [PATCH 71/71] release: v4.0.0-beta1 --- .changelog/v4.0.0-beta1.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changelog/v4.0.0-beta1.md diff --git a/.changelog/v4.0.0-beta1.md b/.changelog/v4.0.0-beta1.md new file mode 100644 index 00000000..1befaf75 --- /dev/null +++ b/.changelog/v4.0.0-beta1.md @@ -0,0 +1,7 @@ +> 对应核心版本: [**v4.0.0-beta1**](https://github.com/simple-robot/simpler-robot/releases/tag/v4.0.0-beta1) + + +我们欢迎并期望着您的的[反馈](https://github.com/simple-robot/simbot-component-qq-guild/issues)或[协助](https://github.com/simple-robot/simbot-component-qq-guild/pulls), +感谢您的贡献与支持! + +也欢迎您为我们献上一颗 `star`,这是对我们最大的鼓励与认可!