From dc033b15dcf9cb3fd41b934dc238636f84b577cf Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Fri, 21 Jun 2024 14:10:49 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat(core):=20OneBotMember=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E8=AE=BE=E7=BD=AE=E5=A4=B4=E8=A1=94API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-website.yml | 2 +- Writerside/topics/onebot11-OneBotMember.md | 46 ++++++++++++++++++ .../onebot/v11/core/actor/OneBotMember.kt | 47 +++++++++++++++++++ .../core/actor/internal/OneBotMemberImpl.kt | 31 ++++++++++++ .../v11/core/api/GetGroupMemberInfoApi.kt | 24 +++++----- 5 files changed, 137 insertions(+), 13 deletions(-) diff --git a/.github/workflows/deploy-website.yml b/.github/workflows/deploy-website.yml index ef5110b..2e3b911 100644 --- a/.github/workflows/deploy-website.yml +++ b/.github/workflows/deploy-website.yml @@ -4,7 +4,7 @@ on: branches: - main - master - - dev/main +# - dev/main paths: - 'Writerside/**' - '.github/workflows/deploy-website.yml' diff --git a/Writerside/topics/onebot11-OneBotMember.md b/Writerside/topics/onebot11-OneBotMember.md index a9fd525..ae4e87d 100644 --- a/Writerside/topics/onebot11-OneBotMember.md +++ b/Writerside/topics/onebot11-OneBotMember.md @@ -418,6 +418,52 @@ member.unbanReserve() +### 设置头衔 + +可以通过 `setSpecialTitle(String?)` 设置此成员在群内的特殊头衔。 + + + +想要获取头衔,可以通过 +[获取原始类型](#获取原始类型) +取到 `title`。 + + + + + + +```Kotlin +val member: OneBotMember = ... +member.setSpecialTitle("newTitle") +``` + + + + +```Java +OneBotMember member = ...; +member.setSpecialTitleAsync("newTitle"); +``` +{switcher-key=%ja%} + +```Java +OneBotMember member = ...; +member.setSpecialTitleBlocking("newTitle"); +``` +{switcher-key=%jb%} + +```Java +OneBotMember member = ...; +member.setSpecialTitleReserve("newTitle") + .transform(SuspendReserves.mono()) + .subscribe(); +``` +{switcher-key=%jr%} + + + + ## 获取 OneBotMember diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/OneBotMember.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/OneBotMember.kt index 617cc52..31c8fd3 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/OneBotMember.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/OneBotMember.kt @@ -128,6 +128,53 @@ public interface OneBotMember : Member, DeleteSupport, OneBotStrangerAware { @ST public suspend fun getSourceMemberInfo(): GetGroupMemberInfoResult + /** + * 使用 [SetGroupSpecialTitleApi] 为群成员设置专属头衔。 + * 通常要求bot拥有群主权限。 + * + * 如果想要获取当前成员的专属头衔,使用 [getSourceMemberInfo] + * 后可通过 [GetGroupMemberInfoResult.title] 获取。 + * + * @param specialTitle 要设置的新头衔。为空或为 `null` 则代表取消头衔。 + * @throws Throwable 任何可能在请求API过程中产生的异常。 + */ + @ST + public suspend fun setSpecialTitle(specialTitle: String? = null) + + /** + * 使用 [SetGroupSpecialTitleApi] 为群成员设置专属头衔。 + * 通常要求bot拥有群主权限。 + * + * 如果想要获取当前成员的专属头衔,使用 [getSourceMemberInfo] + * 后可通过 [GetGroupMemberInfoResult.title] 获取。 + * + * @param specialTitle 要设置的新头衔。为空或为 `null` 则代表取消头衔。 + * @param duration 有效期,最终取秒值,如果小于0则会被忽略。 + * 不一定有效果,更多说明参考 [SetGroupSpecialTitleApi] 和 [SetGroupSpecialTitleApi.create]。 + * @param timeUnit [duration] 的时间单位。 + * @throws Throwable 任何可能在请求API过程中产生的异常。 + */ + @ST + @JvmSynthetic + public suspend fun setSpecialTitle(specialTitle: String, duration: Long, timeUnit: TimeUnit) + + /** + * 使用 [SetGroupSpecialTitleApi] 为群成员设置专属头衔。 + * 通常要求bot拥有群主权限。 + * + * 如果想要获取当前成员的专属头衔,使用 [getSourceMemberInfo] + * 后可通过 [GetGroupMemberInfoResult.title] 获取。 + * + * @param specialTitle 要设置的新头衔。为空或为 `null` 则代表取消头衔。 + * @param duration 有效期,最终取秒值,如果小于0则会被忽略。 + * 不一定有效果,更多说明参考 [SetGroupSpecialTitleApi] 和 [SetGroupSpecialTitleApi.create]。 + * @throws Throwable 任何可能在请求API过程中产生的异常。 + */ + @JvmSynthetic + public suspend fun setSpecialTitle(specialTitle: String, duration: Duration) + + + /** * 向此成员发送消息。 * diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotMemberImpl.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotMemberImpl.kt index 3d618dd..c7efa77 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotMemberImpl.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotMemberImpl.kt @@ -183,6 +183,37 @@ internal abstract class OneBotMemberImpl( ).requestDataBy(bot) } + override suspend fun setSpecialTitle(specialTitle: String?) { + setSpecialTitle0(specialTitle, null) + } + + override suspend fun setSpecialTitle(specialTitle: String, duration: Long, timeUnit: TimeUnit) { + if (duration < 0L) { + setSpecialTitle0(specialTitle, null) + } else { + setSpecialTitle0(specialTitle, timeUnit.toSeconds(duration)) + } + } + + override suspend fun setSpecialTitle(specialTitle: String, duration: Duration) { + val seconds = duration.inWholeSeconds + + if (seconds < 0L) { + setSpecialTitle0(specialTitle, null) + } else { + setSpecialTitle0(specialTitle, seconds) + } + } + + private suspend fun setSpecialTitle0(specialTitle: String?, duration: Long?) { + SetGroupSpecialTitleApi.create( + groupId = groupIdOrFailure, + userId = id, + specialTitle = specialTitle, + duration = duration + ).requestDataBy(bot) + } + override suspend fun setNick(newNick: String?) { SetGroupCardApi.create( groupId = groupIdOrFailure, diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/api/GetGroupMemberInfoApi.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/api/GetGroupMemberInfoApi.kt index d35b033..af529e5 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/api/GetGroupMemberInfoApi.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/api/GetGroupMemberInfoApi.kt @@ -109,20 +109,20 @@ public data class GetGroupMemberInfoResult @ApiResultConstructor constructor( @SerialName("user_id") public val userId: LongID, public val nickname: String, - public val card: String, - public val sex: String, - public val age: Int, - public val area: String, + public val card: String = "", + public val sex: String = "unknown", + public val age: Int = -1, + public val area: String = "", @SerialName("join_time") - public val joinTime: Int, + public val joinTime: Int = -1, @SerialName("last_sent_time") - public val lastSentTime: Int, - public val level: String, - public val role: String, - public val unfriendly: Boolean, - public val title: String, + public val lastSentTime: Int = -1, + public val level: String = "", + public val role: String = "member", + public val unfriendly: Boolean = false, + public val title: String = "", @SerialName("title_expire_time") - public val titleExpireTime: Int, + public val titleExpireTime: Int = -1, @SerialName("card_changeable") - public val cardChangeable: Boolean, + public val cardChangeable: Boolean = false, ) From 0d531ea82113d87a1973e2e3bce57cd3efc21e8b Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Fri, 21 Jun 2024 15:11:02 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat(core):=20OneBotGroup=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=20setAnonymous?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Writerside/topics/onebot11-OneBotGroup.md | 41 ++++++++++++++++++- .../onebot/v11/core/actor/OneBotGroup.kt | 14 +++++-- .../onebot/v11/core/actor/OneBotMember.kt | 2 - .../core/actor/internal/OneBotGroupImpl.kt | 8 ++++ 4 files changed, 58 insertions(+), 7 deletions(-) diff --git a/Writerside/topics/onebot11-OneBotGroup.md b/Writerside/topics/onebot11-OneBotGroup.md index c6eb0db..a179591 100644 --- a/Writerside/topics/onebot11-OneBotGroup.md +++ b/Writerside/topics/onebot11-OneBotGroup.md @@ -265,7 +265,6 @@ group.setNameReserve("newName") - ### 设置bot群备注 可以通过 `setBotGroupNick(String?)` 来设置bot在群内的群备注。 @@ -398,6 +397,46 @@ group.getAllHonorInfoReserve() - `strong_newbie` - `emotion` +### 设置匿名聊天 + +可以通过 `setAnonymous(Boolean)` 来设置是否允许匿名聊天,`true` 为开启。 + +> 这可能需要bot拥有管理权限。 + + + + +```Kotlin +val group: OneBotGroup = ... +group.setAnonymous(true) +``` + + + + +```Java +OneBotGroup group = ...; +group.setAnonymousAsync(true); +``` +{switcher-key=%ja%} + +```Java +OneBotGroup group = ...; +group.setAnonymousBlocking(true); +``` +{switcher-key=%jb%} + +```Java +OneBotGroup group = ...; +group.setAnonymousReserve(true) + .transform(SuspendReserves.mono()) + .subscribe(); +``` +{switcher-key=%jr%} + + + + ## 获取 OneBotGroup `OneBotGroup` 主要来自 `OneBotBot` 获取或与群相关的事件。 diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/OneBotGroup.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/OneBotGroup.kt index f53540a..a1ba96d 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/OneBotGroup.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/OneBotGroup.kt @@ -24,10 +24,7 @@ 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.component.onebot.common.annotations.OneBotInternalImplementationsOnly -import love.forte.simbot.component.onebot.v11.core.api.GetGroupHonorInfoApi -import love.forte.simbot.component.onebot.v11.core.api.GetGroupHonorInfoResult -import love.forte.simbot.component.onebot.v11.core.api.SetGroupAdminApi -import love.forte.simbot.component.onebot.v11.core.api.SetGroupLeaveApi +import love.forte.simbot.component.onebot.v11.core.api.* import love.forte.simbot.component.onebot.v11.core.bot.OneBotBot import love.forte.simbot.component.onebot.v11.message.OneBotMessageReceipt import love.forte.simbot.definition.ChatGroup @@ -136,6 +133,15 @@ public interface OneBotGroup : ChatGroup, DeleteSupport { @STP override suspend fun botAsMember(): OneBotMember + /** + * 使用 [SetGroupAnonymousApi] 设置群组匿名(的开关)。 + * + * @param enable 是否开启(允许)匿名聊天。true为允许。 + * @throws Throwable 任何可能在请求API过程中产生的异常。 + */ + @ST + public suspend fun setAnonymous(enable: Boolean) + /** * 向此群内发送消息。 * diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/OneBotMember.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/OneBotMember.kt index 31c8fd3..c8c24f5 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/OneBotMember.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/OneBotMember.kt @@ -173,8 +173,6 @@ public interface OneBotMember : Member, DeleteSupport, OneBotStrangerAware { @JvmSynthetic public suspend fun setSpecialTitle(specialTitle: String, duration: Duration) - - /** * 向此成员发送消息。 * diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotGroupImpl.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotGroupImpl.kt index 9acfcdd..e1d2827 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotGroupImpl.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotGroupImpl.kt @@ -75,6 +75,14 @@ internal abstract class OneBotGroupImpl( return result.dataOrThrow.toMember(bot) } + + override suspend fun setAnonymous(enable: Boolean) { + SetGroupAnonymousApi.create( + groupId = id, + enable = enable + ).requestDataBy(bot) + } + override suspend fun send(text: String): OneBotMessageReceipt { return sendGroupTextMsgApi( target = id, From cb6b1a461459727f337ab924066ac5503ce114a4 Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Fri, 21 Jun 2024 16:18:17 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20OneBot=E9=85=8D=E7=BD=AE=E4=B8=AD?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E9=85=8D=E7=BD=AE=E9=BB=98=E8=AE=A4=E7=9A=84?= =?UTF-8?q?=20ImageAdditional=20=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Writerside/topics/onebot11-bot-config.md | 71 +++++++++++-------- buildSrc/src/main/kotlin/P.kt | 2 +- .../onebot/v11/core/OneBot11Usage.kt | 4 +- .../core/actor/internal/OneBotFriendImpl.kt | 4 +- .../core/actor/internal/OneBotGroupImpl.kt | 4 +- .../core/actor/internal/OneBotMemberImpl.kt | 4 +- .../v11/core/bot/OneBotBotConfiguration.kt | 26 +++++++ .../bot/OneBotBotSerializableConfiguration.kt | 27 ++++++- .../message/OneBotGroupMessageEventImpl.kt | 4 +- .../message/OneBotPrivateMessageEventImpl.kt | 4 +- .../onebot/v11/core/utils/MessageResolvers.kt | 29 ++++++++ .../kotlin/BotDefaultImageAdditionalTests.kt | 71 +++++++++++++++++++ .../v11/message/MessageElementResolvers.kt | 55 ++++++++------ .../v11/message/MessageElementResolvers.js.kt | 10 ++- .../message/MessageElementResolvers.jvm.kt | 28 +++++--- .../message/MessageElementResolvers.native.kt | 10 ++- 16 files changed, 277 insertions(+), 76 deletions(-) create mode 100644 simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/utils/MessageResolvers.kt create mode 100644 simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/jvmTest/kotlin/BotDefaultImageAdditionalTests.kt diff --git a/Writerside/topics/onebot11-bot-config.md b/Writerside/topics/onebot11-bot-config.md index 84db472..d107303 100644 --- a/Writerside/topics/onebot11-bot-config.md +++ b/Writerside/topics/onebot11-bot-config.md @@ -1,7 +1,5 @@ # Bot配置文件 -TODO - Bot配置文件通常情况下是配合Spring Boot starter的时候用的。 当使用Spring Boot starter时, @@ -19,33 +17,45 @@ Bot配置文件通常情况下是配合Spring Boot starter的时候用的。 ```json { - // 固定值 - "component": "simbot.onebot11", - "authorization": { - // 唯一ID,作为组件内 Bot 的 id,用于组件内去重。可以随便编,但建议是bot的qq号 - "botUniqueId": "123456", - // api地址,是个http/https服务器的路径,默认localhost:3000 - "apiServerHost": "http://localhost:3000", - // 订阅事件的服务器地址,是个ws/wss路径,默认localhost:3001 - "eventServerHost":"ws://localhost:3001", - // 配置的 token,可以是null - "accessToken": null - }, - // 额外的可选配置 - // config本身以及其内的各项属性绝大多数都可省略或null - "config": { - // API请求中的超时请求配置。整数数字,单位毫秒,默认为 `null`。 - "apiHttpRequestTimeoutMillis": null, - // API请求中的超时请求配置。整数数字,单位毫秒,默认为 `null`。 - "apiHttpConnectTimeoutMillis": null, - // API请求中的超时请求配置。整数数字,单位毫秒,默认为 `null`。 - "apiHttpSocketTimeoutMillis": null, - // 每次尝试连接到 ws 服务时的最大重试次数,大于等于0的整数,默认为 2147483647 - "wsConnectMaxRetryTimes": null, - // 每次尝试连接到 ws 服务时,如果需要重新尝试,则每次尝试之间的等待时长 - // 整数数字,单位毫秒,默认为 3500 - "wsConnectRetryDelayMillis": null, - } + // 固定值 + "component": "simbot.onebot11", + "authorization": { + // 唯一ID,作为组件内 Bot 的 id,用于组件内去重。可以随便编,但建议是bot的qq号 + "botUniqueId": "123456", + // api地址,是个http/https服务器的路径,默认localhost:3000 + "apiServerHost": "http://localhost:3000", + // 订阅事件的服务器地址,是个ws/wss路径,默认localhost:3001 + "eventServerHost": "ws://localhost:3001", + // 配置的 token,可以是null + "accessToken": null + }, + // 额外的可选配置 + // config本身以及其内的各项属性绝大多数都可省略或null + "config": { + // API请求中的超时请求配置。整数数字,单位毫秒,默认为 `null`。 + "apiHttpRequestTimeoutMillis": null, + // API请求中的超时请求配置。整数数字,单位毫秒,默认为 `null`。 + "apiHttpConnectTimeoutMillis": null, + // API请求中的超时请求配置。整数数字,单位毫秒,默认为 `null`。 + "apiHttpSocketTimeoutMillis": null, + // 每次尝试连接到 ws 服务时的最大重试次数,大于等于0的整数,默认为 2147483647 + "wsConnectMaxRetryTimes": null, + // 每次尝试连接到 ws 服务时,如果需要重新尝试,则每次尝试之间的等待时长 + // 整数数字,单位毫秒,默认为 3500 + "wsConnectRetryDelayMillis": null, + // 当使用非 [OneBotImage] 类型作为图片资源发送消息时, + // 默认根据 [Resource] 得到一个可能存在的 [OneBotImage.AdditionalParams]。 + // 注意!这无法影响直接使用 [OneBotImage] 的情况。 + // defaultImageAdditionalParams 默认为 `null`。 + "defaultImageAdditionalParams": { + // default: null + "localFileToBase64": null, + "type": null, + "cache": null, + "proxy": null, + "timeout": null + } + } } ``` @@ -66,3 +76,6 @@ Bot配置文件通常情况下是配合Spring Boot starter的时候用的。 +## 属性说明 + +TODO diff --git a/buildSrc/src/main/kotlin/P.kt b/buildSrc/src/main/kotlin/P.kt index 5e5bd27..eb07e7c 100644 --- a/buildSrc/src/main/kotlin/P.kt +++ b/buildSrc/src/main/kotlin/P.kt @@ -41,7 +41,7 @@ object P { override val homepage: String get() = HOMEPAGE - private val baseVersion = v(0, 7, 0) + private val baseVersion = v(0, 8, 0) val snapshotVersion = baseVersion - Version.SNAPSHOT override val version = if (isSnapshot()) snapshotVersion else baseVersion diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/OneBot11Usage.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/OneBot11Usage.kt index bb1a2ee..3173d5a 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/OneBot11Usage.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/OneBot11Usage.kt @@ -120,12 +120,12 @@ private class OneBot11UsageBuilderImpl : OneBot11UsageBuilder { * @throws [NoSuchElementException] if no such element is found. */ @OptIn(ExperimentalContracts::class) -public inline fun Application.oneBot11Bots(block: OneBotBotManager.() -> Unit) { +public inline fun Application.oneBot11Bots(block: OneBotBotManager.() -> T): T { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } - botManagers.firstOneBotBotManager().block() + return botManagers.firstOneBotBotManager().block() } /** diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotFriendImpl.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotFriendImpl.kt index 1aabaa6..6bdbe34 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotFriendImpl.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotFriendImpl.kt @@ -26,12 +26,12 @@ import love.forte.simbot.component.onebot.v11.core.api.SendLikeApi import love.forte.simbot.component.onebot.v11.core.bot.internal.OneBotBotImpl import love.forte.simbot.component.onebot.v11.core.bot.requestDataBy import love.forte.simbot.component.onebot.v11.core.internal.message.toReceipt +import love.forte.simbot.component.onebot.v11.core.utils.resolveToOneBotSegmentList import love.forte.simbot.component.onebot.v11.core.utils.sendPrivateMsgApi import love.forte.simbot.component.onebot.v11.core.utils.sendPrivateTextMsgApi import love.forte.simbot.component.onebot.v11.event.message.RawPrivateMessageEvent import love.forte.simbot.component.onebot.v11.message.OneBotMessageContent import love.forte.simbot.component.onebot.v11.message.OneBotMessageReceipt -import love.forte.simbot.component.onebot.v11.message.resolveToOneBotSegmentList import love.forte.simbot.message.Message import love.forte.simbot.message.MessageContent import kotlin.coroutines.CoroutineContext @@ -60,7 +60,7 @@ internal abstract class OneBotFriendImpl : OneBotFriend { override suspend fun send(message: Message): OneBotMessageReceipt { return sendPrivateMsgApi( target = id, - message = message.resolveToOneBotSegmentList() + message = message.resolveToOneBotSegmentList(bot) ).requestDataBy(bot).toReceipt(bot) } diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotGroupImpl.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotGroupImpl.kt index e1d2827..04cdf8a 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotGroupImpl.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotGroupImpl.kt @@ -30,11 +30,11 @@ import love.forte.simbot.component.onebot.v11.core.bot.internal.OneBotBotImpl import love.forte.simbot.component.onebot.v11.core.bot.requestDataBy import love.forte.simbot.component.onebot.v11.core.bot.requestResultBy import love.forte.simbot.component.onebot.v11.core.internal.message.toReceipt +import love.forte.simbot.component.onebot.v11.core.utils.resolveToOneBotSegmentList import love.forte.simbot.component.onebot.v11.core.utils.sendGroupMsgApi import love.forte.simbot.component.onebot.v11.core.utils.sendGroupTextMsgApi import love.forte.simbot.component.onebot.v11.message.OneBotMessageContent import love.forte.simbot.component.onebot.v11.message.OneBotMessageReceipt -import love.forte.simbot.component.onebot.v11.message.resolveToOneBotSegmentList import love.forte.simbot.message.Message import love.forte.simbot.message.MessageContent import kotlin.concurrent.Volatile @@ -104,7 +104,7 @@ internal abstract class OneBotGroupImpl( override suspend fun send(message: Message): OneBotMessageReceipt { return sendGroupMsgApi( target = id, - message = message.resolveToOneBotSegmentList() + message = message.resolveToOneBotSegmentList(bot) ).requestDataBy(bot).toReceipt(bot) } diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotMemberImpl.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotMemberImpl.kt index c7efa77..da394ad 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotMemberImpl.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/actor/internal/OneBotMemberImpl.kt @@ -29,13 +29,13 @@ import love.forte.simbot.component.onebot.v11.core.api.* import love.forte.simbot.component.onebot.v11.core.bot.internal.OneBotBotImpl import love.forte.simbot.component.onebot.v11.core.bot.requestDataBy import love.forte.simbot.component.onebot.v11.core.internal.message.toReceipt +import love.forte.simbot.component.onebot.v11.core.utils.resolveToOneBotSegmentList import love.forte.simbot.component.onebot.v11.core.utils.sendPrivateMsgApi import love.forte.simbot.component.onebot.v11.core.utils.sendPrivateTextMsgApi import love.forte.simbot.component.onebot.v11.event.message.RawGroupMessageEvent import love.forte.simbot.component.onebot.v11.event.message.RawPrivateMessageEvent import love.forte.simbot.component.onebot.v11.message.OneBotMessageContent import love.forte.simbot.component.onebot.v11.message.OneBotMessageReceipt -import love.forte.simbot.component.onebot.v11.message.resolveToOneBotSegmentList import love.forte.simbot.message.Message import love.forte.simbot.message.MessageContent import kotlin.concurrent.Volatile @@ -82,7 +82,7 @@ internal abstract class OneBotMemberImpl( override suspend fun send(message: Message): OneBotMessageReceipt { return sendPrivateMsgApi( target = id, - message = message.resolveToOneBotSegmentList() + message = message.resolveToOneBotSegmentList(bot) ).requestDataBy(bot).toReceipt(bot) } diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/bot/OneBotBotConfiguration.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/bot/OneBotBotConfiguration.kt index 28f8cbe..e1fa9ea 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/bot/OneBotBotConfiguration.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/bot/OneBotBotConfiguration.kt @@ -25,6 +25,9 @@ import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.overwriteWith import love.forte.simbot.common.function.ConfigurerFunction import love.forte.simbot.common.function.invokeBy +import love.forte.simbot.component.onebot.common.annotations.ExperimentalOneBotAPI +import love.forte.simbot.component.onebot.v11.message.segment.OneBotImage +import love.forte.simbot.resource.Resource import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext @@ -174,4 +177,27 @@ public class OneBotBotConfiguration { */ public var wsConnectRetryDelayMillis: Long = 3500L + /** + * 当使用非 [OneBotImage] 类型作为图片资源发送消息时, + * 默认根据 [Resource] 得到一个可能存在的 [OneBotImage.AdditionalParams]。 + * 注意!这无法影响直接使用 [OneBotImage] 的情况。 + */ + @ExperimentalOneBotAPI + public var defaultImageAdditionalParamsProvider: ((Resource) -> OneBotImage.AdditionalParams?)? = null + + /** + * 配置 [defaultImageAdditionalParamsProvider] + */ + @ExperimentalOneBotAPI + public fun defaultImageAdditionalParamsProvider(block: ((Resource) -> OneBotImage.AdditionalParams?)?) { + defaultImageAdditionalParamsProvider = block + } + + /** + * 配置 [defaultImageAdditionalParamsProvider] + */ + @ExperimentalOneBotAPI + public fun defaultImageAdditionalParams(params: OneBotImage.AdditionalParams?) { + defaultImageAdditionalParamsProvider = { params } + } } diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/bot/OneBotBotSerializableConfiguration.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/bot/OneBotBotSerializableConfiguration.kt index 646699a..06ee067 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/bot/OneBotBotSerializableConfiguration.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/bot/OneBotBotSerializableConfiguration.kt @@ -23,6 +23,7 @@ import kotlinx.serialization.Serializable import love.forte.simbot.bot.SerializableBotConfiguration import love.forte.simbot.component.onebot.common.annotations.InternalOneBotAPI import love.forte.simbot.component.onebot.v11.core.component.OneBot11Component +import love.forte.simbot.component.onebot.v11.message.segment.OneBotImage /** @@ -69,9 +70,32 @@ public data class OneBotBotSerializableConfiguration( /** * @see OneBotBotConfiguration.wsConnectRetryDelayMillis */ - val wsConnectRetryDelayMillis: Long? = null + val wsConnectRetryDelayMillis: Long? = null, + + /** + * @see OneBotBotConfiguration.defaultImageAdditionalParams + */ + val defaultImageAdditionalParams: AdditionalParams? = null, ) + @Serializable + public data class AdditionalParams( + val localFileToBase64: Boolean = false, + val type: String? = null, + val cache: Boolean? = null, + val proxy: Boolean? = null, + val timeout: Int? = null, + ) { + internal fun resolve(): OneBotImage.AdditionalParams = + OneBotImage.AdditionalParams().apply { + localFileToBase64 = this@AdditionalParams.localFileToBase64 + type = this@AdditionalParams.type + cache = this@AdditionalParams.cache + proxy = this@AdditionalParams.proxy + timeout = this@AdditionalParams.timeout + } + } + public fun toConfiguration(): OneBotBotConfiguration = OneBotBotConfiguration().also { conf -> conf.botUniqueId = authorization.botUniqueId @@ -85,6 +109,7 @@ public data class OneBotBotSerializableConfiguration( apiHttpSocketTimeoutMillis?.also { conf.apiHttpSocketTimeoutMillis = it } wsConnectMaxRetryTimes?.also { conf.wsConnectMaxRetryTimes = it } wsConnectRetryDelayMillis?.also { conf.wsConnectRetryDelayMillis = it } + defaultImageAdditionalParams?.also { conf.defaultImageAdditionalParams(it.resolve()) } } } } diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/event/internal/message/OneBotGroupMessageEventImpl.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/event/internal/message/OneBotGroupMessageEventImpl.kt index 087f989..ac8c5fd 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/event/internal/message/OneBotGroupMessageEventImpl.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/event/internal/message/OneBotGroupMessageEventImpl.kt @@ -31,12 +31,12 @@ import love.forte.simbot.component.onebot.v11.core.event.message.OneBotNormalGro import love.forte.simbot.component.onebot.v11.core.event.message.OneBotNoticeGroupMessageEvent import love.forte.simbot.component.onebot.v11.core.internal.message.OneBotMessageContentImpl import love.forte.simbot.component.onebot.v11.core.internal.message.toReceipt +import love.forte.simbot.component.onebot.v11.core.utils.resolveToOneBotSegmentList import love.forte.simbot.component.onebot.v11.core.utils.sendGroupMsgApi import love.forte.simbot.component.onebot.v11.core.utils.sendGroupTextMsgApi import love.forte.simbot.component.onebot.v11.event.message.RawGroupMessageEvent import love.forte.simbot.component.onebot.v11.message.OneBotMessageContent import love.forte.simbot.component.onebot.v11.message.OneBotMessageReceipt -import love.forte.simbot.component.onebot.v11.message.resolveToOneBotSegmentList import love.forte.simbot.message.Message import love.forte.simbot.message.MessageContent @@ -87,7 +87,7 @@ internal abstract class OneBotGroupMessageEventImpl( override suspend fun reply(message: Message): OneBotMessageReceipt { return sendGroupMsgApi( target = sourceEvent.groupId, - message = message.resolveToOneBotSegmentList(), + message = message.resolveToOneBotSegmentList(bot), reply = sourceEvent.messageId ).requestDataBy(bot).toReceipt(bot) } diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/event/internal/message/OneBotPrivateMessageEventImpl.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/event/internal/message/OneBotPrivateMessageEventImpl.kt index d6b6dba..b6db1b7 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/event/internal/message/OneBotPrivateMessageEventImpl.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/event/internal/message/OneBotPrivateMessageEventImpl.kt @@ -32,12 +32,12 @@ import love.forte.simbot.component.onebot.v11.core.event.message.OneBotGroupPriv import love.forte.simbot.component.onebot.v11.core.event.message.OneBotPrivateMessageEvent import love.forte.simbot.component.onebot.v11.core.internal.message.OneBotMessageContentImpl import love.forte.simbot.component.onebot.v11.core.internal.message.toReceipt +import love.forte.simbot.component.onebot.v11.core.utils.resolveToOneBotSegmentList import love.forte.simbot.component.onebot.v11.core.utils.sendPrivateMsgApi import love.forte.simbot.component.onebot.v11.core.utils.sendPrivateTextMsgApi import love.forte.simbot.component.onebot.v11.event.message.RawPrivateMessageEvent import love.forte.simbot.component.onebot.v11.message.OneBotMessageContent import love.forte.simbot.component.onebot.v11.message.OneBotMessageReceipt -import love.forte.simbot.component.onebot.v11.message.resolveToOneBotSegmentList import love.forte.simbot.message.Message import love.forte.simbot.message.MessageContent @@ -81,7 +81,7 @@ internal abstract class OneBotPrivateMessageEventImpl( override suspend fun reply(message: Message): OneBotMessageReceipt { return sendPrivateMsgApi( target = sourceEvent.userId, - message = message.resolveToOneBotSegmentList() + message = message.resolveToOneBotSegmentList(bot) ).requestDataBy(bot).toReceipt(bot) } } diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/utils/MessageResolvers.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/utils/MessageResolvers.kt new file mode 100644 index 0000000..19d0a27 --- /dev/null +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/utils/MessageResolvers.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024. ForteScarlet. + * + * This file is part of simbot-component-onebot. + * + * simbot-component-onebot 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-onebot 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-onebot. + * If not, see . + */ + +package love.forte.simbot.component.onebot.v11.core.utils + +import love.forte.simbot.component.onebot.v11.core.bot.OneBotBot +import love.forte.simbot.component.onebot.v11.message.resolveToOneBotSegmentList +import love.forte.simbot.component.onebot.v11.message.segment.OneBotMessageSegment +import love.forte.simbot.message.Message + +internal suspend fun Message.resolveToOneBotSegmentList( + bot: OneBotBot, +): List = resolveToOneBotSegmentList( + defaultImageAdditionalParams = bot.configuration.defaultImageAdditionalParamsProvider +) diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/jvmTest/kotlin/BotDefaultImageAdditionalTests.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/jvmTest/kotlin/BotDefaultImageAdditionalTests.kt new file mode 100644 index 0000000..a4cd8bb --- /dev/null +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/jvmTest/kotlin/BotDefaultImageAdditionalTests.kt @@ -0,0 +1,71 @@ +import kotlinx.coroutines.test.runTest +import love.forte.simbot.component.onebot.v11.core.bot.firstOneBotBotManager +import love.forte.simbot.component.onebot.v11.core.bot.register +import love.forte.simbot.component.onebot.v11.core.useOneBot11 +import love.forte.simbot.component.onebot.v11.message.resolveToOneBotSegment +import love.forte.simbot.component.onebot.v11.message.segment.OneBotImage +import love.forte.simbot.core.application.launchSimpleApplication +import love.forte.simbot.message.OfflineImage.Companion.toOfflineImage +import love.forte.simbot.resource.toResource +import java.nio.file.Files +import kotlin.io.path.Path +import kotlin.io.path.absolutePathString +import kotlin.io.path.writeText +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertIs +import kotlin.test.assertTrue + + +/** + * + * @author ForteScarlet + */ +class BotDefaultImageAdditionalTests { + + @Test + fun botDefaultImageAdditionalTest() = runTest { + val app = launchSimpleApplication { + useOneBot11() + } + + val obm = app.botManagers.firstOneBotBotManager() + val bot1 = obm.register { + botUniqueId = "123" + defaultImageAdditionalParams( + OneBotImage.AdditionalParams().apply { + localFileToBase64 = true + } + ) + } + val bot2 = obm.register { + botUniqueId = "456" + } + + val f = Files.createTempFile( + Path("."), + "temp", + "botDefaultImageAdditionalTestText", + ) + f.toFile().deleteOnExit() + f.writeText("Hello") + + val img = f.toResource().toOfflineImage() + + val seg1 = img.resolveToOneBotSegment( + bot1.configuration.defaultImageAdditionalParamsProvider + ) + + assertIs(seg1) + assertTrue(seg1.data.file.startsWith("base64")) + + val seg2 = img.resolveToOneBotSegment( + bot2.configuration.defaultImageAdditionalParamsProvider + ) + + assertIs(seg2) + assertEquals(f.absolutePathString(), seg2.data.file) + + } + +} diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-message/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/message/MessageElementResolvers.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-message/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/message/MessageElementResolvers.kt index 9ba6f4d..1c03a4a 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-message/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/message/MessageElementResolvers.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-message/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/message/MessageElementResolvers.kt @@ -59,13 +59,15 @@ public fun OneBotMessageSegment.resolveToMessageElement(): Message.Element { */ @InternalOneBotAPI @JvmSynthetic -public suspend fun Message.resolveToOneBotSegmentList(): List { +public suspend fun Message.resolveToOneBotSegmentList( + defaultImageAdditionalParams: ((Resource) -> OneBotImage.AdditionalParams?)? = null, +): List { return when (this) { - is Message.Element -> resolveToOneBotSegment() + is Message.Element -> resolveToOneBotSegment(defaultImageAdditionalParams) ?.let { listOf(it) } ?: emptyList() - is Messages -> mapNotNull { it.resolveToOneBotSegment() } + is Messages -> mapNotNull { it.resolveToOneBotSegment(defaultImageAdditionalParams) } } } @@ -74,7 +76,9 @@ public suspend fun Message.resolveToOneBotSegmentList(): List OneBotImage.AdditionalParams?)? = null, +): OneBotMessageSegment? { return when (this) { // OB组件的 segment 类型,直接使用 is OneBotMessageSegmentElement -> segment @@ -87,7 +91,8 @@ public suspend fun Message.Element.resolveToOneBotSegment(): OneBotMessageSegmen when (this) { // offline image is OfflineImage -> suspendCancellableCoroutine { continuation -> - offlineImageResolver().resolve(this, continuation) + offlineImageResolver(defaultImageAdditionalParams) + .resolve(this, continuation) } // remote images, OneBot组件中实际上没有此类型的实现 @@ -107,27 +112,31 @@ public suspend fun Message.Element.resolveToOneBotSegment(): OneBotMessageSegmen /** * 解析一个 [OfflineImage] 中的内容为 [OneBotMessageSegment]。 */ -internal expect fun offlineImageResolver(): OfflineImageValueResolver> +internal expect fun offlineImageResolver( + defaultImageAdditionalParams: ((Resource) -> OneBotImage.AdditionalParams?)?, +): OfflineImageValueResolver> /** * 给非JVM平台目标使用的共享代码 */ -internal fun commonOfflineImageResolver(): OfflineImageValueResolver> = +internal fun commonOfflineImageResolver( + defaultImageAdditionalParams: ((Resource) -> OneBotImage.AdditionalParams?)?, +): OfflineImageValueResolver> = object : OfflineImageValueResolver> { override fun resolveUnknown(image: OfflineImage, context: Continuation) { resolveUnknown0(context) } override fun resolveByteArray(byteArray: ByteArray, context: Continuation) { - resolveByteArray0(byteArray, context) + resolveByteArray0(defaultImageAdditionalParams, byteArray, context) } override fun resolveString(string: String, context: Continuation) { - resolveString0(string, context) + resolveString0(defaultImageAdditionalParams, string, context) } override fun resolveUnknown(resource: Resource, context: Continuation) { - resolveUnknown0(context) + resolveByteArray(resource.data(), context) } } @@ -143,16 +152,22 @@ internal fun resolveUnknown0(context: Continuation) { * 直接交给 [OneBotImage] 处理, * 应当会被视为 base64 字符串。 */ -internal fun resolveString0(string: String, context: Continuation) { - context.resume( - OneBotImage.create(string.toStringResource()) - ) +internal fun resolveString0( + defaultImageAdditionalParams: ((Resource) -> OneBotImage.AdditionalParams?)?, + string: String, + context: Continuation +) { + val resource = string.toStringResource() + val additional = defaultImageAdditionalParams?.invoke(resource) + context.resume(OneBotImage.create(resource, additional)) } -internal fun resolveByteArray0(byteArray: ByteArray, context: Continuation) { - context.resume( - OneBotImage.create( - byteArray.toResource() - ) - ) +internal fun resolveByteArray0( + defaultImageAdditionalParams: ((Resource) -> OneBotImage.AdditionalParams?)?, + byteArray: ByteArray, + context: Continuation +) { + val resource = byteArray.toResource() + val additional = defaultImageAdditionalParams?.invoke(resource) + context.resume(OneBotImage.create(resource, additional)) } diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-message/src/jsMain/kotlin/love/forte/simbot/component/onebot/v11/message/MessageElementResolvers.js.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-message/src/jsMain/kotlin/love/forte/simbot/component/onebot/v11/message/MessageElementResolvers.js.kt index 577f7f2..e7c2139 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-message/src/jsMain/kotlin/love/forte/simbot/component/onebot/v11/message/MessageElementResolvers.js.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-message/src/jsMain/kotlin/love/forte/simbot/component/onebot/v11/message/MessageElementResolvers.js.kt @@ -17,9 +17,15 @@ package love.forte.simbot.component.onebot.v11.message +import love.forte.simbot.component.onebot.v11.message.segment.OneBotImage import love.forte.simbot.component.onebot.v11.message.segment.OneBotMessageSegment import love.forte.simbot.message.OfflineImageValueResolver +import love.forte.simbot.resource.Resource import kotlin.coroutines.Continuation -internal actual fun offlineImageResolver(): OfflineImageValueResolver> = - commonOfflineImageResolver() +internal actual fun offlineImageResolver( + defaultImageAdditionalParams: ((Resource) -> OneBotImage.AdditionalParams?)?, +): OfflineImageValueResolver> = + commonOfflineImageResolver( + defaultImageAdditionalParams = defaultImageAdditionalParams, + ) diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-message/src/jvmMain/kotlin/love/forte/simbot/component/onebot/v11/message/MessageElementResolvers.jvm.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-message/src/jvmMain/kotlin/love/forte/simbot/component/onebot/v11/message/MessageElementResolvers.jvm.kt index 39faa2a..8de9a6c 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-message/src/jvmMain/kotlin/love/forte/simbot/component/onebot/v11/message/MessageElementResolvers.jvm.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-message/src/jvmMain/kotlin/love/forte/simbot/component/onebot/v11/message/MessageElementResolvers.jvm.kt @@ -17,8 +17,7 @@ package love.forte.simbot.component.onebot.v11.message -import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.* import love.forte.simbot.annotations.Api4J import love.forte.simbot.annotations.InternalSimbotAPI import love.forte.simbot.component.onebot.common.annotations.InternalOneBotAPI @@ -111,14 +110,16 @@ public fun Message.Element.resolveToOneBotSegmentReserve(): SuspendReserve> = +internal actual fun offlineImageResolver( + defaultImageAdditionalParams: ((Resource) -> OneBotImage.AdditionalParams?)?, +): OfflineImageValueResolver> = object : JvmOfflineImageValueResolver>() { override fun resolveUnknownInternal(image: OfflineImage, context: Continuation) { resolveUnknown0(context) } override fun resolveByteArray(byteArray: ByteArray, context: Continuation) { - resolveByteArray0(byteArray, context) + resolveByteArray0(defaultImageAdditionalParams, byteArray, context) } /** @@ -126,9 +127,12 @@ internal actual fun offlineImageResolver(): OfflineImageValueResolver) { + val resource = file.toResource() + val additional = defaultImageAdditionalParams?.invoke(resource) context.resume( OneBotImage.create( - file.toResource() + resource, + additional ) ) } @@ -138,15 +142,18 @@ internal actual fun offlineImageResolver(): OfflineImageValueResolver) { + val resource = path.toResource() + val additional = defaultImageAdditionalParams?.invoke(resource) context.resume( OneBotImage.create( - path.toResource() + resource, + additional ) ) } override fun resolveString(string: String, context: Continuation) { - resolveString0(string, context) + resolveString0(defaultImageAdditionalParams, string, context) } /** @@ -156,14 +163,17 @@ internal actual fun offlineImageResolver(): OfflineImageValueResolver) { + val resource = uri.toResource() + val additional = defaultImageAdditionalParams?.invoke(resource) context.resume( OneBotImage.create( - uri.toResource() + resource, + additional ) ) } override fun resolveUnknownInternal(resource: Resource, context: Continuation) { - resolveUnknown0(context) + resolveByteArray(resource.data(), context) } } diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-message/src/nativeMain/kotlin/love/forte/simbot/component/onebot/v11/message/MessageElementResolvers.native.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-message/src/nativeMain/kotlin/love/forte/simbot/component/onebot/v11/message/MessageElementResolvers.native.kt index 577f7f2..e7c2139 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-message/src/nativeMain/kotlin/love/forte/simbot/component/onebot/v11/message/MessageElementResolvers.native.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-message/src/nativeMain/kotlin/love/forte/simbot/component/onebot/v11/message/MessageElementResolvers.native.kt @@ -17,9 +17,15 @@ package love.forte.simbot.component.onebot.v11.message +import love.forte.simbot.component.onebot.v11.message.segment.OneBotImage import love.forte.simbot.component.onebot.v11.message.segment.OneBotMessageSegment import love.forte.simbot.message.OfflineImageValueResolver +import love.forte.simbot.resource.Resource import kotlin.coroutines.Continuation -internal actual fun offlineImageResolver(): OfflineImageValueResolver> = - commonOfflineImageResolver() +internal actual fun offlineImageResolver( + defaultImageAdditionalParams: ((Resource) -> OneBotImage.AdditionalParams?)?, +): OfflineImageValueResolver> = + commonOfflineImageResolver( + defaultImageAdditionalParams = defaultImageAdditionalParams, + ) From 9ad2d350bc7d8039a269a99940a294ceaebf14df Mon Sep 17 00:00:00 2001 From: ForteScarlet Date: Fri, 21 Jun 2024 18:19:41 +0800 Subject: [PATCH 4/4] release: v0.8.0 --- .changelog/v0.8.0.md | 7 + Writerside/topics/onebot11-OneBotBot.md | 422 +++++++++++++++++- Writerside/v.list | 2 +- .../v11/core/bot/OneBotBotConfiguration.kt | 17 + 4 files changed, 433 insertions(+), 15 deletions(-) create mode 100644 .changelog/v0.8.0.md diff --git a/.changelog/v0.8.0.md b/.changelog/v0.8.0.md new file mode 100644 index 0000000..2529b95 --- /dev/null +++ b/.changelog/v0.8.0.md @@ -0,0 +1,7 @@ +> 对应核心版本: [**v4.0.1**](https://github.com/simple-robot/simpler-robot/releases/tag/v4.0.1) + + +我们欢迎并期望着您的的[反馈](https://github.com/simple-robot/simbot-component-onebot/issues)或[协助](https://github.com/simple-robot/simbot-component-onebot/pulls), +感谢您的贡献与支持! + +也欢迎您为我们献上一颗 `star`,这是对我们最大的鼓励与认可! diff --git a/Writerside/topics/onebot11-OneBotBot.md b/Writerside/topics/onebot11-OneBotBot.md index 32cca1d..616f30f 100644 --- a/Writerside/topics/onebot11-OneBotBot.md +++ b/Writerside/topics/onebot11-OneBotBot.md @@ -48,30 +48,424 @@ OneBot11协议不支持 `Guild` (即频道) 相关的操作,始终得到 `null +启动Bot。在 Spring Boot 环境默认配置下会自动启动扫描注册的所有Bot。 + 除了上述的属性和API,`OneBotBot` 还提供了一些额外的内容: - - - - - - - - - - + +Bot内部在进行一些API调用时使用的JSON序列化器。 + + +注册Bot时使用的配置类信息。 + + +Bot进行API请求时使用的HttpClient。 + + +Bot进行API请求时使用的服务地址的host,来自配置信息。 + + +Bot进行API请求时使用的accessToken,来自配置信息。 + + + +Bot自己的信息,在使用 `start` 之后会通过API查询获取。 +如果尚未 `start` 或调用 `queryLoginInfo()` 就获取会得到异常。 + + + +通过API查询当前Bot的信息,并同时更新 `userId` 和 `name`。 + + + +使用API查询 Cookies。 + + + +使用API查询 Credentials。 + + + +使用API查询 CsrfToken。 + -### 关系对象 +## 关系对象 即对好友和群的相关操作,通过 `contactRelation` 和 `groupRelation` 进行。 -#### OneBotBotFriendRelation -#### OneBotBotGroupRelation +### OneBotBotFriendRelation + +通过 `contactRelation` 获取, +代表对联系人(也就是好友 `OneBotFriend`)和陌生人(`OneBotStranger`)的查询操作。 + + + + +```Kotlin +val bot: OneBotBot = ... +val relation = bot.contactRelation +// 获取所有好友信息并遍历 +relation.contacts + .asFlow() + .collect { friend -> println("Friend: $friend") } + +// 根据ID寻找指定的好友 +val friend = relation.contact(123L.ID) + +// 根据ID寻找指定的陌生人 +val stranger = relation.stranger(456L.ID) +``` + + + + +```Java +final var relation = bot.getContactRelation(); +// 在异步中遍历好友列表,使用bot作为 CoroutineScope +relation.getContacts().collectAsync( + bot, + friend -> System.out.println("Friend: " + friend) +); + +// 也可以使用 transform 或 Collectables 提供的能力转化为某种异步结果 +final var friendCollectable = relation.getContacts(); +Collectables.toListAsync(friendCollectable) + .thenAccept(friendList -> { + // ... + }); + +// 根据ID寻找指定的好友 +relation.getContactAsync(Identifies.of(123L)) + .thenAccept(friend -> { // Warn: nullable + // ... + }); + +// 根据ID寻找指定的陌生人 +relation.getStrangerAsync(Identifies.of(123L)) + .thenAccept(stranger -> { // Warn: nullable + // ... + }); +``` +{switcher-key=%ja%} + +```Java +final var relation = bot.getContactRelation(); +// 查询所有角色列表并阻塞地收集为List +final var list = relation.getContacts() + .transform(SuspendReserves.list()); + +for (var friend : list) { + System.out.println("Friend: " + friend); +} + +// 根据ID寻找指定的好友 +final var friend = relation.getContact(Identifies.of(123L)); + +// 根据ID寻找指定的陌生人 +final var stranger = relation.getStranger(Identifies.of(123L)); +``` +{switcher-key=%jb%} + +```Java +final var relation = bot.getContactRelation(); +// 查询所有的好友并转化为 Flux 后遍历 +relation.getContacts() + .transform(SuspendReserves.flux()) + .subscribe(friend -> System.out.println("Friend: " + friend)); + +// 根据ID寻找指定的好友 +relation.getContactReserve(Identifies.of(123L)) + .transform(SuspendReserves.mono()) + .subscribe(friend -> { + // ... + }); + +// 根据ID寻找指定的陌生人 +relation.getStrangerReserve(Identifies.of(123L)) + .transform(SuspendReserves.mono()) + .subscribe(friend -> { + // ... + }); +``` +{switcher-key=%jr%} + + + + +### OneBotBotGroupRelation + +通过 `contactRelation` 获取, +代表对群(`OneBotGroup`)的查询操作。 + + + + +```Kotlin +val bot: OneBotBot = ... +val relation = bot.groupRelation +// 获取所有群并遍历 +relation.groups + .asFlow() + .collect { group -> println("Group: $group") } + +// 根据ID寻找指定的群 +val group = relation.group(123L.ID) + +// 根据ID直接寻找指定的群内的群成员 +val member = relation.member(123L.ID, 456L.ID) +``` + + + + +```Java +final var relation = bot.getGroupRelation(); +// 在异步中遍历群列表,使用bot作为 CoroutineScope +relation.getGroups().collectAsync( + bot, + group -> System.out.println("Group: " + group) +); + +// 也可以使用 transform 或 Collectables 提供的能力转化为某种异步结果 +final var groupCollectable = relation.getGroups(); +Collectables.toListAsync(groupCollectable) + .thenAccept(groupList -> { + // ... + }); + +// 根据ID寻找指定的群 +relation.getGroupAsync(Identifies.of(123L)) + .thenAccept(group -> { // Warn: nullable + // ... + }); + +// 根据ID寻找指定的群内成员 +relation.getMemberAsync(Identifies.of(123L), Identifies.of(456L)) + .thenAccept(member -> { // Warn: nullable + // ... + }); +``` +{switcher-key=%ja%} + +```Java +final var relation = bot.getGroupRelation(); +// 查询所有角色列表并阻塞地收集为List +final var list = relation.getContacts() + .transform(SuspendReserves.list()); + +for (var friend : list) { + System.out.println("Friend: " + friend); +} + +// 根据ID寻找指定的好友 +final var friend = relation.getContact(Identifies.of(123L)); + +// 根据ID寻找指定的陌生人 +final var stranger = relation.getStranger(Identifies.of(123L)); +``` +{switcher-key=%jb%} + +```Java +final var relation = bot.getGroupRelation(); +// 查询所有的群并转化为 Flux 后遍历 +relation.getGroup() + .transform(SuspendReserves.flux()) + .subscribe(group -> System.out.println("Group: " + group)); + +// 根据ID寻找指定的群 +relation.getGroupReserve(Identifies.of(123L)) + .transform(SuspendReserves.mono()) + .subscribe(group -> { + // ... + }); + +// 根据ID寻找指定的群内成员 +relation.getMemberReserve(Identifies.of(123L), Identifies.of(456L)) + .transform(SuspendReserves.mono()) + .subscribe(group -> { + // ... + }); +``` +{switcher-key=%jr%} + + + + +## OneBotBotManager + +`OneBotBotManager` 实现simbot标准API的 `BotManager`,作为一个Bot管理器, +它用于注册生产与管理 `OneBotBot`。 + +### 获取 OneBotBotManager + +当 `Application` 注册完成后,即可通过其中的 `botManagers` 寻找所需的 `BotManager`。 +在 OneBot 组件中,我们通常要寻找 `OneBotBotManager`。 + + + + +```Kotlin +val application: Application = ... +// 得到第一个 OneBotBotManager +val obManager = application.botManagers.firstOneBotBotManager() + +obManager.all().forEach { + // 遍历所有的bot。。。 +} + +// 通过你配置的 uniqueBotId 获取 +val bot = obManager["123456789".ID] +``` + + + + +```Java +final Application application; + +// 得到第一个 OneBotBotManager +var obManager = application.getBotManagers() + .stream() + .filter(it -> it instanceof OneBotBotManager) + .map(it -> (OneBotBotManager) it) + .findFirst() + .orElseThrow(); + +// 遍历bot +obManager.all().iterator().forEachRemaining(bot -> { + // ... +}); + +// 也可以转成List +final var list = SequencesKt.toList(obManager.all()); + +// 通过你配置的 uniqueBotId 获取 +final var bot = obManager.get(Identifies.of("123456789")); +``` + + + + +### 注册 OneBotBot + +如果你打算以编程的方式动态注册 `OneBotBot`,那么在获取到 `OneBotBotManager` +之后使用 `register` 即可。 + + + + +```Kotlin +val manager: OneBotBotManager = ... +val bot = manager.register { + botUniqueId = "" + apiServerHost = ... + eventServerHost = ... + // 上面是必填属性,不过两个Host有默认值 + // 其他可选参数可自行探索,此处省略 +} +``` + + + + +```Java +final var configuration = new OneBotBotConfiguration(); +configuration.setBotUniqueId(...); +configuration.setApiServerHost(...); +configuration.setEventServerHost(...); +// 上面是必填属性,不过两个Host有默认值 +// 其他可选参数可自行探索,此处省略 + +final var bot = onManager.register(configuration); +``` + + + + + + +注册完Bot后记得使用 `start()` 启动它们! + + +## Spring Boot + +在 Spring Boot starter 默认配置环境下,`OneBotBotManager` 会被自动注册, +并会扫描所有 +Bot配置文件 +并解析、注册为 OneBotBot 后自动在 异步中 启动。 + +在 Spring 中,你可以通过注入 `Application` 来获取到 `BotManager`。 + + + + +```Kotlin +@Component +class MyComponent( + val application: Application +) { + // 假设它会被定时任务或者HTTP接口等其他地方调用 + fun run() { + val obManager = application.botManagers.firstOneBotBotManager() + obManager.all().forEach { + // 遍历所有的bot。。。 + } + + // 通过你配置的 uniqueBotId 获取 + val bot = obManager["123456789".ID] + } +} +``` + + + + +```Java +@Component +public class MyComponent { + private final Application application; + public MyComponent(Application application) { + this.application = application; + } + + // 假设它会被定时任务或者HTTP接口等其他地方调用 + public void run() { + var obManager = application.getBotManagers() + .stream() + .filter(it -> it instanceof OneBotBotManager) + .map(it -> (OneBotBotManager) it) + .findFirst() + .orElseThrow(); + + // 遍历bot + obManager.all().iterator().forEachRemaining(bot -> { + // ... + }); + + // 也可以转成List + final var list = SequencesKt.toList(obManager.all()); + + // 通过你配置的 uniqueBotId 获取 + final var bot = obManager.get(Identifies.of("123456789")); + } +} +``` + + + + + +由于 `Bot` 都是在异步中注册、启动的,因此无法保证Spring启动完成后就可以 +**立即** 获取到你的Bot。 +请做好逻辑处理,当暂时获取不到时,跳过本次处理。 -TODO +或者你也可以监听事件 `OneBotBotStartedEvent` 来得知哪些Bot启动了。 +有关事件的更多内容参考 + 。 + diff --git a/Writerside/v.list b/Writerside/v.list index 1f9fc75..34d35c3 100644 --- a/Writerside/v.list +++ b/Writerside/v.list @@ -7,7 +7,7 @@ - + diff --git a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/bot/OneBotBotConfiguration.kt b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/bot/OneBotBotConfiguration.kt index e1fa9ea..f4416b2 100644 --- a/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/bot/OneBotBotConfiguration.kt +++ b/simbot-component-onebot-v11/simbot-component-onebot-v11-core/src/commonMain/kotlin/love/forte/simbot/component/onebot/v11/core/bot/OneBotBotConfiguration.kt @@ -75,11 +75,28 @@ public class OneBotBotConfiguration { */ public var apiServerHost: Url = Url("http://localhost:3001") + /** + * 配置 [apiServerHost] + * + * @see apiServerHost + */ + public fun setApiServerHost(urlString: String) { + apiServerHost = Url(urlString) + } + /** * 必填属性,订阅事件的目标服务器地址。应当是ws或wss协议。 */ public var eventServerHost: Url = Url("ws://localhost:3001") + /** + * 配置 [eventServerHost] + * + * @see eventServerHost + */ + public fun setEventServerHost(urlString: String) { + eventServerHost = Url(urlString) + } /** * 用于API请求的 [HttpClient] 所使用的[引擎](https://ktor.io/docs/http-client-engines.html)。